// Copyright (c) 2011 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived from // this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Restructured in 2009 by: Jim Blandy // dump_symbols.cc: implement google_breakpad::WriteSymbolFile: // Find all the debugging info in a file and dump it as a Breakpad symbol file. #include "common/linux/dump_symbols.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/dwarf/bytereader-inl.h" #include "common/dwarf/dwarf2diehandler.h" #include "common/dwarf_cfi_to_module.h" #include "common/dwarf_cu_to_module.h" #include "common/dwarf_line_to_module.h" #include "common/dwarf_range_list_handler.h" #include "common/linux/crc32.h" #include "common/linux/eintr_wrapper.h" #include "common/linux/elfutils.h" #include "common/linux/elfutils-inl.h" #include "common/linux/elf_symbols_to_module.h" #include "common/linux/file_id.h" #include "common/memory_allocator.h" #include "common/module.h" #include "common/path_helper.h" #include "common/scoped_ptr.h" #ifndef NO_STABS_SUPPORT #include "common/stabs_reader.h" #include "common/stabs_to_module.h" #endif #include "common/using_std_string.h" // This namespace contains helper functions. namespace { using google_breakpad::DumpOptions; using google_breakpad::DwarfCFIToModule; using google_breakpad::DwarfCUToModule; using google_breakpad::DwarfLineToModule; using google_breakpad::DwarfRangeListHandler; using google_breakpad::ElfClass; using google_breakpad::ElfClass32; using google_breakpad::ElfClass64; using google_breakpad::FileID; using google_breakpad::FindElfSectionByName; using google_breakpad::GetOffset; using google_breakpad::IsValidElf; using google_breakpad::kDefaultBuildIdSize; using google_breakpad::Module; using google_breakpad::PageAllocator; #ifndef NO_STABS_SUPPORT using google_breakpad::StabsToModule; #endif using google_breakpad::scoped_ptr; using google_breakpad::wasteful_vector; // Define AARCH64 ELF architecture if host machine does not include this define. #ifndef EM_AARCH64 #define EM_AARCH64 183 #endif // // FDWrapper // // Wrapper class to make sure opened file is closed. // class FDWrapper { public: explicit FDWrapper(int fd) : fd_(fd) {} ~FDWrapper() { if (fd_ != -1) close(fd_); } int get() { return fd_; } int release() { int fd = fd_; fd_ = -1; return fd; } private: int fd_; }; // // MmapWrapper // // Wrapper class to make sure mapped regions are unmapped. // class MmapWrapper { public: MmapWrapper() : is_set_(false) {} ~MmapWrapper() { if (is_set_ && base_ != NULL) { assert(size_ > 0); munmap(base_, size_); } } void set(void* mapped_address, size_t mapped_size) { is_set_ = true; base_ = mapped_address; size_ = mapped_size; } void release() { assert(is_set_); is_set_ = false; base_ = NULL; size_ = 0; } private: bool is_set_; void* base_; size_t size_; }; // Find the preferred loading address of the binary. template typename ElfClass::Addr GetLoadingAddress( const typename ElfClass::Phdr* program_headers, int nheader) { typedef typename ElfClass::Phdr Phdr; // For non-PIC executables (e_type == ET_EXEC), the load address is // the start address of the first PT_LOAD segment. (ELF requires // the segments to be sorted by load address.) For PIC executables // and dynamic libraries (e_type == ET_DYN), this address will // normally be zero. for (int i = 0; i < nheader; ++i) { const Phdr& header = program_headers[i]; if (header.p_type == PT_LOAD) return header.p_vaddr; } return 0; } // Find the set of address ranges for all PT_LOAD segments. template vector GetPtLoadSegmentRanges( const typename ElfClass::Phdr* program_headers, int nheader) { typedef typename ElfClass::Phdr Phdr; vector ranges; for (int i = 0; i < nheader; ++i) { const Phdr& header = program_headers[i]; if (header.p_type == PT_LOAD) { ranges.push_back(Module::Range(header.p_vaddr, header.p_memsz)); } } return ranges; } #ifndef NO_STABS_SUPPORT template bool LoadStabs(const typename ElfClass::Ehdr* elf_header, const typename ElfClass::Shdr* stab_section, const typename ElfClass::Shdr* stabstr_section, const bool big_endian, Module* module) { // A callback object to handle data from the STABS reader. StabsToModule handler(module); // Find the addresses of the STABS data, and create a STABS reader object. // On Linux, STABS entries always have 32-bit values, regardless of the // address size of the architecture whose code they're describing, and // the strings are always "unitized". const uint8_t* stabs = GetOffset(elf_header, stab_section->sh_offset); const uint8_t* stabstr = GetOffset(elf_header, stabstr_section->sh_offset); google_breakpad::StabsReader reader(stabs, stab_section->sh_size, stabstr, stabstr_section->sh_size, big_endian, 4, true, &handler); // Read the STABS data, and do post-processing. if (!reader.Process()) return false; handler.Finalize(); return true; } #endif // NO_STABS_SUPPORT // A range handler that accepts rangelist data parsed by // dwarf2reader::RangeListReader and populates a range vector (typically // owned by a function) with the results. class DumperRangesHandler : public DwarfCUToModule::RangesHandler { public: DumperRangesHandler(const uint8_t* buffer, uint64_t size, dwarf2reader::ByteReader* reader) : buffer_(buffer), size_(size), reader_(reader) { } bool ReadRanges(uint64_t offset, Module::Address base_address, vector* ranges) { DwarfRangeListHandler handler(base_address, ranges); dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_, &handler); return rangelist_reader.ReadRangeList(offset); } private: const uint8_t* buffer_; uint64_t size_; dwarf2reader::ByteReader* reader_; }; // A line-to-module loader that accepts line number info parsed by // dwarf2reader::LineInfo and populates a Module and a line vector // with the results. class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler { public: // Create a line-to-module converter using BYTE_READER. explicit DumperLineToModule(dwarf2reader::ByteReader* byte_reader) : byte_reader_(byte_reader) { } void StartCompilationUnit(const string& compilation_dir) { compilation_dir_ = compilation_dir; } void ReadProgram(const uint8_t* program, uint64_t length, const uint8_t* string_section, uint64_t string_section_length, const uint8_t* line_string_section, uint64_t line_string_section_length, Module* module, std::vector* lines) { DwarfLineToModule handler(module, compilation_dir_, lines); dwarf2reader::LineInfo parser(program, length, byte_reader_, string_section, string_section_length, line_string_section, line_string_section_length, &handler); parser.Start(); } private: string compilation_dir_; dwarf2reader::ByteReader* byte_reader_; }; template bool LoadDwarf(const string& dwarf_filename, const typename ElfClass::Ehdr* elf_header, const bool big_endian, bool handle_inter_cu_refs, Module* module) { typedef typename ElfClass::Shdr Shdr; const dwarf2reader::Endianness endianness = big_endian ? dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; dwarf2reader::ByteReader byte_reader(endianness); // Construct a context for this file. DwarfCUToModule::FileContext file_context(dwarf_filename, module, handle_inter_cu_refs); // Build a map of the ELF file's sections. const Shdr* sections = GetOffset(elf_header, elf_header->e_shoff); int num_sections = elf_header->e_shnum; const Shdr* section_names = sections + elf_header->e_shstrndx; for (int i = 0; i < num_sections; i++) { const Shdr* section = §ions[i]; string name = GetOffset(elf_header, section_names->sh_offset) + section->sh_name; const uint8_t* contents = GetOffset(elf_header, section->sh_offset); file_context.AddSectionToSectionMap(name, contents, section->sh_size); } // Optional .debug_ranges reader scoped_ptr ranges_handler; dwarf2reader::SectionMap::const_iterator ranges_entry = file_context.section_map().find(".debug_ranges"); if (ranges_entry != file_context.section_map().end()) { const std::pair& ranges_section = ranges_entry->second; ranges_handler.reset( new DumperRangesHandler(ranges_section.first, ranges_section.second, &byte_reader)); } // Parse all the compilation units in the .debug_info section. DumperLineToModule line_to_module(&byte_reader); dwarf2reader::SectionMap::const_iterator debug_info_entry = file_context.section_map().find(".debug_info"); assert(debug_info_entry != file_context.section_map().end()); const std::pair& debug_info_section = debug_info_entry->second; // This should never have been called if the file doesn't have a // .debug_info section. assert(debug_info_section.first); uint64_t debug_info_length = debug_info_section.second; for (uint64_t offset = 0; offset < debug_info_length;) { // Make a handler for the root DIE that populates MODULE with the // data that was found. DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); DwarfCUToModule root_handler(&file_context, &line_to_module, ranges_handler.get(), &reporter); // Make a Dwarf2Handler that drives the DIEHandler. dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); // Make a DWARF parser for the compilation unit at OFFSET. dwarf2reader::CompilationUnit reader(dwarf_filename, file_context.section_map(), offset, &byte_reader, &die_dispatcher); // Process the entire compilation unit; get the offset of the next. offset += reader.Start(); } 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 HEADER's machine architecture is not // supported. template bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, std::vector* register_names) { switch (elf_header->e_machine) { case EM_386: *register_names = DwarfCFIToModule::RegisterNames::I386(); return true; case EM_ARM: *register_names = DwarfCFIToModule::RegisterNames::ARM(); return true; case EM_AARCH64: *register_names = DwarfCFIToModule::RegisterNames::ARM64(); return true; case EM_MIPS: *register_names = DwarfCFIToModule::RegisterNames::MIPS(); return true; case EM_X86_64: *register_names = DwarfCFIToModule::RegisterNames::X86_64(); return true; default: return false; } } template bool LoadDwarfCFI(const string& dwarf_filename, const typename ElfClass::Ehdr* elf_header, const char* section_name, const typename ElfClass::Shdr* section, const bool eh_frame, const typename ElfClass::Shdr* got_section, const typename ElfClass::Shdr* text_section, const bool big_endian, Module* module) { // Find the appropriate set of register names for this file's // architecture. std::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; } const dwarf2reader::Endianness endianness = big_endian ? dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; // Find the call frame information and its size. const uint8_t* cfi = GetOffset(elf_header, 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); byte_reader.SetAddressSize(ElfClass::kAddrSize); // Provide the base addresses for .eh_frame encoded pointers, if // possible. byte_reader.SetCFIDataBase(section->sh_addr, cfi); if (got_section) byte_reader.SetDataBase(got_section->sh_addr); if (text_section) byte_reader.SetTextBase(text_section->sh_addr); dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, section_name); dwarf2reader::CallFrameInfo parser(cfi, cfi_size, &byte_reader, &handler, &dwarf_reporter, eh_frame); parser.Start(); return true; } bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper, void** elf_header) { int obj_fd = open(obj_file.c_str(), O_RDONLY); if (obj_fd < 0) { fprintf(stderr, "Failed to open ELF file '%s': %s\n", obj_file.c_str(), strerror(errno)); return false; } FDWrapper obj_fd_wrapper(obj_fd); struct stat st; if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", obj_file.c_str(), strerror(errno)); return false; } void* obj_base = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); if (obj_base == MAP_FAILED) { fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", obj_file.c_str(), strerror(errno)); return false; } map_wrapper->set(obj_base, st.st_size); *elf_header = obj_base; if (!IsValidElf(*elf_header)) { fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); return false; } return true; } // Get the endianness of ELF_HEADER. If it's invalid, return false. template bool ElfEndianness(const typename ElfClass::Ehdr* elf_header, bool* big_endian) { if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) { *big_endian = false; return true; } if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) { *big_endian = true; return true; } fprintf(stderr, "bad data encoding in ELF header: %d\n", elf_header->e_ident[EI_DATA]); return false; } // Given |left_abspath|, find the absolute path for |right_path| and see if the // two absolute paths are the same. bool IsSameFile(const char* left_abspath, const string& right_path) { char right_abspath[PATH_MAX]; if (!realpath(right_path.c_str(), right_abspath)) return false; return strcmp(left_abspath, right_abspath) == 0; } // Read the .gnu_debuglink and get the debug file name. If anything goes // wrong, return an empty string. string ReadDebugLink(const uint8_t* debuglink, const size_t debuglink_size, const bool big_endian, const string& obj_file, const std::vector& debug_dirs) { // Include '\0' + CRC32 (4 bytes). size_t debuglink_len = strlen(reinterpret_cast(debuglink)) + 5; debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round up to 4 bytes. // Sanity check. if (debuglink_len != debuglink_size) { fprintf(stderr, "Mismatched .gnu_debuglink string / section size: " "%zx %zx\n", debuglink_len, debuglink_size); return string(); } char obj_file_abspath[PATH_MAX]; if (!realpath(obj_file.c_str(), obj_file_abspath)) { fprintf(stderr, "Cannot resolve absolute path for %s\n", obj_file.c_str()); return string(); } std::vector searched_paths; string debuglink_path; std::vector::const_iterator it; for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { const string& debug_dir = *it; debuglink_path = debug_dir + "/" + reinterpret_cast(debuglink); // There is the annoying case of /path/to/foo.so having foo.so as the // debug link file name. Thus this may end up opening /path/to/foo.so again, // and there is a small chance of the two files having the same CRC. if (IsSameFile(obj_file_abspath, debuglink_path)) continue; searched_paths.push_back(debug_dir); int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY); if (debuglink_fd < 0) continue; FDWrapper debuglink_fd_wrapper(debuglink_fd); // The CRC is the last 4 bytes in |debuglink|. const dwarf2reader::Endianness endianness = big_endian ? dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; dwarf2reader::ByteReader byte_reader(endianness); uint32_t expected_crc = byte_reader.ReadFourBytes(&debuglink[debuglink_size - 4]); uint32_t actual_crc = 0; while (true) { const size_t kReadSize = 4096; char buf[kReadSize]; ssize_t bytes_read = HANDLE_EINTR(read(debuglink_fd, &buf, kReadSize)); if (bytes_read < 0) { fprintf(stderr, "Error reading debug ELF file %s.\n", debuglink_path.c_str()); return string(); } if (bytes_read == 0) break; actual_crc = google_breakpad::UpdateCrc32(actual_crc, buf, bytes_read); } if (actual_crc != expected_crc) { fprintf(stderr, "Error reading debug ELF file - CRC32 mismatch: %s\n", debuglink_path.c_str()); continue; } // Found debug file. return debuglink_path; } // Not found case. fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n", obj_file.c_str()); for (it = searched_paths.begin(); it < searched_paths.end(); ++it) { const string& debug_dir = *it; fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink); } return string(); } // // LoadSymbolsInfo // // Holds the state between the two calls to LoadSymbols() in case it's necessary // to follow the .gnu_debuglink section and load debug information from a // different file. // template class LoadSymbolsInfo { public: typedef typename ElfClass::Addr Addr; explicit LoadSymbolsInfo(const std::vector& dbg_dirs) : debug_dirs_(dbg_dirs), has_loading_addr_(false) {} // Keeps track of which sections have been loaded so sections don't // accidentally get loaded twice from two different files. void LoadedSection(const string& section) { if (loaded_sections_.count(section) == 0) { loaded_sections_.insert(section); } else { fprintf(stderr, "Section %s has already been loaded.\n", section.c_str()); } } // The ELF file and linked debug file are expected to have the same preferred // loading address. void set_loading_addr(Addr addr, const string& filename) { if (!has_loading_addr_) { loading_addr_ = addr; loaded_file_ = filename; return; } if (addr != loading_addr_) { fprintf(stderr, "ELF file '%s' and debug ELF file '%s' " "have different load addresses.\n", loaded_file_.c_str(), filename.c_str()); assert(false); } } // Setters and getters const std::vector& debug_dirs() const { return debug_dirs_; } string debuglink_file() const { return debuglink_file_; } void set_debuglink_file(string file) { debuglink_file_ = file; } private: const std::vector& debug_dirs_; // Directories in which to // search for the debug ELF file. string debuglink_file_; // Full path to the debug ELF file. bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid. Addr loading_addr_; // Saves the preferred loading address from the // first call to LoadSymbols(). string loaded_file_; // Name of the file loaded from the first call to // LoadSymbols(). std::set loaded_sections_; // Tracks the Loaded ELF sections // between calls to LoadSymbols(). }; template bool LoadSymbols(const string& obj_file, const bool big_endian, const typename ElfClass::Ehdr* elf_header, const bool read_gnu_debug_link, LoadSymbolsInfo* info, const DumpOptions& options, Module* module) { typedef typename ElfClass::Addr Addr; typedef typename ElfClass::Phdr Phdr; typedef typename ElfClass::Shdr Shdr; Addr loading_addr = GetLoadingAddress( GetOffset(elf_header, elf_header->e_phoff), elf_header->e_phnum); module->SetLoadAddress(loading_addr); info->set_loading_addr(loading_addr, obj_file); // Allow filtering of extraneous debug information in partitioned libraries. // Such libraries contain debug information for all libraries extracted from // the same combined library, implying extensive duplication. vector address_ranges = GetPtLoadSegmentRanges( GetOffset(elf_header, elf_header->e_phoff), elf_header->e_phnum); module->SetAddressRanges(address_ranges); const Shdr* sections = GetOffset(elf_header, elf_header->e_shoff); const Shdr* section_names = sections + elf_header->e_shstrndx; const char* names = GetOffset(elf_header, section_names->sh_offset); const char* names_end = names + section_names->sh_size; bool found_debug_info_section = false; bool found_usable_info = false; if (options.symbol_data != ONLY_CFI) { #ifndef NO_STABS_SUPPORT // Look for STABS debugging information, and load it if present. const Shdr* stab_section = FindElfSectionByName(".stab", SHT_PROGBITS, sections, names, names_end, elf_header->e_shnum); if (stab_section) { const Shdr* stabstr_section = stab_section->sh_link + sections; if (stabstr_section) { found_debug_info_section = true; found_usable_info = true; info->LoadedSection(".stab"); if (!LoadStabs(elf_header, stab_section, stabstr_section, big_endian, module)) { fprintf(stderr, "%s: \".stab\" section found, but failed to load" " STABS debugging information\n", obj_file.c_str()); } } } #endif // NO_STABS_SUPPORT // Look for DWARF debugging information, and load it if present. const Shdr* dwarf_section = FindElfSectionByName(".debug_info", SHT_PROGBITS, sections, names, names_end, elf_header->e_shnum); // .debug_info section type is SHT_PROGBITS for mips on pnacl toolchains, // but MIPS_DWARF for regular gnu toolchains, so both need to be checked if (elf_header->e_machine == EM_MIPS && !dwarf_section) { dwarf_section = FindElfSectionByName(".debug_info", SHT_MIPS_DWARF, sections, names, names_end, elf_header->e_shnum); } if (dwarf_section) { found_debug_info_section = true; found_usable_info = true; info->LoadedSection(".debug_info"); if (!LoadDwarf(obj_file, elf_header, big_endian, options.handle_inter_cu_refs, module)) { fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " "DWARF debugging information\n", obj_file.c_str()); } } // See if there are export symbols available. const Shdr* symtab_section = FindElfSectionByName(".symtab", SHT_SYMTAB, sections, names, names_end, elf_header->e_shnum); const Shdr* strtab_section = FindElfSectionByName(".strtab", SHT_STRTAB, sections, names, names_end, elf_header->e_shnum); if (symtab_section && strtab_section) { info->LoadedSection(".symtab"); const uint8_t* symtab = GetOffset(elf_header, symtab_section->sh_offset); const uint8_t* strtab = GetOffset(elf_header, strtab_section->sh_offset); bool result = ELFSymbolsToModule(symtab, symtab_section->sh_size, strtab, strtab_section->sh_size, big_endian, ElfClass::kAddrSize, module); found_usable_info = found_usable_info || result; } else { // Look in dynsym only if full symbol table was not available. const Shdr* dynsym_section = FindElfSectionByName(".dynsym", SHT_DYNSYM, sections, names, names_end, elf_header->e_shnum); const Shdr* dynstr_section = FindElfSectionByName(".dynstr", SHT_STRTAB, sections, names, names_end, elf_header->e_shnum); if (dynsym_section && dynstr_section) { info->LoadedSection(".dynsym"); const uint8_t* dynsyms = GetOffset(elf_header, dynsym_section->sh_offset); const uint8_t* dynstrs = GetOffset(elf_header, dynstr_section->sh_offset); bool result = ELFSymbolsToModule(dynsyms, dynsym_section->sh_size, dynstrs, dynstr_section->sh_size, big_endian, ElfClass::kAddrSize, module); found_usable_info = found_usable_info || result; } } } if (options.symbol_data != NO_CFI) { // Dwarf Call Frame Information (CFI) is actually independent from // the other DWARF debugging information, and can be used alone. const Shdr* dwarf_cfi_section = FindElfSectionByName(".debug_frame", SHT_PROGBITS, sections, names, names_end, elf_header->e_shnum); // .debug_frame section type is SHT_PROGBITS for mips on pnacl toolchains, // but MIPS_DWARF for regular gnu toolchains, so both need to be checked if (elf_header->e_machine == EM_MIPS && !dwarf_cfi_section) { dwarf_cfi_section = FindElfSectionByName(".debug_frame", SHT_MIPS_DWARF, sections, names, names_end, 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. info->LoadedSection(".debug_frame"); bool result = LoadDwarfCFI(obj_file, elf_header, ".debug_frame", dwarf_cfi_section, false, 0, 0, big_endian, module); found_usable_info = found_usable_info || result; } // Linux C++ exception handling information can also provide // unwinding data. const Shdr* eh_frame_section = FindElfSectionByName(".eh_frame", SHT_PROGBITS, sections, names, names_end, elf_header->e_shnum); if (eh_frame_section) { // Pointers in .eh_frame data may be relative to the base addresses of // certain sections. Provide those sections if present. const Shdr* got_section = FindElfSectionByName(".got", SHT_PROGBITS, sections, names, names_end, elf_header->e_shnum); const Shdr* text_section = FindElfSectionByName(".text", SHT_PROGBITS, sections, names, names_end, elf_header->e_shnum); info->LoadedSection(".eh_frame"); // As above, ignore the return value of this function. bool result = LoadDwarfCFI(obj_file, elf_header, ".eh_frame", eh_frame_section, true, got_section, text_section, big_endian, module); found_usable_info = found_usable_info || result; } } if (!found_debug_info_section) { fprintf(stderr, "%s: file contains no debugging information" " (no \".stab\" or \".debug_info\" sections)\n", obj_file.c_str()); // Failed, but maybe there's a .gnu_debuglink section? if (read_gnu_debug_link) { const Shdr* gnu_debuglink_section = FindElfSectionByName(".gnu_debuglink", SHT_PROGBITS, sections, names, names_end, elf_header->e_shnum); if (gnu_debuglink_section) { if (!info->debug_dirs().empty()) { const uint8_t* debuglink_contents = GetOffset(elf_header, gnu_debuglink_section->sh_offset); string debuglink_file = ReadDebugLink(debuglink_contents, gnu_debuglink_section->sh_size, big_endian, obj_file, info->debug_dirs()); info->set_debuglink_file(debuglink_file); } else { fprintf(stderr, ".gnu_debuglink section found in '%s', " "but no debug path specified.\n", obj_file.c_str()); } } else { fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n", obj_file.c_str()); } } else { // Return true if some usable information was found, since the caller // doesn't want to use .gnu_debuglink. return found_usable_info; } // No debug info was found, let the user try again with .gnu_debuglink // if present. return false; } return true; } // Return the breakpad symbol file identifier for the architecture of // ELF_HEADER. template const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) { typedef typename ElfClass::Half Half; Half arch = elf_header->e_machine; switch (arch) { case EM_386: return "x86"; case EM_ARM: return "arm"; case EM_AARCH64: return "arm64"; case EM_MIPS: return "mips"; case EM_PPC64: return "ppc64"; case EM_PPC: return "ppc"; case EM_S390: return "s390"; case EM_SPARC: return "sparc"; case EM_SPARCV9: return "sparcv9"; case EM_X86_64: return "x86_64"; default: return NULL; } } template bool SanitizeDebugFile(const typename ElfClass::Ehdr* debug_elf_header, const string& debuglink_file, const string& obj_filename, const char* obj_file_architecture, const bool obj_file_is_big_endian) { const char* debug_architecture = ElfArchitecture(debug_elf_header); if (!debug_architecture) { fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", debuglink_file.c_str(), debug_elf_header->e_machine); return false; } if (strcmp(obj_file_architecture, debug_architecture)) { fprintf(stderr, "%s with ELF machine architecture %s does not match " "%s with ELF architecture %s\n", debuglink_file.c_str(), debug_architecture, obj_filename.c_str(), obj_file_architecture); return false; } bool debug_big_endian; if (!ElfEndianness(debug_elf_header, &debug_big_endian)) return false; if (debug_big_endian != obj_file_is_big_endian) { fprintf(stderr, "%s and %s does not match in endianness\n", obj_filename.c_str(), debuglink_file.c_str()); return false; } return true; } template bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header, const string& obj_filename, const string& obj_os, scoped_ptr& module) { PageAllocator allocator; wasteful_vector identifier(&allocator, kDefaultBuildIdSize); if (!FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) { fprintf(stderr, "%s: unable to generate file identifier\n", obj_filename.c_str()); return false; } const char* architecture = ElfArchitecture(elf_header); if (!architecture) { fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", obj_filename.c_str(), elf_header->e_machine); return false; } char name_buf[NAME_MAX] = {}; std::string name = google_breakpad::ElfFileSoNameFromMappedFile( elf_header, name_buf, sizeof(name_buf)) ? name_buf : google_breakpad::BaseName(obj_filename); // Add an extra "0" at the end. PDB files on Windows have an 'age' // number appended to the end of the file identifier; this isn't // really used or necessary on other platforms, but be consistent. string id = FileID::ConvertIdentifierToUUIDString(identifier) + "0"; // This is just the raw Build ID in hex. string code_id = FileID::ConvertIdentifierToString(identifier); module.reset(new Module(name, obj_os, architecture, id, code_id)); return true; } template bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, const string& obj_filename, const string& obj_os, const std::vector& debug_dirs, const DumpOptions& options, Module** out_module) { typedef typename ElfClass::Ehdr Ehdr; *out_module = NULL; scoped_ptr module; if (!InitModuleForElfClass(elf_header, obj_filename, obj_os, module)) { return false; } // Figure out what endianness this file is. bool big_endian; if (!ElfEndianness(elf_header, &big_endian)) return false; LoadSymbolsInfo info(debug_dirs); if (!LoadSymbols(obj_filename, big_endian, elf_header, !debug_dirs.empty(), &info, options, module.get())) { const string debuglink_file = info.debuglink_file(); if (debuglink_file.empty()) return false; // Load debuglink ELF file. fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str()); MmapWrapper debug_map_wrapper; Ehdr* debug_elf_header = NULL; if (!LoadELF(debuglink_file, &debug_map_wrapper, reinterpret_cast(&debug_elf_header)) || !SanitizeDebugFile(debug_elf_header, debuglink_file, obj_filename, module->architecture().c_str(), big_endian)) { return false; } if (!LoadSymbols(debuglink_file, big_endian, debug_elf_header, false, &info, options, module.get())) { return false; } } *out_module = module.release(); return true; } } // namespace namespace google_breakpad { // Not explicitly exported, but not static so it can be used in unit tests. bool ReadSymbolDataInternal(const uint8_t* obj_file, const string& obj_filename, const string& obj_os, const std::vector& debug_dirs, const DumpOptions& options, Module** module) { if (!IsValidElf(obj_file)) { fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); return false; } int elfclass = ElfClass(obj_file); if (elfclass == ELFCLASS32) { return ReadSymbolDataElfClass( reinterpret_cast(obj_file), obj_filename, obj_os, debug_dirs, options, module); } if (elfclass == ELFCLASS64) { return ReadSymbolDataElfClass( reinterpret_cast(obj_file), obj_filename, obj_os, debug_dirs, options, module); } return false; } bool WriteSymbolFile(const string& load_path, const string& obj_file, const string& obj_os, const std::vector& debug_dirs, const DumpOptions& options, std::ostream& sym_stream) { Module* module; if (!ReadSymbolData(load_path, obj_file, obj_os, debug_dirs, options, &module)) return false; bool result = module->Write(sym_stream, options.symbol_data); delete module; return result; } // Read the selected object file's debugging information, and write out the // header only to |stream|. Return true on success; if an error occurs, report // it and return false. bool WriteSymbolFileHeader(const string& load_path, const string& obj_file, const string& obj_os, std::ostream& sym_stream) { MmapWrapper map_wrapper; void* elf_header = NULL; if (!LoadELF(load_path, &map_wrapper, &elf_header)) { fprintf(stderr, "Could not load ELF file: %s\n", obj_file.c_str()); return false; } if (!IsValidElf(elf_header)) { fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); return false; } int elfclass = ElfClass(elf_header); scoped_ptr module; if (elfclass == ELFCLASS32) { if (!InitModuleForElfClass( reinterpret_cast(elf_header), obj_file, obj_os, module)) { fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str()); return false; } } else if (elfclass == ELFCLASS64) { if (!InitModuleForElfClass( reinterpret_cast(elf_header), obj_file, obj_os, module)) { fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str()); return false; } } else { fprintf(stderr, "Unsupported module file: %s\n", obj_file.c_str()); return false; } return module->Write(sym_stream, ALL_SYMBOL_DATA); } bool ReadSymbolData(const string& load_path, const string& obj_file, const string& obj_os, const std::vector& debug_dirs, const DumpOptions& options, Module** module) { MmapWrapper map_wrapper; void* elf_header = NULL; if (!LoadELF(load_path, &map_wrapper, &elf_header)) return false; return ReadSymbolDataInternal(reinterpret_cast(elf_header), obj_file, obj_os, debug_dirs, options, module); } } // namespace google_breakpad