xref: /aosp_15_r20/art/compiler/linker/linker_patch.h (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2017 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_LINKER_PATCH_H_
18 #define ART_COMPILER_LINKER_LINKER_PATCH_H_
19 
20 #include <iosfwd>
21 #include <stdint.h>
22 
23 #include <android-base/logging.h>
24 
25 #include "base/bit_utils.h"
26 #include "base/macros.h"
27 #include "dex/method_reference.h"
28 #include "dex/proto_reference.h"
29 #include "dex/string_reference.h"
30 #include "dex/type_reference.h"
31 
32 namespace art HIDDEN {
33 
34 class DexFile;
35 
36 namespace linker {
37 
38 class LinkerPatch {
39  public:
40   // Note: We explicitly specify the underlying type of the enum because GCC
41   // would otherwise select a bigger underlying type and then complain that
42   //     'art::LinkerPatch::patch_type_' is too small to hold all
43   //     values of 'enum class art::LinkerPatch::Type'
44   // which is ridiculous given we have only a handful of values here. If we
45   // choose to squeeze the Type into fewer than 8 bits, we'll have to declare
46   // patch_type_ as an uintN_t and do explicit static_cast<>s.
47   //
48   // Note: Actual patching is instruction_set-dependent.
49   enum class Type : uint8_t {
50     kIntrinsicReference,      // Boot image reference for an intrinsic, see IntrinsicObjects.
51     kBootImageRelRo,
52     kMethodRelative,
53     kMethodAppImageRelRo,
54     kMethodBssEntry,
55     kJniEntrypointRelative,
56     kCallRelative,
57     kTypeRelative,
58     kTypeAppImageRelRo,
59     kTypeBssEntry,
60     kPublicTypeBssEntry,
61     kPackageTypeBssEntry,
62     kStringRelative,
63     kStringBssEntry,
64     kMethodTypeBssEntry,
65     kCallEntrypoint,
66     kBakerReadBarrierBranch,
67   };
68 
IntrinsicReferencePatch(size_t literal_offset,uint32_t pc_insn_offset,uint32_t intrinsic_data)69   static LinkerPatch IntrinsicReferencePatch(size_t literal_offset,
70                                              uint32_t pc_insn_offset,
71                                              uint32_t intrinsic_data) {
72     LinkerPatch patch(literal_offset, Type::kIntrinsicReference, /* target_dex_file= */ nullptr);
73     patch.intrinsic_data_ = intrinsic_data;
74     patch.pc_insn_offset_ = pc_insn_offset;
75     return patch;
76   }
77 
BootImageRelRoPatch(size_t literal_offset,uint32_t pc_insn_offset,uint32_t boot_image_offset)78   static LinkerPatch BootImageRelRoPatch(size_t literal_offset,
79                                          uint32_t pc_insn_offset,
80                                          uint32_t boot_image_offset) {
81     LinkerPatch patch(literal_offset, Type::kBootImageRelRo, /* target_dex_file= */ nullptr);
82     patch.boot_image_offset_ = boot_image_offset;
83     patch.pc_insn_offset_ = pc_insn_offset;
84     return patch;
85   }
86 
RelativeMethodPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_method_idx)87   static LinkerPatch RelativeMethodPatch(size_t literal_offset,
88                                          const DexFile* target_dex_file,
89                                          uint32_t pc_insn_offset,
90                                          uint32_t target_method_idx) {
91     LinkerPatch patch(literal_offset, Type::kMethodRelative, target_dex_file);
92     patch.method_idx_ = target_method_idx;
93     patch.pc_insn_offset_ = pc_insn_offset;
94     return patch;
95   }
96 
MethodAppImageRelRoPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_method_idx)97   static LinkerPatch MethodAppImageRelRoPatch(size_t literal_offset,
98                                               const DexFile* target_dex_file,
99                                               uint32_t pc_insn_offset,
100                                               uint32_t target_method_idx) {
101     LinkerPatch patch(literal_offset, Type::kMethodAppImageRelRo, target_dex_file);
102     patch.method_idx_ = target_method_idx;
103     patch.pc_insn_offset_ = pc_insn_offset;
104     return patch;
105   }
106 
MethodBssEntryPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_method_idx)107   static LinkerPatch MethodBssEntryPatch(size_t literal_offset,
108                                          const DexFile* target_dex_file,
109                                          uint32_t pc_insn_offset,
110                                          uint32_t target_method_idx) {
111     LinkerPatch patch(literal_offset, Type::kMethodBssEntry, target_dex_file);
112     patch.method_idx_ = target_method_idx;
113     patch.pc_insn_offset_ = pc_insn_offset;
114     return patch;
115   }
116 
RelativeJniEntrypointPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_method_idx)117   static LinkerPatch RelativeJniEntrypointPatch(size_t literal_offset,
118                                                 const DexFile* target_dex_file,
119                                                 uint32_t pc_insn_offset,
120                                                 uint32_t target_method_idx) {
121     LinkerPatch patch(literal_offset, Type::kJniEntrypointRelative, target_dex_file);
122     patch.method_idx_ = target_method_idx;
123     patch.pc_insn_offset_ = pc_insn_offset;
124     return patch;
125   }
126 
RelativeCodePatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t target_method_idx)127   static LinkerPatch RelativeCodePatch(size_t literal_offset,
128                                        const DexFile* target_dex_file,
129                                        uint32_t target_method_idx) {
130     LinkerPatch patch(literal_offset, Type::kCallRelative, target_dex_file);
131     patch.method_idx_ = target_method_idx;
132     return patch;
133   }
134 
RelativeTypePatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_type_idx)135   static LinkerPatch RelativeTypePatch(size_t literal_offset,
136                                        const DexFile* target_dex_file,
137                                        uint32_t pc_insn_offset,
138                                        uint32_t target_type_idx) {
139     LinkerPatch patch(literal_offset, Type::kTypeRelative, target_dex_file);
140     patch.type_idx_ = target_type_idx;
141     patch.pc_insn_offset_ = pc_insn_offset;
142     return patch;
143   }
144 
TypeAppImageRelRoPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_type_idx)145   static LinkerPatch TypeAppImageRelRoPatch(size_t literal_offset,
146                                             const DexFile* target_dex_file,
147                                             uint32_t pc_insn_offset,
148                                             uint32_t target_type_idx) {
149     LinkerPatch patch(literal_offset, Type::kTypeAppImageRelRo, target_dex_file);
150     patch.type_idx_ = target_type_idx;
151     patch.pc_insn_offset_ = pc_insn_offset;
152     return patch;
153   }
154 
TypeBssEntryPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_type_idx)155   static LinkerPatch TypeBssEntryPatch(size_t literal_offset,
156                                        const DexFile* target_dex_file,
157                                        uint32_t pc_insn_offset,
158                                        uint32_t target_type_idx) {
159     LinkerPatch patch(literal_offset, Type::kTypeBssEntry, target_dex_file);
160     patch.type_idx_ = target_type_idx;
161     patch.pc_insn_offset_ = pc_insn_offset;
162     return patch;
163   }
164 
PublicTypeBssEntryPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_type_idx)165   static LinkerPatch PublicTypeBssEntryPatch(size_t literal_offset,
166                                              const DexFile* target_dex_file,
167                                              uint32_t pc_insn_offset,
168                                              uint32_t target_type_idx) {
169     LinkerPatch patch(literal_offset, Type::kPublicTypeBssEntry, target_dex_file);
170     patch.type_idx_ = target_type_idx;
171     patch.pc_insn_offset_ = pc_insn_offset;
172     return patch;
173   }
174 
PackageTypeBssEntryPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_type_idx)175   static LinkerPatch PackageTypeBssEntryPatch(size_t literal_offset,
176                                               const DexFile* target_dex_file,
177                                               uint32_t pc_insn_offset,
178                                               uint32_t target_type_idx) {
179     LinkerPatch patch(literal_offset, Type::kPackageTypeBssEntry, target_dex_file);
180     patch.type_idx_ = target_type_idx;
181     patch.pc_insn_offset_ = pc_insn_offset;
182     return patch;
183   }
184 
RelativeStringPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_string_idx)185   static LinkerPatch RelativeStringPatch(size_t literal_offset,
186                                          const DexFile* target_dex_file,
187                                          uint32_t pc_insn_offset,
188                                          uint32_t target_string_idx) {
189     LinkerPatch patch(literal_offset, Type::kStringRelative, target_dex_file);
190     patch.string_idx_ = target_string_idx;
191     patch.pc_insn_offset_ = pc_insn_offset;
192     return patch;
193   }
194 
StringBssEntryPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_string_idx)195   static LinkerPatch StringBssEntryPatch(size_t literal_offset,
196                                          const DexFile* target_dex_file,
197                                          uint32_t pc_insn_offset,
198                                          uint32_t target_string_idx) {
199     LinkerPatch patch(literal_offset, Type::kStringBssEntry, target_dex_file);
200     patch.string_idx_ = target_string_idx;
201     patch.pc_insn_offset_ = pc_insn_offset;
202     return patch;
203   }
204 
MethodTypeBssEntryPatch(size_t literal_offset,const DexFile * target_dex_file,uint32_t pc_insn_offset,uint32_t target_proto_idx)205   static LinkerPatch MethodTypeBssEntryPatch(size_t literal_offset,
206                                              const DexFile* target_dex_file,
207                                              uint32_t pc_insn_offset,
208                                              uint32_t target_proto_idx) {
209     LinkerPatch patch(literal_offset, Type::kMethodTypeBssEntry, target_dex_file);
210     patch.proto_idx_ = target_proto_idx;
211     patch.pc_insn_offset_ = pc_insn_offset;
212     return patch;
213   }
214 
CallEntrypointPatch(size_t literal_offset,uint32_t entrypoint_offset)215   static LinkerPatch CallEntrypointPatch(size_t literal_offset,
216                                          uint32_t entrypoint_offset) {
217     LinkerPatch patch(literal_offset,
218                       Type::kCallEntrypoint,
219                       /* target_dex_file= */ nullptr);
220     patch.entrypoint_offset_ = entrypoint_offset;
221     return patch;
222   }
223 
224   static LinkerPatch BakerReadBarrierBranchPatch(size_t literal_offset,
225                                                  uint32_t custom_value1 = 0u,
226                                                  uint32_t custom_value2 = 0u) {
227     LinkerPatch patch(literal_offset,
228                       Type::kBakerReadBarrierBranch,
229                       /* target_dex_file= */ nullptr);
230     patch.baker_custom_value1_ = custom_value1;
231     patch.baker_custom_value2_ = custom_value2;
232     return patch;
233   }
234 
235   LinkerPatch(const LinkerPatch& other) = default;
236   LinkerPatch& operator=(const LinkerPatch& other) = default;
237 
LiteralOffset()238   size_t LiteralOffset() const {
239     return literal_offset_;
240   }
241 
GetType()242   Type GetType() const {
243     return patch_type_;
244   }
245 
IntrinsicData()246   uint32_t IntrinsicData() const {
247     DCHECK(patch_type_ == Type::kIntrinsicReference);
248     return intrinsic_data_;
249   }
250 
BootImageOffset()251   uint32_t BootImageOffset() const {
252     DCHECK(patch_type_ == Type::kBootImageRelRo);
253     return boot_image_offset_;
254   }
255 
TargetMethod()256   MethodReference TargetMethod() const {
257     DCHECK(patch_type_ == Type::kMethodRelative ||
258            patch_type_ == Type::kMethodAppImageRelRo ||
259            patch_type_ == Type::kMethodBssEntry ||
260            patch_type_ == Type::kJniEntrypointRelative ||
261            patch_type_ == Type::kCallRelative);
262     return MethodReference(target_dex_file_, method_idx_);
263   }
264 
TargetType()265   TypeReference TargetType() const {
266     DCHECK(patch_type_ == Type::kTypeRelative ||
267            patch_type_ == Type::kTypeAppImageRelRo ||
268            patch_type_ == Type::kTypeBssEntry ||
269            patch_type_ == Type::kPublicTypeBssEntry ||
270            patch_type_ == Type::kPackageTypeBssEntry);
271     return TypeReference(target_dex_file_, dex::TypeIndex(type_idx_));
272   }
273 
TargetString()274   StringReference TargetString() const {
275     DCHECK(patch_type_ == Type::kStringRelative ||
276            patch_type_ == Type::kStringBssEntry);
277     return StringReference(target_dex_file_, dex::StringIndex(string_idx_));
278   }
279 
TargetProto()280   ProtoReference TargetProto() const {
281     DCHECK(patch_type_ == Type::kMethodTypeBssEntry);
282     return ProtoReference(target_dex_file_, dex::ProtoIndex(proto_idx_));
283   }
284 
PcInsnOffset()285   uint32_t PcInsnOffset() const {
286     DCHECK(patch_type_ == Type::kIntrinsicReference ||
287            patch_type_ == Type::kBootImageRelRo ||
288            patch_type_ == Type::kMethodRelative ||
289            patch_type_ == Type::kMethodAppImageRelRo ||
290            patch_type_ == Type::kMethodBssEntry ||
291            patch_type_ == Type::kJniEntrypointRelative ||
292            patch_type_ == Type::kTypeRelative ||
293            patch_type_ == Type::kTypeAppImageRelRo ||
294            patch_type_ == Type::kTypeBssEntry ||
295            patch_type_ == Type::kPublicTypeBssEntry ||
296            patch_type_ == Type::kPackageTypeBssEntry ||
297            patch_type_ == Type::kStringRelative ||
298            patch_type_ == Type::kStringBssEntry ||
299            patch_type_ == Type::kMethodTypeBssEntry);
300     return pc_insn_offset_;
301   }
302 
EntrypointOffset()303   uint32_t EntrypointOffset() const {
304     DCHECK(patch_type_ == Type::kCallEntrypoint);
305     return entrypoint_offset_;
306   }
307 
GetBakerCustomValue1()308   uint32_t GetBakerCustomValue1() const {
309     DCHECK(patch_type_ == Type::kBakerReadBarrierBranch);
310     return baker_custom_value1_;
311   }
312 
GetBakerCustomValue2()313   uint32_t GetBakerCustomValue2() const {
314     DCHECK(patch_type_ == Type::kBakerReadBarrierBranch);
315     return baker_custom_value2_;
316   }
317 
318  private:
LinkerPatch(size_t literal_offset,Type patch_type,const DexFile * target_dex_file)319   LinkerPatch(size_t literal_offset, Type patch_type, const DexFile* target_dex_file)
320       : target_dex_file_(target_dex_file),
321         literal_offset_(literal_offset),
322         patch_type_(patch_type) {
323     cmp1_ = 0u;
324     cmp2_ = 0u;
325     // The compiler rejects methods that are too big, so the compiled code
326     // of a single method really shouln't be anywhere close to 16MiB.
327     DCHECK(IsUint<24>(literal_offset));
328   }
329 
330   const DexFile* target_dex_file_;
331   // TODO: Clean up naming. Some patched locations are literals but others are not.
332   uint32_t literal_offset_ : 24;  // Method code size up to 16MiB.
333   Type patch_type_ : 8;
334   union {
335     uint32_t cmp1_;               // Used for relational operators.
336     uint32_t boot_image_offset_;  // Data to write to the boot image .data.img.rel.ro entry.
337     uint32_t method_idx_;         // Method index for Call/Method patches.
338     uint32_t type_idx_;           // Type index for Type patches.
339     uint32_t string_idx_;         // String index for String patches.
340     uint32_t proto_idx_;          // Proto index for MethodType patches.
341     uint32_t intrinsic_data_;     // Data for IntrinsicObjects.
342     uint32_t entrypoint_offset_;  // Entrypoint offset in the Thread object.
343     uint32_t baker_custom_value1_;
344     static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators");
345     static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators");
346     static_assert(sizeof(string_idx_) == sizeof(cmp1_), "needed by relational operators");
347     static_assert(sizeof(proto_idx_) == sizeof(cmp1_), "needed by relational operators");
348     static_assert(sizeof(intrinsic_data_) == sizeof(cmp1_), "needed by relational operators");
349     static_assert(sizeof(baker_custom_value1_) == sizeof(cmp1_), "needed by relational operators");
350   };
351   union {
352     // Note: To avoid uninitialized padding on 64-bit systems, we use `size_t` for `cmp2_`.
353     // This allows a hashing function to treat an array of linker patches as raw memory.
354     size_t cmp2_;             // Used for relational operators.
355     // Literal offset of the insn loading PC (same as literal_offset if it's the same insn,
356     // may be different if the PC-relative addressing needs multiple insns).
357     uint32_t pc_insn_offset_;
358     uint32_t baker_custom_value2_;
359     static_assert(sizeof(pc_insn_offset_) <= sizeof(cmp2_), "needed by relational operators");
360     static_assert(sizeof(baker_custom_value2_) <= sizeof(cmp2_), "needed by relational operators");
361   };
362 
363   friend bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs);
364   friend bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs);
365 };
366 EXPORT std::ostream& operator<<(std::ostream& os, LinkerPatch::Type type);
367 
368 inline bool operator==(const LinkerPatch& lhs, const LinkerPatch& rhs) {
369   return lhs.literal_offset_ == rhs.literal_offset_ &&
370       lhs.patch_type_ == rhs.patch_type_ &&
371       lhs.target_dex_file_ == rhs.target_dex_file_ &&
372       lhs.cmp1_ == rhs.cmp1_ &&
373       lhs.cmp2_ == rhs.cmp2_;
374 }
375 
376 inline bool operator<(const LinkerPatch& lhs, const LinkerPatch& rhs) {
377   return (lhs.literal_offset_ != rhs.literal_offset_) ? lhs.literal_offset_ < rhs.literal_offset_
378       : (lhs.patch_type_ != rhs.patch_type_) ? lhs.patch_type_ < rhs.patch_type_
379       : (lhs.target_dex_file_ != rhs.target_dex_file_) ? lhs.target_dex_file_ < rhs.target_dex_file_
380       : (lhs.cmp1_ != rhs.cmp1_) ? lhs.cmp1_ < rhs.cmp1_
381       : lhs.cmp2_ < rhs.cmp2_;
382 }
383 
384 }  // namespace linker
385 }  // namespace art
386 
387 #endif  // ART_COMPILER_LINKER_LINKER_PATCH_H_
388