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