// Copyright 2010 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifdef ENABLE_GDB_JIT_INTERFACE #include "src/v8.h" #include "src/base/platform/platform.h" #include "src/bootstrapper.h" #include "src/compiler.h" #include "src/frames-inl.h" #include "src/frames.h" #include "src/gdb-jit.h" #include "src/global-handles.h" #include "src/messages.h" #include "src/natives.h" #include "src/scopes.h" namespace v8 { namespace internal { #ifdef __APPLE__ #define __MACH_O class MachO; class MachOSection; typedef MachO DebugObject; typedef MachOSection DebugSection; #else #define __ELF class ELF; class ELFSection; typedef ELF DebugObject; typedef ELFSection DebugSection; #endif class Writer BASE_EMBEDDED { public: explicit Writer(DebugObject* debug_object) : debug_object_(debug_object), position_(0), capacity_(1024), buffer_(reinterpret_cast(malloc(capacity_))) { } ~Writer() { free(buffer_); } uintptr_t position() const { return position_; } template class Slot { public: Slot(Writer* w, uintptr_t offset) : w_(w), offset_(offset) { } T* operator-> () { return w_->RawSlotAt(offset_); } void set(const T& value) { *w_->RawSlotAt(offset_) = value; } Slot at(int i) { return Slot(w_, offset_ + sizeof(T) * i); } private: Writer* w_; uintptr_t offset_; }; template void Write(const T& val) { Ensure(position_ + sizeof(T)); *RawSlotAt(position_) = val; position_ += sizeof(T); } template Slot SlotAt(uintptr_t offset) { Ensure(offset + sizeof(T)); return Slot(this, offset); } template Slot CreateSlotHere() { return CreateSlotsHere(1); } template Slot CreateSlotsHere(uint32_t count) { uintptr_t slot_position = position_; position_ += sizeof(T) * count; Ensure(position_); return SlotAt(slot_position); } void Ensure(uintptr_t pos) { if (capacity_ < pos) { while (capacity_ < pos) capacity_ *= 2; buffer_ = reinterpret_cast(realloc(buffer_, capacity_)); } } DebugObject* debug_object() { return debug_object_; } byte* buffer() { return buffer_; } void Align(uintptr_t align) { uintptr_t delta = position_ % align; if (delta == 0) return; uintptr_t padding = align - delta; Ensure(position_ += padding); ASSERT((position_ % align) == 0); } void WriteULEB128(uintptr_t value) { do { uint8_t byte = value & 0x7F; value >>= 7; if (value != 0) byte |= 0x80; Write(byte); } while (value != 0); } void WriteSLEB128(intptr_t value) { bool more = true; while (more) { int8_t byte = value & 0x7F; bool byte_sign = byte & 0x40; value >>= 7; if ((value == 0 && !byte_sign) || (value == -1 && byte_sign)) { more = false; } else { byte |= 0x80; } Write(byte); } } void WriteString(const char* str) { do { Write(*str); } while (*str++); } private: template friend class Slot; template T* RawSlotAt(uintptr_t offset) { ASSERT(offset < capacity_ && offset + sizeof(T) <= capacity_); return reinterpret_cast(&buffer_[offset]); } DebugObject* debug_object_; uintptr_t position_; uintptr_t capacity_; byte* buffer_; }; class ELFStringTable; template class DebugSectionBase : public ZoneObject { public: virtual ~DebugSectionBase() { } virtual void WriteBody(Writer::Slot header, Writer* writer) { uintptr_t start = writer->position(); if (WriteBodyInternal(writer)) { uintptr_t end = writer->position(); header->offset = start; #if defined(__MACH_O) header->addr = 0; #endif header->size = end - start; } } virtual bool WriteBodyInternal(Writer* writer) { return false; } typedef THeader Header; }; struct MachOSectionHeader { char sectname[16]; char segname[16]; #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 uint32_t addr; uint32_t size; #else uint64_t addr; uint64_t size; #endif uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; }; class MachOSection : public DebugSectionBase { public: enum Type { S_REGULAR = 0x0u, S_ATTR_COALESCED = 0xbu, S_ATTR_SOME_INSTRUCTIONS = 0x400u, S_ATTR_DEBUG = 0x02000000u, S_ATTR_PURE_INSTRUCTIONS = 0x80000000u }; MachOSection(const char* name, const char* segment, uintptr_t align, uint32_t flags) : name_(name), segment_(segment), align_(align), flags_(flags) { if (align_ != 0) { ASSERT(IsPowerOf2(align)); align_ = WhichPowerOf2(align_); } } virtual ~MachOSection() { } virtual void PopulateHeader(Writer::Slot
header) { header->addr = 0; header->size = 0; header->offset = 0; header->align = align_; header->reloff = 0; header->nreloc = 0; header->flags = flags_; header->reserved1 = 0; header->reserved2 = 0; memset(header->sectname, 0, sizeof(header->sectname)); memset(header->segname, 0, sizeof(header->segname)); ASSERT(strlen(name_) < sizeof(header->sectname)); ASSERT(strlen(segment_) < sizeof(header->segname)); strncpy(header->sectname, name_, sizeof(header->sectname)); strncpy(header->segname, segment_, sizeof(header->segname)); } private: const char* name_; const char* segment_; uintptr_t align_; uint32_t flags_; }; struct ELFSectionHeader { uint32_t name; uint32_t type; uintptr_t flags; uintptr_t address; uintptr_t offset; uintptr_t size; uint32_t link; uint32_t info; uintptr_t alignment; uintptr_t entry_size; }; #if defined(__ELF) class ELFSection : public DebugSectionBase { public: enum Type { TYPE_NULL = 0, TYPE_PROGBITS = 1, TYPE_SYMTAB = 2, TYPE_STRTAB = 3, TYPE_RELA = 4, TYPE_HASH = 5, TYPE_DYNAMIC = 6, TYPE_NOTE = 7, TYPE_NOBITS = 8, TYPE_REL = 9, TYPE_SHLIB = 10, TYPE_DYNSYM = 11, TYPE_LOPROC = 0x70000000, TYPE_X86_64_UNWIND = 0x70000001, TYPE_HIPROC = 0x7fffffff, TYPE_LOUSER = 0x80000000, TYPE_HIUSER = 0xffffffff }; enum Flags { FLAG_WRITE = 1, FLAG_ALLOC = 2, FLAG_EXEC = 4 }; enum SpecialIndexes { INDEX_ABSOLUTE = 0xfff1 }; ELFSection(const char* name, Type type, uintptr_t align) : name_(name), type_(type), align_(align) { } virtual ~ELFSection() { } void PopulateHeader(Writer::Slot
header, ELFStringTable* strtab); virtual void WriteBody(Writer::Slot
header, Writer* w) { uintptr_t start = w->position(); if (WriteBodyInternal(w)) { uintptr_t end = w->position(); header->offset = start; header->size = end - start; } } virtual bool WriteBodyInternal(Writer* w) { return false; } uint16_t index() const { return index_; } void set_index(uint16_t index) { index_ = index; } protected: virtual void PopulateHeader(Writer::Slot
header) { header->flags = 0; header->address = 0; header->offset = 0; header->size = 0; header->link = 0; header->info = 0; header->entry_size = 0; } private: const char* name_; Type type_; uintptr_t align_; uint16_t index_; }; #endif // defined(__ELF) #if defined(__MACH_O) class MachOTextSection : public MachOSection { public: MachOTextSection(uintptr_t align, uintptr_t addr, uintptr_t size) : MachOSection("__text", "__TEXT", align, MachOSection::S_REGULAR | MachOSection::S_ATTR_SOME_INSTRUCTIONS | MachOSection::S_ATTR_PURE_INSTRUCTIONS), addr_(addr), size_(size) { } protected: virtual void PopulateHeader(Writer::Slot
header) { MachOSection::PopulateHeader(header); header->addr = addr_; header->size = size_; } private: uintptr_t addr_; uintptr_t size_; }; #endif // defined(__MACH_O) #if defined(__ELF) class FullHeaderELFSection : public ELFSection { public: FullHeaderELFSection(const char* name, Type type, uintptr_t align, uintptr_t addr, uintptr_t offset, uintptr_t size, uintptr_t flags) : ELFSection(name, type, align), addr_(addr), offset_(offset), size_(size), flags_(flags) { } protected: virtual void PopulateHeader(Writer::Slot
header) { ELFSection::PopulateHeader(header); header->address = addr_; header->offset = offset_; header->size = size_; header->flags = flags_; } private: uintptr_t addr_; uintptr_t offset_; uintptr_t size_; uintptr_t flags_; }; class ELFStringTable : public ELFSection { public: explicit ELFStringTable(const char* name) : ELFSection(name, TYPE_STRTAB, 1), writer_(NULL), offset_(0), size_(0) { } uintptr_t Add(const char* str) { if (*str == '\0') return 0; uintptr_t offset = size_; WriteString(str); return offset; } void AttachWriter(Writer* w) { writer_ = w; offset_ = writer_->position(); // First entry in the string table should be an empty string. WriteString(""); } void DetachWriter() { writer_ = NULL; } virtual void WriteBody(Writer::Slot
header, Writer* w) { ASSERT(writer_ == NULL); header->offset = offset_; header->size = size_; } private: void WriteString(const char* str) { uintptr_t written = 0; do { writer_->Write(*str); written++; } while (*str++); size_ += written; } Writer* writer_; uintptr_t offset_; uintptr_t size_; }; void ELFSection::PopulateHeader(Writer::Slot header, ELFStringTable* strtab) { header->name = strtab->Add(name_); header->type = type_; header->alignment = align_; PopulateHeader(header); } #endif // defined(__ELF) #if defined(__MACH_O) class MachO BASE_EMBEDDED { public: explicit MachO(Zone* zone) : zone_(zone), sections_(6, zone) { } uint32_t AddSection(MachOSection* section) { sections_.Add(section, zone_); return sections_.length() - 1; } void Write(Writer* w, uintptr_t code_start, uintptr_t code_size) { Writer::Slot header = WriteHeader(w); uintptr_t load_command_start = w->position(); Writer::Slot cmd = WriteSegmentCommand(w, code_start, code_size); WriteSections(w, cmd, header, load_command_start); } private: struct MachOHeader { uint32_t magic; uint32_t cputype; uint32_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; #if V8_TARGET_ARCH_X64 uint32_t reserved; #endif }; struct MachOSegmentCommand { uint32_t cmd; uint32_t cmdsize; char segname[16]; #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 uint32_t vmaddr; uint32_t vmsize; uint32_t fileoff; uint32_t filesize; #else uint64_t vmaddr; uint64_t vmsize; uint64_t fileoff; uint64_t filesize; #endif uint32_t maxprot; uint32_t initprot; uint32_t nsects; uint32_t flags; }; enum MachOLoadCommandCmd { LC_SEGMENT_32 = 0x00000001u, LC_SEGMENT_64 = 0x00000019u }; Writer::Slot WriteHeader(Writer* w) { ASSERT(w->position() == 0); Writer::Slot header = w->CreateSlotHere(); #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 header->magic = 0xFEEDFACEu; header->cputype = 7; // i386 header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL #elif V8_TARGET_ARCH_X64 header->magic = 0xFEEDFACFu; header->cputype = 7 | 0x01000000; // i386 | 64-bit ABI header->cpusubtype = 3; // CPU_SUBTYPE_I386_ALL header->reserved = 0; #else #error Unsupported target architecture. #endif header->filetype = 0x1; // MH_OBJECT header->ncmds = 1; header->sizeofcmds = 0; header->flags = 0; return header; } Writer::Slot WriteSegmentCommand(Writer* w, uintptr_t code_start, uintptr_t code_size) { Writer::Slot cmd = w->CreateSlotHere(); #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 cmd->cmd = LC_SEGMENT_32; #else cmd->cmd = LC_SEGMENT_64; #endif cmd->vmaddr = code_start; cmd->vmsize = code_size; cmd->fileoff = 0; cmd->filesize = 0; cmd->maxprot = 7; cmd->initprot = 7; cmd->flags = 0; cmd->nsects = sections_.length(); memset(cmd->segname, 0, 16); cmd->cmdsize = sizeof(MachOSegmentCommand) + sizeof(MachOSection::Header) * cmd->nsects; return cmd; } void WriteSections(Writer* w, Writer::Slot cmd, Writer::Slot header, uintptr_t load_command_start) { Writer::Slot headers = w->CreateSlotsHere(sections_.length()); cmd->fileoff = w->position(); header->sizeofcmds = w->position() - load_command_start; for (int section = 0; section < sections_.length(); ++section) { sections_[section]->PopulateHeader(headers.at(section)); sections_[section]->WriteBody(headers.at(section), w); } cmd->filesize = w->position() - (uintptr_t)cmd->fileoff; } Zone* zone_; ZoneList sections_; }; #endif // defined(__MACH_O) #if defined(__ELF) class ELF BASE_EMBEDDED { public: explicit ELF(Zone* zone) : zone_(zone), sections_(6, zone) { sections_.Add(new(zone) ELFSection("", ELFSection::TYPE_NULL, 0), zone); sections_.Add(new(zone) ELFStringTable(".shstrtab"), zone); } void Write(Writer* w) { WriteHeader(w); WriteSectionTable(w); WriteSections(w); } ELFSection* SectionAt(uint32_t index) { return sections_[index]; } uint32_t AddSection(ELFSection* section) { sections_.Add(section, zone_); section->set_index(sections_.length() - 1); return sections_.length() - 1; } private: struct ELFHeader { uint8_t ident[16]; uint16_t type; uint16_t machine; uint32_t version; uintptr_t entry; uintptr_t pht_offset; uintptr_t sht_offset; uint32_t flags; uint16_t header_size; uint16_t pht_entry_size; uint16_t pht_entry_num; uint16_t sht_entry_size; uint16_t sht_entry_num; uint16_t sht_strtab_index; }; void WriteHeader(Writer* w) { ASSERT(w->position() == 0); Writer::Slot header = w->CreateSlotHere(); #if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_X87 || \ (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT)) const uint8_t ident[16] = { 0x7f, 'E', 'L', 'F', 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_64_BIT const uint8_t ident[16] = { 0x7f, 'E', 'L', 'F', 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; #else #error Unsupported target architecture. #endif memcpy(header->ident, ident, 16); header->type = 1; #if V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_X87 header->machine = 3; #elif V8_TARGET_ARCH_X64 // Processor identification value for x64 is 62 as defined in // System V ABI, AMD64 Supplement // http://www.x86-64.org/documentation/abi.pdf header->machine = 62; #elif V8_TARGET_ARCH_ARM // Set to EM_ARM, defined as 40, in "ARM ELF File Format" at // infocenter.arm.com/help/topic/com.arm.doc.dui0101a/DUI0101A_Elf.pdf header->machine = 40; #else #error Unsupported target architecture. #endif header->version = 1; header->entry = 0; header->pht_offset = 0; header->sht_offset = sizeof(ELFHeader); // Section table follows header. header->flags = 0; header->header_size = sizeof(ELFHeader); header->pht_entry_size = 0; header->pht_entry_num = 0; header->sht_entry_size = sizeof(ELFSection::Header); header->sht_entry_num = sections_.length(); header->sht_strtab_index = 1; } void WriteSectionTable(Writer* w) { // Section headers table immediately follows file header. ASSERT(w->position() == sizeof(ELFHeader)); Writer::Slot headers = w->CreateSlotsHere(sections_.length()); // String table for section table is the first section. ELFStringTable* strtab = static_cast(SectionAt(1)); strtab->AttachWriter(w); for (int i = 0, length = sections_.length(); i < length; i++) { sections_[i]->PopulateHeader(headers.at(i), strtab); } strtab->DetachWriter(); } int SectionHeaderPosition(uint32_t section_index) { return sizeof(ELFHeader) + sizeof(ELFSection::Header) * section_index; } void WriteSections(Writer* w) { Writer::Slot headers = w->SlotAt(sizeof(ELFHeader)); for (int i = 0, length = sections_.length(); i < length; i++) { sections_[i]->WriteBody(headers.at(i), w); } } Zone* zone_; ZoneList sections_; }; class ELFSymbol BASE_EMBEDDED { public: enum Type { TYPE_NOTYPE = 0, TYPE_OBJECT = 1, TYPE_FUNC = 2, TYPE_SECTION = 3, TYPE_FILE = 4, TYPE_LOPROC = 13, TYPE_HIPROC = 15 }; enum Binding { BIND_LOCAL = 0, BIND_GLOBAL = 1, BIND_WEAK = 2, BIND_LOPROC = 13, BIND_HIPROC = 15 }; ELFSymbol(const char* name, uintptr_t value, uintptr_t size, Binding binding, Type type, uint16_t section) : name(name), value(value), size(size), info((binding << 4) | type), other(0), section(section) { } Binding binding() const { return static_cast(info >> 4); } #if (V8_TARGET_ARCH_IA32 || V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_X87 || \ (V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_32_BIT)) struct SerializedLayout { SerializedLayout(uint32_t name, uintptr_t value, uintptr_t size, Binding binding, Type type, uint16_t section) : name(name), value(value), size(size), info((binding << 4) | type), other(0), section(section) { } uint32_t name; uintptr_t value; uintptr_t size; uint8_t info; uint8_t other; uint16_t section; }; #elif V8_TARGET_ARCH_X64 && V8_TARGET_ARCH_64_BIT struct SerializedLayout { SerializedLayout(uint32_t name, uintptr_t value, uintptr_t size, Binding binding, Type type, uint16_t section) : name(name), info((binding << 4) | type), other(0), section(section), value(value), size(size) { } uint32_t name; uint8_t info; uint8_t other; uint16_t section; uintptr_t value; uintptr_t size; }; #endif void Write(Writer::Slot s, ELFStringTable* t) { // Convert symbol names from strings to indexes in the string table. s->name = t->Add(name); s->value = value; s->size = size; s->info = info; s->other = other; s->section = section; } private: const char* name; uintptr_t value; uintptr_t size; uint8_t info; uint8_t other; uint16_t section; }; class ELFSymbolTable : public ELFSection { public: ELFSymbolTable(const char* name, Zone* zone) : ELFSection(name, TYPE_SYMTAB, sizeof(uintptr_t)), locals_(1, zone), globals_(1, zone) { } virtual void WriteBody(Writer::Slot
header, Writer* w) { w->Align(header->alignment); int total_symbols = locals_.length() + globals_.length() + 1; header->offset = w->position(); Writer::Slot symbols = w->CreateSlotsHere(total_symbols); header->size = w->position() - header->offset; // String table for this symbol table should follow it in the section table. ELFStringTable* strtab = static_cast(w->debug_object()->SectionAt(index() + 1)); strtab->AttachWriter(w); symbols.at(0).set(ELFSymbol::SerializedLayout(0, 0, 0, ELFSymbol::BIND_LOCAL, ELFSymbol::TYPE_NOTYPE, 0)); WriteSymbolsList(&locals_, symbols.at(1), strtab); WriteSymbolsList(&globals_, symbols.at(locals_.length() + 1), strtab); strtab->DetachWriter(); } void Add(const ELFSymbol& symbol, Zone* zone) { if (symbol.binding() == ELFSymbol::BIND_LOCAL) { locals_.Add(symbol, zone); } else { globals_.Add(symbol, zone); } } protected: virtual void PopulateHeader(Writer::Slot
header) { ELFSection::PopulateHeader(header); // We are assuming that string table will follow symbol table. header->link = index() + 1; header->info = locals_.length() + 1; header->entry_size = sizeof(ELFSymbol::SerializedLayout); } private: void WriteSymbolsList(const ZoneList* src, Writer::Slot dst, ELFStringTable* strtab) { for (int i = 0, len = src->length(); i < len; i++) { src->at(i).Write(dst.at(i), strtab); } } ZoneList locals_; ZoneList globals_; }; #endif // defined(__ELF) class CodeDescription BASE_EMBEDDED { public: #if V8_TARGET_ARCH_X64 enum StackState { POST_RBP_PUSH, POST_RBP_SET, POST_RBP_POP, STACK_STATE_MAX }; #endif CodeDescription(const char* name, Code* code, Handle