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_DEX2OAT_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
18 #define ART_DEX2OAT_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
19 
20 #include "arch/instruction_set.h"
21 #include "base/safe_map.h"
22 #include "debug/method_debug_info.h"
23 #include "dex/method_reference.h"
24 #include "linker/relative_patcher.h"
25 
26 namespace art {
27 
28 class CompiledMethod;
29 class CompiledMethodStorage;
30 class InstructionSetFeatures;
31 
32 namespace linker {
33 
34 // MultiOatRelativePatcher is a helper class for handling patching across
35 // any number of oat files. It provides storage for method code offsets
36 // and wraps RelativePatcher calls, adjusting relative offsets according
37 // to the value set by SetAdjustment().
38 class MultiOatRelativePatcher final {
39  public:
40   using const_iterator = SafeMap<MethodReference, uint32_t>::const_iterator;
41 
42   MultiOatRelativePatcher(InstructionSet instruction_set,
43                           const InstructionSetFeatures* features,
44                           CompiledMethodStorage* storage);
45 
46   // Mark the start of a new oat file (for statistics retrieval) and set the
47   // adjustment for a new oat file to apply to all relative offsets that are
48   // passed to the MultiOatRelativePatcher.
49   //
50   // The adjustment should be the global offset of the base from which relative
51   // offsets are calculated, such as the start of .rodata for the current oat file.
52   // It must must never point directly to a method's code to avoid relative offsets
53   // with value 0 because this value is used as a missing offset indication in
54   // GetOffset() and an error indication in WriteThunks(). Additionally, it must be
55   // page-aligned, so that it does not skew alignment calculations, say arm64 ADRP.
56   void StartOatFile(uint32_t adjustment);
57 
58   // Get relative offset. Returns 0 when the offset has not been set yet.
GetOffset(MethodReference method_ref)59   uint32_t GetOffset(MethodReference method_ref) {
60     auto it = method_offset_map_.map.find(method_ref);
61     return (it != method_offset_map_.map.end()) ? it->second - adjustment_ : 0u;
62   }
63 
64   // Set the offset.
SetOffset(MethodReference method_ref,uint32_t offset)65   void SetOffset(MethodReference method_ref, uint32_t offset) {
66     method_offset_map_.map.Put(method_ref, offset + adjustment_);
67   }
68 
69   // Wrapper around RelativePatcher::ReserveSpace(), doing offset adjustment.
ReserveSpace(uint32_t offset,const CompiledMethod * compiled_method,MethodReference method_ref)70   uint32_t ReserveSpace(uint32_t offset,
71                         const CompiledMethod* compiled_method,
72                         MethodReference method_ref) {
73     offset += adjustment_;
74     offset = relative_patcher_->ReserveSpace(offset, compiled_method, method_ref);
75     offset -= adjustment_;
76     return offset;
77   }
78 
79   // Wrapper around RelativePatcher::ReserveSpaceEnd(), doing offset adjustment.
ReserveSpaceEnd(uint32_t offset)80   uint32_t ReserveSpaceEnd(uint32_t offset) {
81     offset += adjustment_;
82     offset = relative_patcher_->ReserveSpaceEnd(offset);
83     offset -= adjustment_;
84     return offset;
85   }
86 
87   // Wrapper around RelativePatcher::WriteThunks(), doing offset adjustment.
WriteThunks(OutputStream * out,uint32_t offset)88   uint32_t WriteThunks(OutputStream* out, uint32_t offset) {
89     offset += adjustment_;
90     offset = relative_patcher_->WriteThunks(out, offset);
91     if (offset != 0u) {  // 0u indicates write error.
92       offset -= adjustment_;
93     }
94     return offset;
95   }
96 
97   // Wrapper around RelativePatcher::PatchCall(), doing offset adjustment.
PatchCall(std::vector<uint8_t> * code,uint32_t literal_offset,uint32_t patch_offset,uint32_t target_offset)98   void PatchCall(std::vector<uint8_t>* code,
99                  uint32_t literal_offset,
100                  uint32_t patch_offset,
101                  uint32_t target_offset) {
102     patch_offset += adjustment_;
103     target_offset += adjustment_;
104     relative_patcher_->PatchCall(code, literal_offset, patch_offset, target_offset);
105   }
106 
107   // Wrapper around RelativePatcher::PatchPcRelativeReference(), doing offset adjustment.
PatchPcRelativeReference(std::vector<uint8_t> * code,const LinkerPatch & patch,uint32_t patch_offset,uint32_t target_offset)108   void PatchPcRelativeReference(std::vector<uint8_t>* code,
109                                 const LinkerPatch& patch,
110                                 uint32_t patch_offset,
111                                 uint32_t target_offset) {
112     patch_offset += adjustment_;
113     target_offset += adjustment_;
114     relative_patcher_->PatchPcRelativeReference(code, patch, patch_offset, target_offset);
115   }
116 
PatchEntrypointCall(std::vector<uint8_t> * code,const LinkerPatch & patch,uint32_t patch_offset)117   void PatchEntrypointCall(std::vector<uint8_t>* code,
118                            const LinkerPatch& patch,
119                            uint32_t patch_offset) {
120     patch_offset += adjustment_;
121     relative_patcher_->PatchEntrypointCall(code, patch, patch_offset);
122   }
123 
PatchBakerReadBarrierBranch(std::vector<uint8_t> * code,const LinkerPatch & patch,uint32_t patch_offset)124   void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
125                                    const LinkerPatch& patch,
126                                    uint32_t patch_offset) {
127     patch_offset += adjustment_;
128     relative_patcher_->PatchBakerReadBarrierBranch(code, patch, patch_offset);
129   }
130 
GenerateThunkDebugInfo(size_t executable_offset)131   std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(size_t executable_offset) {
132     executable_offset += adjustment_;
133     return relative_patcher_->GenerateThunkDebugInfo(executable_offset);
134   }
135 
136   // Wrappers around RelativePatcher for statistics retrieval.
137   uint32_t CodeAlignmentSize() const;
138   uint32_t RelativeCallThunksSize() const;
139   uint32_t MiscThunksSize() const;
140 
141  private:
142   class ThunkProvider : public RelativePatcherThunkProvider {
143    public:
ThunkProvider(CompiledMethodStorage * storage)144     explicit ThunkProvider(CompiledMethodStorage* storage)
145         : storage_(storage) {}
146 
147     void GetThunkCode(const LinkerPatch& patch,
148                       /*out*/ ArrayRef<const uint8_t>* code,
149                       /*out*/ std::string* debug_name) override;
150 
151    private:
152     CompiledMethodStorage* storage_;
153   };
154 
155   // Map method reference to assigned offset.
156   // Wrap the map in a class implementing RelativePatcherTargetProvider.
157   class MethodOffsetMap : public RelativePatcherTargetProvider {
158    public:
159     std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) override;
160     SafeMap<MethodReference, uint32_t> map;
161   };
162 
163   ThunkProvider thunk_provider_;
164   MethodOffsetMap method_offset_map_;
165   std::unique_ptr<RelativePatcher> relative_patcher_;
166   uint32_t adjustment_;
167   InstructionSet instruction_set_;
168 
169   uint32_t start_size_code_alignment_;
170   uint32_t start_size_relative_call_thunks_;
171   uint32_t start_size_misc_thunks_;
172 
173   friend class MultiOatRelativePatcherTest;
174 
175   DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher);
176 };
177 
178 }  // namespace linker
179 }  // namespace art
180 
181 #endif  // ART_DEX2OAT_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
182