|  | // 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 <jimb@mozilla.com> <jimb@red-bean.com> | 
|  |  | 
|  | // 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 <assert.h> | 
|  | #include <elf.h> | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <link.h> | 
|  | #include <stdint.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <sys/mman.h> | 
|  | #include <sys/stat.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <iostream> | 
|  | #include <set> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #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> | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #ifndef NO_STABS_SUPPORT | 
|  | template<typename ElfClass> | 
|  | 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<ElfClass, uint8_t>(elf_header, stab_section->sh_offset); | 
|  | const uint8_t* stabstr = | 
|  | GetOffset<ElfClass, uint8_t>(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 size, | 
|  | dwarf2reader::ByteReader* reader) | 
|  | : buffer_(buffer), size_(size), reader_(reader) { } | 
|  |  | 
|  | bool ReadRanges(uint64 offset, Module::Address base_address, | 
|  | vector<Module::Range>* 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 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 length, | 
|  | Module* module, std::vector<Module::Line>* lines) { | 
|  | DwarfLineToModule handler(module, compilation_dir_, lines); | 
|  | dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); | 
|  | parser.Start(); | 
|  | } | 
|  | private: | 
|  | string compilation_dir_; | 
|  | dwarf2reader::ByteReader *byte_reader_; | 
|  | }; | 
|  |  | 
|  | template<typename ElfClass> | 
|  | 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<ElfClass, Shdr>(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<ElfClass, char>(elf_header, | 
|  | section_names->sh_offset) + | 
|  | section->sh_name; | 
|  | const uint8_t *contents = GetOffset<ElfClass, uint8_t>(elf_header, | 
|  | section->sh_offset); | 
|  | file_context.AddSectionToSectionMap(name, contents, section->sh_size); | 
|  | } | 
|  |  | 
|  | // Optional .debug_ranges reader | 
|  | scoped_ptr<DumperRangesHandler> 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<const uint8_t *, uint64>& 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<const uint8_t *, uint64>& 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 debug_info_length = debug_info_section.second; | 
|  | for (uint64 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<typename ElfClass> | 
|  | bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, | 
|  | std::vector<string>* 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<typename ElfClass> | 
|  | 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<string> register_names; | 
|  | if (!DwarfCFIRegisterNames<ElfClass>(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<ElfClass, uint8_t>(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<typename ElfClass> | 
|  | 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<string>& debug_dirs) { | 
|  | // Include '\0' + CRC32 (4 bytes). | 
|  | size_t debuglink_len = strlen(reinterpret_cast<const char *>(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<string> searched_paths; | 
|  | string debuglink_path; | 
|  | std::vector<string>::const_iterator it; | 
|  | for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { | 
|  | const string& debug_dir = *it; | 
|  | debuglink_path = debug_dir + "/" + | 
|  | reinterpret_cast<const char *>(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<typename ElfClass> | 
|  | class LoadSymbolsInfo { | 
|  | public: | 
|  | typedef typename ElfClass::Addr Addr; | 
|  |  | 
|  | explicit LoadSymbolsInfo(const std::vector<string>& 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 §ion) { | 
|  | 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<string>& 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<string>& 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<string> loaded_sections_;  // Tracks the Loaded ELF sections | 
|  | // between calls to LoadSymbols(). | 
|  | }; | 
|  |  | 
|  | template<typename ElfClass> | 
|  | bool LoadSymbols(const string& obj_file, | 
|  | const bool big_endian, | 
|  | const typename ElfClass::Ehdr* elf_header, | 
|  | const bool read_gnu_debug_link, | 
|  | LoadSymbolsInfo<ElfClass>* 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<ElfClass>( | 
|  | GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff), | 
|  | elf_header->e_phnum); | 
|  | module->SetLoadAddress(loading_addr); | 
|  | info->set_loading_addr(loading_addr, obj_file); | 
|  |  | 
|  | const Shdr* sections = | 
|  | GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); | 
|  | const Shdr* section_names = sections + elf_header->e_shstrndx; | 
|  | const char* names = | 
|  | GetOffset<ElfClass, char>(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<ElfClass>(".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<ElfClass>(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<ElfClass>(".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<ElfClass>(".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<ElfClass>(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<ElfClass>(".symtab", SHT_SYMTAB, | 
|  | sections, names, names_end, | 
|  | elf_header->e_shnum); | 
|  | const Shdr* strtab_section = | 
|  | FindElfSectionByName<ElfClass>(".strtab", SHT_STRTAB, | 
|  | sections, names, names_end, | 
|  | elf_header->e_shnum); | 
|  | if (symtab_section && strtab_section) { | 
|  | info->LoadedSection(".symtab"); | 
|  |  | 
|  | const uint8_t* symtab = | 
|  | GetOffset<ElfClass, uint8_t>(elf_header, | 
|  | symtab_section->sh_offset); | 
|  | const uint8_t* strtab = | 
|  | GetOffset<ElfClass, uint8_t>(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<ElfClass>(".dynsym", SHT_DYNSYM, | 
|  | sections, names, names_end, | 
|  | elf_header->e_shnum); | 
|  | const Shdr* dynstr_section = | 
|  | FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB, | 
|  | sections, names, names_end, | 
|  | elf_header->e_shnum); | 
|  | if (dynsym_section && dynstr_section) { | 
|  | info->LoadedSection(".dynsym"); | 
|  |  | 
|  | const uint8_t* dynsyms = | 
|  | GetOffset<ElfClass, uint8_t>(elf_header, | 
|  | dynsym_section->sh_offset); | 
|  | const uint8_t* dynstrs = | 
|  | GetOffset<ElfClass, uint8_t>(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<ElfClass>(".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<ElfClass>(".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<ElfClass>(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<ElfClass>(".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<ElfClass>(".got", SHT_PROGBITS, | 
|  | sections, names, names_end, | 
|  | elf_header->e_shnum); | 
|  | const Shdr* text_section = | 
|  | FindElfSectionByName<ElfClass>(".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<ElfClass>(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<ElfClass>(".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<ElfClass, uint8_t>(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<typename ElfClass> | 
|  | 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<typename ElfClass> | 
|  | 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<ElfClass>(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<ElfClass>(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<typename ElfClass> | 
|  | bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header, | 
|  | const string& obj_filename, | 
|  | scoped_ptr<Module>& module) { | 
|  | PageAllocator allocator; | 
|  | wasteful_vector<uint8_t> 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<ElfClass>(elf_header); | 
|  | if (!architecture) { | 
|  | fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", | 
|  | obj_filename.c_str(), elf_header->e_machine); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | string name = google_breakpad::BaseName(obj_filename); | 
|  | string os = "Linux"; | 
|  | // 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, os, architecture, id, code_id)); | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | template<typename ElfClass> | 
|  | bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, | 
|  | const string& obj_filename, | 
|  | const std::vector<string>& debug_dirs, | 
|  | const DumpOptions& options, | 
|  | Module** out_module) { | 
|  | typedef typename ElfClass::Ehdr Ehdr; | 
|  |  | 
|  | *out_module = NULL; | 
|  |  | 
|  | scoped_ptr<Module> module; | 
|  | if (!InitModuleForElfClass<ElfClass>(elf_header, obj_filename, module)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Figure out what endianness this file is. | 
|  | bool big_endian; | 
|  | if (!ElfEndianness<ElfClass>(elf_header, &big_endian)) | 
|  | return false; | 
|  |  | 
|  | LoadSymbolsInfo<ElfClass> info(debug_dirs); | 
|  | if (!LoadSymbols<ElfClass>(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<void**>(&debug_elf_header)) || | 
|  | !SanitizeDebugFile<ElfClass>(debug_elf_header, debuglink_file, | 
|  | obj_filename, | 
|  | module->architecture().c_str(), | 
|  | big_endian)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!LoadSymbols<ElfClass>(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 std::vector<string>& 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<ElfClass32>( | 
|  | reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, debug_dirs, | 
|  | options, module); | 
|  | } | 
|  | if (elfclass == ELFCLASS64) { | 
|  | return ReadSymbolDataElfClass<ElfClass64>( | 
|  | reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, debug_dirs, | 
|  | options, module); | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool WriteSymbolFile(const string &obj_file, | 
|  | const std::vector<string>& debug_dirs, | 
|  | const DumpOptions& options, | 
|  | std::ostream &sym_stream) { | 
|  | Module* module; | 
|  | if (!ReadSymbolData(obj_file, 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& obj_file, | 
|  | std::ostream &sym_stream) { | 
|  | MmapWrapper map_wrapper; | 
|  | void* elf_header = NULL; | 
|  | if (!LoadELF(obj_file, &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> module; | 
|  | if (elfclass == ELFCLASS32) { | 
|  | if (!InitModuleForElfClass<ElfClass32>( | 
|  | reinterpret_cast<const Elf32_Ehdr*>(elf_header), obj_file, module)) { | 
|  | fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str()); | 
|  | return false; | 
|  | } | 
|  | } else if (elfclass == ELFCLASS64) { | 
|  | if (!InitModuleForElfClass<ElfClass64>( | 
|  | reinterpret_cast<const Elf64_Ehdr*>(elf_header), obj_file, 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& obj_file, | 
|  | const std::vector<string>& debug_dirs, | 
|  | const DumpOptions& options, | 
|  | Module** module) { | 
|  | MmapWrapper map_wrapper; | 
|  | void* elf_header = NULL; | 
|  | if (!LoadELF(obj_file, &map_wrapper, &elf_header)) | 
|  | return false; | 
|  |  | 
|  | return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header), | 
|  | obj_file, debug_dirs, options, module); | 
|  | } | 
|  |  | 
|  | }  // namespace google_breakpad |