1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
18#define ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
19
20#include "arch/instruction_set.h"
21#include "method_reference.h"
22#include "relative_patcher.h"
23#include "safe_map.h"
24
25namespace art {
26
27class CompiledMethod;
28class LinkerPatch;
29class InstructionSetFeatures;
30
31namespace linker {
32
33// MultiOatRelativePatcher is a helper class for handling patching across
34// any number of oat files. It provides storage for method code offsets
35// and wraps RelativePatcher calls, adjusting relative offsets according
36// to the value set by SetAdjustment().
37class MultiOatRelativePatcher FINAL {
38 public:
39  using const_iterator =
40      SafeMap<MethodReference, uint32_t, MethodReferenceComparator>::const_iterator;
41
42  MultiOatRelativePatcher(InstructionSet instruction_set, const InstructionSetFeatures* features);
43
44  // Mark the start of a new oat file (for statistics retrieval) and set the
45  // adjustment for a new oat file to apply to all relative offsets that are
46  // passed to the MultiOatRelativePatcher.
47  //
48  // The adjustment should be the global offset of the base from which relative
49  // offsets are calculated, such as the start of .rodata for the current oat file.
50  // It must must never point directly to a method's code to avoid relative offsets
51  // with value 0 because this value is used as a missing offset indication in
52  // GetOffset() and an error indication in WriteThunks(). Additionally, it must be
53  // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP.
54  void StartOatFile(uint32_t adjustment);
55
56  // Get relative offset. Returns 0 when the offset has not been set yet.
57  uint32_t GetOffset(MethodReference method_ref) {
58    auto it = method_offset_map_.map.find(method_ref);
59    return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u;
60  }
61
62  // Set the offset.
63  void SetOffset(MethodReference method_ref, uint32_t offset) {
64    method_offset_map_.map.Put(method_ref, offset + adjustment_);
65  }
66
67  // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment.
68  uint32_t ReserveSpace(uint32_t offset,
69                        const CompiledMethod* compiled_method,
70                        MethodReference method_ref) {
71    offset += adjustment_;
72    offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref);
73    offset -= adjustment_;
74    return offset;
75  }
76
77  // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment.
78  uint32_t ReserveSpaceEnd(uint32_t offset) {
79    offset += adjustment_;
80    offset = relative_patcher_->ReserveSpaceEnd(offset);
81    offset -= adjustment_;
82    return offset;
83  }
84
85  // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment.
86  uint32_t WriteThunks(OutputStream* out, uint32_t offset) {
87    offset += adjustment_;
88    offset = relative_patcher_->WriteThunks(out, offset);
89    if (offset != 0u) {  // 0u indicates write error.
90      offset -= adjustment_;
91    }
92    return offset;
93  }
94
95  // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment.
96  void PatchCall(std::vector<uint8_t>* code,
97                 uint32_t literal_offset,
98                 uint32_t patch_offset,
99                 uint32_t target_offset) {
100    patch_offset += adjustment_;
101    target_offset += adjustment_;
102    relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset);
103  }
104
105  // Wrapper around RelativePatcher::PatchDexCacheReference(), doing offset adjustment.
106  void PatchPcRelativeReference(std::vector<uint8_t>* code,
107                                const LinkerPatch& patch,
108                                uint32_t patch_offset,
109                                uint32_t target_offset) {
110    patch_offset += adjustment_;
111    target_offset += adjustment_;
112    relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset);
113  }
114
115  // Wrappers around RelativePatcher for statistics retrieval.
116  uint32_t CodeAlignmentSize() const;
117  uint32_t RelativeCallThunksSize() const;
118  uint32_t MiscThunksSize() const;
119
120 private:
121  // Map method reference to assigned offset.
122  // Wrap the map in a class implementing linker::RelativePatcherTargetProvider.
123  class MethodOffsetMap : public linker::RelativePatcherTargetProvider {
124   public:
125    std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) OVERRIDE;
126    SafeMap<MethodReference, uint32_t, MethodReferenceComparator> map;
127  };
128
129  MethodOffsetMap method_offset_map_;
130  std::unique_ptr<RelativePatcher> relative_patcher_;
131  uint32_t adjustment_;
132  InstructionSet instruction_set_;
133
134  uint32_t start_size_code_alignment_;
135  uint32_t start_size_relative_call_thunks_;
136  uint32_t start_size_misc_thunks_;
137
138  friend class MultiOatRelativePatcherTest;
139
140  DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher);
141};
142
143}  // namespace linker
144}  // namespace art
145
146#endif  // ART_COMPILER_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
147