diff options
Diffstat (limited to 'lldb/source/Plugins/ObjectFile')
-rw-r--r-- | lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp | 929 | ||||
-rw-r--r-- | lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h | 197 | ||||
-rw-r--r-- | lldb/source/Plugins/ObjectFile/ELF/elf.h | 240 | ||||
-rw-r--r-- | lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp | 1311 | ||||
-rw-r--r-- | lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h | 131 |
5 files changed, 2808 insertions, 0 deletions
diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp new file mode 100644 index 00000000000..b34dd3db289 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -0,0 +1,929 @@ +//===-- ObjectFileELF.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileELF.h" + +#include <mach/machine.h> +#include <assert.h> + +#include <algorithm> + +#include <stdint.h> +#include "elf.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/Error.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/Stream.h" +#include "lldb/Symbol/ObjectFile.h" + +#define CASE_AND_STREAM(s, def, width) case def: s->Printf("%-*s", width, #def); break; + +static uint32_t ELFMachineToMachCPU(Elf32_Half machine); + +using namespace lldb; +using namespace lldb_private; +using namespace std; + + +#include <mach-o/nlist.h> +#include <mach-o/stab.h> + + +void +ObjectFileELF::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectFileELF::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectFileELF::GetPluginNameStatic() +{ + return "object-file.elf32"; +} + +const char * +ObjectFileELF::GetPluginDescriptionStatic() +{ + return "ELF object file reader (32-bit)."; +} + + +ObjectFile * +ObjectFileELF::CreateInstance (Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) +{ + if (ObjectFileELF::MagicBytesMatch(dataSP)) + { + std::auto_ptr<ObjectFile> objfile_ap(new ObjectFileELF (module, dataSP, file, offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + +bool +ObjectFileELF::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + const uint8_t* magic = data.PeekData(0, 4); + if (magic != NULL) + { + return magic[EI_MAG0] == 0x7f + && magic[EI_MAG1] == 'E' + && magic[EI_MAG2] == 'L' + && magic[EI_MAG3] == 'F'; + } + return false; +} + + +ObjectFileELF::ObjectFileELF(Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) : + ObjectFile (module, file, offset, length, dataSP), + m_header(), + m_program_headers(), + m_section_headers(), + m_sections_ap(), + m_symtab_ap(), + m_shstr_data() +{ + if (file) + m_file = *file; + ::bzero (&m_header, sizeof(m_header)); +} + + +ObjectFileELF::~ObjectFileELF() +{ +} + +ByteOrder +ObjectFileELF::GetByteOrder () const +{ + if (m_header.e_ident[EI_DATA] == ELFDATA2MSB) + return eByteOrderBig; + if (m_header.e_ident[EI_DATA] == ELFDATA2LSB) + return eByteOrderLittle; + return eByteOrderInvalid; +} + +size_t +ObjectFileELF::GetAddressByteSize () const +{ + return m_data.GetAddressByteSize(); +} + +bool +ObjectFileELF::ParseHeader () +{ + m_data.SetAddressByteSize(4); + uint32_t offset = GetOffset(); + if (m_data.GetU8(&offset, m_header.e_ident, EI_NIDENT) == NULL) + return false; + + m_data.SetByteOrder(GetByteOrder()); + + // Read e_type and e_machine + if (m_data.GetU16(&offset, &m_header.e_type, 2) == NULL) + return false; + + // read e_version, e_entry, e_phoff, e_shoff, e_flags + if (m_data.GetU32(&offset, &m_header.e_version, 5) == NULL) + return false; + + // Read e_ehsize, e_phentsize, e_phnum, e_shentsize, e_shnum, e_shstrndx + if (m_data.GetU16(&offset, &m_header.e_ehsize, 6) == NULL) + return false; + + return true; +} + +bool +ObjectFileELF::GetUUID (UUID* uuid) +{ + return false; +} + +uint32_t +ObjectFileELF::GetDependentModules(FileSpecList& files) +{ + return 0; +} + +//---------------------------------------------------------------------- +// ParseProgramHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseProgramHeaders() +{ + // We have already parsed the program headers + if (!m_program_headers.empty()) + return m_program_headers.size(); + + uint32_t offset = 0; + if (m_header.e_phnum > 0) + { + m_program_headers.resize(m_header.e_phnum); + + if (m_program_headers.size() != m_header.e_phnum) + return 0; + + const size_t byte_size = m_header.e_phnum * m_header.e_phentsize; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_header.e_phoff, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + DataExtractor data(buffer_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + uint32_t idx; + for (idx = 0; idx < m_header.e_phnum; ++idx) + { + if (data.GetU32(&offset, &m_program_headers[idx].p_type, 8) == NULL) + return 0; + } + if (idx < m_program_headers.size()) + m_program_headers.resize(idx); + } + + return m_program_headers.size(); +} + + +//---------------------------------------------------------------------- +// ParseSectionHeaders +//---------------------------------------------------------------------- +size_t +ObjectFileELF::ParseSectionHeaders() +{ + // We have already parsed the section headers + if (!m_section_headers.empty()) + return m_section_headers.size(); + + if (m_header.e_shnum > 0) + { + uint32_t offset = 0; + + m_section_headers.resize(m_header.e_shnum); + + if (m_section_headers.size() != m_header.e_shnum) + return 0; + + const size_t byte_size = m_header.e_shnum * m_header.e_shentsize; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_header.e_shoff, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + DataExtractor data(buffer_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + uint32_t idx; + for (idx = 0; idx < m_header.e_shnum; ++idx) + { + if (data.GetU32(&offset, &m_section_headers[idx].sh_name, 10) == NULL) + break; + } + if (idx < m_section_headers.size()) + m_section_headers.resize(idx); + } + + return m_section_headers.size(); +} + +size_t +ObjectFileELF::GetSectionHeaderStringTable() +{ + if (m_shstr_data.GetByteSize() == 0) + { + if (m_header.e_shstrndx && m_header.e_shstrndx < m_section_headers.size()) + { + const size_t byte_size = m_section_headers[m_header.e_shstrndx].sh_size; + DataBufferSP buffer_sp(m_file.ReadFileContents(m_offset + m_section_headers[m_header.e_shstrndx].sh_offset, byte_size)); + + if (buffer_sp.get() == NULL || buffer_sp->GetByteSize() != byte_size) + return 0; + + m_shstr_data.SetData(buffer_sp); + } + } + return m_shstr_data.GetByteSize(); +} + +uint32_t +ObjectFileELF::GetSectionIndexByName(const char *name) +{ + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t offset = 1; // Skip leading NULL string at offset 0; + while (m_shstr_data.ValidOffset(offset)) + { + uint32_t sh_name = offset; // Save offset in case we find a match + const char* sectionHeaderName = m_shstr_data.GetCStr(&offset); + if (sectionHeaderName) + { + if (strcmp(name, sectionHeaderName) == 0) + { + SectionHeaderCollIter pos; + for (pos = m_section_headers.begin(); pos != m_section_headers.end(); ++pos) + { + if ( (*pos).sh_name == sh_name ) + { + // section indexes are 1 based + return std::distance(m_section_headers.begin(), pos) + 1; + } + } + return UINT32_MAX; + } + } + else + { + return UINT32_MAX; + } + } + } + + return UINT32_MAX; +} + +SectionList * +ObjectFileELF::GetSectionList() +{ + if (m_sections_ap.get() == NULL) + { + m_sections_ap.reset(new SectionList()); + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t sh_idx = 0; + const size_t num_sections = m_section_headers.size(); + for (sh_idx = 0; sh_idx < num_sections; ++sh_idx) + { + ConstString section_name(m_shstr_data.PeekCStr(m_section_headers[sh_idx].sh_name)); + uint64_t section_file_size = m_section_headers[sh_idx].sh_type == SHT_NOBITS ? 0 : m_section_headers[sh_idx].sh_size; + SectionSP section_sp(new Section(NULL, // Parent section + GetModule(), // Module to which this section belongs + sh_idx + 1, // Section ID is the 1 based + section_name, // Name of this section + eSectionTypeOther, // TODO: fill this in appropriately for ELF... + m_section_headers[sh_idx].sh_addr, // File VM address + m_section_headers[sh_idx].sh_size, // VM size in bytes of this section + m_section_headers[sh_idx].sh_offset, // Offset to the data for this section in the file + section_file_size, // Size in bytes of this section as found in the the file + m_section_headers[sh_idx].sh_flags)); // Flags for this section + if (section_sp.get()) + m_sections_ap->AddSection(section_sp); + + } + } + } + return m_sections_ap.get(); +} + +static void +ParseSymbols (Symtab *symtab, SectionList *section_list, const Elf32_Shdr &symtab_shdr, const DataExtractor& symtab_data, const DataExtractor& strtab_data) +{ + assert (sizeof(Elf32_Sym) == symtab_shdr.sh_entsize); + const uint32_t num_symbols = symtab_data.GetByteSize() / sizeof(Elf32_Sym); + uint32_t offset = 0; + Elf32_Sym symbol; + uint32_t i; + static ConstString text_section_name(".text"); + static ConstString init_section_name(".init"); + static ConstString fini_section_name(".fini"); + static ConstString ctors_section_name(".ctors"); + static ConstString dtors_section_name(".dtors"); + + static ConstString data_section_name(".data"); + static ConstString rodata_section_name(".rodata"); + static ConstString rodata1_section_name(".rodata1"); + static ConstString data2_section_name(".data1"); + static ConstString bss_section_name(".bss"); + + for (i=0; i<num_symbols; ++i) + { + // if (symtab_data.GetU32(&offset, &symbol.st_name, 3) == 0) + // break; + + if (!symtab_data.ValidOffsetForDataOfSize(offset, sizeof(Elf32_Sym))) + break; + + symbol.st_name = symtab_data.GetU32 (&offset); + symbol.st_value = symtab_data.GetU32 (&offset); + symbol.st_size = symtab_data.GetU32 (&offset); + symbol.st_info = symtab_data.GetU8 (&offset); + symbol.st_other = symtab_data.GetU8 (&offset); + symbol.st_shndx = symtab_data.GetU16 (&offset); + + Section * symbol_section = NULL; + SymbolType symbol_type = eSymbolTypeInvalid; + + switch (symbol.st_shndx) + { + case SHN_ABS: + symbol_type = eSymbolTypeAbsolute; + break; + case SHN_UNDEF: + symbol_type = eSymbolTypeUndefined; + break; + default: + symbol_section = section_list->GetSectionAtIndex (symbol.st_shndx).get(); + break; + } + + switch (ELF32_ST_BIND (symbol.st_info)) + { + default: + case STT_NOTYPE: + // The symbol's type is not specified. + break; + + case STT_OBJECT: + // The symbol is associated with a data object, such as a variable, an array, etc. + symbol_type == eSymbolTypeData; + break; + + case STT_FUNC: + // The symbol is associated with a function or other executable code. + symbol_type == eSymbolTypeCode; + break; + + case STT_SECTION: + // The symbol is associated with a section. Symbol table entries of + // this type exist primarily for relocation and normally have + // STB_LOCAL binding. + break; + + case STT_FILE: + // Conventionally, the symbol's name gives the name of the source + // file associated with the object file. A file symbol has STB_LOCAL + // binding, its section index is SHN_ABS, and it precedes the other + // STB_LOCAL symbols for the file, if it is present. + symbol_type == eSymbolTypeObjectFile; + break; + } + + if (symbol_type == eSymbolTypeInvalid) + { + if (symbol_section) + { + const ConstString §_name = symbol_section->GetName(); + if (sect_name == text_section_name || + sect_name == init_section_name || + sect_name == fini_section_name || + sect_name == ctors_section_name || + sect_name == dtors_section_name) + { + symbol_type = eSymbolTypeCode; + } + else + if (sect_name == data_section_name || + sect_name == data2_section_name || + sect_name == rodata_section_name || + sect_name == rodata1_section_name || + sect_name == bss_section_name) + { + symbol_type = eSymbolTypeData; + } + } + } + + uint64_t symbol_value = symbol.st_value; + if (symbol_section != NULL) + symbol_value -= symbol_section->GetFileAddress(); + const char *symbol_name = strtab_data.PeekCStr(symbol.st_name); + + Symbol dc_symbol(i, // ID is the original symbol table index + symbol_name, // symbol name + false, // Is the symbol name mangled? + symbol_type, // type of this symbol + ELF32_ST_BIND (symbol.st_info) == STB_GLOBAL, // Is this globally visible? + false, // Is this symbol debug info? + false, // Is this symbol a trampoline? + false, // Is this symbol artificial? + symbol_section, // section pointer if symbol_value is an offset within a section, else NULL + symbol_value, // offset from section if section is non-NULL, else the value for this symbol + symbol.st_size, // size in bytes of this symbol + symbol.st_other << 8 | symbol.st_info); // symbol flags + symtab->AddSymbol(dc_symbol); + } +} + + +Symtab * +ObjectFileELF::GetSymtab() +{ + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + uint32_t symtab_idx = UINT32_MAX; + uint32_t dynsym_idx = UINT32_MAX; + uint32_t sh_idx = 0; + const size_t num_sections = m_section_headers.size(); + for (sh_idx = 0; sh_idx < num_sections; ++sh_idx) + { + if (m_section_headers[sh_idx].sh_type == SHT_SYMTAB) + { + symtab_idx = sh_idx; + break; + } + if (m_section_headers[sh_idx].sh_type == SHT_DYNSYM) + { + dynsym_idx = sh_idx; + } + } + + SectionList *section_list = NULL; + static ConstString g_symtab(".symtab"); + static ConstString g_strtab(".strtab"); + static ConstString g_dynsym(".dynsym"); + static ConstString g_dynstr(".dynstr"); + // Check if we found a full symbol table? + if (symtab_idx < num_sections) + { + section_list = GetSectionList(); + if (section_list) + { + Section *symtab_section = section_list->FindSectionByName(g_symtab).get(); + Section *strtab_section = section_list->FindSectionByName(g_strtab).get(); + if (symtab_section && strtab_section) + { + DataExtractor symtab_data; + DataExtractor strtab_data; + if (symtab_section->ReadSectionDataFromObjectFile (this, symtab_data) > 0 && + strtab_section->ReadSectionDataFromObjectFile (this, strtab_data) > 0) + { + ParseSymbols (m_symtab_ap.get(), section_list, m_section_headers[symtab_idx], symtab_data, strtab_data); + } + } + } + } + // Check if we found a reduced symbol table that gets used for dynamic linking? + else if (dynsym_idx < num_sections) + { + section_list = GetSectionList(); + if (section_list) + { + Section *dynsym_section = section_list->FindSectionByName(g_dynsym).get(); + Section *dynstr_section = section_list->FindSectionByName(g_dynstr).get(); + if (dynsym_section && dynstr_section) + { + DataExtractor dynsym_data; + DataExtractor dynstr_data; + if (dynsym_section->ReadSectionDataFromObjectFile (this, dynsym_data) > 0 && + dynstr_section->ReadSectionDataFromObjectFile (this, dynstr_data) > 0) + { + ParseSymbols (m_symtab_ap.get(), section_list, m_section_headers[dynsym_idx], dynsym_data, dynstr_data); + } + } + } + } + } + } + return m_symtab_ap.get(); +} + +// +////---------------------------------------------------------------------- +//// GetNListSymtab +////---------------------------------------------------------------------- +//bool +//ELF32RuntimeFileParser::GetNListSymtab(BinaryDataRef& stabs_data, BinaryDataRef& stabstr_data, bool locals_only, uint32_t& value_size) +//{ +// value_size = 4; // Size in bytes of the nlist n_value member +// return GetSectionInfo(GetSectionIndexByName(".stab"), NULL, NULL, NULL, NULL, NULL, NULL, &stabs_data, NULL) && +// GetSectionInfo(GetSectionIndexByName(".stabstr"), NULL, NULL, NULL, NULL, NULL, NULL, &stabstr_data, NULL); +//} +// +//===----------------------------------------------------------------------===// +// Dump +// +// Dump the specifics of the runtime file container (such as any headers +// segments, sections, etc). +//---------------------------------------------------------------------- +void +ObjectFileELF::Dump(Stream *s) +{ + DumpELFHeader(s, m_header); + s->EOL(); + DumpELFProgramHeaders(s); + s->EOL(); + DumpELFSectionHeaders(s); + s->EOL(); + SectionList *section_list = GetSectionList(); + if (section_list) + section_list->Dump(s, NULL, true); + Symtab *symtab = GetSymtab(); + if (symtab) + symtab->Dump(s, NULL); + s->EOL(); +} + +//---------------------------------------------------------------------- +// DumpELFHeader +// +// Dump the ELF header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader(Stream *s, const Elf32_Ehdr& header) +{ + + s->PutCString ("ELF Header\n"); + s->Printf ("e_ident[EI_MAG0 ] = 0x%2.2x\n", header.e_ident[EI_MAG0]); + s->Printf ("e_ident[EI_MAG1 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG1], header.e_ident[EI_MAG1]); + s->Printf ("e_ident[EI_MAG2 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG2], header.e_ident[EI_MAG2]); + s->Printf ("e_ident[EI_MAG3 ] = 0x%2.2x '%c'\n", header.e_ident[EI_MAG3], header.e_ident[EI_MAG3]); + s->Printf ("e_ident[EI_CLASS ] = 0x%2.2x\n", header.e_ident[EI_CLASS]); + s->Printf ("e_ident[EI_DATA ] = 0x%2.2x ", header.e_ident[EI_DATA]); + DumpELFHeader_e_ident_EI_DATA(s, header.e_ident[EI_DATA]); + s->Printf ("\ne_ident[EI_VERSION] = 0x%2.2x\n", header.e_ident[EI_VERSION]); + s->Printf ("e_ident[EI_PAD ] = 0x%2.2x\n", header.e_ident[EI_PAD]); + + s->Printf("e_type = 0x%4.4x ", header.e_type); + DumpELFHeader_e_type(s, header.e_type); + s->Printf("\ne_machine = 0x%4.4x\n", header.e_machine); + s->Printf("e_version = 0x%8.8x\n", header.e_version); + s->Printf("e_entry = 0x%8.8x\n", header.e_entry); + s->Printf("e_phoff = 0x%8.8x\n", header.e_phoff); + s->Printf("e_shoff = 0x%8.8x\n", header.e_shoff); + s->Printf("e_flags = 0x%8.8x\n", header.e_flags); + s->Printf("e_ehsize = 0x%4.4x\n", header.e_ehsize); + s->Printf("e_phentsize = 0x%4.4x\n", header.e_phentsize); + s->Printf("e_phnum = 0x%4.4x\n", header.e_phnum); + s->Printf("e_shentsize = 0x%4.4x\n", header.e_shentsize); + s->Printf("e_shnum = 0x%4.4x\n", header.e_shnum); + s->Printf("e_shstrndx = 0x%4.4x\n", header.e_shstrndx); +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_type +// +// Dump an token value for the ELF header member e_type +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_type(Stream *s, uint16_t e_type) +{ + switch (e_type) + { + case ET_NONE: *s << "ET_NONE"; break; + case ET_REL: *s << "ET_REL"; break; + case ET_EXEC: *s << "ET_EXEC"; break; + case ET_DYN: *s << "ET_DYN"; break; + case ET_CORE: *s << "ET_CORE"; break; + default: + break; + } +} + +//---------------------------------------------------------------------- +// DumpELFHeader_e_ident_EI_DATA +// +// Dump an token value for the ELF header member e_ident[EI_DATA] +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFHeader_e_ident_EI_DATA(Stream *s, uint16_t ei_data) +{ + switch (ei_data) + { + case ELFDATANONE: *s << "ELFDATANONE"; break; + case ELFDATA2LSB: *s << "ELFDATA2LSB - Little Endian"; break; + case ELFDATA2MSB: *s << "ELFDATA2MSB - Big Endian"; break; + default: + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader +// +// Dump a single ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader(Stream *s, const Elf32_Phdr& ph) +{ + DumpELFProgramHeader_p_type(s, ph.p_type); + s->Printf(" %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x (", ph.p_offset, ph.p_vaddr, ph.p_paddr, ph.p_filesz, ph.p_memsz, ph.p_flags); + DumpELFProgramHeader_p_flags(s, ph.p_flags); + s->Printf(") %8.8x", ph.p_align); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_type +// +// Dump an token value for the ELF program header member p_type which +// describes the type of the program header +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, Elf32_Word p_type) +{ + const int kStrWidth = 10; + switch (p_type) + { + CASE_AND_STREAM(s, PT_NULL , kStrWidth); + CASE_AND_STREAM(s, PT_LOAD , kStrWidth); + CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, PT_INTERP , kStrWidth); + CASE_AND_STREAM(s, PT_NOTE , kStrWidth); + CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, PT_PHDR , kStrWidth); + default: + s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, ""); + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFProgramHeader_p_flags +// +// Dump an token value for the ELF program header member p_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeader_p_flags(Stream *s, Elf32_Word p_flags) +{ + *s << ((p_flags & PF_X) ? "PF_X" : " ") + << (((p_flags & PF_X) && (p_flags & PF_W)) ? '+' : ' ') + << ((p_flags & PF_W) ? "PF_W" : " ") + << (((p_flags & PF_W) && (p_flags & PF_R)) ? '+' : ' ') + << ((p_flags & PF_R) ? "PF_R" : " "); +} + +//---------------------------------------------------------------------- +// DumpELFProgramHeaders +// +// Dump all of the ELF program header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFProgramHeaders(Stream *s) +{ + if (ParseProgramHeaders()) + { + s->PutCString("Program Headers\n"); + s->PutCString("IDX p_type p_offset p_vaddr p_paddr p_filesz p_memsz p_flags p_align\n"); + s->PutCString("==== ---------- -------- -------- -------- -------- -------- ------------------------- --------\n"); + + uint32_t idx = 0; + ProgramHeaderCollConstIter pos; + + for (pos = m_program_headers.begin(); pos != m_program_headers.end(); ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFileELF::DumpELFProgramHeader(s, *pos); + s->EOL(); + } + } +} + + +//---------------------------------------------------------------------- +// DumpELFSectionHeader +// +// Dump a single ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader(Stream *s, const Elf32_Shdr& sh) +{ + s->Printf ("%8.8x ", sh.sh_name); + DumpELFSectionHeader_sh_type(s, sh.sh_type); + s->Printf (" %8.8x (", sh.sh_flags); + DumpELFSectionHeader_sh_flags(s, sh.sh_flags); + s->Printf (") %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x %8.8x", + sh.sh_addr, sh.sh_offset, sh.sh_size, sh.sh_link, sh.sh_info, sh.sh_addralign, sh.sh_entsize); +} + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_type +// +// Dump an token value for the ELF section header member sh_type which +// describes the type of the section +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_type(Stream *s, Elf32_Word sh_type) +{ + const int kStrWidth = 12; + switch (sh_type) + { + CASE_AND_STREAM(s, SHT_NULL , kStrWidth); + CASE_AND_STREAM(s, SHT_PROGBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_SYMTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_STRTAB , kStrWidth); + CASE_AND_STREAM(s, SHT_RELA , kStrWidth); + CASE_AND_STREAM(s, SHT_HASH , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, SHT_NOTE , kStrWidth); + CASE_AND_STREAM(s, SHT_NOBITS , kStrWidth); + CASE_AND_STREAM(s, SHT_REL , kStrWidth); + CASE_AND_STREAM(s, SHT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, SHT_DYNSYM , kStrWidth); + CASE_AND_STREAM(s, SHT_LOPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_HIPROC , kStrWidth); + CASE_AND_STREAM(s, SHT_LOUSER , kStrWidth); + CASE_AND_STREAM(s, SHT_HIUSER , kStrWidth); + default: + s->Printf("0x%8.8x%*s", sh_type, kStrWidth - 10, ""); + break; + } +} + + +//---------------------------------------------------------------------- +// DumpELFSectionHeader_sh_flags +// +// Dump an token value for the ELF section header member sh_flags +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeader_sh_flags(Stream *s, Elf32_Word sh_flags) +{ + *s << ((sh_flags & SHF_WRITE) ? "WRITE" : " ") + << (((sh_flags & SHF_WRITE) && (sh_flags & SHF_ALLOC)) ? '+' : ' ') + << ((sh_flags & SHF_ALLOC) ? "ALLOC" : " ") + << (((sh_flags & SHF_ALLOC) && (sh_flags & SHF_EXECINSTR)) ? '+' : ' ') + << ((sh_flags & SHF_EXECINSTR) ? "EXECINSTR" : " "); +} +//---------------------------------------------------------------------- +// DumpELFSectionHeaders +// +// Dump all of the ELF section header to the specified output stream +//---------------------------------------------------------------------- +void +ObjectFileELF::DumpELFSectionHeaders(Stream *s) +{ + if (ParseSectionHeaders() && GetSectionHeaderStringTable()) + { + s->PutCString("Section Headers\n"); + s->PutCString("IDX name type flags addr offset size link info addralgn entsize Name\n"); + s->PutCString("==== -------- ------------ -------------------------------- -------- -------- -------- -------- -------- -------- -------- ====================\n"); + + uint32_t idx = 0; + SectionHeaderCollConstIter pos; + + for (pos = m_section_headers.begin(); pos != m_section_headers.end(); ++pos, ++idx) + { + s->Printf ("[%2u] ", idx); + ObjectFileELF::DumpELFSectionHeader(s, *pos); + const char* section_name = m_shstr_data.PeekCStr(pos->sh_name); + if (section_name) + *s << ' ' << section_name << "\n"; + } + } +} + +static uint32_t +ELFMachineToMachCPU(Elf32_Half machine) +{ + switch (machine) + { + case EM_SPARC: return CPU_TYPE_SPARC; + case EM_386: return CPU_TYPE_I386; + case EM_68K: return CPU_TYPE_MC680x0; + case EM_88K: return CPU_TYPE_MC88000; + case EM_860: return CPU_TYPE_I860; + case EM_MIPS: return 8; // commented out in mach/machine.h + case EM_PPC: return CPU_TYPE_POWERPC; + case EM_PPC64: return CPU_TYPE_POWERPC64; + case EM_ARM: return 12; // commented out in mach/machine.h + } + return 0; +} + +bool +ObjectFileELF::GetTargetTriple (ConstString &target_triple) +{ + static ConstString g_target_triple; + + if (g_target_triple) + { + target_triple = g_target_triple; + } + else + { + std::string triple; + switch (m_header.e_machine) + { + case EM_SPARC: triple.assign("sparc-"); break; + case EM_386: triple.assign("i386-"); break; + case EM_68K: triple.assign("68k-"); break; + case EM_88K: triple.assign("88k-"); break; + case EM_860: triple.assign("i860-"); break; + case EM_MIPS: triple.assign("mips-"); break; + case EM_PPC: triple.assign("powerpc-"); break; + case EM_PPC64: triple.assign("powerpc64-"); break; + case EM_ARM: triple.assign("arm-"); break; + } + // TODO: determine if there is a vendor in the ELF? Default to "apple" for now + triple += "apple-"; + // TODO: determine if there is an OS in the ELF? Default to "darwin" for now + triple += "darwin10"; + g_target_triple.SetCString(triple.c_str()); + target_triple = g_target_triple; + } + return !target_triple.IsEmpty(); +} + + +//bool +//ELF32RuntimeFileParser::GetArch(ArchSpec &arch) const +//{ +// arch.SetCPUType(ELFMachineToMachCPU(m_header.e_machine)); +// arch.SetCPUSubtype(ArchSpec::eAny); +// return true; +//} + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectFileELF::GetPluginName() +{ + return "ObjectFileELF"; +} + +const char * +ObjectFileELF::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileELF::GetPluginVersion() +{ + return 1; +} + +void +ObjectFileELF::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectFileELF::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectFileELF::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h new file mode 100644 index 00000000000..5d5778c78fb --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -0,0 +1,197 @@ +//===-- ObjectFileELF.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFileELF_h_ +#define liblldb_ObjectFileELF_h_ + +#include <stdint.h> +#include <vector> + +#include "lldb/lldb-private.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Symbol/ObjectFile.h" + +#include "elf.h" + +//---------------------------------------------------------------------- +// This class needs to be hidden as eventually belongs in a plugin that +// will export the ObjectFile protocol +//---------------------------------------------------------------------- +class ObjectFileELF : + public lldb_private::ObjectFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static lldb_private::ObjectFile * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectFileELF (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectFileELF(); + + virtual bool + ParseHeader (); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual size_t + GetAddressByteSize () const; + + virtual lldb_private::Symtab * + GetSymtab(); + + virtual lldb_private::SectionList * + GetSectionList(); + + virtual void + Dump (lldb_private::Stream *s); + + virtual bool + GetTargetTriple (lldb_private::ConstString &target_triple); + + virtual bool + GetUUID (lldb_private::UUID* uuid); + + virtual uint32_t + GetDependentModules(lldb_private::FileSpecList& files); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + + +protected: + typedef std::vector<Elf32_Phdr> ProgramHeaderColl; + typedef ProgramHeaderColl::iterator ProgramHeaderCollIter; + typedef ProgramHeaderColl::const_iterator ProgramHeaderCollConstIter; + + typedef std::vector<Elf32_Shdr> SectionHeaderColl; + typedef SectionHeaderColl::iterator SectionHeaderCollIter; + typedef SectionHeaderColl::const_iterator SectionHeaderCollConstIter; + + Elf32_Ehdr m_header; + ProgramHeaderColl m_program_headers; + SectionHeaderColl m_section_headers; + mutable std::auto_ptr<lldb_private::SectionList> m_sections_ap; + mutable std::auto_ptr<lldb_private::Symtab> m_symtab_ap; + lldb_private::DataExtractor m_shstr_data; + + size_t + ParseSections (); + + size_t + ParseSymtab (bool minimize); + +private: + + // ELF header dump routines + static void + DumpELFHeader (lldb_private::Stream *s, + const Elf32_Ehdr& header); + + static void + DumpELFHeader_e_ident_EI_DATA (lldb_private::Stream *s, + uint16_t ei_data); + static void + DumpELFHeader_e_type (lldb_private::Stream *s, + uint16_t e_type); + + // ELF program header dump routines + void + DumpELFProgramHeaders (lldb_private::Stream *s); + + static void + DumpELFProgramHeader (lldb_private::Stream *s, + const Elf32_Phdr& ph); + + static void + DumpELFProgramHeader_p_type (lldb_private::Stream *s, + Elf32_Word p_type); + + static void + DumpELFProgramHeader_p_flags (lldb_private::Stream *s, + Elf32_Word p_flags); + + // ELF section header dump routines + void + DumpELFSectionHeaders (lldb_private::Stream *s); + + static void + DumpELFSectionHeader (lldb_private::Stream *s, + const Elf32_Shdr& sh); + + static void + DumpELFSectionHeader_sh_type (lldb_private::Stream *s, + Elf32_Word sh_type); + + static void + DumpELFSectionHeader_sh_flags (lldb_private::Stream *s, + Elf32_Word sh_flags); + + size_t + ParseProgramHeaders (); + + size_t + ParseSectionHeaders (); + + size_t + GetSectionHeaderStringTable (); + + uint32_t + GetSectionIndexByName (const char *name); +}; + +#endif // #ifndef liblldb_ObjectFileELF_h_ diff --git a/lldb/source/Plugins/ObjectFile/ELF/elf.h b/lldb/source/Plugins/ObjectFile/ELF/elf.h new file mode 100644 index 00000000000..9d08119c597 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/ELF/elf.h @@ -0,0 +1,240 @@ +//===-- elf.h ---------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __elf_h__ +#define __elf_h__ + +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Addr; +typedef uint32_t Elf32_Off; + + +#define EI_NIDENT 16 + +//---------------------------------------------------------------------- +// ELF Header +//---------------------------------------------------------------------- +typedef struct Elf32_Ehdr_Tag +{ + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +//---------------------------------------------------------------------- +// e_type +// +// This member identifies the object file type. +//---------------------------------------------------------------------- +#define ET_NONE 0 // No file type +#define ET_REL 1 // Relocatable file +#define ET_EXEC 2 // Executable file +#define ET_DYN 3 // Shared object file +#define ET_CORE 4 // Core file +#define ET_LOPROC 0xff00 // Processor-specific +#define ET_HIPROC 0xffff // Processor-specific + +//---------------------------------------------------------------------- +// e_machine +// +// Machine Type +//---------------------------------------------------------------------- +#define EM_NONE 0 // No machine +#define EM_M32 1 // AT&T WE 32100 +#define EM_SPARC 2 // SPARC +#define EM_386 3 // Intel 80386 +#define EM_68K 4 // Motorola 68000 +#define EM_88K 5 // Motorola 88000 +#define EM_860 7 // Intel 80860 +#define EM_MIPS 8 // MIPS RS3000 +#define EM_PPC 20 // PowerPC +#define EM_PPC64 21 // PowerPC64 +#define EM_ARM 40 // ARM + + +//---------------------------------------------------------------------- +// e_ident indexes +//---------------------------------------------------------------------- +#define EI_MAG0 0 // File identification +#define EI_MAG1 1 // File identification +#define EI_MAG2 2 // File identification +#define EI_MAG3 3 // File identification +#define EI_CLASS 4 // File class +#define EI_DATA 5 // Data encoding +#define EI_VERSION 6 // File version +#define EI_PAD 7 // Start of padding bytes + + +//---------------------------------------------------------------------- +// EI_DATA definitions +//---------------------------------------------------------------------- +#define ELFDATANONE 0 // Invalid data encoding +#define ELFDATA2LSB 1 // Little Endian +#define ELFDATA2MSB 2 // Big Endian + +//---------------------------------------------------------------------- +// Section Header +//---------------------------------------------------------------------- +typedef struct Elf32_Shdr_Tag +{ + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +//---------------------------------------------------------------------- +// Section Types (sh_type) +//---------------------------------------------------------------------- +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xffffffff + +//---------------------------------------------------------------------- +// Special Section Indexes +//---------------------------------------------------------------------- +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_HIPROC 0xff1f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_HIRESERVE 0xffff + +//---------------------------------------------------------------------- +// Section Attribute Flags (sh_flags) +//---------------------------------------------------------------------- +#define SHF_WRITE 0x1 +#define SHF_ALLOC 0x2 +#define SHF_EXECINSTR 0x4 +#define SHF_MASKPROC 0xf0000000 + + +//---------------------------------------------------------------------- +// Symbol Table Entry Header +//---------------------------------------------------------------------- +typedef struct Elf32_Sym_Tag +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_Sym; + + +#define ELF32_ST_BIND(i) ((i)>>4) +#define ELF32_ST_TYPE(i) ((i)&0xf) +#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) + +// ST_BIND +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +// ST_TYPE +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + + +//---------------------------------------------------------------------- +// Relocation Entries +//---------------------------------------------------------------------- +typedef struct Elf32_Rel_Tag +{ + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct Elf32_Rela_Tag +{ + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +#define ELF32_R_SYM(i) ((i)>>8) +#define ELF32_R_TYPE(i) ((unsignedchar)(i)) +#define ELF32_R_INFO(s,t) (((s)<<8)+(unsignedchar)(t)) + + +//---------------------------------------------------------------------- +// Program Headers +//---------------------------------------------------------------------- +typedef struct Elf32_Phdr_Tag +{ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +//---------------------------------------------------------------------- +// Program Header Type (p_type) +//---------------------------------------------------------------------- +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + +#define PF_X (1 << 0) // executable +#define PF_W (1 << 1) // writable +#define PF_R (1 << 2) // readable + + +#endif // __elf_h__ diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp new file mode 100644 index 00000000000..682a116d213 --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -0,0 +1,1311 @@ +//===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ObjectFileMachO.h" + +#include <mach-o/nlist.h> +#include <mach-o/stab.h> + +#include "lldb/Core/ArchSpec.h" +#include "lldb/Core/DataBuffer.h" +#include "lldb/Core/FileSpec.h" +#include "lldb/Core/FileSpecList.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Core/Section.h" +#include "lldb/Core/StreamFile.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/Timer.h" +#include "lldb/Core/UUID.h" +#include "lldb/Symbol/ObjectFile.h" + +#ifndef S_DTRACE_DOF +// section contains DTrace Object Format +#define S_DTRACE_DOF 0xf +#endif + +#ifndef S_LAZY_DYLIB_SYMBOL_POINTERS +// section with only lazy symbol pointers to lazy loaded dylibs +#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 +#endif + +using namespace lldb; +using namespace lldb_private; + + +void +ObjectFileMachO::Initialize() +{ + PluginManager::RegisterPlugin (GetPluginNameStatic(), + GetPluginDescriptionStatic(), + CreateInstance); +} + +void +ObjectFileMachO::Terminate() +{ + PluginManager::UnregisterPlugin (CreateInstance); +} + + +const char * +ObjectFileMachO::GetPluginNameStatic() +{ + return "object-file.mach-o"; +} + +const char * +ObjectFileMachO::GetPluginDescriptionStatic() +{ + return "Mach-o object file reader (32 and 64 bit)"; +} + + +ObjectFile * +ObjectFileMachO::CreateInstance (Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) +{ + if (ObjectFileMachO::MagicBytesMatch(dataSP)) + { + std::auto_ptr<ObjectFile> objfile_ap(new ObjectFileMachO (module, dataSP, file, offset, length)); + if (objfile_ap.get() && objfile_ap->ParseHeader()) + return objfile_ap.release(); + } + return NULL; +} + + +static uint32_t +MachHeaderSizeFromMagic(uint32_t magic) +{ + switch (magic) + { + case MH_MAGIC: + case MH_CIGAM: + return sizeof(struct mach_header); + + case MH_MAGIC_64: + case MH_CIGAM_64: + return sizeof(struct mach_header_64); + break; + + default: + break; + } + return 0; +} + + +bool +ObjectFileMachO::MagicBytesMatch (DataBufferSP& dataSP) +{ + DataExtractor data(dataSP, eByteOrderHost, 4); + uint32_t offset = 0; + uint32_t magic = data.GetU32(&offset); + return MachHeaderSizeFromMagic(magic) != 0; +} + + +ObjectFileMachO::ObjectFileMachO(Module* module, DataBufferSP& dataSP, const FileSpec* file, addr_t offset, addr_t length) : + ObjectFile(module, file, offset, length, dataSP), + m_mutex (Mutex::eMutexTypeRecursive), + m_header(), + m_sections_ap(), + m_symtab_ap() +{ + ::bzero (&m_header, sizeof(m_header)); + ::bzero (&m_dysymtab, sizeof(m_dysymtab)); +} + + +ObjectFileMachO::~ObjectFileMachO() +{ +} + + +bool +ObjectFileMachO::ParseHeader () +{ + lldb_private::Mutex::Locker locker(m_mutex); + bool can_parse = false; + uint32_t offset = 0; + m_data.SetByteOrder (eByteOrderHost); + // Leave magic in the original byte order + m_header.magic = m_data.GetU32(&offset); + switch (m_header.magic) + { + case MH_MAGIC: + m_data.SetByteOrder (eByteOrderHost); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_MAGIC_64: + m_data.SetByteOrder (eByteOrderHost); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + case MH_CIGAM: + m_data.SetByteOrder(eByteOrderHost == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(4); + can_parse = true; + break; + + case MH_CIGAM_64: + m_data.SetByteOrder(eByteOrderHost == eByteOrderBig ? eByteOrderLittle : eByteOrderBig); + m_data.SetAddressByteSize(8); + can_parse = true; + break; + + default: + break; + } + + if (can_parse) + { + m_data.GetU32(&offset, &m_header.cputype, 6); + + ArchSpec mach_arch(m_header.cputype, m_header.cpusubtype); + if (mach_arch == m_module->GetArchitecture()) + { + // Read in all only the load command data + DataBufferSP data_sp(m_file.ReadFileContents(m_offset, m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic))); + m_data.SetData (data_sp); + return true; + } + } + else + { + memset(&m_header, 0, sizeof(struct mach_header)); + } + return false; +} + + +ByteOrder +ObjectFileMachO::GetByteOrder () const +{ + lldb_private::Mutex::Locker locker(m_mutex); + return m_data.GetByteOrder (); +} + + +size_t +ObjectFileMachO::GetAddressByteSize () const +{ + lldb_private::Mutex::Locker locker(m_mutex); + return m_data.GetAddressByteSize (); +} + + +Symtab * +ObjectFileMachO::GetSymtab() +{ + lldb_private::Mutex::Locker locker(m_mutex); + if (m_symtab_ap.get() == NULL) + { + m_symtab_ap.reset(new Symtab(this)); + ParseSymtab(false); + } + return m_symtab_ap.get(); +} + + +SectionList * +ObjectFileMachO::GetSectionList() +{ + lldb_private::Mutex::Locker locker(m_mutex); + if (m_sections_ap.get() == NULL) + { + m_sections_ap.reset(new SectionList()); + ParseSections(); + } + return m_sections_ap.get(); +} + + +size_t +ObjectFileMachO::ParseSections () +{ + lldb::user_id_t segID = 0; + lldb::user_id_t sectID = 0; + struct segment_command_64 load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + //bool dump_sections = false; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t load_cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + if (load_cmd.cmd == LC_SEGMENT || load_cmd.cmd == LC_SEGMENT_64) + { + if (m_data.GetU8(&offset, (uint8_t*)load_cmd.segname, 16)) + { + load_cmd.vmaddr = m_data.GetAddress(&offset); + load_cmd.vmsize = m_data.GetAddress(&offset); + load_cmd.fileoff = m_data.GetAddress(&offset); + load_cmd.filesize = m_data.GetAddress(&offset); + if (m_data.GetU32(&offset, &load_cmd.maxprot, 4)) + { + // Keep a list of mach segments around in case we need to + // get at data that isn't stored in the abstracted Sections. + m_mach_segments.push_back (load_cmd); + + ConstString segment_name (load_cmd.segname, std::min<int>(strlen(load_cmd.segname), sizeof(load_cmd.segname))); + // Use a segment ID of the segment index shifted left by 8 so they + // never conflict with any of the sections. + SectionSP segment_sp; + if (segment_name) + { + segment_sp.reset(new Section (NULL, + GetModule(), // Module to which this section belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + segment_name, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + load_cmd.vmaddr, // File VM address == addresses as they are found in the object file + load_cmd.vmsize, // VM size in bytes of this section + load_cmd.fileoff, // Offset to the data for this section in the file + load_cmd.filesize, // Size in bytes of this section as found in the the file + load_cmd.flags)); // Flags for this section + + m_sections_ap->AddSection(segment_sp); + } + + struct section_64 sect64; + ::bzero (§64, sizeof(sect64)); + // Push a section into our mach sections for the section at + // index zero (NO_SECT) + m_mach_sections.push_back(sect64); + uint32_t segment_sect_idx; + const lldb::user_id_t first_segment_sectID = sectID + 1; + + + const uint32_t num_u32s = load_cmd.cmd == LC_SEGMENT ? 7 : 8; + for (segment_sect_idx=0; segment_sect_idx<load_cmd.nsects; ++segment_sect_idx) + { + if (m_data.GetU8(&offset, (uint8_t*)sect64.sectname, sizeof(sect64.sectname)) == NULL) + break; + if (m_data.GetU8(&offset, (uint8_t*)sect64.segname, sizeof(sect64.segname)) == NULL) + break; + sect64.addr = m_data.GetAddress(&offset); + sect64.size = m_data.GetAddress(&offset); + + if (m_data.GetU32(&offset, §64.offset, num_u32s) == NULL) + break; + + // Keep a list of mach sections around in case we need to + // get at data that isn't stored in the abstracted Sections. + m_mach_sections.push_back (sect64); + + ConstString section_name (sect64.sectname, std::min<size_t>(strlen(sect64.sectname), sizeof(sect64.sectname))); + if (!segment_name) + { + // We have a segment with no name so we need to conjure up + // segments that correspond to the section's segname if there + // isn't already such a section. If there is such a section, + // we resize the section so that it spans all sections. + // We also mark these sections as fake so address matches don't + // hit if they land in the gaps between the child sections. + segment_name.SetTrimmedCStringWithLength(sect64.segname, sizeof(sect64.segname)); + segment_sp = m_sections_ap->FindSectionByName (segment_name); + if (segment_sp.get()) + { + Section *segment = segment_sp.get(); + // Grow the section size as needed. + const lldb::addr_t sect64_min_addr = sect64.addr; + const lldb::addr_t sect64_max_addr = sect64_min_addr + sect64.size; + const lldb::addr_t curr_seg_byte_size = segment->GetByteSize(); + const lldb::addr_t curr_seg_min_addr = segment->GetFileAddress(); + const lldb::addr_t curr_seg_max_addr = curr_seg_min_addr + curr_seg_byte_size; + if (sect64_min_addr >= curr_seg_min_addr) + { + const lldb::addr_t new_seg_byte_size = sect64_max_addr - curr_seg_min_addr; + // Only grow the section size if needed + if (new_seg_byte_size > curr_seg_byte_size) + segment->SetByteSize (new_seg_byte_size); + } + else + { + // We need to change the base address of the segment and + // adjust the child section offsets for all existing children. + const lldb::addr_t slide_amount = sect64_min_addr - curr_seg_min_addr; + segment->Slide(slide_amount, false); + segment->GetChildren().Slide (-slide_amount, false); + segment->SetByteSize (curr_seg_max_addr - sect64_min_addr); + } + } + else + { + // Create a fake section for the section's named segment + segment_sp.reset(new Section(segment_sp.get(), // Parent section + GetModule(), // Module to which this section belongs + ++segID << 8, // Section ID is the 1 based segment index shifted right by 8 bits as not to collide with any of the 256 section IDs that are possible + segment_name, // Name of this section + eSectionTypeContainer, // This section is a container of other sections. + sect64.addr, // File VM address == addresses as they are found in the object file + sect64.size, // VM size in bytes of this section + sect64.offset, // Offset to the data for this section in the file + sect64.offset ? sect64.size : 0, // Size in bytes of this section as found in the the file + load_cmd.flags)); // Flags for this section + segment_sp->SetIsFake(true); + m_sections_ap->AddSection(segment_sp); + } + } + assert (segment_sp.get()); + + uint32_t mach_sect_type = sect64.flags & SECTION_TYPE; + static ConstString g_sect_name_objc_data ("__objc_data"); + static ConstString g_sect_name_objc_msgrefs ("__objc_msgrefs"); + static ConstString g_sect_name_objc_selrefs ("__objc_selrefs"); + static ConstString g_sect_name_objc_classrefs ("__objc_classrefs"); + static ConstString g_sect_name_objc_superrefs ("__objc_superrefs"); + static ConstString g_sect_name_objc_const ("__objc_const"); + static ConstString g_sect_name_objc_classlist ("__objc_classlist"); + static ConstString g_sect_name_cfstring ("__cfstring"); + SectionType sect_type = eSectionTypeOther; + + if (section_name == g_sect_name_objc_selrefs) + { + sect_type = eSectionTypeDataCStringPointers; + } + else if (section_name == g_sect_name_objc_msgrefs) + { + sect_type = eSectionTypeDataObjCMessageRefs; + } + else if (section_name == g_sect_name_objc_data || + section_name == g_sect_name_objc_classrefs || + section_name == g_sect_name_objc_superrefs || + section_name == g_sect_name_objc_const || + section_name == g_sect_name_objc_classlist) + { + sect_type = eSectionTypeDataPointers; + } + else if (section_name == g_sect_name_cfstring) + { + sect_type = eSectionTypeDataObjCCFStrings; + } + + if (sect_type == eSectionTypeOther) + { + switch (mach_sect_type) + { + // TODO: categorize sections by other flags for regular sections + case S_REGULAR: + + sect_type = eSectionTypeOther; + break; + case S_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_CSTRING_LITERALS: sect_type = eSectionTypeDataCString; break; // section with only literal C strings + case S_4BYTE_LITERALS: sect_type = eSectionTypeData4; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: sect_type = eSectionTypeData8; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: sect_type = eSectionTypeCode; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: sect_type = eSectionTypeDataPointers; break; // section with only function pointers for termination + case S_COALESCED: sect_type = eSectionTypeOther; break; + case S_GB_ZEROFILL: sect_type = eSectionTypeZeroFill; break; + case S_INTERPOSING: sect_type = eSectionTypeCode; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: sect_type = eSectionTypeData16; break; // section with only 16 byte literals + case S_DTRACE_DOF: sect_type = eSectionTypeDebug; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: sect_type = eSectionTypeDataPointers; break; + default: break; + } + } + + SectionSP section_sp(new Section(segment_sp.get(), + GetModule(), + ++sectID, + section_name, + sect_type, + sect64.addr - segment_sp->GetFileAddress(), + sect64.size, + sect64.offset, + sect64.offset == 0 ? 0 : sect64.size, + sect64.flags)); + segment_sp->GetChildren().AddSection(section_sp); + + if (segment_sp->IsFake()) + { + segment_sp.reset(); + segment_name.Clear(); + } + } + if (m_header.filetype == MH_DSYM) + { + if (first_segment_sectID <= sectID) + { + lldb::user_id_t sect_uid; + for (sect_uid = first_segment_sectID; sect_uid <= sectID; ++sect_uid) + { + SectionSP curr_section_sp(segment_sp->GetChildren().FindSectionByID (sect_uid)); + SectionSP next_section_sp; + if (sect_uid + 1 <= sectID) + next_section_sp = segment_sp->GetChildren().FindSectionByID (sect_uid+1); + + if (curr_section_sp.get()) + { + if (curr_section_sp->GetByteSize() == 0) + { + if (next_section_sp.get() != NULL) + curr_section_sp->SetByteSize ( next_section_sp->GetFileAddress() - curr_section_sp->GetFileAddress() ); + else + curr_section_sp->SetByteSize ( load_cmd.vmsize ); + } + } + } + } + } + } + } + } + else if (load_cmd.cmd == LC_DYSYMTAB) + { + m_dysymtab.cmd = load_cmd.cmd; + m_dysymtab.cmdsize = load_cmd.cmdsize; + m_data.GetU32 (&offset, &m_dysymtab.ilocalsym, (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2); + } + + offset = load_cmd_offset + load_cmd.cmdsize; + } +// if (dump_sections) +// { +// StreamFile s(stdout); +// m_sections_ap->Dump(&s, true); +// } + return sectID; // Return the number of sections we registered with the module +} + +class MachSymtabSectionInfo +{ +public: + + MachSymtabSectionInfo (SectionList *section_list) : + m_section_list (section_list), + m_section_infos() + { + // Get the number of sections down to a depth of 1 to include + // all segments and their sections, but no other sections that + // may be added for debug map or + m_section_infos.resize(section_list->GetNumSections(1)); + } + + + Section * + GetSection (uint8_t n_sect, addr_t file_addr) + { + if (n_sect == 0) + return NULL; + if (n_sect < m_section_infos.size()) + { + if (m_section_infos[n_sect].section == NULL) + { + Section *section = m_section_list->FindSectionByID (n_sect).get(); + m_section_infos[n_sect].section = section; + assert (section != NULL); + m_section_infos[n_sect].vm_range.SetBaseAddress (section->GetFileAddress()); + m_section_infos[n_sect].vm_range.SetByteSize (section->GetByteSize()); + } + if (m_section_infos[n_sect].vm_range.Contains(file_addr)) + return m_section_infos[n_sect].section; + } + return m_section_list->FindSectionContainingFileAddress(file_addr).get(); + } + +protected: + struct SectionInfo + { + SectionInfo () : + vm_range(), + section (NULL) + { + } + + VMRange vm_range; + Section *section; + }; + SectionList *m_section_list; + std::vector<SectionInfo> m_section_infos; +}; + + + +size_t +ObjectFileMachO::ParseSymtab (bool minimize) +{ + Timer scoped_timer(__PRETTY_FUNCTION__, + "ObjectFileMachO::ParseSymtab () module = %s", + m_file.GetFilename().AsCString("")); + struct symtab_command symtab_load_command; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + // Read in the load command and load command size + if (m_data.GetU32(&offset, &symtab_load_command, 2) == NULL) + break; + // Watch for the symbol table load command + if (symtab_load_command.cmd == LC_SYMTAB) + { + // Read in the rest of the symtab load command + if (m_data.GetU32(&offset, &symtab_load_command.symoff, 4)) + { + Symtab *symtab = m_symtab_ap.get(); + SectionList *section_list = GetSectionList(); + assert(section_list); + const size_t addr_size = m_data.GetAddressByteSize(); + const ByteOrder endian = m_data.GetByteOrder(); + bool bit_width_32 = addr_size == 4; + const size_t nlist_size = bit_width_32 ? sizeof(struct nlist) : sizeof(struct nlist_64); + + DataBufferSP symtab_data_sp(m_file.ReadFileContents(m_offset + symtab_load_command.symoff, symtab_load_command.nsyms * nlist_size)); + DataBufferSP strtab_data_sp(m_file.ReadFileContents(m_offset + symtab_load_command.stroff, symtab_load_command.strsize)); + + const char *strtab_data = (const char *)strtab_data_sp->GetBytes(); +// DataExtractor symtab_data(symtab_data_sp, endian, addr_size); +// DataExtractor strtab_data(strtab_data_sp, endian, addr_size); + + static ConstString g_segment_name_TEXT ("__TEXT"); + static ConstString g_segment_name_DATA ("__DATA"); + static ConstString g_segment_name_OBJC ("__OBJC"); + static ConstString g_section_name_eh_frame ("__eh_frame"); + SectionSP text_section_sp(section_list->FindSectionByName(g_segment_name_TEXT)); + SectionSP data_section_sp(section_list->FindSectionByName(g_segment_name_DATA)); + SectionSP objc_section_sp(section_list->FindSectionByName(g_segment_name_OBJC)); + SectionSP eh_frame_section_sp; + if (text_section_sp.get()) + eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName (g_section_name_eh_frame); + else + eh_frame_section_sp = section_list->FindSectionByName (g_section_name_eh_frame); + + uint8_t TEXT_eh_frame_sectID = eh_frame_section_sp.get() ? eh_frame_section_sp->GetID() : NO_SECT; + //uint32_t symtab_offset = 0; + const uint8_t* nlist_data = symtab_data_sp->GetBytes(); + assert (symtab_data_sp->GetByteSize()/nlist_size >= symtab_load_command.nsyms); + + + if (endian != eByteOrderHost) + { + // ... + assert (!"UNIMPLEMENTED: Swap all nlist entries"); + } + uint32_t N_SO_index = UINT_MAX; + + MachSymtabSectionInfo section_info (section_list); + std::vector<uint32_t> N_FUN_indexes; + std::vector<uint32_t> N_NSYM_indexes; + std::vector<uint32_t> N_INCL_indexes; + std::vector<uint32_t> N_BRAC_indexes; + std::vector<uint32_t> N_COMM_indexes; + uint32_t nlist_idx = 0; + Symbol *symbol_ptr = NULL; + + uint32_t sym_idx = 0; + Symbol *sym = symtab->Resize (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + uint32_t num_syms = symtab->GetNumSymbols(); + + //symtab->Reserve (symtab_load_command.nsyms + m_dysymtab.nindirectsyms); + for (nlist_idx = 0; nlist_idx < symtab_load_command.nsyms; ++nlist_idx) + { + struct nlist_64 nlist; + if (bit_width_32) + { + struct nlist* nlist32_ptr = (struct nlist*)(nlist_data + (nlist_idx * nlist_size)); + nlist.n_un.n_strx = nlist32_ptr->n_un.n_strx; + nlist.n_type = nlist32_ptr->n_type; + nlist.n_sect = nlist32_ptr->n_sect; + nlist.n_desc = nlist32_ptr->n_desc; + nlist.n_value = nlist32_ptr->n_value; + } + else + { + nlist = *((struct nlist_64*)(nlist_data + (nlist_idx * nlist_size))); + } + + SymbolType type = eSymbolTypeInvalid; + const char* symbol_name = &strtab_data[nlist.n_un.n_strx]; + if (symbol_name[0] == '\0') + symbol_name = NULL; + Section* symbol_section = NULL; + bool add_nlist = true; + bool is_debug = ((nlist.n_type & N_STAB) != 0); + + assert (sym_idx < num_syms); + + sym[sym_idx].SetDebug (is_debug); + + if (is_debug) + { + switch (nlist.n_type) + { + case N_GSYM: // global symbol: name,,NO_SECT,type,0 + // Sometimes the N_GSYM value contains the address. + if (nlist.n_value != 0) + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeGlobal; + break; + + case N_FNAME: // procedure name (f77 kludge): name,,NO_SECT,0,0 + type = eSymbolTypeFunction; + break; + + case N_FUN: // procedure: name,,n_sect,linenumber,address + if (symbol_name) + { + type = eSymbolTypeFunction; + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_FUN_indexes.push_back(sym_idx); + } + else + { + type = eSymbolTypeFunctionEnd; + + if ( !N_FUN_indexes.empty() ) + { + // Copy the size of the function into the original STAB entry so we don't have + // to hunt for it later + symtab->SymbolAtIndex(N_FUN_indexes.back())->SetByteSize(nlist.n_value); + N_FUN_indexes.pop_back(); + // We dont' really need the end function STAB as it contains the size which + // we already placed with the original symbol, so don't add it if we want a + // minimal symbol table + if (minimize) + add_nlist = false; + } + } + break; + + case N_STSYM: // static symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeStatic; + break; + + case N_LCSYM: // .lcomm symbol: name,,n_sect,type,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeCommonBlock; + break; + + case N_BNSYM: + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + if (minimize) + { + // Skip these if we want minimal symbol tables + add_nlist = false; + } + else + { + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_NSYM_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + } + break; + + case N_ENSYM: + // Set the size of the N_BNSYM to the terminating index of this N_ENSYM + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if (minimize) + { + // Skip these if we want minimal symbol tables + add_nlist = false; + } + else + { + if ( !N_NSYM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_NSYM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_NSYM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + } + break; + + + case N_OPT: // emitted with gcc2_compiled and in gcc source + type = eSymbolTypeCompiler; + break; + + case N_RSYM: // register sym: name,,NO_SECT,type,register + type = eSymbolTypeVariable; + break; + + case N_SLINE: // src line: 0,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + case N_SSYM: // structure elt: name,,NO_SECT,type,struct_offset + type = eSymbolTypeVariableType; + break; + + case N_SO: + type = eSymbolTypeSourceFile; + if (symbol_name == NULL) + { + if (N_SO_index == UINT_MAX) + { + // Skip the extra blank N_SO entries that happen when the entire + // path is contained in the second consecutive N_SO STAB. + if (minimize) + add_nlist = false; + } + else + { + // Set the size of the N_SO to the terminating index of this N_SO + // so that we can always skip the entire N_SO if we need to navigate + // more quickly at the source level when parsing STABS + symbol_ptr = symtab->SymbolAtIndex(N_SO_index); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + } + N_NSYM_indexes.clear(); + N_INCL_indexes.clear(); + N_BRAC_indexes.clear(); + N_COMM_indexes.clear(); + N_FUN_indexes.clear(); + N_SO_index = UINT_MAX; + } + else if (symbol_name[0] == '/') + { + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_SO_index = sym_idx; + } + break; + + case N_OSO: // object file name: name,,0,0,st_mtime + type = eSymbolTypeObjectFile; + break; + + case N_LSYM: // local sym: name,,NO_SECT,type,offset + type = eSymbolTypeLocal; + break; + + //---------------------------------------------------------------------- + // INCL scopes + //---------------------------------------------------------------------- + case N_BINCL: // include file beginning: name,,NO_SECT,0,sum + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + N_INCL_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + case N_EINCL: // include file end: name,,NO_SECT,0,0 + + // Set the size of the N_BINCL to the terminating index of this N_EINCL + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_INCL_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_INCL_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_SOL: // #included file name: name,,n_sect,0,address + type = eSymbolTypeHeaderFile; + break; + + case N_PARAMS: // compiler parameters: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_VERSION: // compiler version: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_OLEVEL: // compiler -O level: name,,NO_SECT,0,0 + type = eSymbolTypeCompiler; + break; + + case N_PSYM: // parameter: name,,NO_SECT,type,offset + type = eSymbolTypeVariable; + break; + + case N_ENTRY: // alternate entry: name,,n_sect,linenumber,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + type = eSymbolTypeLineEntry; + break; + + //---------------------------------------------------------------------- + // Left and Right Braces + //---------------------------------------------------------------------- + case N_LBRAC: // left bracket: 0,,NO_SECT,nesting level,address + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + N_BRAC_indexes.push_back(sym_idx); + type = eSymbolTypeScopeBegin; + break; + + case N_RBRAC: // right bracket: 0,,NO_SECT,nesting level,address + // Set the size of the N_LBRAC to the terminating index of this N_RBRAC + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + if ( !N_BRAC_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_BRAC_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_EXCL: // deleted include file: name,,NO_SECT,0,sum + type = eSymbolTypeHeaderFile; + break; + + //---------------------------------------------------------------------- + // COMM scopes + //---------------------------------------------------------------------- + case N_BCOMM: // begin common: name,,NO_SECT,0,0 + // We use the current number of symbols in the symbol table in lieu of + // using nlist_idx in case we ever start trimming entries out + type = eSymbolTypeScopeBegin; + N_COMM_indexes.push_back(sym_idx); + break; + + case N_ECOML: // end common (local name): 0,,n_sect,0,address + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + // Fall through + + case N_ECOMM: // end common: name,,n_sect,0,0 + // Set the size of the N_BCOMM to the terminating index of this N_ECOMM/N_ECOML + // so that we can always skip the entire symbol if we need to navigate + // more quickly at the source level when parsing STABS + if ( !N_COMM_indexes.empty() ) + { + symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back()); + symbol_ptr->SetByteSize(sym_idx + 1); + symbol_ptr->SetSizeIsSibling(true); + N_COMM_indexes.pop_back(); + } + type = eSymbolTypeScopeEnd; + break; + + case N_LENG: // second stab entry with length information + type = eSymbolTypeAdditional; + break; + + default: break; + } + } + else + { + //uint8_t n_pext = N_PEXT & nlist.n_type; + uint8_t n_type = N_TYPE & nlist.n_type; + sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0); + + if (symbol_name && ::strstr (symbol_name, ".objc") == symbol_name) + { + type = eSymbolTypeRuntime; + } + else + { + switch (n_type) + { + case N_INDR: // Fall through + case N_PBUD: // Fall through + case N_UNDF: + type = eSymbolTypeExtern; + break; + + case N_ABS: + type = eSymbolTypeAbsolute; + break; + + case N_SECT: + symbol_section = section_info.GetSection (nlist.n_sect, nlist.n_value); + + assert(symbol_section != NULL); + if (TEXT_eh_frame_sectID == nlist.n_sect) + { + type = eSymbolTypeException; + } + else + { + uint32_t section_type = symbol_section->GetAllFlagBits() & SECTION_TYPE; + + switch (section_type) + { + case S_REGULAR: break; // regular section + //case S_ZEROFILL: type = eSymbolTypeData; break; // zero fill on demand section + case S_CSTRING_LITERALS: type = eSymbolTypeData; break; // section with only literal C strings + case S_4BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 4 byte literals + case S_8BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 8 byte literals + case S_LITERAL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only pointers to literals + case S_NON_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only non-lazy symbol pointers + case S_LAZY_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; // section with only lazy symbol pointers + case S_SYMBOL_STUBS: type = eSymbolTypeTrampoline; break; // section with only symbol stubs, byte size of stub in the reserved2 field + case S_MOD_INIT_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for initialization + case S_MOD_TERM_FUNC_POINTERS: type = eSymbolTypeCode; break; // section with only function pointers for termination + //case S_COALESCED: type = eSymbolType; break; // section contains symbols that are to be coalesced + //case S_GB_ZEROFILL: type = eSymbolTypeData; break; // zero fill on demand section (that can be larger than 4 gigabytes) + case S_INTERPOSING: type = eSymbolTypeTrampoline; break; // section with only pairs of function pointers for interposing + case S_16BYTE_LITERALS: type = eSymbolTypeData; break; // section with only 16 byte literals + case S_DTRACE_DOF: type = eSymbolTypeInstrumentation; break; + case S_LAZY_DYLIB_SYMBOL_POINTERS: type = eSymbolTypeTrampoline; break; + default: break; + } + + if (type == eSymbolTypeInvalid) + { + const char *symbol_sect_name = symbol_section->GetName().AsCString(); + if (symbol_section->IsDescendant (text_section_sp.get())) + { + if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SELF_MODIFYING_CODE | S_ATTR_SOME_INSTRUCTIONS)) + type = eSymbolTypeData; + else + type = eSymbolTypeCode; + } + else + if (symbol_section->IsDescendant(data_section_sp.get())) + { + if (symbol_sect_name && ::strstr (symbol_sect_name, "__objc") == symbol_sect_name) + { + type = eSymbolTypeRuntime; + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__gcc_except_tab") == symbol_sect_name) + { + type = eSymbolTypeException; + } + else + { + type = eSymbolTypeData; + } + } + else + if (symbol_sect_name && ::strstr (symbol_sect_name, "__IMPORT") == symbol_sect_name) + { + type = eSymbolTypeTrampoline; + } + else + if (symbol_section->IsDescendant(objc_section_sp.get())) + { + type = eSymbolTypeRuntime; + } + } + } + break; + } + } + } + + if (add_nlist) + { + bool symbol_name_is_mangled = false; + if (symbol_name && symbol_name[0] == '_') + { + symbol_name_is_mangled = symbol_name[1] == '_'; + symbol_name++; // Skip the leading underscore + } + uint64_t symbol_value = nlist.n_value; + if (symbol_section != NULL) + symbol_value -= symbol_section->GetFileAddress(); + + sym[sym_idx].SetID (nlist_idx); + sym[sym_idx].SetType (type); + if (symbol_name) + sym[sym_idx].GetMangled().SetValue(symbol_name, symbol_name_is_mangled); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress().SetSection (symbol_section); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress().SetOffset (symbol_value); + sym[sym_idx].SetFlags (nlist.n_type << 16 | nlist.n_desc); + + ++sym_idx; + } + else + { + sym[sym_idx].Clear(); + } + + } + + + // STAB N_GSYM entries end up having a symbol type eSymbolTypeGlobal and when the symbol value + // is zero, the address of the global ends up being in a non-STAB entry. Try and fix up all + // such entries by figuring out what the address for the global is by looking up this non-STAB + // entry and copying the value into the debug symbol's value to save us the hassle in the + // debug symbol parser. + + Symbol *global_symbol = NULL; + for (nlist_idx = 0; + nlist_idx < symtab_load_command.nsyms && (global_symbol = symtab->FindSymbolWithType(eSymbolTypeGlobal, nlist_idx)) != NULL; + nlist_idx++) + { + if (global_symbol->GetValue().GetFileAddress() == 0) + { + std::vector<uint32_t> indexes; + if (symtab->AppendSymbolIndexesWithName(global_symbol->GetMangled().GetName(), indexes) > 0) + { + std::vector<uint32_t>::const_iterator pos; + std::vector<uint32_t>::const_iterator end = indexes.end(); + for (pos = indexes.begin(); pos != end; ++pos) + { + symbol_ptr = symtab->SymbolAtIndex(*pos); + if (symbol_ptr != global_symbol && symbol_ptr->IsDebug() == false) + { + global_symbol->SetValue(symbol_ptr->GetValue()); + break; + } + } + } + } + } + // Now synthesize indirect symbols + if (m_dysymtab.nindirectsyms != 0) + { + DataBufferSP indirect_symbol_indexes_sp(m_file.ReadFileContents(m_offset + m_dysymtab.indirectsymoff, m_dysymtab.nindirectsyms * 4)); + + if (indirect_symbol_indexes_sp && indirect_symbol_indexes_sp->GetByteSize()) + { + DataExtractor indirect_symbol_index_data (indirect_symbol_indexes_sp, m_data.GetByteOrder(), m_data.GetAddressByteSize()); + + for (uint32_t sect_idx = 1; sect_idx < m_mach_sections.size(); ++sect_idx) + { + if ((m_mach_sections[sect_idx].flags & SECTION_TYPE) == S_SYMBOL_STUBS) + { + uint32_t symbol_stub_byte_size = m_mach_sections[sect_idx].reserved2; + if (symbol_stub_byte_size == 0) + continue; + + const uint32_t num_symbol_stubs = m_mach_sections[sect_idx].size / symbol_stub_byte_size; + + if (num_symbol_stubs == 0) + continue; + + const uint32_t symbol_stub_index_offset = m_mach_sections[sect_idx].reserved1; + uint32_t stub_sym_id = symtab_load_command.nsyms; + for (uint32_t stub_idx = 0; stub_idx < num_symbol_stubs; ++stub_idx) + { + const uint32_t symbol_stub_index = symbol_stub_index_offset + stub_idx; + const lldb::addr_t symbol_stub_addr = m_mach_sections[sect_idx].addr + (stub_idx * symbol_stub_byte_size); + uint32_t symbol_stub_offset = symbol_stub_index * 4; + if (indirect_symbol_index_data.ValidOffsetForDataOfSize(symbol_stub_offset, 4)) + { + const uint32_t symbol_index = indirect_symbol_index_data.GetU32 (&symbol_stub_offset); + + Symbol *stub_symbol = symtab->SymbolAtIndex(symbol_index); + if (stub_symbol) + { + Address so_addr(symbol_stub_addr, section_list); + + if (stub_symbol->GetType() == eSymbolTypeExtern) + { + // Change the external symbol into a trampoline that makes sense + // These symbols were N_UNDF N_EXT, and are useless to us, so we + // can re-use them so we don't have to make up a synthetic symbol + // for no good reason. + stub_symbol->SetType (eSymbolTypeTrampoline); + stub_symbol->SetExternal (false); + stub_symbol->GetAddressRangeRef().GetBaseAddress() = so_addr; + stub_symbol->GetAddressRangeRef().SetByteSize (symbol_stub_byte_size); + } + else + { + // Make a synthetic symbol to describe the trampoline stub + if (sym_idx >= num_syms) + { + sym = symtab->Resize (num_syms + 16); + num_syms = symtab->GetNumSymbols(); + } + sym[sym_idx].SetID (stub_sym_id++); + sym[sym_idx].GetMangled() = stub_symbol->GetMangled(); + sym[sym_idx].SetType (eSymbolTypeTrampoline); + sym[sym_idx].SetIsSynthetic (true); + sym[sym_idx].GetAddressRangeRef().GetBaseAddress() = so_addr; + sym[sym_idx].GetAddressRangeRef().SetByteSize (symbol_stub_byte_size); + ++sym_idx; + } + } + } + } + } + } + } + } + + if (sym_idx != symtab->GetNumSymbols()) + symtab->Resize (sym_idx); + + return symtab->GetNumSymbols(); + } + } + offset = cmd_offset + symtab_load_command.cmdsize; + } + return 0; +} + + +void +ObjectFileMachO::Dump (Stream *s) +{ + lldb_private::Mutex::Locker locker(m_mutex); + s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); + s->Indent(); + if (m_header.magic == MH_MAGIC_64 || m_header.magic == MH_CIGAM_64) + s->PutCString("ObjectFileMachO64"); + else + s->PutCString("ObjectFileMachO32"); + + ArchSpec header_arch(m_header.cputype, m_header.cpusubtype); + + *s << ", file = '" << m_file << "', arch = " << header_arch.AsCString() << "\n"; + + if (m_sections_ap.get()) + m_sections_ap->Dump(s, NULL, true); + + if (m_symtab_ap.get()) + m_symtab_ap->Dump(s, NULL); +} + + +bool +ObjectFileMachO::GetUUID (UUID* uuid) +{ + lldb_private::Mutex::Locker locker(m_mutex); + struct uuid_command load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + if (load_cmd.cmd == LC_UUID) + { + const uint8_t *uuid_bytes = m_data.PeekData(offset, 16); + if (uuid_bytes) + { + uuid->SetBytes (uuid_bytes); + return true; + } + return false; + } + offset = cmd_offset + load_cmd.cmdsize; + } + return false; +} + + +uint32_t +ObjectFileMachO::GetDependentModules (FileSpecList& files) +{ + lldb_private::Mutex::Locker locker(m_mutex); + struct load_command load_cmd; + uint32_t offset = MachHeaderSizeFromMagic(m_header.magic); + uint32_t count = 0; + uint32_t i; + for (i=0; i<m_header.ncmds; ++i) + { + const uint32_t cmd_offset = offset; + if (m_data.GetU32(&offset, &load_cmd, 2) == NULL) + break; + + switch (load_cmd.cmd) + { + case LC_LOAD_DYLIB: + case LC_LOAD_WEAK_DYLIB: + case LC_REEXPORT_DYLIB: + case LC_LOAD_DYLINKER: + case LC_LOADFVMLIB: + { + uint32_t name_offset = cmd_offset + m_data.GetU32(&offset); + const char *path = m_data.PeekCStr(name_offset); + // Skip any path that starts with '@' since these are usually: + // @executable_path/.../file + // @rpath/.../file + if (path && path[0] != '@') + { + FileSpec file_spec(path); + if (files.AppendIfUnique(file_spec)) + count++; + } + } + break; + + default: + break; + } + offset = cmd_offset + load_cmd.cmdsize; + } + return count; +} + +bool +ObjectFileMachO::GetTargetTriple (ConstString &target_triple) +{ + lldb_private::Mutex::Locker locker(m_mutex); + std::string triple(GetModule()->GetArchitecture().AsCString()); + triple += "-apple-darwin"; + target_triple.SetCString(triple.c_str()); + if (target_triple) + return true; + return false; +} + + +//------------------------------------------------------------------ +// PluginInterface protocol +//------------------------------------------------------------------ +const char * +ObjectFileMachO::GetPluginName() +{ + return "ObjectFileMachO"; +} + +const char * +ObjectFileMachO::GetShortPluginName() +{ + return GetPluginNameStatic(); +} + +uint32_t +ObjectFileMachO::GetPluginVersion() +{ + return 1; +} + +void +ObjectFileMachO::GetPluginCommandHelp (const char *command, Stream *strm) +{ +} + +Error +ObjectFileMachO::ExecutePluginCommand (Args &command, Stream *strm) +{ + Error error; + error.SetErrorString("No plug-in command are currently supported."); + return error; +} + +Log * +ObjectFileMachO::EnablePluginLogging (Stream *strm, Args &command) +{ + return NULL; +} + + + + diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h new file mode 100644 index 00000000000..3ffeb242b7c --- /dev/null +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.h @@ -0,0 +1,131 @@ +//===-- ObjectFileMachO.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_ObjectFileMachO_h_ +#define liblldb_ObjectFileMachO_h_ + +#include <mach-o/loader.h> +#include "lldb/Core/FileSpec.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Symbol/ObjectFile.h" + +//---------------------------------------------------------------------- +// This class needs to be hidden as eventually belongs in a plugin that +// will export the ObjectFile protocol +//---------------------------------------------------------------------- +class ObjectFileMachO : + public lldb_private::ObjectFile +{ +public: + //------------------------------------------------------------------ + // Static Functions + //------------------------------------------------------------------ + static void + Initialize(); + + static void + Terminate(); + + static const char * + GetPluginNameStatic(); + + static const char * + GetPluginDescriptionStatic(); + + static ObjectFile * + CreateInstance (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + static bool + MagicBytesMatch (lldb::DataBufferSP& dataSP); + + //------------------------------------------------------------------ + // Member Functions + //------------------------------------------------------------------ + ObjectFileMachO (lldb_private::Module* module, + lldb::DataBufferSP& dataSP, + const lldb_private::FileSpec* file, + lldb::addr_t offset, + lldb::addr_t length); + + virtual + ~ObjectFileMachO(); + + virtual bool + ParseHeader (); + + virtual lldb::ByteOrder + GetByteOrder () const; + + virtual size_t + GetAddressByteSize () const; + + virtual lldb_private::Symtab * + GetSymtab(); + + virtual lldb_private::SectionList * + GetSectionList(); + + virtual void + Dump (lldb_private::Stream *s); + + virtual bool + GetTargetTriple (lldb_private::ConstString &target_triple); + + virtual bool + GetUUID (lldb_private::UUID* uuid); + + virtual uint32_t + GetDependentModules (lldb_private::FileSpecList& files); + + //------------------------------------------------------------------ + // PluginInterface protocol + //------------------------------------------------------------------ + virtual const char * + GetPluginName(); + + virtual const char * + GetShortPluginName(); + + virtual uint32_t + GetPluginVersion(); + + virtual void + GetPluginCommandHelp (const char *command, lldb_private::Stream *strm); + + virtual lldb_private::Error + ExecutePluginCommand (lldb_private::Args &command, lldb_private::Stream *strm); + + virtual lldb_private::Log * + EnablePluginLogging (lldb_private::Stream *strm, lldb_private::Args &command); + + + +protected: + mutable lldb_private::Mutex m_mutex; + struct mach_header m_header; + mutable std::auto_ptr<lldb_private::SectionList> m_sections_ap; + mutable std::auto_ptr<lldb_private::Symtab> m_symtab_ap; + + struct dysymtab_command m_dysymtab; + std::vector<struct segment_command_64> m_mach_segments; + std::vector<struct section_64> m_mach_sections; + + size_t + ParseSections (); + + size_t + ParseSymtab (bool minimize); + +}; + +#endif // liblldb_ObjectFileMachO_h_ |