summaryrefslogtreecommitdiffstats
path: root/toolkit/crashreporter/google-breakpad/src/common/linux
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/crashreporter/google-breakpad/src/common/linux')
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/crc32.cc70
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/crc32.h53
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc1159
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h86
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols_unittest.cc204
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h58
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.cc179
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.h148
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump_unittest.cc256
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elf_gnu_compat.h46
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.cc178
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.h58
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module_unittest.cc370
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elfutils-inl.h74
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc194
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.h126
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc202
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h87
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc338
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc202
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h107
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc170
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc104
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.h48
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc230
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h90
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/ignore_ret.h40
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc241
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h93
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc237
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h96
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc213
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc107
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h87
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file_unittest.cc208
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/moz.build56
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.cc53
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.h65
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink_unittest.cc89
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.cc155
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.h59
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.cc263
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.h197
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf_unittest.cc413
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/tests/auto_testfile.h124
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.cc322
-rw-r--r--toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.h117
47 files changed, 8072 insertions, 0 deletions
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.cc
new file mode 100644
index 000000000..8df636ce4
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.cc
@@ -0,0 +1,70 @@
+// Copyright 2014 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/crc32.h"
+
+namespace google_breakpad {
+
+// This implementation is based on the sample implementation in RFC 1952.
+
+// CRC32 polynomial, in reversed form.
+// See RFC 1952, or http://en.wikipedia.org/wiki/Cyclic_redundancy_check
+static const uint32_t kCrc32Polynomial = 0xEDB88320;
+static uint32_t kCrc32Table[256] = { 0 };
+
+#define arraysize(f) (sizeof(f) / sizeof(*f))
+
+static void EnsureCrc32TableInited() {
+ if (kCrc32Table[arraysize(kCrc32Table) - 1])
+ return; // already inited
+ for (uint32_t i = 0; i < arraysize(kCrc32Table); ++i) {
+ uint32_t c = i;
+ for (size_t j = 0; j < 8; ++j) {
+ if (c & 1) {
+ c = kCrc32Polynomial ^ (c >> 1);
+ } else {
+ c >>= 1;
+ }
+ }
+ kCrc32Table[i] = c;
+ }
+}
+
+uint32_t UpdateCrc32(uint32_t start, const void* buf, size_t len) {
+ EnsureCrc32TableInited();
+
+ uint32_t c = start ^ 0xFFFFFFFF;
+ const uint8_t* u = static_cast<const uint8_t*>(buf);
+ for (size_t i = 0; i < len; ++i) {
+ c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8);
+ }
+ return c ^ 0xFFFFFFFF;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.h b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.h
new file mode 100644
index 000000000..e3d9db92b
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/crc32.h
@@ -0,0 +1,53 @@
+// Copyright 2014 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_CRC32_H_
+#define COMMON_LINUX_CRC32_H_
+
+#include <stdint.h>
+
+#include <string>
+
+namespace google_breakpad {
+
+// Updates a CRC32 checksum with |len| bytes from |buf|. |initial| holds the
+// checksum result from the previous update; for the first call, it should be 0.
+uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len);
+
+// Computes a CRC32 checksum using |len| bytes from |buf|.
+inline uint32_t ComputeCrc32(const void* buf, size_t len) {
+ return UpdateCrc32(0, buf, len);
+}
+inline uint32_t ComputeCrc32(const std::string& str) {
+ return ComputeCrc32(str.c_str(), str.size());
+}
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_CRC32_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
new file mode 100644
index 000000000..cd99bee66
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc
@@ -0,0 +1,1159 @@
+// Copyright (c) 2011 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
+
+// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
+// Find all the debugging info in a file and dump it as a Breakpad symbol file.
+
+#include "common/linux/dump_symbols.h"
+
+#include <assert.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <link.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "common/arm_ex_reader.h"
+#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/crc32.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/linux/elfutils.h"
+#include "common/linux/elfutils-inl.h"
+#include "common/linux/elf_symbols_to_module.h"
+#include "common/linux/file_id.h"
+#include "common/memory.h"
+#include "common/module.h"
+#include "common/scoped_ptr.h"
+#ifndef NO_STABS_SUPPORT
+#include "common/stabs_reader.h"
+#include "common/stabs_to_module.h"
+#endif
+#include "common/using_std_string.h"
+
+#ifndef SHT_ARM_EXIDX
+// bionic and older glibc don't define this
+# define SHT_ARM_EXIDX (SHT_LOPROC + 1)
+#endif
+
+// This namespace contains helper functions.
+namespace {
+
+using google_breakpad::DumpOptions;
+using google_breakpad::DwarfCFIToModule;
+using google_breakpad::DwarfCUToModule;
+using google_breakpad::DwarfLineToModule;
+using google_breakpad::ElfClass;
+using google_breakpad::ElfClass32;
+using google_breakpad::ElfClass64;
+using google_breakpad::FileID;
+using google_breakpad::FindElfSectionByName;
+using google_breakpad::GetOffset;
+using google_breakpad::IsValidElf;
+using google_breakpad::kDefaultBuildIdSize;
+using google_breakpad::Module;
+using google_breakpad::PageAllocator;
+#ifndef NO_STABS_SUPPORT
+using google_breakpad::StabsToModule;
+#endif
+using google_breakpad::scoped_ptr;
+using google_breakpad::wasteful_vector;
+
+// Define AARCH64 ELF architecture if host machine does not include this define.
+#ifndef EM_AARCH64
+#define EM_AARCH64 183
+#endif
+
+// Define SHT_ANDROID_REL and SHT_ANDROID_RELA if not defined by the host.
+// Sections with this type contain Android packed relocations.
+#ifndef SHT_ANDROID_REL
+#define SHT_ANDROID_REL (SHT_LOOS + 1)
+#endif
+#ifndef SHT_ANDROID_RELA
+#define SHT_ANDROID_RELA (SHT_LOOS + 2)
+#endif
+
+//
+// FDWrapper
+//
+// Wrapper class to make sure opened file is closed.
+//
+class FDWrapper {
+ public:
+ explicit FDWrapper(int fd) :
+ fd_(fd) {}
+ ~FDWrapper() {
+ if (fd_ != -1)
+ close(fd_);
+ }
+ int get() {
+ return fd_;
+ }
+ int release() {
+ int fd = fd_;
+ fd_ = -1;
+ return fd;
+ }
+ private:
+ int fd_;
+};
+
+//
+// MmapWrapper
+//
+// Wrapper class to make sure mapped regions are unmapped.
+//
+class MmapWrapper {
+ public:
+ MmapWrapper() : is_set_(false) {}
+ ~MmapWrapper() {
+ if (is_set_ && base_ != NULL) {
+ assert(size_ > 0);
+ munmap(base_, size_);
+ }
+ }
+ void set(void *mapped_address, size_t mapped_size) {
+ is_set_ = true;
+ base_ = mapped_address;
+ size_ = mapped_size;
+ }
+ void release() {
+ assert(is_set_);
+ is_set_ = false;
+ base_ = NULL;
+ size_ = 0;
+ }
+
+ private:
+ bool is_set_;
+ void* base_;
+ size_t size_;
+};
+
+// Find the preferred loading address of the binary.
+template<typename ElfClass>
+typename ElfClass::Addr GetLoadingAddress(
+ const typename ElfClass::Phdr* program_headers,
+ int nheader) {
+ typedef typename ElfClass::Phdr Phdr;
+
+ // For non-PIC executables (e_type == ET_EXEC), the load address is
+ // the start address of the first PT_LOAD segment. (ELF requires
+ // the segments to be sorted by load address.) For PIC executables
+ // and dynamic libraries (e_type == ET_DYN), this address will
+ // normally be zero.
+ for (int i = 0; i < nheader; ++i) {
+ const Phdr& header = program_headers[i];
+ if (header.p_type == PT_LOAD)
+ return header.p_vaddr;
+ }
+ return 0;
+}
+
+#ifndef NO_STABS_SUPPORT
+template<typename ElfClass>
+bool LoadStabs(const typename ElfClass::Ehdr* elf_header,
+ const typename ElfClass::Shdr* stab_section,
+ const typename ElfClass::Shdr* stabstr_section,
+ const bool big_endian,
+ Module* module) {
+ // A callback object to handle data from the STABS reader.
+ StabsToModule handler(module);
+ // Find the addresses of the STABS data, and create a STABS reader object.
+ // On Linux, STABS entries always have 32-bit values, regardless of the
+ // address size of the architecture whose code they're describing, and
+ // the strings are always "unitized".
+ const uint8_t* stabs =
+ GetOffset<ElfClass, uint8_t>(elf_header, stab_section->sh_offset);
+ const uint8_t* stabstr =
+ GetOffset<ElfClass, uint8_t>(elf_header, stabstr_section->sh_offset);
+ google_breakpad::StabsReader reader(stabs, stab_section->sh_size,
+ stabstr, stabstr_section->sh_size,
+ big_endian, 4, true, &handler);
+ // Read the STABS data, and do post-processing.
+ if (!reader.Process())
+ return false;
+ handler.Finalize();
+ return true;
+}
+#endif // NO_STABS_SUPPORT
+
+// A line-to-module loader that accepts line number info parsed by
+// dwarf2reader::LineInfo and populates a Module and a line vector
+// with the results.
+class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler {
+ public:
+ // Create a line-to-module converter using BYTE_READER.
+ explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
+ : byte_reader_(byte_reader) { }
+ void StartCompilationUnit(const string& compilation_dir) {
+ compilation_dir_ = compilation_dir;
+ }
+ void ReadProgram(const uint8_t *program, uint64 length,
+ Module* module, std::vector<Module::Line>* lines) {
+ DwarfLineToModule handler(module, compilation_dir_, lines);
+ dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
+ parser.Start();
+ }
+ private:
+ string compilation_dir_;
+ dwarf2reader::ByteReader *byte_reader_;
+};
+
+template<typename ElfClass>
+bool LoadDwarf(const string& dwarf_filename,
+ const typename ElfClass::Ehdr* elf_header,
+ const bool big_endian,
+ bool handle_inter_cu_refs,
+ Module* module) {
+ typedef typename ElfClass::Shdr Shdr;
+
+ const dwarf2reader::Endianness endianness = big_endian ?
+ dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
+ dwarf2reader::ByteReader byte_reader(endianness);
+
+ // Construct a context for this file.
+ DwarfCUToModule::FileContext file_context(dwarf_filename,
+ module,
+ handle_inter_cu_refs);
+
+ // Build a map of the ELF file's sections.
+ const Shdr* sections =
+ GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
+ int num_sections = elf_header->e_shnum;
+ const Shdr* section_names = sections + elf_header->e_shstrndx;
+ for (int i = 0; i < num_sections; i++) {
+ const Shdr* section = &sections[i];
+ string name = GetOffset<ElfClass, char>(elf_header,
+ section_names->sh_offset) +
+ section->sh_name;
+ const uint8_t *contents = GetOffset<ElfClass, uint8_t>(elf_header,
+ section->sh_offset);
+ file_context.AddSectionToSectionMap(name, contents, section->sh_size);
+ }
+
+ // Parse all the compilation units in the .debug_info section.
+ DumperLineToModule line_to_module(&byte_reader);
+ dwarf2reader::SectionMap::const_iterator debug_info_entry =
+ file_context.section_map().find(".debug_info");
+ assert(debug_info_entry != file_context.section_map().end());
+ const std::pair<const uint8_t *, uint64>& debug_info_section =
+ debug_info_entry->second;
+ // This should never have been called if the file doesn't have a
+ // .debug_info section.
+ assert(debug_info_section.first);
+ uint64 debug_info_length = debug_info_section.second;
+ for (uint64 offset = 0; offset < debug_info_length;) {
+ // Make a handler for the root DIE that populates MODULE with the
+ // data that was found.
+ DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset);
+ DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter);
+ // Make a Dwarf2Handler that drives the DIEHandler.
+ dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
+ // Make a DWARF parser for the compilation unit at OFFSET.
+ dwarf2reader::CompilationUnit reader(dwarf_filename,
+ file_context.section_map(),
+ offset,
+ &byte_reader,
+ &die_dispatcher);
+ // Process the entire compilation unit; get the offset of the next.
+ offset += reader.Start();
+ }
+ return true;
+}
+
+// Fill REGISTER_NAMES with the register names appropriate to the
+// machine architecture given in HEADER, indexed by the register
+// numbers used in DWARF call frame information. Return true on
+// success, or false if HEADER's machine architecture is not
+// supported.
+template<typename ElfClass>
+bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
+ std::vector<string>* register_names) {
+ switch (elf_header->e_machine) {
+ case EM_386:
+ *register_names = DwarfCFIToModule::RegisterNames::I386();
+ return true;
+ case EM_ARM:
+ *register_names = DwarfCFIToModule::RegisterNames::ARM();
+ return true;
+ case EM_AARCH64:
+ *register_names = DwarfCFIToModule::RegisterNames::ARM64();
+ return true;
+ case EM_MIPS:
+ *register_names = DwarfCFIToModule::RegisterNames::MIPS();
+ return true;
+ case EM_X86_64:
+ *register_names = DwarfCFIToModule::RegisterNames::X86_64();
+ return true;
+ default:
+ return false;
+ }
+}
+
+template<typename ElfClass>
+bool LoadDwarfCFI(const string& dwarf_filename,
+ const typename ElfClass::Ehdr* elf_header,
+ const char* section_name,
+ const typename ElfClass::Shdr* section,
+ const bool eh_frame,
+ const typename ElfClass::Shdr* got_section,
+ const typename ElfClass::Shdr* text_section,
+ const bool big_endian,
+ Module* module) {
+ // Find the appropriate set of register names for this file's
+ // architecture.
+ std::vector<string> register_names;
+ if (!DwarfCFIRegisterNames<ElfClass>(elf_header, &register_names)) {
+ fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
+ " cannot convert DWARF call frame information\n",
+ dwarf_filename.c_str(), elf_header->e_machine);
+ return false;
+ }
+
+ const dwarf2reader::Endianness endianness = big_endian ?
+ dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
+
+ // Find the call frame information and its size.
+ const uint8_t *cfi =
+ GetOffset<ElfClass, uint8_t>(elf_header, section->sh_offset);
+ size_t cfi_size = section->sh_size;
+
+ // Plug together the parser, handler, and their entourages.
+ DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name);
+ DwarfCFIToModule handler(module, register_names, &module_reporter);
+ dwarf2reader::ByteReader byte_reader(endianness);
+
+ byte_reader.SetAddressSize(ElfClass::kAddrSize);
+
+ // Provide the base addresses for .eh_frame encoded pointers, if
+ // possible.
+ byte_reader.SetCFIDataBase(section->sh_addr, cfi);
+ if (got_section)
+ byte_reader.SetDataBase(got_section->sh_addr);
+ if (text_section)
+ byte_reader.SetTextBase(text_section->sh_addr);
+
+ dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
+ section_name);
+ dwarf2reader::CallFrameInfo parser(cfi, cfi_size,
+ &byte_reader, &handler, &dwarf_reporter,
+ eh_frame);
+ parser.Start();
+ return true;
+}
+
+template<typename ElfClass>
+bool LoadARMexidx(const typename ElfClass::Ehdr* elf_header,
+ const typename ElfClass::Shdr* exidx_section,
+ const typename ElfClass::Shdr* extab_section,
+ uint32_t loading_addr,
+ Module* module) {
+ // To do this properly we need to know:
+ // * the bounds of the .ARM.exidx section in the mapped image
+ // * the bounds of the .ARM.extab section in the mapped image
+ // * the vma of the last byte in the text section associated with the .exidx
+ // The first two are easy. The third is a bit tricky. If we can't
+ // figure out what it is, just pass in zero.
+ const char *exidx_img
+ = GetOffset<ElfClass, char>(elf_header, exidx_section->sh_offset);
+ size_t exidx_size = exidx_section->sh_size;
+ const char *extab_img
+ = GetOffset<ElfClass, char>(elf_header, extab_section->sh_offset);
+ size_t extab_size = extab_section->sh_size;
+
+ // The sh_link field of the exidx section gives the section number
+ // for the associated text section.
+ uint32_t exidx_text_last_svma = 0;
+ int exidx_text_sno = exidx_section->sh_link;
+ typedef typename ElfClass::Shdr Shdr;
+ // |sections| points to the section header table
+ const Shdr* sections
+ = GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
+ const int num_sections = elf_header->e_shnum;
+ if (exidx_text_sno >= 0 && exidx_text_sno < num_sections) {
+ const Shdr* exidx_text_shdr = &sections[exidx_text_sno];
+ if (exidx_text_shdr->sh_size > 0) {
+ exidx_text_last_svma
+ = exidx_text_shdr->sh_addr + exidx_text_shdr->sh_size - 1;
+ }
+ }
+
+ arm_ex_to_module::ARMExToModule handler(module);
+ arm_ex_reader::ExceptionTableInfo
+ parser(exidx_img, exidx_size, extab_img, extab_size, exidx_text_last_svma,
+ &handler,
+ reinterpret_cast<const char*>(elf_header),
+ loading_addr);
+ parser.Start();
+ return true;
+}
+
+bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
+ void** elf_header) {
+ int obj_fd = open(obj_file.c_str(), O_RDONLY);
+ if (obj_fd < 0) {
+ fprintf(stderr, "Failed to open ELF file '%s': %s\n",
+ obj_file.c_str(), strerror(errno));
+ return false;
+ }
+ FDWrapper obj_fd_wrapper(obj_fd);
+ struct stat st;
+ if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
+ fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
+ obj_file.c_str(), strerror(errno));
+ return false;
+ }
+ void* obj_base = mmap(NULL, st.st_size,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
+ if (obj_base == MAP_FAILED) {
+ fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
+ obj_file.c_str(), strerror(errno));
+ return false;
+ }
+ map_wrapper->set(obj_base, st.st_size);
+ *elf_header = obj_base;
+ if (!IsValidElf(*elf_header)) {
+ fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
+ return false;
+ }
+ return true;
+}
+
+// Get the endianness of ELF_HEADER. If it's invalid, return false.
+template<typename ElfClass>
+bool ElfEndianness(const typename ElfClass::Ehdr* elf_header,
+ bool* big_endian) {
+ if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) {
+ *big_endian = false;
+ return true;
+ }
+ if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) {
+ *big_endian = true;
+ return true;
+ }
+
+ fprintf(stderr, "bad data encoding in ELF header: %d\n",
+ elf_header->e_ident[EI_DATA]);
+ return false;
+}
+
+// Given |left_abspath|, find the absolute path for |right_path| and see if the
+// two absolute paths are the same.
+bool IsSameFile(const char* left_abspath, const string& right_path) {
+ char right_abspath[PATH_MAX];
+ if (!realpath(right_path.c_str(), right_abspath))
+ return false;
+ return strcmp(left_abspath, right_abspath) == 0;
+}
+
+// Read the .gnu_debuglink and get the debug file name. If anything goes
+// wrong, return an empty string.
+string ReadDebugLink(const uint8_t *debuglink,
+ const size_t debuglink_size,
+ const bool big_endian,
+ const string& obj_file,
+ const std::vector<string>& debug_dirs) {
+ // Include '\0' + CRC32 (4 bytes).
+ size_t debuglink_len = strlen(reinterpret_cast<const char *>(debuglink)) + 5;
+ debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round up to 4 bytes.
+
+ // Sanity check.
+ if (debuglink_len != debuglink_size) {
+ fprintf(stderr, "Mismatched .gnu_debuglink string / section size: "
+ "%zx %zx\n", debuglink_len, debuglink_size);
+ return string();
+ }
+
+ char obj_file_abspath[PATH_MAX];
+ if (!realpath(obj_file.c_str(), obj_file_abspath)) {
+ fprintf(stderr, "Cannot resolve absolute path for %s\n", obj_file.c_str());
+ return string();
+ }
+
+ std::vector<string> searched_paths;
+ string debuglink_path;
+ std::vector<string>::const_iterator it;
+ for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) {
+ const string& debug_dir = *it;
+ debuglink_path = debug_dir + "/" +
+ reinterpret_cast<const char *>(debuglink);
+
+ // There is the annoying case of /path/to/foo.so having foo.so as the
+ // debug link file name. Thus this may end up opening /path/to/foo.so again,
+ // and there is a small chance of the two files having the same CRC.
+ if (IsSameFile(obj_file_abspath, debuglink_path))
+ continue;
+
+ searched_paths.push_back(debug_dir);
+ int debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
+ if (debuglink_fd < 0)
+ continue;
+
+ FDWrapper debuglink_fd_wrapper(debuglink_fd);
+
+ // The CRC is the last 4 bytes in |debuglink|.
+ const dwarf2reader::Endianness endianness = big_endian ?
+ dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
+ dwarf2reader::ByteReader byte_reader(endianness);
+ uint32_t expected_crc =
+ byte_reader.ReadFourBytes(&debuglink[debuglink_size - 4]);
+
+ uint32_t actual_crc = 0;
+ while (true) {
+ const size_t kReadSize = 4096;
+ char buf[kReadSize];
+ ssize_t bytes_read = HANDLE_EINTR(read(debuglink_fd, &buf, kReadSize));
+ if (bytes_read < 0) {
+ fprintf(stderr, "Error reading debug ELF file %s.\n",
+ debuglink_path.c_str());
+ return string();
+ }
+ if (bytes_read == 0)
+ break;
+ actual_crc = google_breakpad::UpdateCrc32(actual_crc, buf, bytes_read);
+ }
+ if (actual_crc != expected_crc) {
+ fprintf(stderr, "Error reading debug ELF file - CRC32 mismatch: %s\n",
+ debuglink_path.c_str());
+ continue;
+ }
+
+ // Found debug file.
+ return debuglink_path;
+ }
+
+ // Not found case.
+ fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n",
+ obj_file.c_str());
+ for (it = searched_paths.begin(); it < searched_paths.end(); ++it) {
+ const string& debug_dir = *it;
+ fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink);
+ }
+ return string();
+}
+
+//
+// LoadSymbolsInfo
+//
+// Holds the state between the two calls to LoadSymbols() in case it's necessary
+// to follow the .gnu_debuglink section and load debug information from a
+// different file.
+//
+template<typename ElfClass>
+class LoadSymbolsInfo {
+ public:
+ typedef typename ElfClass::Addr Addr;
+
+ explicit LoadSymbolsInfo(const std::vector<string>& dbg_dirs) :
+ debug_dirs_(dbg_dirs),
+ has_loading_addr_(false) {}
+
+ // Keeps track of which sections have been loaded so sections don't
+ // accidentally get loaded twice from two different files.
+ void LoadedSection(const string &section) {
+ if (loaded_sections_.count(section) == 0) {
+ loaded_sections_.insert(section);
+ } else {
+ fprintf(stderr, "Section %s has already been loaded.\n",
+ section.c_str());
+ }
+ }
+
+ // The ELF file and linked debug file are expected to have the same preferred
+ // loading address.
+ void set_loading_addr(Addr addr, const string &filename) {
+ if (!has_loading_addr_) {
+ loading_addr_ = addr;
+ loaded_file_ = filename;
+ return;
+ }
+
+ if (addr != loading_addr_) {
+ fprintf(stderr,
+ "ELF file '%s' and debug ELF file '%s' "
+ "have different load addresses.\n",
+ loaded_file_.c_str(), filename.c_str());
+ assert(false);
+ }
+ }
+
+ // Setters and getters
+ const std::vector<string>& debug_dirs() const {
+ return debug_dirs_;
+ }
+
+ string debuglink_file() const {
+ return debuglink_file_;
+ }
+ void set_debuglink_file(string file) {
+ debuglink_file_ = file;
+ }
+
+ private:
+ const std::vector<string>& debug_dirs_; // Directories in which to
+ // search for the debug ELF file.
+
+ string debuglink_file_; // Full path to the debug ELF file.
+
+ bool has_loading_addr_; // Indicate if LOADING_ADDR_ is valid.
+
+ Addr loading_addr_; // Saves the preferred loading address from the
+ // first call to LoadSymbols().
+
+ string loaded_file_; // Name of the file loaded from the first call to
+ // LoadSymbols().
+
+ std::set<string> loaded_sections_; // Tracks the Loaded ELF sections
+ // between calls to LoadSymbols().
+};
+
+template<typename ElfClass>
+bool LoadSymbols(const string& obj_file,
+ const bool big_endian,
+ const typename ElfClass::Ehdr* elf_header,
+ const bool read_gnu_debug_link,
+ LoadSymbolsInfo<ElfClass>* info,
+ const DumpOptions& options,
+ Module* module) {
+ typedef typename ElfClass::Addr Addr;
+ typedef typename ElfClass::Phdr Phdr;
+ typedef typename ElfClass::Shdr Shdr;
+ typedef typename ElfClass::Word Word;
+
+ Addr loading_addr = GetLoadingAddress<ElfClass>(
+ GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
+ elf_header->e_phnum);
+ module->SetLoadAddress(loading_addr);
+ info->set_loading_addr(loading_addr, obj_file);
+
+ Word debug_section_type =
+ elf_header->e_machine == EM_MIPS ? SHT_MIPS_DWARF : SHT_PROGBITS;
+ const Shdr* sections =
+ GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
+ const Shdr* section_names = sections + elf_header->e_shstrndx;
+ const char* names =
+ GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
+ const char *names_end = names + section_names->sh_size;
+ bool found_debug_info_section = false;
+ bool found_usable_info = false;
+
+ // Reject files that contain Android packed relocations. The pre-packed
+ // version of the file should be symbolized; the packed version is only
+ // intended for use on the target system.
+ if (FindElfSectionByName<ElfClass>(".rel.dyn", SHT_ANDROID_REL,
+ sections, names,
+ names_end, elf_header->e_shnum)) {
+ fprintf(stderr, "%s: file contains a \".rel.dyn\" section "
+ "with type SHT_ANDROID_REL\n", obj_file.c_str());
+ fprintf(stderr, "Files containing Android packed relocations "
+ "may not be symbolized.\n");
+ return false;
+ }
+ if (FindElfSectionByName<ElfClass>(".rela.dyn", SHT_ANDROID_RELA,
+ sections, names,
+ names_end, elf_header->e_shnum)) {
+ fprintf(stderr, "%s: file contains a \".rela.dyn\" section "
+ "with type SHT_ANDROID_RELA\n", obj_file.c_str());
+ fprintf(stderr, "Files containing Android packed relocations "
+ "may not be symbolized.\n");
+ return false;
+ }
+
+ if (options.symbol_data != ONLY_CFI) {
+#ifndef NO_STABS_SUPPORT
+ // Look for STABS debugging information, and load it if present.
+ const Shdr* stab_section =
+ FindElfSectionByName<ElfClass>(".stab", SHT_PROGBITS,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ if (stab_section) {
+ const Shdr* stabstr_section = stab_section->sh_link + sections;
+ if (stabstr_section) {
+ found_debug_info_section = true;
+ found_usable_info = true;
+ info->LoadedSection(".stab");
+ if (!LoadStabs<ElfClass>(elf_header, stab_section, stabstr_section,
+ big_endian, module)) {
+ fprintf(stderr, "%s: \".stab\" section found, but failed to load"
+ " STABS debugging information\n", obj_file.c_str());
+ }
+ }
+ }
+#endif // NO_STABS_SUPPORT
+
+ // Look for DWARF debugging information, and load it if present.
+ const Shdr* dwarf_section =
+ FindElfSectionByName<ElfClass>(".debug_info", debug_section_type,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ if (dwarf_section) {
+ found_debug_info_section = true;
+ found_usable_info = true;
+ info->LoadedSection(".debug_info");
+ if (!LoadDwarf<ElfClass>(obj_file, elf_header, big_endian,
+ options.handle_inter_cu_refs, module)) {
+ fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
+ "DWARF debugging information\n", obj_file.c_str());
+ }
+ }
+
+ // See if there are export symbols available.
+ const Shdr* symtab_section =
+ FindElfSectionByName<ElfClass>(".symtab", SHT_SYMTAB,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ const Shdr* strtab_section =
+ FindElfSectionByName<ElfClass>(".strtab", SHT_STRTAB,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ if (symtab_section && strtab_section) {
+ info->LoadedSection(".symtab");
+
+ const uint8_t* symtab =
+ GetOffset<ElfClass, uint8_t>(elf_header,
+ symtab_section->sh_offset);
+ const uint8_t* strtab =
+ GetOffset<ElfClass, uint8_t>(elf_header,
+ strtab_section->sh_offset);
+ bool result =
+ ELFSymbolsToModule(symtab,
+ symtab_section->sh_size,
+ strtab,
+ strtab_section->sh_size,
+ big_endian,
+ ElfClass::kAddrSize,
+ module);
+ found_usable_info = found_usable_info || result;
+ } else {
+ // Look in dynsym only if full symbol table was not available.
+ const Shdr* dynsym_section =
+ FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ const Shdr* dynstr_section =
+ FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ if (dynsym_section && dynstr_section) {
+ info->LoadedSection(".dynsym");
+
+ const uint8_t* dynsyms =
+ GetOffset<ElfClass, uint8_t>(elf_header,
+ dynsym_section->sh_offset);
+ const uint8_t* dynstrs =
+ GetOffset<ElfClass, uint8_t>(elf_header,
+ dynstr_section->sh_offset);
+ bool result =
+ ELFSymbolsToModule(dynsyms,
+ dynsym_section->sh_size,
+ dynstrs,
+ dynstr_section->sh_size,
+ big_endian,
+ ElfClass::kAddrSize,
+ module);
+ found_usable_info = found_usable_info || result;
+ }
+ }
+ }
+
+ if (options.symbol_data != NO_CFI) {
+ // Dwarf Call Frame Information (CFI) is actually independent from
+ // the other DWARF debugging information, and can be used alone.
+ const Shdr* dwarf_cfi_section =
+ FindElfSectionByName<ElfClass>(".debug_frame", debug_section_type,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ if (dwarf_cfi_section) {
+ // Ignore the return value of this function; even without call frame
+ // information, the other debugging information could be perfectly
+ // useful.
+ info->LoadedSection(".debug_frame");
+ bool result =
+ LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame",
+ dwarf_cfi_section, false, 0, 0, big_endian,
+ module);
+ found_usable_info = found_usable_info || result;
+ }
+
+ // Linux C++ exception handling information can also provide
+ // unwinding data.
+ const Shdr* eh_frame_section =
+ FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ if (eh_frame_section) {
+ // Pointers in .eh_frame data may be relative to the base addresses of
+ // certain sections. Provide those sections if present.
+ const Shdr* got_section =
+ FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ const Shdr* text_section =
+ FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ info->LoadedSection(".eh_frame");
+ // As above, ignore the return value of this function.
+ bool result =
+ LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame",
+ eh_frame_section, true,
+ got_section, text_section, big_endian, module);
+ found_usable_info = found_usable_info || result;
+ }
+ }
+
+ // ARM has special unwind tables that can be used.
+ const Shdr* arm_exidx_section =
+ FindElfSectionByName<ElfClass>(".ARM.exidx", SHT_ARM_EXIDX,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ const Shdr* arm_extab_section =
+ FindElfSectionByName<ElfClass>(".ARM.extab", SHT_PROGBITS,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ // Load information from these sections even if there is
+ // .debug_info, because some functions (e.g., hand-written or
+ // script-generated assembly) could have exidx entries but no DWARF.
+ // (For functions with both, the DWARF info that has already been
+ // parsed will take precedence.)
+ if (arm_exidx_section && arm_extab_section && options.symbol_data != NO_CFI) {
+ info->LoadedSection(".ARM.exidx");
+ info->LoadedSection(".ARM.extab");
+ bool result = LoadARMexidx<ElfClass>(elf_header,
+ arm_exidx_section, arm_extab_section,
+ loading_addr, module);
+ found_usable_info = found_usable_info || result;
+ }
+
+ if (!found_debug_info_section) {
+ fprintf(stderr, "%s: file contains no debugging information"
+ " (no \".stab\" or \".debug_info\" sections)\n",
+ obj_file.c_str());
+
+ // Failed, but maybe there's a .gnu_debuglink section?
+ if (read_gnu_debug_link) {
+ const Shdr* gnu_debuglink_section
+ = FindElfSectionByName<ElfClass>(".gnu_debuglink", SHT_PROGBITS,
+ sections, names,
+ names_end, elf_header->e_shnum);
+ if (gnu_debuglink_section) {
+ if (!info->debug_dirs().empty()) {
+ const uint8_t *debuglink_contents =
+ GetOffset<ElfClass, uint8_t>(elf_header,
+ gnu_debuglink_section->sh_offset);
+ string debuglink_file =
+ ReadDebugLink(debuglink_contents,
+ gnu_debuglink_section->sh_size,
+ big_endian,
+ obj_file,
+ info->debug_dirs());
+ info->set_debuglink_file(debuglink_file);
+ } else {
+ fprintf(stderr, ".gnu_debuglink section found in '%s', "
+ "but no debug path specified.\n", obj_file.c_str());
+ }
+ } else {
+ fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n",
+ obj_file.c_str());
+ }
+ } else {
+ // Return true if some usable information was found, since the caller
+ // doesn't want to use .gnu_debuglink.
+ return found_usable_info;
+ }
+
+ // No debug info was found, let the user try again with .gnu_debuglink
+ // if present.
+ return false;
+ }
+
+ return true;
+}
+
+// Return the breakpad symbol file identifier for the architecture of
+// ELF_HEADER.
+template<typename ElfClass>
+const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
+ typedef typename ElfClass::Half Half;
+ Half arch = elf_header->e_machine;
+ switch (arch) {
+ case EM_386: return "x86";
+ case EM_ARM: return "arm";
+ case EM_AARCH64: return "arm64";
+ case EM_MIPS: return "mips";
+ case EM_PPC64: return "ppc64";
+ case EM_PPC: return "ppc";
+ case EM_S390: return "s390";
+ case EM_SPARC: return "sparc";
+ case EM_SPARCV9: return "sparcv9";
+ case EM_X86_64: return "x86_64";
+ default: return NULL;
+ }
+}
+
+// Return the non-directory portion of FILENAME: the portion after the
+// last slash, or the whole filename if there are no slashes.
+string BaseFileName(const string &filename) {
+ // Lots of copies! basename's behavior is less than ideal.
+ char* c_filename = strdup(filename.c_str());
+ string base = basename(c_filename);
+ free(c_filename);
+ return base;
+}
+
+template<typename ElfClass>
+bool SanitizeDebugFile(const typename ElfClass::Ehdr* debug_elf_header,
+ const string& debuglink_file,
+ const string& obj_filename,
+ const char* obj_file_architecture,
+ const bool obj_file_is_big_endian) {
+ const char* debug_architecture =
+ ElfArchitecture<ElfClass>(debug_elf_header);
+ if (!debug_architecture) {
+ fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
+ debuglink_file.c_str(), debug_elf_header->e_machine);
+ return false;
+ }
+ if (strcmp(obj_file_architecture, debug_architecture)) {
+ fprintf(stderr, "%s with ELF machine architecture %s does not match "
+ "%s with ELF architecture %s\n",
+ debuglink_file.c_str(), debug_architecture,
+ obj_filename.c_str(), obj_file_architecture);
+ return false;
+ }
+ bool debug_big_endian;
+ if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian))
+ return false;
+ if (debug_big_endian != obj_file_is_big_endian) {
+ fprintf(stderr, "%s and %s does not match in endianness\n",
+ obj_filename.c_str(), debuglink_file.c_str());
+ return false;
+ }
+ return true;
+}
+
+template<typename ElfClass>
+bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
+ const string& obj_filename,
+ scoped_ptr<Module>& module) {
+ PageAllocator allocator;
+ wasteful_vector<uint8_t> identifier(&allocator, kDefaultBuildIdSize);
+ if (!FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) {
+ fprintf(stderr, "%s: unable to generate file identifier\n",
+ obj_filename.c_str());
+ return false;
+ }
+
+ const char *architecture = ElfArchitecture<ElfClass>(elf_header);
+ if (!architecture) {
+ fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
+ obj_filename.c_str(), elf_header->e_machine);
+ return false;
+ }
+
+ string name = BaseFileName(obj_filename);
+ string os = "Linux";
+ // Add an extra "0" at the end. PDB files on Windows have an 'age'
+ // number appended to the end of the file identifier; this isn't
+ // really used or necessary on other platforms, but be consistent.
+ string id = FileID::ConvertIdentifierToUUIDString(identifier) + "0";
+ // This is just the raw Build ID in hex.
+ string code_id = FileID::ConvertIdentifierToString(identifier);
+
+ module.reset(new Module(name, os, architecture, id, code_id));
+
+ return true;
+}
+
+template<typename ElfClass>
+bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
+ const string& obj_filename,
+ const std::vector<string>& debug_dirs,
+ const DumpOptions& options,
+ Module** out_module) {
+ typedef typename ElfClass::Ehdr Ehdr;
+
+ *out_module = NULL;
+
+ scoped_ptr<Module> module;
+ if (!InitModuleForElfClass<ElfClass>(elf_header, obj_filename, module)) {
+ return false;
+ }
+
+ // Figure out what endianness this file is.
+ bool big_endian;
+ if (!ElfEndianness<ElfClass>(elf_header, &big_endian))
+ return false;
+
+ LoadSymbolsInfo<ElfClass> info(debug_dirs);
+ if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
+ !debug_dirs.empty(), &info,
+ options, module.get())) {
+ const string debuglink_file = info.debuglink_file();
+ if (debuglink_file.empty())
+ return false;
+
+ // Load debuglink ELF file.
+ fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str());
+ MmapWrapper debug_map_wrapper;
+ Ehdr* debug_elf_header = NULL;
+ if (!LoadELF(debuglink_file, &debug_map_wrapper,
+ reinterpret_cast<void**>(&debug_elf_header)) ||
+ !SanitizeDebugFile<ElfClass>(debug_elf_header, debuglink_file,
+ obj_filename,
+ module->architecture().c_str(),
+ big_endian)) {
+ return false;
+ }
+
+ if (!LoadSymbols<ElfClass>(debuglink_file, big_endian,
+ debug_elf_header, false, &info,
+ options, module.get())) {
+ return false;
+ }
+ }
+
+ *out_module = module.release();
+ return true;
+}
+
+} // namespace
+
+namespace google_breakpad {
+
+// Not explicitly exported, but not static so it can be used in unit tests.
+bool ReadSymbolDataInternal(const uint8_t* obj_file,
+ const string& obj_filename,
+ const std::vector<string>& debug_dirs,
+ const DumpOptions& options,
+ Module** module) {
+ if (!IsValidElf(obj_file)) {
+ fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str());
+ return false;
+ }
+
+ int elfclass = ElfClass(obj_file);
+ if (elfclass == ELFCLASS32) {
+ return ReadSymbolDataElfClass<ElfClass32>(
+ reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, debug_dirs,
+ options, module);
+ }
+ if (elfclass == ELFCLASS64) {
+ return ReadSymbolDataElfClass<ElfClass64>(
+ reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, debug_dirs,
+ options, module);
+ }
+
+ return false;
+}
+
+bool WriteSymbolFile(const string &obj_file,
+ const std::vector<string>& debug_dirs,
+ const DumpOptions& options,
+ std::ostream &sym_stream) {
+ Module* module;
+ if (!ReadSymbolData(obj_file, debug_dirs, options, &module))
+ return false;
+
+ bool result = module->Write(sym_stream, options.symbol_data);
+ delete module;
+ return result;
+}
+
+// Read the selected object file's debugging information, and write out the
+// header only to |stream|. Return true on success; if an error occurs, report
+// it and return false.
+bool WriteSymbolFileHeader(const string& obj_file,
+ std::ostream &sym_stream) {
+ MmapWrapper map_wrapper;
+ void* elf_header = NULL;
+ if (!LoadELF(obj_file, &map_wrapper, &elf_header)) {
+ fprintf(stderr, "Could not load ELF file: %s\n", obj_file.c_str());
+ return false;
+ }
+
+ if (!IsValidElf(elf_header)) {
+ fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
+ return false;
+ }
+
+ int elfclass = ElfClass(elf_header);
+ scoped_ptr<Module> module;
+ if (elfclass == ELFCLASS32) {
+ if (!InitModuleForElfClass<ElfClass32>(
+ reinterpret_cast<const Elf32_Ehdr*>(elf_header), obj_file, module)) {
+ fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str());
+ return false;
+ }
+ } else if (elfclass == ELFCLASS64) {
+ if (!InitModuleForElfClass<ElfClass64>(
+ reinterpret_cast<const Elf64_Ehdr*>(elf_header), obj_file, module)) {
+ fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str());
+ return false;
+ }
+ } else {
+ fprintf(stderr, "Unsupported module file: %s\n", obj_file.c_str());
+ return false;
+ }
+
+ return module->Write(sym_stream, ALL_SYMBOL_DATA);
+}
+
+bool ReadSymbolData(const string& obj_file,
+ const std::vector<string>& debug_dirs,
+ const DumpOptions& options,
+ Module** module) {
+ MmapWrapper map_wrapper;
+ void* elf_header = NULL;
+ if (!LoadELF(obj_file, &map_wrapper, &elf_header))
+ return false;
+
+ return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header),
+ obj_file, debug_dirs, options, module);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h
new file mode 100644
index 000000000..1f204cbad
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h
@@ -0,0 +1,86 @@
+// -*- 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.
+
+// 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>
+#include <vector>
+
+#include "common/symbol_data.h"
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+class Module;
+
+struct DumpOptions {
+ DumpOptions(SymbolData symbol_data, bool handle_inter_cu_refs)
+ : symbol_data(symbol_data),
+ handle_inter_cu_refs(handle_inter_cu_refs) {
+ }
+
+ SymbolData symbol_data;
+ bool handle_inter_cu_refs;
+};
+
+// 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_DIRS.
+// SYMBOL_DATA allows limiting the type of symbol data written.
+bool WriteSymbolFile(const string &obj_file,
+ const std::vector<string>& debug_dirs,
+ const DumpOptions& options,
+ std::ostream &sym_stream);
+
+// Read the selected object file's debugging information, and write out the
+// header only to |stream|. Return true on success; if an error occurs, report
+// it and return false.
+bool WriteSymbolFileHeader(const string& obj_file,
+ std::ostream &sym_stream);
+
+// As above, but simply return the debugging information in MODULE
+// instead of writing it to a stream. The caller owns the resulting
+// Module object and must delete it when finished.
+bool ReadSymbolData(const string& obj_file,
+ const std::vector<string>& debug_dirs,
+ const DumpOptions& options,
+ Module** module);
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_DUMP_SYMBOLS_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols_unittest.cc
new file mode 100644
index 000000000..bb7b20076
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols_unittest.cc
@@ -0,0 +1,204 @@
+// 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 <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/linux/elf_gnu_compat.h"
+#include "common/linux/elfutils.h"
+#include "common/linux/dump_symbols.h"
+#include "common/linux/synth_elf.h"
+#include "common/module.h"
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+bool ReadSymbolDataInternal(const uint8_t* obj_file,
+ const string& obj_filename,
+ const std::vector<string>& debug_dir,
+ const DumpOptions& options,
+ Module** module);
+
+using google_breakpad::synth_elf::ELF;
+using google_breakpad::synth_elf::Notes;
+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 std::stringstream;
+using std::vector;
+using ::testing::Test;
+using ::testing::Types;
+
+template<typename ElfClass>
+class DumpSymbols : public Test {
+ public:
+ void GetElfContents(ELF& elf) {
+ string contents;
+ ASSERT_TRUE(elf.GetContents(&contents));
+ ASSERT_LT(0U, 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;
+};
+
+typedef Types<ElfClass32, ElfClass64> ElfClasses;
+
+TYPED_TEST_CASE(DumpSymbols, ElfClasses);
+
+TYPED_TEST(DumpSymbols, Invalid) {
+ Elf32_Ehdr header;
+ memset(&header, 0, sizeof(header));
+ Module* module;
+ DumpOptions options(ALL_SYMBOL_DATA, true);
+ EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(&header),
+ "foo",
+ vector<string>(),
+ options,
+ &module));
+}
+
+TYPED_TEST(DumpSymbols, SimplePublic) {
+ ELF elf(TypeParam::kMachine, TypeParam::kClass, 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, TypeParam::kAddrSize, table);
+ syms.AddSymbol("superfunc",
+ (typename TypeParam::Addr)0x1000,
+ (typename TypeParam::Addr)0x10,
+ // ELF32_ST_INFO works for 32-or 64-bit.
+ 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(typename TypeParam::Sym)); // entsize
+
+ elf.Finish();
+ this->GetElfContents(elf);
+
+ Module* module;
+ DumpOptions options(ALL_SYMBOL_DATA, true);
+ EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
+ "foo",
+ vector<string>(),
+ options,
+ &module));
+
+ stringstream s;
+ module->Write(s, ALL_SYMBOL_DATA);
+ const string expected =
+ string("MODULE Linux ") + TypeParam::kMachineName
+ + " 000000000000000000000000000000000 foo\n"
+ "INFO CODE_ID 00000000000000000000000000000000\n"
+ "PUBLIC 1000 0 superfunc\n";
+ EXPECT_EQ(expected, s.str());
+ delete module;
+}
+
+TYPED_TEST(DumpSymbols, SimpleBuildID) {
+ ELF elf(TypeParam::kMachine, TypeParam::kClass, kLittleEndian);
+ // Zero out text section for simplicity.
+ Section text(kLittleEndian);
+ text.Append(4096, 0);
+ elf.AddSection(".text", text, SHT_PROGBITS);
+
+ // Add a Build ID
+ const uint8_t kExpectedIdentifierBytes[] =
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13};
+ Notes notes(kLittleEndian);
+ notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
+ sizeof(kExpectedIdentifierBytes));
+ elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
+
+ // Add a public symbol.
+ StringTable table(kLittleEndian);
+ SymbolTable syms(kLittleEndian, TypeParam::kAddrSize, table);
+ syms.AddSymbol("superfunc",
+ (typename TypeParam::Addr)0x1000,
+ (typename TypeParam::Addr)0x10,
+ // ELF32_ST_INFO works for 32-or 64-bit.
+ 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(typename TypeParam::Sym)); // entsize
+
+ elf.Finish();
+ this->GetElfContents(elf);
+
+ Module* module;
+ DumpOptions options(ALL_SYMBOL_DATA, true);
+ EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
+ "foo",
+ vector<string>(),
+ options,
+ &module));
+
+ stringstream s;
+ module->Write(s, ALL_SYMBOL_DATA);
+ const string expected =
+ string("MODULE Linux ") + TypeParam::kMachineName
+ + " 030201000504070608090A0B0C0D0E0F0 foo\n"
+ "INFO CODE_ID 000102030405060708090A0B0C0D0E0F10111213\n"
+ "PUBLIC 1000 0 superfunc\n";
+ EXPECT_EQ(expected, s.str());
+ delete module;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h b/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h
new file mode 100644
index 000000000..3f1d18481
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h
@@ -0,0 +1,58 @@
+// 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_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ } while (eintr_wrapper_result == -1 && errno == EINTR); \
+ eintr_wrapper_result; \
+})
+
+#define IGNORE_EINTR(x) ({ \
+ __typeof__(x) eintr_wrapper_result; \
+ do { \
+ eintr_wrapper_result = (x); \
+ if (eintr_wrapper_result == -1 && errno == EINTR) { \
+ eintr_wrapper_result = 0; \
+ } \
+ } while (0); \
+ eintr_wrapper_result; \
+})
+
+#endif // COMMON_LINUX_EINTR_WRAPPER_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.cc
new file mode 100644
index 000000000..0e7db7b1f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.cc
@@ -0,0 +1,179 @@
+// 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.
+
+// elf_core_dump.cc: Implement google_breakpad::ElfCoreDump.
+// See elf_core_dump.h for details.
+
+#include "common/linux/elf_core_dump.h"
+
+#include <stddef.h>
+#include <string.h>
+
+namespace google_breakpad {
+
+// Implementation of ElfCoreDump::Note.
+
+ElfCoreDump::Note::Note() {}
+
+ElfCoreDump::Note::Note(const MemoryRange& content) : content_(content) {}
+
+bool ElfCoreDump::Note::IsValid() const {
+ return GetHeader() != NULL;
+}
+
+const ElfCoreDump::Nhdr* ElfCoreDump::Note::GetHeader() const {
+ return content_.GetData<Nhdr>(0);
+}
+
+ElfCoreDump::Word ElfCoreDump::Note::GetType() const {
+ const Nhdr* header = GetHeader();
+ // 0 is not being used as a NOTE type.
+ return header ? header->n_type : 0;
+}
+
+MemoryRange ElfCoreDump::Note::GetName() const {
+ const Nhdr* header = GetHeader();
+ if (header) {
+ return content_.Subrange(sizeof(Nhdr), header->n_namesz);
+ }
+ return MemoryRange();
+}
+
+MemoryRange ElfCoreDump::Note::GetDescription() const {
+ const Nhdr* header = GetHeader();
+ if (header) {
+ return content_.Subrange(AlignedSize(sizeof(Nhdr) + header->n_namesz),
+ header->n_descsz);
+ }
+ return MemoryRange();
+}
+
+ElfCoreDump::Note ElfCoreDump::Note::GetNextNote() const {
+ MemoryRange next_content;
+ const Nhdr* header = GetHeader();
+ if (header) {
+ size_t next_offset = AlignedSize(sizeof(Nhdr) + header->n_namesz);
+ next_offset = AlignedSize(next_offset + header->n_descsz);
+ next_content =
+ content_.Subrange(next_offset, content_.length() - next_offset);
+ }
+ return Note(next_content);
+}
+
+// static
+size_t ElfCoreDump::Note::AlignedSize(size_t size) {
+ size_t mask = sizeof(Word) - 1;
+ return (size + mask) & ~mask;
+}
+
+
+// Implementation of ElfCoreDump.
+
+ElfCoreDump::ElfCoreDump() {}
+
+ElfCoreDump::ElfCoreDump(const MemoryRange& content)
+ : content_(content) {
+}
+
+void ElfCoreDump::SetContent(const MemoryRange& content) {
+ content_ = content;
+}
+
+bool ElfCoreDump::IsValid() const {
+ const Ehdr* header = GetHeader();
+ return (header &&
+ header->e_ident[0] == ELFMAG0 &&
+ header->e_ident[1] == ELFMAG1 &&
+ header->e_ident[2] == ELFMAG2 &&
+ header->e_ident[3] == ELFMAG3 &&
+ header->e_ident[4] == kClass &&
+ header->e_version == EV_CURRENT &&
+ header->e_type == ET_CORE);
+}
+
+const ElfCoreDump::Ehdr* ElfCoreDump::GetHeader() const {
+ return content_.GetData<Ehdr>(0);
+}
+
+const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const {
+ const Ehdr* header = GetHeader();
+ if (header) {
+ return reinterpret_cast<const Phdr*>(content_.GetArrayElement(
+ header->e_phoff, header->e_phentsize, index));
+ }
+ return NULL;
+}
+
+const ElfCoreDump::Phdr* ElfCoreDump::GetFirstProgramHeaderOfType(
+ Word type) const {
+ for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) {
+ const Phdr* program = GetProgramHeader(i);
+ if (program->p_type == type) {
+ return program;
+ }
+ }
+ return NULL;
+}
+
+unsigned ElfCoreDump::GetProgramHeaderCount() const {
+ const Ehdr* header = GetHeader();
+ return header ? header->e_phnum : 0;
+}
+
+bool ElfCoreDump::CopyData(void* buffer, Addr virtual_address, size_t length) {
+ for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) {
+ const Phdr* program = GetProgramHeader(i);
+ if (program->p_type != PT_LOAD)
+ continue;
+
+ size_t offset_in_segment = virtual_address - program->p_vaddr;
+ if (virtual_address >= program->p_vaddr &&
+ offset_in_segment < program->p_filesz) {
+ const void* data =
+ content_.GetData(program->p_offset + offset_in_segment, length);
+ if (data) {
+ memcpy(buffer, data, length);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ElfCoreDump::Note ElfCoreDump::GetFirstNote() const {
+ MemoryRange note_content;
+ const Phdr* program_header = GetFirstProgramHeaderOfType(PT_NOTE);
+ if (program_header) {
+ note_content = content_.Subrange(program_header->p_offset,
+ program_header->p_filesz);
+ }
+ return Note(note_content);
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.h
new file mode 100644
index 000000000..d03c7a88d
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump.h
@@ -0,0 +1,148 @@
+// 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.
+
+// elf_core_dump.h: Define the google_breakpad::ElfCoreDump class, which
+// encapsulates an ELF core dump file mapped into memory.
+
+#ifndef COMMON_LINUX_ELF_CORE_DUMP_H_
+#define COMMON_LINUX_ELF_CORE_DUMP_H_
+
+#include <elf.h>
+#include <link.h>
+#include <stddef.h>
+
+#include "common/memory_range.h"
+
+namespace google_breakpad {
+
+// A class encapsulating an ELF core dump file mapped into memory, which
+// provides methods for accessing program headers and the note section.
+class ElfCoreDump {
+ public:
+ // ELF types based on the value of __WORDSIZE.
+ typedef ElfW(Ehdr) Ehdr;
+ typedef ElfW(Nhdr) Nhdr;
+ typedef ElfW(Phdr) Phdr;
+ typedef ElfW(Word) Word;
+ typedef ElfW(Addr) Addr;
+#if __WORDSIZE == 32
+ static const int kClass = ELFCLASS32;
+#elif __WORDSIZE == 64
+ static const int kClass = ELFCLASS64;
+#else
+#error "Unsupported __WORDSIZE for ElfCoreDump."
+#endif
+
+ // A class encapsulating the note content in a core dump, which provides
+ // methods for accessing the name and description of a note.
+ class Note {
+ public:
+ Note();
+
+ // Constructor that takes the note content from |content|.
+ explicit Note(const MemoryRange& content);
+
+ // Returns true if this note is valid, i,e. a note header is found in
+ // |content_|, or false otherwise.
+ bool IsValid() const;
+
+ // Returns the note header, or NULL if no note header is found in
+ // |content_|.
+ const Nhdr* GetHeader() const;
+
+ // Returns the note type, or 0 if no note header is found in |content_|.
+ Word GetType() const;
+
+ // Returns a memory range covering the note name, or an empty range
+ // if no valid note name is found in |content_|.
+ MemoryRange GetName() const;
+
+ // Returns a memory range covering the note description, or an empty
+ // range if no valid note description is found in |content_|.
+ MemoryRange GetDescription() const;
+
+ // Returns the note following this note, or an empty note if no valid
+ // note is found after this note.
+ Note GetNextNote() const;
+
+ private:
+ // Returns the size in bytes round up to the word alignment, specified
+ // for the note section, of a given size in bytes.
+ static size_t AlignedSize(size_t size);
+
+ // Note content.
+ MemoryRange content_;
+ };
+
+ ElfCoreDump();
+
+ // Constructor that takes the core dump content from |content|.
+ explicit ElfCoreDump(const MemoryRange& content);
+
+ // Sets the core dump content to |content|.
+ void SetContent(const MemoryRange& content);
+
+ // Returns true if a valid ELF header in the core dump, or false otherwise.
+ bool IsValid() const;
+
+ // Returns the ELF header in the core dump, or NULL if no ELF header
+ // is found in |content_|.
+ const Ehdr* GetHeader() const;
+
+ // Returns the |index|-th program header in the core dump, or NULL if no
+ // ELF header is found in |content_| or |index| is out of bounds.
+ const Phdr* GetProgramHeader(unsigned index) const;
+
+ // Returns the first program header of |type| in the core dump, or NULL if
+ // no ELF header is found in |content_| or no program header of |type| is
+ // found.
+ const Phdr* GetFirstProgramHeaderOfType(Word type) const;
+
+ // Returns the number of program headers in the core dump, or 0 if no
+ // ELF header is found in |content_|.
+ unsigned GetProgramHeaderCount() const;
+
+ // Copies |length| bytes of data starting at |virtual_address| in the core
+ // dump to |buffer|. |buffer| should be a valid pointer to a buffer of at
+ // least |length| bytes. Returns true if the data to be copied is found in
+ // the core dump, or false otherwise.
+ bool CopyData(void* buffer, Addr virtual_address, size_t length);
+
+ // Returns the first note found in the note section of the core dump, or
+ // an empty note if no note is found.
+ Note GetFirstNote() const;
+
+ private:
+ // Core dump content.
+ MemoryRange content_;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_ELF_CORE_DUMP_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump_unittest.cc
new file mode 100644
index 000000000..9b41dceee
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_core_dump_unittest.cc
@@ -0,0 +1,256 @@
+// 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.
+
+// elf_core_dump_unittest.cc: Unit tests for google_breakpad::ElfCoreDump.
+
+#include <sys/procfs.h>
+
+#include <set>
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "common/linux/elf_core_dump.h"
+#include "common/linux/memory_mapped_file.h"
+#include "common/tests/file_utils.h"
+#include "common/linux/tests/crash_generator.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::AutoTempDir;
+using google_breakpad::CrashGenerator;
+using google_breakpad::ElfCoreDump;
+using google_breakpad::MemoryMappedFile;
+using google_breakpad::MemoryRange;
+using google_breakpad::WriteFile;
+using std::set;
+
+TEST(ElfCoreDumpTest, DefaultConstructor) {
+ ElfCoreDump core;
+ EXPECT_FALSE(core.IsValid());
+ EXPECT_EQ(NULL, core.GetHeader());
+ EXPECT_EQ(0U, core.GetProgramHeaderCount());
+ EXPECT_EQ(NULL, core.GetProgramHeader(0));
+ EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD));
+ EXPECT_FALSE(core.GetFirstNote().IsValid());
+}
+
+TEST(ElfCoreDumpTest, TestElfHeader) {
+ ElfCoreDump::Ehdr header;
+ memset(&header, 0, sizeof(header));
+
+ AutoTempDir temp_dir;
+ string core_path = temp_dir.path() + "/core";
+ const char* core_file = core_path.c_str();
+ MemoryMappedFile mapped_core_file;
+ ElfCoreDump core;
+
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+ EXPECT_EQ(NULL, core.GetHeader());
+ EXPECT_EQ(0U, core.GetProgramHeaderCount());
+ EXPECT_EQ(NULL, core.GetProgramHeader(0));
+ EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD));
+ EXPECT_FALSE(core.GetFirstNote().IsValid());
+
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+
+ header.e_ident[0] = ELFMAG0;
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+
+ header.e_ident[1] = ELFMAG1;
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+
+ header.e_ident[2] = ELFMAG2;
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+
+ header.e_ident[3] = ELFMAG3;
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+
+ header.e_ident[4] = ElfCoreDump::kClass;
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+
+ header.e_version = EV_CURRENT;
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_FALSE(core.IsValid());
+
+ header.e_type = ET_CORE;
+ ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
+ ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
+ core.SetContent(mapped_core_file.content());
+ EXPECT_TRUE(core.IsValid());
+}
+
+TEST(ElfCoreDumpTest, ValidCoreFile) {
+ CrashGenerator crash_generator;
+ if (!crash_generator.HasDefaultCorePattern()) {
+ fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped "
+ "due to non-default core pattern");
+ return;
+ }
+
+ const unsigned kNumOfThreads = 3;
+ const unsigned kCrashThread = 1;
+ const int kCrashSignal = SIGABRT;
+ ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
+ kCrashSignal, NULL));
+ pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread);
+ set<pid_t> expected_thread_ids;
+ for (unsigned i = 0; i < kNumOfThreads; ++i) {
+ expected_thread_ids.insert(crash_generator.GetThreadId(i));
+ }
+
+#if defined(__ANDROID__)
+ struct stat st;
+ if (stat(crash_generator.GetCoreFilePath().c_str(), &st) != 0) {
+ fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped "
+ "due to no core file being generated");
+ return;
+ }
+#endif
+
+ MemoryMappedFile mapped_core_file;
+ ASSERT_TRUE(
+ mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str(), 0));
+
+ ElfCoreDump core;
+ core.SetContent(mapped_core_file.content());
+ EXPECT_TRUE(core.IsValid());
+
+ // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
+ // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
+ // Thread Name Type
+ // -------------------------------------------------------------------
+ // 1st thread CORE NT_PRSTATUS
+ // process-wide CORE NT_PRPSINFO
+ // process-wide CORE NT_AUXV
+ // 1st thread CORE NT_FPREGSET
+ // 1st thread LINUX NT_PRXFPREG
+ // 1st thread LINUX NT_386_TLS
+ //
+ // 2nd thread CORE NT_PRSTATUS
+ // 2nd thread CORE NT_FPREGSET
+ // 2nd thread LINUX NT_PRXFPREG
+ // 2nd thread LINUX NT_386_TLS
+ //
+ // 3rd thread CORE NT_PRSTATUS
+ // 3rd thread CORE NT_FPREGSET
+ // 3rd thread LINUX NT_PRXFPREG
+ // 3rd thread LINUX NT_386_TLS
+
+ size_t num_nt_prpsinfo = 0;
+ size_t num_nt_prstatus = 0;
+ size_t num_pr_fpvalid = 0;
+#if defined(__i386__) || defined(__x86_64__)
+ size_t num_nt_fpregset = 0;
+#endif
+#if defined(__i386__)
+ size_t num_nt_prxfpreg = 0;
+#endif
+ set<pid_t> actual_thread_ids;
+ ElfCoreDump::Note note = core.GetFirstNote();
+ while (note.IsValid()) {
+ MemoryRange name = note.GetName();
+ MemoryRange description = note.GetDescription();
+ EXPECT_FALSE(name.IsEmpty());
+ EXPECT_FALSE(description.IsEmpty());
+
+ switch (note.GetType()) {
+ case NT_PRPSINFO: {
+ EXPECT_TRUE(description.data() != NULL);
+ EXPECT_EQ(sizeof(elf_prpsinfo), description.length());
+ ++num_nt_prpsinfo;
+ break;
+ }
+ case NT_PRSTATUS: {
+ EXPECT_TRUE(description.data() != NULL);
+ EXPECT_EQ(sizeof(elf_prstatus), description.length());
+ const elf_prstatus* status = description.GetData<elf_prstatus>(0);
+ actual_thread_ids.insert(status->pr_pid);
+ if (num_nt_prstatus == 0) {
+ EXPECT_EQ(expected_crash_thread_id, status->pr_pid);
+ EXPECT_EQ(kCrashSignal, status->pr_info.si_signo);
+ }
+ ++num_nt_prstatus;
+ if (status->pr_fpvalid)
+ ++num_pr_fpvalid;
+ break;
+ }
+#if defined(__i386__) || defined(__x86_64__)
+ case NT_FPREGSET: {
+ EXPECT_TRUE(description.data() != NULL);
+ EXPECT_EQ(sizeof(user_fpregs_struct), description.length());
+ ++num_nt_fpregset;
+ break;
+ }
+#endif
+#if defined(__i386__)
+ case NT_PRXFPREG: {
+ EXPECT_TRUE(description.data() != NULL);
+ EXPECT_EQ(sizeof(user_fpxregs_struct), description.length());
+ ++num_nt_prxfpreg;
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+ note = note.GetNextNote();
+ }
+
+ EXPECT_TRUE(expected_thread_ids == actual_thread_ids);
+ EXPECT_EQ(1U, num_nt_prpsinfo);
+ EXPECT_EQ(kNumOfThreads, num_nt_prstatus);
+#if defined(__i386__) || defined(__x86_64__)
+ EXPECT_EQ(num_pr_fpvalid, num_nt_fpregset);
+#endif
+#if defined(__i386__)
+ EXPECT_EQ(num_pr_fpvalid, num_nt_prxfpreg);
+#endif
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_gnu_compat.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_gnu_compat.h
new file mode 100644
index 000000000..f870cbc7d
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_gnu_compat.h
@@ -0,0 +1,46 @@
+// -*- mode: C++ -*-
+
+// Copyright (c) 2013, 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: Lei Zhang <thestig@google.com>
+
+// elf_gnu_compat.h: #defines unique to glibc's elf.h.
+
+#ifndef COMMON_LINUX_ELF_GNU_COMPAT_H_
+#define COMMON_LINUX_ELF_GNU_COMPAT_H_
+
+#include <elf.h>
+
+// A note type on GNU systems corresponding to the .note.gnu.build-id section.
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+#endif // COMMON_LINUX_ELF_GNU_COMPAT_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.cc
new file mode 100644
index 000000000..562875e11
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.cc
@@ -0,0 +1,178 @@
+// -*- 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 <cxxabi.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(iterator->value);
+ ext->name = SymbolString(iterator->name_offset, strings);
+#if !defined(__ANDROID__) // Android NDK doesn't provide abi::__cxa_demangle.
+ int status = 0;
+ char* demangled =
+ abi::__cxa_demangle(ext->name.c_str(), NULL, NULL, &status);
+ if (demangled) {
+ if (status == 0)
+ ext->name = demangled;
+ free(demangled);
+ }
+#endif
+ module->AddExtern(ext);
+ }
+ ++iterator;
+ }
+ return true;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.h
new file mode 100644
index 000000000..2e7c09715
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module.h
@@ -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_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module_unittest.cc
new file mode 100644
index 000000000..8984449ab
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elf_symbols_to_module_unittest.cc
@@ -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"
+#include "common/using_std_string.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::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(&section_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));
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils-inl.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils-inl.h
new file mode 100644
index 000000000..e56b37a9f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils-inl.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2012, 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_ELFUTILS_INL_H__
+#define COMMON_LINUX_ELFUTILS_INL_H__
+
+#include "common/linux/linux_libc_support.h"
+#include "elfutils.h"
+
+namespace google_breakpad {
+
+template<typename ElfClass, typename T>
+const T* GetOffset(const typename ElfClass::Ehdr* elf_header,
+ typename ElfClass::Off offset) {
+ return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) +
+ offset);
+}
+
+template<typename ElfClass>
+const typename ElfClass::Shdr* FindElfSectionByName(
+ const char* name,
+ typename ElfClass::Word section_type,
+ const typename ElfClass::Shdr* sections,
+ const char* section_names,
+ const char* names_end,
+ int nsection) {
+ assert(name != NULL);
+ assert(sections != NULL);
+ assert(nsection > 0);
+
+ int name_len = my_strlen(name);
+ if (name_len == 0)
+ return NULL;
+
+ for (int i = 0; i < nsection; ++i) {
+ const char* section_name = section_names + sections[i].sh_name;
+ if (sections[i].sh_type == section_type &&
+ names_end - section_name >= name_len + 1 &&
+ my_strcmp(name, section_name) == 0) {
+ return sections + i;
+ }
+ }
+ return NULL;
+}
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_ELFUTILS_INL_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc
new file mode 100644
index 000000000..a79391c13
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.cc
@@ -0,0 +1,194 @@
+// Copyright (c) 2012, 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/elfutils.h"
+
+#include <assert.h>
+#include <string.h>
+
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/elfutils-inl.h"
+
+namespace google_breakpad {
+
+namespace {
+
+template<typename ElfClass>
+void FindElfClassSection(const char *elf_base,
+ const char *section_name,
+ typename ElfClass::Word section_type,
+ const void **section_start,
+ size_t *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);
+
+ const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
+ assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
+
+ const Shdr* sections =
+ GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
+ const Shdr* section_names = sections + elf_header->e_shstrndx;
+ const char* names =
+ GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
+ const char *names_end = names + section_names->sh_size;
+
+ const Shdr* section =
+ FindElfSectionByName<ElfClass>(section_name, section_type,
+ sections, names, names_end,
+ elf_header->e_shnum);
+
+ if (section != NULL && section->sh_size > 0) {
+ *section_start = elf_base + section->sh_offset;
+ *section_size = section->sh_size;
+ }
+}
+
+template<typename ElfClass>
+void FindElfClassSegment(const char *elf_base,
+ typename ElfClass::Word segment_type,
+ const void **segment_start,
+ size_t *segment_size) {
+ typedef typename ElfClass::Ehdr Ehdr;
+ typedef typename ElfClass::Phdr Phdr;
+
+ assert(elf_base);
+ assert(segment_start);
+ assert(segment_size);
+
+ assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0);
+
+ const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base);
+ assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
+
+ const Phdr* phdrs =
+ GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff);
+
+ for (int i = 0; i < elf_header->e_phnum; ++i) {
+ if (phdrs[i].p_type == segment_type) {
+ *segment_start = elf_base + phdrs[i].p_offset;
+ *segment_size = phdrs[i].p_filesz;
+ return;
+ }
+ }
+}
+
+} // namespace
+
+bool IsValidElf(const void* elf_base) {
+ return my_strncmp(reinterpret_cast<const char*>(elf_base),
+ ELFMAG, SELFMAG) == 0;
+}
+
+int ElfClass(const void* elf_base) {
+ const ElfW(Ehdr)* elf_header =
+ reinterpret_cast<const ElfW(Ehdr)*>(elf_base);
+
+ return elf_header->e_ident[EI_CLASS];
+}
+
+bool FindElfSection(const void *elf_mapped_base,
+ const char *section_name,
+ uint32_t section_type,
+ const void **section_start,
+ size_t *section_size,
+ int *elfclass) {
+ assert(elf_mapped_base);
+ assert(section_start);
+ assert(section_size);
+
+ *section_start = NULL;
+ *section_size = 0;
+
+ if (!IsValidElf(elf_mapped_base))
+ return false;
+
+ int cls = ElfClass(elf_mapped_base);
+ if (elfclass) {
+ *elfclass = cls;
+ }
+
+ const char* elf_base =
+ static_cast<const char*>(elf_mapped_base);
+
+ if (cls == ELFCLASS32) {
+ FindElfClassSection<ElfClass32>(elf_base, section_name, section_type,
+ section_start, section_size);
+ return *section_start != NULL;
+ } else if (cls == ELFCLASS64) {
+ FindElfClassSection<ElfClass64>(elf_base, section_name, section_type,
+ section_start, section_size);
+ return *section_start != NULL;
+ }
+
+ return false;
+}
+
+bool FindElfSegment(const void *elf_mapped_base,
+ uint32_t segment_type,
+ const void **segment_start,
+ size_t *segment_size,
+ int *elfclass) {
+ assert(elf_mapped_base);
+ assert(segment_start);
+ assert(segment_size);
+
+ *segment_start = NULL;
+ *segment_size = 0;
+
+ if (!IsValidElf(elf_mapped_base))
+ return false;
+
+ int cls = ElfClass(elf_mapped_base);
+ if (elfclass) {
+ *elfclass = cls;
+ }
+
+ const char* elf_base =
+ static_cast<const char*>(elf_mapped_base);
+
+ if (cls == ELFCLASS32) {
+ FindElfClassSegment<ElfClass32>(elf_base, segment_type,
+ segment_start, segment_size);
+ return *segment_start != NULL;
+ } else if (cls == ELFCLASS64) {
+ FindElfClassSegment<ElfClass64>(elf_base, segment_type,
+ segment_start, segment_size);
+ return *segment_start != NULL;
+ }
+
+ return false;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.h b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.h
new file mode 100644
index 000000000..f34ba8314
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/elfutils.h
@@ -0,0 +1,126 @@
+// Copyright (c) 2012, 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.
+//
+// elfutils.h: Utilities for dealing with ELF files.
+//
+
+#ifndef COMMON_LINUX_ELFUTILS_H_
+#define COMMON_LINUX_ELFUTILS_H_
+
+#include <elf.h>
+#include <link.h>
+#include <stdint.h>
+
+namespace google_breakpad {
+
+// Traits classes so consumers can write templatized code to deal
+// with specific ELF bits.
+struct ElfClass32 {
+ typedef Elf32_Addr Addr;
+ typedef Elf32_Ehdr Ehdr;
+ typedef Elf32_Nhdr Nhdr;
+ typedef Elf32_Phdr Phdr;
+ typedef Elf32_Shdr Shdr;
+ typedef Elf32_Half Half;
+ typedef Elf32_Off Off;
+ typedef Elf32_Sym Sym;
+ typedef Elf32_Word Word;
+
+ static const int kClass = ELFCLASS32;
+ static const uint16_t kMachine = EM_386;
+ static const size_t kAddrSize = sizeof(Elf32_Addr);
+ static constexpr const char* kMachineName = "x86";
+};
+
+struct ElfClass64 {
+ typedef Elf64_Addr Addr;
+ typedef Elf64_Ehdr Ehdr;
+ typedef Elf64_Nhdr Nhdr;
+ typedef Elf64_Phdr Phdr;
+ typedef Elf64_Shdr Shdr;
+ typedef Elf64_Half Half;
+ typedef Elf64_Off Off;
+ typedef Elf64_Sym Sym;
+ typedef Elf64_Word Word;
+
+ static const int kClass = ELFCLASS64;
+ static const uint16_t kMachine = EM_X86_64;
+ static const size_t kAddrSize = sizeof(Elf64_Addr);
+ static constexpr const char* kMachineName = "x86_64";
+};
+
+bool IsValidElf(const void* elf_header);
+int ElfClass(const void* elf_base);
+
+// 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.
+bool FindElfSection(const void *elf_mapped_base,
+ const char *section_name,
+ uint32_t section_type,
+ const void **section_start,
+ size_t *section_size,
+ int *elfclass);
+
+// Internal helper method, exposed for convenience for callers
+// that already have more info.
+template<typename ElfClass>
+const typename ElfClass::Shdr*
+FindElfSectionByName(const char* name,
+ typename ElfClass::Word section_type,
+ const typename ElfClass::Shdr* sections,
+ const char* section_names,
+ const char* names_end,
+ int nsection);
+
+// Attempt to find the first segment of type |segment_type| in the ELF
+// binary data at |elf_mapped_base|. On success, returns true and sets
+// |*segment_start| to point to the start of the segment data, and
+// and |*segment_size| to the size of the segment's data. If |elfclass|
+// is not NULL, set |*elfclass| to the ELF file class.
+bool FindElfSegment(const void *elf_mapped_base,
+ uint32_t segment_type,
+ const void **segment_start,
+ size_t *segment_size,
+ int *elfclass);
+
+// Convert an offset from an Elf header into a pointer to the mapped
+// address in the current process. Takes an extra template parameter
+// to specify the return type to avoid having to dynamic_cast the
+// result.
+template<typename ElfClass, typename T>
+const T*
+GetOffset(const typename ElfClass::Ehdr* elf_header,
+ typename ElfClass::Off offset);
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_ELFUTILS_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
new file mode 100644
index 000000000..311e03020
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc
@@ -0,0 +1,202 @@
+// 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 <string.h>
+
+#include <algorithm>
+#include <string>
+
+#include "common/linux/elf_gnu_compat.h"
+#include "common/linux/elfutils.h"
+#include "common/linux/linux_libc_support.h"
+#include "common/linux/memory_mapped_file.h"
+#include "common/using_std_string.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// Used in a few places for backwards-compatibility.
+const size_t kMDGUIDSize = sizeof(MDGUID);
+
+FileID::FileID(const char* path) : path_(path) {}
+
+// ELF note name and desc are 32-bits word padded.
+#define NOTE_PADDING(a) ((a + 3) & ~3)
+
+// These 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 bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
+ wasteful_vector<uint8_t>& identifier) {
+ typedef typename ElfClass::Nhdr Nhdr;
+
+ const void* section_end = reinterpret_cast<const char*>(section) + length;
+ const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
+ while (reinterpret_cast<const void *>(note_header) < section_end) {
+ if (note_header->n_type == NT_GNU_BUILD_ID)
+ break;
+ note_header = reinterpret_cast<const Nhdr*>(
+ reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
+ NOTE_PADDING(note_header->n_namesz) +
+ NOTE_PADDING(note_header->n_descsz));
+ }
+ if (reinterpret_cast<const void *>(note_header) >= section_end ||
+ note_header->n_descsz == 0) {
+ return false;
+ }
+
+ const uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) +
+ sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
+ identifier.insert(identifier.end(),
+ build_id,
+ build_id + note_header->n_descsz);
+
+ return true;
+}
+
+// Attempt to locate a .note.gnu.build-id section in an ELF binary
+// and copy it into |identifier|.
+static bool FindElfBuildIDNote(const void* elf_mapped_base,
+ wasteful_vector<uint8_t>& identifier) {
+ void* note_section;
+ size_t note_size;
+ int elfclass;
+ if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
+ (const void**)&note_section, &note_size, &elfclass) ||
+ note_size == 0) &&
+ (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
+ (const void**)&note_section, &note_size, &elfclass) ||
+ note_size == 0)) {
+ return false;
+ }
+
+ if (elfclass == ELFCLASS32) {
+ return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
+ identifier);
+ } else if (elfclass == ELFCLASS64) {
+ return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size,
+ 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,
+ wasteful_vector<uint8_t>& identifier) {
+ identifier.resize(kMDGUIDSize);
+
+ void* text_section;
+ size_t text_size;
+ if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
+ (const void**)&text_section, &text_size, NULL) ||
+ text_size == 0) {
+ return false;
+ }
+
+ // Only provide |kMDGUIDSize| bytes to keep identifiers produced by this
+ // function backwards-compatible.
+ my_memset(&identifier[0], 0, kMDGUIDSize);
+ const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
+ const uint8_t* ptr_end = ptr + std::min(text_size, static_cast<size_t>(4096));
+ while (ptr < ptr_end) {
+ for (unsigned i = 0; i < kMDGUIDSize; i++)
+ identifier[i] ^= ptr[i];
+ ptr += kMDGUIDSize;
+ }
+ return true;
+}
+
+// static
+bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
+ wasteful_vector<uint8_t>& identifier) {
+ // 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(wasteful_vector<uint8_t>& identifier) {
+ MemoryMappedFile mapped_file(path_.c_str(), 0);
+ if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
+ return false;
+
+ return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
+}
+
+// These three functions are not ever called in an unsafe context, so it's OK
+// to allocate memory and use libc.
+static string bytes_to_hex_string(const uint8_t* bytes, size_t count) {
+ string result;
+ for (unsigned int idx = 0; idx < count; ++idx) {
+ char buf[3];
+ snprintf(buf, sizeof(buf), "%02X", bytes[idx]);
+ result.append(buf);
+ }
+ return result;
+}
+
+// static
+string FileID::ConvertIdentifierToUUIDString(
+ const wasteful_vector<uint8_t>& identifier) {
+ uint8_t identifier_swapped[kMDGUIDSize] = { 0 };
+
+ // Endian-ness swap to match dump processor expectation.
+ memcpy(identifier_swapped, &identifier[0],
+ std::min(kMDGUIDSize, identifier.size()));
+ 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);
+
+ return bytes_to_hex_string(identifier_swapped, kMDGUIDSize);
+}
+
+// static
+string FileID::ConvertIdentifierToString(
+ const wasteful_vector<uint8_t>& identifier) {
+ return bytes_to_hex_string(&identifier[0], identifier.size());
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h
new file mode 100644
index 000000000..a1d2fd6ed
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h
@@ -0,0 +1,87 @@
+// 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 <string>
+
+#include "common/linux/guid_creator.h"
+#include "common/memory.h"
+
+namespace google_breakpad {
+
+// GNU binutils' ld defaults to 'sha1', which is 160 bits == 20 bytes,
+// so this is enough to fit that, which most binaries will use.
+// This is just a sensible default for auto_wasteful_vector so most
+// callers can get away with stack allocation.
+static const size_t kDefaultBuildIdSize = 20;
+
+class FileID {
+ public:
+ explicit FileID(const char* path);
+ ~FileID() {}
+
+ // Load the identifier for the elf file path specified in the constructor into
+ // |identifier|.
+ //
+ // 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(wasteful_vector<uint8_t>& identifier);
+
+ // Load the identifier for the elf file mapped into memory at |base| into
+ // |identifier|. Return false if the identifier could not be created for this
+ // file.
+ static bool ElfFileIdentifierFromMappedFile(
+ const void* base,
+ wasteful_vector<uint8_t>& identifier);
+
+ // Convert the |identifier| data to a string. The string will
+ // be formatted as a UUID in all uppercase without dashes.
+ // (e.g., 22F065BBFC9C49F780FE26A7CEBD7BCE).
+ static std::string ConvertIdentifierToUUIDString(
+ const wasteful_vector<uint8_t>& identifier);
+
+ // Convert the entire |identifier| data to a hex string.
+ static std::string ConvertIdentifierToString(
+ const wasteful_vector<uint8_t>& identifier);
+
+ private:
+ // Storage for the path specified
+ std::string path_;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_FILE_ID_H__
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc
new file mode 100644
index 000000000..3a8193034
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc
@@ -0,0 +1,338 @@
+// 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 <string>
+#include <vector>
+
+#include "common/linux/elf_gnu_compat.h"
+#include "common/linux/elfutils.h"
+#include "common/linux/file_id.h"
+#include "common/linux/safe_readlink.h"
+#include "common/linux/synth_elf.h"
+#include "common/test_assembler.h"
+#include "common/tests/auto_tempdir.h"
+#include "common/using_std_string.h"
+#include "breakpad_googletest_includes.h"
+
+using namespace google_breakpad;
+using google_breakpad::synth_elf::ELF;
+using google_breakpad::synth_elf::Notes;
+using google_breakpad::test_assembler::kLittleEndian;
+using google_breakpad::test_assembler::Section;
+using std::vector;
+using ::testing::Types;
+
+namespace {
+
+// Simply calling Section::Append(size, byte) produces a uninteresting pattern
+// that tends to get hashed to 0000...0000. This populates the section with
+// data to produce better hashes.
+void PopulateSection(Section* section, int size, int prime_number) {
+ for (int i = 0; i < size; i++)
+ section->Append(1, (i % prime_number) % 256);
+}
+
+typedef wasteful_vector<uint8_t> id_vector;
+
+} // namespace
+
+#ifndef __ANDROID__
+// This test is disabled on Android: It will always fail, since there is no
+// 'strip' binary installed on test devices.
+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];
+ ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
+
+ // copy our binary to a temp file, and strip it
+ AutoTempDir temp_dir;
+ string templ = temp_dir.path() + "/file-id-unittest";
+ char cmdline[4096];
+ sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str());
+ ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
+ sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str());
+ ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
+ sprintf(cmdline, "strip \"%s\"", templ.c_str());
+ ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline;
+
+ PageAllocator allocator;
+ id_vector identifier1(&allocator, kDefaultBuildIdSize);
+ id_vector identifier2(&allocator, kDefaultBuildIdSize);
+
+ FileID fileid1(exe_name);
+ EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1));
+ FileID fileid2(templ.c_str());
+ EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2));
+
+ string identifier_string1 =
+ FileID::ConvertIdentifierToUUIDString(identifier1);
+ string identifier_string2 =
+ FileID::ConvertIdentifierToUUIDString(identifier2);
+ EXPECT_EQ(identifier_string1, identifier_string2);
+}
+#endif // !__ANDROID__
+
+template<typename ElfClass>
+class FileIDTest : public testing::Test {
+public:
+ void GetElfContents(ELF& elf) {
+ string contents;
+ ASSERT_TRUE(elf.GetContents(&contents));
+ ASSERT_LT(0U, contents.size());
+
+ elfdata_v.clear();
+ elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end());
+ elfdata = &elfdata_v[0];
+ }
+
+ id_vector make_vector() {
+ return id_vector(&allocator, kDefaultBuildIdSize);
+ }
+
+ template<size_t N>
+ string get_file_id(const uint8_t (&data)[N]) {
+ id_vector expected_identifier(make_vector());
+ expected_identifier.insert(expected_identifier.end(),
+ &data[0],
+ data + N);
+ return FileID::ConvertIdentifierToUUIDString(expected_identifier);
+ }
+
+ vector<uint8_t> elfdata_v;
+ uint8_t* elfdata;
+ PageAllocator allocator;
+};
+
+typedef Types<ElfClass32, ElfClass64> ElfClasses;
+
+TYPED_TEST_CASE(FileIDTest, ElfClasses);
+
+TYPED_TEST(FileIDTest, ElfClass) {
+ const char expected_identifier_string[] =
+ "80808080808000000000008080808080";
+ const size_t kTextSectionSize = 128;
+
+ ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
+ Section text(kLittleEndian);
+ for (size_t i = 0; i < kTextSectionSize; ++i) {
+ text.D8(i * 3);
+ }
+ elf.AddSection(".text", text, SHT_PROGBITS);
+ elf.Finish();
+ this->GetElfContents(elf);
+
+ id_vector identifier(this->make_vector());
+ EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
+ identifier));
+
+ string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
+ EXPECT_EQ(expected_identifier_string, identifier_string);
+}
+
+TYPED_TEST(FileIDTest, BuildID) {
+ const uint8_t kExpectedIdentifierBytes[] =
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13};
+ const string expected_identifier_string =
+ this->get_file_id(kExpectedIdentifierBytes);
+
+ ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
+ Section text(kLittleEndian);
+ text.Append(4096, 0);
+ elf.AddSection(".text", text, SHT_PROGBITS);
+ Notes notes(kLittleEndian);
+ notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
+ sizeof(kExpectedIdentifierBytes));
+ elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
+ elf.Finish();
+ this->GetElfContents(elf);
+
+ id_vector identifier(this->make_vector());
+ EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
+ identifier));
+ EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
+
+ string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
+ EXPECT_EQ(expected_identifier_string, identifier_string);
+}
+
+// Test that a build id note with fewer bytes than usual is handled.
+TYPED_TEST(FileIDTest, BuildIDShort) {
+ const uint8_t kExpectedIdentifierBytes[] =
+ {0x00, 0x01, 0x02, 0x03};
+ const string expected_identifier_string =
+ this->get_file_id(kExpectedIdentifierBytes);
+
+ ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
+ Section text(kLittleEndian);
+ text.Append(4096, 0);
+ elf.AddSection(".text", text, SHT_PROGBITS);
+ Notes notes(kLittleEndian);
+ notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
+ sizeof(kExpectedIdentifierBytes));
+ elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
+ elf.Finish();
+ this->GetElfContents(elf);
+
+ id_vector identifier(this->make_vector());
+ EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
+ identifier));
+ EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
+
+ string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
+ EXPECT_EQ(expected_identifier_string, identifier_string);
+}
+
+// Test that a build id note with more bytes than usual is handled.
+TYPED_TEST(FileIDTest, BuildIDLong) {
+ const uint8_t kExpectedIdentifierBytes[] =
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
+ const string expected_identifier_string =
+ this->get_file_id(kExpectedIdentifierBytes);
+
+ ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
+ Section text(kLittleEndian);
+ text.Append(4096, 0);
+ elf.AddSection(".text", text, SHT_PROGBITS);
+ Notes notes(kLittleEndian);
+ notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
+ sizeof(kExpectedIdentifierBytes));
+ elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE);
+ elf.Finish();
+ this->GetElfContents(elf);
+
+ id_vector identifier(this->make_vector());
+ EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
+ identifier));
+ EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
+
+ string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
+ EXPECT_EQ(expected_identifier_string, identifier_string);
+}
+
+TYPED_TEST(FileIDTest, BuildIDPH) {
+ const uint8_t kExpectedIdentifierBytes[] =
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13};
+ const string expected_identifier_string =
+ this->get_file_id(kExpectedIdentifierBytes);
+
+ ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
+ Section text(kLittleEndian);
+ text.Append(4096, 0);
+ elf.AddSection(".text", text, SHT_PROGBITS);
+ Notes notes(kLittleEndian);
+ notes.AddNote(0, "Linux",
+ reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4);
+ notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifierBytes,
+ sizeof(kExpectedIdentifierBytes));
+ int note_idx = elf.AddSection(".note", notes, SHT_NOTE);
+ elf.AddSegment(note_idx, note_idx, PT_NOTE);
+ elf.Finish();
+ this->GetElfContents(elf);
+
+ id_vector identifier(this->make_vector());
+ EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
+ identifier));
+ EXPECT_EQ(sizeof(kExpectedIdentifierBytes), identifier.size());
+
+ string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
+ EXPECT_EQ(expected_identifier_string, identifier_string);
+}
+
+// Test to make sure two files with different text sections produce
+// different hashes when not using a build id.
+TYPED_TEST(FileIDTest, UniqueHashes) {
+ {
+ ELF elf1(EM_386, TypeParam::kClass, kLittleEndian);
+ Section foo_1(kLittleEndian);
+ PopulateSection(&foo_1, 32, 5);
+ elf1.AddSection(".foo", foo_1, SHT_PROGBITS);
+ Section text_1(kLittleEndian);
+ PopulateSection(&text_1, 4096, 17);
+ elf1.AddSection(".text", text_1, SHT_PROGBITS);
+ elf1.Finish();
+ this->GetElfContents(elf1);
+ }
+
+ id_vector identifier_1(this->make_vector());
+ EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
+ identifier_1));
+ string identifier_string_1 =
+ FileID::ConvertIdentifierToUUIDString(identifier_1);
+
+ {
+ ELF elf2(EM_386, TypeParam::kClass, kLittleEndian);
+ Section text_2(kLittleEndian);
+ Section foo_2(kLittleEndian);
+ PopulateSection(&foo_2, 32, 5);
+ elf2.AddSection(".foo", foo_2, SHT_PROGBITS);
+ PopulateSection(&text_2, 4096, 31);
+ elf2.AddSection(".text", text_2, SHT_PROGBITS);
+ elf2.Finish();
+ this->GetElfContents(elf2);
+ }
+
+ id_vector identifier_2(this->make_vector());
+ EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata,
+ identifier_2));
+ string identifier_string_2 =
+ FileID::ConvertIdentifierToUUIDString(identifier_2);
+
+ EXPECT_NE(identifier_string_1, identifier_string_2);
+}
+
+TYPED_TEST(FileIDTest, ConvertIdentifierToString) {
+ const uint8_t kIdentifierBytes[] =
+ {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
+ const char* kExpected =
+ "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F";
+
+ id_vector identifier(this->make_vector());
+ identifier.insert(identifier.end(),
+ kIdentifierBytes,
+ kIdentifierBytes + sizeof(kIdentifierBytes));
+ ASSERT_EQ(kExpected,
+ FileID::ConvertIdentifierToString(identifier));
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc
new file mode 100644
index 000000000..6d86fb369
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc
@@ -0,0 +1,202 @@
+// 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 <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <iostream>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+GoogleCrashdumpUploader::GoogleCrashdumpUploader(const string& product,
+ const string& version,
+ const string& guid,
+ const string& ptime,
+ const string& ctime,
+ const string& email,
+ const string& comments,
+ const string& minidump_pathname,
+ const string& crash_server,
+ const string& proxy_host,
+ const 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 string& product,
+ const string& version,
+ const string& guid,
+ const string& ptime,
+ const string& ctime,
+ const string& email,
+ const string& comments,
+ const string& minidump_pathname,
+ const string& crash_server,
+ const string& proxy_host,
+ const 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 string& product,
+ const string& version,
+ const string& guid,
+ const string& ptime,
+ const string& ctime,
+ const string& email,
+ const string& comments,
+ const string& minidump_pathname,
+ const string& crash_server,
+ const string& proxy_host,
+ const string& proxy_userpassword,
+ LibcurlWrapper* http_layer) {
+ product_ = product;
+ version_ = version;
+ guid_ = guid;
+ ptime_ = ptime;
+ ctime_ = ctime;
+ email_ = email;
+ comments_ = comments;
+ http_layer_.reset(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() {
+ 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(int* http_status_code,
+ string* http_response_header,
+ string* http_response_body) {
+ 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_,
+ http_status_code,
+ http_response_header,
+ http_response_body);
+}
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h
new file mode 100644
index 000000000..a2d0575b5
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h
@@ -0,0 +1,107 @@
+// 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.
+
+
+#ifndef COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_
+#define COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_
+
+#include <string>
+#include <map>
+
+#include "common/linux/libcurl_wrapper.h"
+#include "common/scoped_ptr.h"
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+class GoogleCrashdumpUploader {
+ public:
+ GoogleCrashdumpUploader(const string& product,
+ const string& version,
+ const string& guid,
+ const string& ptime,
+ const string& ctime,
+ const string& email,
+ const string& comments,
+ const string& minidump_pathname,
+ const string& crash_server,
+ const string& proxy_host,
+ const string& proxy_userpassword);
+
+ GoogleCrashdumpUploader(const string& product,
+ const string& version,
+ const string& guid,
+ const string& ptime,
+ const string& ctime,
+ const string& email,
+ const string& comments,
+ const string& minidump_pathname,
+ const string& crash_server,
+ const string& proxy_host,
+ const string& proxy_userpassword,
+ LibcurlWrapper* http_layer);
+
+ void Init(const string& product,
+ const string& version,
+ const string& guid,
+ const string& ptime,
+ const string& ctime,
+ const string& email,
+ const string& comments,
+ const string& minidump_pathname,
+ const string& crash_server,
+ const string& proxy_host,
+ const string& proxy_userpassword,
+ LibcurlWrapper* http_layer);
+ bool Upload(int* http_status_code,
+ string* http_response_header,
+ string* http_response_body);
+
+ private:
+ bool CheckRequiredParametersArePresent();
+
+ scoped_ptr<LibcurlWrapper> http_layer_;
+ string product_;
+ string version_;
+ string guid_;
+ string ptime_;
+ string ctime_;
+ string email_;
+ string comments_;
+ string minidump_pathname_;
+
+ string crash_server_;
+ string proxy_host_;
+ string proxy_userpassword_;
+
+ std::map<string, string> parameters_;
+};
+}
+
+#endif // COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc
new file mode 100644
index 000000000..e94c5d62a
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc
@@ -0,0 +1,170 @@
+// 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 <string>
+
+#include "common/linux/google_crashdump_uploader.h"
+#include "breakpad_googletest_includes.h"
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+using ::testing::Return;
+using ::testing::_;
+
+class MockLibcurlWrapper : public LibcurlWrapper {
+ public:
+ MOCK_METHOD0(Init, bool());
+ MOCK_METHOD2(SetProxy, bool(const string& proxy_host,
+ const string& proxy_userpwd));
+ MOCK_METHOD2(AddFile, bool(const string& upload_file_path,
+ const string& basename));
+ MOCK_METHOD5(SendRequest,
+ bool(const string& url,
+ const std::map<string, string>& parameters,
+ int* http_status_code,
+ string* http_header_data,
+ string* http_response_data));
+};
+
+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(NULL, NULL, NULL));
+}
+
+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(NULL, NULL, NULL));
+}
+
+
+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(NULL, NULL, NULL));
+}
+
+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(NULL, NULL, NULL));
+
+ // Test with empty product version.
+ GoogleCrashdumpUploader uploader1("product",
+ "",
+ "AAA-BBB",
+ "",
+ "",
+ "",
+ "",
+ "/tmp/foo.dmp",
+ "",
+ "",
+ "");
+
+ ASSERT_FALSE(uploader1.Upload(NULL, NULL, NULL));
+
+ // Test with empty client GUID.
+ GoogleCrashdumpUploader uploader2("product",
+ "1.0",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "/tmp/foo.dmp",
+ "",
+ "",
+ "");
+ ASSERT_FALSE(uploader2.Upload(NULL, NULL, NULL));
+}
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc
new file mode 100644
index 000000000..bfb308ee2
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc
@@ -0,0 +1,104 @@
+// 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 <pthread.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:
+ static uint32_t BytesToUInt32(const uint8_t bytes[]) {
+ return ((uint32_t) bytes[0]
+ | ((uint32_t) bytes[1] << 8)
+ | ((uint32_t) bytes[2] << 16)
+ | ((uint32_t) bytes[3] << 24));
+ }
+
+ static void UInt32ToBytes(uint8_t bytes[], uint32_t n) {
+ bytes[0] = n & 0xff;
+ bytes[1] = (n >> 8) & 0xff;
+ bytes[2] = (n >> 16) & 0xff;
+ bytes[3] = (n >> 24) & 0xff;
+ }
+
+ static bool CreateGUID(GUID *guid) {
+ InitOnce();
+ guid->data1 = random();
+ guid->data2 = (uint16_t)(random());
+ guid->data3 = (uint16_t)(random());
+ UInt32ToBytes(&guid->data4[0], random());
+ UInt32ToBytes(&guid->data4[4], random());
+ return true;
+ }
+
+ private:
+ static void InitOnce() {
+ pthread_once(&once_control, &InitOnceImpl);
+ }
+
+ static void InitOnceImpl() {
+ srandom(time(NULL));
+ }
+
+ static pthread_once_t once_control;
+};
+
+pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT;
+
+bool CreateGUID(GUID *guid) {
+ return GUIDGenerator::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;
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.h b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.h
new file mode 100644
index 000000000..c86d856c4
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.h
@@ -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
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc
new file mode 100644
index 000000000..702526af7
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.cc
@@ -0,0 +1,230 @@
+// 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;
+
+ string *response = reinterpret_cast<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> &parameters,
+ const map<string, string> &files,
+ const string &proxy,
+ const string &proxy_user_pwd,
+ const string &ca_certificate_file,
+ string *response_body,
+ long *response_code,
+ string *error_description) {
+ if (response_code != NULL)
+ *response_code = 0;
+
+ if (!CheckParameters(parameters))
+ return false;
+
+ // We may have been linked statically; if curl_easy_init is in the
+ // current binary, no need to search for a dynamic version.
+ void* curl_lib = dlopen(NULL, RTLD_NOW);
+ if (!CheckCurlLib(curl_lib)) {
+ fprintf(stderr,
+ "Failed to open curl lib from binary, use libcurl.so instead\n");
+ dlerror(); // Clear dlerror before attempting to open libraries.
+ dlclose(curl_lib);
+ curl_lib = NULL;
+ }
+ if (!curl_lib) {
+ 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);
+ // Support multithread by disabling timeout handling, would get SIGSEGV with
+ // Curl_resolv_timeout in stack trace otherwise.
+ // See https://curl.haxx.se/libcurl/c/threadsafe.html
+ (*curl_easy_setopt)(curl, CURLOPT_NOSIGNAL, 1);
+ // 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 files.
+ for (iter = files.begin(); iter != files.end(); ++iter) {
+ (*curl_formadd)(&formpost, &lastptr,
+ CURLFORM_COPYNAME, iter->first.c_str(),
+ CURLFORM_FILE, iter->second.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);
+ if (response_code != NULL) {
+ CURLcode (*curl_easy_getinfo)(CURL *, CURLINFO, ...);
+ *(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo");
+ (*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code);
+ }
+ 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::CheckCurlLib(void* curl_lib) {
+ return curl_lib &&
+ dlsym(curl_lib, "curl_easy_init") &&
+ dlsym(curl_lib, "curl_easy_setopt");
+}
+
+// static
+bool HTTPUpload::CheckParameters(const map<string, string> &parameters) {
+ 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
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h
new file mode 100644
index 000000000..bc1d5d570
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/http_upload.h
@@ -0,0 +1,90 @@
+// 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>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+using std::map;
+
+class HTTPUpload {
+ public:
+ // Sends the given sets of parameters and files as a multipart POST
+ // request to the given URL.
+ // Each key in |files| is 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 response_code is non-NULL, it will be set to the HTTP response code
+ // received (or 0 if the request failed before getting an HTTP response).
+ // 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> &parameters,
+ const map<string, string> &files,
+ const string &proxy,
+ const string &proxy_user_pwd,
+ const string &ca_certificate_file,
+ string *response_body,
+ long *response_code,
+ 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> &parameters);
+
+ // Checks the curl_lib parameter points to a valid curl lib.
+ static bool CheckCurlLib(void* curl_lib);
+
+ // 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__
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/ignore_ret.h b/toolkit/crashreporter/google-breakpad/src/common/linux/ignore_ret.h
new file mode 100644
index 000000000..efd274c20
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/ignore_ret.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2012 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_IGNORE_RET_H_
+#define COMMON_LINUX_IGNORE_RET_H_
+
+// Some compilers are prone to warn about unused return values. In cases where
+// either a) the call cannot fail, or b) there is nothing that can be done when
+// the call fails, IGNORE_RET() can be used to mark the return code as ignored.
+// This avoids spurious compiler warnings.
+
+#define IGNORE_RET(x) do { if (x) {} } while (0)
+
+#endif // COMMON_LINUX_IGNORE_RET_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc
new file mode 100644
index 000000000..fd4e34cd8
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc
@@ -0,0 +1,241 @@
+// 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"
+#include "common/using_std_string.h"
+
+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;
+}
+
+LibcurlWrapper::~LibcurlWrapper() {}
+
+bool LibcurlWrapper::SetProxy(const string& proxy_host,
+ const 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 string& upload_file_path,
+ const 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;
+
+ string *response = reinterpret_cast<string *>(userp);
+ size_t real_size = size * nmemb;
+ response->append(reinterpret_cast<char *>(ptr), real_size);
+ return real_size;
+}
+
+bool LibcurlWrapper::SendRequest(const string& url,
+ const std::map<string, string>& parameters,
+ int* http_status_code,
+ string* http_header_data,
+ string* http_response_data) {
+ (*easy_setopt_)(curl_, CURLOPT_URL, url.c_str());
+ std::map<string, 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 (http_response_data != NULL) {
+ http_response_data->clear();
+ (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
+ (*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
+ reinterpret_cast<void *>(http_response_data));
+ }
+ if (http_header_data != NULL) {
+ http_header_data->clear();
+ (*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback);
+ (*easy_setopt_)(curl_, CURLOPT_HEADERDATA,
+ reinterpret_cast<void *>(http_header_data));
+ }
+
+ CURLcode err_code = CURLE_OK;
+ err_code = (*easy_perform_)(curl_);
+ easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
+ (dlsym(curl_lib_, "curl_easy_strerror"));
+
+ if (http_status_code != NULL) {
+ (*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code);
+ }
+
+#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(easy_getinfo_,
+ "curl_easy_getinfo",
+ CURLcode(*)(CURL *, CURLINFO info, ...));
+
+ 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;
+}
+
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h
new file mode 100644
index 000000000..25905ad8f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h
@@ -0,0 +1,93 @@
+// 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.
+
+#ifndef COMMON_LINUX_LIBCURL_WRAPPER_H_
+#define COMMON_LINUX_LIBCURL_WRAPPER_H_
+
+#include <string>
+#include <map>
+
+#include "common/using_std_string.h"
+#include "third_party/curl/curl.h"
+
+namespace google_breakpad {
+class LibcurlWrapper {
+ public:
+ LibcurlWrapper();
+ virtual ~LibcurlWrapper();
+ virtual bool Init();
+ virtual bool SetProxy(const string& proxy_host,
+ const string& proxy_userpwd);
+ virtual bool AddFile(const string& upload_file_path,
+ const string& basename);
+ virtual bool SendRequest(const string& url,
+ const std::map<string, string>& parameters,
+ int* http_status_code,
+ string* http_header_data,
+ string* http_response_data);
+ 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
+ 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 *);
+ CURLcode (*easy_getinfo_)(CURL *, CURLINFO info, ...);
+ void (*formfree_)(struct curl_httppost *);
+
+};
+}
+
+#endif // COMMON_LINUX_LIBCURL_WRAPPER_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc
new file mode 100644
index 000000000..08b0325e6
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.cc
@@ -0,0 +1,237 @@
+// Copyright (c) 2012, 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 source file provides replacements for libc functions that we need. If
+// we call the libc functions directly we risk crashing in the dynamic linker
+// as it tries to resolve uncached PLT entries.
+
+#include "common/linux/linux_libc_support.h"
+
+#include <stddef.h>
+
+extern "C" {
+
+size_t my_strlen(const char* s) {
+ size_t len = 0;
+ while (*s++) len++;
+ return len;
+}
+
+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++;
+ }
+}
+
+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.
+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 unsigned integer when expressed in base 10.
+unsigned my_uint_len(uintmax_t i) {
+ if (!i)
+ return 1;
+
+ int len = 0;
+ while (i) {
+ len++;
+ i /= 10;
+ }
+
+ return len;
+}
+
+// Convert an unsigned 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_uint_len| to get the
+// required length.
+// i: the unsigned integer to serialise.
+// i_len: the length of the integer in base 10 (see |my_uint_len|).
+void my_uitos(char* output, uintmax_t i, unsigned i_len) {
+ for (unsigned index = i_len; index; --index, i /= 10)
+ output[index - 1] = '0' + (i % 10);
+}
+
+const char* my_strchr(const char* haystack, char needle) {
+ while (*haystack && *haystack != needle)
+ haystack++;
+ if (*haystack == needle)
+ return haystack;
+ return (const char*) 0;
+}
+
+const char* my_strrchr(const char* haystack, char needle) {
+ const char* ret = NULL;
+ while (*haystack) {
+ if (*haystack == needle)
+ ret = haystack;
+ haystack++;
+ }
+ return ret;
+}
+
+void* my_memchr(const void* src, int needle, size_t src_len) {
+ const unsigned char* p = (const unsigned char*)src;
+ const unsigned char* p_end = p + src_len;
+ for (; p < p_end; ++p) {
+ if (*p == needle)
+ return (void*)p;
+ }
+ return NULL;
+}
+
+// Read a hex value
+// result: (output) the resulting value
+// s: a string
+// Returns a pointer to the first invalid charactor.
+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;
+}
+
+const char* my_read_decimal_ptr(uintptr_t* result, const char* s) {
+ uintptr_t r = 0;
+
+ for (;; ++s) {
+ if (*s >= '0' && *s <= '9') {
+ r *= 10;
+ r += *s - '0';
+ } else {
+ break;
+ }
+ }
+ *result = r;
+ return s;
+}
+
+void my_memset(void* ip, char c, size_t len) {
+ char* p = (char *) ip;
+ while (len--)
+ *p++ = c;
+}
+
+size_t my_strlcpy(char* s1, const char* s2, size_t len) {
+ size_t pos1 = 0;
+ size_t pos2 = 0;
+
+ while (s2[pos2] != '\0') {
+ if (pos1 + 1 < len) {
+ s1[pos1] = s2[pos2];
+ pos1++;
+ }
+ pos2++;
+ }
+ if (len > 0)
+ s1[pos1] = '\0';
+
+ return pos2;
+}
+
+size_t my_strlcat(char* s1, const char* s2, size_t len) {
+ size_t pos1 = 0;
+
+ while (pos1 < len && s1[pos1] != '\0')
+ pos1++;
+
+ if (pos1 == len)
+ return pos1;
+
+ return pos1 + my_strlcpy(s1 + pos1, s2, len - pos1);
+}
+
+int my_isspace(int ch) {
+ // Matches the C locale.
+ const char spaces[] = " \t\f\n\r\t\v";
+ for (size_t i = 0; i < sizeof(spaces); i++) {
+ if (ch == spaces[i])
+ return 1;
+ }
+ return 0;
+}
+
+} // extern "C"
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h
new file mode 100644
index 000000000..ec5a8d6b6
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h
@@ -0,0 +1,96 @@
+// 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" {
+
+extern size_t my_strlen(const char* s);
+
+extern int my_strcmp(const char* a, const char* b);
+
+extern int my_strncmp(const char* a, const char* b, size_t len);
+
+// Parse a non-negative integer.
+// result: (output) the resulting non-negative integer
+// s: a NUL terminated string
+// Return true iff successful.
+extern bool my_strtoui(int* result, const char* s);
+
+// Return the length of the given unsigned integer when expressed in base 10.
+extern unsigned my_uint_len(uintmax_t i);
+
+// Convert an unsigned 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_uint_len| to get the
+// required length.
+// i: the unsigned integer to serialise.
+// i_len: the length of the integer in base 10 (see |my_uint_len|).
+extern void my_uitos(char* output, uintmax_t i, unsigned i_len);
+
+extern const char* my_strchr(const char* haystack, char needle);
+
+extern const char* my_strrchr(const char* haystack, char needle);
+
+// Read a hex value
+// result: (output) the resulting value
+// s: a string
+// Returns a pointer to the first invalid charactor.
+extern const char* my_read_hex_ptr(uintptr_t* result, const char* s);
+
+extern const char* my_read_decimal_ptr(uintptr_t* result, const char* s);
+
+extern void my_memset(void* ip, char c, size_t len);
+
+extern void* my_memchr(const void* src, int c, size_t len);
+
+// The following are considered safe to use in a compromised environment.
+// Besides, this gives the compiler an opportunity to optimize their calls.
+#define my_memcpy memcpy
+#define my_memmove memmove
+#define my_memcmp memcmp
+
+extern size_t my_strlcpy(char* s1, const char* s2, size_t len);
+
+extern size_t my_strlcat(char* s1, const char* s2, size_t len);
+
+extern int my_isspace(int ch);
+
+} // extern "C"
+
+#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc
new file mode 100644
index 000000000..adadfed44
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc
@@ -0,0 +1,213 @@
+// 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 "breakpad_googletest_includes.h"
+#include "common/linux/linux_libc_support.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;
+ int libc_result = strcmp(test_data[i*2], test_data[i*2 + 1]);
+ if (libc_result > 1)
+ libc_result = 1;
+ else if (libc_result < -1)
+ libc_result = -1;
+ ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), libc_result);
+ }
+}
+
+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, uint_len) {
+ ASSERT_EQ(my_uint_len(0), 1U);
+ ASSERT_EQ(my_uint_len(2), 1U);
+ ASSERT_EQ(my_uint_len(5), 1U);
+ ASSERT_EQ(my_uint_len(9), 1U);
+ ASSERT_EQ(my_uint_len(10), 2U);
+ ASSERT_EQ(my_uint_len(99), 2U);
+ ASSERT_EQ(my_uint_len(100), 3U);
+ ASSERT_EQ(my_uint_len(101), 3U);
+ ASSERT_EQ(my_uint_len(1000), 4U);
+ // 0xFFFFFFFFFFFFFFFF
+ ASSERT_EQ(my_uint_len(18446744073709551615LLU), 20U);
+}
+
+TEST(LinuxLibcSupportTest, uitos) {
+ char buf[32];
+
+ my_uitos(buf, 0, 1);
+ ASSERT_EQ(0, memcmp(buf, "0", 1));
+
+ my_uitos(buf, 1, 1);
+ ASSERT_EQ(0, memcmp(buf, "1", 1));
+
+ my_uitos(buf, 10, 2);
+ ASSERT_EQ(0, memcmp(buf, "10", 2));
+
+ my_uitos(buf, 63, 2);
+ ASSERT_EQ(0, memcmp(buf, "63", 2));
+
+ my_uitos(buf, 101, 3);
+ ASSERT_EQ(0, memcmp(buf, "101", 2));
+
+ // 0xFFFFFFFFFFFFFFFF
+ my_uitos(buf, 18446744073709551615LLU, 20);
+ ASSERT_EQ(0, memcmp(buf, "18446744073709551615", 20));
+}
+
+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'));
+
+ static const char abc3[] = "abcabcabc";
+ ASSERT_EQ(abc3, my_strchr(abc3, 'a'));
+}
+
+TEST(LinuxLibcSupportTest, strrchr) {
+ ASSERT_EQ(NULL, my_strrchr("abc", 'd'));
+ ASSERT_EQ(NULL, my_strrchr("", 'd'));
+ ASSERT_EQ(NULL, my_strrchr("efghi", 'd'));
+
+ ASSERT_TRUE(my_strrchr("a", 'a'));
+ ASSERT_TRUE(my_strrchr("abc", 'a'));
+ ASSERT_TRUE(my_strrchr("bcda", 'a'));
+ ASSERT_TRUE(my_strrchr("sdfasdf", 'a'));
+
+ static const char abc3[] = "abcabcabc";
+ ASSERT_EQ(abc3 + 6, my_strrchr(abc3, 'a'));
+}
+
+TEST(LinuxLibcSupportTest, memchr) {
+ ASSERT_EQ(NULL, my_memchr("abc", 'd', 3));
+ ASSERT_EQ(NULL, my_memchr("abcd", 'd', 3));
+ ASSERT_EQ(NULL, my_memchr("a", 'a', 0));
+
+ static const char abc3[] = "abcabcabc";
+ ASSERT_EQ(abc3, my_memchr(abc3, 'a', 3));
+ ASSERT_EQ(abc3, my_memchr(abc3, 'a', 9));
+ ASSERT_EQ(abc3+1, my_memchr(abc3, 'b', 9));
+ ASSERT_EQ(abc3+2, my_memchr(abc3, 'c', 9));
+}
+
+TEST(LinuxLibcSupportTest, read_hex_ptr) {
+ uintptr_t result;
+ const char* last;
+
+ last = my_read_hex_ptr(&result, "");
+ ASSERT_EQ(result, 0U);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0");
+ ASSERT_EQ(result, 0U);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0123");
+ ASSERT_EQ(result, 0x123U);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0123a");
+ ASSERT_EQ(result, 0x123aU);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0123a-");
+ ASSERT_EQ(result, 0x123aU);
+ ASSERT_EQ(*last, '-');
+}
+
+TEST(LinuxLibcSupportTest, read_decimal_ptr) {
+ uintptr_t result;
+ const char* last;
+
+ last = my_read_decimal_ptr(&result, "0");
+ ASSERT_EQ(result, 0U);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_decimal_ptr(&result, "0123");
+ ASSERT_EQ(result, 123U);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_decimal_ptr(&result, "1234");
+ ASSERT_EQ(result, 1234U);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_decimal_ptr(&result, "01234-");
+ ASSERT_EQ(result, 1234U);
+ ASSERT_EQ(*last, '-');
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc
new file mode 100644
index 000000000..4e938269f
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.cc
@@ -0,0 +1,107 @@
+// 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.
+
+// memory_mapped_file.cc: Implement google_breakpad::MemoryMappedFile.
+// See memory_mapped_file.h for details.
+
+#include "common/linux/memory_mapped_file.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#if defined(__ANDROID__)
+#include <sys/stat.h>
+#endif
+#include <unistd.h>
+
+#include "common/memory_range.h"
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+MemoryMappedFile::MemoryMappedFile() {}
+
+MemoryMappedFile::MemoryMappedFile(const char* path, size_t offset) {
+ Map(path, offset);
+}
+
+MemoryMappedFile::~MemoryMappedFile() {
+ Unmap();
+}
+
+#include <unistd.h>
+
+bool MemoryMappedFile::Map(const char* path, size_t offset) {
+ Unmap();
+
+ int fd = sys_open(path, O_RDONLY, 0);
+ if (fd == -1) {
+ return false;
+ }
+
+#if defined(__x86_64__) || defined(__aarch64__) || \
+ (defined(__mips__) && _MIPS_SIM == _ABI64)
+
+ struct kernel_stat st;
+ if (sys_fstat(fd, &st) == -1 || st.st_size < 0) {
+#else
+ struct kernel_stat64 st;
+ if (sys_fstat64(fd, &st) == -1 || st.st_size < 0) {
+#endif
+ sys_close(fd);
+ return false;
+ }
+
+ // Strangely file size can be negative, but we check above that it is not.
+ size_t file_len = static_cast<size_t>(st.st_size);
+ // If the file does not extend beyond the offset, simply use an empty
+ // MemoryRange and return true. Don't bother to call mmap()
+ // even though mmap() can handle an empty file on some platforms.
+ if (offset >= file_len) {
+ sys_close(fd);
+ return true;
+ }
+
+ void* data = sys_mmap(NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset);
+ sys_close(fd);
+ if (data == MAP_FAILED) {
+ return false;
+ }
+
+ content_.Set(data, file_len - offset);
+ return true;
+}
+
+void MemoryMappedFile::Unmap() {
+ if (content_.data()) {
+ sys_munmap(const_cast<uint8_t*>(content_.data()), content_.length());
+ content_.Set(NULL, 0);
+ }
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h
new file mode 100644
index 000000000..fa660cc91
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file.h
@@ -0,0 +1,87 @@
+// 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.
+
+// memory_mapped_file.h: Define the google_breakpad::MemoryMappedFile
+// class, which maps a file into memory for read-only access.
+
+#ifndef COMMON_LINUX_MEMORY_MAPPED_FILE_H_
+#define COMMON_LINUX_MEMORY_MAPPED_FILE_H_
+
+#include <stddef.h>
+#include "common/basictypes.h"
+#include "common/memory_range.h"
+
+namespace google_breakpad {
+
+// A utility class for mapping a file into memory for read-only access of
+// the file content. Its implementation avoids calling into libc functions
+// by directly making system calls for open, close, mmap, and munmap.
+class MemoryMappedFile {
+ public:
+ MemoryMappedFile();
+
+ // Constructor that calls Map() to map a file at |path| into memory.
+ // If Map() fails, the object behaves as if it is default constructed.
+ MemoryMappedFile(const char* path, size_t offset);
+
+ ~MemoryMappedFile();
+
+ // Maps a file at |path| into memory, which can then be accessed via
+ // content() as a MemoryRange object or via data(), and returns true on
+ // success. Mapping an empty file will succeed but with data() and size()
+ // returning NULL and 0, respectively. An existing mapping is unmapped
+ // before a new mapping is created.
+ bool Map(const char* path, size_t offset);
+
+ // Unmaps the memory for the mapped file. It's a no-op if no file is
+ // mapped.
+ void Unmap();
+
+ // Returns a MemoryRange object that covers the memory for the mapped
+ // file. The MemoryRange object is empty if no file is mapped.
+ const MemoryRange& content() const { return content_; }
+
+ // Returns a pointer to the beginning of the memory for the mapped file.
+ // or NULL if no file is mapped or the mapped file is empty.
+ const void* data() const { return content_.data(); }
+
+ // Returns the size in bytes of the mapped file, or zero if no file
+ // is mapped.
+ size_t size() const { return content_.length(); }
+
+ private:
+ // Mapped file content as a MemoryRange object.
+ MemoryRange content_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile);
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_MEMORY_MAPPED_FILE_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file_unittest.cc
new file mode 100644
index 000000000..fad59f40c
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/memory_mapped_file_unittest.cc
@@ -0,0 +1,208 @@
+// 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.
+
+// memory_mapped_file_unittest.cc:
+// Unit tests for google_breakpad::MemoryMappedFile.
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "common/linux/memory_mapped_file.h"
+#include "common/tests/auto_tempdir.h"
+#include "common/tests/file_utils.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::AutoTempDir;
+using google_breakpad::MemoryMappedFile;
+using google_breakpad::WriteFile;
+
+namespace {
+
+class MemoryMappedFileTest : public testing::Test {
+ protected:
+ void ExpectNoMappedData(const MemoryMappedFile& mapped_file) {
+ EXPECT_TRUE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() == NULL);
+ EXPECT_EQ(0U, mapped_file.size());
+ }
+};
+
+} // namespace
+
+TEST_F(MemoryMappedFileTest, DefaultConstructor) {
+ MemoryMappedFile mapped_file;
+ ExpectNoMappedData(mapped_file);
+}
+
+TEST_F(MemoryMappedFileTest, UnmapWithoutMap) {
+ MemoryMappedFile mapped_file;
+ mapped_file.Unmap();
+}
+
+TEST_F(MemoryMappedFileTest, MapNonexistentFile) {
+ {
+ MemoryMappedFile mapped_file("nonexistent-file", 0);
+ ExpectNoMappedData(mapped_file);
+ }
+ {
+ MemoryMappedFile mapped_file;
+ EXPECT_FALSE(mapped_file.Map("nonexistent-file", 0));
+ ExpectNoMappedData(mapped_file);
+ }
+}
+
+TEST_F(MemoryMappedFileTest, MapEmptyFile) {
+ AutoTempDir temp_dir;
+ string test_file = temp_dir.path() + "/empty_file";
+ ASSERT_TRUE(WriteFile(test_file.c_str(), NULL, 0));
+
+ {
+ MemoryMappedFile mapped_file(test_file.c_str(), 0);
+ ExpectNoMappedData(mapped_file);
+ }
+ {
+ MemoryMappedFile mapped_file;
+ EXPECT_TRUE(mapped_file.Map(test_file.c_str(), 0));
+ ExpectNoMappedData(mapped_file);
+ }
+}
+
+TEST_F(MemoryMappedFileTest, MapNonEmptyFile) {
+ char data[256];
+ size_t data_size = sizeof(data);
+ for (size_t i = 0; i < data_size; ++i) {
+ data[i] = i;
+ }
+
+ AutoTempDir temp_dir;
+ string test_file = temp_dir.path() + "/test_file";
+ ASSERT_TRUE(WriteFile(test_file.c_str(), data, data_size));
+
+ {
+ MemoryMappedFile mapped_file(test_file.c_str(), 0);
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data_size, mapped_file.size());
+ EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size));
+ }
+ {
+ MemoryMappedFile mapped_file;
+ EXPECT_TRUE(mapped_file.Map(test_file.c_str(), 0));
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data_size, mapped_file.size());
+ EXPECT_EQ(0, memcmp(data, mapped_file.data(), data_size));
+ }
+}
+
+TEST_F(MemoryMappedFileTest, RemapAfterMap) {
+ char data1[256];
+ size_t data1_size = sizeof(data1);
+ for (size_t i = 0; i < data1_size; ++i) {
+ data1[i] = i;
+ }
+
+ char data2[50];
+ size_t data2_size = sizeof(data2);
+ for (size_t i = 0; i < data2_size; ++i) {
+ data2[i] = 255 - i;
+ }
+
+ AutoTempDir temp_dir;
+ string test_file1 = temp_dir.path() + "/test_file1";
+ string test_file2 = temp_dir.path() + "/test_file2";
+ ASSERT_TRUE(WriteFile(test_file1.c_str(), data1, data1_size));
+ ASSERT_TRUE(WriteFile(test_file2.c_str(), data2, data2_size));
+
+ {
+ MemoryMappedFile mapped_file(test_file1.c_str(), 0);
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data1_size, mapped_file.size());
+ EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size));
+
+ mapped_file.Map(test_file2.c_str(), 0);
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data2_size, mapped_file.size());
+ EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size));
+ }
+ {
+ MemoryMappedFile mapped_file;
+ EXPECT_TRUE(mapped_file.Map(test_file1.c_str(), 0));
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data1_size, mapped_file.size());
+ EXPECT_EQ(0, memcmp(data1, mapped_file.data(), data1_size));
+
+ mapped_file.Map(test_file2.c_str(), 0);
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data2_size, mapped_file.size());
+ EXPECT_EQ(0, memcmp(data2, mapped_file.data(), data2_size));
+ }
+}
+
+TEST_F(MemoryMappedFileTest, MapWithOffset) {
+ // Put more data in the test file this time. Offsets can only be
+ // done on page boundaries, so we need a two page file to test this.
+ const int page_size = 4096;
+ char data1[2 * page_size];
+ size_t data1_size = sizeof(data1);
+ for (size_t i = 0; i < data1_size; ++i) {
+ data1[i] = i & 0x7f;
+ }
+
+ AutoTempDir temp_dir;
+ string test_file1 = temp_dir.path() + "/test_file1";
+ ASSERT_TRUE(WriteFile(test_file1.c_str(), data1, data1_size));
+ {
+ MemoryMappedFile mapped_file(test_file1.c_str(), page_size);
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data1_size - page_size, mapped_file.size());
+ EXPECT_EQ(
+ 0,
+ memcmp(data1 + page_size, mapped_file.data(), data1_size - page_size));
+ }
+ {
+ MemoryMappedFile mapped_file;
+ mapped_file.Map(test_file1.c_str(), page_size);
+ EXPECT_FALSE(mapped_file.content().IsEmpty());
+ EXPECT_TRUE(mapped_file.data() != NULL);
+ EXPECT_EQ(data1_size - page_size, mapped_file.size());
+ EXPECT_EQ(
+ 0,
+ memcmp(data1 + page_size, mapped_file.data(), data1_size - page_size));
+ }
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build
new file mode 100644
index 000000000..7450f6ba3
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/moz.build
@@ -0,0 +1,56 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ 'elfutils.cc',
+ 'guid_creator.cc',
+ 'linux_libc_support.cc',
+ 'memory_mapped_file.cc',
+ 'safe_readlink.cc',
+]
+
+# file_id.cc cannot be built in unified mode because it uses a custom DISABLE_STL_WRAPPING
+SOURCES += [
+ 'file_id.cc',
+]
+
+if CONFIG['OS_TARGET'] != 'Android':
+ UNIFIED_SOURCES += [
+ 'http_upload.cc',
+ ]
+
+HostLibrary('host_breakpad_linux_common_s')
+HOST_SOURCES += [
+ 'crc32.cc',
+ 'dump_symbols.cc',
+ 'elf_symbols_to_module.cc',
+ 'elfutils.cc',
+ 'file_id.cc',
+ 'guid_creator.cc',
+ 'linux_libc_support.cc',
+ 'memory_mapped_file.cc',
+]
+
+HOST_CXXFLAGS += [
+ '-O2',
+ '-g',
+]
+
+if CONFIG['OS_TARGET'] == 'Android':
+ LOCAL_INCLUDES += [
+ '/toolkit/crashreporter/google-breakpad/src/common/android/include',
+ ]
+
+Library('breakpad_linux_common_s')
+
+FINAL_LIBRARY = 'xul'
+
+HOST_DEFINES['NO_STABS_SUPPORT'] = True
+
+include('/toolkit/crashreporter/crashreporter.mozbuild')
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-shadow']
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.cc
new file mode 100644
index 000000000..870c28af3
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.cc
@@ -0,0 +1,53 @@
+// 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.
+
+// safe_readlink.cc: Implement google_breakpad::SafeReadLink.
+// See safe_readlink.h for details.
+
+#include <stddef.h>
+
+#include "third_party/lss/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+bool SafeReadLink(const char* path, char* buffer, size_t buffer_size) {
+ // sys_readlink() does not add a NULL byte to |buffer|. In order to return
+ // a NULL-terminated string in |buffer|, |buffer_size| should be at least
+ // one byte longer than the expected path length. Also, sys_readlink()
+ // returns the actual path length on success, which does not count the
+ // NULL byte, so |result_size| should be less than |buffer_size|.
+ ssize_t result_size = sys_readlink(path, buffer, buffer_size);
+ if (result_size >= 0 && static_cast<size_t>(result_size) < buffer_size) {
+ buffer[result_size] = '\0';
+ return true;
+ }
+ return false;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.h b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.h
new file mode 100644
index 000000000..4ae131b58
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink.h
@@ -0,0 +1,65 @@
+// 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.
+
+// safe_readlink.h: Define the google_breakpad::SafeReadLink function,
+// which wraps sys_readlink and gurantees the result is NULL-terminated.
+
+#ifndef COMMON_LINUX_SAFE_READLINK_H_
+#define COMMON_LINUX_SAFE_READLINK_H_
+
+#include <stddef.h>
+
+namespace google_breakpad {
+
+// This function wraps sys_readlink() and performs the same functionalty,
+// but guarantees |buffer| is NULL-terminated if sys_readlink() returns
+// no error. It takes the same arguments as sys_readlink(), but unlike
+// sys_readlink(), it returns true on success.
+//
+// |buffer_size| specifies the size of |buffer| in bytes. As this function
+// always NULL-terminates |buffer| on success, |buffer_size| should be
+// at least one byte longer than the expected path length (e.g. PATH_MAX,
+// which is typically defined as the maximum length of a path name
+// including the NULL byte).
+//
+// The implementation of this function calls sys_readlink() instead of
+// readlink(), it can thus be used in the context where calling to libc
+// functions is discouraged.
+bool SafeReadLink(const char* path, char* buffer, size_t buffer_size);
+
+// Same as the three-argument version of SafeReadLink() but deduces the
+// size of |buffer| if it is a char array of known size.
+template <size_t N>
+bool SafeReadLink(const char* path, char (&buffer)[N]) {
+ return SafeReadLink(path, buffer, sizeof(buffer));
+}
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_SAFE_READLINK_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink_unittest.cc
new file mode 100644
index 000000000..d346b2a80
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/safe_readlink_unittest.cc
@@ -0,0 +1,89 @@
+// 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.
+
+// safe_readlink_unittest.cc: Unit tests for google_breakpad::SafeReadLink.
+
+#include "breakpad_googletest_includes.h"
+#include "common/linux/safe_readlink.h"
+
+using google_breakpad::SafeReadLink;
+
+TEST(SafeReadLinkTest, ZeroBufferSize) {
+ char buffer[1];
+ EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 0));
+}
+
+TEST(SafeReadLinkTest, BufferSizeTooSmall) {
+ char buffer[1];
+ EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, 1));
+}
+
+TEST(SafeReadLinkTest, BoundaryBufferSize) {
+ char buffer[PATH_MAX];
+ EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer, sizeof(buffer)));
+ size_t path_length = strlen(buffer);
+ EXPECT_LT(0U, path_length);
+ EXPECT_GT(sizeof(buffer), path_length);
+
+ // Buffer size equals to the expected path length plus 1 for the NULL byte.
+ char buffer2[PATH_MAX];
+ EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2, path_length + 1));
+ EXPECT_EQ(path_length, strlen(buffer2));
+ EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX));
+
+ // Buffer size equals to the expected path length.
+ EXPECT_FALSE(SafeReadLink("/proc/self/exe", buffer, path_length));
+}
+
+TEST(SafeReadLinkTest, NonexistentPath) {
+ char buffer[PATH_MAX];
+ EXPECT_FALSE(SafeReadLink("nonexistent_path", buffer, sizeof(buffer)));
+}
+
+TEST(SafeReadLinkTest, NonSymbolicLinkPath) {
+ char actual_path[PATH_MAX];
+ EXPECT_TRUE(SafeReadLink("/proc/self/exe", actual_path, sizeof(actual_path)));
+
+ char buffer[PATH_MAX];
+ EXPECT_FALSE(SafeReadLink(actual_path, buffer, sizeof(buffer)));
+}
+
+TEST(SafeReadLinkTest, DeduceBufferSizeFromCharArray) {
+ char buffer[PATH_MAX];
+ char* buffer_pointer = buffer;
+ EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer_pointer, sizeof(buffer)));
+ size_t path_length = strlen(buffer);
+
+ // Use the template version of SafeReadLink to deduce the buffer size
+ // from the char array.
+ char buffer2[PATH_MAX];
+ EXPECT_TRUE(SafeReadLink("/proc/self/exe", buffer2));
+ EXPECT_EQ(path_length, strlen(buffer2));
+ EXPECT_EQ(0, strncmp(buffer, buffer2, PATH_MAX));
+}
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.cc
new file mode 100644
index 000000000..bbd3181e1
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.cc
@@ -0,0 +1,155 @@
+// 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.
+
+// symbol_upload.cc: implemented google_breakpad::sym_upload::Start, a helper
+// function for linux symbol upload tool.
+
+#include "common/linux/http_upload.h"
+#include "common/linux/symbol_upload.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include <functional>
+#include <vector>
+
+namespace google_breakpad {
+namespace sym_upload {
+
+void TokenizeByChar(const string &source_string, int c,
+ std::vector<string> *results) {
+ assert(results);
+ string::size_type cur_pos = 0, next_pos = 0;
+ while ((next_pos = source_string.find(c, cur_pos)) != string::npos) {
+ if (next_pos != cur_pos)
+ results->push_back(source_string.substr(cur_pos, next_pos - cur_pos));
+ cur_pos = next_pos + 1;
+ }
+ if (cur_pos < source_string.size() && next_pos != cur_pos)
+ results->push_back(source_string.substr(cur_pos));
+}
+
+//=============================================================================
+// Parse out the module line which have 5 parts.
+// MODULE <os> <cpu> <uuid> <module-name>
+bool ModuleDataForSymbolFile(const string &file,
+ std::vector<string> *module_parts) {
+ assert(module_parts);
+ const size_t kModulePartNumber = 5;
+ FILE* fp = fopen(file.c_str(), "r");
+ if (fp) {
+ char buffer[1024];
+ if (fgets(buffer, sizeof(buffer), fp)) {
+ string line(buffer);
+ string::size_type line_break_pos = line.find_first_of('\n');
+ if (line_break_pos == string::npos) {
+ assert(0 && "The file is invalid!");
+ fclose(fp);
+ return false;
+ }
+ line.resize(line_break_pos);
+ const char kDelimiter = ' ';
+ TokenizeByChar(line, kDelimiter, module_parts);
+ if (module_parts->size() != kModulePartNumber)
+ module_parts->clear();
+ }
+ fclose(fp);
+ }
+
+ return module_parts->size() == kModulePartNumber;
+}
+
+//=============================================================================
+string CompactIdentifier(const string &uuid) {
+ std::vector<string> components;
+ TokenizeByChar(uuid, '-', &components);
+ string result;
+ for (size_t i = 0; i < components.size(); ++i)
+ result += components[i];
+ return result;
+}
+
+//=============================================================================
+void Start(Options *options) {
+ std::map<string, string> parameters;
+ options->success = false;
+ std::vector<string> module_parts;
+ if (!ModuleDataForSymbolFile(options->symbolsPath, &module_parts)) {
+ fprintf(stderr, "Failed to parse symbol file!\n");
+ return;
+ }
+
+ string compacted_id = CompactIdentifier(module_parts[3]);
+
+ // Add parameters
+ if (!options->version.empty())
+ parameters["version"] = options->version;
+
+ // MODULE <os> <cpu> <uuid> <module-name>
+ // 0 1 2 3 4
+ parameters["os"] = module_parts[1];
+ parameters["cpu"] = module_parts[2];
+ parameters["debug_file"] = module_parts[4];
+ parameters["code_file"] = module_parts[4];
+ parameters["debug_identifier"] = compacted_id;
+
+ std::map<string, string> files;
+ files["symbol_file"] = options->symbolsPath;
+
+ string response, error;
+ long response_code;
+ bool success = HTTPUpload::SendRequest(options->uploadURLStr,
+ parameters,
+ files,
+ options->proxy,
+ options->proxy_user_pwd,
+ "",
+ &response,
+ &response_code,
+ &error);
+
+ if (!success) {
+ printf("Failed to send symbol file: %s\n", error.c_str());
+ printf("Response code: %ld\n", response_code);
+ printf("Response:\n");
+ printf("%s\n", response.c_str());
+ } else if (response_code == 0) {
+ printf("Failed to send symbol file: No response code\n");
+ } else if (response_code != 200) {
+ printf("Failed to send symbol file: Response code %ld\n", response_code);
+ printf("Response:\n");
+ printf("%s\n", response.c_str());
+ } else {
+ printf("Successfully sent the symbol file.\n");
+ }
+ options->success = success;
+}
+
+} // namespace sym_upload
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.h b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.h
new file mode 100644
index 000000000..0a469692a
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/symbol_upload.h
@@ -0,0 +1,59 @@
+// -*- 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.
+
+// symbol_upload.h: helper functions for linux symbol upload tool.
+
+#ifndef COMMON_LINUX_SYMBOL_UPLOAD_H_
+#define COMMON_LINUX_SYMBOL_UPLOAD_H_
+
+#include <string>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+namespace sym_upload {
+
+typedef struct {
+ string symbolsPath;
+ string uploadURLStr;
+ string proxy;
+ string proxy_user_pwd;
+ string version;
+ bool success;
+} Options;
+
+// Starts upload to symbol server with options.
+void Start(Options* options);
+
+} // namespace sym_upload
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_SYMBOL_UPLOAD_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.cc
new file mode 100644
index 000000000..98e81dab7
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.cc
@@ -0,0 +1,263 @@
+#include "common/linux/synth_elf.h"
+
+#include <assert.h>
+#include <elf.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "common/linux/elf_gnu_compat.h"
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+namespace synth_elf {
+
+ELF::ELF(uint16_t machine,
+ uint8_t file_class,
+ Endianness endianness)
+ : Section(endianness),
+ addr_size_(file_class == ELFCLASS64 ? 8 : 4),
+ program_count_(0),
+ program_header_table_(endianness),
+ 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);
+
+ sections_.push_back(ElfSection(section, type, addr, offset, offset_label,
+ size));
+ return index;
+}
+
+void ELF::AppendSection(ElfSection &section) {
+ // NULL and NOBITS sections have no content, so they
+ // don't need to be written to the file.
+ if (section.type_ == SHT_NULL) {
+ section.offset_label_ = 0;
+ } else if (section.type_ == SHT_NOBITS) {
+ section.offset_label_ = section.offset_;
+ } else {
+ Mark(&section.offset_label_);
+ Append(section);
+ Align(4);
+ }
+}
+
+void ELF::AddSegment(int start, int end, uint32_t type, uint32_t flags) {
+ assert(start > 0);
+ assert(size_t(start) < sections_.size());
+ assert(end > 0);
+ assert(size_t(end) < sections_.size());
+ ++program_count_;
+
+ // p_type
+ program_header_table_.D32(type);
+
+ if (addr_size_ == 8) {
+ // p_flags
+ program_header_table_.D32(flags);
+ }
+
+ size_t filesz = 0;
+ size_t memsz = 0;
+ bool prev_was_nobits = false;
+ for (int i = start; i <= end; ++i) {
+ size_t size = sections_[i].size_;
+ if (sections_[i].type_ != SHT_NOBITS) {
+ assert(!prev_was_nobits);
+ // non SHT_NOBITS sections are 4-byte aligned (see AddSection)
+ size = (size + 3) & ~3;
+ filesz += size;
+ } else {
+ prev_was_nobits = true;
+ }
+ memsz += size;
+ }
+
+ program_header_table_
+ // p_offset
+ .Append(endianness(), addr_size_, sections_[start].offset_label_)
+ // p_vaddr
+ .Append(endianness(), addr_size_, sections_[start].addr_)
+ // p_paddr
+ .Append(endianness(), addr_size_, sections_[start].addr_)
+ // p_filesz
+ .Append(endianness(), addr_size_, filesz)
+ // p_memsz
+ .Append(endianness(), addr_size_, memsz);
+
+ if (addr_size_ == 4) {
+ // p_flags
+ program_header_table_.D32(flags);
+ }
+
+ // p_align
+ program_header_table_.Append(endianness(), addr_size_, 0);
+}
+
+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());
+ if (program_count_) {
+ Mark(&program_header_label_);
+ Append(program_header_table_);
+ } else {
+ program_header_label_ = 0;
+ }
+
+ for (vector<ElfSection>::iterator it = sections_.begin();
+ it < sections_.end(); ++it) {
+ AppendSection(*it);
+ }
+ section_count_label_ = section_count_;
+ program_count_label_ = program_count_;
+
+ // Section header table starts here.
+ Mark(&section_header_label_);
+ Append(section_header_table_);
+}
+
+SymbolTable::SymbolTable(Endianness endianness,
+ size_t addr_size,
+ StringTable& table) : Section(endianness),
+ table_(table) {
+#ifndef NDEBUG
+ addr_size_ = addr_size;
+#endif
+ 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);
+}
+
+void Notes::AddNote(int type, const string &name, const uint8_t* desc_bytes,
+ size_t desc_size) {
+ // Elf32_Nhdr and Elf64_Nhdr are exactly the same.
+ Elf32_Nhdr note_header;
+ memset(&note_header, 0, sizeof(note_header));
+ note_header.n_namesz = name.length() + 1;
+ note_header.n_descsz = desc_size;
+ note_header.n_type = type;
+
+ Append(reinterpret_cast<const uint8_t*>(&note_header),
+ sizeof(note_header));
+ AppendCString(name);
+ Align(4);
+ Append(desc_bytes, desc_size);
+ Align(4);
+}
+
+} // namespace synth_elf
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.h b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.h
new file mode 100644
index 000000000..1d2a20ca2
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf.h
@@ -0,0 +1,197 @@
+// -*- 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 <vector>
+#include <map>
+#include <string>
+#include <utility>
+
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+namespace synth_elf {
+
+using std::list;
+using std::vector;
+using std::map;
+using std::pair;
+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);
+
+ // Add a segment containing from section index start to section index end.
+ // The indexes must have been gotten from AddSection.
+ void AddSegment(int start, int end, uint32_t type, uint32_t flags = 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_;
+ // The program header table itself.
+ Section program_header_table_;
+
+ // 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_;
+
+ // Record of an added section
+ struct ElfSection : public Section {
+ ElfSection(const Section& section, uint32_t type, uint32_t addr,
+ uint32_t offset, Label offset_label, uint32_t size)
+ : Section(section), type_(type), addr_(addr), offset_(offset)
+ , offset_label_(offset_label), size_(size) {
+ }
+
+ uint32_t type_;
+ uint32_t addr_;
+ uint32_t offset_;
+ Label offset_label_;
+ uint32_t size_;
+ };
+
+ vector<ElfSection> sections_;
+
+ void AppendSection(ElfSection &section);
+};
+
+// 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:
+#ifndef NDEBUG
+ size_t addr_size_;
+#endif
+ StringTable& table_;
+};
+
+// A class for note sections
+class Notes : public Section {
+public:
+ Notes(Endianness endianness)
+ : Section(endianness) {
+ }
+
+ // Add a note.
+ void AddNote(int type, const string &name, const uint8_t* desc_bytes,
+ size_t desc_size);
+};
+
+} // namespace synth_elf
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_SYNTH_ELF_H_
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf_unittest.cc
new file mode 100644
index 000000000..3715b6e60
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/synth_elf_unittest.cc
@@ -0,0 +1,413 @@
+// 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/elfutils.h"
+#include "common/linux/synth_elf.h"
+#include "common/using_std_string.h"
+
+using google_breakpad::ElfClass32;
+using google_breakpad::ElfClass64;
+using google_breakpad::synth_elf::ELF;
+using google_breakpad::synth_elf::Notes;
+using google_breakpad::synth_elf::Section;
+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 ::testing::Test;
+using ::testing::Types;
+
+class StringTableTest : public Test {
+public:
+ StringTableTest() : table(kLittleEndian) {}
+
+ StringTable table;
+};
+
+TEST_F(StringTableTest, Empty) {
+ EXPECT_EQ(1U, 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(0U, 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(1U, 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(0U, 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()));
+}
+
+template<typename ElfClass>
+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__)
+
+typedef Types<ElfClass32, ElfClass64> ElfClasses;
+
+TYPED_TEST_CASE(BasicElf, ElfClasses);
+
+TYPED_TEST(BasicElf, EmptyLE) {
+ typedef typename TypeParam::Ehdr Ehdr;
+ typedef typename TypeParam::Phdr Phdr;
+ typedef typename TypeParam::Shdr Shdr;
+ const size_t kStringTableSize = sizeof("\0.shstrtab");
+ const size_t kStringTableAlign = 4 - kStringTableSize % 4;
+ const size_t kExpectedSize = sizeof(Ehdr) +
+ // Two sections, SHT_NULL + the section header string table.
+ 2 * sizeof(Shdr) +
+ kStringTableSize + kStringTableAlign;
+
+ // It doesn't really matter that the machine type is right for the class.
+ ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
+ elf.Finish();
+ EXPECT_EQ(kExpectedSize, elf.Size());
+
+ string contents;
+ ASSERT_TRUE(elf.GetContents(&contents));
+ ASSERT_EQ(kExpectedSize, contents.size());
+ const Ehdr* header =
+ reinterpret_cast<const Ehdr*>(contents.data());
+ const uint8_t kIdent[] = {
+ ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
+ TypeParam::kClass, 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(static_cast<unsigned int>(EV_CURRENT), header->e_version);
+ EXPECT_EQ(0U, header->e_entry);
+ EXPECT_EQ(0U, header->e_phoff);
+ EXPECT_EQ(sizeof(Ehdr) + kStringTableSize + kStringTableAlign,
+ header->e_shoff);
+ EXPECT_EQ(0U, header->e_flags);
+ EXPECT_EQ(sizeof(Ehdr), header->e_ehsize);
+ EXPECT_EQ(sizeof(Phdr), header->e_phentsize);
+ EXPECT_EQ(0, header->e_phnum);
+ EXPECT_EQ(sizeof(Shdr), header->e_shentsize);
+ EXPECT_EQ(2, header->e_shnum);
+ EXPECT_EQ(1, header->e_shstrndx);
+
+ const Shdr* shdr =
+ reinterpret_cast<const Shdr*>(contents.data() + header->e_shoff);
+ EXPECT_EQ(0U, shdr[0].sh_name);
+ EXPECT_EQ(static_cast<unsigned int>(SHT_NULL), shdr[0].sh_type);
+ EXPECT_EQ(0U, shdr[0].sh_flags);
+ EXPECT_EQ(0U, shdr[0].sh_addr);
+ EXPECT_EQ(0U, shdr[0].sh_offset);
+ EXPECT_EQ(0U, shdr[0].sh_size);
+ EXPECT_EQ(0U, shdr[0].sh_link);
+ EXPECT_EQ(0U, shdr[0].sh_info);
+ EXPECT_EQ(0U, shdr[0].sh_addralign);
+ EXPECT_EQ(0U, shdr[0].sh_entsize);
+
+ EXPECT_EQ(1U, shdr[1].sh_name);
+ EXPECT_EQ(static_cast<unsigned int>(SHT_STRTAB), shdr[1].sh_type);
+ EXPECT_EQ(0U, shdr[1].sh_flags);
+ EXPECT_EQ(0U, shdr[1].sh_addr);
+ EXPECT_EQ(sizeof(Ehdr), shdr[1].sh_offset);
+ EXPECT_EQ(kStringTableSize, shdr[1].sh_size);
+ EXPECT_EQ(0U, shdr[1].sh_link);
+ EXPECT_EQ(0U, shdr[1].sh_info);
+ EXPECT_EQ(0U, shdr[1].sh_addralign);
+ EXPECT_EQ(0U, shdr[1].sh_entsize);
+}
+
+TYPED_TEST(BasicElf, BasicLE) {
+ typedef typename TypeParam::Ehdr Ehdr;
+ typedef typename TypeParam::Phdr Phdr;
+ typedef typename TypeParam::Shdr Shdr;
+ const size_t kStringTableSize = sizeof("\0.text\0.bss\0.shstrtab");
+ const size_t kStringTableAlign = 4 - kStringTableSize % 4;
+ const size_t kExpectedSize = sizeof(Ehdr) +
+ // Four sections, SHT_NULL + the section header string table +
+ // 4096 bytes of the size-aligned .text section + one program header.
+ sizeof(Phdr) + 4 * sizeof(Shdr) + 4096 +
+ kStringTableSize + kStringTableAlign;
+
+ // It doesn't really matter that the machine type is right for the class.
+ ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
+ Section text(kLittleEndian);
+ text.Append(4094, 0);
+ int text_idx = elf.AddSection(".text", text, SHT_PROGBITS);
+ Section bss(kLittleEndian);
+ bss.Append(16, 0);
+ int bss_idx = elf.AddSection(".bss", bss, SHT_NOBITS);
+ elf.AddSegment(text_idx, bss_idx, PT_LOAD);
+ elf.Finish();
+ EXPECT_EQ(kExpectedSize, elf.Size());
+
+ string contents;
+ ASSERT_TRUE(elf.GetContents(&contents));
+ ASSERT_EQ(kExpectedSize, contents.size());
+ const Ehdr* header =
+ reinterpret_cast<const Ehdr*>(contents.data());
+ const uint8_t kIdent[] = {
+ ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3,
+ TypeParam::kClass, 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(static_cast<unsigned int>(EV_CURRENT), header->e_version);
+ EXPECT_EQ(0U, header->e_entry);
+ EXPECT_EQ(sizeof(Ehdr), header->e_phoff);
+ EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096 + kStringTableSize +
+ kStringTableAlign, header->e_shoff);
+ EXPECT_EQ(0U, header->e_flags);
+ EXPECT_EQ(sizeof(Ehdr), header->e_ehsize);
+ EXPECT_EQ(sizeof(Phdr), header->e_phentsize);
+ EXPECT_EQ(1, header->e_phnum);
+ EXPECT_EQ(sizeof(Shdr), header->e_shentsize);
+ EXPECT_EQ(4, header->e_shnum);
+ EXPECT_EQ(3, header->e_shstrndx);
+
+ const Shdr* shdr =
+ reinterpret_cast<const Shdr*>(contents.data() + header->e_shoff);
+ EXPECT_EQ(0U, shdr[0].sh_name);
+ EXPECT_EQ(static_cast<unsigned int>(SHT_NULL), shdr[0].sh_type);
+ EXPECT_EQ(0U, shdr[0].sh_flags);
+ EXPECT_EQ(0U, shdr[0].sh_addr);
+ EXPECT_EQ(0U, shdr[0].sh_offset);
+ EXPECT_EQ(0U, shdr[0].sh_size);
+ EXPECT_EQ(0U, shdr[0].sh_link);
+ EXPECT_EQ(0U, shdr[0].sh_info);
+ EXPECT_EQ(0U, shdr[0].sh_addralign);
+ EXPECT_EQ(0U, shdr[0].sh_entsize);
+
+ EXPECT_EQ(1U, shdr[1].sh_name);
+ EXPECT_EQ(static_cast<unsigned int>(SHT_PROGBITS), shdr[1].sh_type);
+ EXPECT_EQ(0U, shdr[1].sh_flags);
+ EXPECT_EQ(0U, shdr[1].sh_addr);
+ EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), shdr[1].sh_offset);
+ EXPECT_EQ(4094U, shdr[1].sh_size);
+ EXPECT_EQ(0U, shdr[1].sh_link);
+ EXPECT_EQ(0U, shdr[1].sh_info);
+ EXPECT_EQ(0U, shdr[1].sh_addralign);
+ EXPECT_EQ(0U, shdr[1].sh_entsize);
+
+ EXPECT_EQ(sizeof("\0.text"), shdr[2].sh_name);
+ EXPECT_EQ(static_cast<unsigned int>(SHT_NOBITS), shdr[2].sh_type);
+ EXPECT_EQ(0U, shdr[2].sh_flags);
+ EXPECT_EQ(0U, shdr[2].sh_addr);
+ EXPECT_EQ(0U, shdr[2].sh_offset);
+ EXPECT_EQ(16U, shdr[2].sh_size);
+ EXPECT_EQ(0U, shdr[2].sh_link);
+ EXPECT_EQ(0U, shdr[2].sh_info);
+ EXPECT_EQ(0U, shdr[2].sh_addralign);
+ EXPECT_EQ(0U, shdr[2].sh_entsize);
+
+ EXPECT_EQ(sizeof("\0.text\0.bss"), shdr[3].sh_name);
+ EXPECT_EQ(static_cast<unsigned int>(SHT_STRTAB), shdr[3].sh_type);
+ EXPECT_EQ(0U, shdr[3].sh_flags);
+ EXPECT_EQ(0U, shdr[3].sh_addr);
+ EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr) + 4096, shdr[3].sh_offset);
+ EXPECT_EQ(kStringTableSize, shdr[3].sh_size);
+ EXPECT_EQ(0U, shdr[3].sh_link);
+ EXPECT_EQ(0U, shdr[3].sh_info);
+ EXPECT_EQ(0U, shdr[3].sh_addralign);
+ EXPECT_EQ(0U, shdr[3].sh_entsize);
+
+ const Phdr* phdr =
+ reinterpret_cast<const Phdr*>(contents.data() + header->e_phoff);
+ EXPECT_EQ(static_cast<unsigned int>(PT_LOAD), phdr->p_type);
+ EXPECT_EQ(sizeof(Ehdr) + sizeof(Phdr), phdr->p_offset);
+ EXPECT_EQ(0U, phdr->p_vaddr);
+ EXPECT_EQ(0U, phdr->p_paddr);
+ EXPECT_EQ(4096U, phdr->p_filesz);
+ EXPECT_EQ(4096U + 16U, phdr->p_memsz);
+ EXPECT_EQ(0U, phdr->p_flags);
+ EXPECT_EQ(0U, phdr->p_align);
+}
+
+class ElfNotesTest : public Test {};
+
+TEST_F(ElfNotesTest, Empty) {
+ Notes notes(kLittleEndian);
+ string contents;
+ ASSERT_TRUE(notes.GetContents(&contents));
+ EXPECT_EQ(0U, contents.size());
+}
+
+TEST_F(ElfNotesTest, Notes) {
+ Notes notes(kLittleEndian);
+ notes.AddNote(1, "Linux", reinterpret_cast<const uint8_t *>("\x42\x02\0\0"),
+ 4);
+ notes.AddNote(2, "a", reinterpret_cast<const uint8_t *>("foobar"),
+ sizeof("foobar") - 1);
+
+ const uint8_t kExpectedNotesContents[] = {
+ // Note 1
+ 0x06, 0x00, 0x00, 0x00, // name size, including terminating zero
+ 0x04, 0x00, 0x00, 0x00, // desc size
+ 0x01, 0x00, 0x00, 0x00, // type
+ 'L', 'i', 'n', 'u', 'x', 0x00, 0x00, 0x00, // padded "Linux"
+ 0x42, 0x02, 0x00, 0x00, // desc
+ // Note 2
+ 0x02, 0x00, 0x00, 0x00, // name size
+ 0x06, 0x00, 0x00, 0x00, // desc size
+ 0x02, 0x00, 0x00, 0x00, // type
+ 'a', 0x00, 0x00, 0x00, // padded "a"
+ 'f', 'o', 'o', 'b', 'a', 'r', 0x00, 0x00, // padded "foobar"
+ };
+ const size_t kExpectedNotesSize = sizeof(kExpectedNotesContents);
+ EXPECT_EQ(kExpectedNotesSize, notes.Size());
+
+ string notes_contents;
+ ASSERT_TRUE(notes.GetContents(&notes_contents));
+ EXPECT_EQ(0, memcmp(kExpectedNotesContents,
+ notes_contents.data(),
+ notes_contents.size()));
+}
+
+#endif // defined(__i386__) || defined(__x86_64__)
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/tests/auto_testfile.h b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/auto_testfile.h
new file mode 100644
index 000000000..92fe017b9
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/auto_testfile.h
@@ -0,0 +1,124 @@
+// Copyright (c) 2013, 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.
+
+// Utility class for creating a temporary file for unit tests
+// that is deleted in the destructor.
+
+#ifndef GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE
+#define GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include "breakpad_googletest_includes.h"
+#include "common/linux/eintr_wrapper.h"
+#include "common/tests/auto_tempdir.h"
+
+namespace google_breakpad {
+
+class AutoTestFile {
+ public:
+ // Create a new empty test file.
+ // test_prefix: (input) test-specific prefix, can't be NULL.
+ explicit AutoTestFile(const char* test_prefix) {
+ Init(test_prefix);
+ }
+
+ // Create a new test file, and fill it with initial data from a C string.
+ // The terminating zero is not written.
+ // test_prefix: (input) test-specific prefix, can't be NULL.
+ // text: (input) initial content.
+ AutoTestFile(const char* test_prefix, const char* text) {
+ Init(test_prefix);
+ if (fd_ >= 0)
+ WriteText(text, static_cast<size_t>(strlen(text)));
+ }
+
+ AutoTestFile(const char* test_prefix, const char* text, size_t text_len) {
+ Init(test_prefix);
+ if (fd_ >= 0)
+ WriteText(text, text_len);
+ }
+
+ // Destroy test file on scope exit.
+ ~AutoTestFile() {
+ if (fd_ >= 0) {
+ close(fd_);
+ fd_ = -1;
+ }
+ }
+
+ // Returns true iff the test file could be created properly.
+ // Useful in tests inside EXPECT_TRUE(file.IsOk());
+ bool IsOk() {
+ return fd_ >= 0;
+ }
+
+ // Returns the Posix file descriptor for the test file, or -1
+ // If IsOk() returns false. Note: on Windows, this always returns -1.
+ int GetFd() {
+ return fd_;
+ }
+
+ private:
+ void Init(const char* test_prefix) {
+ fd_ = -1;
+ char path_templ[PATH_MAX];
+ int ret = snprintf(path_templ, sizeof(path_templ),
+ TEMPDIR "/%s-unittest.XXXXXX",
+ test_prefix);
+ if (ret >= static_cast<int>(sizeof(path_templ)))
+ return;
+
+ fd_ = mkstemp(path_templ);
+ if (fd_ < 0)
+ return;
+
+ unlink(path_templ);
+ }
+
+ void WriteText(const char* text, size_t text_len) {
+ ssize_t r = HANDLE_EINTR(write(fd_, text, text_len));
+ if (r != static_cast<ssize_t>(text_len)) {
+ close(fd_);
+ fd_ = -1;
+ return;
+ }
+
+ lseek(fd_, 0, SEEK_SET);
+ }
+
+ int fd_;
+};
+
+} // namespace google_breakpad
+
+#endif // GOOGLE_BREAKPAD_COMMON_LINUX_TESTS_AUTO_TESTFILE
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.cc b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.cc
new file mode 100644
index 000000000..c9491f6f2
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.cc
@@ -0,0 +1,322 @@
+// 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.
+
+// crash_generator.cc: Implement google_breakpad::CrashGenerator.
+// See crash_generator.h for details.
+
+#include "common/linux/tests/crash_generator.h"
+
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <string>
+
+#if defined(__ANDROID__)
+#include "common/android/testing/pthread_fixes.h"
+#endif
+#include "common/linux/eintr_wrapper.h"
+#include "common/tests/auto_tempdir.h"
+#include "common/tests/file_utils.h"
+#include "common/using_std_string.h"
+
+namespace {
+
+struct ThreadData {
+ pthread_t thread;
+ pthread_barrier_t* barrier;
+ pid_t* thread_id_ptr;
+};
+
+const char* const kProcFilesToCopy[] = {
+ "auxv", "cmdline", "environ", "maps", "status"
+};
+const size_t kNumProcFilesToCopy =
+ sizeof(kProcFilesToCopy) / sizeof(kProcFilesToCopy[0]);
+
+int gettid() {
+ // Glibc does not provide a wrapper for this.
+ return syscall(__NR_gettid);
+}
+
+int tkill(pid_t tid, int sig) {
+ // Glibc does not provide a wrapper for this.
+ return syscall(__NR_tkill, tid, sig);
+}
+
+// Core file size limit set to 1 MB, which is big enough for test purposes.
+const rlim_t kCoreSizeLimit = 1024 * 1024;
+
+void *thread_function(void *data) {
+ ThreadData* thread_data = reinterpret_cast<ThreadData*>(data);
+ volatile pid_t thread_id = gettid();
+ *(thread_data->thread_id_ptr) = thread_id;
+ int result = pthread_barrier_wait(thread_data->barrier);
+ if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
+ perror("Failed to wait for sync barrier");
+ exit(1);
+ }
+ while (true) {
+ pthread_yield();
+ }
+}
+
+} // namespace
+
+namespace google_breakpad {
+
+CrashGenerator::CrashGenerator()
+ : shared_memory_(NULL),
+ shared_memory_size_(0) {
+}
+
+CrashGenerator::~CrashGenerator() {
+ UnmapSharedMemory();
+}
+
+bool CrashGenerator::HasDefaultCorePattern() const {
+ char buffer[8];
+ ssize_t buffer_size = sizeof(buffer);
+ return ReadFile("/proc/sys/kernel/core_pattern", buffer, &buffer_size) &&
+ buffer_size == 5 && memcmp(buffer, "core", 4) == 0;
+}
+
+string CrashGenerator::GetCoreFilePath() const {
+ return temp_dir_.path() + "/core";
+}
+
+string CrashGenerator::GetDirectoryOfProcFilesCopy() const {
+ return temp_dir_.path() + "/proc";
+}
+
+pid_t CrashGenerator::GetThreadId(unsigned index) const {
+ return reinterpret_cast<pid_t*>(shared_memory_)[index];
+}
+
+pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) {
+ return reinterpret_cast<pid_t*>(shared_memory_) + index;
+}
+
+bool CrashGenerator::MapSharedMemory(size_t memory_size) {
+ if (!UnmapSharedMemory())
+ return false;
+
+ void* mapped_memory = mmap(0, memory_size, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+ if (mapped_memory == MAP_FAILED) {
+ perror("CrashGenerator: Failed to map shared memory");
+ return false;
+ }
+
+ memset(mapped_memory, 0, memory_size);
+ shared_memory_ = mapped_memory;
+ shared_memory_size_ = memory_size;
+ return true;
+}
+
+bool CrashGenerator::UnmapSharedMemory() {
+ if (!shared_memory_)
+ return true;
+
+ if (munmap(shared_memory_, shared_memory_size_) == 0) {
+ shared_memory_ = NULL;
+ shared_memory_size_ = 0;
+ return true;
+ }
+
+ perror("CrashGenerator: Failed to unmap shared memory");
+ return false;
+}
+
+bool CrashGenerator::SetCoreFileSizeLimit(rlim_t limit) const {
+ struct rlimit limits = { limit, limit };
+ if (setrlimit(RLIMIT_CORE, &limits) == -1) {
+ perror("CrashGenerator: Failed to set core file size limit");
+ return false;
+ }
+ return true;
+}
+
+bool CrashGenerator::CreateChildCrash(
+ unsigned num_threads, unsigned crash_thread, int crash_signal,
+ pid_t* child_pid) {
+ if (num_threads == 0 || crash_thread >= num_threads) {
+ fprintf(stderr, "CrashGenerator: Invalid thread counts; num_threads=%u"
+ " crash_thread=%u\n", num_threads, crash_thread);
+ return false;
+ }
+
+ if (!MapSharedMemory(num_threads * sizeof(pid_t))) {
+ perror("CrashGenerator: Unable to map shared memory");
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ if (chdir(temp_dir_.path().c_str()) == -1) {
+ perror("CrashGenerator: Failed to change directory");
+ exit(1);
+ }
+ if (SetCoreFileSizeLimit(kCoreSizeLimit)) {
+ CreateThreadsInChildProcess(num_threads);
+ string proc_dir = GetDirectoryOfProcFilesCopy();
+ if (mkdir(proc_dir.c_str(), 0755) == -1) {
+ perror("CrashGenerator: Failed to create proc directory");
+ exit(1);
+ }
+ if (!CopyProcFiles(getpid(), proc_dir.c_str())) {
+ fprintf(stderr, "CrashGenerator: Failed to copy proc files\n");
+ exit(1);
+ }
+ // On Android the signal sometimes doesn't seem to get sent even though
+ // tkill returns '0'. Retry a couple of times if the signal doesn't get
+ // through on the first go:
+ // https://code.google.com/p/google-breakpad/issues/detail?id=579
+#if defined(__ANDROID__)
+ const int kRetries = 60;
+ const unsigned int kSleepTimeInSeconds = 1;
+#else
+ const int kRetries = 1;
+ const unsigned int kSleepTimeInSeconds = 600;
+#endif
+ for (int i = 0; i < kRetries; i++) {
+ if (tkill(*GetThreadIdPointer(crash_thread), crash_signal) == -1) {
+ perror("CrashGenerator: Failed to kill thread by signal");
+ } else {
+ // At this point, we've queued the signal for delivery, but there's no
+ // guarantee when it'll be delivered. We don't want the main thread to
+ // race and exit before the thread we signaled is processed. So sleep
+ // long enough that we won't flake even under fairly high load.
+ // TODO: See if we can't be a bit more deterministic. There doesn't
+ // seem to be an API to check on signal delivery status, so we can't
+ // really poll and wait for the kernel to declare the signal has been
+ // delivered. If it has, and things worked, we'd be killed, so the
+ // sleep length doesn't really matter.
+ sleep(kSleepTimeInSeconds);
+ }
+ }
+ } else {
+ perror("CrashGenerator: Failed to set core limit");
+ }
+ exit(1);
+ } else if (pid == -1) {
+ perror("CrashGenerator: Failed to create child process");
+ return false;
+ }
+
+ int status;
+ if (HANDLE_EINTR(waitpid(pid, &status, 0)) == -1) {
+ perror("CrashGenerator: Failed to wait for child process");
+ return false;
+ }
+ if (!WIFSIGNALED(status) || WTERMSIG(status) != crash_signal) {
+ fprintf(stderr, "CrashGenerator: Child process not killed by the expected signal\n"
+ " exit status=0x%x pid=%u signaled=%s sig=%d expected=%d\n",
+ status, pid, WIFSIGNALED(status) ? "true" : "false",
+ WTERMSIG(status), crash_signal);
+ return false;
+ }
+
+ if (child_pid)
+ *child_pid = pid;
+ return true;
+}
+
+bool CrashGenerator::CopyProcFiles(pid_t pid, const char* path) const {
+ char from_path[PATH_MAX], to_path[PATH_MAX];
+ for (size_t i = 0; i < kNumProcFilesToCopy; ++i) {
+ int num_chars = snprintf(from_path, PATH_MAX, "/proc/%d/%s",
+ pid, kProcFilesToCopy[i]);
+ if (num_chars < 0 || num_chars >= PATH_MAX)
+ return false;
+
+ num_chars = snprintf(to_path, PATH_MAX, "%s/%s",
+ path, kProcFilesToCopy[i]);
+ if (num_chars < 0 || num_chars >= PATH_MAX)
+ return false;
+
+ if (!CopyFile(from_path, to_path))
+ return false;
+ }
+ return true;
+}
+
+void CrashGenerator::CreateThreadsInChildProcess(unsigned num_threads) {
+ *GetThreadIdPointer(0) = getpid();
+
+ if (num_threads <= 1)
+ return;
+
+ // This method does not clean up any pthread resource, as the process
+ // is expected to be killed anyway.
+ ThreadData* thread_data = new ThreadData[num_threads];
+
+ // Create detached threads so that we do not worry about pthread_join()
+ // later being called or not.
+ pthread_attr_t thread_attributes;
+ if (pthread_attr_init(&thread_attributes) != 0 ||
+ pthread_attr_setdetachstate(&thread_attributes,
+ PTHREAD_CREATE_DETACHED) != 0) {
+ fprintf(stderr, "CrashGenerator: Failed to initialize thread attribute\n");
+ exit(1);
+ }
+
+ pthread_barrier_t thread_barrier;
+ if (pthread_barrier_init(&thread_barrier, NULL, num_threads) != 0) {
+ fprintf(stderr, "CrashGenerator: Failed to initialize thread barrier\n");
+ exit(1);
+ }
+
+ for (unsigned i = 1; i < num_threads; ++i) {
+ thread_data[i].barrier = &thread_barrier;
+ thread_data[i].thread_id_ptr = GetThreadIdPointer(i);
+ if (pthread_create(&thread_data[i].thread, &thread_attributes,
+ thread_function, &thread_data[i]) != 0) {
+ fprintf(stderr, "CrashGenerator: Failed to create thread %d\n", i);
+ exit(1);
+ }
+ }
+
+ int result = pthread_barrier_wait(&thread_barrier);
+ if (result != 0 && result != PTHREAD_BARRIER_SERIAL_THREAD) {
+ fprintf(stderr, "CrashGenerator: Failed to wait for thread barrier\n");
+ exit(1);
+ }
+
+ pthread_barrier_destroy(&thread_barrier);
+ pthread_attr_destroy(&thread_attributes);
+ delete[] thread_data;
+}
+
+} // namespace google_breakpad
diff --git a/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.h b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.h
new file mode 100644
index 000000000..7e2fcbf98
--- /dev/null
+++ b/toolkit/crashreporter/google-breakpad/src/common/linux/tests/crash_generator.h
@@ -0,0 +1,117 @@
+// 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.
+
+// crash_generator.h: Define the google_breakpad::CrashGenerator class,
+// which is used to generate a crash (and a core dump file) for testing.
+
+#ifndef COMMON_LINUX_TESTS_CRASH_GENERATOR_H_
+#define COMMON_LINUX_TESTS_CRASH_GENERATOR_H_
+
+#include <sys/resource.h>
+
+#include <string>
+
+#include "common/tests/auto_tempdir.h"
+#include "common/using_std_string.h"
+
+namespace google_breakpad {
+
+// A utility class for generating a crash (and a core dump file) for
+// testing. It creates a child process with the specified number of
+// threads, which is then termainated by the specified signal. A core
+// dump file is expected to be created upon the termination of the child
+// process, which can then be used for testing code that processes core
+// dump files.
+class CrashGenerator {
+ public:
+ CrashGenerator();
+
+ ~CrashGenerator();
+
+ // Returns true if a core dump file named 'core' will be generated in
+ // the current directory for a test that produces a crash by checking
+ // if /proc/sys/kernel/core_pattern has the default value 'core'.
+ bool HasDefaultCorePattern() const;
+
+ // Returns the expected path of the core dump file.
+ string GetCoreFilePath() const;
+
+ // Returns the directory of a copy of proc files of the child process.
+ string GetDirectoryOfProcFilesCopy() const;
+
+ // Creates a crash (and a core dump file) by creating a child process with
+ // |num_threads| threads, and the terminating the child process by sending
+ // a signal with number |crash_signal| to the |crash_thread|-th thread.
+ // Returns true on success.
+ bool CreateChildCrash(unsigned num_threads, unsigned crash_thread,
+ int crash_signal, pid_t* child_pid);
+
+ // Returns the thread ID of the |index|-th thread in the child process.
+ // This method does not validate |index|.
+ pid_t GetThreadId(unsigned index) const;
+
+ private:
+ // Copies the following proc files of the process with |pid| to the directory
+ // at |path|: auxv, cmdline, environ, maps, status
+ // The directory must have been created. Returns true on success.
+ bool CopyProcFiles(pid_t pid, const char* path) const;
+
+ // Creates |num_threads| threads in the child process.
+ void CreateThreadsInChildProcess(unsigned num_threads);
+
+ // Sets the maximum size of core dump file (both the soft and hard limit)
+ // to |limit| bytes. Returns true on success.
+ bool SetCoreFileSizeLimit(rlim_t limit) const;
+
+ // Creates a shared memory of |memory_size| bytes for communicating thread
+ // IDs between the parent and child process. Returns true on success.
+ bool MapSharedMemory(size_t memory_size);
+
+ // Releases any shared memory created by MapSharedMemory(). Returns true on
+ // success.
+ bool UnmapSharedMemory();
+
+ // Returns the pointer to the thread ID of the |index|-th thread in the child
+ // process. This method does not validate |index|.
+ pid_t* GetThreadIdPointer(unsigned index);
+
+ // Temporary directory in which a core file is generated.
+ AutoTempDir temp_dir_;
+
+ // Shared memory for communicating thread IDs between the parent and
+ // child process.
+ void* shared_memory_;
+
+ // Number of bytes mapped for |shared_memory_|.
+ size_t shared_memory_size_;
+};
+
+} // namespace google_breakpad
+
+#endif // COMMON_LINUX_TESTS_CRASH_GENERATOR_H_