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