From 3e768ed9c01a244cdb1bc0d6aec34fb25821fbcc Mon Sep 17 00:00:00 2001 From: jimblandy Date: Tue, 16 Mar 2010 16:31:49 +0000 Subject: Breakpad Linux dumper: Add support for dumping DWARF CFI as STACK CFI records. Define a new DWARF parser class, dwarf2reader::CallFrameInfo. Extend google_breakpad::Module to store and write out 'STACK CFI' records. Define a new google_breakpad::DwarfCFIToModule class, to accept DWARF CFI data from the parser and populate a Module with the equivalent STACK CFI records. Extend the Linux symbol dumping tool, dump_syms, to use dwarf2reader::CallFrameInfo, google_breakpad::DwarfCFIToModule, and google_breakpad::Module to extract DWARF CFI from the executable or shared library files and write it to the Breakpad symbol file. Define CFISection, a new class derived from TestAssembler::Section, for use in creating DWARF CFI data for test cases. a=jimblandy, r=nealsid git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@550 4c0a9323-5329-0410-9bdc-e9ce6186880e --- src/common/linux/dump_symbols.cc | 133 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) (limited to 'src/common/linux/dump_symbols.cc') diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 993bdbd5..79d7ad36 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -49,6 +49,7 @@ #include "common/dwarf/dwarf2diehandler.h" #include "common/linux/dump_stabs.h" #include "common/linux/dump_symbols.h" +#include "common/linux/dwarf_cfi_to_module.h" #include "common/linux/dwarf_cu_to_module.h" #include "common/linux/dwarf_line_to_module.h" #include "common/linux/file_id.h" @@ -59,6 +60,7 @@ namespace { using google_breakpad::DumpStabsHandler; +using google_breakpad::DwarfCFIToModule; using google_breakpad::DwarfCUToModule; using google_breakpad::DwarfLineToModule; using google_breakpad::Module; @@ -215,6 +217,119 @@ static bool LoadDwarf(const string &dwarf_filename, return true; } +// Fill REGISTER_NAMES with the register names appropriate to the +// machine architecture given in HEADER, indexed by the register +// numbers used in DWARF call frame information. Return true on +// success, or false if we don't recognize HEADER's machine +// architecture. +static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header, + vector *register_names) +{ + static const char *const i386_names[] = { + "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", + "$eip", "$eflags", "$unused1", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$unused2", "$unused3", + "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$fcw", "$fsw", "$mxcsr", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", + "$tr", "$ldtr", + NULL + }; + + static const char *const x86_64_names[] = { + "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", + "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", + "$rip", + "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$rflags", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", + "$fs.base", "$gs.base", "$unused3", "$unused4", + "$tr", "$ldtr", + "$mxcsr", "$fcw", "$fsw", + NULL + }; + + const char * const *name_table; + switch (elf_header->e_machine) { + case EM_386: + name_table = i386_names; + break; + + case EM_X86_64: + name_table = x86_64_names; + break; + + default: + return false; + } + + register_names->clear(); + for (int i = 0; name_table[i]; i++) + register_names->push_back(name_table[i]); + return true; +} + +static bool LoadDwarfCFI(const string &dwarf_filename, + const ElfW(Ehdr) *elf_header, + const char *section_name, + const ElfW(Shdr) *section, + Module *module) { + // Find the appropriate set of register names for this file's + // architecture. + vector register_names; + if (!DwarfCFIRegisterNames(elf_header, ®ister_names)) { + fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" + " cannot convert DWARF call frame information\n", + dwarf_filename.c_str(), elf_header->e_machine); + return false; + } + + // Figure out what endianness this file is. + dwarf2reader::Endianness endianness; + if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) + endianness = dwarf2reader::ENDIANNESS_LITTLE; + else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) + endianness = dwarf2reader::ENDIANNESS_BIG; + else { + fprintf(stderr, "%s: bad data encoding in ELF header: %d\n", + dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]); + return false; + } + + // Find the call frame information and its size. + const char *cfi = reinterpret_cast(section->sh_offset); + size_t cfi_size = section->sh_size; + + // Plug together the parser, handler, and their entourages. + DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); + DwarfCFIToModule handler(module, register_names, &module_reporter); + dwarf2reader::ByteReader byte_reader(endianness); + // Since we're using the ElfW macro, we're not actually capable of + // processing both ELF32 and ELF64 files with the same program; that + // would take a bit more work. But this will work out well enough. + if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) + byte_reader.SetAddressSize(4); + else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) + byte_reader.SetAddressSize(8); + else { + fprintf(stderr, "%s: bad file class in ELF header: %d\n", + dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]); + return false; + } + + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, + section_name); + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, &byte_reader, + &handler, &dwarf_reporter); + parser.Start(); + return true; +} + static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header, Module *module) { // Translate all offsets in section headers into address. @@ -228,6 +343,8 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header, reinterpret_cast(elf_header->e_shoff); const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; bool found_debug_info_section = false; + + // Look for STABS debugging information, and load it if present. const ElfW(Shdr) *stab_section = FindSectionByName(".stab", sections, section_names, elf_header->e_shnum); @@ -240,6 +357,8 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header, " debugging information\n"); } } + + // Look for DWARF debugging information, and load it if present. const ElfW(Shdr) *dwarf_section = FindSectionByName(".debug_info", sections, section_names, elf_header->e_shnum); @@ -249,6 +368,20 @@ static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header, fprintf(stderr, "\".debug_info\" section found, but failed to load " "DWARF debugging information\n"); } + + // Dwarf Call Frame Information (CFI) is actually independent from + // the other DWARF debugging information, and can be used alone. + const ElfW(Shdr) *dwarf_cfi_section = + FindSectionByName(".debug_frame", sections, section_names, + elf_header->e_shnum); + if (dwarf_cfi_section) { + // Ignore the return value of this function; even without call frame + // information, the other debugging information could be perfectly + // useful. + LoadDwarfCFI(obj_file, elf_header, ".debug_frame", + dwarf_cfi_section, module); + } + if (!found_debug_info_section) { fprintf(stderr, "file contains no debugging information" " (no \".stab\" or \".debug_info\" sections)\n"); -- cgit v1.2.1