1 // Copyright 2018 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_RELOC_INFO_H_
6 #define V8_RELOC_INFO_H_
7 
8 #include "src/globals.h"
9 #include "src/objects.h"
10 
11 namespace v8 {
12 namespace internal {
13 
14 class CodeReference;
15 class EmbeddedData;
16 
17 // Specifies whether to perform icache flush operations on RelocInfo updates.
18 // If FLUSH_ICACHE_IF_NEEDED, the icache will always be flushed if an
19 // instruction was modified. If SKIP_ICACHE_FLUSH the flush will always be
20 // skipped (only use this if you will flush the icache manually before it is
21 // executed).
22 enum ICacheFlushMode { FLUSH_ICACHE_IF_NEEDED, SKIP_ICACHE_FLUSH };
23 
24 // -----------------------------------------------------------------------------
25 // Relocation information
26 
27 // Relocation information consists of the address (pc) of the datum
28 // to which the relocation information applies, the relocation mode
29 // (rmode), and an optional data field. The relocation mode may be
30 // "descriptive" and not indicate a need for relocation, but simply
31 // describe a property of the datum. Such rmodes are useful for GC
32 // and nice disassembly output.
33 
34 class RelocInfo {
35  public:
36   // This string is used to add padding comments to the reloc info in cases
37   // where we are not sure to have enough space for patching in during
38   // lazy deoptimization. This is the case if we have indirect calls for which
39   // we do not normally record relocation info.
40   static const char* const kFillerCommentString;
41 
42   // The minimum size of a comment is equal to two bytes for the extra tagged
43   // pc and kPointerSize for the actual pointer to the comment.
44   static const int kMinRelocCommentSize = 2 + kPointerSize;
45 
46   // The maximum size for a call instruction including pc-jump.
47   static const int kMaxCallSize = 6;
48 
49   // The maximum pc delta that will use the short encoding.
50   static const int kMaxSmallPCDelta;
51 
52   enum Mode : int8_t {
53     // Please note the order is important (see IsRealRelocMode, IsGCRelocMode,
54     // and IsShareableRelocMode predicates below).
55 
56     CODE_TARGET,
57     RELATIVE_CODE_TARGET,  // LAST_CODE_TARGET_MODE
58     EMBEDDED_OBJECT,       // LAST_GCED_ENUM
59 
60     JS_TO_WASM_CALL,
61     WASM_CALL,  // FIRST_SHAREABLE_RELOC_MODE
62     WASM_STUB_CALL,
63 
64     RUNTIME_ENTRY,
65     COMMENT,
66 
67     EXTERNAL_REFERENCE,  // The address of an external C++ function.
68     INTERNAL_REFERENCE,  // An address inside the same function.
69 
70     // Encoded internal reference, used only on MIPS, MIPS64 and PPC.
71     INTERNAL_REFERENCE_ENCODED,
72 
73     // An off-heap instruction stream target. See http://goo.gl/Z2HUiM.
74     OFF_HEAP_TARGET,
75 
76     // Marks constant and veneer pools. Only used on ARM and ARM64.
77     // They use a custom noncompact encoding.
78     CONST_POOL,
79     VENEER_POOL,
80 
81     DEOPT_SCRIPT_OFFSET,
82     DEOPT_INLINING_ID,  // Deoptimization source position.
83     DEOPT_REASON,       // Deoptimization reason index.
84     DEOPT_ID,           // Deoptimization inlining id.
85 
86     // This is not an actual reloc mode, but used to encode a long pc jump that
87     // cannot be encoded as part of another record.
88     PC_JUMP,
89 
90     // Pseudo-types
91     NUMBER_OF_MODES,
92     NONE,  // never recorded value
93 
94     LAST_CODE_TARGET_MODE = RELATIVE_CODE_TARGET,
95     FIRST_REAL_RELOC_MODE = CODE_TARGET,
96     LAST_REAL_RELOC_MODE = VENEER_POOL,
97     LAST_GCED_ENUM = EMBEDDED_OBJECT,
98     FIRST_SHAREABLE_RELOC_MODE = WASM_CALL,
99   };
100 
101   STATIC_ASSERT(NUMBER_OF_MODES <= kBitsPerInt);
102 
103   RelocInfo() = default;
104 
105   RelocInfo(Address pc, Mode rmode, intptr_t data, Code* host,
106             Address constant_pool = kNullAddress)
pc_(pc)107       : pc_(pc),
108         rmode_(rmode),
109         data_(data),
110         host_(host),
111         constant_pool_(constant_pool) {}
112 
IsRealRelocMode(Mode mode)113   static constexpr bool IsRealRelocMode(Mode mode) {
114     return mode >= FIRST_REAL_RELOC_MODE && mode <= LAST_REAL_RELOC_MODE;
115   }
116   // Is the relocation mode affected by GC?
IsGCRelocMode(Mode mode)117   static constexpr bool IsGCRelocMode(Mode mode) {
118     return mode <= LAST_GCED_ENUM;
119   }
IsShareableRelocMode(Mode mode)120   static constexpr bool IsShareableRelocMode(Mode mode) {
121     static_assert(RelocInfo::NONE >= RelocInfo::FIRST_SHAREABLE_RELOC_MODE,
122                   "Users of this function rely on NONE being a sharable "
123                   "relocation mode.");
124     return mode >= RelocInfo::FIRST_SHAREABLE_RELOC_MODE;
125   }
IsCodeTarget(Mode mode)126   static constexpr bool IsCodeTarget(Mode mode) { return mode == CODE_TARGET; }
IsCodeTargetMode(Mode mode)127   static constexpr bool IsCodeTargetMode(Mode mode) {
128     return mode <= LAST_CODE_TARGET_MODE;
129   }
IsRelativeCodeTarget(Mode mode)130   static constexpr bool IsRelativeCodeTarget(Mode mode) {
131     return mode == RELATIVE_CODE_TARGET;
132   }
IsEmbeddedObject(Mode mode)133   static constexpr bool IsEmbeddedObject(Mode mode) {
134     return mode == EMBEDDED_OBJECT;
135   }
IsRuntimeEntry(Mode mode)136   static constexpr bool IsRuntimeEntry(Mode mode) {
137     return mode == RUNTIME_ENTRY;
138   }
IsWasmCall(Mode mode)139   static constexpr bool IsWasmCall(Mode mode) { return mode == WASM_CALL; }
IsWasmStubCall(Mode mode)140   static constexpr bool IsWasmStubCall(Mode mode) {
141     return mode == WASM_STUB_CALL;
142   }
IsComment(Mode mode)143   static constexpr bool IsComment(Mode mode) { return mode == COMMENT; }
IsConstPool(Mode mode)144   static constexpr bool IsConstPool(Mode mode) { return mode == CONST_POOL; }
IsVeneerPool(Mode mode)145   static constexpr bool IsVeneerPool(Mode mode) { return mode == VENEER_POOL; }
IsDeoptPosition(Mode mode)146   static constexpr bool IsDeoptPosition(Mode mode) {
147     return mode == DEOPT_SCRIPT_OFFSET || mode == DEOPT_INLINING_ID;
148   }
IsDeoptReason(Mode mode)149   static constexpr bool IsDeoptReason(Mode mode) {
150     return mode == DEOPT_REASON;
151   }
IsDeoptId(Mode mode)152   static constexpr bool IsDeoptId(Mode mode) { return mode == DEOPT_ID; }
IsExternalReference(Mode mode)153   static constexpr bool IsExternalReference(Mode mode) {
154     return mode == EXTERNAL_REFERENCE;
155   }
IsInternalReference(Mode mode)156   static constexpr bool IsInternalReference(Mode mode) {
157     return mode == INTERNAL_REFERENCE;
158   }
IsInternalReferenceEncoded(Mode mode)159   static constexpr bool IsInternalReferenceEncoded(Mode mode) {
160     return mode == INTERNAL_REFERENCE_ENCODED;
161   }
IsOffHeapTarget(Mode mode)162   static constexpr bool IsOffHeapTarget(Mode mode) {
163     return mode == OFF_HEAP_TARGET;
164   }
IsNone(Mode mode)165   static constexpr bool IsNone(Mode mode) { return mode == NONE; }
IsWasmReference(Mode mode)166   static constexpr bool IsWasmReference(Mode mode) {
167     return IsWasmPtrReference(mode);
168   }
IsJsToWasmCall(Mode mode)169   static constexpr bool IsJsToWasmCall(Mode mode) {
170     return mode == JS_TO_WASM_CALL;
171   }
IsWasmPtrReference(Mode mode)172   static constexpr bool IsWasmPtrReference(Mode mode) {
173     return mode == WASM_CALL || mode == JS_TO_WASM_CALL;
174   }
175 
IsOnlyForSerializer(Mode mode)176   static bool IsOnlyForSerializer(Mode mode) {
177 #ifdef V8_TARGET_ARCH_IA32
178     // On ia32, inlined off-heap trampolines must be relocated.
179     DCHECK_NE((kApplyMask & ModeMask(OFF_HEAP_TARGET)), 0);
180     DCHECK_EQ((kApplyMask & ModeMask(EXTERNAL_REFERENCE)), 0);
181     return mode == EXTERNAL_REFERENCE;
182 #else
183     DCHECK_EQ((kApplyMask & ModeMask(OFF_HEAP_TARGET)), 0);
184     DCHECK_EQ((kApplyMask & ModeMask(EXTERNAL_REFERENCE)), 0);
185     return mode == EXTERNAL_REFERENCE || mode == OFF_HEAP_TARGET;
186 #endif
187   }
188 
ModeMask(Mode mode)189   static constexpr int ModeMask(Mode mode) { return 1 << mode; }
190 
191   // Accessors
pc()192   Address pc() const { return pc_; }
rmode()193   Mode rmode() const { return rmode_; }
data()194   intptr_t data() const { return data_; }
host()195   Code* host() const { return host_; }
constant_pool()196   Address constant_pool() const { return constant_pool_; }
197 
198   // Apply a relocation by delta bytes. When the code object is moved, PC
199   // relative addresses have to be updated as well as absolute addresses
200   // inside the code (internal references).
201   // Do not forget to flush the icache afterwards!
202   V8_INLINE void apply(intptr_t delta);
203 
204   // Is the pointer this relocation info refers to coded like a plain pointer
205   // or is it strange in some way (e.g. relative or patched into a series of
206   // instructions).
207   bool IsCodedSpecially();
208 
209   // The static pendant to IsCodedSpecially, just for off-heap targets. Used
210   // during deserialization, when we don't actually have a RelocInfo handy.
211   static bool OffHeapTargetIsCodedSpecially();
212 
213   // If true, the pointer this relocation info refers to is an entry in the
214   // constant pool, otherwise the pointer is embedded in the instruction stream.
215   bool IsInConstantPool();
216 
217   // Returns the deoptimization id for the entry associated with the reloc info
218   // where {kind} is the deoptimization kind.
219   // This is only used for printing RUNTIME_ENTRY relocation info.
220   int GetDeoptimizationId(Isolate* isolate, DeoptimizeKind kind);
221 
222   Address wasm_call_address() const;
223   Address wasm_stub_call_address() const;
224   Address js_to_wasm_address() const;
225 
226   uint32_t wasm_call_tag() const;
227 
228   void set_wasm_call_address(
229       Address, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
230   void set_wasm_stub_call_address(
231       Address, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
232   void set_js_to_wasm_address(
233       Address, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
234 
235   void set_target_address(
236       Address target,
237       WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
238       ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
239 
240   // this relocation applies to;
241   // can only be called if IsCodeTarget(rmode_) || IsRuntimeEntry(rmode_)
242   V8_INLINE Address target_address();
243   V8_INLINE HeapObject* target_object();
244   V8_INLINE Handle<HeapObject> target_object_handle(Assembler* origin);
245   V8_INLINE void set_target_object(
246       Heap* heap, HeapObject* target,
247       WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
248       ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
249   V8_INLINE Address target_runtime_entry(Assembler* origin);
250   V8_INLINE void set_target_runtime_entry(
251       Address target,
252       WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
253       ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
254   V8_INLINE Address target_off_heap_target();
255   V8_INLINE Cell* target_cell();
256   V8_INLINE Handle<Cell> target_cell_handle();
257   V8_INLINE void set_target_cell(
258       Cell* cell, WriteBarrierMode write_barrier_mode = UPDATE_WRITE_BARRIER,
259       ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
260   V8_INLINE void set_target_external_reference(
261       Address, ICacheFlushMode icache_flush_mode = FLUSH_ICACHE_IF_NEEDED);
262 
263   // Returns the address of the constant pool entry where the target address
264   // is held.  This should only be called if IsInConstantPool returns true.
265   V8_INLINE Address constant_pool_entry_address();
266 
267   // Read the address of the word containing the target_address in an
268   // instruction stream.  What this means exactly is architecture-independent.
269   // The only architecture-independent user of this function is the serializer.
270   // The serializer uses it to find out how many raw bytes of instruction to
271   // output before the next target.  Architecture-independent code shouldn't
272   // dereference the pointer it gets back from this.
273   V8_INLINE Address target_address_address();
274 
275   // This indicates how much space a target takes up when deserializing a code
276   // stream.  For most architectures this is just the size of a pointer.  For
277   // an instruction like movw/movt where the target bits are mixed into the
278   // instruction bits the size of the target will be zero, indicating that the
279   // serializer should not step forwards in memory after a target is resolved
280   // and written.  In this case the target_address_address function above
281   // should return the end of the instructions to be patched, allowing the
282   // deserializer to deserialize the instructions as raw bytes and put them in
283   // place, ready to be patched with the target.
284   V8_INLINE int target_address_size();
285 
286   // Read the reference in the instruction this relocation
287   // applies to; can only be called if rmode_ is EXTERNAL_REFERENCE.
288   V8_INLINE Address target_external_reference();
289 
290   // Read the reference in the instruction this relocation
291   // applies to; can only be called if rmode_ is INTERNAL_REFERENCE.
292   V8_INLINE Address target_internal_reference();
293 
294   // Return the reference address this relocation applies to;
295   // can only be called if rmode_ is INTERNAL_REFERENCE.
296   V8_INLINE Address target_internal_reference_address();
297 
298   // Wipe out a relocation to a fixed value, used for making snapshots
299   // reproducible.
300   V8_INLINE void WipeOut();
301 
302   template <typename ObjectVisitor>
303   inline void Visit(ObjectVisitor* v);
304 
305   // Check whether the given code contains relocation information that
306   // either is position-relative or movable by the garbage collector.
307   static bool RequiresRelocationAfterCodegen(const CodeDesc& desc);
308   static bool RequiresRelocation(Code* code);
309 
310 #ifdef ENABLE_DISASSEMBLER
311   // Printing
312   static const char* RelocModeName(Mode rmode);
313   void Print(Isolate* isolate, std::ostream& os);  // NOLINT
314 #endif                                             // ENABLE_DISASSEMBLER
315 #ifdef VERIFY_HEAP
316   void Verify(Isolate* isolate);
317 #endif
318 
319   static const int kApplyMask;  // Modes affected by apply.  Depends on arch.
320 
321   // In addition to modes covered by the apply mask (which is applied at GC
322   // time, among others), this covers all modes that are relocated by
323   // Code::CopyFromNoFlush after code generation.
PostCodegenRelocationMask()324   static int PostCodegenRelocationMask() {
325     return ModeMask(RelocInfo::CODE_TARGET) |
326            ModeMask(RelocInfo::EMBEDDED_OBJECT) |
327            ModeMask(RelocInfo::RUNTIME_ENTRY) |
328            ModeMask(RelocInfo::RELATIVE_CODE_TARGET) | kApplyMask;
329   }
330 
331  private:
332   // On ARM/ARM64, note that pc_ is the address of the instruction referencing
333   // the constant pool and not the address of the constant pool entry.
334   Address pc_;
335   Mode rmode_;
336   intptr_t data_ = 0;
337   Code* host_;
338   Address constant_pool_ = kNullAddress;
339   friend class RelocIterator;
340 };
341 
342 // RelocInfoWriter serializes a stream of relocation info. It writes towards
343 // lower addresses.
344 class RelocInfoWriter BASE_EMBEDDED {
345  public:
RelocInfoWriter()346   RelocInfoWriter() : pos_(nullptr), last_pc_(nullptr) {}
347 
pos()348   byte* pos() const { return pos_; }
last_pc()349   byte* last_pc() const { return last_pc_; }
350 
351   void Write(const RelocInfo* rinfo);
352 
353   // Update the state of the stream after reloc info buffer
354   // and/or code is moved while the stream is active.
Reposition(byte * pos,byte * pc)355   void Reposition(byte* pos, byte* pc) {
356     pos_ = pos;
357     last_pc_ = pc;
358   }
359 
360   // Max size (bytes) of a written RelocInfo. Longest encoding is
361   // ExtraTag, VariableLengthPCJump, ExtraTag, pc_delta, data_delta.
362   static constexpr int kMaxSize = 1 + 4 + 1 + 1 + kPointerSize;
363 
364  private:
365   inline uint32_t WriteLongPCJump(uint32_t pc_delta);
366 
367   inline void WriteShortTaggedPC(uint32_t pc_delta, int tag);
368   inline void WriteShortData(intptr_t data_delta);
369 
370   inline void WriteMode(RelocInfo::Mode rmode);
371   inline void WriteModeAndPC(uint32_t pc_delta, RelocInfo::Mode rmode);
372   inline void WriteIntData(int data_delta);
373   inline void WriteData(intptr_t data_delta);
374 
375   byte* pos_;
376   byte* last_pc_;
377 
378   DISALLOW_COPY_AND_ASSIGN(RelocInfoWriter);
379 };
380 
381 // A RelocIterator iterates over relocation information.
382 // Typical use:
383 //
384 //   for (RelocIterator it(code); !it.done(); it.next()) {
385 //     // do something with it.rinfo() here
386 //   }
387 //
388 // A mask can be specified to skip unwanted modes.
389 class RelocIterator : public Malloced {
390  public:
391   // Create a new iterator positioned at
392   // the beginning of the reloc info.
393   // Relocation information with mode k is included in the
394   // iteration iff bit k of mode_mask is set.
395   explicit RelocIterator(Code* code, int mode_mask = -1);
396   explicit RelocIterator(EmbeddedData* embedded_data, Code* code,
397                          int mode_mask);
398   explicit RelocIterator(const CodeDesc& desc, int mode_mask = -1);
399   explicit RelocIterator(const CodeReference code_reference,
400                          int mode_mask = -1);
401   explicit RelocIterator(Vector<byte> instructions,
402                          Vector<const byte> reloc_info, Address const_pool,
403                          int mode_mask = -1);
404   RelocIterator(RelocIterator&&) = default;
405   RelocIterator& operator=(RelocIterator&&) = default;
406 
407   // Iteration
done()408   bool done() const { return done_; }
409   void next();
410 
411   // Return pointer valid until next next().
rinfo()412   RelocInfo* rinfo() {
413     DCHECK(!done());
414     return &rinfo_;
415   }
416 
417  private:
418   RelocIterator(Code* host, Address pc, Address constant_pool, const byte* pos,
419                 const byte* end, int mode_mask);
420 
421   // Advance* moves the position before/after reading.
422   // *Read* reads from current byte(s) into rinfo_.
423   // *Get* just reads and returns info on current byte.
424   void Advance(int bytes = 1) { pos_ -= bytes; }
425   int AdvanceGetTag();
426   RelocInfo::Mode GetMode();
427 
428   void AdvanceReadLongPCJump();
429 
430   void ReadShortTaggedPC();
431   void ReadShortData();
432 
433   void AdvanceReadPC();
434   void AdvanceReadInt();
435   void AdvanceReadData();
436 
437   // If the given mode is wanted, set it in rinfo_ and return true.
438   // Else return false. Used for efficiently skipping unwanted modes.
SetMode(RelocInfo::Mode mode)439   bool SetMode(RelocInfo::Mode mode) {
440     return (mode_mask_ & (1 << mode)) ? (rinfo_.rmode_ = mode, true) : false;
441   }
442 
443   const byte* pos_;
444   const byte* end_;
445   RelocInfo rinfo_;
446   bool done_ = false;
447   const int mode_mask_;
448 
449   DISALLOW_COPY_AND_ASSIGN(RelocIterator);
450 };
451 
452 }  // namespace internal
453 }  // namespace v8
454 
455 #endif  // V8_RELOC_INFO_H_
456