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 #include "src/reloc-info.h" 6 7 #include "src/assembler-arch-inl.h" 8 #include "src/code-stubs.h" 9 #include "src/deoptimize-reason.h" 10 #include "src/deoptimizer.h" 11 #include "src/heap/heap-write-barrier-inl.h" 12 #include "src/objects/code-inl.h" 13 #include "src/snapshot/snapshot.h" 14 15 namespace v8 { 16 namespace internal { 17 18 const char* const RelocInfo::kFillerCommentString = "DEOPTIMIZATION PADDING"; 19 20 // ----------------------------------------------------------------------------- 21 // Implementation of RelocInfoWriter and RelocIterator 22 // 23 // Relocation information is written backwards in memory, from high addresses 24 // towards low addresses, byte by byte. Therefore, in the encodings listed 25 // below, the first byte listed it at the highest address, and successive 26 // bytes in the record are at progressively lower addresses. 27 // 28 // Encoding 29 // 30 // The most common modes are given single-byte encodings. Also, it is 31 // easy to identify the type of reloc info and skip unwanted modes in 32 // an iteration. 33 // 34 // The encoding relies on the fact that there are fewer than 14 35 // different relocation modes using standard non-compact encoding. 36 // 37 // The first byte of a relocation record has a tag in its low 2 bits: 38 // Here are the record schemes, depending on the low tag and optional higher 39 // tags. 40 // 41 // Low tag: 42 // 00: embedded_object: [6-bit pc delta] 00 43 // 44 // 01: code_target: [6-bit pc delta] 01 45 // 46 // 10: wasm_stub_call: [6-bit pc delta] 10 47 // 48 // 11: long_record [6 bit reloc mode] 11 49 // followed by pc delta 50 // followed by optional data depending on type. 51 // 52 // If a pc delta exceeds 6 bits, it is split into a remainder that fits into 53 // 6 bits and a part that does not. The latter is encoded as a long record 54 // with PC_JUMP as pseudo reloc info mode. The former is encoded as part of 55 // the following record in the usual way. The long pc jump record has variable 56 // length: 57 // pc-jump: [PC_JUMP] 11 58 // [7 bits data] 0 59 // ... 60 // [7 bits data] 1 61 // (Bits 6..31 of pc delta, with leading zeroes 62 // dropped, and last non-zero chunk tagged with 1.) 63 64 const int kTagBits = 2; 65 const int kTagMask = (1 << kTagBits) - 1; 66 const int kLongTagBits = 6; 67 68 const int kEmbeddedObjectTag = 0; 69 const int kCodeTargetTag = 1; 70 const int kWasmStubCallTag = 2; 71 const int kDefaultTag = 3; 72 73 const int kSmallPCDeltaBits = kBitsPerByte - kTagBits; 74 const int kSmallPCDeltaMask = (1 << kSmallPCDeltaBits) - 1; 75 const int RelocInfo::kMaxSmallPCDelta = kSmallPCDeltaMask; 76 77 const int kChunkBits = 7; 78 const int kChunkMask = (1 << kChunkBits) - 1; 79 const int kLastChunkTagBits = 1; 80 const int kLastChunkTagMask = 1; 81 const int kLastChunkTag = 1; 82 83 uint32_t RelocInfoWriter::WriteLongPCJump(uint32_t pc_delta) { 84 // Return if the pc_delta can fit in kSmallPCDeltaBits bits. 85 // Otherwise write a variable length PC jump for the bits that do 86 // not fit in the kSmallPCDeltaBits bits. 87 if (is_uintn(pc_delta, kSmallPCDeltaBits)) return pc_delta; 88 WriteMode(RelocInfo::PC_JUMP); 89 uint32_t pc_jump = pc_delta >> kSmallPCDeltaBits; 90 DCHECK_GT(pc_jump, 0); 91 // Write kChunkBits size chunks of the pc_jump. 92 for (; pc_jump > 0; pc_jump = pc_jump >> kChunkBits) { 93 byte b = pc_jump & kChunkMask; 94 *--pos_ = b << kLastChunkTagBits; 95 } 96 // Tag the last chunk so it can be identified. 97 *pos_ = *pos_ | kLastChunkTag; 98 // Return the remaining kSmallPCDeltaBits of the pc_delta. 99 return pc_delta & kSmallPCDeltaMask; 100 } 101 102 void RelocInfoWriter::WriteShortTaggedPC(uint32_t pc_delta, int tag) { 103 // Write a byte of tagged pc-delta, possibly preceded by an explicit pc-jump. 104 pc_delta = WriteLongPCJump(pc_delta); 105 *--pos_ = pc_delta << kTagBits | tag; 106 } 107 108 void RelocInfoWriter::WriteShortData(intptr_t data_delta) { 109 *--pos_ = static_cast<byte>(data_delta); 110 } 111 112 void RelocInfoWriter::WriteMode(RelocInfo::Mode rmode) { 113 STATIC_ASSERT(RelocInfo::NUMBER_OF_MODES <= (1 << kLongTagBits)); 114 *--pos_ = static_cast<int>((rmode << kTagBits) | kDefaultTag); 115 } 116 117 void RelocInfoWriter::WriteModeAndPC(uint32_t pc_delta, RelocInfo::Mode rmode) { 118 // Write two-byte tagged pc-delta, possibly preceded by var. length pc-jump. 119 pc_delta = WriteLongPCJump(pc_delta); 120 WriteMode(rmode); 121 *--pos_ = pc_delta; 122 } 123 124 void RelocInfoWriter::WriteIntData(int number) { 125 for (int i = 0; i < kIntSize; i++) { 126 *--pos_ = static_cast<byte>(number); 127 // Signed right shift is arithmetic shift. Tested in test-utils.cc. 128 number = number >> kBitsPerByte; 129 } 130 } 131 132 void RelocInfoWriter::WriteData(intptr_t data_delta) { 133 for (int i = 0; i < kIntptrSize; i++) { 134 *--pos_ = static_cast<byte>(data_delta); 135 // Signed right shift is arithmetic shift. Tested in test-utils.cc. 136 data_delta = data_delta >> kBitsPerByte; 137 } 138 } 139 140 void RelocInfoWriter::Write(const RelocInfo* rinfo) { 141 RelocInfo::Mode rmode = rinfo->rmode(); 142 #ifdef DEBUG 143 byte* begin_pos = pos_; 144 #endif 145 DCHECK(rinfo->rmode() < RelocInfo::NUMBER_OF_MODES); 146 DCHECK_GE(rinfo->pc() - reinterpret_cast<Address>(last_pc_), 0); 147 // Use unsigned delta-encoding for pc. 148 uint32_t pc_delta = 149 static_cast<uint32_t>(rinfo->pc() - reinterpret_cast<Address>(last_pc_)); 150 151 // The two most common modes are given small tags, and usually fit in a byte. 152 if (rmode == RelocInfo::EMBEDDED_OBJECT) { 153 WriteShortTaggedPC(pc_delta, kEmbeddedObjectTag); 154 } else if (rmode == RelocInfo::CODE_TARGET) { 155 WriteShortTaggedPC(pc_delta, kCodeTargetTag); 156 DCHECK_LE(begin_pos - pos_, RelocInfo::kMaxCallSize); 157 } else if (rmode == RelocInfo::WASM_STUB_CALL) { 158 WriteShortTaggedPC(pc_delta, kWasmStubCallTag); 159 } else { 160 WriteModeAndPC(pc_delta, rmode); 161 if (RelocInfo::IsComment(rmode)) { 162 WriteData(rinfo->data()); 163 } else if (RelocInfo::IsDeoptReason(rmode)) { 164 DCHECK_LT(rinfo->data(), 1 << kBitsPerByte); 165 WriteShortData(rinfo->data()); 166 } else if (RelocInfo::IsConstPool(rmode) || 167 RelocInfo::IsVeneerPool(rmode) || RelocInfo::IsDeoptId(rmode) || 168 RelocInfo::IsDeoptPosition(rmode)) { 169 WriteIntData(static_cast<int>(rinfo->data())); 170 } 171 } 172 last_pc_ = reinterpret_cast<byte*>(rinfo->pc()); 173 #ifdef DEBUG 174 DCHECK_LE(begin_pos - pos_, kMaxSize); 175 #endif 176 } 177 178 inline int RelocIterator::AdvanceGetTag() { return *--pos_ & kTagMask; } 179 180 inline RelocInfo::Mode RelocIterator::GetMode() { 181 return static_cast<RelocInfo::Mode>((*pos_ >> kTagBits) & 182 ((1 << kLongTagBits) - 1)); 183 } 184 185 inline void RelocIterator::ReadShortTaggedPC() { 186 rinfo_.pc_ += *pos_ >> kTagBits; 187 } 188 189 inline void RelocIterator::AdvanceReadPC() { rinfo_.pc_ += *--pos_; } 190 191 void RelocIterator::AdvanceReadInt() { 192 int x = 0; 193 for (int i = 0; i < kIntSize; i++) { 194 x |= static_cast<int>(*--pos_) << i * kBitsPerByte; 195 } 196 rinfo_.data_ = x; 197 } 198 199 void RelocIterator::AdvanceReadData() { 200 intptr_t x = 0; 201 for (int i = 0; i < kIntptrSize; i++) { 202 x |= static_cast<intptr_t>(*--pos_) << i * kBitsPerByte; 203 } 204 rinfo_.data_ = x; 205 } 206 207 void RelocIterator::AdvanceReadLongPCJump() { 208 // Read the 32-kSmallPCDeltaBits most significant bits of the 209 // pc jump in kChunkBits bit chunks and shift them into place. 210 // Stop when the last chunk is encountered. 211 uint32_t pc_jump = 0; 212 for (int i = 0; i < kIntSize; i++) { 213 byte pc_jump_part = *--pos_; 214 pc_jump |= (pc_jump_part >> kLastChunkTagBits) << i * kChunkBits; 215 if ((pc_jump_part & kLastChunkTagMask) == 1) break; 216 } 217 // The least significant kSmallPCDeltaBits bits will be added 218 // later. 219 rinfo_.pc_ += pc_jump << kSmallPCDeltaBits; 220 } 221 222 inline void RelocIterator::ReadShortData() { 223 uint8_t unsigned_b = *pos_; 224 rinfo_.data_ = unsigned_b; 225 } 226 227 void RelocIterator::next() { 228 DCHECK(!done()); 229 // Basically, do the opposite of RelocInfoWriter::Write. 230 // Reading of data is as far as possible avoided for unwanted modes, 231 // but we must always update the pc. 232 // 233 // We exit this loop by returning when we find a mode we want. 234 while (pos_ > end_) { 235 int tag = AdvanceGetTag(); 236 if (tag == kEmbeddedObjectTag) { 237 ReadShortTaggedPC(); 238 if (SetMode(RelocInfo::EMBEDDED_OBJECT)) return; 239 } else if (tag == kCodeTargetTag) { 240 ReadShortTaggedPC(); 241 if (SetMode(RelocInfo::CODE_TARGET)) return; 242 } else if (tag == kWasmStubCallTag) { 243 ReadShortTaggedPC(); 244 if (SetMode(RelocInfo::WASM_STUB_CALL)) return; 245 } else { 246 DCHECK_EQ(tag, kDefaultTag); 247 RelocInfo::Mode rmode = GetMode(); 248 if (rmode == RelocInfo::PC_JUMP) { 249 AdvanceReadLongPCJump(); 250 } else { 251 AdvanceReadPC(); 252 if (RelocInfo::IsComment(rmode)) { 253 if (SetMode(rmode)) { 254 AdvanceReadData(); 255 return; 256 } 257 Advance(kIntptrSize); 258 } else if (RelocInfo::IsDeoptReason(rmode)) { 259 Advance(); 260 if (SetMode(rmode)) { 261 ReadShortData(); 262 return; 263 } 264 } else if (RelocInfo::IsConstPool(rmode) || 265 RelocInfo::IsVeneerPool(rmode) || 266 RelocInfo::IsDeoptId(rmode) || 267 RelocInfo::IsDeoptPosition(rmode)) { 268 if (SetMode(rmode)) { 269 AdvanceReadInt(); 270 return; 271 } 272 Advance(kIntSize); 273 } else if (SetMode(static_cast<RelocInfo::Mode>(rmode))) { 274 return; 275 } 276 } 277 } 278 } 279 done_ = true; 280 } 281 282 RelocIterator::RelocIterator(Code* code, int mode_mask) 283 : RelocIterator(code, code->raw_instruction_start(), code->constant_pool(), 284 code->relocation_end(), code->relocation_start(), 285 mode_mask) {} 286 287 RelocIterator::RelocIterator(const CodeReference code_reference, int mode_mask) 288 : RelocIterator(nullptr, code_reference.instruction_start(), 289 code_reference.constant_pool(), 290 code_reference.relocation_end(), 291 code_reference.relocation_start(), mode_mask) {} 292 293 RelocIterator::RelocIterator(EmbeddedData* embedded_data, Code* code, 294 int mode_mask) 295 : RelocIterator( 296 code, embedded_data->InstructionStartOfBuiltin(code->builtin_index()), 297 code->constant_pool(), 298 code->relocation_start() + code->relocation_size(), 299 code->relocation_start(), mode_mask) {} 300 301 RelocIterator::RelocIterator(const CodeDesc& desc, int mode_mask) 302 : RelocIterator(nullptr, reinterpret_cast<Address>(desc.buffer), 0, 303 desc.buffer + desc.buffer_size, 304 desc.buffer + desc.buffer_size - desc.reloc_size, 305 mode_mask) {} 306 307 RelocIterator::RelocIterator(Vector<byte> instructions, 308 Vector<const byte> reloc_info, Address const_pool, 309 int mode_mask) 310 : RelocIterator(nullptr, reinterpret_cast<Address>(instructions.start()), 311 const_pool, reloc_info.start() + reloc_info.size(), 312 reloc_info.start(), mode_mask) {} 313 314 RelocIterator::RelocIterator(Code* host, Address pc, Address constant_pool, 315 const byte* pos, const byte* end, int mode_mask) 316 : pos_(pos), end_(end), mode_mask_(mode_mask) { 317 // Relocation info is read backwards. 318 DCHECK_GE(pos_, end_); 319 rinfo_.host_ = host; 320 rinfo_.pc_ = pc; 321 rinfo_.constant_pool_ = constant_pool; 322 if (mode_mask_ == 0) pos_ = end_; 323 next(); 324 } 325 326 // ----------------------------------------------------------------------------- 327 // Implementation of RelocInfo 328 329 // static 330 bool RelocInfo::OffHeapTargetIsCodedSpecially() { 331 #if defined(V8_TARGET_ARCH_ARM) || defined(V8_TARGET_ARCH_ARM64) || \ 332 defined(V8_TARGET_ARCH_X64) 333 return false; 334 #elif defined(V8_TARGET_ARCH_IA32) || defined(V8_TARGET_ARCH_MIPS) || \ 335 defined(V8_TARGET_ARCH_MIPS64) || defined(V8_TARGET_ARCH_PPC) || \ 336 defined(V8_TARGET_ARCH_S390) 337 return true; 338 #endif 339 } 340 341 Address RelocInfo::wasm_call_address() const { 342 DCHECK_EQ(rmode_, WASM_CALL); 343 return Assembler::target_address_at(pc_, constant_pool_); 344 } 345 346 void RelocInfo::set_wasm_call_address(Address address, 347 ICacheFlushMode icache_flush_mode) { 348 DCHECK_EQ(rmode_, WASM_CALL); 349 Assembler::set_target_address_at(pc_, constant_pool_, address, 350 icache_flush_mode); 351 } 352 353 Address RelocInfo::wasm_stub_call_address() const { 354 DCHECK_EQ(rmode_, WASM_STUB_CALL); 355 return Assembler::target_address_at(pc_, constant_pool_); 356 } 357 358 void RelocInfo::set_wasm_stub_call_address(Address address, 359 ICacheFlushMode icache_flush_mode) { 360 DCHECK_EQ(rmode_, WASM_STUB_CALL); 361 Assembler::set_target_address_at(pc_, constant_pool_, address, 362 icache_flush_mode); 363 } 364 365 void RelocInfo::set_target_address(Address target, 366 WriteBarrierMode write_barrier_mode, 367 ICacheFlushMode icache_flush_mode) { 368 DCHECK(IsCodeTargetMode(rmode_) || IsRuntimeEntry(rmode_) || 369 IsWasmCall(rmode_)); 370 Assembler::set_target_address_at(pc_, constant_pool_, target, 371 icache_flush_mode); 372 if (write_barrier_mode == UPDATE_WRITE_BARRIER && host() != nullptr && 373 IsCodeTargetMode(rmode_)) { 374 Code* target_code = Code::GetCodeFromTargetAddress(target); 375 MarkingBarrierForCode(host(), this, target_code); 376 } 377 } 378 379 bool RelocInfo::RequiresRelocationAfterCodegen(const CodeDesc& desc) { 380 RelocIterator it(desc, RelocInfo::PostCodegenRelocationMask()); 381 return !it.done(); 382 } 383 384 bool RelocInfo::RequiresRelocation(Code* code) { 385 RelocIterator it(code, RelocInfo::kApplyMask); 386 return !it.done(); 387 } 388 389 #ifdef ENABLE_DISASSEMBLER 390 const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) { 391 switch (rmode) { 392 case NONE: 393 return "no reloc"; 394 case EMBEDDED_OBJECT: 395 return "embedded object"; 396 case CODE_TARGET: 397 return "code target"; 398 case RELATIVE_CODE_TARGET: 399 return "relative code target"; 400 case RUNTIME_ENTRY: 401 return "runtime entry"; 402 case COMMENT: 403 return "comment"; 404 case EXTERNAL_REFERENCE: 405 return "external reference"; 406 case INTERNAL_REFERENCE: 407 return "internal reference"; 408 case INTERNAL_REFERENCE_ENCODED: 409 return "encoded internal reference"; 410 case OFF_HEAP_TARGET: 411 return "off heap target"; 412 case DEOPT_SCRIPT_OFFSET: 413 return "deopt script offset"; 414 case DEOPT_INLINING_ID: 415 return "deopt inlining id"; 416 case DEOPT_REASON: 417 return "deopt reason"; 418 case DEOPT_ID: 419 return "deopt index"; 420 case CONST_POOL: 421 return "constant pool"; 422 case VENEER_POOL: 423 return "veneer pool"; 424 case WASM_CALL: 425 return "internal wasm call"; 426 case WASM_STUB_CALL: 427 return "wasm stub call"; 428 case JS_TO_WASM_CALL: 429 return "js to wasm call"; 430 case NUMBER_OF_MODES: 431 case PC_JUMP: 432 UNREACHABLE(); 433 } 434 return "unknown relocation type"; 435 } 436 437 void RelocInfo::Print(Isolate* isolate, std::ostream& os) { // NOLINT 438 os << reinterpret_cast<const void*>(pc_) << " " << RelocModeName(rmode_); 439 if (IsComment(rmode_)) { 440 os << " (" << reinterpret_cast<char*>(data_) << ")"; 441 } else if (rmode_ == DEOPT_SCRIPT_OFFSET || rmode_ == DEOPT_INLINING_ID) { 442 os << " (" << data() << ")"; 443 } else if (rmode_ == DEOPT_REASON) { 444 os << " (" 445 << DeoptimizeReasonToString(static_cast<DeoptimizeReason>(data_)) << ")"; 446 } else if (rmode_ == EMBEDDED_OBJECT) { 447 os << " (" << Brief(target_object()) << ")"; 448 } else if (rmode_ == EXTERNAL_REFERENCE) { 449 if (isolate) { 450 ExternalReferenceEncoder ref_encoder(isolate); 451 os << " (" 452 << ref_encoder.NameOfAddress(isolate, target_external_reference()) 453 << ") "; 454 } 455 os << " (" << reinterpret_cast<const void*>(target_external_reference()) 456 << ")"; 457 } else if (IsCodeTargetMode(rmode_)) { 458 const Address code_target = target_address(); 459 Code* code = Code::GetCodeFromTargetAddress(code_target); 460 DCHECK(code->IsCode()); 461 os << " (" << Code::Kind2String(code->kind()); 462 if (Builtins::IsBuiltin(code)) { 463 os << " " << Builtins::name(code->builtin_index()); 464 } else if (code->kind() == Code::STUB) { 465 os << " " << CodeStub::MajorName(CodeStub::GetMajorKey(code)); 466 } 467 os << ") (" << reinterpret_cast<const void*>(target_address()) << ")"; 468 } else if (IsRuntimeEntry(rmode_) && isolate->deoptimizer_data() != nullptr) { 469 // Deoptimization bailouts are stored as runtime entries. 470 DeoptimizeKind type; 471 if (Deoptimizer::IsDeoptimizationEntry(isolate, target_address(), &type)) { 472 int id = GetDeoptimizationId(isolate, type); 473 os << " (" << Deoptimizer::MessageFor(type) << " deoptimization bailout " 474 << id << ")"; 475 } 476 } else if (IsConstPool(rmode_)) { 477 os << " (size " << static_cast<int>(data_) << ")"; 478 } 479 480 os << "\n"; 481 } 482 #endif // ENABLE_DISASSEMBLER 483 484 #ifdef VERIFY_HEAP 485 void RelocInfo::Verify(Isolate* isolate) { 486 switch (rmode_) { 487 case EMBEDDED_OBJECT: 488 Object::VerifyPointer(isolate, target_object()); 489 break; 490 case CODE_TARGET: 491 case RELATIVE_CODE_TARGET: { 492 // convert inline target address to code object 493 Address addr = target_address(); 494 CHECK_NE(addr, kNullAddress); 495 // Check that we can find the right code object. 496 Code* code = Code::GetCodeFromTargetAddress(addr); 497 Object* found = isolate->FindCodeObject(addr); 498 CHECK(found->IsCode()); 499 CHECK(code->address() == HeapObject::cast(found)->address()); 500 break; 501 } 502 case INTERNAL_REFERENCE: 503 case INTERNAL_REFERENCE_ENCODED: { 504 Address target = target_internal_reference(); 505 Address pc = target_internal_reference_address(); 506 Code* code = Code::cast(isolate->FindCodeObject(pc)); 507 CHECK(target >= code->InstructionStart()); 508 CHECK(target <= code->InstructionEnd()); 509 break; 510 } 511 case OFF_HEAP_TARGET: { 512 Address addr = target_off_heap_target(); 513 CHECK_NE(addr, kNullAddress); 514 CHECK_NOT_NULL(InstructionStream::TryLookupCode(isolate, addr)); 515 break; 516 } 517 case RUNTIME_ENTRY: 518 case COMMENT: 519 case EXTERNAL_REFERENCE: 520 case DEOPT_SCRIPT_OFFSET: 521 case DEOPT_INLINING_ID: 522 case DEOPT_REASON: 523 case DEOPT_ID: 524 case CONST_POOL: 525 case VENEER_POOL: 526 case WASM_CALL: 527 case WASM_STUB_CALL: 528 case JS_TO_WASM_CALL: 529 case NONE: 530 break; 531 case NUMBER_OF_MODES: 532 case PC_JUMP: 533 UNREACHABLE(); 534 break; 535 } 536 } 537 #endif // VERIFY_HEAP 538 539 } // namespace internal 540 } // namespace v8 541