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