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