Home | History | Annotate | Download | only in src
      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)
    107       : pc_(pc),
    108         rmode_(rmode),
    109         data_(data),
    110         host_(host),
    111         constant_pool_(constant_pool) {}
    112 
    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?
    117   static constexpr bool IsGCRelocMode(Mode mode) {
    118     return mode <= LAST_GCED_ENUM;
    119   }
    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   }
    126   static constexpr bool IsCodeTarget(Mode mode) { return mode == CODE_TARGET; }
    127   static constexpr bool IsCodeTargetMode(Mode mode) {
    128     return mode <= LAST_CODE_TARGET_MODE;
    129   }
    130   static constexpr bool IsRelativeCodeTarget(Mode mode) {
    131     return mode == RELATIVE_CODE_TARGET;
    132   }
    133   static constexpr bool IsEmbeddedObject(Mode mode) {
    134     return mode == EMBEDDED_OBJECT;
    135   }
    136   static constexpr bool IsRuntimeEntry(Mode mode) {
    137     return mode == RUNTIME_ENTRY;
    138   }
    139   static constexpr bool IsWasmCall(Mode mode) { return mode == WASM_CALL; }
    140   static constexpr bool IsWasmStubCall(Mode mode) {
    141     return mode == WASM_STUB_CALL;
    142   }
    143   static constexpr bool IsComment(Mode mode) { return mode == COMMENT; }
    144   static constexpr bool IsConstPool(Mode mode) { return mode == CONST_POOL; }
    145   static constexpr bool IsVeneerPool(Mode mode) { return mode == VENEER_POOL; }
    146   static constexpr bool IsDeoptPosition(Mode mode) {
    147     return mode == DEOPT_SCRIPT_OFFSET || mode == DEOPT_INLINING_ID;
    148   }
    149   static constexpr bool IsDeoptReason(Mode mode) {
    150     return mode == DEOPT_REASON;
    151   }
    152   static constexpr bool IsDeoptId(Mode mode) { return mode == DEOPT_ID; }
    153   static constexpr bool IsExternalReference(Mode mode) {
    154     return mode == EXTERNAL_REFERENCE;
    155   }
    156   static constexpr bool IsInternalReference(Mode mode) {
    157     return mode == INTERNAL_REFERENCE;
    158   }
    159   static constexpr bool IsInternalReferenceEncoded(Mode mode) {
    160     return mode == INTERNAL_REFERENCE_ENCODED;
    161   }
    162   static constexpr bool IsOffHeapTarget(Mode mode) {
    163     return mode == OFF_HEAP_TARGET;
    164   }
    165   static constexpr bool IsNone(Mode mode) { return mode == NONE; }
    166   static constexpr bool IsWasmReference(Mode mode) {
    167     return IsWasmPtrReference(mode);
    168   }
    169   static constexpr bool IsJsToWasmCall(Mode mode) {
    170     return mode == JS_TO_WASM_CALL;
    171   }
    172   static constexpr bool IsWasmPtrReference(Mode mode) {
    173     return mode == WASM_CALL || mode == JS_TO_WASM_CALL;
    174   }
    175 
    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 
    189   static constexpr int ModeMask(Mode mode) { return 1 << mode; }
    190 
    191   // Accessors
    192   Address pc() const { return pc_; }
    193   Mode rmode() const { return rmode_; }
    194   intptr_t data() const { return data_; }
    195   Code* host() const { return host_; }
    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.
    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:
    346   RelocInfoWriter() : pos_(nullptr), last_pc_(nullptr) {}
    347 
    348   byte* pos() const { return pos_; }
    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.
    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
    408   bool done() const { return done_; }
    409   void next();
    410 
    411   // Return pointer valid until next next().
    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.
    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