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