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 
PatchBakerReadBarrierBranch(std::vector<uint8_t> * code,const LinkerPatch & patch,uint32_t patch_offset)117   void PatchBakerReadBarrierBranch(std::vector<uint8_t>* code,
118                                    const LinkerPatch& patch,
119                                    uint32_t patch_offset) {
120     patch_offset += adjustment_;
121     relative_patcher_->PatchBakerReadBarrierBranch(code, patch, patch_offset);
122   }
123 
GenerateThunkDebugInfo(size_t executable_offset)124   std::vector<debug::MethodDebugInfo> GenerateThunkDebugInfo(size_t executable_offset) {
125     executable_offset += adjustment_;
126     return relative_patcher_->GenerateThunkDebugInfo(executable_offset);
127   }
128 
129   // Wrappers around RelativePatcher for statistics retrieval.
130   uint32_t CodeAlignmentSize() const;
131   uint32_t RelativeCallThunksSize() const;
132   uint32_t MiscThunksSize() const;
133 
134  private:
135   class ThunkProvider : public RelativePatcherThunkProvider {
136    public:
ThunkProvider(CompiledMethodStorage * storage)137     explicit ThunkProvider(CompiledMethodStorage* storage)
138         : storage_(storage) {}
139 
140     void GetThunkCode(const LinkerPatch& patch,
141                       /*out*/ ArrayRef<const uint8_t>* code,
142                       /*out*/ std::string* debug_name) override;
143 
144    private:
145     CompiledMethodStorage* storage_;
146   };
147 
148   // Map method reference to assigned offset.
149   // Wrap the map in a class implementing RelativePatcherTargetProvider.
150   class MethodOffsetMap : public RelativePatcherTargetProvider {
151    public:
152     std::pair<bool, uint32_t> FindMethodOffset(MethodReference ref) override;
153     SafeMap<MethodReference, uint32_t> map;
154   };
155 
156   ThunkProvider thunk_provider_;
157   MethodOffsetMap method_offset_map_;
158   std::unique_ptr<RelativePatcher> relative_patcher_;
159   uint32_t adjustment_;
160   InstructionSet instruction_set_;
161 
162   uint32_t start_size_code_alignment_;
163   uint32_t start_size_relative_call_thunks_;
164   uint32_t start_size_misc_thunks_;
165 
166   friend class MultiOatRelativePatcherTest;
167 
168   DISALLOW_COPY_AND_ASSIGN(MultiOatRelativePatcher);
169 };
170 
171 }  // namespace linker
172 }  // namespace art
173 
174 #endif  // ART_DEX2OAT_LINKER_MULTI_OAT_RELATIVE_PATCHER_H_
175