v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
eh-frame.cc
Go to the documentation of this file.
1// Copyright 2016 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
6
7#include <iomanip>
8#include <ostream>
9
11
12#if !defined(V8_TARGET_ARCH_X64) && !defined(V8_TARGET_ARCH_ARM) && \
13 !defined(V8_TARGET_ARCH_ARM64) && !defined(V8_TARGET_ARCH_S390X) && \
14 !defined(V8_TARGET_ARCH_PPC64)
15
16// Placeholders for unsupported architectures.
17
18namespace v8 {
19namespace internal {
20
23
25
27
30}
31
32#ifdef ENABLE_DISASSEMBLER
33
34const char* EhFrameDisassembler::DwarfRegisterCodeToString(int) {
36}
37
38#endif
39
40} // namespace internal
41} // namespace v8
42
43#endif
44
45namespace v8 {
46namespace internal {
47
52
54
55// static
56void EhFrameWriter::WriteEmptyEhFrame(std::ostream& stream) {
58
59 // .eh_frame pointer encoding specifier.
61
62 // Lookup table size encoding.
63 stream.put(EhFrameConstants::kUData4);
64
65 // Lookup table entries encoding.
67
68 // Dummy pointers and 0 entries in the lookup table.
69 char dummy_data[EhFrameConstants::kEhFrameHdrSize - 4] = {0};
70 stream.write(&dummy_data[0], sizeof(dummy_data));
71}
72
74 : cie_size_(0),
75 last_pc_offset_(0),
76 writer_state_(InternalState::kUndefined),
77 base_register_(no_reg),
78 base_offset_(0),
79 eh_frame_buffer_(zone) {}
80
88
90 static const int kCIEIdentifier = 0;
91 static const int kCIEVersion = 3;
92 static const int kAugmentationDataSize = 2;
93 static const uint8_t kAugmentationString[] = {'z', 'L', 'R', 0};
94
95 // Placeholder for the size of the CIE.
96 int size_offset = eh_frame_offset();
98
99 // CIE identifier and version.
100 int record_start_offset = eh_frame_offset();
101 WriteInt32(kCIEIdentifier);
102 WriteByte(kCIEVersion);
103
104 // Augmentation data contents descriptor: LSDA and FDE encoding.
105 WriteBytes(&kAugmentationString[0], sizeof(kAugmentationString));
106
107 // Alignment factors.
110
112
113 // Augmentation data.
114 WriteULeb128(kAugmentationDataSize);
115 // No language-specific data area (LSDA).
117 // FDE pointers encoding.
119
120 // Write directives to build the initial state of the unwinding table.
121 DCHECK_EQ(eh_frame_offset() - size_offset,
124
125 WritePaddingToAlignedSize(eh_frame_offset() - record_start_offset);
126
127 int record_end_offset = eh_frame_offset();
128 int encoded_cie_size = record_end_offset - record_start_offset;
129 cie_size_ = record_end_offset - size_offset;
130
131 // Patch the size of the CIE now that we know it.
132 PatchInt32(size_offset, encoded_cie_size);
133}
134
137
138 // Placeholder for size of the FDE. Will be filled in Finish().
141
142 // Backwards offset to the CIE.
144
145 // Placeholder for pointer to procedure. Will be filled in Finish().
148
149 // Placeholder for size of the procedure. Will be filled in Finish().
152
153 // No augmentation data.
154 WriteByte(0);
155}
156
159
160 //
161 // In order to calculate offsets in the .eh_frame_hdr, we must know the layout
162 // of the DSO generated by perf inject, which is assumed to be the following:
163 //
164 // | ... | |
165 // +---------------+ <-- (F) --- | Larger offsets in file
166 // | | ^ |
167 // | Instructions | | .text v
168 // | | v
169 // +---------------+ <-- (E) ---
170 // |///////////////|
171 // |////Padding////|
172 // |///////////////|
173 // +---------------+ <-- (D) ---
174 // | | ^
175 // | CIE | |
176 // | | |
177 // +---------------+ <-- (C) |
178 // | | | .eh_frame
179 // | FDE | |
180 // | | |
181 // +---------------+ |
182 // | terminator | v
183 // +---------------+ <-- (B) ---
184 // | version | ^
185 // +---------------+ |
186 // | encoding | |
187 // | specifiers | |
188 // +---------------+ <---(A) | .eh_frame_hdr
189 // | offset to | |
190 // | .eh_frame | |
191 // +---------------+ |
192 // | ... | ...
193 //
194 // (F) is aligned to a 16-byte boundary.
195 // (D) is aligned to a 8-byte boundary.
196 // (B) is aligned to a 4-byte boundary.
197 // (C), (E) and (A) have no alignment requirements.
198 //
199 // The distance between (A) and (B) is 4 bytes.
200 //
201 // The size of the FDE is required to be a multiple of the pointer size, which
202 // means that (B) will be naturally aligned to a 4-byte boundary on all the
203 // architectures we support.
204 //
205 // Because (E) has no alignment requirements, there is padding between (E) and
206 // (D). (F) is aligned at a 16-byte boundary, thus to a 8-byte one as well.
207 //
208
209 int eh_frame_size = eh_frame_offset();
210
212
213 // .eh_frame pointer encoding specifier.
215 // Lookup table size encoding specifier.
217 // Lookup table entries encoding specifier.
219
220 // Pointer to .eh_frame, relative to this offset (A -> D in the diagram).
223
224 // Number of entries in the LUT, one for the only routine.
225 WriteInt32(1);
226
227 // Pointer to the start of the routine, relative to the beginning of the
228 // .eh_frame_hdr (B -> F in the diagram).
229 WriteInt32(-(RoundUp(code_size, 8) + eh_frame_size));
230
231 // Pointer to the start of the associated FDE, relative to the start of the
232 // .eh_frame_hdr (B -> C in the diagram).
233 WriteInt32(-(eh_frame_size - cie_size_));
234
235 DCHECK_EQ(eh_frame_offset() - eh_frame_size,
237}
238
241 DCHECK_GE(unpadded_size, 0);
242
243 int padding_size = RoundUp(unpadded_size, kSystemPointerSize) - unpadded_size;
244
245 uint8_t nop = static_cast<uint8_t>(EhFrameConstants::DwarfOpcodes::kNop);
246 static const uint8_t kPadding[] = {nop, nop, nop, nop, nop, nop, nop, nop};
247 DCHECK_LE(padding_size, static_cast<int>(sizeof(kPadding)));
248 WriteBytes(&kPadding[0], padding_size);
249}
250
276
284
292
304
305void EhFrameWriter::RecordRegisterSavedToStack(int dwarf_register_code,
306 int offset) {
309 int factored_offset = offset / EhFrameConstants::kDataAlignmentFactor;
310 if (factored_offset >= 0) {
314 (dwarf_register_code & EhFrameConstants::kSavedRegisterMask));
315 WriteULeb128(factored_offset);
316 } else {
318 WriteULeb128(dwarf_register_code);
319 WriteSLeb128(factored_offset);
320 }
321}
322
326
332
336
348
349void EhFrameWriter::Finish(int code_size) {
352
355
356 // Write the size of the FDE now that we know it.
357 // The encoded size does not include the size field itself.
358 int encoded_fde_size = eh_frame_offset() - fde_offset() - kInt32Size;
359 PatchInt32(fde_offset(), encoded_fde_size);
360
361 // Write size and offset to procedure.
363 -(RoundUp(code_size, 8) + GetProcedureAddressOffset()));
365
366 // Terminate the .eh_frame.
367 static const uint8_t kTerminator[EhFrameConstants::kEhFrameTerminatorSize] = {
368 0};
370
371 WriteEhFrameHdr(code_size);
372
374}
375
378 desc->unwinding_info_size = static_cast<int>(eh_frame_buffer_.size());
379 desc->unwinding_info = eh_frame_buffer_.data();
380}
381
382void EhFrameWriter::WriteULeb128(uint32_t value) {
383 do {
384 uint8_t chunk = value & 0x7F;
385 value >>= 7;
386 if (value != 0) chunk |= 0x80;
387 WriteByte(chunk);
388 } while (value != 0);
389}
390
391void EhFrameWriter::WriteSLeb128(int32_t value) {
392 static const int kSignBitMask = 0x40;
393 bool done;
394 do {
395 uint8_t chunk = value & 0x7F;
396 value >>= 7;
397 done = ((value == 0) && ((chunk & kSignBitMask) == 0)) ||
398 ((value == -1) && ((chunk & kSignBitMask) != 0));
399 if (!done) chunk |= 0x80;
400 WriteByte(chunk);
401 } while (!done);
402}
403
405 int size = 0;
406 uint32_t result = DecodeULeb128(next_, &size);
407 DCHECK_LE(next_ + size, end_);
408 next_ += size;
409 return result;
410}
411
413 int size = 0;
414 int32_t result = DecodeSLeb128(next_, &size);
415 DCHECK_LE(next_ + size, end_);
416 next_ += size;
417 return result;
418}
419
420// static
421uint32_t EhFrameIterator::DecodeULeb128(const uint8_t* encoded,
422 int* encoded_size) {
423 const uint8_t* current = encoded;
424 uint32_t result = 0;
425 int shift = 0;
426
427 do {
428 DCHECK_LT(shift, 8 * static_cast<int>(sizeof(result)));
429 result |= (*current & 0x7F) << shift;
430 shift += 7;
431 } while (*current++ >= 128);
432
433 DCHECK_NOT_NULL(encoded_size);
434 *encoded_size = static_cast<int>(current - encoded);
435
436 return result;
437}
438
439// static
440int32_t EhFrameIterator::DecodeSLeb128(const uint8_t* encoded,
441 int* encoded_size) {
442 static const uint8_t kSignBitMask = 0x40;
443
444 const uint8_t* current = encoded;
445 int32_t result = 0;
446 int shift = 0;
447 uint8_t chunk;
448
449 do {
450 chunk = *current++;
451 DCHECK_LT(shift, 8 * static_cast<int>(sizeof(result)));
452 result |= (chunk & 0x7F) << shift;
453 shift += 7;
454 } while (chunk >= 128);
455
456 // Sign extend the result if the last chunk has the sign bit set.
457 if (chunk & kSignBitMask) result |= (~0ull) << shift;
458
459 DCHECK_NOT_NULL(encoded_size);
460 *encoded_size = static_cast<int>(current - encoded);
461
462 return result;
463}
464
465#ifdef ENABLE_DISASSEMBLER
466
467namespace {
468
469class V8_NODISCARD StreamModifiersScope final {
470 public:
471 explicit StreamModifiersScope(std::ostream* stream)
472 : stream_(stream), flags_(stream->flags()) {}
473 ~StreamModifiersScope() { stream_->flags(flags_); }
474
475 private:
476 std::ostream* stream_;
477 std::ios::fmtflags flags_;
478};
479
480} // namespace
481
482// static
483void EhFrameDisassembler::DumpDwarfDirectives(std::ostream& stream,
484 const uint8_t* start,
485 const uint8_t* end) {
486 StreamModifiersScope modifiers_scope(&stream);
487
488 EhFrameIterator eh_frame_iterator(start, end);
489 uint32_t offset_in_procedure = 0;
490
491 while (!eh_frame_iterator.Done()) {
492 stream << eh_frame_iterator.current_address() << " ";
493
494 uint8_t bytecode = eh_frame_iterator.GetNextByte();
495
496 if (((bytecode >> EhFrameConstants::kLocationMaskSize) & 0xFF) ==
498 int value = (bytecode & EhFrameConstants::kLocationMask) *
500 offset_in_procedure += value;
501 stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
502 << ")\n";
503 continue;
504 }
505
506 if (((bytecode >> EhFrameConstants::kSavedRegisterMaskSize) & 0xFF) ==
508 int32_t decoded_offset = eh_frame_iterator.GetNextULeb128();
509 stream << "| "
510 << DwarfRegisterCodeToString(bytecode &
512 << " saved at base" << std::showpos
514 << std::noshowpos << '\n';
515 continue;
516 }
517
518 if (((bytecode >> EhFrameConstants::kFollowInitialRuleMaskSize) & 0xFF) ==
520 stream << "| "
521 << DwarfRegisterCodeToString(bytecode &
523 << " follows rule in CIE\n";
524 continue;
525 }
526
527 switch (static_cast<EhFrameConstants::DwarfOpcodes>(bytecode)) {
529 stream << "| "
530 << DwarfRegisterCodeToString(eh_frame_iterator.GetNextULeb128());
531 int32_t decoded_offset = eh_frame_iterator.GetNextSLeb128();
532 stream << " saved at base" << std::showpos
534 << std::noshowpos << '\n';
535 break;
536 }
538 int value = eh_frame_iterator.GetNextByte() *
540 offset_in_procedure += value;
541 stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
542 << ")\n";
543 break;
544 }
546 int value = eh_frame_iterator.GetNextUInt16() *
548 offset_in_procedure += value;
549 stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
550 << ")\n";
551 break;
552 }
554 int value = eh_frame_iterator.GetNextUInt32() *
556 offset_in_procedure += value;
557 stream << "| pc_offset=" << offset_in_procedure << " (delta=" << value
558 << ")\n";
559 break;
560 }
562 uint32_t base_register = eh_frame_iterator.GetNextULeb128();
563 uint32_t base_offset = eh_frame_iterator.GetNextULeb128();
564 stream << "| base_register=" << DwarfRegisterCodeToString(base_register)
565 << ", base_offset=" << base_offset << '\n';
566 break;
567 }
569 stream << "| base_offset=" << eh_frame_iterator.GetNextULeb128()
570 << '\n';
571 break;
572 }
574 stream << "| base_register="
575 << DwarfRegisterCodeToString(eh_frame_iterator.GetNextULeb128())
576 << '\n';
577 break;
578 }
580 stream << "| "
581 << DwarfRegisterCodeToString(eh_frame_iterator.GetNextULeb128())
582 << " not modified from previous frame\n";
583 break;
584 }
586 stream << "| nop\n";
587 break;
588 default:
589 UNREACHABLE();
590 }
591 }
592}
593
594void EhFrameDisassembler::DisassembleToStream(std::ostream& stream) {
595 // The encoded CIE size does not include the size field itself.
596 const int cie_size =
599 const int fde_offset = cie_size;
600
601 const uint8_t* cie_directives_start =
603 const uint8_t* cie_directives_end = start_ + cie_size;
604 DCHECK_LE(cie_directives_start, cie_directives_end);
605
606 stream << reinterpret_cast<const void*>(start_) << " .eh_frame: CIE\n";
607 DumpDwarfDirectives(stream, cie_directives_start, cie_directives_end);
608
609 Address procedure_offset_address =
610 reinterpret_cast<Address>(start_) + fde_offset +
612 int32_t procedure_offset =
613 base::ReadUnalignedValue<int32_t>(procedure_offset_address);
614
615 Address procedure_size_address = reinterpret_cast<Address>(start_) +
616 fde_offset +
618 uint32_t procedure_size =
619 base::ReadUnalignedValue<uint32_t>(procedure_size_address);
620
621 const uint8_t* fde_start = start_ + fde_offset;
622 stream << reinterpret_cast<const void*>(fde_start) << " .eh_frame: FDE\n"
623 << reinterpret_cast<const void*>(procedure_offset_address)
624 << " | procedure_offset=" << procedure_offset << '\n'
625 << reinterpret_cast<const void*>(procedure_size_address)
626 << " | procedure_size=" << procedure_size << '\n';
627
628 const int fde_directives_offset = fde_offset + 4 * kInt32Size + 1;
629
630 const uint8_t* fde_directives_start = start_ + fde_directives_offset;
631 const uint8_t* fde_directives_end = end_ - EhFrameConstants::kEhFrameHdrSize -
633 DCHECK_LE(fde_directives_start, fde_directives_end);
634
635 DumpDwarfDirectives(stream, fde_directives_start, fde_directives_end);
636
637 const uint8_t* fde_terminator_start = fde_directives_end;
638 stream << reinterpret_cast<const void*>(fde_terminator_start)
639 << " .eh_frame: terminator\n";
640
641 const uint8_t* eh_frame_hdr_start =
642 fde_terminator_start + EhFrameConstants::kEhFrameTerminatorSize;
643 stream << reinterpret_cast<const void*>(eh_frame_hdr_start)
644 << " .eh_frame_hdr\n";
645}
646
647#endif
648
649} // namespace internal
650} // namespace v8
static const int kLocationMask
Definition eh-frame.h:44
static const int kFollowInitialRuleTag
Definition eh-frame.h:51
static const int kProcedureSizeOffsetInFde
Definition eh-frame.h:56
static const int kSavedRegisterMaskSize
Definition eh-frame.h:49
static const int kEhFrameTerminatorSize
Definition eh-frame.h:59
static const int kFdeVersionSize
Definition eh-frame.h:65
static const int kInitialStateOffsetInCie
Definition eh-frame.h:58
static const int kSavedRegisterTag
Definition eh-frame.h:47
static const int kLocationMaskSize
Definition eh-frame.h:45
static const int kLocationTag
Definition eh-frame.h:43
static const int kSavedRegisterMask
Definition eh-frame.h:48
static const int kFollowInitialRuleMaskSize
Definition eh-frame.h:53
static const int kDataAlignmentFactor
Definition eh-frame.h:63
static const int kEhFrameHdrSize
Definition eh-frame.h:69
static const int kProcedureAddressOffsetInFde
Definition eh-frame.h:55
static const int kCodeAlignmentFactor
Definition eh-frame.h:62
static const int kEhFrameHdrVersion
Definition eh-frame.h:68
static const int kFdeEncodingSpecifiersSize
Definition eh-frame.h:66
static const int kFollowInitialRuleMask
Definition eh-frame.h:52
static int32_t DecodeSLeb128(const uint8_t *encoded, int *encoded_size)
Definition eh-frame.cc:440
static uint32_t DecodeULeb128(const uint8_t *encoded, int *encoded_size)
Definition eh-frame.cc:421
int GetProcedureAddressOffset() const
Definition eh-frame.h:181
void WriteEhFrameHdr(int code_size)
Definition eh-frame.cc:157
void SetBaseAddressOffset(int base_offset)
Definition eh-frame.cc:277
void WritePaddingToAlignedSize(int unpadded_size)
Definition eh-frame.cc:239
void WriteBytes(const uint8_t *start, int size)
Definition eh-frame.h:145
void SetBaseAddressRegisterAndOffset(Register base_register, int base_offset)
Definition eh-frame.cc:293
static int RegisterToDwarfCode(Register name)
void WriteOpcode(EhFrameConstants::DwarfOpcodes opcode)
Definition eh-frame.h:142
void WriteSLeb128(int32_t value)
Definition eh-frame.cc:391
void WriteByte(uint8_t value)
Definition eh-frame.h:141
void PatchInt32(int base_offset, uint32_t value)
Definition eh-frame.h:154
void WriteInt16(uint16_t value)
Definition eh-frame.h:148
ZoneVector< uint8_t > eh_frame_buffer_
Definition eh-frame.h:210
void Finish(int code_size)
Definition eh-frame.cc:349
void AdvanceLocation(int pc_offset)
Definition eh-frame.cc:251
int GetProcedureSizeOffset() const
Definition eh-frame.h:185
void GetEhFrame(CodeDesc *desc)
Definition eh-frame.cc:376
void RecordRegisterNotModified(Register name)
Definition eh-frame.cc:323
static void WriteEmptyEhFrame(std::ostream &stream)
Definition eh-frame.cc:56
static const uint32_t kInt32Placeholder
Definition eh-frame.h:136
InternalState writer_state_
Definition eh-frame.h:207
void SetBaseAddressRegister(Register base_register)
Definition eh-frame.cc:285
void RecordRegisterSavedToStack(Register name, int offset)
Definition eh-frame.h:104
Register base_register() const
Definition eh-frame.h:130
void RecordRegisterFollowsInitialRule(Register name)
Definition eh-frame.cc:333
void WriteULeb128(uint32_t value)
Definition eh-frame.cc:382
void WriteInt32(uint32_t value)
Definition eh-frame.h:151
void reserve(size_t new_cap)
uint8_t *const start_
Definition assembler.cc:131
JSRegExp::Flags flags_
const v8::base::TimeTicks end_
Definition sweeper.cc:54
int start
int end
int32_t offset
ZoneVector< RpoNumber > & result
int pc_offset
int int32_t
Definition unicode.cc:40
static V ReadUnalignedValue(Address p)
Definition memory.h:28
constexpr Register no_reg
constexpr int kMaxUInt8
Definition globals.h:378
constexpr int kMaxUInt16
Definition globals.h:382
constexpr int kSystemPointerSize
Definition globals.h:410
constexpr int kInt32Size
Definition globals.h:401
return value
Definition map-inl.h:893
#define STATIC_CONST_MEMBER_DEFINITION
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr T RoundUp(T x, intptr_t m)
Definition macros.h:387
#define V8_NODISCARD
Definition v8config.h:693