v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
gdb-jit.cc
Go to the documentation of this file.
1// Copyright 2010 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 <iterator>
8#include <map>
9#include <memory>
10#include <optional>
11#include <vector>
12
14#include "src/api/api-inl.h"
16#include "src/base/bits.h"
17#include "src/base/hashmap.h"
18#include "src/base/memory.h"
21#include "src/base/strings.h"
22#include "src/base/vector.h"
28#include "src/objects/objects.h"
29#include "src/utils/ostreams.h"
31
32namespace v8 {
33namespace internal {
34namespace GDBJITInterface {
35
36#ifdef ENABLE_GDB_JIT_INTERFACE
37
38#ifdef __APPLE__
39#define __MACH_O
40class MachO;
41class MachOSection;
42using DebugObject = MachO;
43using DebugSection = MachOSection;
44#else
45#define __ELF
46class ELF;
47class ELFSection;
48using DebugObject = ELF;
49using DebugSection = ELFSection;
50#endif
51
52class Writer {
53 public:
54 explicit Writer(DebugObject* debug_object)
55 : debug_object_(debug_object),
56 position_(0),
57 capacity_(1024),
58 buffer_(reinterpret_cast<uint8_t*>(base::Malloc(capacity_))) {}
59
60 ~Writer() { base::Free(buffer_); }
61
62 uintptr_t position() const { return position_; }
63
64 template <typename T>
65 class Slot {
66 public:
67 Slot(Writer* w, uintptr_t offset) : w_(w), offset_(offset) {}
68
69 T* operator->() { return w_->RawSlotAt<T>(offset_); }
70
71 void set(const T& value) {
72 base::WriteUnalignedValue(w_->AddressAt<T>(offset_), value);
73 }
74
75 Slot<T> at(int i) { return Slot<T>(w_, offset_ + sizeof(T) * i); }
76
77 private:
78 Writer* w_;
79 uintptr_t offset_;
80 };
81
82 template <typename T>
83 void Write(const T& val) {
84 Ensure(position_ + sizeof(T));
85 base::WriteUnalignedValue(AddressAt<T>(position_), val);
86 position_ += sizeof(T);
87 }
88
89 template <typename T>
90 Slot<T> SlotAt(uintptr_t offset) {
91 Ensure(offset + sizeof(T));
92 return Slot<T>(this, offset);
93 }
94
95 template <typename T>
96 Slot<T> CreateSlotHere() {
97 return CreateSlotsHere<T>(1);
98 }
99
100 template <typename T>
101 Slot<T> CreateSlotsHere(uint32_t count) {
102 uintptr_t slot_position = position_;
103 position_ += sizeof(T) * count;
104 Ensure(position_);
105 return SlotAt<T>(slot_position);
106 }
107
108 void Ensure(uintptr_t pos) {
109 if (capacity_ < pos) {
110 while (capacity_ < pos) capacity_ *= 2;
111 buffer_ = reinterpret_cast<uint8_t*>(base::Realloc(buffer_, capacity_));
112 }
113 }
114
115 DebugObject* debug_object() { return debug_object_; }
116
117 uint8_t* buffer() { return buffer_; }
118
119 void Align(uintptr_t align) {
120 uintptr_t delta = position_ % align;
121 if (delta == 0) return;
122 uintptr_t padding = align - delta;
123 Ensure(position_ += padding);
124 DCHECK_EQ(position_ % align, 0);
125 }
126
127 void WriteULEB128(uintptr_t value) {
128 do {
129 uint8_t byte = value & 0x7F;
130 value >>= 7;
131 if (value != 0) byte |= 0x80;
132 Write<uint8_t>(byte);
133 } while (value != 0);
134 }
135
136 void WriteSLEB128(intptr_t value) {
137 bool more = true;
138 while (more) {
139 int8_t byte = value & 0x7F;
140 bool byte_sign = byte & 0x40;
141 value >>= 7;
142
143 if ((value == 0 && !byte_sign) || (value == -1 && byte_sign)) {
144 more = false;
145 } else {
146 byte |= 0x80;
147 }
148
149 Write<int8_t>(byte);
150 }
151 }
152
153 void WriteString(const char* str) {
154 do {
155 Write<char>(*str);
156 } while (*str++);
157 }
158
159 private:
160 template <typename T>
161 friend class Slot;
162
163 template <typename T>
164 Address AddressAt(uintptr_t offset) {
165 DCHECK(offset < capacity_ && offset + sizeof(T) <= capacity_);
166 return reinterpret_cast<Address>(&buffer_[offset]);
167 }
168
169 template <typename T>
170 T* RawSlotAt(uintptr_t offset) {
171 DCHECK(offset < capacity_ && offset + sizeof(T) <= capacity_);
172 return reinterpret_cast<T*>(&buffer_[offset]);
173 }
174
175 DebugObject* debug_object_;
176 uintptr_t position_;
177 uintptr_t capacity_;
178 uint8_t* buffer_;
179};
180
181class ELFStringTable;
182
183template <typename THeader>
184class DebugSectionBase : public ZoneObject {
185 public:
186 virtual ~DebugSectionBase() = default;
187
188 virtual void WriteBody(Writer::Slot<THeader> header, Writer* writer) {
189 uintptr_t start = writer->position();
190 if (WriteBodyInternal(writer)) {
191 uintptr_t end = writer->position();
192 header->offset = static_cast<uint32_t>(start);
193#if defined(__MACH_O)
194 header->addr = 0;
195#endif
196 header->size = end - start;
197 }
198 }
199
200 virtual bool WriteBodyInternal(Writer* writer) { return false; }
201
202 using Header = THeader;
203};
204
205struct MachOSectionHeader {
206 char sectname[16];
207 char segname[16];
208#if V8_TARGET_ARCH_IA32
209 uint32_t addr;
210 uint32_t size;
211#else
212 uint64_t addr;
213 uint64_t size;
214#endif
215 uint32_t offset;
216 uint32_t align;
217 uint32_t reloff;
218 uint32_t nreloc;
219 uint32_t flags;
220 uint32_t reserved1;
221 uint32_t reserved2;
222};
223
224class MachOSection : public DebugSectionBase<MachOSectionHeader> {
225 public:
226 enum Type {
227 S_REGULAR = 0x0u,
228 S_ATTR_COALESCED = 0xBu,
229 S_ATTR_SOME_INSTRUCTIONS = 0x400u,
230 S_ATTR_DEBUG = 0x02000000u,
231 S_ATTR_PURE_INSTRUCTIONS = 0x80000000u
232 };
233
234 MachOSection(const char* name, const char* segment, uint32_t align,
235 uint32_t flags)
236 : name_(name), segment_(segment), align_(align), flags_(flags) {
237 if (align_ != 0) {
238 DCHECK(base::bits::IsPowerOfTwo(align));
239 align_ = base::bits::WhichPowerOfTwo(align_);
240 }
241 }
242
243 ~MachOSection() override = default;
244
245 virtual void PopulateHeader(Writer::Slot<Header> header) {
246 header->addr = 0;
247 header->size = 0;
248 header->offset = 0;
249 header->align = align_;
250 header->reloff = 0;
251 header->nreloc = 0;
252 header->flags = flags_;
253 header->reserved1 = 0;
254 header->reserved2 = 0;
255 memset(header->sectname, 0, sizeof(header->sectname));
256 memset(header->segname, 0, sizeof(header->segname));
257 DCHECK(strlen(name_) < sizeof(header->sectname));
258 DCHECK(strlen(segment_) < sizeof(header->segname));
259 strncpy(header->sectname, name_, sizeof(header->sectname));
260 strncpy(header->segname, segment_, sizeof(header->segname));
261 }
262
263 private:
264 const char* name_;
265 const char* segment_;
266 uint32_t align_;
267 uint32_t flags_;
268};
269
270struct ELFSectionHeader {
271 uint32_t name;
272 uint32_t type;
273 uintptr_t flags;
274 uintptr_t address;
275 uintptr_t offset;
276 uintptr_t size;
277 uint32_t link;
278 uint32_t info;
279 uintptr_t alignment;
280 uintptr_t entry_size;
281};
282
283#if defined(__ELF)
284class ELFSection : public DebugSectionBase<ELFSectionHeader> {
285 public:
286 enum Type {
287 TYPE_NULL = 0,
288 TYPE_PROGBITS = 1,
289 TYPE_SYMTAB = 2,
290 TYPE_STRTAB = 3,
291 TYPE_RELA = 4,
292 TYPE_HASH = 5,
293 TYPE_DYNAMIC = 6,
294 TYPE_NOTE = 7,
295 TYPE_NOBITS = 8,
296 TYPE_REL = 9,
297 TYPE_SHLIB = 10,
298 TYPE_DYNSYM = 11,
299 TYPE_LOPROC = 0x70000000,
300 TYPE_X86_64_UNWIND = 0x70000001,
301 TYPE_HIPROC = 0x7FFFFFFF,
302 TYPE_LOUSER = 0x80000000,
303 TYPE_HIUSER = 0xFFFFFFFF
304 };
305
306 enum Flags { FLAG_WRITE = 1, FLAG_ALLOC = 2, FLAG_EXEC = 4 };
307
308 enum SpecialIndexes { INDEX_ABSOLUTE = 0xFFF1 };
309
310 ELFSection(const char* name, Type type, uintptr_t align)
311 : name_(name), type_(type), align_(align) {}
312
313 ~ELFSection() override = default;
314
315 void PopulateHeader(Writer::Slot<Header> header, ELFStringTable* strtab);
316
317 void WriteBody(Writer::Slot<Header> header, Writer* w) override {
318 uintptr_t start = w->position();
319 if (WriteBodyInternal(w)) {
320 uintptr_t end = w->position();
321 header->offset = start;
322 header->size = end - start;
323 }
324 }
325
326 bool WriteBodyInternal(Writer* w) override { return false; }
327
328 uint16_t index() const { return index_; }
329 void set_index(uint16_t index) { index_ = index; }
330
331 protected:
332 virtual void PopulateHeader(Writer::Slot<Header> header) {
333 header->flags = 0;
334 header->address = 0;
335 header->offset = 0;
336 header->size = 0;
337 header->link = 0;
338 header->info = 0;
339 header->entry_size = 0;
340 }
341
342 private:
343 const char* name_;
344 Type type_;
345 uintptr_t align_;
346 uint16_t index_;
347};
348#endif // defined(__ELF)
349
350#if defined(__MACH_O)
351class MachOTextSection : public MachOSection {
352 public:
353 MachOTextSection(uint32_t align, uintptr_t addr, uintptr_t size)
354 : MachOSection("__text", "__TEXT", align,
355 MachOSection::S_REGULAR |
356 MachOSection::S_ATTR_SOME_INSTRUCTIONS |
357 MachOSection::S_ATTR_PURE_INSTRUCTIONS),
358 addr_(addr),
359 size_(size) {}
360
361 protected:
362 virtual void PopulateHeader(Writer::Slot<Header> header) {
363 MachOSection::PopulateHeader(header);
364 header->addr = addr_;
365 header->size = size_;
366 }
367
368 private:
369 uintptr_t addr_;
370 uintptr_t size_;
371};
372#endif // defined(__MACH_O)
373
374#if defined(__ELF)
375class FullHeaderELFSection : public ELFSection {
376 public:
377 FullHeaderELFSection(const char* name, Type type, uintptr_t align,
378 uintptr_t addr, uintptr_t offset, uintptr_t size,
379 uintptr_t flags)
380 : ELFSection(name, type, align),
381 addr_(addr),
383 size_(size),
384 flags_(flags) {}
385
386 protected:
387 void PopulateHeader(Writer::Slot<Header> header) override {
388 ELFSection::PopulateHeader(header);
389 header->address = addr_;
390 header->offset = offset_;
391 header->size = size_;
392 header->flags = flags_;
393 }
394
395 private:
396 uintptr_t addr_;
397 uintptr_t offset_;
398 uintptr_t size_;
399 uintptr_t flags_;
400};
401
402class ELFStringTable : public ELFSection {
403 public:
404 explicit ELFStringTable(const char* name)
405 : ELFSection(name, TYPE_STRTAB, 1),
406 writer_(nullptr),
407 offset_(0),
408 size_(0) {}
409
410 uintptr_t Add(const char* str) {
411 if (*str == '\0') return 0;
412
413 uintptr_t offset = size_;
414 WriteString(str);
415 return offset;
416 }
417
418 void AttachWriter(Writer* w) {
419 writer_ = w;
420 offset_ = writer_->position();
421
422 // First entry in the string table should be an empty string.
423 WriteString("");
424 }
425
426 void DetachWriter() { writer_ = nullptr; }
427
428 void WriteBody(Writer::Slot<Header> header, Writer* w) override {
429 DCHECK_NULL(writer_);
430 header->offset = offset_;
431 header->size = size_;
432 }
433
434 private:
435 void WriteString(const char* str) {
436 uintptr_t written = 0;
437 do {
438 writer_->Write(*str);
439 written++;
440 } while (*str++);
441 size_ += written;
442 }
443
444 Writer* writer_;
445
446 uintptr_t offset_;
447 uintptr_t size_;
448};
449
450void ELFSection::PopulateHeader(Writer::Slot<ELFSection::Header> header,
451 ELFStringTable* strtab) {
452 header->name = static_cast<uint32_t>(strtab->Add(name_));
453 header->type = type_;
454 header->alignment = align_;
455 PopulateHeader(header);
456}
457#endif // defined(__ELF)
458
459#if defined(__MACH_O)
460class MachO {
461 public:
462 explicit MachO(Zone* zone) : sections_(zone) {}
463
464 size_t AddSection(MachOSection* section) {
465 sections_.push_back(section);
466 return sections_.size() - 1;
467 }
468
469 void Write(Writer* w, uintptr_t code_start, uintptr_t code_size) {
470 Writer::Slot<MachOHeader> header = WriteHeader(w);
471 uintptr_t load_command_start = w->position();
472 Writer::Slot<MachOSegmentCommand> cmd =
473 WriteSegmentCommand(w, code_start, code_size);
474 WriteSections(w, cmd, header, load_command_start);
475 }
476
477 private:
478 struct MachOHeader {
479 uint32_t magic;
480 uint32_t cputype;
481 uint32_t cpusubtype;
482 uint32_t filetype;
483 uint32_t ncmds;
484 uint32_t sizeofcmds;
485 uint32_t flags;
486#if V8_TARGET_ARCH_X64
487 uint32_t reserved;
488#endif
489 };
490
491 struct MachOSegmentCommand {
492 uint32_t cmd;
493 uint32_t cmdsize;
494 char segname[16];
495#if V8_TARGET_ARCH_IA32
496 uint32_t vmaddr;
497 uint32_t vmsize;
498 uint32_t fileoff;
499 uint32_t filesize;
500#else
501 uint64_t vmaddr;
502 uint64_t vmsize;
503 uint64_t fileoff;
504 uint64_t filesize;
505#endif
506 uint32_t maxprot;
507 uint32_t initprot;
508 uint32_t nsects;
509 uint32_t flags;
510 };
511
512 enum MachOLoadCommandCmd {
513 LC_SEGMENT_32 = 0x00000001u,
514 LC_SEGMENT_64 = 0x00000019u
515 };
516
517 Writer::Slot<MachOHeader> WriteHeader(Writer* w) {
518 DCHECK_EQ(w->position(), 0);
519 Writer::Slot<MachOHeader> header = w->CreateSlotHere<MachOHeader>();
520#if V8_TARGET_ARCH_IA32
521 header->magic = 0xFEEDFACEu;
522 header->cputype = 7; // i386
523 header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL
524#elif V8_TARGET_ARCH_X64
525 header->magic = 0xFEEDFACFu;
526 header->cputype = 7 | 0x01000000; // i386 | 64-bit ABI
527 header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL
528 header->reserved = 0;
529#else
530#error Unsupported target architecture.
531#endif
532 header->filetype = 0x1; // MH_OBJECT
533 header->ncmds = 1;
534 header->sizeofcmds = 0;
535 header->flags = 0;
536 return header;
537 }
538
539 Writer::Slot<MachOSegmentCommand> WriteSegmentCommand(Writer* w,
540 uintptr_t code_start,
541 uintptr_t code_size) {
542 Writer::Slot<MachOSegmentCommand> cmd =
543 w->CreateSlotHere<MachOSegmentCommand>();
544#if V8_TARGET_ARCH_IA32
545 cmd->cmd = LC_SEGMENT_32;
546#else
547 cmd->cmd = LC_SEGMENT_64;
548#endif
549 cmd->vmaddr = code_start;
550 cmd->vmsize = code_size;
551 cmd->fileoff = 0;
552 cmd->filesize = 0;
553 cmd->maxprot = 7;
554 cmd->initprot = 7;
555 cmd->flags = 0;
556 cmd->nsects = static_cast<uint32_t>(sections_.size());
557 memset(cmd->segname, 0, 16);
558 cmd->cmdsize = sizeof(MachOSegmentCommand) +
559 sizeof(MachOSection::Header) * cmd->nsects;
560 return cmd;
561 }
562
563 void WriteSections(Writer* w, Writer::Slot<MachOSegmentCommand> cmd,
564 Writer::Slot<MachOHeader> header,
565 uintptr_t load_command_start) {
566 Writer::Slot<MachOSection::Header> headers =
567 w->CreateSlotsHere<MachOSection::Header>(
568 static_cast<uint32_t>(sections_.size()));
569 cmd->fileoff = w->position();
570 header->sizeofcmds =
571 static_cast<uint32_t>(w->position() - load_command_start);
572 uint32_t index = 0;
573 for (MachOSection* section : sections_) {
574 section->PopulateHeader(headers.at(index));
575 section->WriteBody(headers.at(index), w);
576 index++;
577 }
578 cmd->filesize = w->position() - (uintptr_t)cmd->fileoff;
579 }
580
582};
583#endif // defined(__MACH_O)
584
585#if defined(__ELF)
586class ELF {
587 public:
588 explicit ELF(Zone* zone) : sections_(zone) {
589 sections_.push_back(zone->New<ELFSection>("", ELFSection::TYPE_NULL, 0));
590 sections_.push_back(zone->New<ELFStringTable>(".shstrtab"));
591 }
592
593 void Write(Writer* w) {
594 WriteHeader(w);
595 WriteSectionTable(w);
596 WriteSections(w);
597 }
598
599 ELFSection* SectionAt(uint32_t index) { return *sections_.Find(index); }
600
601 size_t AddSection(ELFSection* section) {
602 sections_.push_back(section);
603 section->set_index(sections_.size() - 1);
604 return sections_.size() - 1;
605 }
606
607 private:
608 struct ELFHeader {
609 uint8_t ident[16];
610 uint16_t type;
611 uint16_t machine;
612 uint32_t version;
613 uintptr_t entry;
614 uintptr_t pht_offset;
615 uintptr_t sht_offset;
616 uint32_t flags;
617 uint16_t header_size;
618 uint16_t pht_entry_size;
619 uint16_t pht_entry_num;
620 uint16_t sht_entry_size;
621 uint16_t sht_entry_num;
622 uint16_t sht_strtab_index;
623 };
624
625 void WriteHeader(Writer* w) {
626 DCHECK_EQ(w->position(), 0);
627 Writer::Slot<ELFHeader> header = w->CreateSlotHere<ELFHeader>();
628#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM)
629 const uint8_t ident[16] = {0x7F, 'E', 'L', 'F', 1, 1, 1, 0,
630 0, 0, 0, 0, 0, 0, 0, 0};
631#elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_64_BIT || \
632 V8_TARGET_ARCH_PPC64 && V8_TARGET_LITTLE_ENDIAN
633 const uint8_t ident[16] = {0x7F, 'E', 'L', 'F', 2, 1, 1, 0,
634 0, 0, 0, 0, 0, 0, 0, 0};
635#elif V8_TARGET_ARCH_S390X
636 const uint8_t ident[16] = {0x7F, 'E', 'L', 'F', 2, 2, 1, 3,
637 0, 0, 0, 0, 0, 0, 0, 0};
638#else
639#error Unsupported target architecture.
640#endif
641 memcpy(header->ident, ident, 16);
642 header->type = 1;
643#if V8_TARGET_ARCH_IA32
644 header->machine = 3;
645#elif V8_TARGET_ARCH_X64
646 // Processor identification value for x64 is 62 as defined in
647 // System V ABI, AMD64 Supplement
648 // http://www.x86-64.org/documentation/abi.pdf
649 header->machine = 62;
650#elif V8_TARGET_ARCH_ARM
651 // Set to EM_ARM, defined as 40, in "ARM ELF File Format" at
652 // infocenter.arm.com/help/topic/com.arm.doc.dui0101a/DUI0101A_Elf.pdf
653 header->machine = 40;
654#elif V8_TARGET_ARCH_PPC64 && V8_OS_LINUX
655 // Set to EM_PPC64, defined as 21, in Power ABI,
656 // Join the next 4 lines, omitting the spaces and double-slashes.
657 // https://www-03.ibm.com/technologyconnect/tgcm/TGCMFileServlet.wss/
658 // ABI64BitOpenPOWERv1.1_16July2015_pub.pdf?
659 // id=B81AEC1A37F5DAF185257C3E004E8845&linkid=1n0000&c_t=
660 // c9xw7v5dzsj7gt1ifgf4cjbcnskqptmr
661 header->machine = 21;
662#elif V8_TARGET_ARCH_S390X
663 // Processor identification value is 22 (EM_S390) as defined in the ABI:
664 // http://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_s390.html#AEN1691
665 // http://refspecs.linuxbase.org/ELF/zSeries/lzsabi0_zSeries.html#AEN1599
666 header->machine = 22;
667#else
668#error Unsupported target architecture.
669#endif
670 header->version = 1;
671 header->entry = 0;
672 header->pht_offset = 0;
673 header->sht_offset = sizeof(ELFHeader); // Section table follows header.
674 header->flags = 0;
675 header->header_size = sizeof(ELFHeader);
676 header->pht_entry_size = 0;
677 header->pht_entry_num = 0;
678 header->sht_entry_size = sizeof(ELFSection::Header);
679 header->sht_entry_num = sections_.size();
680 header->sht_strtab_index = 1;
681 }
682
683 void WriteSectionTable(Writer* w) {
684 // Section headers table immediately follows file header.
685 DCHECK(w->position() == sizeof(ELFHeader));
686
687 Writer::Slot<ELFSection::Header> headers =
688 w->CreateSlotsHere<ELFSection::Header>(
689 static_cast<uint32_t>(sections_.size()));
690
691 // String table for section table is the first section.
692 ELFStringTable* strtab = static_cast<ELFStringTable*>(SectionAt(1));
693 strtab->AttachWriter(w);
694 uint32_t index = 0;
695 for (ELFSection* section : sections_) {
696 section->PopulateHeader(headers.at(index), strtab);
697 index++;
698 }
699 strtab->DetachWriter();
700 }
701
702 int SectionHeaderPosition(uint32_t section_index) {
703 return sizeof(ELFHeader) + sizeof(ELFSection::Header) * section_index;
704 }
705
706 void WriteSections(Writer* w) {
707 Writer::Slot<ELFSection::Header> headers =
708 w->SlotAt<ELFSection::Header>(sizeof(ELFHeader));
709
710 uint32_t index = 0;
711 for (ELFSection* section : sections_) {
712 section->WriteBody(headers.at(index), w);
713 index++;
714 }
715 }
716
718};
719
720class ELFSymbol {
721 public:
722 enum Type {
723 TYPE_NOTYPE = 0,
724 TYPE_OBJECT = 1,
725 TYPE_FUNC = 2,
726 TYPE_SECTION = 3,
727 TYPE_FILE = 4,
728 TYPE_LOPROC = 13,
729 TYPE_HIPROC = 15
730 };
731
732 enum Binding {
733 BIND_LOCAL = 0,
734 BIND_GLOBAL = 1,
735 BIND_WEAK = 2,
736 BIND_LOPROC = 13,
737 BIND_HIPROC = 15
738 };
739
740 ELFSymbol(const char* name, uintptr_t value, uintptr_t size, Binding binding,
741 Type type, uint16_t section)
742 : name(name),
743 value(value),
744 size(size),
745 info((binding << 4) | type),
746 other(0),
747 section(section) {}
748
749 Binding binding() const { return static_cast<Binding>(info >> 4); }
750#if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM)
751 struct SerializedLayout {
752 SerializedLayout(uint32_t name, uintptr_t value, uintptr_t size,
753 Binding binding, Type type, uint16_t section)
754 : name(name),
755 value(value),
756 size(size),
757 info((binding << 4) | type),
758 other(0),
759 section(section) {}
760
761 uint32_t name;
762 uintptr_t value;
763 uintptr_t size;
764 uint8_t info;
765 uint8_t other;
766 uint16_t section;
767 };
768#elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_64_BIT || \
769 V8_TARGET_ARCH_PPC64 && V8_OS_LINUX || V8_TARGET_ARCH_S390X
770 struct SerializedLayout {
771 SerializedLayout(uint32_t name, uintptr_t value, uintptr_t size,
772 Binding binding, Type type, uint16_t section)
773 : name(name),
774 info((binding << 4) | type),
775 other(0),
776 section(section),
777 value(value),
778 size(size) {}
779
780 uint32_t name;
781 uint8_t info;
782 uint8_t other;
783 uint16_t section;
784 uintptr_t value;
785 uintptr_t size;
786 };
787#endif
788
789 void Write(Writer::Slot<SerializedLayout> s, ELFStringTable* t) const {
790 // Convert symbol names from strings to indexes in the string table.
791 s->name = static_cast<uint32_t>(t->Add(name));
792 s->value = value;
793 s->size = size;
794 s->info = info;
795 s->other = other;
796 s->section = section;
797 }
798
799 private:
800 const char* name;
801 uintptr_t value;
802 uintptr_t size;
803 uint8_t info;
804 uint8_t other;
805 uint16_t section;
806};
807
808class ELFSymbolTable : public ELFSection {
809 public:
810 ELFSymbolTable(const char* name, Zone* zone)
811 : ELFSection(name, TYPE_SYMTAB, sizeof(uintptr_t)),
812 locals_(zone),
813 globals_(zone) {}
814
815 void WriteBody(Writer::Slot<Header> header, Writer* w) override {
816 w->Align(header->alignment);
817 size_t total_symbols = locals_.size() + globals_.size() + 1;
818 header->offset = w->position();
819
820 Writer::Slot<ELFSymbol::SerializedLayout> symbols =
821 w->CreateSlotsHere<ELFSymbol::SerializedLayout>(
822 static_cast<uint32_t>(total_symbols));
823
824 header->size = w->position() - header->offset;
825
826 // String table for this symbol table should follow it in the section table.
827 ELFStringTable* strtab =
828 static_cast<ELFStringTable*>(w->debug_object()->SectionAt(index() + 1));
829 strtab->AttachWriter(w);
830 symbols.at(0).set(ELFSymbol::SerializedLayout(
831 0, 0, 0, ELFSymbol::BIND_LOCAL, ELFSymbol::TYPE_NOTYPE, 0));
832 WriteSymbolsList(&locals_, symbols.at(1), strtab);
833 WriteSymbolsList(&globals_,
834 symbols.at(static_cast<uint32_t>(locals_.size() + 1)),
835 strtab);
836 strtab->DetachWriter();
837 }
838
839 void Add(const ELFSymbol& symbol) {
840 if (symbol.binding() == ELFSymbol::BIND_LOCAL) {
841 locals_.push_back(symbol);
842 } else {
843 globals_.push_back(symbol);
844 }
845 }
846
847 protected:
848 void PopulateHeader(Writer::Slot<Header> header) override {
849 ELFSection::PopulateHeader(header);
850 // We are assuming that string table will follow symbol table.
851 header->link = index() + 1;
852 header->info = static_cast<uint32_t>(locals_.size() + 1);
853 header->entry_size = sizeof(ELFSymbol::SerializedLayout);
854 }
855
856 private:
857 void WriteSymbolsList(const ZoneChunkList<ELFSymbol>* src,
858 Writer::Slot<ELFSymbol::SerializedLayout> dst,
859 ELFStringTable* strtab) {
860 int i = 0;
861 for (const ELFSymbol& symbol : *src) {
862 symbol.Write(dst.at(i++), strtab);
863 }
864 }
865
868};
869#endif // defined(__ELF)
870
871class LineInfo : public Malloced {
872 public:
873 void SetPosition(intptr_t pc, int pos, bool is_statement) {
874 AddPCInfo(PCInfo(pc, pos, is_statement));
875 }
876
877 struct PCInfo {
878 PCInfo(intptr_t pc, int pos, bool is_statement)
879 : pc_(pc), pos_(pos), is_statement_(is_statement) {}
880
881 intptr_t pc_;
882 int pos_;
883 bool is_statement_;
884 };
885
886 std::vector<PCInfo>* pc_info() { return &pc_info_; }
887
888 private:
889 void AddPCInfo(const PCInfo& pc_info) { pc_info_.push_back(pc_info); }
890
891 std::vector<PCInfo> pc_info_;
892};
893
894class CodeDescription {
895 public:
896#if V8_TARGET_ARCH_X64
897 enum StackState {
898 POST_RBP_PUSH,
899 POST_RBP_SET,
900 POST_RBP_POP,
901 STACK_STATE_MAX
902 };
903#endif
904
905 CodeDescription(const char* name, base::AddressRegion region,
906 Tagged<SharedFunctionInfo> shared, LineInfo* lineinfo,
907 bool is_function)
908 : name_(name),
909 shared_info_(shared),
910 lineinfo_(lineinfo),
911 is_function_(is_function),
912 code_region_(region) {}
913
914 const char* name() const { return name_; }
915
916 LineInfo* lineinfo() const { return lineinfo_; }
917
918 bool is_function() const { return is_function_; }
919
920 bool has_scope_info() const { return !shared_info_.is_null(); }
921
922 Tagged<ScopeInfo> scope_info() const {
923 DCHECK(has_scope_info());
924 return shared_info_->scope_info();
925 }
926
927 uintptr_t CodeStart() const { return code_region_.begin(); }
928
929 uintptr_t CodeEnd() const { return code_region_.end(); }
930
931 uintptr_t CodeSize() const { return code_region_.size(); }
932
933 bool has_script() {
934 return !shared_info_.is_null() && IsScript(shared_info_->script());
935 }
936
937 Tagged<Script> script() { return Cast<Script>(shared_info_->script()); }
938
939 bool IsLineInfoAvailable() { return lineinfo_ != nullptr; }
940
941 base::AddressRegion region() { return code_region_; }
942
943#if V8_TARGET_ARCH_X64
944 uintptr_t GetStackStateStartAddress(StackState state) const {
945 DCHECK(state < STACK_STATE_MAX);
946 return stack_state_start_addresses_[state];
947 }
948
949 void SetStackStateStartAddress(StackState state, uintptr_t addr) {
950 DCHECK(state < STACK_STATE_MAX);
951 stack_state_start_addresses_[state] = addr;
952 }
953#endif
954
955 std::unique_ptr<char[]> GetFilename() {
956 if (!shared_info_.is_null() && IsString(script()->name())) {
957 return Cast<String>(script()->name())->ToCString();
958 } else {
959 std::unique_ptr<char[]> result(new char[1]);
960 result[0] = 0;
961 return result;
962 }
963 }
964
965 int GetScriptLineNumber(int pos) {
966 if (!shared_info_.is_null()) {
967 return script()->GetLineNumber(pos) + 1;
968 } else {
969 return 0;
970 }
971 }
972
973 private:
974 const char* name_;
975 Tagged<SharedFunctionInfo> shared_info_;
976 LineInfo* lineinfo_;
977 bool is_function_;
978 base::AddressRegion code_region_;
979#if V8_TARGET_ARCH_X64
980 uintptr_t stack_state_start_addresses_[STACK_STATE_MAX];
981#endif
982};
983
984#if defined(__ELF)
985static void CreateSymbolsTable(CodeDescription* desc, Zone* zone, ELF* elf,
986 size_t text_section_index) {
987 ELFSymbolTable* symtab = zone->New<ELFSymbolTable>(".symtab", zone);
988 ELFStringTable* strtab = zone->New<ELFStringTable>(".strtab");
989
990 // Symbol table should be followed by the linked string table.
991 elf->AddSection(symtab);
992 elf->AddSection(strtab);
993
994 symtab->Add(ELFSymbol("V8 Code", 0, 0, ELFSymbol::BIND_LOCAL,
995 ELFSymbol::TYPE_FILE, ELFSection::INDEX_ABSOLUTE));
996
997 symtab->Add(ELFSymbol(desc->name(), 0, desc->CodeSize(),
998 ELFSymbol::BIND_GLOBAL, ELFSymbol::TYPE_FUNC,
999 text_section_index));
1000}
1001#endif // defined(__ELF)
1002
1003class DebugInfoSection : public DebugSection {
1004 public:
1005 explicit DebugInfoSection(CodeDescription* desc)
1006#if defined(__ELF)
1007 : ELFSection(".debug_info", TYPE_PROGBITS, 1),
1008#else
1009 : MachOSection("__debug_info", "__DWARF", 1,
1010 MachOSection::S_REGULAR | MachOSection::S_ATTR_DEBUG),
1011#endif
1012 desc_(desc) {
1013 }
1014
1015 // DWARF2 standard
1016 enum DWARF2LocationOp {
1017 DW_OP_reg0 = 0x50,
1018 DW_OP_reg1 = 0x51,
1019 DW_OP_reg2 = 0x52,
1020 DW_OP_reg3 = 0x53,
1021 DW_OP_reg4 = 0x54,
1022 DW_OP_reg5 = 0x55,
1023 DW_OP_reg6 = 0x56,
1024 DW_OP_reg7 = 0x57,
1025 DW_OP_reg8 = 0x58,
1026 DW_OP_reg9 = 0x59,
1027 DW_OP_reg10 = 0x5A,
1028 DW_OP_reg11 = 0x5B,
1029 DW_OP_reg12 = 0x5C,
1030 DW_OP_reg13 = 0x5D,
1031 DW_OP_reg14 = 0x5E,
1032 DW_OP_reg15 = 0x5F,
1033 DW_OP_reg16 = 0x60,
1034 DW_OP_reg17 = 0x61,
1035 DW_OP_reg18 = 0x62,
1036 DW_OP_reg19 = 0x63,
1037 DW_OP_reg20 = 0x64,
1038 DW_OP_reg21 = 0x65,
1039 DW_OP_reg22 = 0x66,
1040 DW_OP_reg23 = 0x67,
1041 DW_OP_reg24 = 0x68,
1042 DW_OP_reg25 = 0x69,
1043 DW_OP_reg26 = 0x6A,
1044 DW_OP_reg27 = 0x6B,
1045 DW_OP_reg28 = 0x6C,
1046 DW_OP_reg29 = 0x6D,
1047 DW_OP_reg30 = 0x6E,
1048 DW_OP_reg31 = 0x6F,
1049 DW_OP_fbreg = 0x91 // 1 param: SLEB128 offset
1050 };
1051
1052 enum DWARF2Encoding { DW_ATE_ADDRESS = 0x1, DW_ATE_SIGNED = 0x5 };
1053
1054 bool WriteBodyInternal(Writer* w) override {
1055 uintptr_t cu_start = w->position();
1056 Writer::Slot<uint32_t> size = w->CreateSlotHere<uint32_t>();
1057 uintptr_t start = w->position();
1058 w->Write<uint16_t>(2); // DWARF version.
1059 w->Write<uint32_t>(0); // Abbreviation table offset.
1060 w->Write<uint8_t>(sizeof(intptr_t));
1061
1062 w->WriteULEB128(1); // Abbreviation code.
1063 w->WriteString(desc_->GetFilename().get());
1064 w->Write<intptr_t>(desc_->CodeStart());
1065 w->Write<intptr_t>(desc_->CodeStart() + desc_->CodeSize());
1066 w->Write<uint32_t>(0);
1067
1068 uint32_t ty_offset = static_cast<uint32_t>(w->position() - cu_start);
1069 w->WriteULEB128(3);
1070 w->Write<uint8_t>(kSystemPointerSize);
1071 w->WriteString("v8value");
1072
1073 if (desc_->has_scope_info()) {
1074 Tagged<ScopeInfo> scope = desc_->scope_info();
1075 w->WriteULEB128(2);
1076 w->WriteString(desc_->name());
1077 w->Write<intptr_t>(desc_->CodeStart());
1078 w->Write<intptr_t>(desc_->CodeStart() + desc_->CodeSize());
1079 Writer::Slot<uint32_t> fb_block_size = w->CreateSlotHere<uint32_t>();
1080 uintptr_t fb_block_start = w->position();
1081#if V8_TARGET_ARCH_IA32
1082 w->Write<uint8_t>(DW_OP_reg5); // The frame pointer's here on ia32
1083#elif V8_TARGET_ARCH_X64
1084 w->Write<uint8_t>(DW_OP_reg6); // and here on x64.
1085#elif V8_TARGET_ARCH_ARM
1086 UNIMPLEMENTED();
1087#elif V8_TARGET_ARCH_MIPS
1088 UNIMPLEMENTED();
1089#elif V8_TARGET_ARCH_MIPS64
1090 UNIMPLEMENTED();
1091#elif V8_TARGET_ARCH_LOONG64
1092 UNIMPLEMENTED();
1093#elif V8_TARGET_ARCH_PPC64 && V8_OS_LINUX
1094 w->Write<uint8_t>(DW_OP_reg31); // The frame pointer is here on PPC64.
1095#elif V8_TARGET_ARCH_S390X
1096 w->Write<uint8_t>(DW_OP_reg11); // The frame pointer's here on S390.
1097#else
1098#error Unsupported target architecture.
1099#endif
1100 fb_block_size.set(static_cast<uint32_t>(w->position() - fb_block_start));
1101
1102 int params = scope->ParameterCount();
1103 int context_slots = scope->ContextLocalCount();
1104 // The real slot ID is internal_slots + context_slot_id.
1105 int internal_slots = scope->ContextHeaderLength();
1106 int current_abbreviation = 4;
1107
1108 for (int param = 0; param < params; ++param) {
1109 w->WriteULEB128(current_abbreviation++);
1110 w->WriteString("param");
1111 w->Write(std::to_string(param).c_str());
1112 w->Write<uint32_t>(ty_offset);
1113 Writer::Slot<uint32_t> block_size = w->CreateSlotHere<uint32_t>();
1114 uintptr_t block_start = w->position();
1115 w->Write<uint8_t>(DW_OP_fbreg);
1116 w->WriteSLEB128(StandardFrameConstants::kFixedFrameSizeAboveFp +
1117 kSystemPointerSize * (params - param - 1));
1118 block_size.set(static_cast<uint32_t>(w->position() - block_start));
1119 }
1120
1121 // See contexts.h for more information.
1122 DCHECK(internal_slots == 2 || internal_slots == 3);
1123 DCHECK_EQ(Context::SCOPE_INFO_INDEX, 0);
1124 DCHECK_EQ(Context::PREVIOUS_INDEX, 1);
1125 DCHECK_EQ(Context::EXTENSION_INDEX, 2);
1126 w->WriteULEB128(current_abbreviation++);
1127 w->WriteString(".scope_info");
1128 w->WriteULEB128(current_abbreviation++);
1129 w->WriteString(".previous");
1130 if (internal_slots == 3) {
1131 w->WriteULEB128(current_abbreviation++);
1132 w->WriteString(".extension");
1133 }
1134
1135 for (int context_slot = 0; context_slot < context_slots; ++context_slot) {
1136 w->WriteULEB128(current_abbreviation++);
1137 w->WriteString("context_slot");
1138 w->Write(std::to_string(context_slot + internal_slots).c_str());
1139 }
1140
1141 {
1142 w->WriteULEB128(current_abbreviation++);
1143 w->WriteString("__function");
1144 w->Write<uint32_t>(ty_offset);
1145 Writer::Slot<uint32_t> block_size = w->CreateSlotHere<uint32_t>();
1146 uintptr_t block_start = w->position();
1147 w->Write<uint8_t>(DW_OP_fbreg);
1148 w->WriteSLEB128(StandardFrameConstants::kFunctionOffset);
1149 block_size.set(static_cast<uint32_t>(w->position() - block_start));
1150 }
1151
1152 {
1153 w->WriteULEB128(current_abbreviation++);
1154 w->WriteString("__context");
1155 w->Write<uint32_t>(ty_offset);
1156 Writer::Slot<uint32_t> block_size = w->CreateSlotHere<uint32_t>();
1157 uintptr_t block_start = w->position();
1158 w->Write<uint8_t>(DW_OP_fbreg);
1159 w->WriteSLEB128(StandardFrameConstants::kContextOffset);
1160 block_size.set(static_cast<uint32_t>(w->position() - block_start));
1161 }
1162
1163 w->WriteULEB128(0); // Terminate the sub program.
1164 }
1165
1166 w->WriteULEB128(0); // Terminate the compile unit.
1167 size.set(static_cast<uint32_t>(w->position() - start));
1168 return true;
1169 }
1170
1171 private:
1172 CodeDescription* desc_;
1173};
1174
1175class DebugAbbrevSection : public DebugSection {
1176 public:
1177 explicit DebugAbbrevSection(CodeDescription* desc)
1178#ifdef __ELF
1179 : ELFSection(".debug_abbrev", TYPE_PROGBITS, 1),
1180#else
1181 : MachOSection("__debug_abbrev", "__DWARF", 1,
1182 MachOSection::S_REGULAR | MachOSection::S_ATTR_DEBUG),
1183#endif
1184 desc_(desc) {
1185 }
1186
1187 // DWARF2 standard, figure 14.
1188 enum DWARF2Tags {
1189 DW_TAG_FORMAL_PARAMETER = 0x05,
1190 DW_TAG_POINTER_TYPE = 0xF,
1191 DW_TAG_COMPILE_UNIT = 0x11,
1192 DW_TAG_STRUCTURE_TYPE = 0x13,
1193 DW_TAG_BASE_TYPE = 0x24,
1194 DW_TAG_SUBPROGRAM = 0x2E,
1195 DW_TAG_VARIABLE = 0x34
1196 };
1197
1198 // DWARF2 standard, figure 16.
1199 enum DWARF2ChildrenDetermination { DW_CHILDREN_NO = 0, DW_CHILDREN_YES = 1 };
1200
1201 // DWARF standard, figure 17.
1202 enum DWARF2Attribute {
1203 DW_AT_LOCATION = 0x2,
1204 DW_AT_NAME = 0x3,
1205 DW_AT_BYTE_SIZE = 0xB,
1206 DW_AT_STMT_LIST = 0x10,
1207 DW_AT_LOW_PC = 0x11,
1208 DW_AT_HIGH_PC = 0x12,
1209 DW_AT_ENCODING = 0x3E,
1210 DW_AT_FRAME_BASE = 0x40,
1211 DW_AT_TYPE = 0x49
1212 };
1213
1214 // DWARF2 standard, figure 19.
1215 enum DWARF2AttributeForm {
1216 DW_FORM_ADDR = 0x1,
1217 DW_FORM_BLOCK4 = 0x4,
1218 DW_FORM_STRING = 0x8,
1219 DW_FORM_DATA4 = 0x6,
1220 DW_FORM_BLOCK = 0x9,
1221 DW_FORM_DATA1 = 0xB,
1222 DW_FORM_FLAG = 0xC,
1223 DW_FORM_REF4 = 0x13
1224 };
1225
1226 void WriteVariableAbbreviation(Writer* w, int abbreviation_code,
1227 bool has_value, bool is_parameter) {
1228 w->WriteULEB128(abbreviation_code);
1229 w->WriteULEB128(is_parameter ? DW_TAG_FORMAL_PARAMETER : DW_TAG_VARIABLE);
1230 w->Write<uint8_t>(DW_CHILDREN_NO);
1231 w->WriteULEB128(DW_AT_NAME);
1232 w->WriteULEB128(DW_FORM_STRING);
1233 if (has_value) {
1234 w->WriteULEB128(DW_AT_TYPE);
1235 w->WriteULEB128(DW_FORM_REF4);
1236 w->WriteULEB128(DW_AT_LOCATION);
1237 w->WriteULEB128(DW_FORM_BLOCK4);
1238 }
1239 w->WriteULEB128(0);
1240 w->WriteULEB128(0);
1241 }
1242
1243 bool WriteBodyInternal(Writer* w) override {
1244 int current_abbreviation = 1;
1245 bool extra_info = desc_->has_scope_info();
1246 DCHECK(desc_->IsLineInfoAvailable());
1247 w->WriteULEB128(current_abbreviation++);
1248 w->WriteULEB128(DW_TAG_COMPILE_UNIT);
1249 w->Write<uint8_t>(extra_info ? DW_CHILDREN_YES : DW_CHILDREN_NO);
1250 w->WriteULEB128(DW_AT_NAME);
1251 w->WriteULEB128(DW_FORM_STRING);
1252 w->WriteULEB128(DW_AT_LOW_PC);
1253 w->WriteULEB128(DW_FORM_ADDR);
1254 w->WriteULEB128(DW_AT_HIGH_PC);
1255 w->WriteULEB128(DW_FORM_ADDR);
1256 w->WriteULEB128(DW_AT_STMT_LIST);
1257 w->WriteULEB128(DW_FORM_DATA4);
1258 w->WriteULEB128(0);
1259 w->WriteULEB128(0);
1260
1261 if (extra_info) {
1262 Tagged<ScopeInfo> scope = desc_->scope_info();
1263 int params = scope->ParameterCount();
1264 int context_slots = scope->ContextLocalCount();
1265 // The real slot ID is internal_slots + context_slot_id.
1266 int internal_slots = Context::MIN_CONTEXT_SLOTS;
1267 // Total children is params + context_slots + internal_slots + 2
1268 // (__function and __context).
1269
1270 // The extra duplication below seems to be necessary to keep
1271 // gdb from getting upset on OSX.
1272 w->WriteULEB128(current_abbreviation++); // Abbreviation code.
1273 w->WriteULEB128(DW_TAG_SUBPROGRAM);
1274 w->Write<uint8_t>(DW_CHILDREN_YES);
1275 w->WriteULEB128(DW_AT_NAME);
1276 w->WriteULEB128(DW_FORM_STRING);
1277 w->WriteULEB128(DW_AT_LOW_PC);
1278 w->WriteULEB128(DW_FORM_ADDR);
1279 w->WriteULEB128(DW_AT_HIGH_PC);
1280 w->WriteULEB128(DW_FORM_ADDR);
1281 w->WriteULEB128(DW_AT_FRAME_BASE);
1282 w->WriteULEB128(DW_FORM_BLOCK4);
1283 w->WriteULEB128(0);
1284 w->WriteULEB128(0);
1285
1286 w->WriteULEB128(current_abbreviation++);
1287 w->WriteULEB128(DW_TAG_STRUCTURE_TYPE);
1288 w->Write<uint8_t>(DW_CHILDREN_NO);
1289 w->WriteULEB128(DW_AT_BYTE_SIZE);
1290 w->WriteULEB128(DW_FORM_DATA1);
1291 w->WriteULEB128(DW_AT_NAME);
1292 w->WriteULEB128(DW_FORM_STRING);
1293 w->WriteULEB128(0);
1294 w->WriteULEB128(0);
1295
1296 for (int param = 0; param < params; ++param) {
1297 WriteVariableAbbreviation(w, current_abbreviation++, true, true);
1298 }
1299
1300 for (int internal_slot = 0; internal_slot < internal_slots;
1301 ++internal_slot) {
1302 WriteVariableAbbreviation(w, current_abbreviation++, false, false);
1303 }
1304
1305 for (int context_slot = 0; context_slot < context_slots; ++context_slot) {
1306 WriteVariableAbbreviation(w, current_abbreviation++, false, false);
1307 }
1308
1309 // The function.
1310 WriteVariableAbbreviation(w, current_abbreviation++, true, false);
1311
1312 // The context.
1313 WriteVariableAbbreviation(w, current_abbreviation++, true, false);
1314
1315 w->WriteULEB128(0); // Terminate the sibling list.
1316 }
1317
1318 w->WriteULEB128(0); // Terminate the table.
1319 return true;
1320 }
1321
1322 private:
1323 CodeDescription* desc_;
1324};
1325
1326class DebugLineSection : public DebugSection {
1327 public:
1328 explicit DebugLineSection(CodeDescription* desc)
1329#ifdef __ELF
1330 : ELFSection(".debug_line", TYPE_PROGBITS, 1),
1331#else
1332 : MachOSection("__debug_line", "__DWARF", 1,
1333 MachOSection::S_REGULAR | MachOSection::S_ATTR_DEBUG),
1334#endif
1335 desc_(desc) {
1336 }
1337
1338 // DWARF2 standard, figure 34.
1339 enum DWARF2Opcodes {
1340 DW_LNS_COPY = 1,
1341 DW_LNS_ADVANCE_PC = 2,
1342 DW_LNS_ADVANCE_LINE = 3,
1343 DW_LNS_SET_FILE = 4,
1344 DW_LNS_SET_COLUMN = 5,
1345 DW_LNS_NEGATE_STMT = 6
1346 };
1347
1348 // DWARF2 standard, figure 35.
1349 enum DWARF2ExtendedOpcode {
1350 DW_LNE_END_SEQUENCE = 1,
1351 DW_LNE_SET_ADDRESS = 2,
1352 DW_LNE_DEFINE_FILE = 3
1353 };
1354
1355 bool WriteBodyInternal(Writer* w) override {
1356 // Write prologue.
1357 Writer::Slot<uint32_t> total_length = w->CreateSlotHere<uint32_t>();
1358 uintptr_t start = w->position();
1359
1360 // Used for special opcodes
1361 const int8_t line_base = 1;
1362 const uint8_t line_range = 7;
1363 const int8_t max_line_incr = (line_base + line_range - 1);
1364 const uint8_t opcode_base = DW_LNS_NEGATE_STMT + 1;
1365
1366 w->Write<uint16_t>(2); // Field version.
1367 Writer::Slot<uint32_t> prologue_length = w->CreateSlotHere<uint32_t>();
1368 uintptr_t prologue_start = w->position();
1369 w->Write<uint8_t>(1); // Field minimum_instruction_length.
1370 w->Write<uint8_t>(1); // Field default_is_stmt.
1371 w->Write<int8_t>(line_base); // Field line_base.
1372 w->Write<uint8_t>(line_range); // Field line_range.
1373 w->Write<uint8_t>(opcode_base); // Field opcode_base.
1374 w->Write<uint8_t>(0); // DW_LNS_COPY operands count.
1375 w->Write<uint8_t>(1); // DW_LNS_ADVANCE_PC operands count.
1376 w->Write<uint8_t>(1); // DW_LNS_ADVANCE_LINE operands count.
1377 w->Write<uint8_t>(1); // DW_LNS_SET_FILE operands count.
1378 w->Write<uint8_t>(1); // DW_LNS_SET_COLUMN operands count.
1379 w->Write<uint8_t>(0); // DW_LNS_NEGATE_STMT operands count.
1380 w->Write<uint8_t>(0); // Empty include_directories sequence.
1381 w->WriteString(desc_->GetFilename().get()); // File name.
1382 w->WriteULEB128(0); // Current directory.
1383 w->WriteULEB128(0); // Unknown modification time.
1384 w->WriteULEB128(0); // Unknown file size.
1385 w->Write<uint8_t>(0);
1386 prologue_length.set(static_cast<uint32_t>(w->position() - prologue_start));
1387
1388 WriteExtendedOpcode(w, DW_LNE_SET_ADDRESS, sizeof(intptr_t));
1389 w->Write<intptr_t>(desc_->CodeStart());
1390 w->Write<uint8_t>(DW_LNS_COPY);
1391
1392 intptr_t pc = 0;
1393 intptr_t line = 1;
1394 bool is_statement = true;
1395
1396 std::vector<LineInfo::PCInfo>* pc_info = desc_->lineinfo()->pc_info();
1397 std::sort(pc_info->begin(), pc_info->end(), &ComparePCInfo);
1398
1399 for (size_t i = 0; i < pc_info->size(); i++) {
1400 LineInfo::PCInfo* info = &pc_info->at(i);
1401 DCHECK(info->pc_ >= pc);
1402
1403 // Reduce bloating in the debug line table by removing duplicate line
1404 // entries (per DWARF2 standard).
1405 intptr_t new_line = desc_->GetScriptLineNumber(info->pos_);
1406 if (new_line == line) {
1407 continue;
1408 }
1409
1410 // Mark statement boundaries. For a better debugging experience, mark
1411 // the last pc address in the function as a statement (e.g. "}"), so that
1412 // a user can see the result of the last line executed in the function,
1413 // should control reach the end.
1414 if ((i + 1) == pc_info->size()) {
1415 if (!is_statement) {
1416 w->Write<uint8_t>(DW_LNS_NEGATE_STMT);
1417 }
1418 } else if (is_statement != info->is_statement_) {
1419 w->Write<uint8_t>(DW_LNS_NEGATE_STMT);
1420 is_statement = !is_statement;
1421 }
1422
1423 // Generate special opcodes, if possible. This results in more compact
1424 // debug line tables. See the DWARF 2.0 standard to learn more about
1425 // special opcodes.
1426 uintptr_t pc_diff = info->pc_ - pc;
1427 intptr_t line_diff = new_line - line;
1428
1429 // Compute special opcode (see DWARF 2.0 standard)
1430 intptr_t special_opcode =
1431 (line_diff - line_base) + (line_range * pc_diff) + opcode_base;
1432
1433 // If special_opcode is less than or equal to 255, it can be used as a
1434 // special opcode. If line_diff is larger than the max line increment
1435 // allowed for a special opcode, or if line_diff is less than the minimum
1436 // line that can be added to the line register (i.e. line_base), then
1437 // special_opcode can't be used.
1438 if ((special_opcode >= opcode_base) && (special_opcode <= 255) &&
1439 (line_diff <= max_line_incr) && (line_diff >= line_base)) {
1440 w->Write<uint8_t>(special_opcode);
1441 } else {
1442 w->Write<uint8_t>(DW_LNS_ADVANCE_PC);
1443 w->WriteSLEB128(pc_diff);
1444 w->Write<uint8_t>(DW_LNS_ADVANCE_LINE);
1445 w->WriteSLEB128(line_diff);
1446 w->Write<uint8_t>(DW_LNS_COPY);
1447 }
1448
1449 // Increment the pc and line operands.
1450 pc += pc_diff;
1451 line += line_diff;
1452 }
1453 // Advance the pc to the end of the routine, since the end sequence opcode
1454 // requires this.
1455 w->Write<uint8_t>(DW_LNS_ADVANCE_PC);
1456 w->WriteSLEB128(desc_->CodeSize() - pc);
1457 WriteExtendedOpcode(w, DW_LNE_END_SEQUENCE, 0);
1458 total_length.set(static_cast<uint32_t>(w->position() - start));
1459 return true;
1460 }
1461
1462 private:
1463 void WriteExtendedOpcode(Writer* w, DWARF2ExtendedOpcode op,
1464 size_t operands_size) {
1465 w->Write<uint8_t>(0);
1466 w->WriteULEB128(operands_size + 1);
1467 w->Write<uint8_t>(op);
1468 }
1469
1470 static bool ComparePCInfo(const LineInfo::PCInfo& a,
1471 const LineInfo::PCInfo& b) {
1472 if (a.pc_ == b.pc_) {
1473 if (a.is_statement_ != b.is_statement_) {
1474 return !b.is_statement_;
1475 }
1476 return false;
1477 }
1478 return a.pc_ < b.pc_;
1479 }
1480
1481 CodeDescription* desc_;
1482};
1483
1484#if V8_TARGET_ARCH_X64
1485
1486class UnwindInfoSection : public DebugSection {
1487 public:
1488 explicit UnwindInfoSection(CodeDescription* desc);
1489 bool WriteBodyInternal(Writer* w) override;
1490
1491 int WriteCIE(Writer* w);
1492 void WriteFDE(Writer* w, int);
1493
1494 void WriteFDEStateOnEntry(Writer* w);
1495 void WriteFDEStateAfterRBPPush(Writer* w);
1496 void WriteFDEStateAfterRBPSet(Writer* w);
1497 void WriteFDEStateAfterRBPPop(Writer* w);
1498
1499 void WriteLength(Writer* w, Writer::Slot<uint32_t>* length_slot,
1500 int initial_position);
1501
1502 private:
1503 CodeDescription* desc_;
1504
1505 // DWARF3 Specification, Table 7.23
1506 enum CFIInstructions {
1507 DW_CFA_ADVANCE_LOC = 0x40,
1508 DW_CFA_OFFSET = 0x80,
1509 DW_CFA_RESTORE = 0xC0,
1510 DW_CFA_NOP = 0x00,
1511 DW_CFA_SET_LOC = 0x01,
1512 DW_CFA_ADVANCE_LOC1 = 0x02,
1513 DW_CFA_ADVANCE_LOC2 = 0x03,
1514 DW_CFA_ADVANCE_LOC4 = 0x04,
1515 DW_CFA_OFFSET_EXTENDED = 0x05,
1516 DW_CFA_RESTORE_EXTENDED = 0x06,
1517 DW_CFA_UNDEFINED = 0x07,
1518 DW_CFA_SAME_VALUE = 0x08,
1519 DW_CFA_REGISTER = 0x09,
1520 DW_CFA_REMEMBER_STATE = 0x0A,
1521 DW_CFA_RESTORE_STATE = 0x0B,
1522 DW_CFA_DEF_CFA = 0x0C,
1523 DW_CFA_DEF_CFA_REGISTER = 0x0D,
1524 DW_CFA_DEF_CFA_OFFSET = 0x0E,
1525
1526 DW_CFA_DEF_CFA_EXPRESSION = 0x0F,
1527 DW_CFA_EXPRESSION = 0x10,
1528 DW_CFA_OFFSET_EXTENDED_SF = 0x11,
1529 DW_CFA_DEF_CFA_SF = 0x12,
1530 DW_CFA_DEF_CFA_OFFSET_SF = 0x13,
1531 DW_CFA_VAL_OFFSET = 0x14,
1532 DW_CFA_VAL_OFFSET_SF = 0x15,
1533 DW_CFA_VAL_EXPRESSION = 0x16
1534 };
1535
1536 // System V ABI, AMD64 Supplement, Version 0.99.5, Figure 3.36
1537 enum RegisterMapping {
1538 // Only the relevant ones have been added to reduce clutter.
1539 AMD64_RBP = 6,
1540 AMD64_RSP = 7,
1541 AMD64_RA = 16
1542 };
1543
1544 enum CFIConstants {
1545 CIE_ID = 0,
1546 CIE_VERSION = 1,
1547 CODE_ALIGN_FACTOR = 1,
1548 DATA_ALIGN_FACTOR = 1,
1549 RETURN_ADDRESS_REGISTER = AMD64_RA
1550 };
1551};
1552
1553void UnwindInfoSection::WriteLength(Writer* w,
1554 Writer::Slot<uint32_t>* length_slot,
1555 int initial_position) {
1556 uint32_t align = (w->position() - initial_position) % kSystemPointerSize;
1557
1558 if (align != 0) {
1559 for (uint32_t i = 0; i < (kSystemPointerSize - align); i++) {
1560 w->Write<uint8_t>(DW_CFA_NOP);
1561 }
1562 }
1563
1564 DCHECK_EQ((w->position() - initial_position) % kSystemPointerSize, 0);
1565 length_slot->set(static_cast<uint32_t>(w->position() - initial_position));
1566}
1567
1568UnwindInfoSection::UnwindInfoSection(CodeDescription* desc)
1569#ifdef __ELF
1570 : ELFSection(".eh_frame", TYPE_X86_64_UNWIND, 1),
1571#else
1572 : MachOSection("__eh_frame", "__TEXT", sizeof(uintptr_t),
1573 MachOSection::S_REGULAR),
1574#endif
1575 desc_(desc) {
1576}
1577
1578int UnwindInfoSection::WriteCIE(Writer* w) {
1579 Writer::Slot<uint32_t> cie_length_slot = w->CreateSlotHere<uint32_t>();
1580 uint32_t cie_position = static_cast<uint32_t>(w->position());
1581
1582 // Write out the CIE header. Currently no 'common instructions' are
1583 // emitted onto the CIE; every FDE has its own set of instructions.
1584
1585 w->Write<uint32_t>(CIE_ID);
1586 w->Write<uint8_t>(CIE_VERSION);
1587 w->Write<uint8_t>(0); // Null augmentation string.
1588 w->WriteSLEB128(CODE_ALIGN_FACTOR);
1589 w->WriteSLEB128(DATA_ALIGN_FACTOR);
1590 w->Write<uint8_t>(RETURN_ADDRESS_REGISTER);
1591
1592 WriteLength(w, &cie_length_slot, cie_position);
1593
1594 return cie_position;
1595}
1596
1597void UnwindInfoSection::WriteFDE(Writer* w, int cie_position) {
1598 // The only FDE for this function. The CFA is the current RBP.
1599 Writer::Slot<uint32_t> fde_length_slot = w->CreateSlotHere<uint32_t>();
1600 int fde_position = static_cast<uint32_t>(w->position());
1601 w->Write<int32_t>(fde_position - cie_position + 4);
1602
1603 w->Write<uintptr_t>(desc_->CodeStart());
1604 w->Write<uintptr_t>(desc_->CodeSize());
1605
1606 WriteFDEStateOnEntry(w);
1607 WriteFDEStateAfterRBPPush(w);
1608 WriteFDEStateAfterRBPSet(w);
1609 WriteFDEStateAfterRBPPop(w);
1610
1611 WriteLength(w, &fde_length_slot, fde_position);
1612}
1613
1614void UnwindInfoSection::WriteFDEStateOnEntry(Writer* w) {
1615 // The first state, just after the control has been transferred to the the
1616 // function.
1617
1618 // RBP for this function will be the value of RSP after pushing the RBP
1619 // for the previous function. The previous RBP has not been pushed yet.
1620 w->Write<uint8_t>(DW_CFA_DEF_CFA_SF);
1621 w->WriteULEB128(AMD64_RSP);
1622 w->WriteSLEB128(-kSystemPointerSize);
1623
1624 // The RA is stored at location CFA + kCallerPCOffset. This is an invariant,
1625 // and hence omitted from the next states.
1626 w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED);
1627 w->WriteULEB128(AMD64_RA);
1629
1630 // The RBP of the previous function is still in RBP.
1631 w->Write<uint8_t>(DW_CFA_SAME_VALUE);
1632 w->WriteULEB128(AMD64_RBP);
1633
1634 // Last location described by this entry.
1635 w->Write<uint8_t>(DW_CFA_SET_LOC);
1636 w->Write<uint64_t>(
1637 desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_PUSH));
1638}
1639
1640void UnwindInfoSection::WriteFDEStateAfterRBPPush(Writer* w) {
1641 // The second state, just after RBP has been pushed.
1642
1643 // RBP / CFA for this function is now the current RSP, so just set the
1644 // offset from the previous rule (from -8) to 0.
1645 w->Write<uint8_t>(DW_CFA_DEF_CFA_OFFSET);
1646 w->WriteULEB128(0);
1647
1648 // The previous RBP is stored at CFA + kCallerFPOffset. This is an invariant
1649 // in this and the next state, and hence omitted in the next state.
1650 w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED);
1651 w->WriteULEB128(AMD64_RBP);
1653
1654 // Last location described by this entry.
1655 w->Write<uint8_t>(DW_CFA_SET_LOC);
1656 w->Write<uint64_t>(
1657 desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_SET));
1658}
1659
1660void UnwindInfoSection::WriteFDEStateAfterRBPSet(Writer* w) {
1661 // The third state, after the RBP has been set.
1662
1663 // The CFA can now directly be set to RBP.
1664 w->Write<uint8_t>(DW_CFA_DEF_CFA);
1665 w->WriteULEB128(AMD64_RBP);
1666 w->WriteULEB128(0);
1667
1668 // Last location described by this entry.
1669 w->Write<uint8_t>(DW_CFA_SET_LOC);
1670 w->Write<uint64_t>(
1671 desc_->GetStackStateStartAddress(CodeDescription::POST_RBP_POP));
1672}
1673
1674void UnwindInfoSection::WriteFDEStateAfterRBPPop(Writer* w) {
1675 // The fourth (final) state. The RBP has been popped (just before issuing a
1676 // return).
1677
1678 // The CFA can is now calculated in the same way as in the first state.
1679 w->Write<uint8_t>(DW_CFA_DEF_CFA_SF);
1680 w->WriteULEB128(AMD64_RSP);
1681 w->WriteSLEB128(-kSystemPointerSize);
1682
1683 // The RBP
1684 w->Write<uint8_t>(DW_CFA_OFFSET_EXTENDED);
1685 w->WriteULEB128(AMD64_RBP);
1687
1688 // Last location described by this entry.
1689 w->Write<uint8_t>(DW_CFA_SET_LOC);
1690 w->Write<uint64_t>(desc_->CodeEnd());
1691}
1692
1693bool UnwindInfoSection::WriteBodyInternal(Writer* w) {
1694 uint32_t cie_position = WriteCIE(w);
1695 WriteFDE(w, cie_position);
1696 return true;
1697}
1698
1699#endif // V8_TARGET_ARCH_X64
1700
1701static void CreateDWARFSections(CodeDescription* desc, Zone* zone,
1702 DebugObject* obj) {
1703 if (desc->IsLineInfoAvailable()) {
1704 obj->AddSection(zone->New<DebugInfoSection>(desc));
1705 obj->AddSection(zone->New<DebugAbbrevSection>(desc));
1706 obj->AddSection(zone->New<DebugLineSection>(desc));
1707 }
1708#if V8_TARGET_ARCH_X64
1709 obj->AddSection(zone->New<UnwindInfoSection>(desc));
1710#endif
1711}
1712
1713// -------------------------------------------------------------------
1714// Binary GDB JIT Interface as described in
1715// http://sourceware.org/gdb/onlinedocs/gdb/Declarations.html
1716extern "C" {
1717enum JITAction { JIT_NOACTION = 0, JIT_REGISTER_FN, JIT_UNREGISTER_FN };
1718
1719struct JITCodeEntry {
1720 JITCodeEntry* next_;
1721 JITCodeEntry* prev_;
1722 Address symfile_addr_;
1723 uint64_t symfile_size_;
1724};
1725
1726struct JITDescriptor {
1727 uint32_t version_;
1728 uint32_t action_flag_;
1729 JITCodeEntry* relevant_entry_;
1730 JITCodeEntry* first_entry_;
1731};
1732
1733// GDB will place breakpoint into this function.
1734// To prevent GCC from inlining or removing it we place noinline attribute
1735// and inline assembler statement inside.
1736void __attribute__((noinline)) __jit_debug_register_code() { __asm__(""); }
1737
1738// GDB will inspect contents of this descriptor.
1739// Static initialization is necessary to prevent GDB from seeing
1740// uninitialized descriptor.
1741JITDescriptor __jit_debug_descriptor = {1, 0, nullptr, nullptr};
1742
1743#ifdef OBJECT_PRINT
1744void __gdb_print_v8_object(TaggedBase object) {
1745 StdoutStream os;
1746 Print(object, os);
1747 os << std::flush;
1748}
1749#endif
1750}
1751
1752static JITCodeEntry* CreateCodeEntry(Address symfile_addr,
1753 uintptr_t symfile_size) {
1754 JITCodeEntry* entry = static_cast<JITCodeEntry*>(
1755 base::Malloc(sizeof(JITCodeEntry) + symfile_size));
1756
1757 entry->symfile_addr_ = reinterpret_cast<Address>(entry + 1);
1758 entry->symfile_size_ = symfile_size;
1759 MemCopy(reinterpret_cast<void*>(entry->symfile_addr_),
1760 reinterpret_cast<void*>(symfile_addr), symfile_size);
1761
1762 entry->prev_ = entry->next_ = nullptr;
1763
1764 return entry;
1765}
1766
1767static void DestroyCodeEntry(JITCodeEntry* entry) { base::Free(entry); }
1768
1769static void RegisterCodeEntry(JITCodeEntry* entry) {
1770 entry->next_ = __jit_debug_descriptor.first_entry_;
1771 if (entry->next_ != nullptr) entry->next_->prev_ = entry;
1772 __jit_debug_descriptor.first_entry_ = __jit_debug_descriptor.relevant_entry_ =
1773 entry;
1774
1775 __jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
1776 __jit_debug_register_code();
1777}
1778
1779static void UnregisterCodeEntry(JITCodeEntry* entry) {
1780 if (entry->prev_ != nullptr) {
1781 entry->prev_->next_ = entry->next_;
1782 } else {
1783 __jit_debug_descriptor.first_entry_ = entry->next_;
1784 }
1785
1786 if (entry->next_ != nullptr) {
1787 entry->next_->prev_ = entry->prev_;
1788 }
1789
1790 __jit_debug_descriptor.relevant_entry_ = entry;
1791 __jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
1792 __jit_debug_register_code();
1793}
1794
1795static JITCodeEntry* CreateELFObject(CodeDescription* desc, Isolate* isolate) {
1796#ifdef __MACH_O
1797 Zone zone(isolate->allocator(), ZONE_NAME);
1798 MachO mach_o(&zone);
1799 Writer w(&mach_o);
1800
1801 const uint32_t code_alignment = static_cast<uint32_t>(kCodeAlignment);
1802 static_assert(code_alignment == kCodeAlignment,
1803 "Unsupported code alignment value");
1804 mach_o.AddSection(zone.New<MachOTextSection>(
1805 code_alignment, desc->CodeStart(), desc->CodeSize()));
1806
1807 CreateDWARFSections(desc, &zone, &mach_o);
1808
1809 mach_o.Write(&w, desc->CodeStart(), desc->CodeSize());
1810#else
1811 Zone zone(isolate->allocator(), ZONE_NAME);
1812 ELF elf(&zone);
1813 Writer w(&elf);
1814
1815 size_t text_section_index = elf.AddSection(zone.New<FullHeaderELFSection>(
1816 ".text", ELFSection::TYPE_NOBITS, kCodeAlignment, desc->CodeStart(), 0,
1817 desc->CodeSize(), ELFSection::FLAG_ALLOC | ELFSection::FLAG_EXEC));
1818
1819 CreateSymbolsTable(desc, &zone, &elf, text_section_index);
1820
1821 CreateDWARFSections(desc, &zone, &elf);
1822
1823 elf.Write(&w);
1824#endif
1825
1826 return CreateCodeEntry(reinterpret_cast<Address>(w.buffer()), w.position());
1827}
1828
1829// Like base::AddressRegion::StartAddressLess but also compares |end| when
1830// |begin| is equal.
1831struct AddressRegionLess {
1832 bool operator()(const base::AddressRegion& a,
1833 const base::AddressRegion& b) const {
1834 if (a.begin() == b.begin()) return a.end() < b.end();
1835 return a.begin() < b.begin();
1836 }
1837};
1838
1839using CodeMap = std::map<base::AddressRegion, JITCodeEntry*, AddressRegionLess>;
1840
1841static CodeMap* GetCodeMap() {
1842 // TODO(jgruber): Don't leak.
1843 static CodeMap* code_map = nullptr;
1844 if (code_map == nullptr) code_map = new CodeMap();
1845 return code_map;
1846}
1847
1848static uint32_t HashCodeAddress(Address addr) {
1849 static const uintptr_t kGoldenRatio = 2654435761u;
1850 return static_cast<uint32_t>((addr >> kCodeAlignmentBits) * kGoldenRatio);
1851}
1852
1853static base::HashMap* GetLineMap() {
1854 static base::HashMap* line_map = nullptr;
1855 if (line_map == nullptr) {
1856 line_map = new base::HashMap();
1857 }
1858 return line_map;
1859}
1860
1861static void PutLineInfo(Address addr, LineInfo* info) {
1862 base::HashMap* line_map = GetLineMap();
1863 base::HashMap::Entry* e = line_map->LookupOrInsert(
1864 reinterpret_cast<void*>(addr), HashCodeAddress(addr));
1865 if (e->value != nullptr) delete static_cast<LineInfo*>(e->value);
1866 e->value = info;
1867}
1868
1869static LineInfo* GetLineInfo(Address addr) {
1870 void* value = GetLineMap()->Remove(reinterpret_cast<void*>(addr),
1871 HashCodeAddress(addr));
1872 return static_cast<LineInfo*>(value);
1873}
1874
1875static void AddUnwindInfo(CodeDescription* desc) {
1876#if V8_TARGET_ARCH_X64
1877 if (desc->is_function()) {
1878 // To avoid propagating unwinding information through
1879 // compilation pipeline we use an approximation.
1880 // For most use cases this should not affect usability.
1881 static const int kFramePointerPushOffset = 1;
1882 static const int kFramePointerSetOffset = 4;
1883 static const int kFramePointerPopOffset = -3;
1884
1885 uintptr_t frame_pointer_push_address =
1886 desc->CodeStart() + kFramePointerPushOffset;
1887
1888 uintptr_t frame_pointer_set_address =
1889 desc->CodeStart() + kFramePointerSetOffset;
1890
1891 uintptr_t frame_pointer_pop_address =
1892 desc->CodeEnd() + kFramePointerPopOffset;
1893
1894 desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH,
1895 frame_pointer_push_address);
1896 desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET,
1897 frame_pointer_set_address);
1898 desc->SetStackStateStartAddress(CodeDescription::POST_RBP_POP,
1899 frame_pointer_pop_address);
1900 } else {
1901 desc->SetStackStateStartAddress(CodeDescription::POST_RBP_PUSH,
1902 desc->CodeStart());
1903 desc->SetStackStateStartAddress(CodeDescription::POST_RBP_SET,
1904 desc->CodeStart());
1905 desc->SetStackStateStartAddress(CodeDescription::POST_RBP_POP,
1906 desc->CodeEnd());
1907 }
1908#endif // V8_TARGET_ARCH_X64
1909}
1910
1912
1913static std::optional<std::pair<CodeMap::iterator, CodeMap::iterator>>
1914GetOverlappingRegions(CodeMap* map, const base::AddressRegion region) {
1915 DCHECK_LT(region.begin(), region.end());
1916
1917 if (map->empty()) return {};
1918
1919 // Find the first overlapping entry.
1920
1921 // If successful, points to the first element not less than `region`. The
1922 // returned iterator has the key in `first` and the value in `second`.
1923 auto it = map->lower_bound(region);
1924 auto start_it = it;
1925
1926 if (it == map->end()) {
1927 start_it = map->begin();
1928 // Find the first overlapping entry.
1929 for (; start_it != map->end(); ++start_it) {
1930 if (start_it->first.end() > region.begin()) {
1931 break;
1932 }
1933 }
1934 } else if (it != map->begin()) {
1935 for (--it; it != map->begin(); --it) {
1936 if ((*it).first.end() <= region.begin()) break;
1937 start_it = it;
1938 }
1939 if (it == map->begin() && it->first.end() > region.begin()) {
1940 start_it = it;
1941 }
1942 }
1943
1944 if (start_it == map->end()) {
1945 return {};
1946 }
1947
1948 // Find the first non-overlapping entry after `region`.
1949
1950 const auto end_it = map->lower_bound({region.end(), 0});
1951
1952 // Return a range containing intersecting regions.
1953
1954 if (std::distance(start_it, end_it) < 1)
1955 return {}; // No overlapping entries.
1956
1957 return {{start_it, end_it}};
1958}
1959
1960// Remove entries from the map that intersect the given address region,
1961// and deregister them from GDB.
1962static void RemoveJITCodeEntries(CodeMap* map,
1963 const base::AddressRegion region) {
1964 if (auto overlap = GetOverlappingRegions(map, region)) {
1965 auto start_it = overlap->first;
1966 auto end_it = overlap->second;
1967 for (auto it = start_it; it != end_it; it++) {
1968 JITCodeEntry* old_entry = (*it).second;
1969 UnregisterCodeEntry(old_entry);
1970 DestroyCodeEntry(old_entry);
1971 }
1972
1973 map->erase(start_it, end_it);
1974 }
1975}
1976
1977// Insert the entry into the map and register it with GDB.
1978static void AddJITCodeEntry(CodeMap* map, const base::AddressRegion region,
1979 JITCodeEntry* entry, bool dump_if_enabled,
1980 const char* name_hint) {
1981#if defined(DEBUG) && !V8_OS_WIN
1982 static int file_num = 0;
1983 if (v8_flags.gdbjit_dump && dump_if_enabled) {
1984 static const int kMaxFileNameSize = 64;
1985 char file_name[64];
1986
1987 SNPrintF(base::Vector<char>(file_name, kMaxFileNameSize),
1988 "/tmp/elfdump%s%d.o", (name_hint != nullptr) ? name_hint : "",
1989 file_num++);
1990 WriteBytes(file_name, reinterpret_cast<uint8_t*>(entry->symfile_addr_),
1991 static_cast<int>(entry->symfile_size_));
1992 }
1993#endif
1994
1995 auto result = map->emplace(region, entry);
1996 DCHECK(result.second); // Insertion happened.
1997 USE(result);
1998
1999 RegisterCodeEntry(entry);
2000}
2001
2002static void AddCode(const char* name, base::AddressRegion region,
2003 Tagged<SharedFunctionInfo> shared, LineInfo* lineinfo,
2004 Isolate* isolate, bool is_function) {
2006 CodeDescription code_desc(name, region, shared, lineinfo, is_function);
2007
2008 CodeMap* code_map = GetCodeMap();
2009 RemoveJITCodeEntries(code_map, region);
2010
2011 if (!v8_flags.gdbjit_full && !code_desc.IsLineInfoAvailable()) {
2012 delete lineinfo;
2013 return;
2014 }
2015
2016 AddUnwindInfo(&code_desc);
2017 JITCodeEntry* entry = CreateELFObject(&code_desc, isolate);
2018
2019 delete lineinfo;
2020
2021 const char* name_hint = nullptr;
2022 bool should_dump = false;
2023 if (v8_flags.gdbjit_dump) {
2024 if (strlen(v8_flags.gdbjit_dump_filter) == 0) {
2025 name_hint = name;
2026 should_dump = true;
2027 } else if (name != nullptr) {
2028 name_hint = strstr(name, v8_flags.gdbjit_dump_filter);
2029 should_dump = (name_hint != nullptr);
2030 }
2031 }
2032 AddJITCodeEntry(code_map, region, entry, should_dump, name_hint);
2033}
2034
2035void EventHandler(const v8::JitCodeEvent* event) {
2036 if (!v8_flags.gdbjit) return;
2037 if ((event->code_type != v8::JitCodeEvent::JIT_CODE) &&
2039 return;
2040 }
2041 base::MutexGuard lock_guard(mutex.Pointer());
2042 switch (event->type) {
2044 Address addr = reinterpret_cast<Address>(event->code_start);
2045 LineInfo* lineinfo = GetLineInfo(addr);
2046 std::string event_name(event->name.str, event->name.len);
2047 // It's called UnboundScript in the API but it's a SharedFunctionInfo.
2049 event->script.IsEmpty() ? Tagged<SharedFunctionInfo>()
2050 : *Utils::OpenDirectHandle(*event->script);
2051 Isolate* isolate = reinterpret_cast<Isolate*>(event->isolate);
2052 bool is_function = false;
2053 // TODO(zhin): See if we can use event->code_type to determine
2054 // is_function, the difference currently is that JIT_CODE is SparkPlug,
2055 // TurboProp, TurboFan, whereas CodeKindIsOptimizedJSFunction is only
2056 // TurboProp and TurboFan. is_function is used for AddUnwindInfo, and the
2057 // prologue that SP generates probably matches that of TP/TF, so we can
2058 // use event->code_type here instead of finding the Code.
2059 // TODO(zhin): Rename is_function to be more accurate.
2060 if (event->code_type == v8::JitCodeEvent::JIT_CODE) {
2061 Tagged<Code> lookup_result =
2062 isolate->heap()->FindCodeForInnerPointer(addr);
2063 is_function = CodeKindIsOptimizedJSFunction(lookup_result->kind());
2064 }
2065 AddCode(event_name.c_str(), {addr, event->code_len}, shared, lineinfo,
2066 isolate, is_function);
2067 break;
2068 }
2070 // Enabling the GDB JIT interface should disable code compaction.
2071 UNREACHABLE();
2073 // Do nothing. Instead, adding code causes eviction of any entry whose
2074 // address range intersects the address range of the added code.
2075 break;
2077 LineInfo* line_info = reinterpret_cast<LineInfo*>(event->user_data);
2078 line_info->SetPosition(static_cast<intptr_t>(event->line_info.offset),
2079 static_cast<int>(event->line_info.pos),
2080 event->line_info.position_type ==
2082 break;
2083 }
2085 v8::JitCodeEvent* mutable_event = const_cast<v8::JitCodeEvent*>(event);
2086 mutable_event->user_data = new LineInfo();
2087 break;
2088 }
2090 LineInfo* line_info = reinterpret_cast<LineInfo*>(event->user_data);
2091 PutLineInfo(reinterpret_cast<Address>(event->code_start), line_info);
2092 break;
2093 }
2094 }
2095}
2096
2097void AddRegionForTesting(const base::AddressRegion region) {
2098 // For testing purposes we don't care about JITCodeEntry, pass nullptr.
2099 auto result = GetCodeMap()->emplace(region, nullptr);
2100 DCHECK(result.second); // Insertion happened.
2101 USE(result);
2102}
2103
2104void ClearCodeMapForTesting() { GetCodeMap()->clear(); }
2105
2106size_t NumOverlapEntriesForTesting(const base::AddressRegion region) {
2107 if (auto overlaps = GetOverlappingRegions(GetCodeMap(), region)) {
2108 return std::distance(overlaps->first, overlaps->second);
2109 }
2110 return 0;
2111}
2112
2113#endif
2114} // namespace GDBJITInterface
2115} // namespace internal
2116} // namespace v8
2117
2118#undef __MACH_O
2119#undef __ELF
int pos_
#define T
const char * name
Definition builtins.cc:39
SourcePosition pos
static v8::internal::DirectHandle< To > OpenDirectHandle(v8::Local< From > handle)
Definition api.h:279
Entry * LookupOrInsert(const Key &key, uint32_t hash)
Definition hashmap.h:223
T * New(Args &&... args)
Definition zone.h:114
Operand const offset_
Register const index_
const CodeDesc * code_desc
base::OwnedVector< uint8_t > buffer_
Definition assembler.cc:111
const int size_
Definition assembler.cc:132
JSRegExp::Flags flags_
const ObjectRef type_
int start
Handle< SharedFunctionInfo > info
int end
const int position_
other heap size flags(e.g. initial_heap_size) take precedence") DEFINE_SIZE_T( max_shared_heap_size
OptionalOpIndex index
int32_t offset
ZoneVector< RpoNumber > & result
LiftoffAssembler::CacheState state
int position
Definition liveedit.cc:290
const char * name_
base::Mutex mutex
#define LAZY_MUTEX_INITIALIZER
Definition mutex.h:105
EmbedderStackState
Definition common.h:15
PointerTemplateHashMapImpl< DefaultAllocationPolicy > HashMap
Definition hashmap.h:520
void * Malloc(size_t size)
Definition memory.h:36
void Free(void *memory)
Definition memory.h:63
int WriteBytes(const char *filename, const uint8_t *bytes, int size, bool verbose)
Definition utils.cc:201
V8_EXPORT_PRIVATE base::Vector< Flag > Flags()
Definition flags.cc:300
constexpr bool CodeKindIsOptimizedJSFunction(CodeKind kind)
Definition code-kind.h:66
Tagged(T object) -> Tagged< T >
constexpr intptr_t kCodeAlignment
Definition globals.h:964
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
void Print(Tagged< Object > obj)
Definition objects.h:774
constexpr int kSystemPointerSize
Definition globals.h:410
constexpr int kCodeAlignmentBits
Definition globals.h:962
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
__attribute__((tls_model(V8_TLS_MODEL))) extern thread_local Isolate *g_current_isolate_ V8_CONSTINIT
void MemCopy(void *dest, const void *src, size_t size)
Definition memcopy.h:124
std::vector< ValueType > locals_
std::vector< ValueType > globals_
ReadOnlySegmentForSerialization *const segment_
Node * prev_
#define DCHECK_NULL(val)
Definition logging.h:491
#define UNIMPLEMENTED()
Definition logging.h:66
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define USE(...)
Definition macros.h:293
struct name_t name
Local< UnboundScript > script
struct line_info_t line_info
std::unique_ptr< ValueMirror > value
wasm::ValueType type
#define ZONE_NAME
Definition zone.h:22