mirror of
https://github.com/tomahawk-player/tomahawk.git
synced 2025-08-19 12:21:52 +02:00
* Added breakpad support for Linux.
This commit is contained in:
824
thirdparty/breakpad/common/linux/dump_symbols.cc
vendored
Normal file
824
thirdparty/breakpad/common/linux/dump_symbols.cc
vendored
Normal file
@@ -0,0 +1,824 @@
|
||||
// Copyright (c) 2010 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 <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/linux/elf_symbols_to_module.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/module.h"
|
||||
#include "common/stabs_reader.h"
|
||||
#include "common/stabs_to_module.h"
|
||||
|
||||
// This namespace contains helper functions.
|
||||
namespace {
|
||||
|
||||
using google_breakpad::DwarfCFIToModule;
|
||||
using google_breakpad::DwarfCUToModule;
|
||||
using google_breakpad::DwarfLineToModule;
|
||||
using google_breakpad::Module;
|
||||
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.
|
||||
// Make life easier when want to find something by offset.
|
||||
static void FixAddress(void *obj_base) {
|
||||
ElfW(Addr) base = reinterpret_cast<ElfW(Addr)>(obj_base);
|
||||
ElfW(Ehdr) *elf_header = static_cast<ElfW(Ehdr) *>(obj_base);
|
||||
elf_header->e_phoff += base;
|
||||
elf_header->e_shoff += base;
|
||||
ElfW(Shdr) *sections = reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
for (int i = 0; i < elf_header->e_shnum; ++i)
|
||||
sections[i].sh_offset += base;
|
||||
}
|
||||
|
||||
// Find the preferred loading address of the binary.
|
||||
static ElfW(Addr) GetLoadingAddress(const ElfW(Phdr) *program_headers,
|
||||
int nheader) {
|
||||
for (int i = 0; i < nheader; ++i) {
|
||||
const ElfW(Phdr) &header = program_headers[i];
|
||||
// For executable, it is the PT_LOAD segment with offset to zero.
|
||||
if (header.p_type == PT_LOAD &&
|
||||
header.p_offset == 0)
|
||||
return header.p_vaddr;
|
||||
}
|
||||
// For other types of ELF, return 0.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool IsValidElf(const ElfW(Ehdr) *elf_header) {
|
||||
return memcmp(elf_header, ELFMAG, SELFMAG) == 0;
|
||||
}
|
||||
|
||||
static const ElfW(Shdr) *FindSectionByName(const char *name,
|
||||
const ElfW(Shdr) *sections,
|
||||
const ElfW(Shdr) *section_names,
|
||||
int nsection) {
|
||||
assert(name != NULL);
|
||||
assert(sections != NULL);
|
||||
assert(nsection > 0);
|
||||
|
||||
int name_len = strlen(name);
|
||||
if (name_len == 0)
|
||||
return NULL;
|
||||
|
||||
// Find the end of the section name section, to make sure that
|
||||
// comparisons don't run off the end of the section.
|
||||
const char *names_end =
|
||||
reinterpret_cast<char*>(section_names->sh_offset + section_names->sh_size);
|
||||
|
||||
for (int i = 0; i < nsection; ++i) {
|
||||
const char *section_name =
|
||||
reinterpret_cast<char*>(section_names->sh_offset + sections[i].sh_name);
|
||||
if (names_end - section_name >= name_len + 1 &&
|
||||
strcmp(name, section_name) == 0) {
|
||||
if (sections[i].sh_type == SHT_NOBITS) {
|
||||
fprintf(stderr,
|
||||
"Section %s found, but ignored because type=SHT_NOBITS.\n",
|
||||
name);
|
||||
return NULL;
|
||||
}
|
||||
return sections + i;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool LoadStabs(const ElfW(Ehdr) *elf_header,
|
||||
const ElfW(Shdr) *stab_section,
|
||||
const ElfW(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".
|
||||
uint8_t *stabs = reinterpret_cast<uint8_t *>(stab_section->sh_offset);
|
||||
uint8_t *stabstr = reinterpret_cast<uint8_t *>(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;
|
||||
}
|
||||
|
||||
// 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::LineToModuleFunctor {
|
||||
public:
|
||||
// Create a line-to-module converter using BYTE_READER.
|
||||
explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||
: byte_reader_(byte_reader) { }
|
||||
void operator()(const char *program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines) {
|
||||
DwarfLineToModule handler(module, lines);
|
||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||
parser.Start();
|
||||
}
|
||||
private:
|
||||
dwarf2reader::ByteReader *byte_reader_;
|
||||
};
|
||||
|
||||
static bool LoadDwarf(const string &dwarf_filename,
|
||||
const ElfW(Ehdr) *elf_header,
|
||||
const bool big_endian,
|
||||
Module *module) {
|
||||
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);
|
||||
|
||||
// Build a map of the ELF file's sections.
|
||||
const ElfW(Shdr) *sections
|
||||
= reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
int num_sections = elf_header->e_shnum;
|
||||
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
|
||||
for (int i = 0; i < num_sections; i++) {
|
||||
const ElfW(Shdr) *section = §ions[i];
|
||||
string name = reinterpret_cast<const char *>(section_names->sh_offset
|
||||
+ section->sh_name);
|
||||
const char *contents = reinterpret_cast<const char *>(section->sh_offset);
|
||||
uint64 length = section->sh_size;
|
||||
file_context.section_map[name] = std::make_pair(contents, length);
|
||||
}
|
||||
|
||||
// Parse all the compilation units in the .debug_info section.
|
||||
DumperLineToModule line_to_module(&byte_reader);
|
||||
std::pair<const char *, uint64> debug_info_section
|
||||
= file_context.section_map[".debug_info"];
|
||||
// We 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 we find.
|
||||
DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset);
|
||||
DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter);
|
||||
// Make a Dwarf2Handler that drives our DIEHandler.
|
||||
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
|
||||
// Make a DWARF parser for the compilation unit at OFFSET.
|
||||
dwarf2reader::CompilationUnit reader(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 we don't recognize HEADER's machine
|
||||
// architecture.
|
||||
static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header,
|
||||
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_X86_64:
|
||||
*register_names = DwarfCFIToModule::RegisterNames::X86_64();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool LoadDwarfCFI(const string &dwarf_filename,
|
||||
const ElfW(Ehdr) *elf_header,
|
||||
const char *section_name,
|
||||
const ElfW(Shdr) *section,
|
||||
const bool eh_frame,
|
||||
const ElfW(Shdr) *got_section,
|
||||
const ElfW(Shdr) *text_section,
|
||||
const bool big_endian,
|
||||
Module *module) {
|
||||
// Find the appropriate set of register names for this file's
|
||||
// architecture.
|
||||
vector<string> 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 char *cfi = reinterpret_cast<const char *>(section->sh_offset);
|
||||
size_t cfi_size = section->sh_size;
|
||||
|
||||
// Plug together the parser, handler, and their entourages.
|
||||
DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name);
|
||||
DwarfCFIToModule handler(module, register_names, &module_reporter);
|
||||
dwarf2reader::ByteReader byte_reader(endianness);
|
||||
// Since we're using the ElfW macro, we're not actually capable of
|
||||
// processing both ELF32 and ELF64 files with the same program; that
|
||||
// would take a bit more work. But this will work out well enough.
|
||||
if (elf_header->e_ident[EI_CLASS] == ELFCLASS32)
|
||||
byte_reader.SetAddressSize(4);
|
||||
else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64)
|
||||
byte_reader.SetAddressSize(8);
|
||||
else {
|
||||
fprintf(stderr, "%s: bad file class in ELF header: %d\n",
|
||||
dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]);
|
||||
return false;
|
||||
}
|
||||
// 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 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.
|
||||
bool ElfEndianness(const ElfW(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;
|
||||
}
|
||||
|
||||
// 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 preferred
|
||||
// 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 preferred 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,
|
||||
const bool big_endian,
|
||||
ElfW(Ehdr) *elf_header,
|
||||
const bool read_gnu_debug_link,
|
||||
LoadSymbolsInfo *info,
|
||||
Module *module) {
|
||||
// Translate all offsets in section headers into address.
|
||||
FixAddress(elf_header);
|
||||
ElfW(Addr) loading_addr = GetLoadingAddress(
|
||||
reinterpret_cast<ElfW(Phdr) *>(elf_header->e_phoff),
|
||||
elf_header->e_phnum);
|
||||
module->SetLoadAddress(loading_addr);
|
||||
info->set_loading_addr(loading_addr, obj_file);
|
||||
|
||||
const ElfW(Shdr) *sections =
|
||||
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
|
||||
bool found_debug_info_section = false;
|
||||
bool found_usable_info = false;
|
||||
|
||||
// Look for STABS debugging information, and load it if present.
|
||||
const ElfW(Shdr) *stab_section
|
||||
= FindSectionByName(".stab", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (stab_section) {
|
||||
const ElfW(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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Look for DWARF debugging information, and load it if present.
|
||||
const ElfW(Shdr) *dwarf_section
|
||||
= FindSectionByName(".debug_info", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (dwarf_section) {
|
||||
found_debug_info_section = true;
|
||||
found_usable_info = true;
|
||||
info->LoadedSection(".debug_info");
|
||||
if (!LoadDwarf(obj_file, elf_header, big_endian, module))
|
||||
fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
|
||||
"DWARF debugging information\n", obj_file.c_str());
|
||||
}
|
||||
|
||||
// Dwarf Call Frame Information (CFI) is actually independent from
|
||||
// the other DWARF debugging information, and can be used alone.
|
||||
const ElfW(Shdr) *dwarf_cfi_section =
|
||||
FindSectionByName(".debug_frame", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (dwarf_cfi_section) {
|
||||
// Ignore the return value of this function; even without call frame
|
||||
// information, the other debugging information could be perfectly
|
||||
// useful.
|
||||
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 ElfW(Shdr) *eh_frame_section =
|
||||
FindSectionByName(".eh_frame", sections, section_names,
|
||||
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 ElfW(Shdr) *got_section =
|
||||
FindSectionByName(".got", sections, section_names, elf_header->e_shnum);
|
||||
const ElfW(Shdr) *text_section =
|
||||
FindSectionByName(".text", sections, section_names,
|
||||
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 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());
|
||||
}
|
||||
} else {
|
||||
// The caller doesn't want to consult .gnu_debuglink.
|
||||
// See if there are export symbols available.
|
||||
const ElfW(Shdr) *dynsym_section =
|
||||
FindSectionByName(".dynsym", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
const ElfW(Shdr) *dynstr_section =
|
||||
FindSectionByName(".dynstr", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (dynsym_section && dynstr_section) {
|
||||
info->LoadedSection(".dynsym");
|
||||
fprintf(stderr, "Have .dynsym + .dynstr\n");
|
||||
|
||||
uint8_t* dynsyms =
|
||||
reinterpret_cast<uint8_t*>(dynsym_section->sh_offset);
|
||||
uint8_t* dynstrs =
|
||||
reinterpret_cast<uint8_t*>(dynstr_section->sh_offset);
|
||||
bool result =
|
||||
ELFSymbolsToModule(dynsyms,
|
||||
dynsym_section->sh_size,
|
||||
dynstrs,
|
||||
dynstr_section->sh_size,
|
||||
big_endian,
|
||||
// This could change to something more useful
|
||||
// when support for dumping cross-architecture
|
||||
// symbols is finished.
|
||||
sizeof(ElfW(Addr)),
|
||||
module);
|
||||
found_usable_info = found_usable_info || result;
|
||||
}
|
||||
|
||||
// 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.
|
||||
const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) {
|
||||
ElfW(Half) arch = elf_header->e_machine;
|
||||
switch (arch) {
|
||||
case EM_386: return "x86";
|
||||
case EM_ARM: return "arm";
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Format the Elf file identifier in IDENTIFIER as a UUID with the
|
||||
// dashes removed.
|
||||
std::string FormatIdentifier(unsigned char identifier[16]) {
|
||||
char identifier_str[40];
|
||||
google_breakpad::FileID::ConvertIdentifierToString(
|
||||
identifier,
|
||||
identifier_str,
|
||||
sizeof(identifier_str));
|
||||
std::string id_no_dash;
|
||||
for (int i = 0; identifier_str[i] != '\0'; ++i)
|
||||
if (identifier_str[i] != '-')
|
||||
id_no_dash += identifier_str[i];
|
||||
// Add an extra "0" by 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 let's preserve
|
||||
// the pattern.
|
||||
id_no_dash += '0';
|
||||
return id_no_dash;
|
||||
}
|
||||
|
||||
// Return the non-directory portion of FILENAME: the portion after the
|
||||
// last slash, or the whole filename if there are no slashes.
|
||||
std::string BaseFileName(const std::string &filename) {
|
||||
// Lots of copies! basename's behavior is less than ideal.
|
||||
char *c_filename = strdup(filename.c_str());
|
||||
std::string base = basename(c_filename);
|
||||
free(c_filename);
|
||||
return base;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Not explicitly exported, but not static so it can be used in unit tests.
|
||||
// Ideally obj_file would be const, but internally this code does write
|
||||
// to some ELF header fields to make its work simpler.
|
||||
bool WriteSymbolFileInternal(uint8_t* obj_file,
|
||||
const std::string &obj_filename,
|
||||
const std::string &debug_dir,
|
||||
std::ostream &sym_stream) {
|
||||
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_file);
|
||||
|
||||
if (!IsValidElf(elf_header)) {
|
||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char identifier[16];
|
||||
if (!google_breakpad::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;
|
||||
}
|
||||
|
||||
// Figure out what endianness this file is.
|
||||
bool big_endian;
|
||||
if (!ElfEndianness(elf_header, &big_endian))
|
||||
return false;
|
||||
|
||||
std::string name = BaseFileName(obj_filename);
|
||||
std::string os = "Linux";
|
||||
std::string id = FormatIdentifier(identifier);
|
||||
|
||||
LoadSymbolsInfo info(debug_dir);
|
||||
Module module(name, os, architecture, id);
|
||||
if (!LoadSymbols(obj_filename, big_endian, elf_header, !debug_dir.empty(),
|
||||
&info, &module)) {
|
||||
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_filename.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_filename.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_stream))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
const std::string &debug_dir,
|
||||
std::ostream &sym_stream) {
|
||||
MmapWrapper map_wrapper;
|
||||
ElfW(Ehdr) *elf_header = NULL;
|
||||
if (!LoadELF(obj_file, &map_wrapper, &elf_header))
|
||||
return false;
|
||||
|
||||
return WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(elf_header),
|
||||
obj_file, debug_dir, sym_stream);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
54
thirdparty/breakpad/common/linux/dump_symbols.h
vendored
Normal file
54
thirdparty/breakpad/common/linux/dump_symbols.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010, 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.
|
||||
|
||||
// dump_symbols.h: Read debugging information from an ELF file, and write
|
||||
// it out as a Breakpad symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
#define COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Find all the debugging information in OBJ_FILE, an ELF executable
|
||||
// or shared library, and write it to SYM_STREAM in the Breakpad symbol
|
||||
// file format.
|
||||
// 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,
|
||||
std::ostream &sym_stream);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DUMP_SYMBOLS_H__
|
162
thirdparty/breakpad/common/linux/dump_symbols_unittest.cc
vendored
Normal file
162
thirdparty/breakpad/common/linux/dump_symbols_unittest.cc
vendored
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
// dump_symbols_unittest.cc:
|
||||
// Unittests for google_breakpad::DumpSymbols
|
||||
|
||||
#include <elf.h>
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/synth_elf.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
bool WriteSymbolFileInternal(uint8_t* obj_file,
|
||||
const std::string &obj_filename,
|
||||
const std::string &debug_dir,
|
||||
std::ostream &sym_stream);
|
||||
}
|
||||
|
||||
using google_breakpad::synth_elf::ELF;
|
||||
using google_breakpad::synth_elf::StringTable;
|
||||
using google_breakpad::synth_elf::SymbolTable;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using google_breakpad::WriteSymbolFileInternal;
|
||||
using std::string;
|
||||
using std::stringstream;
|
||||
using std::vector;
|
||||
using ::testing::Test;
|
||||
|
||||
class DumpSymbols : public Test {
|
||||
public:
|
||||
void GetElfContents(ELF& elf) {
|
||||
string contents;
|
||||
ASSERT_TRUE(elf.GetContents(&contents));
|
||||
ASSERT_LT(0, contents.size());
|
||||
|
||||
elfdata_v.clear();
|
||||
elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
|
||||
elfdata = &elfdata_v[0];
|
||||
}
|
||||
|
||||
vector<uint8_t> elfdata_v;
|
||||
uint8_t* elfdata;
|
||||
};
|
||||
|
||||
TEST_F(DumpSymbols, Invalid) {
|
||||
Elf32_Ehdr header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
stringstream s;
|
||||
EXPECT_FALSE(WriteSymbolFileInternal(reinterpret_cast<uint8_t*>(&header),
|
||||
"foo",
|
||||
"",
|
||||
s));
|
||||
}
|
||||
|
||||
// TODO(ted): Fix the dump_symbols code to deal with cross-word-size
|
||||
// ELF files.
|
||||
#if __ELF_NATIVE_CLASS == 32
|
||||
TEST_F(DumpSymbols, SimplePublic32) {
|
||||
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf.AddSection(".text", text, SHT_PROGBITS);
|
||||
|
||||
// Add a public symbol.
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 4, table);
|
||||
syms.AddSymbol("superfunc", (uint32_t)0x1000, (uint32_t)0x10,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
elf.AddSection(".dynsym", syms,
|
||||
SHT_DYNSYM, // type
|
||||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(Elf32_Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
GetElfContents(elf);
|
||||
|
||||
stringstream s;
|
||||
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
|
||||
"foo",
|
||||
"",
|
||||
s));
|
||||
EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
#if __ELF_NATIVE_CLASS == 64
|
||||
TEST_F(DumpSymbols, SimplePublic64) {
|
||||
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf.AddSection(".text", text, SHT_PROGBITS);
|
||||
|
||||
// Add a public symbol.
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 8, table);
|
||||
syms.AddSymbol("superfunc", (uint64_t)0x1000, (uint64_t)0x10,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
elf.AddSection(".dynsym", syms,
|
||||
SHT_DYNSYM, // type
|
||||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(Elf64_Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
GetElfContents(elf);
|
||||
|
||||
stringstream s;
|
||||
ASSERT_TRUE(WriteSymbolFileInternal(elfdata,
|
||||
"foo",
|
||||
"",
|
||||
s));
|
||||
EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
}
|
||||
#endif
|
47
thirdparty/breakpad/common/linux/eintr_wrapper.h
vendored
Normal file
47
thirdparty/breakpad/common/linux/eintr_wrapper.h
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
#ifndef COMMON_LINUX_EINTR_WRAPPER_H_
|
||||
#define COMMON_LINUX_EINTR_WRAPPER_H_
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
// This provides a wrapper around system calls which may be interrupted by a
|
||||
// signal and return EINTR. See man 7 signal.
|
||||
//
|
||||
|
||||
#define HANDLE_EINTR(x) ({ \
|
||||
typeof(x) __eintr_result__; \
|
||||
do { \
|
||||
__eintr_result__ = x; \
|
||||
} while (__eintr_result__ == -1 && errno == EINTR); \
|
||||
__eintr_result__;\
|
||||
})
|
||||
|
||||
#endif // ifndef COMMON_LINUX_EINTR_WRAPPER_H_
|
168
thirdparty/breakpad/common/linux/elf_symbols_to_module.cc
vendored
Normal file
168
thirdparty/breakpad/common/linux/elf_symbols_to_module.cc
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
#include "common/linux/elf_symbols_to_module.h"
|
||||
|
||||
#include <elf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/byte_cursor.h"
|
||||
#include "common/module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ELFSymbolIterator {
|
||||
public:
|
||||
// The contents of an ELF symbol, adjusted for the host's endianness,
|
||||
// word size, and so on. Corresponds to the data in Elf32_Sym / Elf64_Sym.
|
||||
struct Symbol {
|
||||
// True if this iterator has reached the end of the symbol array. When
|
||||
// this is set, the other members of this structure are not valid.
|
||||
bool at_end;
|
||||
|
||||
// The number of this symbol within the list.
|
||||
size_t index;
|
||||
|
||||
// The current symbol's name offset. This is the offset within the
|
||||
// string table.
|
||||
size_t name_offset;
|
||||
|
||||
// The current symbol's value, size, info and shndx fields.
|
||||
uint64_t value;
|
||||
uint64_t size;
|
||||
unsigned char info;
|
||||
uint16_t shndx;
|
||||
};
|
||||
|
||||
// Create an ELFSymbolIterator walking the symbols in BUFFER. Treat the
|
||||
// symbols as big-endian if BIG_ENDIAN is true, as little-endian
|
||||
// otherwise. Assume each symbol has a 'value' field whose size is
|
||||
// VALUE_SIZE.
|
||||
//
|
||||
ELFSymbolIterator(const ByteBuffer *buffer, bool big_endian,
|
||||
size_t value_size)
|
||||
: value_size_(value_size), cursor_(buffer, big_endian) {
|
||||
// Actually, weird sizes could be handled just fine, but they're
|
||||
// probably mistakes --- expressed in bits, say.
|
||||
assert(value_size == 4 || value_size == 8);
|
||||
symbol_.index = 0;
|
||||
Fetch();
|
||||
}
|
||||
|
||||
// Move to the next symbol. This function's behavior is undefined if
|
||||
// at_end() is true when it is called.
|
||||
ELFSymbolIterator &operator++() { Fetch(); symbol_.index++; return *this; }
|
||||
|
||||
// Dereferencing this iterator produces a reference to an Symbol structure
|
||||
// that holds the current symbol's values. The symbol is owned by this
|
||||
// SymbolIterator, and will be invalidated at the next call to operator++.
|
||||
const Symbol &operator*() const { return symbol_; }
|
||||
const Symbol *operator->() const { return &symbol_; }
|
||||
|
||||
private:
|
||||
// Read the symbol at cursor_, and set symbol_ appropriately.
|
||||
void Fetch() {
|
||||
// Elf32_Sym and Elf64_Sym have different layouts.
|
||||
unsigned char other;
|
||||
if (value_size_ == 4) {
|
||||
// Elf32_Sym
|
||||
cursor_
|
||||
.Read(4, false, &symbol_.name_offset)
|
||||
.Read(4, false, &symbol_.value)
|
||||
.Read(4, false, &symbol_.size)
|
||||
.Read(1, false, &symbol_.info)
|
||||
.Read(1, false, &other)
|
||||
.Read(2, false, &symbol_.shndx);
|
||||
} else {
|
||||
// Elf64_Sym
|
||||
cursor_
|
||||
.Read(4, false, &symbol_.name_offset)
|
||||
.Read(1, false, &symbol_.info)
|
||||
.Read(1, false, &other)
|
||||
.Read(2, false, &symbol_.shndx)
|
||||
.Read(8, false, &symbol_.value)
|
||||
.Read(8, false, &symbol_.size);
|
||||
}
|
||||
symbol_.at_end = !cursor_;
|
||||
}
|
||||
|
||||
// The size of symbols' value field, in bytes.
|
||||
size_t value_size_;
|
||||
|
||||
// A byte cursor traversing buffer_.
|
||||
ByteCursor cursor_;
|
||||
|
||||
// Values for the symbol this iterator refers to.
|
||||
Symbol symbol_;
|
||||
};
|
||||
|
||||
const char *SymbolString(ptrdiff_t offset, ByteBuffer& strings) {
|
||||
if (offset < 0 || (size_t) offset >= strings.Size()) {
|
||||
// Return the null string.
|
||||
offset = 0;
|
||||
}
|
||||
return reinterpret_cast<const char *>(strings.start + offset);
|
||||
}
|
||||
|
||||
bool ELFSymbolsToModule(const uint8_t *symtab_section,
|
||||
size_t symtab_size,
|
||||
const uint8_t *string_section,
|
||||
size_t string_size,
|
||||
const bool big_endian,
|
||||
size_t value_size,
|
||||
Module *module) {
|
||||
ByteBuffer symbols(symtab_section, symtab_size);
|
||||
// Ensure that the string section is null-terminated.
|
||||
if (string_section[string_size - 1] != '\0') {
|
||||
const void* null_terminator = memrchr(string_section, '\0', string_size);
|
||||
string_size = reinterpret_cast<const uint8_t*>(null_terminator)
|
||||
- string_section;
|
||||
}
|
||||
ByteBuffer strings(string_section, string_size);
|
||||
|
||||
// The iterator walking the symbol table.
|
||||
ELFSymbolIterator iterator(&symbols, big_endian, value_size);
|
||||
|
||||
while(!iterator->at_end) {
|
||||
if (ELF32_ST_TYPE(iterator->info) == STT_FUNC &&
|
||||
iterator->shndx != SHN_UNDEF) {
|
||||
Module::Extern *ext = new Module::Extern;
|
||||
ext->name = SymbolString(iterator->name_offset, strings);
|
||||
ext->address = iterator->value;
|
||||
module->AddExtern(ext);
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
58
thirdparty/breakpad/common/linux/elf_symbols_to_module.h
vendored
Normal file
58
thirdparty/breakpad/common/linux/elf_symbols_to_module.h
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
// elf_symbols_to_module.h: Exposes ELFSymbolsToModule, a function
|
||||
// for reading ELF symbol tables and inserting exported symbol names
|
||||
// into a google_breakpad::Module as Extern definitions.
|
||||
|
||||
#ifndef BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_
|
||||
#define BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class Module;
|
||||
|
||||
bool ELFSymbolsToModule(const uint8_t *symtab_section,
|
||||
size_t symtab_size,
|
||||
const uint8_t *string_section,
|
||||
size_t string_size,
|
||||
const bool big_endian,
|
||||
size_t value_size,
|
||||
Module *module);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
||||
#endif // BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_
|
370
thirdparty/breakpad/common/linux/elf_symbols_to_module_unittest.cc
vendored
Normal file
370
thirdparty/breakpad/common/linux/elf_symbols_to_module_unittest.cc
vendored
Normal file
@@ -0,0 +1,370 @@
|
||||
// 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
// elf_symbols_to_module_unittest.cc:
|
||||
// Unittests for google_breakpad::ELFSymbolsToModule
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/elf_symbols_to_module.h"
|
||||
#include "common/linux/synth_elf.h"
|
||||
#include "common/module.h"
|
||||
#include "common/test_assembler.h"
|
||||
|
||||
using google_breakpad::Module;
|
||||
using google_breakpad::synth_elf::StringTable;
|
||||
using google_breakpad::test_assembler::Endianness;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
using ::testing::Test;
|
||||
using ::testing::TestWithParam;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
class ELFSymbolsToModuleTestFixture {
|
||||
public:
|
||||
ELFSymbolsToModuleTestFixture(Endianness endianness,
|
||||
size_t value_size) : module("a", "b", "c", "d"),
|
||||
section(endianness),
|
||||
table(endianness),
|
||||
value_size(value_size) {}
|
||||
|
||||
bool ProcessSection() {
|
||||
string section_contents, table_contents;
|
||||
section.GetContents(§ion_contents);
|
||||
table.GetContents(&table_contents);
|
||||
|
||||
bool ret = ELFSymbolsToModule(reinterpret_cast<const uint8_t*>(section_contents.data()),
|
||||
section_contents.size(),
|
||||
reinterpret_cast<const uint8_t*>(table_contents.data()),
|
||||
table_contents.size(),
|
||||
section.endianness() == kBigEndian,
|
||||
value_size,
|
||||
&module);
|
||||
module.GetExterns(&externs, externs.end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
Module module;
|
||||
Section section;
|
||||
StringTable table;
|
||||
string section_contents;
|
||||
// 4 or 8 (bytes)
|
||||
size_t value_size;
|
||||
|
||||
vector<Module::Extern *> externs;
|
||||
};
|
||||
|
||||
class ELFSymbolsToModuleTest32 : public ELFSymbolsToModuleTestFixture,
|
||||
public TestWithParam<Endianness> {
|
||||
public:
|
||||
ELFSymbolsToModuleTest32() : ELFSymbolsToModuleTestFixture(GetParam(), 4) {}
|
||||
|
||||
void AddElf32Sym(const string& name, uint32_t value,
|
||||
uint32_t size, unsigned info, uint16_t shndx) {
|
||||
section
|
||||
.D32(table.Add(name))
|
||||
.D32(value)
|
||||
.D32(size)
|
||||
.D8(info)
|
||||
.D8(0) // other
|
||||
.D16(shndx);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest32, NoFuncs) {
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)0, externs.size());
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest32, OneFunc) {
|
||||
const string kFuncName = "superfunc";
|
||||
const uint32_t kFuncAddr = 0x1000;
|
||||
const uint32_t kFuncSize = 0x10;
|
||||
|
||||
AddElf32Sym(kFuncName, kFuncAddr, kFuncSize,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 1);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)1, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr, extern1->address);
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest32, NameOutOfBounds) {
|
||||
const string kFuncName = "";
|
||||
const uint32_t kFuncAddr = 0x1000;
|
||||
const uint32_t kFuncSize = 0x10;
|
||||
|
||||
table.Add("Foo");
|
||||
table.Add("Bar");
|
||||
// Can't use AddElf32Sym because it puts in a valid string offset.
|
||||
section
|
||||
.D32((uint32_t)table.Here().Value() + 1)
|
||||
.D32(kFuncAddr)
|
||||
.D32(kFuncSize)
|
||||
.D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC))
|
||||
.D8(0) // other
|
||||
.D16(SHN_UNDEF + 1);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)1, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr, extern1->address);
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest32, NonTerminatedStringTable) {
|
||||
const string kFuncName = "";
|
||||
const uint32_t kFuncAddr = 0x1000;
|
||||
const uint32_t kFuncSize = 0x10;
|
||||
|
||||
table.Add("Foo");
|
||||
table.Add("Bar");
|
||||
// Add a non-null-terminated string to the end of the string table
|
||||
Label l;
|
||||
table
|
||||
.Mark(&l)
|
||||
.Append("Unterminated");
|
||||
// Can't use AddElf32Sym because it puts in a valid string offset.
|
||||
section
|
||||
.D32((uint32_t)l.Value())
|
||||
.D32(kFuncAddr)
|
||||
.D32(kFuncSize)
|
||||
.D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC))
|
||||
.D8(0) // other
|
||||
.D16(SHN_UNDEF + 1);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)1, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr, extern1->address);
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest32, MultipleFuncs) {
|
||||
const string kFuncName1 = "superfunc";
|
||||
const uint32_t kFuncAddr1 = 0x10001000;
|
||||
const uint32_t kFuncSize1 = 0x10;
|
||||
const string kFuncName2 = "awesomefunc";
|
||||
const uint32_t kFuncAddr2 = 0x20002000;
|
||||
const uint32_t kFuncSize2 = 0x2f;
|
||||
const string kFuncName3 = "megafunc";
|
||||
const uint32_t kFuncAddr3 = 0x30003000;
|
||||
const uint32_t kFuncSize3 = 0x3c;
|
||||
|
||||
AddElf32Sym(kFuncName1, kFuncAddr1, kFuncSize1,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 1);
|
||||
AddElf32Sym(kFuncName2, kFuncAddr2, kFuncSize2,
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 2);
|
||||
AddElf32Sym(kFuncName3, kFuncAddr3, kFuncSize3,
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 3);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)3, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName1, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address);
|
||||
Module::Extern *extern2 = externs[1];
|
||||
EXPECT_EQ(kFuncName2, extern2->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address);
|
||||
Module::Extern *extern3 = externs[2];
|
||||
EXPECT_EQ(kFuncName3, extern3->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address);
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest32, SkipStuff) {
|
||||
const string kFuncName = "superfunc";
|
||||
const uint32_t kFuncAddr = 0x1000;
|
||||
const uint32_t kFuncSize = 0x10;
|
||||
|
||||
// Should skip functions in SHN_UNDEF
|
||||
AddElf32Sym("skipme", 0xFFFF, 0x10,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF);
|
||||
AddElf32Sym(kFuncName, kFuncAddr, kFuncSize,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 1);
|
||||
// Should skip non-STT_FUNC entries.
|
||||
AddElf32Sym("skipmetoo", 0xAAAA, 0x10,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FILE),
|
||||
SHN_UNDEF + 1);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)1, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr, extern1->address);
|
||||
}
|
||||
|
||||
// Run all the 32-bit tests with both endianness
|
||||
INSTANTIATE_TEST_CASE_P(Endian,
|
||||
ELFSymbolsToModuleTest32,
|
||||
::testing::Values(kLittleEndian, kBigEndian));
|
||||
|
||||
// Similar tests, but with 64-bit values. Ostensibly this could be
|
||||
// shoehorned into the parameterization by using ::testing::Combine,
|
||||
// but that would make it difficult to get the types right since these
|
||||
// actual test cases aren't parameterized. This could also be written
|
||||
// as a type-parameterized test, but combining that with a value-parameterized
|
||||
// test seemed really ugly, and also makes it harder to test 64-bit
|
||||
// values.
|
||||
class ELFSymbolsToModuleTest64 : public ELFSymbolsToModuleTestFixture,
|
||||
public TestWithParam<Endianness> {
|
||||
public:
|
||||
ELFSymbolsToModuleTest64() : ELFSymbolsToModuleTestFixture(GetParam(), 8) {}
|
||||
|
||||
void AddElf64Sym(const string& name, uint64_t value,
|
||||
uint64_t size, unsigned info, uint16_t shndx) {
|
||||
section
|
||||
.D32(table.Add(name))
|
||||
.D8(info)
|
||||
.D8(0) // other
|
||||
.D16(shndx)
|
||||
.D64(value)
|
||||
.D64(size);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest64, NoFuncs) {
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)0, externs.size());
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest64, OneFunc) {
|
||||
const string kFuncName = "superfunc";
|
||||
const uint64_t kFuncAddr = 0x1000200030004000ULL;
|
||||
const uint64_t kFuncSize = 0x1000;
|
||||
|
||||
AddElf64Sym(kFuncName, kFuncAddr, kFuncSize,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 1);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)1, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr, extern1->address);
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest64, MultipleFuncs) {
|
||||
const string kFuncName1 = "superfunc";
|
||||
const uint64_t kFuncAddr1 = 0x1000100010001000ULL;
|
||||
const uint64_t kFuncSize1 = 0x1000;
|
||||
const string kFuncName2 = "awesomefunc";
|
||||
const uint64_t kFuncAddr2 = 0x2000200020002000ULL;
|
||||
const uint64_t kFuncSize2 = 0x2f00;
|
||||
const string kFuncName3 = "megafunc";
|
||||
const uint64_t kFuncAddr3 = 0x3000300030003000ULL;
|
||||
const uint64_t kFuncSize3 = 0x3c00;
|
||||
|
||||
AddElf64Sym(kFuncName1, kFuncAddr1, kFuncSize1,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 1);
|
||||
AddElf64Sym(kFuncName2, kFuncAddr2, kFuncSize2,
|
||||
ELF64_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 2);
|
||||
AddElf64Sym(kFuncName3, kFuncAddr3, kFuncSize3,
|
||||
ELF64_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 3);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)3, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName1, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address);
|
||||
Module::Extern *extern2 = externs[1];
|
||||
EXPECT_EQ(kFuncName2, extern2->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address);
|
||||
Module::Extern *extern3 = externs[2];
|
||||
EXPECT_EQ(kFuncName3, extern3->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address);
|
||||
}
|
||||
|
||||
TEST_P(ELFSymbolsToModuleTest64, SkipStuff) {
|
||||
const string kFuncName = "superfunc";
|
||||
const uint64_t kFuncAddr = 0x1000100010001000ULL;
|
||||
const uint64_t kFuncSize = 0x1000;
|
||||
|
||||
// Should skip functions in SHN_UNDEF
|
||||
AddElf64Sym("skipme", 0xFFFF, 0x10,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF);
|
||||
AddElf64Sym(kFuncName, kFuncAddr, kFuncSize,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
// Doesn't really matter, just can't be SHN_UNDEF.
|
||||
SHN_UNDEF + 1);
|
||||
// Should skip non-STT_FUNC entries.
|
||||
AddElf64Sym("skipmetoo", 0xAAAA, 0x10,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FILE),
|
||||
SHN_UNDEF + 1);
|
||||
|
||||
ProcessSection();
|
||||
|
||||
ASSERT_EQ((size_t)1, externs.size());
|
||||
Module::Extern *extern1 = externs[0];
|
||||
EXPECT_EQ(kFuncName, extern1->name);
|
||||
EXPECT_EQ((Module::Address)kFuncAddr, extern1->address);
|
||||
}
|
||||
|
||||
// Run all the 64-bit tests with both endianness
|
||||
INSTANTIATE_TEST_CASE_P(Endian,
|
||||
ELFSymbolsToModuleTest64,
|
||||
::testing::Values(kLittleEndian, kBigEndian));
|
300
thirdparty/breakpad/common/linux/file_id.cc
vendored
Normal file
300
thirdparty/breakpad/common/linux/file_id.cc
vendored
Normal file
@@ -0,0 +1,300 @@
|
||||
// Copyright (c) 2006, 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.
|
||||
//
|
||||
// file_id.cc: Return a unique identifier for a file
|
||||
//
|
||||
// See file_id.h for documentation
|
||||
//
|
||||
|
||||
#include "common/linux/file_id.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#if defined(__ANDROID__)
|
||||
#include "client/linux/android_link.h"
|
||||
#else
|
||||
#include <link.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "third_party/lss/linux_syscall_support.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
#ifndef NT_GNU_BUILD_ID
|
||||
#define NT_GNU_BUILD_ID 3
|
||||
#endif
|
||||
|
||||
FileID::FileID(const char* path) {
|
||||
strncpy(path_, path, sizeof(path_));
|
||||
}
|
||||
|
||||
struct ElfClass32 {
|
||||
typedef Elf32_Ehdr Ehdr;
|
||||
typedef Elf32_Nhdr Nhdr;
|
||||
typedef Elf32_Shdr Shdr;
|
||||
static const int kClass = ELFCLASS32;
|
||||
};
|
||||
|
||||
struct ElfClass64 {
|
||||
typedef Elf64_Ehdr Ehdr;
|
||||
typedef Elf64_Nhdr Nhdr;
|
||||
typedef Elf64_Shdr Shdr;
|
||||
static const int kClass = ELFCLASS64;
|
||||
};
|
||||
|
||||
// These six functions are also used inside the crashed process, so be safe
|
||||
// and use the syscall/libc wrappers instead of direct syscalls or libc.
|
||||
template<typename ElfClass>
|
||||
static void FindElfClassSection(const char *elf_base,
|
||||
const char *section_name,
|
||||
uint32_t section_type,
|
||||
const void **section_start,
|
||||
int *section_size) {
|
||||
typedef typename ElfClass::Ehdr Ehdr;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
|
||||
assert(elf_base);
|
||||
assert(section_start);
|
||||
assert(section_size);
|
||||
|
||||
assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
|
||||
|
||||
int name_len = my_strlen(section_name);
|
||||
|
||||
const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
|
||||
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
|
||||
|
||||
const Shdr* sections =
|
||||
reinterpret_cast<const Shdr*>(elf_base + elf_header->e_shoff);
|
||||
const Shdr* string_section = sections + elf_header->e_shstrndx;
|
||||
|
||||
const Shdr* section = NULL;
|
||||
for (int i = 0; i < elf_header->e_shnum; ++i) {
|
||||
if (sections[i].sh_type == section_type) {
|
||||
const char* section_name = (char*)(elf_base +
|
||||
string_section->sh_offset +
|
||||
sections[i].sh_name);
|
||||
if (!my_strncmp(section_name, section_name, name_len)) {
|
||||
section = §ions[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (section != NULL && section->sh_size > 0) {
|
||||
*section_start = elf_base + section->sh_offset;
|
||||
*section_size = section->sh_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to find a section named |section_name| of type |section_type|
|
||||
// in the ELF binary data at |elf_mapped_base|. On success, returns true
|
||||
// and sets |*section_start| to point to the start of the section data,
|
||||
// and |*section_size| to the size of the section's data. If |elfclass|
|
||||
// is not NULL, set |*elfclass| to the ELF file class.
|
||||
static bool FindElfSection(const void *elf_mapped_base,
|
||||
const char *section_name,
|
||||
uint32_t section_type,
|
||||
const void **section_start,
|
||||
int *section_size,
|
||||
int *elfclass) {
|
||||
assert(elf_mapped_base);
|
||||
assert(section_start);
|
||||
assert(section_size);
|
||||
|
||||
*section_start = NULL;
|
||||
*section_size = 0;
|
||||
|
||||
const char* elf_base =
|
||||
static_cast<const char*>(elf_mapped_base);
|
||||
const ElfW(Ehdr)* elf_header =
|
||||
reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
|
||||
if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0)
|
||||
return false;
|
||||
|
||||
if (elfclass) {
|
||||
*elfclass = elf_header->e_ident[EI_CLASS];
|
||||
}
|
||||
|
||||
if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) {
|
||||
FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
|
||||
section_start, section_size);
|
||||
return *section_start != NULL;
|
||||
} else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) {
|
||||
FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
|
||||
section_start, section_size);
|
||||
return *section_start != NULL;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename ElfClass>
|
||||
static bool ElfClassBuildIDNoteIdentifier(const void *section,
|
||||
uint8_t identifier[kMDGUIDSize])
|
||||
{
|
||||
typedef typename ElfClass::Nhdr Nhdr;
|
||||
|
||||
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
|
||||
if (note_header->n_type != NT_GNU_BUILD_ID ||
|
||||
note_header->n_descsz == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* build_id = reinterpret_cast<const char*>(section) +
|
||||
sizeof(Nhdr) + note_header->n_namesz;
|
||||
// Copy as many bits of the build ID as will fit
|
||||
// into the GUID space.
|
||||
my_memset(identifier, 0, kMDGUIDSize);
|
||||
memcpy(identifier, build_id,
|
||||
std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attempt to locate a .note.gnu.build-id section in an ELF binary
|
||||
// and copy as many bytes of it as will fit into |identifier|.
|
||||
static bool FindElfBuildIDNote(const void *elf_mapped_base,
|
||||
uint8_t identifier[kMDGUIDSize])
|
||||
{
|
||||
void* note_section;
|
||||
int note_size, elfclass;
|
||||
if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
|
||||
(const void**)¬e_section, ¬e_size, &elfclass) ||
|
||||
note_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elfclass == ELFCLASS32) {
|
||||
return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, identifier);
|
||||
} else if (elfclass == ELFCLASS64) {
|
||||
return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, identifier);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt to locate the .text section of an ELF binary and generate
|
||||
// a simple hash by XORing the first page worth of bytes into |identifier|.
|
||||
static bool HashElfTextSection(const void *elf_mapped_base,
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
|
||||
void* text_section;
|
||||
int text_size;
|
||||
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
|
||||
(const void**)&text_section, &text_size, NULL) ||
|
||||
text_size == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
my_memset(identifier, 0, kMDGUIDSize);
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
|
||||
const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
|
||||
while (ptr < ptr_end) {
|
||||
for (unsigned i = 0; i < kMDGUIDSize; i++)
|
||||
identifier[i] ^= ptr[i];
|
||||
ptr += kMDGUIDSize;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool FileID::ElfFileIdentifierFromMappedFile(void* base,
|
||||
uint8_t identifier[kMDGUIDSize])
|
||||
{
|
||||
// Look for a build id note first.
|
||||
if (FindElfBuildIDNote(base, identifier))
|
||||
return true;
|
||||
|
||||
// Fall back on hashing the first page of the text section.
|
||||
return HashElfTextSection(base, identifier);
|
||||
}
|
||||
|
||||
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
|
||||
int fd = open(path_, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return false;
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
void* base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
|
||||
close(fd);
|
||||
if (base == MAP_FAILED)
|
||||
return false;
|
||||
|
||||
bool success = ElfFileIdentifierFromMappedFile(base, identifier);
|
||||
munmap(base, st.st_size);
|
||||
return success;
|
||||
}
|
||||
|
||||
// static
|
||||
void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
|
||||
char* buffer, int buffer_length) {
|
||||
uint8_t identifier_swapped[kMDGUIDSize];
|
||||
|
||||
// Endian-ness swap to match dump processor expectation.
|
||||
memcpy(identifier_swapped, identifier, kMDGUIDSize);
|
||||
uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
|
||||
*data1 = htonl(*data1);
|
||||
uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
|
||||
*data2 = htons(*data2);
|
||||
uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
|
||||
*data3 = htons(*data3);
|
||||
|
||||
int buffer_idx = 0;
|
||||
for (unsigned int idx = 0;
|
||||
(buffer_idx < buffer_length) && (idx < kMDGUIDSize);
|
||||
++idx) {
|
||||
int hi = (identifier_swapped[idx] >> 4) & 0x0F;
|
||||
int lo = (identifier_swapped[idx]) & 0x0F;
|
||||
|
||||
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
|
||||
buffer[buffer_idx++] = '-';
|
||||
|
||||
buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
|
||||
buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
|
||||
}
|
||||
|
||||
// NULL terminate
|
||||
buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
77
thirdparty/breakpad/common/linux/file_id.h
vendored
Normal file
77
thirdparty/breakpad/common/linux/file_id.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright (c) 2006, 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.
|
||||
//
|
||||
// file_id.h: Return a unique identifier for a file
|
||||
//
|
||||
|
||||
#ifndef COMMON_LINUX_FILE_ID_H__
|
||||
#define COMMON_LINUX_FILE_ID_H__
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static const size_t kMDGUIDSize = sizeof(MDGUID);
|
||||
|
||||
class FileID {
|
||||
public:
|
||||
explicit FileID(const char* path);
|
||||
~FileID() {}
|
||||
|
||||
// Load the identifier for the elf file path specified in the constructor into
|
||||
// |identifier|. Return false if the identifier could not be created for the
|
||||
// file.
|
||||
// The current implementation will look for a .note.gnu.build-id
|
||||
// section and use that as the file id, otherwise it falls back to
|
||||
// XORing the first 4096 bytes of the .text section to generate an identifier.
|
||||
bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]);
|
||||
|
||||
// Load the identifier for the elf file mapped into memory at |base| into
|
||||
// |identifier|. Return false if the identifier could not be created for the
|
||||
// file.
|
||||
static bool ElfFileIdentifierFromMappedFile(void* base,
|
||||
uint8_t identifier[kMDGUIDSize]);
|
||||
|
||||
// Convert the |identifier| data to a NULL terminated string. The string will
|
||||
// be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE).
|
||||
// The |buffer| should be at least 37 bytes long to receive all of the data
|
||||
// and termination. Shorter buffers will contain truncated data.
|
||||
static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
|
||||
char* buffer, int buffer_length);
|
||||
|
||||
private:
|
||||
// Storage for the path specified
|
||||
char path_[PATH_MAX];
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_FILE_ID_H__
|
183
thirdparty/breakpad/common/linux/file_id_unittest.cc
vendored
Normal file
183
thirdparty/breakpad/common/linux/file_id_unittest.cc
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
// Copyright (c) 2010, 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.
|
||||
|
||||
// Unit tests for FileID
|
||||
|
||||
#include <elf.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/synth_elf.h"
|
||||
#include "common/test_assembler.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
using google_breakpad::synth_elf::BuildIDNote;
|
||||
using google_breakpad::synth_elf::ELF;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Section;
|
||||
|
||||
TEST(FileIDStripTest, StripSelf) {
|
||||
// Calculate the File ID of this binary using
|
||||
// FileID::ElfFileIdentifier, then make a copy of this binary,
|
||||
// strip it, and ensure that the result is the same.
|
||||
char exe_name[PATH_MAX];
|
||||
ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1);
|
||||
ASSERT_NE(len, -1);
|
||||
exe_name[len] = '\0';
|
||||
|
||||
// copy our binary to a temp file, and strip it
|
||||
char templ[] = "/tmp/file-id-unittest-XXXXXX";
|
||||
mktemp(templ);
|
||||
char cmdline[4096];
|
||||
sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ);
|
||||
ASSERT_EQ(system(cmdline), 0);
|
||||
sprintf(cmdline, "strip \"%s\"", templ);
|
||||
ASSERT_EQ(system(cmdline), 0);
|
||||
|
||||
uint8_t identifier1[sizeof(MDGUID)];
|
||||
uint8_t identifier2[sizeof(MDGUID)];
|
||||
FileID fileid1(exe_name);
|
||||
EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1));
|
||||
FileID fileid2(templ);
|
||||
EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2));
|
||||
char identifier_string1[37];
|
||||
char identifier_string2[37];
|
||||
FileID::ConvertIdentifierToString(identifier1, identifier_string1,
|
||||
37);
|
||||
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
|
||||
37);
|
||||
EXPECT_STREQ(identifier_string1, identifier_string2);
|
||||
unlink(templ);
|
||||
}
|
||||
|
||||
class FileIDTest : public testing::Test {
|
||||
public:
|
||||
void GetElfContents(ELF& elf) {
|
||||
string contents;
|
||||
ASSERT_TRUE(elf.GetContents(&contents));
|
||||
ASSERT_LT(0, contents.size());
|
||||
|
||||
elfdata_v.clear();
|
||||
elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
|
||||
elfdata = &elfdata_v[0];
|
||||
}
|
||||
|
||||
vector<uint8_t> elfdata_v;
|
||||
uint8_t* elfdata;
|
||||
};
|
||||
|
||||
TEST_F(FileIDTest, ElfClass) {
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
const char expected_identifier_string[] =
|
||||
"80808080-8080-0000-0000-008080808080";
|
||||
char identifier_string[sizeof(expected_identifier_string)];
|
||||
const size_t kTextSectionSize = 128;
|
||||
|
||||
ELF elf32(EM_386, ELFCLASS32, kLittleEndian);
|
||||
Section text32(kLittleEndian);
|
||||
for (size_t i = 0; i < kTextSectionSize; ++i) {
|
||||
text32.D8(i * 3);
|
||||
}
|
||||
elf32.AddSection(".text", text32, SHT_PROGBITS);
|
||||
elf32.Finish();
|
||||
GetElfContents(elf32);
|
||||
|
||||
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
|
||||
|
||||
FileID::ConvertIdentifierToString(identifier, identifier_string,
|
||||
sizeof(identifier_string));
|
||||
EXPECT_STREQ(expected_identifier_string, identifier_string);
|
||||
|
||||
memset(identifier, 0, sizeof(identifier));
|
||||
memset(identifier_string, 0, sizeof(identifier_string));
|
||||
|
||||
ELF elf64(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
Section text64(kLittleEndian);
|
||||
for (size_t i = 0; i < kTextSectionSize; ++i) {
|
||||
text64.D8(i * 3);
|
||||
}
|
||||
elf64.AddSection(".text", text64, SHT_PROGBITS);
|
||||
elf64.Finish();
|
||||
GetElfContents(elf64);
|
||||
|
||||
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
|
||||
|
||||
FileID::ConvertIdentifierToString(identifier, identifier_string,
|
||||
sizeof(identifier_string));
|
||||
EXPECT_STREQ(expected_identifier_string, identifier_string);
|
||||
}
|
||||
|
||||
TEST_F(FileIDTest, BuildID) {
|
||||
const uint8_t kExpectedIdentifier[sizeof(MDGUID)] =
|
||||
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
|
||||
char expected_identifier_string[] =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
FileID::ConvertIdentifierToString(kExpectedIdentifier,
|
||||
expected_identifier_string,
|
||||
sizeof(expected_identifier_string));
|
||||
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
char identifier_string[sizeof(expected_identifier_string)];
|
||||
|
||||
ELF elf32(EM_386, ELFCLASS32, kLittleEndian);
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf32.AddSection(".text", text, SHT_PROGBITS);
|
||||
BuildIDNote::AppendSection(elf32,
|
||||
kExpectedIdentifier,
|
||||
sizeof(kExpectedIdentifier));
|
||||
elf32.Finish();
|
||||
GetElfContents(elf32);
|
||||
|
||||
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
|
||||
|
||||
FileID::ConvertIdentifierToString(identifier, identifier_string,
|
||||
sizeof(identifier_string));
|
||||
EXPECT_STREQ(expected_identifier_string, identifier_string);
|
||||
|
||||
memset(identifier, 0, sizeof(identifier));
|
||||
memset(identifier_string, 0, sizeof(identifier_string));
|
||||
|
||||
ELF elf64(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
// Re-use empty text section from previous test
|
||||
elf64.AddSection(".text", text, SHT_PROGBITS);
|
||||
BuildIDNote::AppendSection(elf64,
|
||||
kExpectedIdentifier,
|
||||
sizeof(kExpectedIdentifier));
|
||||
elf64.Finish();
|
||||
GetElfContents(elf64);
|
||||
|
||||
EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(elfdata, identifier));
|
||||
|
||||
FileID::ConvertIdentifierToString(identifier, identifier_string,
|
||||
sizeof(identifier_string));
|
||||
EXPECT_STREQ(expected_identifier_string, identifier_string);
|
||||
}
|
199
thirdparty/breakpad/common/linux/google_crashdump_uploader.cc
vendored
Normal file
199
thirdparty/breakpad/common/linux/google_crashdump_uploader.cc
vendored
Normal file
@@ -0,0 +1,199 @@
|
||||
// Copyright (c) 2009, 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.
|
||||
|
||||
|
||||
#include "common/linux/google_crashdump_uploader.h"
|
||||
#include "common/linux/libcurl_wrapper.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product,
|
||||
const std::string& version,
|
||||
const std::string& guid,
|
||||
const std::string& ptime,
|
||||
const std::string& ctime,
|
||||
const std::string& email,
|
||||
const std::string& comments,
|
||||
const std::string& minidump_pathname,
|
||||
const std::string& crash_server,
|
||||
const std::string& proxy_host,
|
||||
const std::string& proxy_userpassword) {
|
||||
LibcurlWrapper* http_layer = new LibcurlWrapper();
|
||||
Init(product,
|
||||
version,
|
||||
guid,
|
||||
ptime,
|
||||
ctime,
|
||||
email,
|
||||
comments,
|
||||
minidump_pathname,
|
||||
crash_server,
|
||||
proxy_host,
|
||||
proxy_userpassword,
|
||||
http_layer);
|
||||
}
|
||||
|
||||
GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product,
|
||||
const std::string& version,
|
||||
const std::string& guid,
|
||||
const std::string& ptime,
|
||||
const std::string& ctime,
|
||||
const std::string& email,
|
||||
const std::string& comments,
|
||||
const std::string& minidump_pathname,
|
||||
const std::string& crash_server,
|
||||
const std::string& proxy_host,
|
||||
const std::string& proxy_userpassword,
|
||||
LibcurlWrapper* http_layer) {
|
||||
Init(product,
|
||||
version,
|
||||
guid,
|
||||
ptime,
|
||||
ctime,
|
||||
email,
|
||||
comments,
|
||||
minidump_pathname,
|
||||
crash_server,
|
||||
proxy_host,
|
||||
proxy_userpassword,
|
||||
http_layer);
|
||||
}
|
||||
|
||||
void GoogleCrashdumpUploader::Init(const std::string& product,
|
||||
const std::string& version,
|
||||
const std::string& guid,
|
||||
const std::string& ptime,
|
||||
const std::string& ctime,
|
||||
const std::string& email,
|
||||
const std::string& comments,
|
||||
const std::string& minidump_pathname,
|
||||
const std::string& crash_server,
|
||||
const std::string& proxy_host,
|
||||
const std::string& proxy_userpassword,
|
||||
LibcurlWrapper* http_layer) {
|
||||
product_ = product;
|
||||
version_ = version;
|
||||
guid_ = guid;
|
||||
ptime_ = ptime;
|
||||
ctime_ = ctime;
|
||||
email_ = email;
|
||||
comments_ = comments;
|
||||
http_layer_ = http_layer;
|
||||
|
||||
crash_server_ = crash_server;
|
||||
proxy_host_ = proxy_host;
|
||||
proxy_userpassword_ = proxy_userpassword;
|
||||
minidump_pathname_ = minidump_pathname;
|
||||
std::cout << "Uploader initializing";
|
||||
std::cout << "\tProduct: " << product_;
|
||||
std::cout << "\tVersion: " << version_;
|
||||
std::cout << "\tGUID: " << guid_;
|
||||
if (!ptime_.empty()) {
|
||||
std::cout << "\tProcess uptime: " << ptime_;
|
||||
}
|
||||
if (!ctime_.empty()) {
|
||||
std::cout << "\tCumulative Process uptime: " << ctime_;
|
||||
}
|
||||
if (!email_.empty()) {
|
||||
std::cout << "\tEmail: " << email_;
|
||||
}
|
||||
if (!comments_.empty()) {
|
||||
std::cout << "\tComments: " << comments_;
|
||||
}
|
||||
}
|
||||
|
||||
bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() {
|
||||
std::string error_text;
|
||||
if (product_.empty()) {
|
||||
error_text.append("\nProduct name must be specified.");
|
||||
}
|
||||
|
||||
if (version_.empty()) {
|
||||
error_text.append("\nProduct version must be specified.");
|
||||
}
|
||||
|
||||
if (guid_.empty()) {
|
||||
error_text.append("\nClient ID must be specified.");
|
||||
}
|
||||
|
||||
if (minidump_pathname_.empty()) {
|
||||
error_text.append("\nMinidump pathname must be specified.");
|
||||
}
|
||||
|
||||
if (!error_text.empty()) {
|
||||
std::cout << error_text;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool GoogleCrashdumpUploader::Upload() {
|
||||
bool ok = http_layer_->Init();
|
||||
if (!ok) {
|
||||
std::cout << "http layer init failed";
|
||||
return ok;
|
||||
}
|
||||
|
||||
if (!CheckRequiredParametersArePresent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
int err = stat(minidump_pathname_.c_str(), &st);
|
||||
if (err) {
|
||||
std::cout << minidump_pathname_ << " could not be found";
|
||||
return false;
|
||||
}
|
||||
|
||||
parameters_["prod"] = product_;
|
||||
parameters_["ver"] = version_;
|
||||
parameters_["guid"] = guid_;
|
||||
parameters_["ptime"] = ptime_;
|
||||
parameters_["ctime"] = ctime_;
|
||||
parameters_["email"] = email_;
|
||||
parameters_["comments_"] = comments_;
|
||||
if (!http_layer_->AddFile(minidump_pathname_,
|
||||
"upload_file_minidump")) {
|
||||
return false;
|
||||
}
|
||||
std::cout << "Sending request to " << crash_server_;
|
||||
return http_layer_->SendRequest(crash_server_,
|
||||
parameters_,
|
||||
NULL);
|
||||
}
|
||||
}
|
98
thirdparty/breakpad/common/linux/google_crashdump_uploader.h
vendored
Normal file
98
thirdparty/breakpad/common/linux/google_crashdump_uploader.h
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright (c) 2009, 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.
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class LibcurlWrapper;
|
||||
|
||||
class GoogleCrashdumpUploader {
|
||||
public:
|
||||
GoogleCrashdumpUploader(const std::string& product,
|
||||
const std::string& version,
|
||||
const std::string& guid,
|
||||
const std::string& ptime,
|
||||
const std::string& ctime,
|
||||
const std::string& email,
|
||||
const std::string& comments,
|
||||
const std::string& minidump_pathname,
|
||||
const std::string& crash_server,
|
||||
const std::string& proxy_host,
|
||||
const std::string& proxy_userpassword);
|
||||
|
||||
GoogleCrashdumpUploader(const std::string& product,
|
||||
const std::string& version,
|
||||
const std::string& guid,
|
||||
const std::string& ptime,
|
||||
const std::string& ctime,
|
||||
const std::string& email,
|
||||
const std::string& comments,
|
||||
const std::string& minidump_pathname,
|
||||
const std::string& crash_server,
|
||||
const std::string& proxy_host,
|
||||
const std::string& proxy_userpassword,
|
||||
LibcurlWrapper* http_layer);
|
||||
|
||||
void Init(const std::string& product,
|
||||
const std::string& version,
|
||||
const std::string& guid,
|
||||
const std::string& ptime,
|
||||
const std::string& ctime,
|
||||
const std::string& email,
|
||||
const std::string& comments,
|
||||
const std::string& minidump_pathname,
|
||||
const std::string& crash_server,
|
||||
const std::string& proxy_host,
|
||||
const std::string& proxy_userpassword,
|
||||
LibcurlWrapper* http_layer);
|
||||
bool Upload();
|
||||
|
||||
private:
|
||||
bool CheckRequiredParametersArePresent();
|
||||
|
||||
LibcurlWrapper* http_layer_;
|
||||
std::string product_;
|
||||
std::string version_;
|
||||
std::string guid_;
|
||||
std::string ptime_;
|
||||
std::string ctime_;
|
||||
std::string email_;
|
||||
std::string comments_;
|
||||
std::string minidump_pathname_;
|
||||
|
||||
std::string crash_server_;
|
||||
std::string proxy_host_;
|
||||
std::string proxy_userpassword_;
|
||||
|
||||
std::map<std::string, std::string> parameters_;
|
||||
};
|
||||
}
|
166
thirdparty/breakpad/common/linux/google_crashdump_uploader_test.cc
vendored
Normal file
166
thirdparty/breakpad/common/linux/google_crashdump_uploader_test.cc
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2009, 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.
|
||||
|
||||
// Unit test for crash dump uploader.
|
||||
|
||||
#include "common/linux/google_crashdump_uploader.h"
|
||||
#include "common/linux/libcurl_wrapper.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using ::testing::Return;
|
||||
using ::testing::_;
|
||||
|
||||
class MockLibcurlWrapper : public LibcurlWrapper {
|
||||
public:
|
||||
MOCK_METHOD0(Init, bool());
|
||||
MOCK_METHOD2(SetProxy, bool(const std::string& proxy_host,
|
||||
const std::string& proxy_userpwd));
|
||||
MOCK_METHOD2(AddFile, bool(const std::string& upload_file_path,
|
||||
const std::string& basename));
|
||||
MOCK_METHOD3(SendRequest,
|
||||
bool(const std::string& url,
|
||||
const std::map<std::string, std::string>& parameters,
|
||||
std::string* server_response));
|
||||
};
|
||||
|
||||
class GoogleCrashdumpUploaderTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) {
|
||||
MockLibcurlWrapper m;
|
||||
EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false));
|
||||
GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar",
|
||||
"1.0",
|
||||
"AAA-BBB",
|
||||
"",
|
||||
"",
|
||||
"test@test.com",
|
||||
"none",
|
||||
"/tmp/foo.dmp",
|
||||
"http://foo.com",
|
||||
"",
|
||||
"",
|
||||
&m);
|
||||
ASSERT_FALSE(uploader->Upload());
|
||||
}
|
||||
|
||||
TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) {
|
||||
// Create a temp file
|
||||
char tempfn[80] = "/tmp/googletest-upload-XXXXXX";
|
||||
int fd = mkstemp(tempfn);
|
||||
ASSERT_NE(fd, -1);
|
||||
close(fd);
|
||||
|
||||
MockLibcurlWrapper m;
|
||||
EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true));
|
||||
EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true));
|
||||
EXPECT_CALL(m,
|
||||
SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true));
|
||||
GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar",
|
||||
"1.0",
|
||||
"AAA-BBB",
|
||||
"",
|
||||
"",
|
||||
"test@test.com",
|
||||
"none",
|
||||
tempfn,
|
||||
"http://foo.com",
|
||||
"",
|
||||
"",
|
||||
&m);
|
||||
ASSERT_TRUE(uploader->Upload());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) {
|
||||
MockLibcurlWrapper m;
|
||||
EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true));
|
||||
EXPECT_CALL(m, SendRequest(_,_,_)).Times(0);
|
||||
GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar",
|
||||
"1.0",
|
||||
"AAA-BBB",
|
||||
"",
|
||||
"",
|
||||
"test@test.com",
|
||||
"none",
|
||||
"/tmp/foo.dmp",
|
||||
"http://foo.com",
|
||||
"",
|
||||
"",
|
||||
&m);
|
||||
ASSERT_FALSE(uploader->Upload());
|
||||
}
|
||||
|
||||
TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) {
|
||||
// Test with empty product name.
|
||||
GoogleCrashdumpUploader uploader("",
|
||||
"1.0",
|
||||
"AAA-BBB",
|
||||
"",
|
||||
"",
|
||||
"test@test.com",
|
||||
"none",
|
||||
"/tmp/foo.dmp",
|
||||
"http://foo.com",
|
||||
"",
|
||||
"");
|
||||
ASSERT_FALSE(uploader.Upload());
|
||||
|
||||
// Test with empty product version.
|
||||
GoogleCrashdumpUploader uploader1("product",
|
||||
"",
|
||||
"AAA-BBB",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"/tmp/foo.dmp",
|
||||
"",
|
||||
"",
|
||||
"");
|
||||
|
||||
ASSERT_FALSE(uploader1.Upload());
|
||||
|
||||
// Test with empty client GUID.
|
||||
GoogleCrashdumpUploader uploader2("product",
|
||||
"1.0",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"/tmp/foo.dmp",
|
||||
"",
|
||||
"",
|
||||
"");
|
||||
ASSERT_FALSE(uploader2.Upload());
|
||||
}
|
||||
}
|
96
thirdparty/breakpad/common/linux/guid_creator.cc
vendored
Normal file
96
thirdparty/breakpad/common/linux/guid_creator.cc
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2006, 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.
|
||||
|
||||
#include "common/linux/guid_creator.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//
|
||||
// GUIDGenerator
|
||||
//
|
||||
// This class is used to generate random GUID.
|
||||
// Currently use random number to generate a GUID since Linux has
|
||||
// no native GUID generator. This should be OK since we don't expect
|
||||
// crash to happen very offen.
|
||||
//
|
||||
class GUIDGenerator {
|
||||
public:
|
||||
GUIDGenerator() {
|
||||
srandom(time(NULL));
|
||||
}
|
||||
|
||||
static u_int32_t BytesToUInt32(const u_int8_t bytes[]) {
|
||||
return ((u_int32_t) bytes[0]
|
||||
| ((u_int32_t) bytes[1] << 8)
|
||||
| ((u_int32_t) bytes[2] << 16)
|
||||
| ((u_int32_t) bytes[3] << 24));
|
||||
}
|
||||
|
||||
static void UInt32ToBytes(u_int8_t bytes[], u_int32_t n) {
|
||||
bytes[0] = n & 0xff;
|
||||
bytes[1] = (n >> 8) & 0xff;
|
||||
bytes[2] = (n >> 16) & 0xff;
|
||||
bytes[3] = (n >> 24) & 0xff;
|
||||
}
|
||||
|
||||
bool CreateGUID(GUID *guid) const {
|
||||
guid->data1 = random();
|
||||
guid->data2 = (u_int16_t)(random());
|
||||
guid->data3 = (u_int16_t)(random());
|
||||
UInt32ToBytes(&guid->data4[0], random());
|
||||
UInt32ToBytes(&guid->data4[4], random());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Guid generator.
|
||||
const GUIDGenerator kGuidGenerator;
|
||||
|
||||
bool CreateGUID(GUID *guid) {
|
||||
return kGuidGenerator.CreateGUID(guid);
|
||||
}
|
||||
|
||||
// Parse guid to string.
|
||||
bool GUIDToString(const GUID *guid, char *buf, int buf_len) {
|
||||
// Should allow more space the the max length of GUID.
|
||||
assert(buf_len > kGUIDStringLength);
|
||||
int num = snprintf(buf, buf_len, kGUIDFormatString,
|
||||
guid->data1, guid->data2, guid->data3,
|
||||
GUIDGenerator::BytesToUInt32(&(guid->data4[0])),
|
||||
GUIDGenerator::BytesToUInt32(&(guid->data4[4])));
|
||||
if (num != kGUIDStringLength)
|
||||
return false;
|
||||
|
||||
buf[num] = '\0';
|
||||
return true;
|
||||
}
|
48
thirdparty/breakpad/common/linux/guid_creator.h
vendored
Normal file
48
thirdparty/breakpad/common/linux/guid_creator.h
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright (c) 2006, 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.
|
||||
|
||||
#ifndef COMMON_LINUX_GUID_CREATOR_H__
|
||||
#define COMMON_LINUX_GUID_CREATOR_H__
|
||||
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
typedef MDGUID GUID;
|
||||
|
||||
// Format string for parsing GUID.
|
||||
#define kGUIDFormatString "%08x-%04x-%04x-%08x-%08x"
|
||||
// Length of GUID string. Don't count the ending '\0'.
|
||||
#define kGUIDStringLength 36
|
||||
|
||||
// Create a guid.
|
||||
bool CreateGUID(GUID *guid);
|
||||
|
||||
// Get the string from guid.
|
||||
bool GUIDToString(const GUID *guid, char *buf, int buf_len);
|
||||
|
||||
#endif
|
197
thirdparty/breakpad/common/linux/http_upload.cc
vendored
Normal file
197
thirdparty/breakpad/common/linux/http_upload.cc
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
// Copyright (c) 2006, 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.
|
||||
|
||||
#include "common/linux/http_upload.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include "third_party/curl/curl.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Callback to get the response data from server.
|
||||
static size_t WriteCallback(void *ptr, size_t size,
|
||||
size_t nmemb, void *userp) {
|
||||
if (!userp)
|
||||
return 0;
|
||||
|
||||
std::string *response = reinterpret_cast<std::string *>(userp);
|
||||
size_t real_size = size * nmemb;
|
||||
response->append(reinterpret_cast<char *>(ptr), real_size);
|
||||
return real_size;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
static const char kUserAgent[] = "Breakpad/1.0 (Linux)";
|
||||
|
||||
// static
|
||||
bool HTTPUpload::SendRequest(const string &url,
|
||||
const map<string, string> ¶meters,
|
||||
const string &upload_file,
|
||||
const string &file_part_name,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
const string &ca_certificate_file,
|
||||
string *response_body,
|
||||
string *error_description) {
|
||||
if (!CheckParameters(parameters))
|
||||
return false;
|
||||
|
||||
void *curl_lib = dlopen("libcurl.so", RTLD_NOW);
|
||||
if (!curl_lib) {
|
||||
if (error_description != NULL)
|
||||
*error_description = dlerror();
|
||||
curl_lib = dlopen("libcurl.so.4", RTLD_NOW);
|
||||
}
|
||||
if (!curl_lib) {
|
||||
// Debian gives libcurl a different name when it is built against GnuTLS
|
||||
// instead of OpenSSL.
|
||||
curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW);
|
||||
}
|
||||
if (!curl_lib) {
|
||||
curl_lib = dlopen("libcurl.so.3", RTLD_NOW);
|
||||
}
|
||||
if (!curl_lib) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CURL* (*curl_easy_init)(void);
|
||||
*(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init");
|
||||
CURL *curl = (*curl_easy_init)();
|
||||
if (error_description != NULL)
|
||||
*error_description = "No Error";
|
||||
|
||||
if (!curl) {
|
||||
dlclose(curl_lib);
|
||||
return false;
|
||||
}
|
||||
|
||||
CURLcode err_code = CURLE_OK;
|
||||
CURLcode (*curl_easy_setopt)(CURL *, CURLoption, ...);
|
||||
*(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt");
|
||||
(*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str());
|
||||
(*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent);
|
||||
// Set proxy information if necessary.
|
||||
if (!proxy.empty())
|
||||
(*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str());
|
||||
if (!proxy_user_pwd.empty())
|
||||
(*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str());
|
||||
|
||||
if (!ca_certificate_file.empty())
|
||||
(*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str());
|
||||
|
||||
struct curl_httppost *formpost = NULL;
|
||||
struct curl_httppost *lastptr = NULL;
|
||||
// Add form data.
|
||||
CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...);
|
||||
*(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd");
|
||||
map<string, string>::const_iterator iter = parameters.begin();
|
||||
for (; iter != parameters.end(); ++iter)
|
||||
(*curl_formadd)(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
CURLFORM_COPYCONTENTS, iter->second.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
// Add form file.
|
||||
(*curl_formadd)(&formpost, &lastptr,
|
||||
CURLFORM_COPYNAME, file_part_name.c_str(),
|
||||
CURLFORM_FILE, upload_file.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
(*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost);
|
||||
|
||||
// Disable 100-continue header.
|
||||
struct curl_slist *headerlist = NULL;
|
||||
char buf[] = "Expect:";
|
||||
struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *);
|
||||
*(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append");
|
||||
headerlist = (*curl_slist_append)(headerlist, buf);
|
||||
(*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist);
|
||||
|
||||
if (response_body != NULL) {
|
||||
(*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
(*curl_easy_setopt)(curl, CURLOPT_WRITEDATA,
|
||||
reinterpret_cast<void *>(response_body));
|
||||
}
|
||||
|
||||
// Fail if 400+ is returned from the web server.
|
||||
(*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1);
|
||||
|
||||
CURLcode (*curl_easy_perform)(CURL *);
|
||||
*(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform");
|
||||
err_code = (*curl_easy_perform)(curl);
|
||||
const char* (*curl_easy_strerror)(CURLcode);
|
||||
*(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror");
|
||||
#ifndef NDEBUG
|
||||
if (err_code != CURLE_OK)
|
||||
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
|
||||
url.c_str(),
|
||||
(*curl_easy_strerror)(err_code));
|
||||
#endif
|
||||
if (error_description != NULL)
|
||||
*error_description = (*curl_easy_strerror)(err_code);
|
||||
|
||||
void (*curl_easy_cleanup)(CURL *);
|
||||
*(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup");
|
||||
(*curl_easy_cleanup)(curl);
|
||||
if (formpost != NULL) {
|
||||
void (*curl_formfree)(struct curl_httppost *);
|
||||
*(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree");
|
||||
(*curl_formfree)(formpost);
|
||||
}
|
||||
if (headerlist != NULL) {
|
||||
void (*curl_slist_free_all)(struct curl_slist *);
|
||||
*(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all");
|
||||
(*curl_slist_free_all)(headerlist);
|
||||
}
|
||||
dlclose(curl_lib);
|
||||
return err_code == CURLE_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
bool HTTPUpload::CheckParameters(const map<string, string> ¶meters) {
|
||||
for (map<string, string>::const_iterator pos = parameters.begin();
|
||||
pos != parameters.end(); ++pos) {
|
||||
const string &str = pos->first;
|
||||
if (str.size() == 0)
|
||||
return false; // disallow empty parameter names
|
||||
for (unsigned int i = 0; i < str.size(); ++i) {
|
||||
int c = str[i];
|
||||
if (c < 32 || c == '"' || c > 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
84
thirdparty/breakpad/common/linux/http_upload.h
vendored
Normal file
84
thirdparty/breakpad/common/linux/http_upload.h
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2006, 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.
|
||||
|
||||
// HTTPUpload provides a "nice" API to send a multipart HTTP(S) POST
|
||||
// request using libcurl. It currently supports requests that contain
|
||||
// a set of string parameters (key/value pairs), and a file to upload.
|
||||
|
||||
#ifndef COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
#define COMMON_LINUX_HTTP_UPLOAD_H__
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
|
||||
class HTTPUpload {
|
||||
public:
|
||||
// Sends the given set of parameters, along with the contents of
|
||||
// upload_file, as a multipart POST request to the given URL.
|
||||
// file_part_name contains the name of the file part of the request
|
||||
// (i.e. it corresponds to the name= attribute on an <input type="file">.
|
||||
// Parameter names must contain only printable ASCII characters,
|
||||
// and may not contain a quote (") character.
|
||||
// Only HTTP(S) URLs are currently supported. Returns true on success.
|
||||
// If the request is successful and response_body is non-NULL,
|
||||
// the response body will be returned in response_body.
|
||||
// If the send fails, a description of the error will be
|
||||
// returned in error_description.
|
||||
static bool SendRequest(const string &url,
|
||||
const map<string, string> ¶meters,
|
||||
const string &upload_file,
|
||||
const string &file_part_name,
|
||||
const string &proxy,
|
||||
const string &proxy_user_pwd,
|
||||
const string &ca_certificate_file,
|
||||
string *response_body,
|
||||
string *error_description);
|
||||
|
||||
private:
|
||||
// Checks that the given list of parameters has only printable
|
||||
// ASCII characters in the parameter name, and does not contain
|
||||
// any quote (") characters. Returns true if so.
|
||||
static bool CheckParameters(const map<string, string> ¶meters);
|
||||
|
||||
// No instances of this class should be created.
|
||||
// Disallow all constructors, destructors, and operator=.
|
||||
HTTPUpload();
|
||||
explicit HTTPUpload(const HTTPUpload &);
|
||||
void operator=(const HTTPUpload &);
|
||||
~HTTPUpload();
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_HTTP_UPLOAD_H__
|
223
thirdparty/breakpad/common/linux/libcurl_wrapper.cc
vendored
Normal file
223
thirdparty/breakpad/common/linux/libcurl_wrapper.cc
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright (c) 2009, 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.
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
#include "common/linux/libcurl_wrapper.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace google_breakpad {
|
||||
LibcurlWrapper::LibcurlWrapper()
|
||||
: init_ok_(false),
|
||||
formpost_(NULL),
|
||||
lastptr_(NULL),
|
||||
headerlist_(NULL) {
|
||||
curl_lib_ = dlopen("libcurl.so", RTLD_NOW);
|
||||
if (!curl_lib_) {
|
||||
curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW);
|
||||
}
|
||||
if (!curl_lib_) {
|
||||
curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW);
|
||||
}
|
||||
if (!curl_lib_) {
|
||||
std::cout << "Could not find libcurl via dlopen";
|
||||
return;
|
||||
}
|
||||
std::cout << "LibcurlWrapper init succeeded";
|
||||
init_ok_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
bool LibcurlWrapper::SetProxy(const std::string& proxy_host,
|
||||
const std::string& proxy_userpwd) {
|
||||
if (!init_ok_) {
|
||||
return false;
|
||||
}
|
||||
// Set proxy information if necessary.
|
||||
if (!proxy_host.empty()) {
|
||||
(*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str());
|
||||
} else {
|
||||
std::cout << "SetProxy called with empty proxy host.";
|
||||
return false;
|
||||
}
|
||||
if (!proxy_userpwd.empty()) {
|
||||
(*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str());
|
||||
} else {
|
||||
std::cout << "SetProxy called with empty proxy username/password.";
|
||||
return false;
|
||||
}
|
||||
std::cout << "Set proxy host to " << proxy_host;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LibcurlWrapper::AddFile(const std::string& upload_file_path,
|
||||
const std::string& basename) {
|
||||
if (!init_ok_) {
|
||||
return false;
|
||||
}
|
||||
std::cout << "Adding " << upload_file_path << " to form upload.";
|
||||
// Add form file.
|
||||
(*formadd_)(&formpost_, &lastptr_,
|
||||
CURLFORM_COPYNAME, basename.c_str(),
|
||||
CURLFORM_FILE, upload_file_path.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Callback to get the response data from server.
|
||||
static size_t WriteCallback(void *ptr, size_t size,
|
||||
size_t nmemb, void *userp) {
|
||||
if (!userp)
|
||||
return 0;
|
||||
|
||||
std::string *response = reinterpret_cast<std::string *>(userp);
|
||||
size_t real_size = size * nmemb;
|
||||
response->append(reinterpret_cast<char *>(ptr), real_size);
|
||||
return real_size;
|
||||
}
|
||||
|
||||
bool LibcurlWrapper::SendRequest(const std::string& url,
|
||||
const std::map<std::string, std::string>& parameters,
|
||||
std::string* server_response) {
|
||||
(*easy_setopt_)(curl_, CURLOPT_URL, url.c_str());
|
||||
std::map<std::string, std::string>::const_iterator iter = parameters.begin();
|
||||
for (; iter != parameters.end(); ++iter)
|
||||
(*formadd_)(&formpost_, &lastptr_,
|
||||
CURLFORM_COPYNAME, iter->first.c_str(),
|
||||
CURLFORM_COPYCONTENTS, iter->second.c_str(),
|
||||
CURLFORM_END);
|
||||
|
||||
(*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_);
|
||||
if (server_response != NULL) {
|
||||
(*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
(*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
|
||||
reinterpret_cast<void *>(server_response));
|
||||
}
|
||||
|
||||
CURLcode err_code = CURLE_OK;
|
||||
err_code = (*easy_perform_)(curl_);
|
||||
easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
|
||||
(dlsym(curl_lib_, "curl_easy_strerror"));
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (err_code != CURLE_OK)
|
||||
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
|
||||
url.c_str(),
|
||||
(*easy_strerror_)(err_code));
|
||||
#endif
|
||||
if (headerlist_ != NULL) {
|
||||
(*slist_free_all_)(headerlist_);
|
||||
}
|
||||
|
||||
(*easy_cleanup_)(curl_);
|
||||
if (formpost_ != NULL) {
|
||||
(*formfree_)(formpost_);
|
||||
}
|
||||
|
||||
return err_code == CURLE_OK;
|
||||
}
|
||||
|
||||
bool LibcurlWrapper::Init() {
|
||||
if (!init_ok_) {
|
||||
std::cout << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SetFunctionPointers()) {
|
||||
std::cout << "Could not find function pointers";
|
||||
init_ok_ = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
curl_ = (*easy_init_)();
|
||||
|
||||
last_curl_error_ = "No Error";
|
||||
|
||||
if (!curl_) {
|
||||
dlclose(curl_lib_);
|
||||
std::cout << "Curl initialization failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disable 100-continue header.
|
||||
char buf[] = "Expect:";
|
||||
|
||||
headerlist_ = (*slist_append_)(headerlist_, buf);
|
||||
(*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_);
|
||||
return true;
|
||||
}
|
||||
|
||||
#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name, type) \
|
||||
var = reinterpret_cast<type>(dlsym(curl_lib_, function_name)); \
|
||||
if (!var) { \
|
||||
std::cout << "Could not find libcurl function " << function_name; \
|
||||
init_ok_ = false; \
|
||||
return false; \
|
||||
}
|
||||
|
||||
bool LibcurlWrapper::SetFunctionPointers() {
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(easy_init_,
|
||||
"curl_easy_init",
|
||||
CURL*(*)());
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_,
|
||||
"curl_easy_setopt",
|
||||
CURLcode(*)(CURL*, CURLoption, ...));
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(formadd_, "curl_formadd",
|
||||
CURLFORMcode(*)(curl_httppost**, curl_httppost**, ...));
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(slist_append_, "curl_slist_append",
|
||||
curl_slist*(*)(curl_slist*, const char*));
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(easy_perform_,
|
||||
"curl_easy_perform",
|
||||
CURLcode(*)(CURL*));
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_,
|
||||
"curl_easy_cleanup",
|
||||
void(*)(CURL*));
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_,
|
||||
"curl_slist_free_all",
|
||||
void(*)(curl_slist*));
|
||||
|
||||
SET_AND_CHECK_FUNCTION_POINTER(formfree_,
|
||||
"curl_formfree",
|
||||
void(*)(curl_httppost*));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
83
thirdparty/breakpad/common/linux/libcurl_wrapper.h
vendored
Normal file
83
thirdparty/breakpad/common/linux/libcurl_wrapper.h
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2009, 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.
|
||||
|
||||
// A wrapper for libcurl to do HTTP Uploads, to support easy mocking
|
||||
// and unit testing of the HTTPUpload class.
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "third_party/curl/curl.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
class LibcurlWrapper {
|
||||
public:
|
||||
LibcurlWrapper();
|
||||
virtual bool Init();
|
||||
virtual bool SetProxy(const std::string& proxy_host,
|
||||
const std::string& proxy_userpwd);
|
||||
virtual bool AddFile(const std::string& upload_file_path,
|
||||
const std::string& basename);
|
||||
virtual bool SendRequest(const std::string& url,
|
||||
const std::map<std::string, std::string>& parameters,
|
||||
std::string* server_response);
|
||||
private:
|
||||
// This function initializes class state corresponding to function
|
||||
// pointers into the CURL library.
|
||||
bool SetFunctionPointers();
|
||||
|
||||
bool init_ok_; // Whether init succeeded
|
||||
void* curl_lib_; // Pointer to result of dlopen() on
|
||||
// curl library
|
||||
std::string last_curl_error_; // The text of the last error when
|
||||
// dealing
|
||||
// with CURL.
|
||||
|
||||
CURL *curl_; // Pointer for handle for CURL calls.
|
||||
|
||||
CURL* (*easy_init_)(void);
|
||||
|
||||
// Stateful pointers for calling into curl_formadd()
|
||||
struct curl_httppost *formpost_;
|
||||
struct curl_httppost *lastptr_;
|
||||
struct curl_slist *headerlist_;
|
||||
|
||||
// Function pointers into CURL library
|
||||
CURLcode (*easy_setopt_)(CURL *, CURLoption, ...);
|
||||
CURLFORMcode (*formadd_)(struct curl_httppost **,
|
||||
struct curl_httppost **, ...);
|
||||
struct curl_slist* (*slist_append_)(struct curl_slist *, const char *);
|
||||
void (*slist_free_all_)(struct curl_slist *);
|
||||
CURLcode (*easy_perform_)(CURL *);
|
||||
const char* (*easy_strerror_)(CURLcode);
|
||||
void (*easy_cleanup_)(CURL *);
|
||||
void (*formfree_)(struct curl_httppost *);
|
||||
|
||||
};
|
||||
}
|
178
thirdparty/breakpad/common/linux/linux_libc_support.h
vendored
Normal file
178
thirdparty/breakpad/common/linux/linux_libc_support.h
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright (c) 2009, 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.
|
||||
|
||||
// This header provides replacements for libc functions that we need. We if
|
||||
// call the libc functions directly we risk crashing in the dynamic linker as
|
||||
// it tries to resolve uncached PLT entries.
|
||||
|
||||
#ifndef CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_
|
||||
#define CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
static inline size_t
|
||||
my_strlen(const char* s) {
|
||||
size_t len = 0;
|
||||
while (*s++) len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline int
|
||||
my_strcmp(const char* a, const char* b) {
|
||||
for (;;) {
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
else if (*a > *b)
|
||||
return 1;
|
||||
else if (*a == 0)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
my_strncmp(const char* a, const char* b, size_t len) {
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
if (*a < *b)
|
||||
return -1;
|
||||
else if (*a > *b)
|
||||
return 1;
|
||||
else if (*a == 0)
|
||||
return 0;
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parse a non-negative integer.
|
||||
// result: (output) the resulting non-negative integer
|
||||
// s: a NUL terminated string
|
||||
// Return true iff successful.
|
||||
static inline bool
|
||||
my_strtoui(int* result, const char* s) {
|
||||
if (*s == 0)
|
||||
return false;
|
||||
int r = 0;
|
||||
for (;; s++) {
|
||||
if (*s == 0)
|
||||
break;
|
||||
const int old_r = r;
|
||||
r *= 10;
|
||||
if (*s < '0' || *s > '9')
|
||||
return false;
|
||||
r += *s - '0';
|
||||
if (r < old_r)
|
||||
return false;
|
||||
}
|
||||
|
||||
*result = r;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Return the length of the given, non-negative integer when expressed in base
|
||||
// 10.
|
||||
static inline unsigned
|
||||
my_int_len(int i) {
|
||||
if (!i)
|
||||
return 1;
|
||||
|
||||
int len = 0;
|
||||
while (i) {
|
||||
len++;
|
||||
i /= 10;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// Convert a non-negative integer to a string
|
||||
// output: (output) the resulting string is written here. This buffer must be
|
||||
// large enough to hold the resulting string. Call |my_int_len| to get the
|
||||
// required length.
|
||||
// i: the non-negative integer to serialise.
|
||||
// i_len: the length of the integer in base 10 (see |my_int_len|).
|
||||
static inline void
|
||||
my_itos(char* output, int i, unsigned i_len) {
|
||||
for (unsigned index = i_len; index; --index, i /= 10)
|
||||
output[index - 1] = '0' + (i % 10);
|
||||
}
|
||||
|
||||
static inline const char*
|
||||
my_strchr(const char* haystack, char needle) {
|
||||
while (*haystack && *haystack != needle)
|
||||
haystack++;
|
||||
if (*haystack == needle)
|
||||
return haystack;
|
||||
return (const char*) 0;
|
||||
}
|
||||
|
||||
// Read a hex value
|
||||
// result: (output) the resulting value
|
||||
// s: a string
|
||||
// Returns a pointer to the first invalid charactor.
|
||||
static inline const char*
|
||||
my_read_hex_ptr(uintptr_t* result, const char* s) {
|
||||
uintptr_t r = 0;
|
||||
|
||||
for (;; ++s) {
|
||||
if (*s >= '0' && *s <= '9') {
|
||||
r <<= 4;
|
||||
r += *s - '0';
|
||||
} else if (*s >= 'a' && *s <= 'f') {
|
||||
r <<= 4;
|
||||
r += (*s - 'a') + 10;
|
||||
} else if (*s >= 'A' && *s <= 'F') {
|
||||
r <<= 4;
|
||||
r += (*s - 'A') + 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*result = r;
|
||||
return s;
|
||||
}
|
||||
|
||||
static inline void
|
||||
my_memset(void* ip, char c, size_t len) {
|
||||
char* p = (char *) ip;
|
||||
while (len--)
|
||||
*p++ = c;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_
|
153
thirdparty/breakpad/common/linux/linux_libc_support_unittest.cc
vendored
Normal file
153
thirdparty/breakpad/common/linux/linux_libc_support_unittest.cc
vendored
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2009, 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.
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
namespace {
|
||||
typedef testing::Test LinuxLibcSupportTest;
|
||||
}
|
||||
|
||||
TEST(LinuxLibcSupportTest, strlen) {
|
||||
static const char* test_data[] = { "", "a", "aa", "aaa", "aabc", NULL };
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (!test_data[i])
|
||||
break;
|
||||
ASSERT_EQ(strlen(test_data[i]), my_strlen(test_data[i]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LinuxLibcSupportTest, strcmp) {
|
||||
static const char* test_data[] = {
|
||||
"", "",
|
||||
"a", "",
|
||||
"", "a",
|
||||
"a", "b",
|
||||
"a", "a",
|
||||
"ab", "aa",
|
||||
"abc", "ab",
|
||||
"abc", "abc",
|
||||
NULL,
|
||||
};
|
||||
|
||||
for (unsigned i = 0; ; ++i) {
|
||||
if (!test_data[i*2])
|
||||
break;
|
||||
ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]),
|
||||
strcmp(test_data[i*2], test_data[i*2 + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(LinuxLibcSupportTest, strtoui) {
|
||||
int result;
|
||||
|
||||
ASSERT_FALSE(my_strtoui(&result, ""));
|
||||
ASSERT_FALSE(my_strtoui(&result, "-1"));
|
||||
ASSERT_FALSE(my_strtoui(&result, "-"));
|
||||
ASSERT_FALSE(my_strtoui(&result, "a"));
|
||||
ASSERT_FALSE(my_strtoui(&result, "23472893472938472987987398472398"));
|
||||
|
||||
ASSERT_TRUE(my_strtoui(&result, "0"));
|
||||
ASSERT_EQ(result, 0);
|
||||
ASSERT_TRUE(my_strtoui(&result, "1"));
|
||||
ASSERT_EQ(result, 1);
|
||||
ASSERT_TRUE(my_strtoui(&result, "12"));
|
||||
ASSERT_EQ(result, 12);
|
||||
ASSERT_TRUE(my_strtoui(&result, "123"));
|
||||
ASSERT_EQ(result, 123);
|
||||
ASSERT_TRUE(my_strtoui(&result, "0123"));
|
||||
ASSERT_EQ(result, 123);
|
||||
}
|
||||
|
||||
TEST(LinuxLibcSupportTest, int_len) {
|
||||
ASSERT_EQ(my_int_len(0), 1);
|
||||
ASSERT_EQ(my_int_len(2), 1);
|
||||
ASSERT_EQ(my_int_len(5), 1);
|
||||
ASSERT_EQ(my_int_len(9), 1);
|
||||
ASSERT_EQ(my_int_len(10), 2);
|
||||
ASSERT_EQ(my_int_len(99), 2);
|
||||
ASSERT_EQ(my_int_len(100), 3);
|
||||
ASSERT_EQ(my_int_len(101), 3);
|
||||
ASSERT_EQ(my_int_len(1000), 4);
|
||||
}
|
||||
|
||||
TEST(LinuxLibcSupportTest, itos) {
|
||||
char buf[10];
|
||||
|
||||
my_itos(buf, 0, 1);
|
||||
ASSERT_EQ(0, memcmp(buf, "0", 1));
|
||||
|
||||
my_itos(buf, 1, 1);
|
||||
ASSERT_EQ(0, memcmp(buf, "1", 1));
|
||||
|
||||
my_itos(buf, 10, 2);
|
||||
ASSERT_EQ(0, memcmp(buf, "10", 2));
|
||||
|
||||
my_itos(buf, 63, 2);
|
||||
ASSERT_EQ(0, memcmp(buf, "63", 2));
|
||||
|
||||
my_itos(buf, 101, 3);
|
||||
ASSERT_EQ(0, memcmp(buf, "101", 2));
|
||||
}
|
||||
|
||||
TEST(LinuxLibcSupportTest, strchr) {
|
||||
ASSERT_EQ(NULL, my_strchr("abc", 'd'));
|
||||
ASSERT_EQ(NULL, my_strchr("", 'd'));
|
||||
ASSERT_EQ(NULL, my_strchr("efghi", 'd'));
|
||||
|
||||
ASSERT_TRUE(my_strchr("a", 'a'));
|
||||
ASSERT_TRUE(my_strchr("abc", 'a'));
|
||||
ASSERT_TRUE(my_strchr("bcda", 'a'));
|
||||
ASSERT_TRUE(my_strchr("sdfasdf", 'a'));
|
||||
}
|
||||
|
||||
TEST(LinuxLibcSupportTest, read_hex_ptr) {
|
||||
uintptr_t result;
|
||||
const char* last;
|
||||
|
||||
last = my_read_hex_ptr(&result, "");
|
||||
ASSERT_EQ(result, 0);
|
||||
ASSERT_EQ(*last, 0);
|
||||
|
||||
last = my_read_hex_ptr(&result, "0");
|
||||
ASSERT_EQ(result, 0);
|
||||
ASSERT_EQ(*last, 0);
|
||||
|
||||
last = my_read_hex_ptr(&result, "0123");
|
||||
ASSERT_EQ(result, 0x123);
|
||||
ASSERT_EQ(*last, 0);
|
||||
|
||||
last = my_read_hex_ptr(&result, "0123a");
|
||||
ASSERT_EQ(result, 0x123a);
|
||||
ASSERT_EQ(*last, 0);
|
||||
|
||||
last = my_read_hex_ptr(&result, "0123a-");
|
||||
ASSERT_EQ(result, 0x123a);
|
||||
ASSERT_EQ(*last, '-');
|
||||
}
|
204
thirdparty/breakpad/common/linux/synth_elf.cc
vendored
Normal file
204
thirdparty/breakpad/common/linux/synth_elf.cc
vendored
Normal file
@@ -0,0 +1,204 @@
|
||||
#include "common/linux/synth_elf.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <elf.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace synth_elf {
|
||||
|
||||
#ifndef NT_GNU_BUILD_ID
|
||||
#define NT_GNU_BUILD_ID 3
|
||||
#endif
|
||||
|
||||
ELF::ELF(uint16_t machine,
|
||||
uint8_t file_class,
|
||||
Endianness endianness)
|
||||
: Section(endianness),
|
||||
addr_size_(file_class == ELFCLASS64 ? 8 : 4),
|
||||
program_count_(0),
|
||||
section_count_(0),
|
||||
section_header_table_(endianness),
|
||||
section_header_strings_(endianness) {
|
||||
// Could add support for more machine types here if needed.
|
||||
assert(machine == EM_386 ||
|
||||
machine == EM_X86_64 ||
|
||||
machine == EM_ARM);
|
||||
assert(file_class == ELFCLASS32 || file_class == ELFCLASS64);
|
||||
|
||||
start() = 0;
|
||||
// Add ELF header
|
||||
// e_ident
|
||||
// EI_MAG0...EI_MAG3
|
||||
D8(ELFMAG0);
|
||||
D8(ELFMAG1);
|
||||
D8(ELFMAG2);
|
||||
D8(ELFMAG3);
|
||||
// EI_CLASS
|
||||
D8(file_class);
|
||||
// EI_DATA
|
||||
D8(endianness == kLittleEndian ? ELFDATA2LSB : ELFDATA2MSB);
|
||||
// EI_VERSION
|
||||
D8(EV_CURRENT);
|
||||
// EI_OSABI
|
||||
D8(ELFOSABI_SYSV);
|
||||
// EI_ABIVERSION
|
||||
D8(0);
|
||||
// EI_PAD
|
||||
Append(7, 0);
|
||||
assert(Size() == EI_NIDENT);
|
||||
|
||||
// e_type
|
||||
D16(ET_EXEC); //TODO: allow passing ET_DYN?
|
||||
// e_machine
|
||||
D16(machine);
|
||||
// e_version
|
||||
D32(EV_CURRENT);
|
||||
// e_entry
|
||||
Append(endianness, addr_size_, 0);
|
||||
// e_phoff
|
||||
Append(endianness, addr_size_, program_header_label_);
|
||||
// e_shoff
|
||||
Append(endianness, addr_size_, section_header_label_);
|
||||
// e_flags
|
||||
D32(0);
|
||||
// e_ehsize
|
||||
D16(addr_size_ == 8 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr));
|
||||
// e_phentsize
|
||||
D16(addr_size_ == 8 ? sizeof(Elf64_Phdr) : sizeof(Elf32_Phdr));
|
||||
// e_phnum
|
||||
D16(program_count_label_);
|
||||
// e_shentsize
|
||||
D16(addr_size_ == 8 ? sizeof(Elf64_Shdr) : sizeof(Elf32_Shdr));
|
||||
// e_shnum
|
||||
D16(section_count_label_);
|
||||
// e_shstrndx
|
||||
D16(section_header_string_index_);
|
||||
|
||||
// Add an empty section for SHN_UNDEF.
|
||||
Section shn_undef;
|
||||
AddSection("", shn_undef, SHT_NULL);
|
||||
}
|
||||
|
||||
int ELF::AddSection(const string& name, const Section& section,
|
||||
uint32_t type, uint32_t flags, uint64_t addr,
|
||||
uint32_t link, uint64_t entsize, uint64_t offset) {
|
||||
Label offset_label;
|
||||
Label string_label(section_header_strings_.Add(name));
|
||||
size_t size = section.Size();
|
||||
|
||||
int index = section_count_;
|
||||
++section_count_;
|
||||
|
||||
section_header_table_
|
||||
// sh_name
|
||||
.D32(string_label)
|
||||
// sh_type
|
||||
.D32(type)
|
||||
// sh_flags
|
||||
.Append(endianness(), addr_size_, flags)
|
||||
// sh_addr
|
||||
.Append(endianness(), addr_size_, addr)
|
||||
// sh_offset
|
||||
.Append(endianness(), addr_size_, offset_label)
|
||||
// sh_size
|
||||
.Append(endianness(), addr_size_, size)
|
||||
// sh_link
|
||||
.D32(link)
|
||||
// sh_info
|
||||
.D32(0)
|
||||
// sh_addralign
|
||||
.Append(endianness(), addr_size_, 0)
|
||||
// sh_entsize
|
||||
.Append(endianness(), addr_size_, entsize);
|
||||
|
||||
// NULL and NOBITS sections have no content, so they
|
||||
// don't need to be written to the file.
|
||||
if (type == SHT_NULL) {
|
||||
offset_label = 0;
|
||||
} else if (type == SHT_NOBITS) {
|
||||
offset_label = offset;
|
||||
} else {
|
||||
Mark(&offset_label);
|
||||
Append(section);
|
||||
Align(4);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
void ELF::Finish() {
|
||||
// Add the section header string table at the end.
|
||||
section_header_string_index_ = section_count_;
|
||||
//printf(".shstrtab size: %ld\n", section_header_strings_.Size());
|
||||
AddSection(".shstrtab", section_header_strings_, SHT_STRTAB);
|
||||
//printf("section_count_: %ld, sections_.size(): %ld\n",
|
||||
// section_count_, sections_.size());
|
||||
section_count_label_ = section_count_;
|
||||
program_count_label_ = program_count_;
|
||||
// TODO: allow adding entries to program header table
|
||||
program_header_label_ = 0;
|
||||
|
||||
// Section header table starts here.
|
||||
Mark(§ion_header_label_);
|
||||
Append(section_header_table_);
|
||||
}
|
||||
|
||||
SymbolTable::SymbolTable(Endianness endianness,
|
||||
size_t addr_size,
|
||||
StringTable& table) : Section(endianness),
|
||||
addr_size_(addr_size),
|
||||
table_(table) {
|
||||
assert(addr_size_ == 4 || addr_size_ == 8);
|
||||
}
|
||||
|
||||
void SymbolTable::AddSymbol(const string& name, uint32_t value,
|
||||
uint32_t size, unsigned info, uint16_t shndx) {
|
||||
assert(addr_size_ == 4);
|
||||
D32(table_.Add(name));
|
||||
D32(value);
|
||||
D32(size);
|
||||
D8(info);
|
||||
D8(0); // other
|
||||
D16(shndx);
|
||||
}
|
||||
|
||||
void SymbolTable::AddSymbol(const string& name, uint64_t value,
|
||||
uint64_t size, unsigned info, uint16_t shndx) {
|
||||
assert(addr_size_ == 8);
|
||||
D32(table_.Add(name));
|
||||
D8(info);
|
||||
D8(0); // other
|
||||
D16(shndx);
|
||||
D64(value);
|
||||
D64(size);
|
||||
}
|
||||
|
||||
BuildIDNote::BuildIDNote(const uint8_t* id_bytes,
|
||||
size_t id_size,
|
||||
Endianness endianness) : Section(endianness) {
|
||||
const char kNoteName[] = "GNU";
|
||||
// Elf32_Nhdr and Elf64_Nhdr are exactly the same.
|
||||
Elf32_Nhdr note_header;
|
||||
memset(¬e_header, 0, sizeof(note_header));
|
||||
note_header.n_namesz = sizeof(kNoteName);
|
||||
note_header.n_descsz = id_size;
|
||||
note_header.n_type = NT_GNU_BUILD_ID;
|
||||
|
||||
Append(reinterpret_cast<const uint8_t*>(¬e_header),
|
||||
sizeof(note_header));
|
||||
AppendCString(kNoteName);
|
||||
Append(id_bytes, id_size);
|
||||
}
|
||||
|
||||
// static
|
||||
void BuildIDNote::AppendSection(ELF& elf,
|
||||
const uint8_t* id_bytes,
|
||||
size_t id_size) {
|
||||
const char kBuildIDSectionName[] = ".note.gnu.build-id";
|
||||
BuildIDNote note(id_bytes, id_size, elf.endianness());
|
||||
elf.AddSection(kBuildIDSectionName, note, SHT_NOTE);
|
||||
}
|
||||
|
||||
} // namespace synth_elf
|
||||
} // namespace google_breakpad
|
164
thirdparty/breakpad/common/linux/synth_elf.h
vendored
Normal file
164
thirdparty/breakpad/common/linux/synth_elf.h
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
// -*- mode: C++ -*-
|
||||
|
||||
// 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
// synth_elf.h: Interface to synth_elf::ELF: fake ELF generator.
|
||||
|
||||
#ifndef COMMON_LINUX_SYNTH_ELF_H_
|
||||
#define COMMON_LINUX_SYNTH_ELF_H_
|
||||
|
||||
#include "common/test_assembler.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace synth_elf {
|
||||
|
||||
using std::list;
|
||||
using std::map;
|
||||
using std::pair;
|
||||
using std::string;
|
||||
using test_assembler::Endianness;
|
||||
using test_assembler::kLittleEndian;
|
||||
using test_assembler::kUnsetEndian;
|
||||
using test_assembler::Label;
|
||||
using test_assembler::Section;
|
||||
|
||||
// String tables are common in ELF headers, so subclass Section
|
||||
// to make them easy to generate.
|
||||
class StringTable : public Section {
|
||||
public:
|
||||
StringTable(Endianness endianness = kUnsetEndian)
|
||||
: Section(endianness) {
|
||||
start() = 0;
|
||||
empty_string = Add("");
|
||||
}
|
||||
|
||||
// Add the string s to the string table, and return
|
||||
// a label containing the offset into the string table
|
||||
// at which it was added.
|
||||
Label Add(const string& s) {
|
||||
if (strings_.find(s) != strings_.end())
|
||||
return strings_[s];
|
||||
|
||||
Label string_label(Here());
|
||||
AppendCString(s);
|
||||
strings_[s] = string_label;
|
||||
return string_label;
|
||||
}
|
||||
|
||||
// All StringTables contain an empty string as their first
|
||||
// entry.
|
||||
Label empty_string;
|
||||
|
||||
// Avoid inserting duplicate strings.
|
||||
map<string,Label> strings_;
|
||||
};
|
||||
|
||||
// A Section representing an entire ELF file.
|
||||
class ELF : public Section {
|
||||
public:
|
||||
ELF(uint16_t machine, // EM_386, etc
|
||||
uint8_t file_class, // ELFCLASS{32,64}
|
||||
Endianness endianness = kLittleEndian);
|
||||
|
||||
// Add the Section section to the section header table and append it
|
||||
// to the file. Returns the index of the section in the section
|
||||
// header table.
|
||||
int AddSection(const string& name, const Section& section,
|
||||
uint32_t type, uint32_t flags = 0, uint64_t addr = 0,
|
||||
uint32_t link = 0, uint64_t entsize = 0, uint64_t offset = 0);
|
||||
|
||||
// Write out all data. GetContents may be used after this.
|
||||
void Finish();
|
||||
|
||||
private:
|
||||
// Size of an address, in bytes.
|
||||
const size_t addr_size_;
|
||||
|
||||
// Offset to the program header table.
|
||||
Label program_header_label_;
|
||||
// Number of entries in the program header table.
|
||||
int program_count_;
|
||||
Label program_count_label_;
|
||||
|
||||
// Offset to the section header table.
|
||||
Label section_header_label_;
|
||||
// Number of entries in the section header table.
|
||||
int section_count_;
|
||||
Label section_count_label_;
|
||||
// The section header table itself.
|
||||
Section section_header_table_;
|
||||
|
||||
// Index of the section header string table in the section
|
||||
// header table.
|
||||
Label section_header_string_index_;
|
||||
// Section containing the names of section header table entries.
|
||||
StringTable section_header_strings_;
|
||||
};
|
||||
|
||||
// A class to build .symtab or .dynsym sections.
|
||||
class SymbolTable : public Section {
|
||||
public:
|
||||
// table is the StringTable that contains symbol names. The caller
|
||||
// must ensure that it remains alive for the life of the
|
||||
// SymbolTable.
|
||||
SymbolTable(Endianness endianness, size_t addr_size, StringTable& table);
|
||||
|
||||
// Add an Elf32_Sym.
|
||||
void AddSymbol(const string& name, uint32_t value,
|
||||
uint32_t size, unsigned info, uint16_t shndx);
|
||||
// Add an Elf64_Sym.
|
||||
void AddSymbol(const string& name, uint64_t value,
|
||||
uint64_t size, unsigned info, uint16_t shndx);
|
||||
|
||||
private:
|
||||
size_t addr_size_;
|
||||
StringTable& table_;
|
||||
};
|
||||
|
||||
// A class to build GNU Build ID note sections
|
||||
class BuildIDNote : public Section {
|
||||
public:
|
||||
BuildIDNote(const uint8_t* id_bytes, size_t id_size, Endianness endianness);
|
||||
|
||||
// Append a new Build ID note section to |elf|.
|
||||
static void AppendSection(ELF& elf, const uint8_t* id_bytes, size_t id_size);
|
||||
};
|
||||
|
||||
} // namespace synth_elf
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_SYNTH_ELF_H_
|
265
thirdparty/breakpad/common/linux/synth_elf_unittest.cc
vendored
Normal file
265
thirdparty/breakpad/common/linux/synth_elf_unittest.cc
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
// 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.
|
||||
|
||||
// Original author: Ted Mielczarek <ted.mielczarek@gmail.com>
|
||||
|
||||
// synth_elf_unittest.cc:
|
||||
// Unittests for google_breakpad::synth_elf::ELF
|
||||
|
||||
#include <elf.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/synth_elf.h"
|
||||
|
||||
using google_breakpad::synth_elf::ELF;
|
||||
using google_breakpad::synth_elf::StringTable;
|
||||
using google_breakpad::synth_elf::SymbolTable;
|
||||
using google_breakpad::test_assembler::Endianness;
|
||||
using google_breakpad::test_assembler::kBigEndian;
|
||||
using google_breakpad::test_assembler::kLittleEndian;
|
||||
using google_breakpad::test_assembler::Label;
|
||||
using std::string;
|
||||
using ::testing::Test;
|
||||
|
||||
class StringTableTest : public Test {
|
||||
public:
|
||||
StringTableTest() : table(kLittleEndian) {}
|
||||
|
||||
StringTable table;
|
||||
};
|
||||
|
||||
TEST_F(StringTableTest, Empty) {
|
||||
EXPECT_EQ(1, table.Size());
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
const char* kExpectedContents = "\0";
|
||||
EXPECT_EQ(0, memcmp(kExpectedContents,
|
||||
contents.c_str(),
|
||||
contents.size()));
|
||||
ASSERT_TRUE(table.empty_string.IsKnownConstant());
|
||||
EXPECT_EQ(0, table.empty_string.Value());
|
||||
}
|
||||
|
||||
TEST_F(StringTableTest, Basic) {
|
||||
const string s1("table fills with strings");
|
||||
const string s2("offsets preserved as labels");
|
||||
const string s3("verified with tests");
|
||||
const char* kExpectedContents =
|
||||
"\0table fills with strings\0"
|
||||
"offsets preserved as labels\0"
|
||||
"verified with tests\0";
|
||||
Label l1(table.Add(s1));
|
||||
Label l2(table.Add(s2));
|
||||
Label l3(table.Add(s3));
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
EXPECT_EQ(0, memcmp(kExpectedContents,
|
||||
contents.c_str(),
|
||||
contents.size()));
|
||||
// empty_string is at zero, other strings start at 1.
|
||||
ASSERT_TRUE(l1.IsKnownConstant());
|
||||
EXPECT_EQ(1, l1.Value());
|
||||
// Each string has an extra byte for a trailing null.
|
||||
EXPECT_EQ(1 + s1.length() + 1, l2.Value());
|
||||
EXPECT_EQ(1 + s1.length() + 1 + s2.length() + 1, l3.Value());
|
||||
}
|
||||
|
||||
TEST_F(StringTableTest, Duplicates) {
|
||||
const string s1("string 1");
|
||||
const string s2("string 2");
|
||||
const string s3("");
|
||||
const char* kExpectedContents = "\0string 1\0string 2\0";
|
||||
Label l1(table.Add(s1));
|
||||
Label l2(table.Add(s2));
|
||||
// Adding strings twice should return the same Label.
|
||||
Label l3(table.Add(s3));
|
||||
Label l4(table.Add(s2));
|
||||
string contents;
|
||||
ASSERT_TRUE(table.GetContents(&contents));
|
||||
EXPECT_EQ(0, memcmp(kExpectedContents,
|
||||
contents.c_str(),
|
||||
contents.size()));
|
||||
EXPECT_EQ(0, table.empty_string.Value());
|
||||
EXPECT_EQ(table.empty_string.Value(), l3.Value());
|
||||
EXPECT_EQ(l2.Value(), l4.Value());
|
||||
}
|
||||
|
||||
class SymbolTableTest : public Test {};
|
||||
|
||||
TEST_F(SymbolTableTest, Simple32) {
|
||||
StringTable table(kLittleEndian);
|
||||
SymbolTable syms(kLittleEndian, 4, table);
|
||||
|
||||
const string kFuncName1 = "superfunc";
|
||||
const uint32_t kFuncAddr1 = 0x10001000;
|
||||
const uint32_t kFuncSize1 = 0x10;
|
||||
const string kFuncName2 = "awesomefunc";
|
||||
const uint32_t kFuncAddr2 = 0x20002000;
|
||||
const uint32_t kFuncSize2 = 0x2f;
|
||||
const string kFuncName3 = "megafunc";
|
||||
const uint32_t kFuncAddr3 = 0x30003000;
|
||||
const uint32_t kFuncSize3 = 0x3c;
|
||||
|
||||
syms.AddSymbol(kFuncName1, kFuncAddr1, kFuncSize1,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
syms.AddSymbol(kFuncName2, kFuncAddr2, kFuncSize2,
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
SHN_UNDEF + 2);
|
||||
syms.AddSymbol(kFuncName3, kFuncAddr3, kFuncSize3,
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC),
|
||||
SHN_UNDEF + 3);
|
||||
|
||||
const char kExpectedStringTable[] = "\0superfunc\0awesomefunc\0megafunc";
|
||||
const size_t kExpectedStringTableSize = sizeof(kExpectedStringTable);
|
||||
EXPECT_EQ(kExpectedStringTableSize, table.Size());
|
||||
string table_contents;
|
||||
table.GetContents(&table_contents);
|
||||
EXPECT_EQ(0, memcmp(kExpectedStringTable,
|
||||
table_contents.c_str(),
|
||||
table_contents.size()));
|
||||
|
||||
const uint8_t kExpectedSymbolContents[] = {
|
||||
// Symbol 1
|
||||
0x01, 0x00, 0x00, 0x00, // name
|
||||
0x00, 0x10, 0x00, 0x10, // value
|
||||
0x10, 0x00, 0x00, 0x00, // size
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), // info
|
||||
0x00, // other
|
||||
0x01, 0x00, // shndx
|
||||
// Symbol 2
|
||||
0x0B, 0x00, 0x00, 0x00, // name
|
||||
0x00, 0x20, 0x00, 0x20, // value
|
||||
0x2f, 0x00, 0x00, 0x00, // size
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info
|
||||
0x00, // other
|
||||
0x02, 0x00, // shndx
|
||||
// Symbol 3
|
||||
0x17, 0x00, 0x00, 0x00, // name
|
||||
0x00, 0x30, 0x00, 0x30, // value
|
||||
0x3c, 0x00, 0x00, 0x00, // size
|
||||
ELF32_ST_INFO(STB_LOCAL, STT_FUNC), // info
|
||||
0x00, // other
|
||||
0x03, 0x00, // shndx
|
||||
};
|
||||
const size_t kExpectedSymbolSize = sizeof(kExpectedSymbolContents);
|
||||
EXPECT_EQ(kExpectedSymbolSize, syms.Size());
|
||||
|
||||
string symbol_contents;
|
||||
syms.GetContents(&symbol_contents);
|
||||
EXPECT_EQ(0, memcmp(kExpectedSymbolContents,
|
||||
symbol_contents.c_str(),
|
||||
symbol_contents.size()));
|
||||
}
|
||||
|
||||
class BasicElf : public Test {};
|
||||
|
||||
// Doesn't seem worthwhile writing the tests to be endian-independent
|
||||
// when they're unlikely to ever be run on big-endian systems.
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
|
||||
TEST_F(BasicElf, EmptyLE32) {
|
||||
const size_t kStringTableSize = sizeof("\0.shstrtab");
|
||||
const size_t kStringTableAlign = 4 - kStringTableSize % 4;
|
||||
const size_t kExpectedSize = sizeof(Elf32_Ehdr) +
|
||||
// Two sections, SHT_NULL + the section header string table.
|
||||
2 * sizeof(Elf32_Shdr) +
|
||||
kStringTableSize + kStringTableAlign;
|
||||
|
||||
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
|
||||
elf.Finish();
|
||||
EXPECT_EQ(kExpectedSize, elf.Size());
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(elf.GetContents(&contents));
|
||||
ASSERT_EQ(kExpectedSize, contents.size());
|
||||
const Elf32_Ehdr* header =
|
||||
reinterpret_cast<const Elf32_Ehdr*>(contents.data());
|
||||
const uint8_t kIdent[] = {
|
||||
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
||||
ELFCLASS32, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent)));
|
||||
EXPECT_EQ(ET_EXEC, header->e_type);
|
||||
EXPECT_EQ(EM_386, header->e_machine);
|
||||
EXPECT_EQ(EV_CURRENT, header->e_version);
|
||||
EXPECT_EQ(0, header->e_entry);
|
||||
EXPECT_EQ(0, header->e_phoff);
|
||||
EXPECT_EQ(sizeof(Elf32_Ehdr) + kStringTableSize + kStringTableAlign,
|
||||
header->e_shoff);
|
||||
EXPECT_EQ(0, header->e_flags);
|
||||
EXPECT_EQ(sizeof(Elf32_Ehdr), header->e_ehsize);
|
||||
EXPECT_EQ(sizeof(Elf32_Phdr), header->e_phentsize);
|
||||
EXPECT_EQ(0, header->e_phnum);
|
||||
EXPECT_EQ(sizeof(Elf32_Shdr), header->e_shentsize);
|
||||
EXPECT_EQ(2, header->e_shnum);
|
||||
EXPECT_EQ(1, header->e_shstrndx);
|
||||
}
|
||||
|
||||
TEST_F(BasicElf, EmptyLE64) {
|
||||
const size_t kStringTableSize = sizeof("\0.shstrtab");
|
||||
const size_t kStringTableAlign = 4 - kStringTableSize % 4;
|
||||
const size_t kExpectedSize = sizeof(Elf64_Ehdr) +
|
||||
// Two sections, SHT_NULL + the section header string table.
|
||||
2 * sizeof(Elf64_Shdr) +
|
||||
kStringTableSize + kStringTableAlign;
|
||||
|
||||
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
elf.Finish();
|
||||
EXPECT_EQ(kExpectedSize, elf.Size());
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(elf.GetContents(&contents));
|
||||
ASSERT_EQ(kExpectedSize, contents.size());
|
||||
const Elf64_Ehdr* header =
|
||||
reinterpret_cast<const Elf64_Ehdr*>(contents.data());
|
||||
const uint8_t kIdent[] = {
|
||||
ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
|
||||
ELFCLASS64, ELFDATA2LSB, EV_CURRENT, ELFOSABI_SYSV,
|
||||
0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
EXPECT_EQ(0, memcmp(kIdent, header->e_ident, sizeof(kIdent)));
|
||||
EXPECT_EQ(ET_EXEC, header->e_type);
|
||||
EXPECT_EQ(EM_X86_64, header->e_machine);
|
||||
EXPECT_EQ(EV_CURRENT, header->e_version);
|
||||
EXPECT_EQ(0, header->e_entry);
|
||||
EXPECT_EQ(0, header->e_phoff);
|
||||
EXPECT_EQ(sizeof(Elf64_Ehdr) + kStringTableSize + kStringTableAlign,
|
||||
header->e_shoff);
|
||||
EXPECT_EQ(0, header->e_flags);
|
||||
EXPECT_EQ(sizeof(Elf64_Ehdr), header->e_ehsize);
|
||||
EXPECT_EQ(sizeof(Elf64_Phdr), header->e_phentsize);
|
||||
EXPECT_EQ(0, header->e_phnum);
|
||||
EXPECT_EQ(sizeof(Elf64_Shdr), header->e_shentsize);
|
||||
EXPECT_EQ(2, header->e_shnum);
|
||||
EXPECT_EQ(1, header->e_shstrndx);
|
||||
}
|
||||
|
||||
#endif // defined(__i386__) || defined(__x86_64__)
|
Reference in New Issue
Block a user