mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-11-24 11:45:44 +01:00
Add functionality to read the .gnu_debuglink section and load symbols from a debug ELF file.
Review URL: http://breakpad.appspot.com/126001 git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@624 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
parent
0dd6c95b3f
commit
56eac4dd7a
@ -46,7 +46,10 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/dwarf/bytereader-inl.h"
|
#include "common/dwarf/bytereader-inl.h"
|
||||||
#include "common/dwarf/dwarf2diehandler.h"
|
#include "common/dwarf/dwarf2diehandler.h"
|
||||||
@ -67,6 +70,64 @@ using google_breakpad::DwarfLineToModule;
|
|||||||
using google_breakpad::Module;
|
using google_breakpad::Module;
|
||||||
using google_breakpad::StabsToModule;
|
using google_breakpad::StabsToModule;
|
||||||
|
|
||||||
|
//
|
||||||
|
// 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() {
|
||||||
|
assert(is_set_);
|
||||||
|
if (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_);
|
||||||
|
base_ = NULL;
|
||||||
|
size_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool is_set_;
|
||||||
|
void *base_;
|
||||||
|
size_t size_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Fix offset into virtual address by adding the mapped base into offsets.
|
// Fix offset into virtual address by adding the mapped base into offsets.
|
||||||
// Make life easier when want to find something by offset.
|
// Make life easier when want to find something by offset.
|
||||||
static void FixAddress(void *obj_base) {
|
static void FixAddress(void *obj_base) {
|
||||||
@ -160,7 +221,7 @@ static bool LoadStabs(const ElfW(Ehdr) *elf_header,
|
|||||||
class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
|
class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
|
||||||
public:
|
public:
|
||||||
// Create a line-to-module converter using BYTE_READER.
|
// Create a line-to-module converter using BYTE_READER.
|
||||||
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||||
: byte_reader_(byte_reader) { }
|
: byte_reader_(byte_reader) { }
|
||||||
void operator()(const char *program, uint64 length,
|
void operator()(const char *program, uint64 length,
|
||||||
Module *module, vector<Module::Line> *lines) {
|
Module *module, vector<Module::Line> *lines) {
|
||||||
@ -304,6 +365,37 @@ static bool LoadDwarfCFI(const string &dwarf_filename,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LoadELF(const std::string &obj_file, MmapWrapper* map_wrapper,
|
||||||
|
ElfW(Ehdr) **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 = reinterpret_cast<ElfW(Ehdr) *>(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.
|
// Get the endianness of ELF_HEADER. If it's invalid, return false.
|
||||||
bool ElfEndianness(const ElfW(Ehdr) *elf_header, bool *big_endian) {
|
bool ElfEndianness(const ElfW(Ehdr) *elf_header, bool *big_endian) {
|
||||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
|
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
|
||||||
@ -320,9 +412,112 @@ bool ElfEndianness(const ElfW(Ehdr) *elf_header, bool *big_endian) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read the .gnu_debuglink and get the debug file name. If anything goes
|
||||||
|
// wrong, return an empty string.
|
||||||
|
static std::string ReadDebugLink(const ElfW(Shdr) *debuglink_section,
|
||||||
|
const std::string &obj_file,
|
||||||
|
const std::string &debug_dir) {
|
||||||
|
char *debuglink = reinterpret_cast<char *>(debuglink_section->sh_offset);
|
||||||
|
size_t debuglink_len = strlen(debuglink) + 5; // '\0' + CRC32.
|
||||||
|
debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round to nearest 4 bytes.
|
||||||
|
|
||||||
|
// Sanity check.
|
||||||
|
if (debuglink_len != debuglink_section->sh_size) {
|
||||||
|
fprintf(stderr, "Mismatched .gnu_debuglink string / section size: "
|
||||||
|
"%zx %zx\n", debuglink_len, debuglink_section->sh_size);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string debuglink_path = debug_dir + "/" + debuglink;
|
||||||
|
int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
|
||||||
|
if (debuglink_fd < 0) {
|
||||||
|
fprintf(stderr, "Failed to open debug ELF file '%s' for '%s': %s\n",
|
||||||
|
debuglink_path.c_str(), obj_file.c_str(), strerror(errno));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
FDWrapper debuglink_fd_wrapper(debuglink_fd);
|
||||||
|
// TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink
|
||||||
|
// section.
|
||||||
|
|
||||||
|
return debuglink_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// LoadSymbolsInfo
|
||||||
|
//
|
||||||
|
// Holds the state between the two calls to LoadSymbols() in case we have to
|
||||||
|
// follow the .gnu_debuglink section and load debug information from a
|
||||||
|
// different file.
|
||||||
|
//
|
||||||
|
class LoadSymbolsInfo {
|
||||||
|
public:
|
||||||
|
explicit LoadSymbolsInfo(const std::string &dbg_dir) :
|
||||||
|
debug_dir_(dbg_dir),
|
||||||
|
has_loading_addr_(false) {}
|
||||||
|
|
||||||
|
// Keeps track of which sections have been loaded so we don't accidentally
|
||||||
|
// load it twice from two different files.
|
||||||
|
void LoadedSection(const std::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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We expect the ELF file and linked debug file to have the same prefered
|
||||||
|
// loading address.
|
||||||
|
void set_loading_addr(ElfW(Addr) addr, const std::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::string &debug_dir() const {
|
||||||
|
return debug_dir_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string debuglink_file() const {
|
||||||
|
return debuglink_file_;
|
||||||
|
}
|
||||||
|
void set_debuglink_file(std::string file) {
|
||||||
|
debuglink_file_ = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string &debug_dir_; // Directory with the debug ELF file.
|
||||||
|
|
||||||
|
std::string debuglink_file_; // Full path to the debug ELF file.
|
||||||
|
|
||||||
|
bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid.
|
||||||
|
|
||||||
|
ElfW(Addr) loading_addr_; // Saves the prefered loading address from the
|
||||||
|
// first call to LoadSymbols().
|
||||||
|
|
||||||
|
std::string loaded_file_; // Name of the file loaded from the first call to
|
||||||
|
// LoadSymbols().
|
||||||
|
|
||||||
|
std::set<std::string> loaded_sections_; // Tracks the Loaded ELF sections
|
||||||
|
// between calls to LoadSymbols().
|
||||||
|
};
|
||||||
|
|
||||||
static bool LoadSymbols(const std::string &obj_file,
|
static bool LoadSymbols(const std::string &obj_file,
|
||||||
const bool big_endian,
|
const bool big_endian,
|
||||||
ElfW(Ehdr) *elf_header,
|
ElfW(Ehdr) *elf_header,
|
||||||
|
const bool read_gnu_debug_link,
|
||||||
|
LoadSymbolsInfo *info,
|
||||||
Module *module) {
|
Module *module) {
|
||||||
// Translate all offsets in section headers into address.
|
// Translate all offsets in section headers into address.
|
||||||
FixAddress(elf_header);
|
FixAddress(elf_header);
|
||||||
@ -330,6 +525,7 @@ static bool LoadSymbols(const std::string &obj_file,
|
|||||||
reinterpret_cast<ElfW(Phdr) *>(elf_header->e_phoff),
|
reinterpret_cast<ElfW(Phdr) *>(elf_header->e_phoff),
|
||||||
elf_header->e_phnum);
|
elf_header->e_phnum);
|
||||||
module->SetLoadAddress(loading_addr);
|
module->SetLoadAddress(loading_addr);
|
||||||
|
info->set_loading_addr(loading_addr, obj_file);
|
||||||
|
|
||||||
const ElfW(Shdr) *sections =
|
const ElfW(Shdr) *sections =
|
||||||
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||||
@ -344,6 +540,7 @@ static bool LoadSymbols(const std::string &obj_file,
|
|||||||
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
|
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
|
||||||
if (stabstr_section) {
|
if (stabstr_section) {
|
||||||
found_debug_info_section = true;
|
found_debug_info_section = true;
|
||||||
|
info->LoadedSection(".stab");
|
||||||
if (!LoadStabs(elf_header, stab_section, stabstr_section, big_endian,
|
if (!LoadStabs(elf_header, stab_section, stabstr_section, big_endian,
|
||||||
module)) {
|
module)) {
|
||||||
fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS"
|
fprintf(stderr, "%s: \".stab\" section found, but failed to load STABS"
|
||||||
@ -358,6 +555,7 @@ static bool LoadSymbols(const std::string &obj_file,
|
|||||||
elf_header->e_shnum);
|
elf_header->e_shnum);
|
||||||
if (dwarf_section) {
|
if (dwarf_section) {
|
||||||
found_debug_info_section = true;
|
found_debug_info_section = true;
|
||||||
|
info->LoadedSection(".debug_info");
|
||||||
if (!LoadDwarf(obj_file, elf_header, big_endian, module))
|
if (!LoadDwarf(obj_file, elf_header, big_endian, module))
|
||||||
fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
|
fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
|
||||||
"DWARF debugging information\n", obj_file.c_str());
|
"DWARF debugging information\n", obj_file.c_str());
|
||||||
@ -372,6 +570,7 @@ static bool LoadSymbols(const std::string &obj_file,
|
|||||||
// Ignore the return value of this function; even without call frame
|
// Ignore the return value of this function; even without call frame
|
||||||
// information, the other debugging information could be perfectly
|
// information, the other debugging information could be perfectly
|
||||||
// useful.
|
// useful.
|
||||||
|
info->LoadedSection(".debug_frame");
|
||||||
LoadDwarfCFI(obj_file, elf_header, ".debug_frame",
|
LoadDwarfCFI(obj_file, elf_header, ".debug_frame",
|
||||||
dwarf_cfi_section, false, 0, 0, big_endian, module);
|
dwarf_cfi_section, false, 0, 0, big_endian, module);
|
||||||
}
|
}
|
||||||
@ -389,6 +588,7 @@ static bool LoadSymbols(const std::string &obj_file,
|
|||||||
const ElfW(Shdr) *text_section =
|
const ElfW(Shdr) *text_section =
|
||||||
FindSectionByName(".text", sections, section_names,
|
FindSectionByName(".text", sections, section_names,
|
||||||
elf_header->e_shnum);
|
elf_header->e_shnum);
|
||||||
|
info->LoadedSection(".eh_frame");
|
||||||
// As above, ignore the return value of this function.
|
// As above, ignore the return value of this function.
|
||||||
LoadDwarfCFI(obj_file, elf_header, ".eh_frame", eh_frame_section, true,
|
LoadDwarfCFI(obj_file, elf_header, ".eh_frame", eh_frame_section, true,
|
||||||
got_section, text_section, big_endian, module);
|
got_section, text_section, big_endian, module);
|
||||||
@ -398,63 +598,32 @@ static bool LoadSymbols(const std::string &obj_file,
|
|||||||
fprintf(stderr, "%s: file contains no debugging information"
|
fprintf(stderr, "%s: file contains no debugging information"
|
||||||
" (no \".stab\" or \".debug_info\" sections)\n",
|
" (no \".stab\" or \".debug_info\" sections)\n",
|
||||||
obj_file.c_str());
|
obj_file.c_str());
|
||||||
|
|
||||||
|
// Failed, but maybe we can find a .gnu_debuglink section?
|
||||||
|
if (read_gnu_debug_link) {
|
||||||
|
const ElfW(Shdr) *gnu_debuglink_section
|
||||||
|
= FindSectionByName(".gnu_debuglink", sections, section_names,
|
||||||
|
elf_header->e_shnum);
|
||||||
|
if (gnu_debuglink_section) {
|
||||||
|
if (!info->debug_dir().empty()) {
|
||||||
|
std::string debuglink_file =
|
||||||
|
ReadDebugLink(gnu_debuglink_section, obj_file, info->debug_dir());
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
// 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(void *mapped_address, size_t mapped_size) :
|
|
||||||
base_(mapped_address), size_(mapped_size) {
|
|
||||||
}
|
|
||||||
~MmapWrapper() {
|
|
||||||
if (base_ != NULL) {
|
|
||||||
assert(size_ > 0);
|
|
||||||
munmap(base_, size_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void release() {
|
|
||||||
base_ = NULL;
|
|
||||||
size_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void *base_;
|
|
||||||
size_t size_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Return the breakpad symbol file identifier for the architecture of
|
// Return the breakpad symbol file identifier for the architecture of
|
||||||
// ELF_HEADER.
|
// ELF_HEADER.
|
||||||
const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) {
|
const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) {
|
||||||
@ -507,33 +676,12 @@ std::string BaseFileName(const std::string &filename) {
|
|||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) {
|
bool WriteSymbolFile(const std::string &obj_file,
|
||||||
int obj_fd = open(obj_file.c_str(), O_RDONLY);
|
const std::string &debug_dir, FILE *sym_file) {
|
||||||
if (obj_fd < 0) {
|
MmapWrapper map_wrapper;
|
||||||
fprintf(stderr, "Failed to open ELF file '%s': %s\n",
|
ElfW(Ehdr) *elf_header = NULL;
|
||||||
obj_file.c_str(), strerror(errno));
|
if (!LoadELF(obj_file, &map_wrapper, &elf_header))
|
||||||
return false;
|
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;
|
|
||||||
}
|
|
||||||
MmapWrapper map_wrapper(obj_base, st.st_size);
|
|
||||||
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base);
|
|
||||||
if (!IsValidElf(elf_header)) {
|
|
||||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char identifier[16];
|
unsigned char identifier[16];
|
||||||
google_breakpad::FileID file_id(obj_file.c_str());
|
google_breakpad::FileID file_id(obj_file.c_str());
|
||||||
@ -559,9 +707,48 @@ bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) {
|
|||||||
std::string os = "Linux";
|
std::string os = "Linux";
|
||||||
std::string id = FormatIdentifier(identifier);
|
std::string id = FormatIdentifier(identifier);
|
||||||
|
|
||||||
|
LoadSymbolsInfo info(debug_dir);
|
||||||
Module module(name, os, architecture, id);
|
Module module(name, os, architecture, id);
|
||||||
if (!LoadSymbols(obj_file, big_endian, elf_header, &module))
|
if (!LoadSymbols(obj_file, big_endian, elf_header, true, &info, &module)) {
|
||||||
return false;
|
const std::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;
|
||||||
|
ElfW(Ehdr) *debug_elf_header = NULL;
|
||||||
|
if (!LoadELF(debuglink_file, &debug_map_wrapper, &debug_elf_header))
|
||||||
|
return false;
|
||||||
|
// Sanity checks to make sure everything matches up.
|
||||||
|
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(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_file.c_str(), architecture);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool debug_big_endian;
|
||||||
|
if (!ElfEndianness(debug_elf_header, &debug_big_endian))
|
||||||
|
return false;
|
||||||
|
if (debug_big_endian != big_endian) {
|
||||||
|
fprintf(stderr, "%s and %s does not match in endianness\n",
|
||||||
|
obj_file.c_str(), debuglink_file.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LoadSymbols(debuglink_file, debug_big_endian, debug_elf_header,
|
||||||
|
false, &info, &module)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!module.Write(sym_file))
|
if (!module.Write(sym_file))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -44,7 +44,10 @@ namespace google_breakpad {
|
|||||||
// Find all the debugging information in OBJ_FILE, an ELF executable
|
// Find all the debugging information in OBJ_FILE, an ELF executable
|
||||||
// or shared library, and write it to SYM_FILE in the Breakpad symbol
|
// or shared library, and write it to SYM_FILE in the Breakpad symbol
|
||||||
// file format.
|
// file format.
|
||||||
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file);
|
// If OBJ_FILE has been stripped but contains a .gnu_debuglink section,
|
||||||
|
// then look for the debug file in DEBUG_DIR.
|
||||||
|
bool WriteSymbolFile(const std::string &obj_file,
|
||||||
|
const std::string &debug_dir, FILE *sym_file);
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
@ -32,17 +32,21 @@
|
|||||||
|
|
||||||
#include "common/linux/dump_symbols.h"
|
#include "common/linux/dump_symbols.h"
|
||||||
|
|
||||||
using namespace google_breakpad;
|
using google_breakpad::WriteSymbolFile;
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
if (argc != 2) {
|
if (argc < 2 || argc > 3) {
|
||||||
fprintf(stderr, "Usage: %s <binary-with-debugging-info>\n", argv[0]);
|
fprintf(stderr, "Usage: %s <binary-with-debugging-info> "
|
||||||
|
"[directory-for-debug-file]\n", argv[0]);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *binary = argv[1];
|
const char *binary = argv[1];
|
||||||
|
std::string debug_dir;
|
||||||
|
if (argc == 3)
|
||||||
|
debug_dir = argv[2];
|
||||||
|
|
||||||
if (!WriteSymbolFile(binary, stdout)) {
|
if (!WriteSymbolFile(binary, debug_dir, stdout)) {
|
||||||
fprintf(stderr, "Failed to write symbol file.\n");
|
fprintf(stderr, "Failed to write symbol file.\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user