зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 1adfbab318a6 (bug 1264367) for breaking crash stacks CLOSED TREE
--HG-- extra : amend_source : 3dc85338e38502f95b5f667babbd6cf0282c70d1
This commit is contained in:
Родитель
04c44176b6
Коммит
265355461f
|
@ -3,39 +3,49 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/memory.h"
|
||||
|
||||
using std::string;
|
||||
//TODO: move this somewhere common, this is copied from dump_symbols.cc
|
||||
// Format the Elf file identifier in IDENTIFIER as a UUID with the
|
||||
// dashes removed.
|
||||
void FormatIdentifier(unsigned char identifier[google_breakpad::kMDGUIDSize],
|
||||
char result_guid[40]) {
|
||||
char identifier_str[40];
|
||||
google_breakpad::FileID::ConvertIdentifierToString(
|
||||
identifier,
|
||||
identifier_str,
|
||||
sizeof(identifier_str));
|
||||
int bufpos = 0;
|
||||
for (int i = 0; identifier_str[i] != '\0'; ++i)
|
||||
if (identifier_str[i] != '-')
|
||||
result_guid[bufpos++] = identifier_str[i];
|
||||
// Add an extra "0" by the end. PDB files on Windows have an 'age'
|
||||
// number appended to the end of the file identifier; this isn't
|
||||
// really used or necessary on other platforms, but let's preserve
|
||||
// the pattern.
|
||||
result_guid[bufpos++] = '0';
|
||||
// And null terminate.
|
||||
result_guid[bufpos] = '\0';
|
||||
}
|
||||
|
||||
using google_breakpad::auto_wasteful_vector;
|
||||
using google_breakpad::FileID;
|
||||
using google_breakpad::PageAllocator;
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: fileid <elf file>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
PageAllocator allocator;
|
||||
auto_wasteful_vector<uint8_t, sizeof(MDGUID)> identifier(&allocator);
|
||||
FileID file_id(argv[1]);
|
||||
unsigned char identifier[google_breakpad::kMDGUIDSize];
|
||||
google_breakpad::FileID file_id(argv[1]);
|
||||
if (!file_id.ElfFileIdentifier(identifier)) {
|
||||
fprintf(stderr, "%s: unable to generate file identifier\n",
|
||||
argv[1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
string result_guid = FileID::ConvertIdentifierToUUIDString(identifier);
|
||||
|
||||
// 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.
|
||||
printf("%s0\n", result_guid.c_str());
|
||||
char result_guid[40];
|
||||
FormatIdentifier(identifier, result_guid);
|
||||
printf("%s\n", result_guid);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
|
||||
# Ignore common compiled artifacts.
|
||||
*~
|
||||
*.dwo
|
||||
*.o
|
||||
lib*.a
|
||||
/breakpad.pc
|
||||
|
@ -46,7 +45,6 @@ lib*.a
|
|||
/src/tools/linux/md2core/minidump-2-core
|
||||
/src/tools/linux/symupload/minidump_upload
|
||||
/src/tools/linux/symupload/sym_upload
|
||||
/src/tools/mac/dump_syms/dump_syms
|
||||
|
||||
# Ignore autotools generated artifacts.
|
||||
.deps
|
||||
|
|
|
@ -35,31 +35,23 @@
|
|||
deps = {
|
||||
# Logging code.
|
||||
"src/src/third_party/glog":
|
||||
"https://github.com/google/glog.git" +
|
||||
"@v0.3.4",
|
||||
"http://google-glog.googlecode.com/svn/trunk@97",
|
||||
|
||||
# Testing libraries and utilities.
|
||||
"src/src/testing":
|
||||
"https://github.com/google/googlemock.git" +
|
||||
"@release-1.7.0",
|
||||
"src/src/testing/gtest":
|
||||
"https://github.com/google/googletest.git" +
|
||||
"@release-1.7.0",
|
||||
"src/src/testing": "http://googlemock.googlecode.com/svn/trunk@408",
|
||||
"src/src/testing/gtest": "http://googletest.googlecode.com/svn/trunk@615",
|
||||
|
||||
# Protobuf.
|
||||
"src/src/third_party/protobuf/protobuf":
|
||||
"https://github.com/google/protobuf.git" +
|
||||
"@cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac",
|
||||
"http://protobuf.googlecode.com/svn/trunk@407",
|
||||
|
||||
# GYP project generator.
|
||||
"src/src/tools/gyp":
|
||||
"https://chromium.googlesource.com/external/gyp/" +
|
||||
"@e8ab0833a42691cd2184bd4c45d779e43821d3e0",
|
||||
"src/src/tools/gyp": "http://gyp.googlecode.com/svn/trunk@1886",
|
||||
|
||||
# Linux syscall support.
|
||||
"src/src/third_party/lss":
|
||||
"https://chromium.googlesource.com/linux-syscall-support/" +
|
||||
"@3f6478ac95edf86cd3da300c2c0d34a438f5dbeb",
|
||||
"@9292030109847793f7a6689adac1ddafb412fe14"
|
||||
}
|
||||
|
||||
hooks = [
|
||||
|
|
|
@ -1 +1 @@
|
|||
704f41ec901c419f8c321742114b415e6f5ceacc
|
||||
c53ed143108948eb7e2d7ee77dc8c0d92050ce7c
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -6,7 +6,7 @@ on Android, and later generate valid stack traces from the minidumps
|
|||
it generates.
|
||||
|
||||
This release supports ARM, x86 and MIPS based Android systems.
|
||||
This release requires NDK release r11c or higher.
|
||||
This release requires NDK release r10c or higher.
|
||||
|
||||
I. Building the client library:
|
||||
===============================
|
||||
|
|
|
@ -3,80 +3,45 @@
|
|||
Breakpad is a set of client and server components which implement a
|
||||
crash-reporting system.
|
||||
|
||||
* [Homepage](https://chromium.googlesource.com/breakpad/breakpad/)
|
||||
* [Documentation](https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/)
|
||||
* [Bugs](https://bugs.chromium.org/p/google-breakpad/)
|
||||
* Discussion/Questions: [google-breakpad-discuss@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-discuss)
|
||||
* Developer/Reviews: [google-breakpad-dev@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-dev)
|
||||
* Tests: [![Build Status](https://travis-ci.org/google/breakpad.svg?branch=master)](https://travis-ci.org/google/breakpad)
|
||||
* Coverage [![Coverity Status](https://scan.coverity.com/projects/9215/badge.svg)](https://scan.coverity.com/projects/google-breakpad)
|
||||
## Getting started in 32-bit mode (from trunk)
|
||||
|
||||
## Getting started (from master)
|
||||
|
||||
1. First, [download depot_tools](http://dev.chromium.org/developers/how-tos/install-depot-tools)
|
||||
and ensure that they’re in your `PATH`.
|
||||
|
||||
2. Create a new directory for checking out the source code (it must be named
|
||||
breakpad).
|
||||
|
||||
```sh
|
||||
mkdir breakpad && cd breakpad
|
||||
```
|
||||
|
||||
3. Run the `fetch` tool from depot_tools to download all the source repos.
|
||||
|
||||
```sh
|
||||
fetch breakpad
|
||||
cd src
|
||||
```
|
||||
|
||||
4. Build the source.
|
||||
|
||||
```sh
|
||||
./configure && make
|
||||
```
|
||||
|
||||
You can also cd to another directory and run configure from there to build
|
||||
outside the source tree.
|
||||
|
||||
This will build the processor tools (`src/processor/minidump_stackwalk`,
|
||||
`src/processor/minidump_dump`, etc), and when building on Linux it will
|
||||
also build the client libraries and some tools
|
||||
(`src/tools/linux/dump_syms/dump_syms`,
|
||||
`src/tools/linux/md2core/minidump-2-core`, etc).
|
||||
|
||||
5. Optionally, run tests.
|
||||
|
||||
```sh
|
||||
make check
|
||||
```
|
||||
|
||||
6. Optionally, install the built libraries
|
||||
|
||||
```sh
|
||||
make install
|
||||
```
|
||||
```sh
|
||||
# Configure
|
||||
CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure
|
||||
# Build
|
||||
make
|
||||
# Test
|
||||
make check
|
||||
# Install
|
||||
make install
|
||||
```
|
||||
|
||||
If you need to reconfigure your build be sure to run `make distclean` first.
|
||||
|
||||
To update an existing checkout to a newer revision, you can
|
||||
`git pull` as usual, but then you should run `gclient sync` to ensure that the
|
||||
dependent repos are up-to-date.
|
||||
## To request change review:
|
||||
|
||||
## To request change review
|
||||
1. Get a copy of depot_tools repo.
|
||||
http://dev.chromium.org/developers/how-tos/install-depot-tools
|
||||
|
||||
1. Follow the steps above to get the source and build it.
|
||||
2. Create a new directory for checking out the source code.
|
||||
mkdir breakpad && cd breakpad
|
||||
|
||||
2. Make changes. Build and test your changes.
|
||||
3. Run the `fetch` tool from depot_tools to download all the source repos.
|
||||
`fetch breakpad`
|
||||
|
||||
4. Make changes. Build and test your changes.
|
||||
For core code like processor use methods above.
|
||||
For linux/mac/windows, there are test targets in each project file.
|
||||
|
||||
3. Commit your changes to your local repo and upload them to the server.
|
||||
5. Commit your changes to your local repo and upload them to the server.
|
||||
http://dev.chromium.org/developers/contributing-code
|
||||
e.g. `git commit ... && git cl upload ...`
|
||||
You will be prompted for credential and a description.
|
||||
|
||||
4. At https://chromium-review.googlesource.com/ you'll find your issue listed;
|
||||
click on it, then “Add reviewer”, and enter in the code reviewer. Depending
|
||||
on your settings, you may not see an email, but the reviewer has been
|
||||
notified with google-breakpad-dev@googlegroups.com always CC’d.
|
||||
6. At https://codereview.chromium.org/ you'll find your issue listed; click on
|
||||
it, and select Publish+Mail, and enter in the code reviewer and CC
|
||||
google-breakpad-dev@googlegroups.com
|
||||
|
||||
## Documentation
|
||||
|
||||
Visit https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/
|
||||
|
|
|
@ -56,66 +56,6 @@ m4_ifndef([AC_AUTOCONF_VERSION],
|
|||
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
|
||||
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
|
||||
|
||||
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# AM_PROG_AR([ACT-IF-FAIL])
|
||||
# -------------------------
|
||||
# Try to determine the archiver interface, and trigger the ar-lib wrapper
|
||||
# if it is needed. If the detection of archiver interface fails, run
|
||||
# ACT-IF-FAIL (default is to abort configure with a proper error message).
|
||||
AC_DEFUN([AM_PROG_AR],
|
||||
[AC_BEFORE([$0], [LT_INIT])dnl
|
||||
AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl
|
||||
AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||
AC_REQUIRE_AUX_FILE([ar-lib])dnl
|
||||
AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false])
|
||||
: ${AR=ar}
|
||||
|
||||
AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface],
|
||||
[AC_LANG_PUSH([C])
|
||||
am_cv_ar_interface=ar
|
||||
AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])],
|
||||
[am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
|
||||
AC_TRY_EVAL([am_ar_try])
|
||||
if test "$ac_status" -eq 0; then
|
||||
am_cv_ar_interface=ar
|
||||
else
|
||||
am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD'
|
||||
AC_TRY_EVAL([am_ar_try])
|
||||
if test "$ac_status" -eq 0; then
|
||||
am_cv_ar_interface=lib
|
||||
else
|
||||
am_cv_ar_interface=unknown
|
||||
fi
|
||||
fi
|
||||
rm -f conftest.lib libconftest.a
|
||||
])
|
||||
AC_LANG_POP([C])])
|
||||
|
||||
case $am_cv_ar_interface in
|
||||
ar)
|
||||
;;
|
||||
lib)
|
||||
# Microsoft lib, so override with the ar-lib wrapper script.
|
||||
# FIXME: It is wrong to rewrite AR.
|
||||
# But if we don't then we get into trouble of one sort or another.
|
||||
# A longer-term fix would be to have automake use am__AR in this case,
|
||||
# and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something
|
||||
# similar.
|
||||
AR="$am_aux_dir/ar-lib $AR"
|
||||
;;
|
||||
unknown)
|
||||
m4_default([$1],
|
||||
[AC_MSG_ERROR([could not determine $AR interface])])
|
||||
;;
|
||||
esac
|
||||
AC_SUBST([AR])dnl
|
||||
])
|
||||
|
||||
# Figure out how to run the assembler. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2001-2014 Free Software Foundation, Inc.
|
||||
|
|
|
@ -71,12 +71,9 @@ LOCAL_ARM_MODE := arm
|
|||
# List of client source files, directly taken from Makefile.am
|
||||
LOCAL_SRC_FILES := \
|
||||
src/client/linux/crash_generation/crash_generation_client.cc \
|
||||
src/client/linux/dump_writer_common/thread_info.cc \
|
||||
src/client/linux/dump_writer_common/ucontext_reader.cc \
|
||||
src/client/linux/handler/exception_handler.cc \
|
||||
src/client/linux/handler/minidump_descriptor.cc \
|
||||
src/client/linux/log/log.cc \
|
||||
src/client/linux/microdump_writer/microdump_writer.cc \
|
||||
src/client/linux/minidump_writer/linux_dumper.cc \
|
||||
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
|
||||
src/client/linux/minidump_writer/minidump_writer.cc \
|
||||
|
|
|
@ -29,4 +29,3 @@
|
|||
|
||||
APP_STL := stlport_static
|
||||
APP_ABI := all
|
||||
APP_CXXFLAGS := -std=c++11 -D__STDC_LIMIT_MACROS
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
scriptversion=2012-10-14.11; # UTC
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#! /bin/sh
|
||||
# Attempt to guess a canonical system name.
|
||||
# Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2016-01-01'
|
||||
timestamp='2015-03-04'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
|
@ -27,7 +27,7 @@ timestamp='2016-01-01'
|
|||
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
|
||||
#
|
||||
# You can get the latest version of this script from:
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
|
||||
#
|
||||
# Please send patches to <config-patches@gnu.org>.
|
||||
|
||||
|
@ -50,7 +50,7 @@ version="\
|
|||
GNU config.guess ($timestamp)
|
||||
|
||||
Originally written by Per Bothner.
|
||||
Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
|
@ -221,7 +221,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
|||
release='-gnu'
|
||||
;;
|
||||
*)
|
||||
release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
|
||||
release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
|
||||
;;
|
||||
esac
|
||||
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
|
||||
|
@ -249,9 +249,6 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
|||
*:MirBSD:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
|
||||
exit ;;
|
||||
*:Sortix:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-sortix
|
||||
exit ;;
|
||||
alpha:OSF1:*:*)
|
||||
case $UNAME_RELEASE in
|
||||
*4.0)
|
||||
|
@ -965,9 +962,6 @@ EOF
|
|||
ia64:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
k1om:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
m32r*:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
|
@ -1044,7 +1038,7 @@ EOF
|
|||
echo ${UNAME_MACHINE}-dec-linux-${LIBC}
|
||||
exit ;;
|
||||
x86_64:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-pc-linux-${LIBC}
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
exit ;;
|
||||
xtensa*:Linux:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||
|
@ -1123,7 +1117,7 @@ EOF
|
|||
# uname -m prints for DJGPP always 'pc', but it prints nothing about
|
||||
# the processor, so we play safe by assuming i586.
|
||||
# Note: whatever this is, it MUST be the same as what config.sub
|
||||
# prints for the "djgpp" host, or else GDB configure will decide that
|
||||
# prints for the "djgpp" host, or else GDB configury will decide that
|
||||
# this is a cross-build.
|
||||
echo i586-pc-msdosdjgpp
|
||||
exit ;;
|
||||
|
@ -1393,9 +1387,6 @@ EOF
|
|||
x86_64:VMkernel:*:*)
|
||||
echo ${UNAME_MACHINE}-unknown-esx
|
||||
exit ;;
|
||||
amd64:Isilon\ OneFS:*:*)
|
||||
echo x86_64-unknown-onefs
|
||||
exit ;;
|
||||
esac
|
||||
|
||||
cat >&2 <<EOF
|
||||
|
@ -1405,9 +1396,9 @@ This script, last modified $timestamp, has failed to recognize
|
|||
the operating system you are using. It is advised that you
|
||||
download the most up to date version of the config scripts from
|
||||
|
||||
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
|
||||
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
|
||||
and
|
||||
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
|
||||
http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
|
||||
|
||||
If the version you run ($0) is already up to date, please
|
||||
send the following data and any information you think might be
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#! /bin/sh
|
||||
# Configuration validation subroutine script.
|
||||
# Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
# Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
timestamp='2016-01-01'
|
||||
timestamp='2015-03-08'
|
||||
|
||||
# This file is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
|
@ -33,7 +33,7 @@ timestamp='2016-01-01'
|
|||
# Otherwise, we print the canonical config type on stdout and succeed.
|
||||
|
||||
# You can get the latest version of this script from:
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
|
||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
|
||||
|
||||
# This file is supposed to be the same for all GNU packages
|
||||
# and recognize all the CPU types, system types and aliases
|
||||
|
@ -53,7 +53,8 @@ timestamp='2016-01-01'
|
|||
me=`echo "$0" | sed -e 's,.*/,,'`
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
|
||||
Usage: $0 [OPTION] CPU-MFR-OPSYS
|
||||
$0 [OPTION] ALIAS
|
||||
|
||||
Canonicalize a configuration name.
|
||||
|
||||
|
@ -67,7 +68,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
|||
version="\
|
||||
GNU config.sub ($timestamp)
|
||||
|
||||
Copyright 1992-2016 Free Software Foundation, Inc.
|
||||
Copyright 1992-2015 Free Software Foundation, Inc.
|
||||
|
||||
This is free software; see the source for copying conditions. There is NO
|
||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||
|
@ -254,7 +255,6 @@ case $basic_machine in
|
|||
| arc | arceb \
|
||||
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
|
||||
| avr | avr32 \
|
||||
| ba \
|
||||
| be32 | be64 \
|
||||
| bfin \
|
||||
| c4x | c8051 | clipper \
|
||||
|
@ -305,7 +305,7 @@ case $basic_machine in
|
|||
| riscv32 | riscv64 \
|
||||
| rl78 | rx \
|
||||
| score \
|
||||
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
|
||||
| sh64 | sh64le \
|
||||
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
|
||||
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
|
||||
|
@ -376,7 +376,6 @@ case $basic_machine in
|
|||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
|
||||
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
|
||||
| avr-* | avr32-* \
|
||||
| ba-* \
|
||||
| be32-* | be64-* \
|
||||
| bfin-* | bs2000-* \
|
||||
| c[123]* | c30-* | [cjt]90-* | c4x-* \
|
||||
|
@ -429,13 +428,12 @@ case $basic_machine in
|
|||
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
||||
| pyramid-* \
|
||||
| riscv32-* | riscv64-* \
|
||||
| rl78-* | romp-* | rs6000-* | rx-* \
|
||||
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
|
||||
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
|
||||
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
|
||||
| sparclite-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
|
||||
| sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx?-* \
|
||||
| tahoe-* \
|
||||
| tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
|
||||
| tile*-* \
|
||||
|
@ -520,7 +518,7 @@ case $basic_machine in
|
|||
basic_machine=i386-pc
|
||||
os=-aros
|
||||
;;
|
||||
asmjs)
|
||||
asmjs)
|
||||
basic_machine=asmjs-unknown
|
||||
;;
|
||||
aux)
|
||||
|
@ -1378,7 +1376,7 @@ case $os in
|
|||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
|
||||
| -sym* | -kopensolaris* | -plan9* \
|
||||
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
|
||||
| -aos* | -aros* | -cloudabi* | -sortix* \
|
||||
| -aos* | -aros* | -cloudabi* \
|
||||
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
||||
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
|
||||
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
|
||||
|
@ -1398,8 +1396,7 @@ case $os in
|
|||
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
|
||||
| -onefs* | -tirtos*)
|
||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
|
||||
# Remember, each alternative MUST END IN *, to match a version number.
|
||||
;;
|
||||
-qnx*)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
scriptversion=2013-05-30.07; # UTC
|
||||
|
||||
# Copyright (C) 1999-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
# install - install a program, script, or datafile
|
||||
|
||||
scriptversion=2013-12-25.23; # UTC
|
||||
scriptversion=2011-11-20.07; # UTC
|
||||
|
||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||
|
@ -41,15 +41,19 @@ scriptversion=2013-12-25.23; # UTC
|
|||
# This script is compatible with the BSD install script, but was written
|
||||
# from scratch.
|
||||
|
||||
tab=' '
|
||||
nl='
|
||||
'
|
||||
IFS=" $tab$nl"
|
||||
IFS=" "" $nl"
|
||||
|
||||
# Set DOITPROG to "echo" to test this script.
|
||||
# set DOITPROG to echo to test this script
|
||||
|
||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||
doit=${DOITPROG-}
|
||||
doit_exec=${doit:-exec}
|
||||
if test -z "$doit"; then
|
||||
doit_exec=exec
|
||||
else
|
||||
doit_exec=$doit
|
||||
fi
|
||||
|
||||
# Put in absolute file names if you don't have them in your path;
|
||||
# or use environment vars.
|
||||
|
@ -64,6 +68,17 @@ mvprog=${MVPROG-mv}
|
|||
rmprog=${RMPROG-rm}
|
||||
stripprog=${STRIPPROG-strip}
|
||||
|
||||
posix_glob='?'
|
||||
initialize_posix_glob='
|
||||
test "$posix_glob" != "?" || {
|
||||
if (set -f) 2>/dev/null; then
|
||||
posix_glob=
|
||||
else
|
||||
posix_glob=:
|
||||
fi
|
||||
}
|
||||
'
|
||||
|
||||
posix_mkdir=
|
||||
|
||||
# Desired mode of installed file.
|
||||
|
@ -82,7 +97,7 @@ dir_arg=
|
|||
dst_arg=
|
||||
|
||||
copy_on_change=false
|
||||
is_target_a_directory=possibly
|
||||
no_target_directory=
|
||||
|
||||
usage="\
|
||||
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||
|
@ -122,57 +137,46 @@ while test $# -ne 0; do
|
|||
-d) dir_arg=true;;
|
||||
|
||||
-g) chgrpcmd="$chgrpprog $2"
|
||||
shift;;
|
||||
shift;;
|
||||
|
||||
--help) echo "$usage"; exit $?;;
|
||||
|
||||
-m) mode=$2
|
||||
case $mode in
|
||||
*' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
case $mode in
|
||||
*' '* | *' '* | *'
|
||||
'* | *'*'* | *'?'* | *'['*)
|
||||
echo "$0: invalid mode: $mode" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-o) chowncmd="$chownprog $2"
|
||||
shift;;
|
||||
shift;;
|
||||
|
||||
-s) stripcmd=$stripprog;;
|
||||
|
||||
-t)
|
||||
is_target_a_directory=always
|
||||
dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
-t) dst_arg=$2
|
||||
# Protect names problematic for 'test' and other utilities.
|
||||
case $dst_arg in
|
||||
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||
esac
|
||||
shift;;
|
||||
|
||||
-T) is_target_a_directory=never;;
|
||||
-T) no_target_directory=true;;
|
||||
|
||||
--version) echo "$0 $scriptversion"; exit $?;;
|
||||
|
||||
--) shift
|
||||
break;;
|
||||
--) shift
|
||||
break;;
|
||||
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
-*) echo "$0: invalid option: $1" >&2
|
||||
exit 1;;
|
||||
|
||||
*) break;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# We allow the use of options -d and -T together, by making -d
|
||||
# take the precedence; this is for compatibility with GNU install.
|
||||
|
||||
if test -n "$dir_arg"; then
|
||||
if test -n "$dst_arg"; then
|
||||
echo "$0: target directory not allowed when installing a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||
# When -d is used, all remaining arguments are directories to create.
|
||||
# When -t is used, the destination is already specified.
|
||||
|
@ -203,15 +207,6 @@ if test $# -eq 0; then
|
|||
exit 0
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
if test $# -gt 1 || test "$is_target_a_directory" = always; then
|
||||
if test ! -d "$dst_arg"; then
|
||||
echo "$0: $dst_arg: Is not a directory." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -z "$dir_arg"; then
|
||||
do_exit='(exit $ret); exit $ret'
|
||||
trap "ret=129; $do_exit" 1
|
||||
|
@ -228,16 +223,16 @@ if test -z "$dir_arg"; then
|
|||
|
||||
*[0-7])
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw='% 200'
|
||||
u_plus_rw='% 200'
|
||||
fi
|
||||
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
|
||||
*)
|
||||
if test -z "$stripcmd"; then
|
||||
u_plus_rw=
|
||||
u_plus_rw=
|
||||
else
|
||||
u_plus_rw=,u+rw
|
||||
u_plus_rw=,u+rw
|
||||
fi
|
||||
cp_umask=$mode$u_plus_rw;;
|
||||
esac
|
||||
|
@ -274,15 +269,41 @@ do
|
|||
# If destination is a directory, append the input filename; won't work
|
||||
# if double slashes aren't ignored.
|
||||
if test -d "$dst"; then
|
||||
if test "$is_target_a_directory" = never; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
if test -n "$no_target_directory"; then
|
||||
echo "$0: $dst_arg: Is a directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
dstdir=$dst
|
||||
dst=$dstdir/`basename "$src"`
|
||||
dstdir_status=0
|
||||
else
|
||||
dstdir=`dirname "$dst"`
|
||||
# Prefer dirname, but fall back on a substitute if dirname fails.
|
||||
dstdir=`
|
||||
(dirname "$dst") 2>/dev/null ||
|
||||
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
|
||||
X"$dst" : 'X\(//\)[^/]' \| \
|
||||
X"$dst" : 'X\(//\)$' \| \
|
||||
X"$dst" : 'X\(/\)' \| . 2>/dev/null ||
|
||||
echo X"$dst" |
|
||||
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)[^/].*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\/\)$/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
/^X\(\/\).*/{
|
||||
s//\1/
|
||||
q
|
||||
}
|
||||
s/.*/./; q'
|
||||
`
|
||||
|
||||
test -d "$dstdir"
|
||||
dstdir_status=$?
|
||||
fi
|
||||
|
@ -293,74 +314,74 @@ do
|
|||
if test $dstdir_status != 0; then
|
||||
case $posix_mkdir in
|
||||
'')
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
# Create intermediate dirs using mode 755 as modified by the umask.
|
||||
# This is like FreeBSD 'install' as of 1997-10-28.
|
||||
umask=`umask`
|
||||
case $stripcmd.$umask in
|
||||
# Optimize common cases.
|
||||
*[2367][2367]) mkdir_umask=$umask;;
|
||||
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;;
|
||||
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
*[0-7])
|
||||
mkdir_umask=`expr $umask + 22 \
|
||||
- $umask % 100 % 40 + $umask % 20 \
|
||||
- $umask % 10 % 4 + $umask % 2
|
||||
`;;
|
||||
*) mkdir_umask=$umask,go-w;;
|
||||
esac
|
||||
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
# With -d, create the new directory with the user-specified mode.
|
||||
# Otherwise, rely on $mkdir_umask.
|
||||
if test -n "$dir_arg"; then
|
||||
mkdir_mode=-m$mode
|
||||
else
|
||||
mkdir_mode=
|
||||
fi
|
||||
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
posix_mkdir=false
|
||||
case $umask in
|
||||
*[123567][0-7][0-7])
|
||||
# POSIX mkdir -p sets u+wx bits regardless of umask, which
|
||||
# is incompatible with FreeBSD 'install' when (umask & 300) != 0.
|
||||
;;
|
||||
*)
|
||||
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
|
||||
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0
|
||||
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
if (umask $mkdir_umask &&
|
||||
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1
|
||||
then
|
||||
if test -z "$dir_arg" || {
|
||||
# Check for POSIX incompatibilities with -m.
|
||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||
# other-writable bit of parent directory when it shouldn't.
|
||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||
case $ls_ld_tmpdir in
|
||||
d????-?r-*) different_mode=700;;
|
||||
d????-?--*) different_mode=755;;
|
||||
*) false;;
|
||||
esac &&
|
||||
$mkdirprog -m$different_mode -p -- "$tmpdir" && {
|
||||
ls_ld_tmpdir_1=`ls -ld "$tmpdir"`
|
||||
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
|
||||
}
|
||||
}
|
||||
then posix_mkdir=:
|
||||
fi
|
||||
rmdir "$tmpdir/d" "$tmpdir"
|
||||
else
|
||||
# Remove any dirs left behind by ancient mkdir implementations.
|
||||
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null
|
||||
fi
|
||||
trap '' 0;;
|
||||
esac;;
|
||||
esac
|
||||
|
||||
if
|
||||
$posix_mkdir && (
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
umask $mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
|
||||
)
|
||||
then :
|
||||
else
|
||||
|
@ -370,51 +391,53 @@ do
|
|||
# directory the slow way, step by step, checking for races as we go.
|
||||
|
||||
case $dstdir in
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
/*) prefix='/';;
|
||||
[-=\(\)!]*) prefix='./';;
|
||||
*) prefix='';;
|
||||
esac
|
||||
|
||||
eval "$initialize_posix_glob"
|
||||
|
||||
oIFS=$IFS
|
||||
IFS=/
|
||||
set -f
|
||||
$posix_glob set -f
|
||||
set fnord $dstdir
|
||||
shift
|
||||
set +f
|
||||
$posix_glob set +f
|
||||
IFS=$oIFS
|
||||
|
||||
prefixes=
|
||||
|
||||
for d
|
||||
do
|
||||
test X"$d" = X && continue
|
||||
test X"$d" = X && continue
|
||||
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
prefix=$prefix$d
|
||||
if test -d "$prefix"; then
|
||||
prefixes=
|
||||
else
|
||||
if $posix_mkdir; then
|
||||
(umask=$mkdir_umask &&
|
||||
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
|
||||
# Don't fail if two instances are running concurrently.
|
||||
test -d "$prefix" || exit 1
|
||||
else
|
||||
case $prefix in
|
||||
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
|
||||
*) qprefix=$prefix;;
|
||||
esac
|
||||
prefixes="$prefixes '$qprefix'"
|
||||
fi
|
||||
fi
|
||||
prefix=$prefix/
|
||||
done
|
||||
|
||||
if test -n "$prefixes"; then
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
# Don't fail if two instances are running concurrently.
|
||||
(umask $mkdir_umask &&
|
||||
eval "\$doit_exec \$mkdirprog $prefixes") ||
|
||||
test -d "$dstdir" || exit 1
|
||||
obsolete_mkdir_used=true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
@ -449,12 +472,15 @@ do
|
|||
|
||||
# If -C, don't bother to copy if it wouldn't change the file.
|
||||
if $copy_on_change &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
set -f &&
|
||||
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||
|
||||
eval "$initialize_posix_glob" &&
|
||||
$posix_glob set -f &&
|
||||
set X $old && old=:$2:$4:$5:$6 &&
|
||||
set X $new && new=:$2:$4:$5:$6 &&
|
||||
set +f &&
|
||||
$posix_glob set +f &&
|
||||
|
||||
test "$old" = "$new" &&
|
||||
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||
then
|
||||
|
@ -467,24 +493,24 @@ do
|
|||
# to itself, or perhaps because mv is so ancient that it does not
|
||||
# support -f.
|
||||
{
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
# Now remove or move aside any old file at destination location.
|
||||
# We try this two ways since rm can't unlink itself on some
|
||||
# systems and the destination file might be busy for other
|
||||
# reasons. In this case, the final cleanup might fail but the new
|
||||
# file should still install successfully.
|
||||
{
|
||||
test ! -f "$dst" ||
|
||||
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||
} ||
|
||||
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||
(exit 1); exit 1
|
||||
}
|
||||
} &&
|
||||
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
# Now rename the file to the real destination.
|
||||
$doit $mvcmd "$dsttmp" "$dst"
|
||||
}
|
||||
fi || exit 1
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
scriptversion=2013-10-28.13; # UTC
|
||||
|
||||
# Copyright (C) 1996-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
scriptversion=2013-07-13.22; # UTC
|
||||
|
||||
# Copyright (C) 2011-2014 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2011-2013 Free Software Foundation, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
@ -106,14 +106,11 @@ trap "st=143; $do_exit" 15
|
|||
# Test script is run here.
|
||||
"$@" >$log_file 2>&1
|
||||
estatus=$?
|
||||
|
||||
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||
tweaked_estatus=1
|
||||
else
|
||||
tweaked_estatus=$estatus
|
||||
estatus=1
|
||||
fi
|
||||
|
||||
case $tweaked_estatus:$expect_failure in
|
||||
case $estatus:$expect_failure in
|
||||
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||
|
@ -122,12 +119,6 @@ case $tweaked_estatus:$expect_failure in
|
|||
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||
esac
|
||||
|
||||
# Report the test outcome and exit status in the logs, so that one can
|
||||
# know whether the test passed or failed simply by looking at the '.log'
|
||||
# file, without the need of also peaking into the corresponding '.trs'
|
||||
# file (automake bug#11814).
|
||||
echo "$res $test_name (exit status: $estatus)" >>$log_file
|
||||
|
||||
# Report outcome to console.
|
||||
echo "${col}${res}${std}: $test_name"
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
GERRIT_HOST: True
|
||||
GERRIT_SQUASH_UPLOADS: True
|
||||
CODE_REVIEW_SERVER: chromium-review.googlesource.com
|
||||
# This file is used by gcl to get repository specific information.
|
||||
CODE_REVIEW_SERVER: codereview.chromium.org
|
||||
CC_LIST: google-breakpad-dev@googlegroups.com
|
||||
TRY_ON_UPLOAD: False
|
||||
VIEW_VC: https://chromium.googlesource.com/breakpad/breakpad/+/
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -41,7 +41,6 @@ AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
|
|||
AM_CONFIG_HEADER(src/config.h)
|
||||
AM_MAINTAINER_MODE
|
||||
|
||||
AM_PROG_AR
|
||||
AM_PROG_AS
|
||||
AC_PROG_CC
|
||||
AM_PROG_CC_C_O
|
||||
|
@ -76,9 +75,6 @@ m4_include(m4/ax_pthread.m4)
|
|||
AX_PTHREAD
|
||||
AC_CHECK_HEADERS([a.out.h])
|
||||
|
||||
m4_include(m4/ax_cxx_compile_stdcxx.m4)
|
||||
AX_CXX_COMPILE_STDCXX(11, noext, mandatory)
|
||||
|
||||
# Only build Linux client libs when compiling for Linux
|
||||
case $host in
|
||||
*-*-linux* | *-android* )
|
||||
|
@ -95,14 +91,6 @@ case $host in
|
|||
esac
|
||||
AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue)
|
||||
|
||||
# Some tools (like mac ones) only support x86 currently.
|
||||
case $host_cpu in
|
||||
i?86|x86_64)
|
||||
X86_HOST=true
|
||||
;;
|
||||
esac
|
||||
AM_CONDITIONAL(X86_HOST, test x$X86_HOST = xtrue)
|
||||
|
||||
AC_ARG_ENABLE(processor,
|
||||
AS_HELP_STRING([--disable-processor],
|
||||
[Don't build processor library]
|
||||
|
@ -143,39 +131,6 @@ if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools
|
|||
AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(system-test-libs,
|
||||
AS_HELP_STRING([--enable-system-test-libs],
|
||||
[Use gtest/gmock/etc... from the system instead ]
|
||||
[of the local copies (default is local)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
system_test_libs=true
|
||||
;;
|
||||
no)
|
||||
system_test_libs=false
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --enable-system-test-libs)
|
||||
;;
|
||||
esac],
|
||||
[system_test_libs=false])
|
||||
AM_CONDITIONAL(SYSTEM_TEST_LIBS, test x$system_test_libs = xtrue)
|
||||
|
||||
AC_ARG_VAR([GMOCK_CONFIG], [Path to gmock-config script])
|
||||
AC_ARG_VAR([GMOCK_CFLAGS], [Compiler flags for gmock])
|
||||
AC_ARG_VAR([GMOCK_LIBS], [Linker flags for gmock])
|
||||
AC_ARG_VAR([GTEST_CONFIG], [Path to gtest-config script])
|
||||
AC_ARG_VAR([GTEST_CFLAGS], [Compiler flags for gtest])
|
||||
AC_ARG_VAR([GTEST_LIBS], [Linker flags for gtest])
|
||||
if test x$system_test_libs = xtrue; then
|
||||
AC_CHECK_TOOL([GMOCK_CONFIG], [gmock-config])
|
||||
AC_CHECK_TOOL([GTEST_CONFIG], [gtest-config])
|
||||
GMOCK_CFLAGS=`$GMOCK_CONFIG --cppflags --cxxflags`
|
||||
GMOCK_LIBS=`$GMOCK_CONFIG --ldflags --libs`
|
||||
GTEST_CFLAGS=`$GTEST_CONFIG --cppflags --cxxflags`
|
||||
GTEST_LIBS=`$GTEST_CONFIG --ldflags --libs`
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(selftest,
|
||||
AS_HELP_STRING([--enable-selftest],
|
||||
[Run extra tests with "make check" ]
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
#ifndef BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
#define BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "testing/include/gmock/gmock.h"
|
||||
|
||||
// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
|
||||
// (illegal instruction) instead of SIGSEGV (segmentation fault). Also,
|
||||
|
|
|
@ -263,8 +263,8 @@ void Breakpad::UncaughtExceptionHandler(NSException *exception) {
|
|||
NSSetUncaughtExceptionHandler(NULL);
|
||||
if (current_breakpad_) {
|
||||
current_breakpad_->HandleUncaughtException(exception);
|
||||
BreakpadRelease(current_breakpad_);
|
||||
}
|
||||
BreakpadRelease(current_breakpad_);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
|
|
@ -123,9 +123,6 @@ CrashGenerationServer::Stop()
|
|||
void* dummy;
|
||||
pthread_join(thread_, &dummy);
|
||||
|
||||
close(control_pipe_in_);
|
||||
close(control_pipe_out_);
|
||||
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -235,13 +235,7 @@ uintptr_t ThreadInfo::GetInstructionPointer() const {
|
|||
}
|
||||
|
||||
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||
#if _MIPS_SIM == _ABI64
|
||||
out->context_flags = MD_CONTEXT_MIPS64_FULL;
|
||||
#elif _MIPS_SIM == _ABIO32
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
#else
|
||||
# error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||
out->iregs[i] = mcontext.gregs[i];
|
||||
|
|
|
@ -219,13 +219,7 @@ uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
|||
}
|
||||
|
||||
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||
#if _MIPS_SIM == _ABI64
|
||||
out->context_flags = MD_CONTEXT_MIPS64_FULL;
|
||||
#elif _MIPS_SIM == _ABIO32
|
||||
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||
#else
|
||||
#error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||
out->iregs[i] = uc->uc_mcontext.gregs[i];
|
||||
|
|
|
@ -118,7 +118,7 @@ namespace {
|
|||
// all these signals must be Core (see man 7 signal) because we rethrow the
|
||||
// signal after handling it and expect that it'll be fatal.
|
||||
const int kExceptionSignals[] = {
|
||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP
|
||||
SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS
|
||||
};
|
||||
const int kNumHandledSignals =
|
||||
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||
#include "common/linux/eintr_wrapper.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/ignore_ret.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/tests/auto_tempdir.h"
|
||||
|
@ -93,6 +94,10 @@ void FlushInstructionCache(const char* memory, uint32_t memory_size) {
|
|||
#endif
|
||||
}
|
||||
|
||||
// Length of a formatted GUID string =
|
||||
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
|
||||
const int kGUIDStringSize = 37;
|
||||
|
||||
void sigchld_handler(int signo) { }
|
||||
|
||||
int CreateTMPFile(const string& dir, string* path) {
|
||||
|
@ -257,6 +262,8 @@ TEST(ExceptionHandlerTest, ChildCrashWithFD) {
|
|||
ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
|
||||
}
|
||||
|
||||
#endif // !ADDRESS_SANITIZER
|
||||
|
||||
static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
|
||||
void* context,
|
||||
bool succeeded) {
|
||||
|
@ -298,6 +305,8 @@ static bool InstallRaiseSIGKILL() {
|
|||
return sigaction(SIGSEGV, &sa, NULL) != -1;
|
||||
}
|
||||
|
||||
#ifndef ADDRESS_SANITIZER
|
||||
|
||||
static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter,
|
||||
ExceptionHandler::MinidumpCallback done,
|
||||
string path) {
|
||||
|
@ -812,7 +821,19 @@ TEST(ExceptionHandlerTest, ModuleInfo) {
|
|||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
|
||||
char module_identifier_buffer[kGUIDStringSize];
|
||||
FileID::ConvertIdentifierToString(kModuleGUID,
|
||||
module_identifier_buffer,
|
||||
sizeof(module_identifier_buffer));
|
||||
string module_identifier(module_identifier_buffer);
|
||||
// Strip out dashes
|
||||
size_t pos;
|
||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
||||
module_identifier.erase(pos, 1);
|
||||
}
|
||||
// And append a zero, because module IDs include an "age" field
|
||||
// which is always zero on Linux.
|
||||
module_identifier += "0";
|
||||
|
||||
// Get some memory.
|
||||
char* memory =
|
||||
|
@ -857,8 +878,6 @@ TEST(ExceptionHandlerTest, ModuleInfo) {
|
|||
unlink(minidump_desc.path());
|
||||
}
|
||||
|
||||
#ifndef ADDRESS_SANITIZER
|
||||
|
||||
static const unsigned kControlMsgSize =
|
||||
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
|
||||
|
||||
|
@ -911,6 +930,8 @@ CrashHandler(const void* crash_context, size_t crash_context_size,
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifndef ADDRESS_SANITIZER
|
||||
|
||||
TEST(ExceptionHandlerTest, ExternalDumper) {
|
||||
int fds[2];
|
||||
ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
|
||||
|
|
|
@ -38,13 +38,9 @@ struct MicrodumpExtraInfo {
|
|||
const char* build_fingerprint;
|
||||
const char* product_info;
|
||||
const char* gpu_fingerprint;
|
||||
const char* process_type;
|
||||
|
||||
MicrodumpExtraInfo()
|
||||
: build_fingerprint(NULL),
|
||||
product_info(NULL),
|
||||
gpu_fingerprint(NULL),
|
||||
process_type(NULL) {}
|
||||
: build_fingerprint(NULL), product_info(NULL), gpu_fingerprint(NULL) {}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
|
||||
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
|
@ -42,15 +40,11 @@
|
|||
#include "client/linux/handler/microdump_extra_info.h"
|
||||
#include "client/linux/log/log.h"
|
||||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/memory.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::auto_wasteful_vector;
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using google_breakpad::kDefaultBuildIdSize;
|
||||
using google_breakpad::LinuxDumper;
|
||||
using google_breakpad::LinuxPtraceDumper;
|
||||
using google_breakpad::MappingInfo;
|
||||
|
@ -62,72 +56,6 @@ using google_breakpad::UContextReader;
|
|||
|
||||
const size_t kLineBufferSize = 2048;
|
||||
|
||||
#if !defined(__LP64__)
|
||||
// The following are only used by DumpFreeSpace, so need to be compiled
|
||||
// in conditionally in the same way.
|
||||
|
||||
template <typename Dst, typename Src>
|
||||
Dst saturated_cast(Src src) {
|
||||
if (src >= std::numeric_limits<Dst>::max())
|
||||
return std::numeric_limits<Dst>::max();
|
||||
if (src <= std::numeric_limits<Dst>::min())
|
||||
return std::numeric_limits<Dst>::min();
|
||||
return static_cast<Dst>(src);
|
||||
}
|
||||
|
||||
int Log2Floor(uint64_t n) {
|
||||
// Copied from chromium src/base/bits.h
|
||||
if (n == 0)
|
||||
return -1;
|
||||
int log = 0;
|
||||
uint64_t value = n;
|
||||
for (int i = 5; i >= 0; --i) {
|
||||
int shift = (1 << i);
|
||||
uint64_t x = value >> shift;
|
||||
if (x != 0) {
|
||||
value = x;
|
||||
log += shift;
|
||||
}
|
||||
}
|
||||
assert(value == 1u);
|
||||
return log;
|
||||
}
|
||||
|
||||
bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) {
|
||||
// Because of load biasing, we can end up with a situation where two
|
||||
// mappings actually overlap. So we will define adjacency to also include a
|
||||
// b start address that lies within a's address range (including starting
|
||||
// immediately after a).
|
||||
// Because load biasing only ever moves the start address backwards, the end
|
||||
// address should still increase.
|
||||
return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr;
|
||||
}
|
||||
|
||||
bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) {
|
||||
// Return true if mapping a is before mapping b.
|
||||
// For the same reason (load biasing) we compare end addresses, which - unlike
|
||||
// start addresses - will not have been modified.
|
||||
return a->start_addr + a->size < b->start_addr + b->size;
|
||||
}
|
||||
|
||||
size_t NextOrderedMapping(
|
||||
const google_breakpad::wasteful_vector<MappingInfo*>& mappings,
|
||||
size_t curr) {
|
||||
// Find the mapping that directly follows mappings[curr].
|
||||
// If no such mapping exists, return |invalid| to indicate this.
|
||||
const size_t invalid = std::numeric_limits<size_t>::max();
|
||||
size_t best = invalid;
|
||||
for (size_t next = 0; next < mappings.size(); ++next) {
|
||||
if (MappingLessThan(mappings[curr], mappings[next]) &&
|
||||
(best == invalid || MappingLessThan(mappings[next], mappings[best]))) {
|
||||
best = next;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
#endif // !__LP64__
|
||||
|
||||
class MicrodumpWriter {
|
||||
public:
|
||||
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||
|
@ -164,11 +92,7 @@ class MicrodumpWriter {
|
|||
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||
DumpProductInformation();
|
||||
DumpOSInformation();
|
||||
DumpProcessType();
|
||||
DumpGPUInformation();
|
||||
#if !defined(__LP64__)
|
||||
DumpFreeSpace();
|
||||
#endif
|
||||
success = DumpCrashingThread();
|
||||
if (success)
|
||||
success = DumpMappings();
|
||||
|
@ -234,16 +158,6 @@ class MicrodumpWriter {
|
|||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpProcessType() {
|
||||
LogAppend("P ");
|
||||
if (microdump_extra_info_.process_type) {
|
||||
LogAppend(microdump_extra_info_.process_type);
|
||||
} else {
|
||||
LogAppend("UNKNOWN");
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
|
||||
void DumpOSInformation() {
|
||||
const uint8_t n_cpus = static_cast<uint8_t>(sysconf(_SC_NPROCESSORS_CONF));
|
||||
|
||||
|
@ -265,13 +179,7 @@ class MicrodumpWriter {
|
|||
#elif defined(__i386__)
|
||||
const char kArch[] = "x86";
|
||||
#elif defined(__mips__)
|
||||
# if _MIPS_SIM == _ABIO32
|
||||
const char kArch[] = "mips";
|
||||
# elif _MIPS_SIM == _ABI64
|
||||
const char kArch[] = "mips64";
|
||||
# else
|
||||
# error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet"
|
||||
#endif
|
||||
|
@ -422,31 +330,21 @@ class MicrodumpWriter {
|
|||
bool member,
|
||||
unsigned int mapping_id,
|
||||
const uint8_t* identifier) {
|
||||
|
||||
auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
|
||||
dumper_->allocator());
|
||||
|
||||
MDGUID module_identifier;
|
||||
if (identifier) {
|
||||
// GUID was provided by caller.
|
||||
identifier_bytes.insert(identifier_bytes.end(),
|
||||
identifier,
|
||||
identifier + sizeof(MDGUID));
|
||||
my_memcpy(&module_identifier, identifier, sizeof(MDGUID));
|
||||
} else {
|
||||
dumper_->ElfFileIdentifierForMapping(
|
||||
mapping,
|
||||
member,
|
||||
mapping_id,
|
||||
identifier_bytes);
|
||||
reinterpret_cast<uint8_t*>(&module_identifier));
|
||||
}
|
||||
|
||||
// Copy as many bytes of |identifier| as will fit into a MDGUID
|
||||
MDGUID module_identifier = {0};
|
||||
memcpy(&module_identifier, &identifier_bytes[0],
|
||||
std::min(sizeof(MDGUID), identifier_bytes.size()));
|
||||
|
||||
char file_name[NAME_MAX];
|
||||
char file_path[NAME_MAX];
|
||||
dumper_->GetMappingEffectiveNameAndPath(
|
||||
LinuxDumper::GetMappingEffectiveNameAndPath(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||
|
||||
LogAppend("M ");
|
||||
|
@ -472,80 +370,6 @@ class MicrodumpWriter {
|
|||
LogCommitLine();
|
||||
}
|
||||
|
||||
#if !defined(__LP64__)
|
||||
void DumpFreeSpace() {
|
||||
const google_breakpad::wasteful_vector<MappingInfo*>& mappings =
|
||||
dumper_->mappings();
|
||||
if (mappings.size() == 0) return;
|
||||
|
||||
// This is complicated by the fact that mappings is not in order. It should
|
||||
// be mostly in order, however the mapping that contains the entry point for
|
||||
// the process is always at the front of the vector.
|
||||
|
||||
static const int HBITS = sizeof(size_t) * 8;
|
||||
size_t hole_histogram[HBITS];
|
||||
my_memset(hole_histogram, 0, sizeof(hole_histogram));
|
||||
|
||||
// Find the lowest address mapping.
|
||||
size_t curr = 0;
|
||||
for (size_t i = 1; i < mappings.size(); ++i) {
|
||||
if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i;
|
||||
}
|
||||
|
||||
uintptr_t lo_addr = mappings[curr]->start_addr;
|
||||
|
||||
size_t hole_cnt = 0;
|
||||
size_t hole_max = 0;
|
||||
size_t hole_sum = 0;
|
||||
|
||||
while (true) {
|
||||
// Skip to the end of an adjacent run of mappings. This is an optimization
|
||||
// for the fact that mappings is mostly sorted.
|
||||
while (curr != mappings.size() - 1 &&
|
||||
MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) {
|
||||
++curr;
|
||||
}
|
||||
|
||||
size_t next = NextOrderedMapping(mappings, curr);
|
||||
if (next == std::numeric_limits<size_t>::max())
|
||||
break;
|
||||
|
||||
uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size;
|
||||
uintptr_t hole_hi = mappings[next]->start_addr;
|
||||
|
||||
if (hole_hi > hole_lo) {
|
||||
size_t hole_sz = hole_hi - hole_lo;
|
||||
hole_sum += hole_sz;
|
||||
hole_max = std::max(hole_sz, hole_max);
|
||||
++hole_cnt;
|
||||
++hole_histogram[Log2Floor(hole_sz)];
|
||||
}
|
||||
curr = next;
|
||||
}
|
||||
|
||||
uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size;
|
||||
|
||||
LogAppend("H ");
|
||||
LogAppend(lo_addr);
|
||||
LogAppend(" ");
|
||||
LogAppend(hi_addr);
|
||||
LogAppend(" ");
|
||||
LogAppend(saturated_cast<uint16_t>(hole_cnt));
|
||||
LogAppend(" ");
|
||||
LogAppend(hole_max);
|
||||
LogAppend(" ");
|
||||
LogAppend(hole_sum);
|
||||
for (unsigned int i = 0; i < HBITS; ++i) {
|
||||
if (!hole_histogram[i]) continue;
|
||||
LogAppend(" ");
|
||||
LogAppend(saturated_cast<uint8_t>(i));
|
||||
LogAppend(":");
|
||||
LogAppend(saturated_cast<uint8_t>(hole_histogram[i]));
|
||||
}
|
||||
LogCommitLine();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write information about the mappings in effect.
|
||||
bool DumpMappings() {
|
||||
// First write all the mappings from the dumper
|
||||
|
|
|
@ -49,9 +49,8 @@ namespace google_breakpad {
|
|||
|
||||
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
|
||||
const char* core_path,
|
||||
const char* procfs_path,
|
||||
const char* root_prefix)
|
||||
: LinuxDumper(pid, root_prefix),
|
||||
const char* procfs_path)
|
||||
: LinuxDumper(pid),
|
||||
core_path_(core_path),
|
||||
procfs_path_(procfs_path),
|
||||
thread_infos_(&allocator_, 8) {
|
||||
|
|
|
@ -47,9 +47,7 @@ class LinuxCoreDumper : public LinuxDumper {
|
|||
// its proc files at |procfs_path|. If |procfs_path| is a copy of
|
||||
// /proc/<pid>, it should contain the following files:
|
||||
// auxv, cmdline, environ, exe, maps, status
|
||||
// See LinuxDumper for the purpose of |root_prefix|.
|
||||
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path,
|
||||
const char* root_prefix = "");
|
||||
LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path);
|
||||
|
||||
// Implements LinuxDumper::BuildProcPath().
|
||||
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
|
||||
|
|
|
@ -39,16 +39,6 @@
|
|||
|
||||
using namespace google_breakpad;
|
||||
|
||||
TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) {
|
||||
const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root");
|
||||
const MappingInfo mapping = { 0, 0, 0, false, "/usr/lib/libc.so" };
|
||||
|
||||
char path[PATH_MAX];
|
||||
dumper.GetMappingAbsolutePath(mapping, path);
|
||||
|
||||
EXPECT_STREQ("/mnt/root/usr/lib/libc.so", path);
|
||||
}
|
||||
|
||||
TEST(LinuxCoreDumperTest, BuildProcPath) {
|
||||
const pid_t pid = getpid();
|
||||
const char procfs_path[] = "/procfs_copy";
|
||||
|
|
|
@ -85,186 +85,17 @@ inline static bool IsMappedFileOpenUnsafe(
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
#if defined(__CHROMEOS__)
|
||||
|
||||
namespace {
|
||||
|
||||
// Recover memory mappings before writing dump on ChromeOS
|
||||
//
|
||||
// On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from
|
||||
// addresses. ChromeOS' hugepage implementation replaces some segments with
|
||||
// anonymous private pages, which is a restriction of current implementation
|
||||
// in Linux kernel at the time of writing. Thus, breakpad can no longer
|
||||
// symbolize addresses from those text segments replaced with hugepages.
|
||||
//
|
||||
// This postprocess tries to recover the mappings. Because hugepages are always
|
||||
// inserted in between some .text sections, it tries to infer the names and
|
||||
// offsets of the segments, by looking at segments immediately precede and
|
||||
// succeed them.
|
||||
//
|
||||
// For example, a text segment before hugepage optimization
|
||||
// 02001000-03002000 r-xp /opt/google/chrome/chrome
|
||||
//
|
||||
// can be broken into
|
||||
// 02001000-02200000 r-xp /opt/google/chrome/chrome
|
||||
// 02200000-03000000 r-xp
|
||||
// 03000000-03002000 r-xp /opt/google/chrome/chrome
|
||||
//
|
||||
// For more details, see:
|
||||
// crbug.com/628040 ChromeOS' use of hugepages confuses crash symbolization
|
||||
|
||||
// Copied from CrOS' hugepage implementation, which is unlikely to change.
|
||||
// The hugepage size is 2M.
|
||||
const unsigned int kHpageShift = 21;
|
||||
const size_t kHpageSize = (1 << kHpageShift);
|
||||
const size_t kHpageMask = (~(kHpageSize - 1));
|
||||
|
||||
// Find and merge anonymous r-xp segments with surrounding named segments.
|
||||
// There are two cases:
|
||||
|
||||
// Case 1: curr, next
|
||||
// curr is anonymous
|
||||
// curr is r-xp
|
||||
// curr.size >= 2M
|
||||
// curr.size is a multiple of 2M.
|
||||
// next is backed by some file.
|
||||
// curr and next are contiguous.
|
||||
// offset(next) == sizeof(curr)
|
||||
void TryRecoverMappings(MappingInfo *curr, MappingInfo *next) {
|
||||
// Merged segments are marked with size = 0.
|
||||
if (curr->size == 0 || next->size == 0)
|
||||
return;
|
||||
|
||||
if (curr->size >= kHpageSize &&
|
||||
curr->exec &&
|
||||
(curr->size & kHpageMask) == curr->size &&
|
||||
(curr->start_addr & kHpageMask) == curr->start_addr &&
|
||||
curr->name[0] == '\0' &&
|
||||
next->name[0] != '\0' &&
|
||||
curr->start_addr + curr->size == next->start_addr &&
|
||||
curr->size == next->offset) {
|
||||
|
||||
// matched
|
||||
my_strlcpy(curr->name, next->name, NAME_MAX);
|
||||
if (next->exec) {
|
||||
// (curr, next)
|
||||
curr->size += next->size;
|
||||
next->size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Case 2: prev, curr, next
|
||||
// curr is anonymous
|
||||
// curr is r-xp
|
||||
// curr.size >= 2M
|
||||
// curr.size is a multiple of 2M.
|
||||
// next and prev are backed by the same file.
|
||||
// prev, curr and next are contiguous.
|
||||
// offset(next) == offset(prev) + sizeof(prev) + sizeof(curr)
|
||||
void TryRecoverMappings(MappingInfo *prev, MappingInfo *curr,
|
||||
MappingInfo *next) {
|
||||
// Merged segments are marked with size = 0.
|
||||
if (prev->size == 0 || curr->size == 0 || next->size == 0)
|
||||
return;
|
||||
|
||||
if (curr->size >= kHpageSize &&
|
||||
curr->exec &&
|
||||
(curr->size & kHpageMask) == curr->size &&
|
||||
(curr->start_addr & kHpageMask) == curr->start_addr &&
|
||||
curr->name[0] == '\0' &&
|
||||
next->name[0] != '\0' &&
|
||||
curr->start_addr + curr->size == next->start_addr &&
|
||||
prev->start_addr + prev->size == curr->start_addr &&
|
||||
my_strncmp(prev->name, next->name, NAME_MAX) == 0 &&
|
||||
next->offset == prev->offset + prev->size + curr->size) {
|
||||
|
||||
// matched
|
||||
my_strlcpy(curr->name, prev->name, NAME_MAX);
|
||||
if (prev->exec) {
|
||||
curr->offset = prev->offset;
|
||||
curr->start_addr = prev->start_addr;
|
||||
if (next->exec) {
|
||||
// (prev, curr, next)
|
||||
curr->size += prev->size + next->size;
|
||||
prev->size = 0;
|
||||
next->size = 0;
|
||||
} else {
|
||||
// (prev, curr), next
|
||||
curr->size += prev->size;
|
||||
prev->size = 0;
|
||||
}
|
||||
} else {
|
||||
curr->offset = prev->offset + prev->size;
|
||||
if (next->exec) {
|
||||
// prev, (curr, next)
|
||||
curr->size += next->size;
|
||||
next->size = 0;
|
||||
} else {
|
||||
// prev, curr, next
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mappings_ is sorted excepted for the first entry.
|
||||
// This function tries to merge segemnts into the first entry,
|
||||
// then check for other sorted entries.
|
||||
// See LinuxDumper::EnumerateMappings().
|
||||
void CrOSPostProcessMappings(wasteful_vector<MappingInfo*>& mappings) {
|
||||
// Find the candidate "next" to first segment, which is the only one that
|
||||
// could be out-of-order.
|
||||
size_t l = 1;
|
||||
size_t r = mappings.size();
|
||||
size_t next = mappings.size();
|
||||
while (l < r) {
|
||||
int m = (l + r) / 2;
|
||||
if (mappings[m]->start_addr > mappings[0]->start_addr)
|
||||
r = next = m;
|
||||
else
|
||||
l = m + 1;
|
||||
}
|
||||
|
||||
// Try to merge segments into the first.
|
||||
if (next < mappings.size()) {
|
||||
TryRecoverMappings(mappings[0], mappings[next]);
|
||||
if (next - 1 > 0)
|
||||
TryRecoverMappings(mappings[next - 1], mappings[0], mappings[next]);
|
||||
}
|
||||
|
||||
// Iterate through normal, sorted cases.
|
||||
// Normal case 1.
|
||||
for (size_t i = 1; i < mappings.size() - 1; i++)
|
||||
TryRecoverMappings(mappings[i], mappings[i + 1]);
|
||||
|
||||
// Normal case 2.
|
||||
for (size_t i = 1; i < mappings.size() - 2; i++)
|
||||
TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]);
|
||||
|
||||
// Collect merged (size == 0) segments.
|
||||
size_t f, e;
|
||||
for (f = e = 0; e < mappings.size(); e++)
|
||||
if (mappings[e]->size > 0)
|
||||
mappings[f++] = mappings[e];
|
||||
mappings.resize(f);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif // __CHROMEOS__
|
||||
|
||||
// All interesting auvx entry types are below AT_SYSINFO_EHDR
|
||||
#define AT_MAX AT_SYSINFO_EHDR
|
||||
|
||||
LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix)
|
||||
LinuxDumper::LinuxDumper(pid_t pid)
|
||||
: pid_(pid),
|
||||
root_prefix_(root_prefix),
|
||||
crash_address_(0),
|
||||
crash_signal_(0),
|
||||
crash_thread_(pid),
|
||||
threads_(&allocator_, 8),
|
||||
mappings_(&allocator_),
|
||||
auxv_(&allocator_, AT_MAX + 1) {
|
||||
assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX);
|
||||
// The passed-in size to the constructor (above) is only a hint.
|
||||
// Must call .resize() to do actual initialization of the elements.
|
||||
auxv_.resize(AT_MAX + 1);
|
||||
|
@ -281,11 +112,6 @@ bool LinuxDumper::LateInit() {
|
|||
#if defined(__ANDROID__)
|
||||
LatePostprocessMappings();
|
||||
#endif
|
||||
|
||||
#if defined(__CHROMEOS__)
|
||||
CrOSPostProcessMappings(mappings_);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -293,8 +119,9 @@ bool
|
|||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
wasteful_vector<uint8_t>& identifier) {
|
||||
uint8_t identifier[sizeof(MDGUID)]) {
|
||||
assert(!member || mapping_id < mappings_.size());
|
||||
my_memset(identifier, 0, sizeof(MDGUID));
|
||||
if (IsMappedFileOpenUnsafe(mapping))
|
||||
return false;
|
||||
|
||||
|
@ -312,9 +139,14 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
|||
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
|
||||
}
|
||||
|
||||
char filename[PATH_MAX];
|
||||
if (!GetMappingAbsolutePath(mapping, filename))
|
||||
char filename[NAME_MAX];
|
||||
size_t filename_len = my_strlen(mapping.name);
|
||||
if (filename_len >= NAME_MAX) {
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
my_memcpy(filename, mapping.name, filename_len);
|
||||
filename[filename_len] = '\0';
|
||||
bool filename_modified = HandleDeletedFileInMapping(filename);
|
||||
|
||||
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||
|
@ -324,19 +156,13 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
|||
bool success =
|
||||
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
|
||||
if (success && member && filename_modified) {
|
||||
mappings_[mapping_id]->name[my_strlen(mapping.name) -
|
||||
mappings_[mapping_id]->name[filename_len -
|
||||
sizeof(kDeletedSuffix) + 1] = '\0';
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
|
||||
char path[PATH_MAX]) const {
|
||||
return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX &&
|
||||
my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX;
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool ElfFileSoNameFromMappedFile(
|
||||
const void* elf_base, char* soname, size_t soname_size) {
|
||||
|
@ -386,16 +212,23 @@ bool ElfFileSoNameFromMappedFile(
|
|||
// for |mapping|. If the SONAME is found copy it into the passed buffer
|
||||
// |soname| and return true. The size of the buffer is |soname_size|.
|
||||
// The SONAME will be truncated if it is too long to fit in the buffer.
|
||||
bool ElfFileSoName(const LinuxDumper& dumper,
|
||||
bool ElfFileSoName(
|
||||
const MappingInfo& mapping, char* soname, size_t soname_size) {
|
||||
if (IsMappedFileOpenUnsafe(mapping)) {
|
||||
// Not safe
|
||||
return false;
|
||||
}
|
||||
|
||||
char filename[PATH_MAX];
|
||||
if (!dumper.GetMappingAbsolutePath(mapping, filename))
|
||||
char filename[NAME_MAX];
|
||||
size_t filename_len = my_strlen(mapping.name);
|
||||
if (filename_len >= NAME_MAX) {
|
||||
assert(false);
|
||||
// name too long
|
||||
return false;
|
||||
}
|
||||
|
||||
my_memcpy(filename, mapping.name, filename_len);
|
||||
filename[filename_len] = '\0';
|
||||
|
||||
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||
if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
|
||||
|
@ -409,6 +242,7 @@ bool ElfFileSoName(const LinuxDumper& dumper,
|
|||
} // namespace
|
||||
|
||||
|
||||
// static
|
||||
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
|
@ -421,10 +255,8 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
|||
// apk on Android). We try to find the name of the shared object (SONAME) by
|
||||
// looking in the file for ELF sections.
|
||||
bool mapped_from_archive = false;
|
||||
if (mapping.exec && mapping.offset != 0) {
|
||||
mapped_from_archive =
|
||||
ElfFileSoName(*this, mapping, file_name, file_name_size);
|
||||
}
|
||||
if (mapping.exec && mapping.offset != 0)
|
||||
mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size);
|
||||
|
||||
if (mapped_from_archive) {
|
||||
// Some tools (e.g., stackwalk) extract the basename from the pathname. In
|
||||
|
@ -748,13 +580,10 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
|
|||
|
||||
// Check |path| against the /proc/pid/exe 'symlink'.
|
||||
char exe_link[NAME_MAX];
|
||||
char new_path[NAME_MAX];
|
||||
if (!BuildProcPath(exe_link, pid_, "exe"))
|
||||
return false;
|
||||
MappingInfo new_mapping = {0};
|
||||
if (!SafeReadLink(exe_link, new_mapping.name))
|
||||
return false;
|
||||
char new_path[PATH_MAX];
|
||||
if (!GetMappingAbsolutePath(new_mapping, new_path))
|
||||
if (!SafeReadLink(exe_link, new_path))
|
||||
return false;
|
||||
if (my_strcmp(path, new_path) != 0)
|
||||
return false;
|
||||
|
|
|
@ -49,7 +49,6 @@
|
|||
|
||||
#include "client/linux/dump_writer_common/mapping_info.h"
|
||||
#include "client/linux/dump_writer_common/thread_info.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/memory.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
||||
|
@ -73,9 +72,7 @@ const char kLinuxGateLibraryName[] = "linux-gate.so";
|
|||
|
||||
class LinuxDumper {
|
||||
public:
|
||||
// The |root_prefix| is prepended to mapping paths before opening them, which
|
||||
// is useful if the crash originates from a chroot.
|
||||
explicit LinuxDumper(pid_t pid, const char* root_prefix = "");
|
||||
explicit LinuxDumper(pid_t pid);
|
||||
|
||||
virtual ~LinuxDumper();
|
||||
|
||||
|
@ -130,7 +127,7 @@ class LinuxDumper {
|
|||
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
wasteful_vector<uint8_t>& identifier);
|
||||
uint8_t identifier[sizeof(MDGUID)]);
|
||||
|
||||
uintptr_t crash_address() const { return crash_address_; }
|
||||
void set_crash_address(uintptr_t crash_address) {
|
||||
|
@ -143,21 +140,16 @@ class LinuxDumper {
|
|||
pid_t crash_thread() const { return crash_thread_; }
|
||||
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
|
||||
|
||||
// Concatenates the |root_prefix_| and |mapping| path. Writes into |path| and
|
||||
// returns true unless the string is too long.
|
||||
bool GetMappingAbsolutePath(const MappingInfo& mapping,
|
||||
char path[PATH_MAX]) const;
|
||||
|
||||
// Extracts the effective path and file name of from |mapping|. In most cases
|
||||
// the effective name/path are just the mapping's path and basename. In some
|
||||
// other cases, however, a library can be mapped from an archive (e.g., when
|
||||
// loading .so libs from an apk on Android) and this method is able to
|
||||
// reconstruct the original file name.
|
||||
void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
char* file_name,
|
||||
size_t file_name_size);
|
||||
static void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||
char* file_path,
|
||||
size_t file_path_size,
|
||||
char* file_name,
|
||||
size_t file_name_size);
|
||||
|
||||
protected:
|
||||
bool ReadAuxv();
|
||||
|
@ -180,9 +172,6 @@ class LinuxDumper {
|
|||
// ID of the crashed process.
|
||||
const pid_t pid_;
|
||||
|
||||
// Path of the root directory to which mapping paths are relative.
|
||||
const char* const root_prefix_;
|
||||
|
||||
// Virtual address at which the process crashed.
|
||||
uintptr_t crash_address_;
|
||||
|
||||
|
|
|
@ -66,7 +66,6 @@ using namespace google_breakpad;
|
|||
|
||||
namespace {
|
||||
|
||||
typedef wasteful_vector<uint8_t> id_vector;
|
||||
typedef testing::Test LinuxPtraceDumperTest;
|
||||
|
||||
/* Fixture for running tests in a child process. */
|
||||
|
@ -106,17 +105,11 @@ class LinuxPtraceDumperChildTest : public testing::Test {
|
|||
* This is achieved by defining a TestBody macro further below.
|
||||
*/
|
||||
virtual void RealTestBody() = 0;
|
||||
|
||||
id_vector make_vector() {
|
||||
return id_vector(&allocator, kDefaultBuildIdSize);
|
||||
}
|
||||
|
||||
private:
|
||||
static const int kFatalFailure = 1;
|
||||
static const int kNonFatalFailure = 2;
|
||||
|
||||
pid_t child_pid_;
|
||||
PageAllocator allocator;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -317,15 +310,14 @@ TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
|
|||
|
||||
// Need to suspend the child so ptrace actually works.
|
||||
ASSERT_TRUE(dumper.ThreadsSuspend());
|
||||
id_vector identifier(make_vector());
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
|
||||
true,
|
||||
index,
|
||||
identifier));
|
||||
|
||||
id_vector empty_identifier(make_vector());
|
||||
empty_identifier.resize(kDefaultBuildIdSize, 0);
|
||||
EXPECT_NE(empty_identifier, identifier);
|
||||
uint8_t empty_identifier[sizeof(MDGUID)];
|
||||
memset(empty_identifier, 0, sizeof(empty_identifier));
|
||||
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
|
||||
EXPECT_TRUE(dumper.ThreadsResume());
|
||||
}
|
||||
#endif
|
||||
|
@ -351,18 +343,19 @@ TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) {
|
|||
}
|
||||
ASSERT_TRUE(found_exe);
|
||||
|
||||
id_vector identifier1(make_vector());
|
||||
id_vector identifier2(make_vector());
|
||||
uint8_t identifier1[sizeof(MDGUID)];
|
||||
uint8_t identifier2[sizeof(MDGUID)];
|
||||
EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i,
|
||||
identifier1));
|
||||
FileID fileid(exe_name);
|
||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2));
|
||||
|
||||
string identifier_string1 =
|
||||
FileID::ConvertIdentifierToUUIDString(identifier1);
|
||||
string identifier_string2 =
|
||||
FileID::ConvertIdentifierToUUIDString(identifier2);
|
||||
EXPECT_EQ(identifier_string1, identifier_string2);
|
||||
char identifier_string1[37];
|
||||
char identifier_string2[37];
|
||||
FileID::ConvertIdentifierToString(identifier1, identifier_string1,
|
||||
37);
|
||||
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
|
||||
37);
|
||||
EXPECT_STREQ(identifier_string1, identifier_string2);
|
||||
}
|
||||
|
||||
/* Get back to normal behavior of TEST*() macros wrt TestBody. */
|
||||
|
|
|
@ -73,7 +73,6 @@
|
|||
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
|
||||
#include "client/minidump_file_writer.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/minidump_type_helper.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
|
@ -82,10 +81,8 @@
|
|||
namespace {
|
||||
|
||||
using google_breakpad::AppMemoryList;
|
||||
using google_breakpad::auto_wasteful_vector;
|
||||
using google_breakpad::ExceptionHandler;
|
||||
using google_breakpad::CpuSet;
|
||||
using google_breakpad::kDefaultBuildIdSize;
|
||||
using google_breakpad::LineReader;
|
||||
using google_breakpad::LinuxDumper;
|
||||
using google_breakpad::LinuxPtraceDumper;
|
||||
|
@ -274,14 +271,6 @@ class MinidumpWriter {
|
|||
if (max_stack_len >= 0 &&
|
||||
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||
stack_len = max_stack_len;
|
||||
// Skip empty chunks of length max_stack_len.
|
||||
uintptr_t int_stack = reinterpret_cast<uintptr_t>(stack);
|
||||
if (max_stack_len > 0) {
|
||||
while (int_stack + max_stack_len < stack_pointer) {
|
||||
int_stack += max_stack_len;
|
||||
}
|
||||
}
|
||||
stack = reinterpret_cast<const void*>(int_stack);
|
||||
}
|
||||
if (!memory.Allocate(stack_len))
|
||||
return false;
|
||||
|
@ -526,7 +515,7 @@ class MinidumpWriter {
|
|||
continue;
|
||||
|
||||
MDRawModule mod;
|
||||
if (!FillRawModule(mapping, true, i, &mod, NULL))
|
||||
if (!FillRawModule(mapping, true, i, mod, NULL))
|
||||
return false;
|
||||
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
|
||||
}
|
||||
|
@ -535,7 +524,7 @@ class MinidumpWriter {
|
|||
iter != mapping_list_.end();
|
||||
++iter) {
|
||||
MDRawModule mod;
|
||||
if (!FillRawModule(iter->first, false, 0, &mod, iter->second))
|
||||
if (!FillRawModule(iter->first, false, 0, mod, iter->second))
|
||||
return false;
|
||||
list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE);
|
||||
}
|
||||
|
@ -549,51 +538,52 @@ class MinidumpWriter {
|
|||
bool FillRawModule(const MappingInfo& mapping,
|
||||
bool member,
|
||||
unsigned int mapping_id,
|
||||
MDRawModule* mod,
|
||||
MDRawModule& mod,
|
||||
const uint8_t* identifier) {
|
||||
my_memset(mod, 0, MD_MODULE_SIZE);
|
||||
my_memset(&mod, 0, MD_MODULE_SIZE);
|
||||
|
||||
mod->base_of_image = mapping.start_addr;
|
||||
mod->size_of_image = mapping.size;
|
||||
mod.base_of_image = mapping.start_addr;
|
||||
mod.size_of_image = mapping.size;
|
||||
|
||||
auto_wasteful_vector<uint8_t, kDefaultBuildIdSize> identifier_bytes(
|
||||
dumper_->allocator());
|
||||
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
|
||||
uint8_t* cv_ptr = cv_buf;
|
||||
|
||||
const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
|
||||
my_memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
|
||||
cv_ptr += sizeof(cv_signature);
|
||||
uint8_t* signature = cv_ptr;
|
||||
cv_ptr += sizeof(MDGUID);
|
||||
if (identifier) {
|
||||
// GUID was provided by caller.
|
||||
identifier_bytes.insert(identifier_bytes.end(),
|
||||
identifier,
|
||||
identifier + sizeof(MDGUID));
|
||||
my_memcpy(signature, identifier, sizeof(MDGUID));
|
||||
} else {
|
||||
// Note: ElfFileIdentifierForMapping() can manipulate the |mapping.name|.
|
||||
dumper_->ElfFileIdentifierForMapping(mapping,
|
||||
member,
|
||||
mapping_id,
|
||||
identifier_bytes);
|
||||
}
|
||||
|
||||
if (!identifier_bytes.empty()) {
|
||||
UntypedMDRVA cv(&minidump_writer_);
|
||||
if (!cv.Allocate(MDCVInfoELF_minsize + identifier_bytes.size()))
|
||||
return false;
|
||||
|
||||
const uint32_t cv_signature = MD_CVINFOELF_SIGNATURE;
|
||||
cv.Copy(&cv_signature, sizeof(cv_signature));
|
||||
cv.Copy(cv.position() + sizeof(cv_signature), &identifier_bytes[0],
|
||||
identifier_bytes.size());
|
||||
|
||||
mod->cv_record = cv.location();
|
||||
dumper_->ElfFileIdentifierForMapping(mapping, member,
|
||||
mapping_id, signature);
|
||||
}
|
||||
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
|
||||
cv_ptr += sizeof(uint32_t);
|
||||
|
||||
char file_name[NAME_MAX];
|
||||
char file_path[NAME_MAX];
|
||||
dumper_->GetMappingEffectiveNameAndPath(
|
||||
LinuxDumper::GetMappingEffectiveNameAndPath(
|
||||
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||
|
||||
const size_t file_name_len = my_strlen(file_name);
|
||||
UntypedMDRVA cv(&minidump_writer_);
|
||||
if (!cv.Allocate(MDCVInfoPDB70_minsize + file_name_len + 1))
|
||||
return false;
|
||||
|
||||
// Write pdb_file_name
|
||||
my_memcpy(cv_ptr, file_name, file_name_len + 1);
|
||||
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + file_name_len + 1);
|
||||
|
||||
mod.cv_record = cv.location();
|
||||
|
||||
MDLocationDescriptor ld;
|
||||
if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld))
|
||||
return false;
|
||||
mod->module_name_rva = ld.rva;
|
||||
mod.module_name_rva = ld.rva;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -700,14 +690,17 @@ class MinidumpWriter {
|
|||
}
|
||||
|
||||
#ifdef __mips__
|
||||
const int32_t debug_tag = DT_MIPS_RLD_MAP;
|
||||
#else
|
||||
const int32_t debug_tag = DT_DEBUG;
|
||||
#endif
|
||||
if (dyn.d_tag == debug_tag) {
|
||||
if (dyn.d_tag == DT_MIPS_RLD_MAP) {
|
||||
r_debug = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr);
|
||||
continue;
|
||||
} else if (dyn.d_tag == DT_NULL) {
|
||||
}
|
||||
#else
|
||||
if (dyn.d_tag == DT_DEBUG) {
|
||||
r_debug = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
else if (dyn.d_tag == DT_NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -833,13 +826,7 @@ class MinidumpWriter {
|
|||
// processor_architecture should always be set, do this first
|
||||
sys_info->processor_architecture =
|
||||
#if defined(__mips__)
|
||||
# if _MIPS_SIM == _ABIO32
|
||||
MD_CPU_ARCHITECTURE_MIPS;
|
||||
# elif _MIPS_SIM == _ABI64
|
||||
MD_CPU_ARCHITECTURE_MIPS64;
|
||||
# else
|
||||
# error "This mips ABI is currently not supported (n32)"
|
||||
#endif
|
||||
#elif defined(__i386__)
|
||||
MD_CPU_ARCHITECTURE_X86;
|
||||
#else
|
||||
|
@ -855,14 +842,15 @@ class MinidumpWriter {
|
|||
ProcCpuInfoReader* const reader = new(allocator) ProcCpuInfoReader(fd);
|
||||
const char* field;
|
||||
while (reader->GetNextField(&field)) {
|
||||
bool is_first_entry = true;
|
||||
for (CpuInfoEntry& entry : cpu_info_table) {
|
||||
if (!is_first_entry && entry.found) {
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
|
||||
i++) {
|
||||
CpuInfoEntry* entry = &cpu_info_table[i];
|
||||
if (i > 0 && entry->found) {
|
||||
// except for the 'processor' field, ignore repeated values.
|
||||
continue;
|
||||
}
|
||||
is_first_entry = false;
|
||||
if (!my_strcmp(field, entry.info_name)) {
|
||||
if (!my_strcmp(field, entry->info_name)) {
|
||||
size_t value_len;
|
||||
const char* value = reader->GetValueAndLen(&value_len);
|
||||
if (value_len == 0)
|
||||
|
@ -872,8 +860,8 @@ class MinidumpWriter {
|
|||
if (my_read_decimal_ptr(&val, value) == value)
|
||||
continue;
|
||||
|
||||
entry.value = static_cast<int>(val);
|
||||
entry.found = true;
|
||||
entry->value = static_cast<int>(val);
|
||||
entry->found = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -889,8 +877,10 @@ class MinidumpWriter {
|
|||
}
|
||||
|
||||
// make sure we got everything we wanted
|
||||
for (const CpuInfoEntry& entry : cpu_info_table) {
|
||||
if (!entry.found) {
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]);
|
||||
i++) {
|
||||
if (!cpu_info_table[i].found) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1026,15 +1016,18 @@ class MinidumpWriter {
|
|||
new(allocator) ProcCpuInfoReader(fd);
|
||||
const char* field;
|
||||
while (reader->GetNextField(&field)) {
|
||||
for (const CpuIdEntry& entry : cpu_id_entries) {
|
||||
if (my_strcmp(entry.field, field) != 0)
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_id_entries)/sizeof(cpu_id_entries[0]);
|
||||
++i) {
|
||||
const CpuIdEntry* entry = &cpu_id_entries[i];
|
||||
if (my_strcmp(entry->field, field) != 0)
|
||||
continue;
|
||||
uintptr_t result = 0;
|
||||
const char* value = reader->GetValue();
|
||||
const char* p = value;
|
||||
if (value[0] == '0' && value[1] == 'x') {
|
||||
p = my_read_hex_ptr(&result, value+2);
|
||||
} else if (entry.format == 'x') {
|
||||
} else if (entry->format == 'x') {
|
||||
p = my_read_hex_ptr(&result, value);
|
||||
} else {
|
||||
p = my_read_decimal_ptr(&result, value);
|
||||
|
@ -1042,8 +1035,8 @@ class MinidumpWriter {
|
|||
if (p == value)
|
||||
continue;
|
||||
|
||||
result &= (1U << entry.bit_length)-1;
|
||||
result <<= entry.bit_lshift;
|
||||
result &= (1U << entry->bit_length)-1;
|
||||
result <<= entry->bit_lshift;
|
||||
sys_info->cpu.arm_cpu_info.cpuid |=
|
||||
static_cast<uint32_t>(result);
|
||||
}
|
||||
|
@ -1097,7 +1090,7 @@ class MinidumpWriter {
|
|||
const char* tag = value;
|
||||
size_t tag_len = value_len;
|
||||
const char* p = my_strchr(tag, ' ');
|
||||
if (p) {
|
||||
if (p != NULL) {
|
||||
tag_len = static_cast<size_t>(p - tag);
|
||||
value += tag_len + 1;
|
||||
value_len -= tag_len + 1;
|
||||
|
@ -1105,10 +1098,14 @@ class MinidumpWriter {
|
|||
tag_len = strlen(tag);
|
||||
value_len = 0;
|
||||
}
|
||||
for (const CpuFeaturesEntry& entry : cpu_features_entries) {
|
||||
if (tag_len == strlen(entry.tag) &&
|
||||
!memcmp(tag, entry.tag, tag_len)) {
|
||||
sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry.hwcaps;
|
||||
for (size_t i = 0;
|
||||
i < sizeof(cpu_features_entries)/
|
||||
sizeof(cpu_features_entries[0]);
|
||||
++i) {
|
||||
const CpuFeaturesEntry* entry = &cpu_features_entries[i];
|
||||
if (tag_len == strlen(entry->tag) &&
|
||||
!memcmp(tag, entry->tag, tag_len)) {
|
||||
sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry->hwcaps;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
|
||||
using namespace google_breakpad;
|
||||
|
||||
// Length of a formatted GUID string =
|
||||
// sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
|
||||
const int kGUIDStringSize = 37;
|
||||
|
||||
namespace {
|
||||
|
||||
typedef testing::Test MinidumpWriterTest;
|
||||
|
@ -133,7 +137,19 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
|||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
|
||||
char module_identifier_buffer[kGUIDStringSize];
|
||||
FileID::ConvertIdentifierToString(kModuleGUID,
|
||||
module_identifier_buffer,
|
||||
sizeof(module_identifier_buffer));
|
||||
string module_identifier(module_identifier_buffer);
|
||||
// Strip out dashes
|
||||
size_t pos;
|
||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
||||
module_identifier.erase(pos, 1);
|
||||
}
|
||||
// And append a zero, because module IDs include an "age" field
|
||||
// which is always zero on Linux.
|
||||
module_identifier += "0";
|
||||
|
||||
// Get some memory.
|
||||
char* memory =
|
||||
|
@ -169,7 +185,6 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
|||
info.start_addr = kMemoryAddress;
|
||||
info.size = memory_size;
|
||||
info.offset = 0;
|
||||
info.exec = false;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
|
@ -215,53 +230,6 @@ TEST(MinidumpWriterTest, MappingInfo) {
|
|||
close(fds[1]);
|
||||
}
|
||||
|
||||
// Test that a binary with a longer-than-usual build id note
|
||||
// makes its way all the way through to the minidump unscathed.
|
||||
// The linux_client_unittest is linked with an explicit --build-id
|
||||
// in Makefile.am.
|
||||
TEST(MinidumpWriterTest, BuildIDLong) {
|
||||
int fds[2];
|
||||
ASSERT_NE(-1, pipe(fds));
|
||||
|
||||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[1]);
|
||||
char b;
|
||||
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
|
||||
close(fds[0]);
|
||||
syscall(__NR_exit);
|
||||
}
|
||||
close(fds[0]);
|
||||
|
||||
ExceptionHandler::CrashContext context;
|
||||
memset(&context, 0, sizeof(context));
|
||||
ASSERT_EQ(0, getcontext(&context.context));
|
||||
context.tid = child;
|
||||
|
||||
AutoTempDir temp_dir;
|
||||
const string dump_path = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||
|
||||
EXPECT_TRUE(WriteMinidump(dump_path.c_str(),
|
||||
child, &context, sizeof(context)));
|
||||
close(fds[1]);
|
||||
|
||||
// Read the minidump. Load the module list, and ensure that
|
||||
// the main module has the correct debug id and code id.
|
||||
Minidump minidump(dump_path);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
|
||||
MinidumpModuleList* module_list = minidump.GetModuleList();
|
||||
ASSERT_TRUE(module_list);
|
||||
const MinidumpModule* module = module_list->GetMainModule();
|
||||
ASSERT_TRUE(module);
|
||||
const string module_identifier = "030201000504070608090A0B0C0D0E0F0";
|
||||
// This is passed explicitly to the linker in Makefile.am
|
||||
const string build_id =
|
||||
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
|
||||
EXPECT_EQ(module_identifier, module->debug_identifier());
|
||||
EXPECT_EQ(build_id, module->code_identifier());
|
||||
}
|
||||
|
||||
// Test that mapping info can be specified, and that it overrides
|
||||
// existing mappings that are wholly contained within the specified
|
||||
// range.
|
||||
|
@ -277,7 +245,19 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
|
|||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
|
||||
};
|
||||
const string module_identifier = "33221100554477668899AABBCCDDEEFF0";
|
||||
char module_identifier_buffer[kGUIDStringSize];
|
||||
FileID::ConvertIdentifierToString(kModuleGUID,
|
||||
module_identifier_buffer,
|
||||
sizeof(module_identifier_buffer));
|
||||
string module_identifier(module_identifier_buffer);
|
||||
// Strip out dashes
|
||||
size_t pos;
|
||||
while ((pos = module_identifier.find('-')) != string::npos) {
|
||||
module_identifier.erase(pos, 1);
|
||||
}
|
||||
// And append a zero, because module IDs include an "age" field
|
||||
// which is always zero on Linux.
|
||||
module_identifier += "0";
|
||||
|
||||
// mmap a file
|
||||
AutoTempDir temp_dir;
|
||||
|
@ -324,7 +304,6 @@ TEST(MinidumpWriterTest, MappingInfoContained) {
|
|||
info.start_addr = kMemoryAddress - memory_size;
|
||||
info.size = memory_size * 3;
|
||||
info.offset = 0;
|
||||
info.exec = false;
|
||||
strcpy(info.name, kMemoryName);
|
||||
|
||||
MappingList mappings;
|
||||
|
@ -431,10 +410,12 @@ TEST(MinidumpWriterTest, DeletedBinary) {
|
|||
EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
|
||||
// Check that the file ID is correct.
|
||||
FileID fileid(helper_path.c_str());
|
||||
PageAllocator allocator;
|
||||
wasteful_vector<uint8_t> identifier(&allocator, kDefaultBuildIdSize);
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
|
||||
string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier);
|
||||
char identifier_string[kGUIDStringSize];
|
||||
FileID::ConvertIdentifierToString(identifier,
|
||||
identifier_string,
|
||||
kGUIDStringSize);
|
||||
string module_identifier(identifier_string);
|
||||
// Strip out dashes
|
||||
size_t pos;
|
||||
|
|
|
@ -583,6 +583,7 @@
|
|||
4DBE4769134A4F080072546A /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; };
|
||||
8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMDefines.h; path = ../../common/mac/GTMDefines.h; sourceTree = SOURCE_ROOT; };
|
||||
8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
8B31022211F0CE1000FCF3E4 /* GTMGarbageCollection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMGarbageCollection.h; path = ../../common/mac/GTMGarbageCollection.h; sourceTree = SOURCE_ROOT; };
|
||||
8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
8B31FFF611F0C90500FCF3E4 /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; };
|
||||
|
@ -954,6 +955,7 @@
|
|||
children = (
|
||||
162F64F0161C577500CD68D5 /* arch_utilities.cc */,
|
||||
162F64F1161C577500CD68D5 /* arch_utilities.h */,
|
||||
8B31022211F0CE1000FCF3E4 /* GTMGarbageCollection.h */,
|
||||
8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */,
|
||||
F9C77E0F0F7DDF650045F7DB /* testing */,
|
||||
F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */,
|
||||
|
|
|
@ -364,7 +364,7 @@ static uint64_t LookupSymbol(const char* symbol_name,
|
|||
return list.n_value;
|
||||
}
|
||||
|
||||
#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
|
||||
#if TARGET_OS_IPHONE
|
||||
static bool HasTaskDyldInfo() {
|
||||
return true;
|
||||
}
|
||||
|
@ -381,9 +381,13 @@ static SInt32 GetOSVersion() {
|
|||
}
|
||||
|
||||
static bool HasTaskDyldInfo() {
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
|
||||
return true;
|
||||
#else
|
||||
return GetOSVersion() >= 0x1060;
|
||||
#endif
|
||||
}
|
||||
#endif // TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= 10_6
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
uint64_t DynamicImages::GetDyldAllImageInfosPointer() {
|
||||
if (HasTaskDyldInfo()) {
|
||||
|
|
|
@ -133,47 +133,25 @@ void MinidumpGenerator::GatherSystemInformation() {
|
|||
vers_path,
|
||||
kCFURLPOSIXPathStyle,
|
||||
false);
|
||||
CFReadStreamRef read_stream = CFReadStreamCreateWithFile(NULL, sys_vers);
|
||||
CFRelease(sys_vers);
|
||||
if (!read_stream) {
|
||||
return;
|
||||
}
|
||||
if (!CFReadStreamOpen(read_stream)) {
|
||||
CFRelease(read_stream);
|
||||
return;
|
||||
}
|
||||
CFMutableDataRef data = NULL;
|
||||
while (true) {
|
||||
// Actual data file tests: Mac at 480 bytes and iOS at 413 bytes.
|
||||
const CFIndex kMaxBufferLength = 1024;
|
||||
UInt8 data_bytes[kMaxBufferLength];
|
||||
CFIndex num_bytes_read =
|
||||
CFReadStreamRead(read_stream, data_bytes, kMaxBufferLength);
|
||||
if (num_bytes_read < 0) {
|
||||
if (data) {
|
||||
CFRelease(data);
|
||||
data = NULL;
|
||||
}
|
||||
break;
|
||||
} else if (num_bytes_read == 0) {
|
||||
break;
|
||||
} else if (!data) {
|
||||
data = CFDataCreateMutable(NULL, 0);
|
||||
}
|
||||
CFDataAppendBytes(data, data_bytes, num_bytes_read);
|
||||
}
|
||||
CFReadStreamClose(read_stream);
|
||||
CFRelease(read_stream);
|
||||
CFDataRef data;
|
||||
SInt32 error;
|
||||
CFURLCreateDataAndPropertiesFromResource(NULL, sys_vers, &data, NULL, NULL,
|
||||
&error);
|
||||
|
||||
if (!data) {
|
||||
CFRelease(sys_vers);
|
||||
return;
|
||||
}
|
||||
CFDictionaryRef list =
|
||||
static_cast<CFDictionaryRef>(CFPropertyListCreateWithData(
|
||||
NULL, data, kCFPropertyListImmutable, NULL, NULL));
|
||||
CFRelease(data);
|
||||
|
||||
CFDictionaryRef list = static_cast<CFDictionaryRef>
|
||||
(CFPropertyListCreateFromXMLData(NULL, data, kCFPropertyListImmutable,
|
||||
NULL));
|
||||
if (!list) {
|
||||
CFRelease(sys_vers);
|
||||
CFRelease(data);
|
||||
return;
|
||||
}
|
||||
|
||||
CFStringRef build_version = static_cast<CFStringRef>
|
||||
(CFDictionaryGetValue(list, CFSTR("ProductBuildVersion")));
|
||||
CFStringRef product_version = static_cast<CFStringRef>
|
||||
|
@ -182,6 +160,8 @@ void MinidumpGenerator::GatherSystemInformation() {
|
|||
string product_str = ConvertToString(product_version);
|
||||
|
||||
CFRelease(list);
|
||||
CFRelease(sys_vers);
|
||||
CFRelease(data);
|
||||
|
||||
strlcpy(build_string_, build_str.c_str(), sizeof(build_string_));
|
||||
|
||||
|
|
|
@ -44,47 +44,6 @@
|
|||
#include "third_party/lss/linux_syscall_support.h"
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <errno.h>
|
||||
|
||||
namespace {
|
||||
|
||||
bool g_need_ftruncate_workaround = false;
|
||||
bool g_checked_need_ftruncate_workaround = false;
|
||||
|
||||
void CheckNeedsFTruncateWorkAround(int file) {
|
||||
if (g_checked_need_ftruncate_workaround) {
|
||||
return;
|
||||
}
|
||||
g_checked_need_ftruncate_workaround = true;
|
||||
|
||||
// Attempt an idempotent truncate that chops off nothing and see if we
|
||||
// run into any sort of errors.
|
||||
off_t offset = sys_lseek(file, 0, SEEK_END);
|
||||
if (offset == -1) {
|
||||
// lseek failed. Don't apply work around. It's unlikely that we can write
|
||||
// to a minidump with either method.
|
||||
return;
|
||||
}
|
||||
|
||||
int result = ftruncate(file, offset);
|
||||
if (result == -1 && errno == EACCES) {
|
||||
// It very likely that we are running into the kernel bug in M devices.
|
||||
// We are going to deploy the workaround for writing minidump files
|
||||
// without uses of ftruncate(). This workaround should be fine even
|
||||
// for kernels without the bug.
|
||||
// See http://crbug.com/542840 for more details.
|
||||
g_need_ftruncate_workaround = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool NeedsFTruncateWorkAround() {
|
||||
return g_need_ftruncate_workaround;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif // defined(__ANDROID__)
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
|
||||
|
@ -116,24 +75,15 @@ void MinidumpFileWriter::SetFile(const int file) {
|
|||
assert(file_ == -1);
|
||||
file_ = file;
|
||||
close_file_when_destroyed_ = false;
|
||||
#if defined(__ANDROID__)
|
||||
CheckNeedsFTruncateWorkAround(file);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool MinidumpFileWriter::Close() {
|
||||
bool result = true;
|
||||
|
||||
if (file_ != -1) {
|
||||
#if defined(__ANDROID__)
|
||||
if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) {
|
||||
if (-1 == ftruncate(file_, position_)) {
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (ftruncate(file_, position_)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if defined(__linux__) && __linux__
|
||||
result = (sys_close(file_) == 0);
|
||||
#else
|
||||
|
@ -270,20 +220,6 @@ bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
|
|||
MDRVA MinidumpFileWriter::Allocate(size_t size) {
|
||||
assert(size);
|
||||
assert(file_ != -1);
|
||||
#if defined(__ANDROID__)
|
||||
if (NeedsFTruncateWorkAround()) {
|
||||
// If ftruncate() is not available. We simply increase the size beyond the
|
||||
// current file size. sys_write() will expand the file when data is written
|
||||
// to it. Because we did not over allocate to fit memory pages, we also
|
||||
// do not need to ftruncate() the file once we are done.
|
||||
size_ += size;
|
||||
|
||||
// We don't need to seek since the file is unchanged.
|
||||
MDRVA current_position = position_;
|
||||
position_ += static_cast<MDRVA>(size);
|
||||
return current_position;
|
||||
}
|
||||
#endif
|
||||
size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
|
||||
|
||||
if (position_ + aligned_size > size_) {
|
||||
|
@ -320,16 +256,14 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
|
|||
#if defined(__linux__) && __linux__
|
||||
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
||||
if (sys_write(file_, src, size) == size) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
||||
if (write(file_, src, size) == size) {
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,12 @@
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
static const int kWaitForHandlerThreadMs = 60000;
|
||||
static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
|
||||
|
||||
// As documented on MSDN, on failure SuspendThread returns (DWORD) -1
|
||||
static const DWORD kFailedToSuspendThread = static_cast<DWORD>(-1);
|
||||
|
||||
// This is passed as the context to the MinidumpWriteDump callback.
|
||||
typedef struct {
|
||||
AppMemoryList::const_iterator iter;
|
||||
|
@ -211,7 +217,6 @@ void ExceptionHandler::Initialize(
|
|||
// Don't attempt to create the thread if we could not create the semaphores.
|
||||
if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) {
|
||||
DWORD thread_id;
|
||||
const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
|
||||
handler_thread_ = CreateThread(NULL, // lpThreadAttributes
|
||||
kExceptionHandlerThreadInitialStackSize,
|
||||
ExceptionHandlerThreadMain,
|
||||
|
@ -348,7 +353,6 @@ ExceptionHandler::~ExceptionHandler() {
|
|||
// inside DllMain.
|
||||
is_shutdown_ = true;
|
||||
ReleaseSemaphore(handler_start_semaphore_, 1, NULL);
|
||||
const int kWaitForHandlerThreadMs = 60000;
|
||||
WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs);
|
||||
#else
|
||||
TerminateThread(handler_thread_, 1);
|
||||
|
@ -777,8 +781,6 @@ bool ExceptionHandler::WriteMinidumpForChild(HANDLE child,
|
|||
EXCEPTION_RECORD ex;
|
||||
CONTEXT ctx;
|
||||
EXCEPTION_POINTERS exinfo = { NULL, NULL };
|
||||
// As documented on MSDN, on failure SuspendThread returns (DWORD) -1
|
||||
const DWORD kFailedToSuspendThread = static_cast<DWORD>(-1);
|
||||
DWORD last_suspend_count = kFailedToSuspendThread;
|
||||
HANDLE child_thread_handle = OpenThread(THREAD_GET_CONTEXT |
|
||||
THREAD_QUERY_INFORMATION |
|
||||
|
|
|
@ -101,8 +101,8 @@ struct elf_prpsinfo {
|
|||
unsigned int pr_uid;
|
||||
unsigned int pr_gid;
|
||||
#elif defined(__mips__)
|
||||
__kernel_uid_t pr_uid;
|
||||
__kernel_gid_t pr_gid;
|
||||
unsigned long pr_uid;
|
||||
unsigned long pr_gid;
|
||||
#else
|
||||
unsigned short pr_uid;
|
||||
unsigned short pr_gid;
|
||||
|
|
|
@ -34,20 +34,12 @@
|
|||
// glibc) and therefore avoid doing otherwise awkward #ifdefs in the code.
|
||||
// The following quirks are currently handled by this file:
|
||||
// - i386: Use the Android NDK but alias user_fxsr_struct > user_fpxregs_struct.
|
||||
// - aarch64:
|
||||
// - NDK r10: Add missing user_regs_struct and user_fpsimd_struct structs.
|
||||
// - NDK r11+: Add missing <stdint.h> include
|
||||
// - aarch64: Add missing user_regs_struct and user_fpsimd_struct structs.
|
||||
// - Other platforms: Just use the Android NDK unchanged.
|
||||
|
||||
// TODO(primiano): remove these changes after Chromium has stably rolled to
|
||||
// an NDK with the appropriate fixes.
|
||||
|
||||
#if defined(ANDROID_NDK_MAJOR_VERSION) && ANDROID_NDK_MAJOR_VERSION > 10
|
||||
#ifdef __aarch64__
|
||||
#include <stdint.h>
|
||||
#endif // __aarch64__
|
||||
#endif // defined(ANDROID_NDK_MAJOR_VERSION) && ANDROID_NDK_MAJOR_VERSION > 10
|
||||
|
||||
#include_next <sys/user.h>
|
||||
|
||||
#ifdef __i386__
|
||||
|
@ -60,7 +52,6 @@ typedef struct user_fxsr_struct user_fpxregs_struct;
|
|||
#endif // __cplusplus
|
||||
#endif // __i386__
|
||||
|
||||
#if !defined(ANDROID_NDK_MAJOR_VERSION) || ANDROID_NDK_MAJOR_VERSION == 10
|
||||
#ifdef __aarch64__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -80,6 +71,5 @@ struct user_fpsimd_struct {
|
|||
} // extern "C"
|
||||
#endif // __cplusplus
|
||||
#endif // __aarch64__
|
||||
#endif // defined(ANDROID_NDK_VERSION) && ANDROID_NDK_MAJOR_VERSION == 10
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_SYS_USER_H
|
||||
|
|
|
@ -35,11 +35,6 @@
|
|||
['OS=="linux"', {
|
||||
'defines': ['HAVE_A_OUT_H'],
|
||||
}],
|
||||
['OS!="android"', {'sources/': [['exclude', '(^|/)android/']]}],
|
||||
['OS!="linux"', {'sources/': [['exclude', '(^|/)linux/']]}],
|
||||
['OS!="mac"', {'sources/': [['exclude', '(^|/)mac/']]}],
|
||||
['OS!="solaris"', {'sources/': [['exclude', '(^|/)solaris/']]}],
|
||||
['OS!="win"', {'sources/': [['exclude', '(^|/)windows/']]}],
|
||||
],
|
||||
},
|
||||
'targets': [
|
||||
|
@ -75,8 +70,6 @@
|
|||
'dwarf/dwarf2reader.cc',
|
||||
'dwarf/dwarf2reader.h',
|
||||
'dwarf/dwarf2reader_test_common.h',
|
||||
'dwarf/elf_reader.cc',
|
||||
'dwarf/elf_reader.h',
|
||||
'dwarf/functioninfo.cc',
|
||||
'dwarf/functioninfo.h',
|
||||
'dwarf/line_state_machine.h',
|
||||
|
|
|
@ -32,15 +32,16 @@
|
|||
#include "common/dwarf/bytereader.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace dwarf2reader {
|
||||
|
||||
inline uint8 ByteReader::ReadOneByte(const uint8_t *buffer) const {
|
||||
inline uint8 ByteReader::ReadOneByte(const char* buffer) const {
|
||||
return buffer[0];
|
||||
}
|
||||
|
||||
inline uint16 ByteReader::ReadTwoBytes(const uint8_t *buffer) const {
|
||||
inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const {
|
||||
const unsigned char *buffer
|
||||
= reinterpret_cast<const unsigned char *>(signed_buffer);
|
||||
const uint16 buffer0 = buffer[0];
|
||||
const uint16 buffer1 = buffer[1];
|
||||
if (endian_ == ENDIANNESS_LITTLE) {
|
||||
|
@ -50,7 +51,9 @@ inline uint16 ByteReader::ReadTwoBytes(const uint8_t *buffer) const {
|
|||
}
|
||||
}
|
||||
|
||||
inline uint64 ByteReader::ReadFourBytes(const uint8_t *buffer) const {
|
||||
inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const {
|
||||
const unsigned char *buffer
|
||||
= reinterpret_cast<const unsigned char *>(signed_buffer);
|
||||
const uint32 buffer0 = buffer[0];
|
||||
const uint32 buffer1 = buffer[1];
|
||||
const uint32 buffer2 = buffer[2];
|
||||
|
@ -62,7 +65,9 @@ inline uint64 ByteReader::ReadFourBytes(const uint8_t *buffer) const {
|
|||
}
|
||||
}
|
||||
|
||||
inline uint64 ByteReader::ReadEightBytes(const uint8_t *buffer) const {
|
||||
inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const {
|
||||
const unsigned char *buffer
|
||||
= reinterpret_cast<const unsigned char *>(signed_buffer);
|
||||
const uint64 buffer0 = buffer[0];
|
||||
const uint64 buffer1 = buffer[1];
|
||||
const uint64 buffer2 = buffer[2];
|
||||
|
@ -84,12 +89,12 @@ inline uint64 ByteReader::ReadEightBytes(const uint8_t *buffer) const {
|
|||
// information, plus one bit saying whether the number continues or
|
||||
// not.
|
||||
|
||||
inline uint64 ByteReader::ReadUnsignedLEB128(const uint8_t *buffer,
|
||||
inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer,
|
||||
size_t* len) const {
|
||||
uint64 result = 0;
|
||||
size_t num_read = 0;
|
||||
unsigned int shift = 0;
|
||||
uint8_t byte;
|
||||
unsigned char byte;
|
||||
|
||||
do {
|
||||
byte = *buffer++;
|
||||
|
@ -109,12 +114,12 @@ inline uint64 ByteReader::ReadUnsignedLEB128(const uint8_t *buffer,
|
|||
// Read a signed LEB128 number. These are like regular LEB128
|
||||
// numbers, except the last byte may have a sign bit set.
|
||||
|
||||
inline int64 ByteReader::ReadSignedLEB128(const uint8_t *buffer,
|
||||
inline int64 ByteReader::ReadSignedLEB128(const char* buffer,
|
||||
size_t* len) const {
|
||||
int64 result = 0;
|
||||
unsigned int shift = 0;
|
||||
size_t num_read = 0;
|
||||
uint8_t byte;
|
||||
unsigned char byte;
|
||||
|
||||
do {
|
||||
byte = *buffer++;
|
||||
|
@ -129,18 +134,18 @@ inline int64 ByteReader::ReadSignedLEB128(const uint8_t *buffer,
|
|||
return result;
|
||||
}
|
||||
|
||||
inline uint64 ByteReader::ReadOffset(const uint8_t *buffer) const {
|
||||
inline uint64 ByteReader::ReadOffset(const char* buffer) const {
|
||||
assert(this->offset_reader_);
|
||||
return (this->*offset_reader_)(buffer);
|
||||
}
|
||||
|
||||
inline uint64 ByteReader::ReadAddress(const uint8_t *buffer) const {
|
||||
inline uint64 ByteReader::ReadAddress(const char* buffer) const {
|
||||
assert(this->address_reader_);
|
||||
return (this->*address_reader_)(buffer);
|
||||
}
|
||||
|
||||
inline void ByteReader::SetCFIDataBase(uint64 section_base,
|
||||
const uint8_t *buffer_base) {
|
||||
const char *buffer_base) {
|
||||
section_base_ = section_base;
|
||||
buffer_base_ = buffer_base;
|
||||
have_section_base_ = true;
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
|
@ -63,7 +62,7 @@ void ByteReader::SetAddressSize(uint8 size) {
|
|||
}
|
||||
}
|
||||
|
||||
uint64 ByteReader::ReadInitialLength(const uint8_t *start, size_t* len) {
|
||||
uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
|
||||
const uint64 initial_length = ReadFourBytes(start);
|
||||
start += 4;
|
||||
|
||||
|
@ -101,7 +100,7 @@ bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
|
|||
}
|
||||
}
|
||||
|
||||
uint64 ByteReader::ReadEncodedPointer(const uint8_t *buffer,
|
||||
uint64 ByteReader::ReadEncodedPointer(const char *buffer,
|
||||
DwarfPointerEncoding encoding,
|
||||
size_t *len) const {
|
||||
// UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
|
||||
|
@ -130,7 +129,7 @@ uint64 ByteReader::ReadEncodedPointer(const uint8_t *buffer,
|
|||
// Round up to the next boundary.
|
||||
uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
|
||||
// Convert back to a pointer.
|
||||
const uint8_t *aligned_buffer = buffer_base_ + (aligned - skew);
|
||||
const char *aligned_buffer = buffer_base_ + (aligned - skew);
|
||||
// Finally, store the length and actually fetch the pointer.
|
||||
*len = aligned_buffer - buffer + AddressSize();
|
||||
return ReadAddress(aligned_buffer);
|
||||
|
@ -243,8 +242,4 @@ uint64 ByteReader::ReadEncodedPointer(const uint8_t *buffer,
|
|||
return pointer;
|
||||
}
|
||||
|
||||
Endianness ByteReader::GetEndianness() const {
|
||||
return endian_;
|
||||
}
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
|
|
@ -31,10 +31,7 @@
|
|||
#ifndef COMMON_DWARF_BYTEREADER_H__
|
||||
#define COMMON_DWARF_BYTEREADER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/dwarf/types.h"
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
|
||||
|
@ -62,22 +59,22 @@ class ByteReader {
|
|||
|
||||
// Read a single byte from BUFFER and return it as an unsigned 8 bit
|
||||
// number.
|
||||
uint8 ReadOneByte(const uint8_t *buffer) const;
|
||||
uint8 ReadOneByte(const char* buffer) const;
|
||||
|
||||
// Read two bytes from BUFFER and return them as an unsigned 16 bit
|
||||
// number, using this ByteReader's endianness.
|
||||
uint16 ReadTwoBytes(const uint8_t *buffer) const;
|
||||
uint16 ReadTwoBytes(const char* buffer) const;
|
||||
|
||||
// Read four bytes from BUFFER and return them as an unsigned 32 bit
|
||||
// number, using this ByteReader's endianness. This function returns
|
||||
// a uint64 so that it is compatible with ReadAddress and
|
||||
// ReadOffset. The number it returns will never be outside the range
|
||||
// of an unsigned 32 bit integer.
|
||||
uint64 ReadFourBytes(const uint8_t *buffer) const;
|
||||
uint64 ReadFourBytes(const char* buffer) const;
|
||||
|
||||
// Read eight bytes from BUFFER and return them as an unsigned 64
|
||||
// bit number, using this ByteReader's endianness.
|
||||
uint64 ReadEightBytes(const uint8_t *buffer) const;
|
||||
uint64 ReadEightBytes(const char* buffer) const;
|
||||
|
||||
// Read an unsigned LEB128 (Little Endian Base 128) number from
|
||||
// BUFFER and return it as an unsigned 64 bit integer. Set LEN to
|
||||
|
@ -96,7 +93,7 @@ class ByteReader {
|
|||
// In other words, we break VALUE into groups of seven bits, put
|
||||
// them in little-endian order, and then write them as eight-bit
|
||||
// bytes with the high bit on all but the last.
|
||||
uint64 ReadUnsignedLEB128(const uint8_t *buffer, size_t *len) const;
|
||||
uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const;
|
||||
|
||||
// Read a signed LEB128 number from BUFFER and return it as an
|
||||
// signed 64 bit integer. Set LEN to the number of bytes read.
|
||||
|
@ -115,7 +112,7 @@ class ByteReader {
|
|||
// In other words, we break VALUE into groups of seven bits, put
|
||||
// them in little-endian order, and then write them as eight-bit
|
||||
// bytes with the high bit on all but the last.
|
||||
int64 ReadSignedLEB128(const uint8_t *buffer, size_t *len) const;
|
||||
int64 ReadSignedLEB128(const char* buffer, size_t* len) const;
|
||||
|
||||
// Indicate that addresses on this architecture are SIZE bytes long. SIZE
|
||||
// must be either 4 or 8. (DWARF allows addresses to be any number of
|
||||
|
@ -138,7 +135,7 @@ class ByteReader {
|
|||
// Read an address from BUFFER and return it as an unsigned 64 bit
|
||||
// integer, respecting this ByteReader's endianness and address size. You
|
||||
// must call SetAddressSize before calling this function.
|
||||
uint64 ReadAddress(const uint8_t *buffer) const;
|
||||
uint64 ReadAddress(const char* buffer) const;
|
||||
|
||||
// DWARF actually defines two slightly different formats: 32-bit DWARF
|
||||
// and 64-bit DWARF. This is *not* related to the size of registers or
|
||||
|
@ -175,14 +172,14 @@ class ByteReader {
|
|||
// - The 32-bit value 0xffffffff, followed by a 64-bit byte count,
|
||||
// indicating that the data whose length is being measured uses
|
||||
// the 64-bit DWARF format.
|
||||
uint64 ReadInitialLength(const uint8_t *start, size_t *len);
|
||||
uint64 ReadInitialLength(const char* start, size_t* len);
|
||||
|
||||
// Read an offset from BUFFER and return it as an unsigned 64 bit
|
||||
// integer, respecting the ByteReader's endianness. In 32-bit DWARF, the
|
||||
// offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes
|
||||
// long. You must call ReadInitialLength or SetOffsetSize before calling
|
||||
// this function; see the comments above for details.
|
||||
uint64 ReadOffset(const uint8_t *buffer) const;
|
||||
uint64 ReadOffset(const char* buffer) const;
|
||||
|
||||
// Return the current offset size, in bytes.
|
||||
// A return value of 4 indicates that we are reading 32-bit DWARF.
|
||||
|
@ -237,7 +234,7 @@ class ByteReader {
|
|||
// is BUFFER_BASE. This allows us to find the address that a given
|
||||
// byte in our buffer would have when loaded into the program the
|
||||
// data describes. We need this to resolve DW_EH_PE_pcrel pointers.
|
||||
void SetCFIDataBase(uint64 section_base, const uint8_t *buffer_base);
|
||||
void SetCFIDataBase(uint64 section_base, const char *buffer_base);
|
||||
|
||||
// Indicate that the base address of the program's ".text" section
|
||||
// is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers.
|
||||
|
@ -276,15 +273,13 @@ class ByteReader {
|
|||
// base address this reader hasn't been given, so you should check
|
||||
// with ValidEncoding and UsableEncoding first if you would rather
|
||||
// die in a more helpful way.
|
||||
uint64 ReadEncodedPointer(const uint8_t *buffer,
|
||||
DwarfPointerEncoding encoding,
|
||||
uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding,
|
||||
size_t *len) const;
|
||||
|
||||
Endianness GetEndianness() const;
|
||||
private:
|
||||
|
||||
// Function pointer type for our address and offset readers.
|
||||
typedef uint64 (ByteReader::*AddressReader)(const uint8_t *) const;
|
||||
typedef uint64 (ByteReader::*AddressReader)(const char*) const;
|
||||
|
||||
// Read an offset from BUFFER and return it as an unsigned 64 bit
|
||||
// integer. DWARF2/3 define offsets as either 4 or 8 bytes,
|
||||
|
@ -307,7 +302,7 @@ class ByteReader {
|
|||
bool have_section_base_, have_text_base_, have_data_base_;
|
||||
bool have_function_base_;
|
||||
uint64 section_base_, text_base_, data_base_, function_base_;
|
||||
const uint8_t *buffer_base_;
|
||||
const char *buffer_base_;
|
||||
};
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
|
||||
// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
@ -73,7 +71,7 @@ TEST_F(Reader, SimpleConstructor) {
|
|||
.LEB128(-0x4f337badf4483f83LL)
|
||||
.D32(0xfec319c9);
|
||||
ASSERT_TRUE(section.GetContents(&contents));
|
||||
const uint8_t *data = reinterpret_cast<const uint8_t *>(contents.data());
|
||||
const char *data = contents.data();
|
||||
EXPECT_EQ(0xc0U, reader.ReadOneByte(data));
|
||||
EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1));
|
||||
EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3));
|
||||
|
@ -377,7 +375,7 @@ TEST_F(Reader, ValidEncodings) {
|
|||
}
|
||||
|
||||
TEST_F(ReaderDeathTest, DW_EH_PE_omit) {
|
||||
static const uint8_t data[] = { 42 };
|
||||
static const char data[1] = { 42 };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetAddressSize(4);
|
||||
EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit,
|
||||
|
@ -386,7 +384,7 @@ TEST_F(ReaderDeathTest, DW_EH_PE_omit) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_absptr4) {
|
||||
static const uint8_t data[] = { 0x27, 0x57, 0xea, 0x40 };
|
||||
static const char data[] = { 0x27, 0x57, 0xea, 0x40 };
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
reader.SetAddressSize(4);
|
||||
EXPECT_EQ(0x40ea5727U,
|
||||
|
@ -396,7 +394,7 @@ TEST_F(Reader, DW_EH_PE_absptr4) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_absptr8) {
|
||||
static const uint8_t data[] = {
|
||||
static const char data[] = {
|
||||
0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50
|
||||
};
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
|
@ -408,7 +406,7 @@ TEST_F(Reader, DW_EH_PE_absptr8) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_uleb128) {
|
||||
static const uint8_t data[] = { 0x81, 0x84, 0x4c };
|
||||
static const char data[] = { 0x81, 0x84, 0x4c };
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
reader.SetAddressSize(4);
|
||||
EXPECT_EQ(0x130201U,
|
||||
|
@ -418,7 +416,7 @@ TEST_F(Reader, DW_EH_PE_uleb128) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_udata2) {
|
||||
static const uint8_t data[] = { 0xf4, 0x8d };
|
||||
static const char data[] = { 0xf4, 0x8d };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetAddressSize(4);
|
||||
EXPECT_EQ(0xf48dU,
|
||||
|
@ -428,7 +426,7 @@ TEST_F(Reader, DW_EH_PE_udata2) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_udata4) {
|
||||
static const uint8_t data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b };
|
||||
static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetAddressSize(8);
|
||||
EXPECT_EQ(0xa5628f8b,
|
||||
|
@ -438,7 +436,7 @@ TEST_F(Reader, DW_EH_PE_udata4) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_udata8Addr8) {
|
||||
static const uint8_t data[] = {
|
||||
static const char data[] = {
|
||||
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
|
||||
};
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
|
@ -450,7 +448,7 @@ TEST_F(Reader, DW_EH_PE_udata8Addr8) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_udata8Addr4) {
|
||||
static const uint8_t data[] = {
|
||||
static const char data[] = {
|
||||
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
|
||||
};
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
|
@ -462,7 +460,7 @@ TEST_F(Reader, DW_EH_PE_udata8Addr4) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_sleb128) {
|
||||
static const uint8_t data[] = { 0x42, 0xff, 0xfb, 0x73 };
|
||||
static const char data[] = { 0x42, 0xff, 0xfb, 0x73 };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetAddressSize(4);
|
||||
EXPECT_EQ(-0x030201U & 0xffffffff,
|
||||
|
@ -472,7 +470,7 @@ TEST_F(Reader, DW_EH_PE_sleb128) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_sdata2) {
|
||||
static const uint8_t data[] = { 0xb9, 0xbf };
|
||||
static const char data[] = { 0xb9, 0xbf };
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
reader.SetAddressSize(8);
|
||||
EXPECT_EQ(0xffffffffffffbfb9ULL,
|
||||
|
@ -482,7 +480,7 @@ TEST_F(Reader, DW_EH_PE_sdata2) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_sdata4) {
|
||||
static const uint8_t data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad };
|
||||
static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad };
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
reader.SetAddressSize(8);
|
||||
EXPECT_EQ(0xffffffffadc2b8f2ULL,
|
||||
|
@ -492,7 +490,7 @@ TEST_F(Reader, DW_EH_PE_sdata4) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_sdata8) {
|
||||
static const uint8_t data[] = {
|
||||
static const char data[] = {
|
||||
0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87
|
||||
};
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
|
@ -504,9 +502,7 @@ TEST_F(Reader, DW_EH_PE_sdata8) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_pcrel) {
|
||||
static const uint8_t data[] = {
|
||||
0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce
|
||||
};
|
||||
static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetAddressSize(4);
|
||||
DwarfPointerEncoding encoding =
|
||||
|
@ -519,9 +515,7 @@ TEST_F(Reader, DW_EH_PE_pcrel) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_textrel) {
|
||||
static const uint8_t data[] = {
|
||||
0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e
|
||||
};
|
||||
static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e };
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
reader.SetAddressSize(4);
|
||||
reader.SetTextBase(0xb91beaf0);
|
||||
|
@ -534,9 +528,7 @@ TEST_F(Reader, DW_EH_PE_textrel) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_datarel) {
|
||||
static const uint8_t data[] = {
|
||||
0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39
|
||||
};
|
||||
static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetAddressSize(8);
|
||||
reader.SetDataBase(0xbef308bd25ce74f0ULL);
|
||||
|
@ -549,9 +541,7 @@ TEST_F(Reader, DW_EH_PE_datarel) {
|
|||
}
|
||||
|
||||
TEST_F(Reader, DW_EH_PE_funcrel) {
|
||||
static const uint8_t data[] = {
|
||||
0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9
|
||||
};
|
||||
static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetAddressSize(4);
|
||||
reader.SetFunctionBase(0x823c3520);
|
||||
|
@ -564,7 +554,7 @@ TEST_F(Reader, DW_EH_PE_funcrel) {
|
|||
}
|
||||
|
||||
TEST(UsableBase, CFI) {
|
||||
static const uint8_t data[] = { 0x42 };
|
||||
static const char data[1] = { 0x42 };
|
||||
ByteReader reader(ENDIANNESS_BIG);
|
||||
reader.SetCFIDataBase(0xb31cbd20, data);
|
||||
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
|
||||
|
@ -627,12 +617,12 @@ TEST(UsableBase, ClearFunction) {
|
|||
|
||||
struct AlignedFixture {
|
||||
AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); }
|
||||
static const uint8_t data[10];
|
||||
static const char data[10];
|
||||
ByteReader reader;
|
||||
size_t pointer_size;
|
||||
};
|
||||
|
||||
const uint8_t AlignedFixture::data[10] = {
|
||||
const char AlignedFixture::data[10] = {
|
||||
0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b
|
||||
};
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
// See dwarf2diehandler.h for details.
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
|
@ -168,7 +167,7 @@ void DIEDispatcher::ProcessAttributeReference(uint64 offset,
|
|||
void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const uint8_t *data,
|
||||
const char* data,
|
||||
uint64 len) {
|
||||
HandlerStack ¤t = die_handlers_.top();
|
||||
// This had better be an attribute of the DIE we were meant to handle.
|
||||
|
|
|
@ -156,8 +156,6 @@
|
|||
#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__
|
||||
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
|
@ -208,7 +206,7 @@ class DIEHandler {
|
|||
uint64 data) { }
|
||||
virtual void ProcessAttributeBuffer(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const uint8_t *data,
|
||||
const char* data,
|
||||
uint64 len) { }
|
||||
virtual void ProcessAttributeString(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
|
@ -311,7 +309,7 @@ class DIEDispatcher: public Dwarf2Handler {
|
|||
void ProcessAttributeBuffer(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const uint8_t *data,
|
||||
const char* data,
|
||||
uint64 len);
|
||||
void ProcessAttributeString(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
|
|
|
@ -32,8 +32,6 @@
|
|||
|
||||
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
|
@ -69,7 +67,7 @@ class MockDIEHandler: public DIEHandler {
|
|||
MOCK_METHOD3(ProcessAttributeReference,
|
||||
void(DwarfAttribute, DwarfForm, uint64));
|
||||
MOCK_METHOD4(ProcessAttributeBuffer,
|
||||
void(DwarfAttribute, DwarfForm, const uint8_t *, uint64));
|
||||
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
||||
MOCK_METHOD3(ProcessAttributeString,
|
||||
void(DwarfAttribute, DwarfForm, const string &));
|
||||
MOCK_METHOD3(ProcessAttributeSignature,
|
||||
|
@ -88,7 +86,7 @@ class MockRootDIEHandler: public RootDIEHandler {
|
|||
MOCK_METHOD3(ProcessAttributeReference,
|
||||
void(DwarfAttribute, DwarfForm, uint64));
|
||||
MOCK_METHOD4(ProcessAttributeBuffer,
|
||||
void(DwarfAttribute, DwarfForm, const uint8_t *, uint64));
|
||||
void(DwarfAttribute, DwarfForm, const char *, uint64));
|
||||
MOCK_METHOD3(ProcessAttributeString,
|
||||
void(DwarfAttribute, DwarfForm, const string &));
|
||||
MOCK_METHOD3(ProcessAttributeSignature,
|
||||
|
@ -187,9 +185,8 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) {
|
|||
MockRootDIEHandler mock_root_handler;
|
||||
DIEDispatcher die_dispatcher(&mock_root_handler);
|
||||
|
||||
const uint8_t buffer[10] = {
|
||||
0x24, 0x24, 0x35, 0x9a, 0xca, 0xcf, 0xa8, 0x84, 0xa7, 0x18
|
||||
};
|
||||
const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca,
|
||||
0xcf, 0xa8, 0x84, 0xa7, 0x18 };
|
||||
string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d";
|
||||
|
||||
// Set expectations.
|
||||
|
|
|
@ -149,10 +149,7 @@ enum DwarfForm {
|
|||
DW_FORM_sec_offset = 0x17,
|
||||
DW_FORM_exprloc = 0x18,
|
||||
DW_FORM_flag_present = 0x19,
|
||||
DW_FORM_ref_sig8 = 0x20,
|
||||
// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
|
||||
DW_FORM_GNU_addr_index = 0x1f01,
|
||||
DW_FORM_GNU_str_index = 0x1f02
|
||||
DW_FORM_ref_sig8 = 0x20
|
||||
};
|
||||
|
||||
// Attribute names and codes
|
||||
|
@ -267,13 +264,6 @@ enum DwarfAttribute {
|
|||
DW_AT_body_begin = 0x2105,
|
||||
DW_AT_body_end = 0x2106,
|
||||
DW_AT_GNU_vector = 0x2107,
|
||||
// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
|
||||
DW_AT_GNU_dwo_name = 0x2130,
|
||||
DW_AT_GNU_dwo_id = 0x2131,
|
||||
DW_AT_GNU_ranges_base = 0x2132,
|
||||
DW_AT_GNU_addr_base = 0x2133,
|
||||
DW_AT_GNU_pubnames = 0x2134,
|
||||
DW_AT_GNU_pubtypes = 0x2135,
|
||||
// VMS extensions.
|
||||
DW_AT_VMS_rtnbeg_pd_address = 0x2201,
|
||||
// UPC extension.
|
||||
|
@ -501,22 +491,7 @@ enum DwarfOpcode {
|
|||
DW_OP_lo_user =0xe0,
|
||||
DW_OP_hi_user =0xff,
|
||||
// GNU extensions
|
||||
DW_OP_GNU_push_tls_address =0xe0,
|
||||
// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
|
||||
DW_OP_GNU_addr_index =0xfb,
|
||||
DW_OP_GNU_const_index =0xfc
|
||||
};
|
||||
|
||||
// Section identifiers for DWP files
|
||||
enum DwarfSectionId {
|
||||
DW_SECT_INFO = 1,
|
||||
DW_SECT_TYPES = 2,
|
||||
DW_SECT_ABBREV = 3,
|
||||
DW_SECT_LINE = 4,
|
||||
DW_SECT_LOC = 5,
|
||||
DW_SECT_STR_OFFSETS = 6,
|
||||
DW_SECT_MACINFO = 7,
|
||||
DW_SECT_MACRO = 8
|
||||
DW_OP_GNU_push_tls_address =0xe0
|
||||
};
|
||||
|
||||
// Source languages. These are values for DW_AT_language.
|
||||
|
|
|
@ -44,8 +44,6 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/line_state_machine.h"
|
||||
|
@ -53,38 +51,11 @@
|
|||
|
||||
namespace dwarf2reader {
|
||||
|
||||
CompilationUnit::CompilationUnit(const string& path,
|
||||
const SectionMap& sections, uint64 offset,
|
||||
CompilationUnit::CompilationUnit(const SectionMap& sections, uint64 offset,
|
||||
ByteReader* reader, Dwarf2Handler* handler)
|
||||
: path_(path), offset_from_section_start_(offset), reader_(reader),
|
||||
sections_(sections), handler_(handler), abbrevs_(),
|
||||
string_buffer_(NULL), string_buffer_length_(0),
|
||||
str_offsets_buffer_(NULL), str_offsets_buffer_length_(0),
|
||||
addr_buffer_(NULL), addr_buffer_length_(0),
|
||||
is_split_dwarf_(false), dwo_id_(0), dwo_name_(),
|
||||
skeleton_dwo_id_(0), ranges_base_(0), addr_base_(0),
|
||||
have_checked_for_dwp_(false), dwp_path_(),
|
||||
dwp_byte_reader_(), dwp_reader_() {}
|
||||
|
||||
// Initialize a compilation unit from a .dwo or .dwp file.
|
||||
// In this case, we need the .debug_addr section from the
|
||||
// executable file that contains the corresponding skeleton
|
||||
// compilation unit. We also inherit the Dwarf2Handler from
|
||||
// the executable file, and call it as if we were still
|
||||
// processing the original compilation unit.
|
||||
|
||||
void CompilationUnit::SetSplitDwarf(const uint8_t* addr_buffer,
|
||||
uint64 addr_buffer_length,
|
||||
uint64 addr_base,
|
||||
uint64 ranges_base,
|
||||
uint64 dwo_id) {
|
||||
is_split_dwarf_ = true;
|
||||
addr_buffer_ = addr_buffer;
|
||||
addr_buffer_length_ = addr_buffer_length;
|
||||
addr_base_ = addr_base;
|
||||
ranges_base_ = ranges_base;
|
||||
skeleton_dwo_id_ = dwo_id;
|
||||
}
|
||||
: offset_from_section_start_(offset), reader_(reader),
|
||||
sections_(sections), handler_(handler), abbrevs_(NULL),
|
||||
string_buffer_(NULL), string_buffer_length_(0) {}
|
||||
|
||||
// Read a DWARF2/3 abbreviation section.
|
||||
// Each abbrev consists of a abbreviation number, a tag, a byte
|
||||
|
@ -112,9 +83,9 @@ void CompilationUnit::ReadAbbrevs() {
|
|||
// The only way to check whether we are reading over the end of the
|
||||
// buffer would be to first compute the size of the leb128 data by
|
||||
// reading it, then go back and read it again.
|
||||
const uint8_t *abbrev_start = iter->second.first +
|
||||
const char* abbrev_start = iter->second.first +
|
||||
header_.abbrev_offset;
|
||||
const uint8_t *abbrevptr = abbrev_start;
|
||||
const char* abbrevptr = abbrev_start;
|
||||
#ifndef NDEBUG
|
||||
const uint64 abbrev_length = iter->second.second - header_.abbrev_offset;
|
||||
#endif
|
||||
|
@ -161,8 +132,8 @@ void CompilationUnit::ReadAbbrevs() {
|
|||
}
|
||||
|
||||
// Skips a single DIE's attributes.
|
||||
const uint8_t *CompilationUnit::SkipDIE(const uint8_t* start,
|
||||
const Abbrev& abbrev) {
|
||||
const char* CompilationUnit::SkipDIE(const char* start,
|
||||
const Abbrev& abbrev) {
|
||||
for (AttributeList::const_iterator i = abbrev.attributes.begin();
|
||||
i != abbrev.attributes.end();
|
||||
i++) {
|
||||
|
@ -172,8 +143,8 @@ const uint8_t *CompilationUnit::SkipDIE(const uint8_t* start,
|
|||
}
|
||||
|
||||
// Skips a single attribute form's data.
|
||||
const uint8_t *CompilationUnit::SkipAttribute(const uint8_t *start,
|
||||
enum DwarfForm form) {
|
||||
const char* CompilationUnit::SkipAttribute(const char* start,
|
||||
enum DwarfForm form) {
|
||||
size_t len;
|
||||
|
||||
switch (form) {
|
||||
|
@ -200,11 +171,9 @@ const uint8_t *CompilationUnit::SkipAttribute(const uint8_t *start,
|
|||
case DW_FORM_ref_sig8:
|
||||
return start + 8;
|
||||
case DW_FORM_string:
|
||||
return start + strlen(reinterpret_cast<const char *>(start)) + 1;
|
||||
return start + strlen(start) + 1;
|
||||
case DW_FORM_udata:
|
||||
case DW_FORM_ref_udata:
|
||||
case DW_FORM_GNU_str_index:
|
||||
case DW_FORM_GNU_addr_index:
|
||||
reader_->ReadUnsignedLEB128(start, &len);
|
||||
return start + len;
|
||||
|
||||
|
@ -249,7 +218,7 @@ const uint8_t *CompilationUnit::SkipAttribute(const uint8_t *start,
|
|||
// the offset in the .debug_abbrev section for our abbrevs, and an
|
||||
// address size.
|
||||
void CompilationUnit::ReadHeader() {
|
||||
const uint8_t *headerptr = buffer_;
|
||||
const char* headerptr = buffer_;
|
||||
size_t initial_length_size;
|
||||
|
||||
assert(headerptr + 4 < buffer_ + buffer_length_);
|
||||
|
@ -266,9 +235,7 @@ void CompilationUnit::ReadHeader() {
|
|||
header_.abbrev_offset = reader_->ReadOffset(headerptr);
|
||||
headerptr += reader_->OffsetSize();
|
||||
|
||||
// Compare against less than or equal because this may be the last
|
||||
// section in the file.
|
||||
assert(headerptr + 1 <= buffer_ + buffer_length_);
|
||||
assert(headerptr + 1 < buffer_ + buffer_length_);
|
||||
header_.address_size = reader_->ReadOneByte(headerptr);
|
||||
reader_->SetAddressSize(header_.address_size);
|
||||
headerptr += 1;
|
||||
|
@ -329,39 +296,17 @@ uint64 CompilationUnit::Start() {
|
|||
string_buffer_length_ = iter->second.second;
|
||||
}
|
||||
|
||||
// Set the string offsets section if we have one.
|
||||
iter = sections_.find(".debug_str_offsets");
|
||||
if (iter != sections_.end()) {
|
||||
str_offsets_buffer_ = iter->second.first;
|
||||
str_offsets_buffer_length_ = iter->second.second;
|
||||
}
|
||||
|
||||
// Set the address section if we have one.
|
||||
iter = sections_.find(".debug_addr");
|
||||
if (iter != sections_.end()) {
|
||||
addr_buffer_ = iter->second.first;
|
||||
addr_buffer_length_ = iter->second.second;
|
||||
}
|
||||
|
||||
// Now that we have our abbreviations, start processing DIE's.
|
||||
ProcessDIEs();
|
||||
|
||||
// If this is a skeleton compilation unit generated with split DWARF,
|
||||
// and the client needs the full debug info, we need to find the full
|
||||
// compilation unit in a .dwo or .dwp file.
|
||||
if (!is_split_dwarf_
|
||||
&& dwo_name_ != NULL
|
||||
&& handler_->NeedSplitDebugInfo())
|
||||
ProcessSplitDwarf();
|
||||
|
||||
return ourlength;
|
||||
}
|
||||
|
||||
// If one really wanted, you could merge SkipAttribute and
|
||||
// ProcessAttribute
|
||||
// This is all boring data manipulation and calling of the handler.
|
||||
const uint8_t *CompilationUnit::ProcessAttribute(
|
||||
uint64 dieoffset, const uint8_t *start, enum DwarfAttribute attr,
|
||||
const char* CompilationUnit::ProcessAttribute(
|
||||
uint64 dieoffset, const char* start, enum DwarfAttribute attr,
|
||||
enum DwarfForm form) {
|
||||
size_t len;
|
||||
|
||||
|
@ -375,46 +320,48 @@ const uint8_t *CompilationUnit::ProcessAttribute(
|
|||
return ProcessAttribute(dieoffset, start, attr, form);
|
||||
|
||||
case DW_FORM_flag_present:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form, 1);
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1);
|
||||
return start;
|
||||
case DW_FORM_data1:
|
||||
case DW_FORM_flag:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadOneByte(start));
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadOneByte(start));
|
||||
return start + 1;
|
||||
case DW_FORM_data2:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadTwoBytes(start));
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadTwoBytes(start));
|
||||
return start + 2;
|
||||
case DW_FORM_data4:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadFourBytes(start));
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadFourBytes(start));
|
||||
return start + 4;
|
||||
case DW_FORM_data8:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadEightBytes(start));
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadEightBytes(start));
|
||||
return start + 8;
|
||||
case DW_FORM_string: {
|
||||
const char *str = reinterpret_cast<const char *>(start);
|
||||
ProcessAttributeString(dieoffset, attr, form, str);
|
||||
const char* str = start;
|
||||
handler_->ProcessAttributeString(dieoffset, attr, form,
|
||||
str);
|
||||
return start + strlen(str) + 1;
|
||||
}
|
||||
case DW_FORM_udata:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadUnsignedLEB128(start, &len));
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadUnsignedLEB128(start,
|
||||
&len));
|
||||
return start + len;
|
||||
|
||||
case DW_FORM_sdata:
|
||||
ProcessAttributeSigned(dieoffset, attr, form,
|
||||
reader_->ReadSignedLEB128(start, &len));
|
||||
handler_->ProcessAttributeSigned(dieoffset, attr, form,
|
||||
reader_->ReadSignedLEB128(start, &len));
|
||||
return start + len;
|
||||
case DW_FORM_addr:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadAddress(start));
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadAddress(start));
|
||||
return start + reader_->AddressSize();
|
||||
case DW_FORM_sec_offset:
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadOffset(start));
|
||||
handler_->ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadOffset(start));
|
||||
return start + reader_->OffsetSize();
|
||||
|
||||
case DW_FORM_ref1:
|
||||
|
@ -493,66 +440,34 @@ const uint8_t *CompilationUnit::ProcessAttribute(
|
|||
const uint64 offset = reader_->ReadOffset(start);
|
||||
assert(string_buffer_ + offset < string_buffer_ + string_buffer_length_);
|
||||
|
||||
const char *str = reinterpret_cast<const char *>(string_buffer_ + offset);
|
||||
ProcessAttributeString(dieoffset, attr, form, str);
|
||||
const char* str = string_buffer_ + offset;
|
||||
handler_->ProcessAttributeString(dieoffset, attr, form,
|
||||
str);
|
||||
return start + reader_->OffsetSize();
|
||||
}
|
||||
|
||||
case DW_FORM_GNU_str_index: {
|
||||
uint64 str_index = reader_->ReadUnsignedLEB128(start, &len);
|
||||
const uint8_t* offset_ptr =
|
||||
str_offsets_buffer_ + str_index * reader_->OffsetSize();
|
||||
const uint64 offset = reader_->ReadOffset(offset_ptr);
|
||||
if (offset >= string_buffer_length_) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char* str = reinterpret_cast<const char *>(string_buffer_) + offset;
|
||||
ProcessAttributeString(dieoffset, attr, form, str);
|
||||
return start + len;
|
||||
break;
|
||||
}
|
||||
case DW_FORM_GNU_addr_index: {
|
||||
uint64 addr_index = reader_->ReadUnsignedLEB128(start, &len);
|
||||
const uint8_t* addr_ptr =
|
||||
addr_buffer_ + addr_base_ + addr_index * reader_->AddressSize();
|
||||
ProcessAttributeUnsigned(dieoffset, attr, form,
|
||||
reader_->ReadAddress(addr_ptr));
|
||||
return start + len;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Unhandled form type\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const uint8_t *CompilationUnit::ProcessDIE(uint64 dieoffset,
|
||||
const uint8_t *start,
|
||||
const Abbrev& abbrev) {
|
||||
const char* CompilationUnit::ProcessDIE(uint64 dieoffset,
|
||||
const char* start,
|
||||
const Abbrev& abbrev) {
|
||||
for (AttributeList::const_iterator i = abbrev.attributes.begin();
|
||||
i != abbrev.attributes.end();
|
||||
i++) {
|
||||
start = ProcessAttribute(dieoffset, start, i->first, i->second);
|
||||
}
|
||||
|
||||
// If this is a compilation unit in a split DWARF object, verify that
|
||||
// the dwo_id matches. If it does not match, we will ignore this
|
||||
// compilation unit.
|
||||
if (abbrev.tag == DW_TAG_compile_unit
|
||||
&& is_split_dwarf_
|
||||
&& dwo_id_ != skeleton_dwo_id_) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
void CompilationUnit::ProcessDIEs() {
|
||||
const uint8_t *dieptr = after_header_;
|
||||
const char* dieptr = after_header_;
|
||||
size_t len;
|
||||
|
||||
// lengthstart is the place the length field is based on.
|
||||
// It is the point in the header after the initial length field
|
||||
const uint8_t *lengthstart = buffer_;
|
||||
const char* lengthstart = buffer_;
|
||||
|
||||
// In 64 bit dwarf, the initial length is 12 bytes, because of the
|
||||
// 0xffffffff at the start.
|
||||
|
@ -600,313 +515,10 @@ void CompilationUnit::ProcessDIEs() {
|
|||
}
|
||||
}
|
||||
|
||||
// Check for a valid ELF file and return the Address size.
|
||||
// Returns 0 if not a valid ELF file.
|
||||
inline int GetElfWidth(const ElfReader& elf) {
|
||||
if (elf.IsElf32File())
|
||||
return 4;
|
||||
if (elf.IsElf64File())
|
||||
return 8;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CompilationUnit::ProcessSplitDwarf() {
|
||||
struct stat statbuf;
|
||||
if (!have_checked_for_dwp_) {
|
||||
// Look for a .dwp file in the same directory as the executable.
|
||||
have_checked_for_dwp_ = true;
|
||||
string dwp_suffix(".dwp");
|
||||
dwp_path_ = path_ + dwp_suffix;
|
||||
if (stat(dwp_path_.c_str(), &statbuf) != 0) {
|
||||
// Fall back to a split .debug file in the same directory.
|
||||
string debug_suffix(".debug");
|
||||
dwp_path_ = path_;
|
||||
size_t found = path_.rfind(debug_suffix);
|
||||
if (found + debug_suffix.length() == path_.length())
|
||||
dwp_path_ = dwp_path_.replace(found, debug_suffix.length(), dwp_suffix);
|
||||
}
|
||||
if (stat(dwp_path_.c_str(), &statbuf) == 0) {
|
||||
ElfReader* elf = new ElfReader(dwp_path_);
|
||||
int width = GetElfWidth(*elf);
|
||||
if (width != 0) {
|
||||
dwp_byte_reader_.reset(new ByteReader(reader_->GetEndianness()));
|
||||
dwp_byte_reader_->SetAddressSize(width);
|
||||
dwp_reader_.reset(new DwpReader(*dwp_byte_reader_, elf));
|
||||
dwp_reader_->Initialize();
|
||||
} else {
|
||||
delete elf;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool found_in_dwp = false;
|
||||
if (dwp_reader_) {
|
||||
// If we have a .dwp file, read the debug sections for the requested CU.
|
||||
SectionMap sections;
|
||||
dwp_reader_->ReadDebugSectionsForCU(dwo_id_, §ions);
|
||||
if (!sections.empty()) {
|
||||
found_in_dwp = true;
|
||||
CompilationUnit dwp_comp_unit(dwp_path_, sections, 0,
|
||||
dwp_byte_reader_.get(), handler_);
|
||||
dwp_comp_unit.SetSplitDwarf(addr_buffer_, addr_buffer_length_, addr_base_,
|
||||
ranges_base_, dwo_id_);
|
||||
dwp_comp_unit.Start();
|
||||
}
|
||||
}
|
||||
if (!found_in_dwp) {
|
||||
// If no .dwp file, try to open the .dwo file.
|
||||
if (stat(dwo_name_, &statbuf) == 0) {
|
||||
ElfReader elf(dwo_name_);
|
||||
int width = GetElfWidth(elf);
|
||||
if (width != 0) {
|
||||
ByteReader reader(ENDIANNESS_LITTLE);
|
||||
reader.SetAddressSize(width);
|
||||
SectionMap sections;
|
||||
ReadDebugSectionsFromDwo(&elf, §ions);
|
||||
CompilationUnit dwo_comp_unit(dwo_name_, sections, 0, &reader,
|
||||
handler_);
|
||||
dwo_comp_unit.SetSplitDwarf(addr_buffer_, addr_buffer_length_,
|
||||
addr_base_, ranges_base_, dwo_id_);
|
||||
dwo_comp_unit.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompilationUnit::ReadDebugSectionsFromDwo(ElfReader* elf_reader,
|
||||
SectionMap* sections) {
|
||||
static const char* const section_names[] = {
|
||||
".debug_abbrev",
|
||||
".debug_info",
|
||||
".debug_str_offsets",
|
||||
".debug_str"
|
||||
};
|
||||
for (unsigned int i = 0u;
|
||||
i < sizeof(section_names)/sizeof(*(section_names)); ++i) {
|
||||
string base_name = section_names[i];
|
||||
string dwo_name = base_name + ".dwo";
|
||||
size_t section_size;
|
||||
const char* section_data = elf_reader->GetSectionByName(dwo_name,
|
||||
§ion_size);
|
||||
if (section_data != NULL)
|
||||
sections->insert(std::make_pair(
|
||||
base_name, std::make_pair(
|
||||
reinterpret_cast<const uint8_t *>(section_data),
|
||||
section_size)));
|
||||
}
|
||||
}
|
||||
|
||||
DwpReader::DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader)
|
||||
: elf_reader_(elf_reader), byte_reader_(byte_reader),
|
||||
cu_index_(NULL), cu_index_size_(0), string_buffer_(NULL),
|
||||
string_buffer_size_(0), version_(0), ncolumns_(0), nunits_(0),
|
||||
nslots_(0), phash_(NULL), pindex_(NULL), shndx_pool_(NULL),
|
||||
offset_table_(NULL), size_table_(NULL), abbrev_data_(NULL),
|
||||
abbrev_size_(0), info_data_(NULL), info_size_(0),
|
||||
str_offsets_data_(NULL), str_offsets_size_(0) {}
|
||||
|
||||
DwpReader::~DwpReader() {
|
||||
if (elf_reader_) delete elf_reader_;
|
||||
}
|
||||
|
||||
void DwpReader::Initialize() {
|
||||
cu_index_ = elf_reader_->GetSectionByName(".debug_cu_index",
|
||||
&cu_index_size_);
|
||||
if (cu_index_ == NULL) {
|
||||
return;
|
||||
}
|
||||
// The .debug_str.dwo section is shared by all CUs in the file.
|
||||
string_buffer_ = elf_reader_->GetSectionByName(".debug_str.dwo",
|
||||
&string_buffer_size_);
|
||||
|
||||
version_ = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(cu_index_));
|
||||
|
||||
if (version_ == 1) {
|
||||
nslots_ = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(cu_index_)
|
||||
+ 3 * sizeof(uint32));
|
||||
phash_ = cu_index_ + 4 * sizeof(uint32);
|
||||
pindex_ = phash_ + nslots_ * sizeof(uint64);
|
||||
shndx_pool_ = pindex_ + nslots_ * sizeof(uint32);
|
||||
if (shndx_pool_ >= cu_index_ + cu_index_size_) {
|
||||
version_ = 0;
|
||||
}
|
||||
} else if (version_ == 2) {
|
||||
ncolumns_ = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(cu_index_) + sizeof(uint32));
|
||||
nunits_ = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(cu_index_) + 2 * sizeof(uint32));
|
||||
nslots_ = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(cu_index_) + 3 * sizeof(uint32));
|
||||
phash_ = cu_index_ + 4 * sizeof(uint32);
|
||||
pindex_ = phash_ + nslots_ * sizeof(uint64);
|
||||
offset_table_ = pindex_ + nslots_ * sizeof(uint32);
|
||||
size_table_ = offset_table_ + ncolumns_ * (nunits_ + 1) * sizeof(uint32);
|
||||
abbrev_data_ = elf_reader_->GetSectionByName(".debug_abbrev.dwo",
|
||||
&abbrev_size_);
|
||||
info_data_ = elf_reader_->GetSectionByName(".debug_info.dwo", &info_size_);
|
||||
str_offsets_data_ = elf_reader_->GetSectionByName(".debug_str_offsets.dwo",
|
||||
&str_offsets_size_);
|
||||
if (size_table_ >= cu_index_ + cu_index_size_) {
|
||||
version_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DwpReader::ReadDebugSectionsForCU(uint64 dwo_id,
|
||||
SectionMap* sections) {
|
||||
if (version_ == 1) {
|
||||
int slot = LookupCU(dwo_id);
|
||||
if (slot == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The index table points to the section index pool, where we
|
||||
// can read a list of section indexes for the debug sections
|
||||
// for the CU whose dwo_id we are looking for.
|
||||
int index = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(pindex_)
|
||||
+ slot * sizeof(uint32));
|
||||
const char* shndx_list = shndx_pool_ + index * sizeof(uint32);
|
||||
for (;;) {
|
||||
if (shndx_list >= cu_index_ + cu_index_size_) {
|
||||
version_ = 0;
|
||||
return;
|
||||
}
|
||||
unsigned int shndx = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(shndx_list));
|
||||
shndx_list += sizeof(uint32);
|
||||
if (shndx == 0)
|
||||
break;
|
||||
const char* section_name = elf_reader_->GetSectionName(shndx);
|
||||
size_t section_size;
|
||||
const char* section_data;
|
||||
// We're only interested in these four debug sections.
|
||||
// The section names in the .dwo file end with ".dwo", but we
|
||||
// add them to the sections table with their normal names.
|
||||
if (!strncmp(section_name, ".debug_abbrev", strlen(".debug_abbrev"))) {
|
||||
section_data = elf_reader_->GetSectionByIndex(shndx, §ion_size);
|
||||
sections->insert(std::make_pair(
|
||||
".debug_abbrev",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (section_data),
|
||||
section_size)));
|
||||
} else if (!strncmp(section_name, ".debug_info", strlen(".debug_info"))) {
|
||||
section_data = elf_reader_->GetSectionByIndex(shndx, §ion_size);
|
||||
sections->insert(std::make_pair(
|
||||
".debug_info",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (section_data),
|
||||
section_size)));
|
||||
} else if (!strncmp(section_name, ".debug_str_offsets",
|
||||
strlen(".debug_str_offsets"))) {
|
||||
section_data = elf_reader_->GetSectionByIndex(shndx, §ion_size);
|
||||
sections->insert(std::make_pair(
|
||||
".debug_str_offsets",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (section_data),
|
||||
section_size)));
|
||||
}
|
||||
}
|
||||
sections->insert(std::make_pair(
|
||||
".debug_str",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (string_buffer_),
|
||||
string_buffer_size_)));
|
||||
} else if (version_ == 2) {
|
||||
uint32 index = LookupCUv2(dwo_id);
|
||||
if (index == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The index points to a row in each of the section offsets table
|
||||
// and the section size table, where we can read the offsets and sizes
|
||||
// of the contributions to each debug section from the CU whose dwo_id
|
||||
// we are looking for. Row 0 of the section offsets table has the
|
||||
// section ids for each column of the table. The size table begins
|
||||
// with row 1.
|
||||
const char* id_row = offset_table_;
|
||||
const char* offset_row = offset_table_
|
||||
+ index * ncolumns_ * sizeof(uint32);
|
||||
const char* size_row =
|
||||
size_table_ + (index - 1) * ncolumns_ * sizeof(uint32);
|
||||
if (size_row + ncolumns_ * sizeof(uint32) > cu_index_ + cu_index_size_) {
|
||||
version_ = 0;
|
||||
return;
|
||||
}
|
||||
for (unsigned int col = 0u; col < ncolumns_; ++col) {
|
||||
uint32 section_id =
|
||||
byte_reader_.ReadFourBytes(reinterpret_cast<const uint8_t *>(id_row)
|
||||
+ col * sizeof(uint32));
|
||||
uint32 offset = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(offset_row)
|
||||
+ col * sizeof(uint32));
|
||||
uint32 size = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(size_row) + col * sizeof(uint32));
|
||||
if (section_id == DW_SECT_ABBREV) {
|
||||
sections->insert(std::make_pair(
|
||||
".debug_abbrev",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (abbrev_data_)
|
||||
+ offset, size)));
|
||||
} else if (section_id == DW_SECT_INFO) {
|
||||
sections->insert(std::make_pair(
|
||||
".debug_info",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (info_data_)
|
||||
+ offset, size)));
|
||||
} else if (section_id == DW_SECT_STR_OFFSETS) {
|
||||
sections->insert(std::make_pair(
|
||||
".debug_str_offsets",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (str_offsets_data_)
|
||||
+ offset, size)));
|
||||
}
|
||||
}
|
||||
sections->insert(std::make_pair(
|
||||
".debug_str",
|
||||
std::make_pair(reinterpret_cast<const uint8_t *> (string_buffer_),
|
||||
string_buffer_size_)));
|
||||
}
|
||||
}
|
||||
|
||||
int DwpReader::LookupCU(uint64 dwo_id) {
|
||||
uint32 slot = static_cast<uint32>(dwo_id) & (nslots_ - 1);
|
||||
uint64 probe = byte_reader_.ReadEightBytes(
|
||||
reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
|
||||
if (probe != 0 && probe != dwo_id) {
|
||||
uint32 secondary_hash =
|
||||
(static_cast<uint32>(dwo_id >> 32) & (nslots_ - 1)) | 1;
|
||||
do {
|
||||
slot = (slot + secondary_hash) & (nslots_ - 1);
|
||||
probe = byte_reader_.ReadEightBytes(
|
||||
reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
|
||||
} while (probe != 0 && probe != dwo_id);
|
||||
}
|
||||
if (probe == 0)
|
||||
return -1;
|
||||
return slot;
|
||||
}
|
||||
|
||||
uint32 DwpReader::LookupCUv2(uint64 dwo_id) {
|
||||
uint32 slot = static_cast<uint32>(dwo_id) & (nslots_ - 1);
|
||||
uint64 probe = byte_reader_.ReadEightBytes(
|
||||
reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
|
||||
uint32 index = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(pindex_) + slot * sizeof(uint32));
|
||||
if (index != 0 && probe != dwo_id) {
|
||||
uint32 secondary_hash =
|
||||
(static_cast<uint32>(dwo_id >> 32) & (nslots_ - 1)) | 1;
|
||||
do {
|
||||
slot = (slot + secondary_hash) & (nslots_ - 1);
|
||||
probe = byte_reader_.ReadEightBytes(
|
||||
reinterpret_cast<const uint8_t *>(phash_) + slot * sizeof(uint64));
|
||||
index = byte_reader_.ReadFourBytes(
|
||||
reinterpret_cast<const uint8_t *>(pindex_) + slot * sizeof(uint32));
|
||||
} while (index != 0 && probe != dwo_id);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
LineInfo::LineInfo(const uint8_t *buffer, uint64 buffer_length,
|
||||
LineInfo::LineInfo(const char* buffer, uint64 buffer_length,
|
||||
ByteReader* reader, LineInfoHandler* handler):
|
||||
handler_(handler), reader_(reader), buffer_(buffer) {
|
||||
#ifndef NDEBUG
|
||||
buffer_length_ = buffer_length;
|
||||
#endif
|
||||
handler_(handler), reader_(reader), buffer_(buffer),
|
||||
buffer_length_(buffer_length) {
|
||||
header_.std_opcode_lengths = NULL;
|
||||
}
|
||||
|
||||
|
@ -919,7 +531,7 @@ uint64 LineInfo::Start() {
|
|||
// The header for a debug_line section is mildly complicated, because
|
||||
// the line info is very tightly encoded.
|
||||
void LineInfo::ReadHeader() {
|
||||
const uint8_t *lineptr = buffer_;
|
||||
const char* lineptr = buffer_;
|
||||
size_t initial_length_size;
|
||||
|
||||
const uint64 initial_length
|
||||
|
@ -966,7 +578,7 @@ void LineInfo::ReadHeader() {
|
|||
if (*lineptr) {
|
||||
uint32 dirindex = 1;
|
||||
while (*lineptr) {
|
||||
const char *dirname = reinterpret_cast<const char *>(lineptr);
|
||||
const char* dirname = lineptr;
|
||||
handler_->DefineDir(dirname, dirindex);
|
||||
lineptr += strlen(dirname) + 1;
|
||||
dirindex++;
|
||||
|
@ -979,7 +591,7 @@ void LineInfo::ReadHeader() {
|
|||
uint32 fileindex = 1;
|
||||
size_t len;
|
||||
while (*lineptr) {
|
||||
const char *filename = reinterpret_cast<const char *>(lineptr);
|
||||
const char* filename = lineptr;
|
||||
lineptr += strlen(filename) + 1;
|
||||
|
||||
uint64 dirindex = reader_->ReadUnsignedLEB128(lineptr, &len);
|
||||
|
@ -1004,7 +616,7 @@ void LineInfo::ReadHeader() {
|
|||
bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
||||
LineInfoHandler* handler,
|
||||
const struct LineInfoHeader &header,
|
||||
const uint8_t *start,
|
||||
const char* start,
|
||||
struct LineStateMachine* lsm,
|
||||
size_t* len,
|
||||
uintptr pc,
|
||||
|
@ -1145,7 +757,7 @@ bool LineInfo::ProcessOneOpcode(ByteReader* reader,
|
|||
}
|
||||
break;
|
||||
case DW_LNE_define_file: {
|
||||
const char *filename = reinterpret_cast<const char *>(start);
|
||||
const char* filename = start;
|
||||
|
||||
templen = strlen(filename) + 1;
|
||||
start += templen;
|
||||
|
@ -1192,7 +804,7 @@ void LineInfo::ReadLines() {
|
|||
|
||||
// lengthstart is the place the length field is based on.
|
||||
// It is the point in the header after the initial length field
|
||||
const uint8_t *lengthstart = buffer_;
|
||||
const char* lengthstart = buffer_;
|
||||
|
||||
// In 64 bit dwarf, the initial length is 12 bytes, because of the
|
||||
// 0xffffffff at the start.
|
||||
|
@ -1201,7 +813,7 @@ void LineInfo::ReadLines() {
|
|||
else
|
||||
lengthstart += 4;
|
||||
|
||||
const uint8_t *lineptr = after_header_;
|
||||
const char* lineptr = after_header_;
|
||||
lsm.Reset(header_.default_is_stmt);
|
||||
|
||||
// The LineInfoHandler interface expects each line's length along
|
||||
|
@ -1700,7 +1312,7 @@ class CallFrameInfo::State {
|
|||
const Entry *entry_;
|
||||
|
||||
// The next instruction to process.
|
||||
const uint8_t *cursor_;
|
||||
const char *cursor_;
|
||||
|
||||
// The current set of rules.
|
||||
RuleMap rules_;
|
||||
|
@ -1798,8 +1410,7 @@ bool CallFrameInfo::State::ParseOperands(const char *format,
|
|||
if (len > bytes_left || expression_length > bytes_left - len)
|
||||
return ReportIncomplete();
|
||||
cursor_ += len;
|
||||
operands->expression = string(reinterpret_cast<const char *>(cursor_),
|
||||
expression_length);
|
||||
operands->expression = string(cursor_, expression_length);
|
||||
cursor_ += expression_length;
|
||||
break;
|
||||
}
|
||||
|
@ -2152,8 +1763,8 @@ bool CallFrameInfo::State::DoRestore(unsigned reg) {
|
|||
return DoRule(reg, rule);
|
||||
}
|
||||
|
||||
bool CallFrameInfo::ReadEntryPrologue(const uint8_t *cursor, Entry *entry) {
|
||||
const uint8_t *buffer_end = buffer_ + buffer_length_;
|
||||
bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) {
|
||||
const char *buffer_end = buffer_ + buffer_length_;
|
||||
|
||||
// Initialize enough of ENTRY for use in error reporting.
|
||||
entry->offset = cursor - buffer_;
|
||||
|
@ -2231,7 +1842,7 @@ bool CallFrameInfo::ReadEntryPrologue(const uint8_t *cursor, Entry *entry) {
|
|||
}
|
||||
|
||||
bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
||||
const uint8_t *cursor = cie->fields;
|
||||
const char *cursor = cie->fields;
|
||||
size_t len;
|
||||
|
||||
assert(cie->kind == kCIE);
|
||||
|
@ -2262,14 +1873,13 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||
return false;
|
||||
}
|
||||
|
||||
const uint8_t *augmentation_start = cursor;
|
||||
const uint8_t *augmentation_end =
|
||||
reinterpret_cast<const uint8_t *>(memchr(augmentation_start, '\0',
|
||||
cie->end - augmentation_start));
|
||||
const char *augmentation_start = cursor;
|
||||
const void *augmentation_end =
|
||||
memchr(augmentation_start, '\0', cie->end - augmentation_start);
|
||||
if (! augmentation_end) return ReportIncomplete(cie);
|
||||
cursor = augmentation_end;
|
||||
cie->augmentation = string(reinterpret_cast<const char *>(augmentation_start),
|
||||
cursor - augmentation_start);
|
||||
cursor = static_cast<const char *>(augmentation_end);
|
||||
cie->augmentation = string(augmentation_start,
|
||||
cursor - augmentation_start);
|
||||
// Skip the terminating '\0'.
|
||||
cursor++;
|
||||
|
||||
|
@ -2315,9 +1925,9 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||
if (size_t(cie->end - cursor) < len + data_size)
|
||||
return ReportIncomplete(cie);
|
||||
cursor += len;
|
||||
const uint8_t *data = cursor;
|
||||
const char *data = cursor;
|
||||
cursor += data_size;
|
||||
const uint8_t *data_end = cursor;
|
||||
const char *data_end = cursor;
|
||||
|
||||
cie->has_z_lsda = false;
|
||||
cie->has_z_personality = false;
|
||||
|
@ -2409,7 +2019,7 @@ bool CallFrameInfo::ReadCIEFields(CIE *cie) {
|
|||
}
|
||||
|
||||
bool CallFrameInfo::ReadFDEFields(FDE *fde) {
|
||||
const uint8_t *cursor = fde->fields;
|
||||
const char *cursor = fde->fields;
|
||||
size_t size;
|
||||
|
||||
fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding,
|
||||
|
@ -2475,10 +2085,10 @@ bool CallFrameInfo::ReadFDEFields(FDE *fde) {
|
|||
}
|
||||
|
||||
bool CallFrameInfo::Start() {
|
||||
const uint8_t *buffer_end = buffer_ + buffer_length_;
|
||||
const uint8_t *cursor;
|
||||
const char *buffer_end = buffer_ + buffer_length_;
|
||||
const char *cursor;
|
||||
bool all_ok = true;
|
||||
const uint8_t *entry_end;
|
||||
const char *entry_end;
|
||||
bool ok;
|
||||
|
||||
// Traverse all the entries in buffer_, skipping CIEs and offering
|
||||
|
|
|
@ -40,30 +40,25 @@
|
|||
#ifndef COMMON_DWARF_DWARF2READER_H__
|
||||
#define COMMON_DWARF_DWARF2READER_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
#include "common/dwarf/types.h"
|
||||
#include "common/using_std_string.h"
|
||||
#include "common/dwarf/elf_reader.h"
|
||||
|
||||
namespace dwarf2reader {
|
||||
struct LineStateMachine;
|
||||
class Dwarf2Handler;
|
||||
class LineInfoHandler;
|
||||
class DwpReader;
|
||||
|
||||
// This maps from a string naming a section to a pair containing a
|
||||
// the data for the section, and the size of the section.
|
||||
typedef std::map<string, std::pair<const uint8_t *, uint64> > SectionMap;
|
||||
typedef std::map<string, std::pair<const char*, uint64> > SectionMap;
|
||||
typedef std::list<std::pair<enum DwarfAttribute, enum DwarfForm> >
|
||||
AttributeList;
|
||||
typedef AttributeList::iterator AttributeIterator;
|
||||
|
@ -90,7 +85,7 @@ class LineInfo {
|
|||
// to the beginning and length of the line information to read.
|
||||
// Reader is a ByteReader class that has the endianness set
|
||||
// properly.
|
||||
LineInfo(const uint8_t *buffer_, uint64 buffer_length,
|
||||
LineInfo(const char* buffer_, uint64 buffer_length,
|
||||
ByteReader* reader, LineInfoHandler* handler);
|
||||
|
||||
virtual ~LineInfo() {
|
||||
|
@ -116,7 +111,7 @@ class LineInfo {
|
|||
static bool ProcessOneOpcode(ByteReader* reader,
|
||||
LineInfoHandler* handler,
|
||||
const struct LineInfoHeader &header,
|
||||
const uint8_t *start,
|
||||
const char* start,
|
||||
struct LineStateMachine* lsm,
|
||||
size_t* len,
|
||||
uintptr pc,
|
||||
|
@ -144,11 +139,9 @@ class LineInfo {
|
|||
// buffer is the buffer for our line info, starting at exactly where
|
||||
// the line info to read is. after_header is the place right after
|
||||
// the end of the line information header.
|
||||
const uint8_t *buffer_;
|
||||
#ifndef NDEBUG
|
||||
const char* buffer_;
|
||||
uint64 buffer_length_;
|
||||
#endif
|
||||
const uint8_t *after_header_;
|
||||
const char* after_header_;
|
||||
};
|
||||
|
||||
// This class is the main interface between the line info reader and
|
||||
|
@ -187,106 +180,6 @@ class LineInfoHandler {
|
|||
uint32 file_num, uint32 line_num, uint32 column_num) { }
|
||||
};
|
||||
|
||||
// This class is the main interface between the reader and the
|
||||
// client. The virtual functions inside this get called for
|
||||
// interesting events that happen during DWARF2 reading.
|
||||
// The default implementation skips everything.
|
||||
class Dwarf2Handler {
|
||||
public:
|
||||
Dwarf2Handler() { }
|
||||
|
||||
virtual ~Dwarf2Handler() { }
|
||||
|
||||
// Start to process a compilation unit at OFFSET from the beginning of the
|
||||
// .debug_info section. Return false if you would like to skip this
|
||||
// compilation unit.
|
||||
virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
|
||||
uint8 offset_size, uint64 cu_length,
|
||||
uint8 dwarf_version) { return false; }
|
||||
|
||||
// When processing a skeleton compilation unit, resulting from a split
|
||||
// DWARF compilation, once the skeleton debug info has been read,
|
||||
// the reader will call this function to ask the client if it needs
|
||||
// the full debug info from the .dwo or .dwp file. Return true if
|
||||
// you need it, or false to skip processing the split debug info.
|
||||
virtual bool NeedSplitDebugInfo() { return true; }
|
||||
|
||||
// Start to process a split compilation unit at OFFSET from the beginning of
|
||||
// the debug_info section in the .dwp/.dwo file. Return false if you would
|
||||
// like to skip this compilation unit.
|
||||
virtual bool StartSplitCompilationUnit(uint64 offset,
|
||||
uint64 cu_length) { return false; }
|
||||
|
||||
// Start to process a DIE at OFFSET from the beginning of the .debug_info
|
||||
// section. Return false if you would like to skip this DIE.
|
||||
virtual bool StartDIE(uint64 offset, enum DwarfTag tag) { return false; }
|
||||
|
||||
// Called when we have an attribute with unsigned data to give to our
|
||||
// handler. The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
||||
// DATA.
|
||||
virtual void ProcessAttributeUnsigned(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) { }
|
||||
|
||||
// Called when we have an attribute with signed data to give to our handler.
|
||||
// The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
||||
// DATA.
|
||||
virtual void ProcessAttributeSigned(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data) { }
|
||||
|
||||
// Called when we have an attribute whose value is a reference to
|
||||
// another DIE. The attribute belongs to the DIE at OFFSET from the
|
||||
// beginning of the .debug_info section. Its name is ATTR, its form
|
||||
// is FORM, and the offset of the DIE being referred to from the
|
||||
// beginning of the .debug_info section is DATA.
|
||||
virtual void ProcessAttributeReference(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) { }
|
||||
|
||||
// Called when we have an attribute with a buffer of data to give to our
|
||||
// handler. The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, DATA points to
|
||||
// the buffer's contents, and its length in bytes is LENGTH. The buffer is
|
||||
// owned by the caller, not the callee, and may not persist for very long.
|
||||
// If you want the data to be available later, it needs to be copied.
|
||||
virtual void ProcessAttributeBuffer(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const uint8_t *data,
|
||||
uint64 len) { }
|
||||
|
||||
// Called when we have an attribute with string data to give to our handler.
|
||||
// The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
||||
// DATA.
|
||||
virtual void ProcessAttributeString(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string& data) { }
|
||||
|
||||
// Called when we have an attribute whose value is the 64-bit signature
|
||||
// of a type unit in the .debug_types section. OFFSET is the offset of
|
||||
// the DIE whose attribute we're reporting. ATTR and FORM are the
|
||||
// attribute's name and form. SIGNATURE is the type unit's signature.
|
||||
virtual void ProcessAttributeSignature(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 signature) { }
|
||||
|
||||
// Called when finished processing the DIE at OFFSET.
|
||||
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
|
||||
// before ends of the previous DIE, as we process children before
|
||||
// ending the parent.
|
||||
virtual void EndDIE(uint64 offset) { }
|
||||
|
||||
};
|
||||
|
||||
// The base of DWARF2/3 debug info is a DIE (Debugging Information
|
||||
// Entry.
|
||||
// DWARF groups DIE's into a tree and calls the root of this tree a
|
||||
|
@ -328,21 +221,12 @@ class CompilationUnit {
|
|||
// Initialize a compilation unit. This requires a map of sections,
|
||||
// the offset of this compilation unit in the .debug_info section, a
|
||||
// ByteReader, and a Dwarf2Handler class to call callbacks in.
|
||||
CompilationUnit(const string& path, const SectionMap& sections, uint64 offset,
|
||||
CompilationUnit(const SectionMap& sections, uint64 offset,
|
||||
ByteReader* reader, Dwarf2Handler* handler);
|
||||
virtual ~CompilationUnit() {
|
||||
if (abbrevs_) delete abbrevs_;
|
||||
}
|
||||
|
||||
// Initialize a compilation unit from a .dwo or .dwp file.
|
||||
// In this case, we need the .debug_addr section from the
|
||||
// executable file that contains the corresponding skeleton
|
||||
// compilation unit. We also inherit the Dwarf2Handler from
|
||||
// the executable file, and call it as if we were still
|
||||
// processing the original compilation unit.
|
||||
void SetSplitDwarf(const uint8_t* addr_buffer, uint64 addr_buffer_length,
|
||||
uint64 addr_base, uint64 ranges_base, uint64 dwo_id);
|
||||
|
||||
// Begin reading a Dwarf2 compilation unit, and calling the
|
||||
// callbacks in the Dwarf2Handler
|
||||
|
||||
|
@ -382,104 +266,29 @@ class CompilationUnit {
|
|||
|
||||
// Processes a single DIE for this compilation unit and return a new
|
||||
// pointer just past the end of it
|
||||
const uint8_t *ProcessDIE(uint64 dieoffset,
|
||||
const uint8_t *start,
|
||||
const Abbrev& abbrev);
|
||||
const char* ProcessDIE(uint64 dieoffset,
|
||||
const char* start,
|
||||
const Abbrev& abbrev);
|
||||
|
||||
// Processes a single attribute and return a new pointer just past the
|
||||
// end of it
|
||||
const uint8_t *ProcessAttribute(uint64 dieoffset,
|
||||
const uint8_t *start,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form);
|
||||
|
||||
// Called when we have an attribute with unsigned data to give to
|
||||
// our handler. The attribute is for the DIE at OFFSET from the
|
||||
// beginning of compilation unit, has a name of ATTR, a form of
|
||||
// FORM, and the actual data of the attribute is in DATA.
|
||||
// If we see a DW_AT_GNU_dwo_id attribute, save the value so that
|
||||
// we can find the debug info in a .dwo or .dwp file.
|
||||
void ProcessAttributeUnsigned(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
if (attr == DW_AT_GNU_dwo_id) {
|
||||
dwo_id_ = data;
|
||||
}
|
||||
else if (attr == DW_AT_GNU_addr_base) {
|
||||
addr_base_ = data;
|
||||
}
|
||||
else if (attr == DW_AT_GNU_ranges_base) {
|
||||
ranges_base_ = data;
|
||||
}
|
||||
// TODO(yunlian): When we add DW_AT_ranges_base from DWARF-5,
|
||||
// that base will apply to DW_AT_ranges attributes in the
|
||||
// skeleton CU as well as in the .dwo/.dwp files.
|
||||
else if (attr == DW_AT_ranges && is_split_dwarf_) {
|
||||
data += ranges_base_;
|
||||
}
|
||||
handler_->ProcessAttributeUnsigned(offset, attr, form, data);
|
||||
}
|
||||
|
||||
// Called when we have an attribute with signed data to give to
|
||||
// our handler. The attribute is for the DIE at OFFSET from the
|
||||
// beginning of compilation unit, has a name of ATTR, a form of
|
||||
// FORM, and the actual data of the attribute is in DATA.
|
||||
void ProcessAttributeSigned(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data) {
|
||||
handler_->ProcessAttributeSigned(offset, attr, form, data);
|
||||
}
|
||||
|
||||
// Called when we have an attribute with a buffer of data to give to
|
||||
// our handler. The attribute is for the DIE at OFFSET from the
|
||||
// beginning of compilation unit, has a name of ATTR, a form of
|
||||
// FORM, and the actual data of the attribute is in DATA, and the
|
||||
// length of the buffer is LENGTH.
|
||||
void ProcessAttributeBuffer(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const uint8_t* data,
|
||||
uint64 len) {
|
||||
handler_->ProcessAttributeBuffer(offset, attr, form, data, len);
|
||||
}
|
||||
|
||||
// Called when we have an attribute with string data to give to
|
||||
// our handler. The attribute is for the DIE at OFFSET from the
|
||||
// beginning of compilation unit, has a name of ATTR, a form of
|
||||
// FORM, and the actual data of the attribute is in DATA.
|
||||
// If we see a DW_AT_GNU_dwo_name attribute, save the value so
|
||||
// that we can find the debug info in a .dwo or .dwp file.
|
||||
void ProcessAttributeString(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const char* data) {
|
||||
if (attr == DW_AT_GNU_dwo_name)
|
||||
dwo_name_ = data;
|
||||
handler_->ProcessAttributeString(offset, attr, form, data);
|
||||
}
|
||||
const char* ProcessAttribute(uint64 dieoffset,
|
||||
const char* start,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form);
|
||||
|
||||
// Processes all DIEs for this compilation unit
|
||||
void ProcessDIEs();
|
||||
|
||||
// Skips the die with attributes specified in ABBREV starting at
|
||||
// START, and return the new place to position the stream to.
|
||||
const uint8_t *SkipDIE(const uint8_t *start, const Abbrev& abbrev);
|
||||
const char* SkipDIE(const char* start,
|
||||
const Abbrev& abbrev);
|
||||
|
||||
// Skips the attribute starting at START, with FORM, and return the
|
||||
// new place to position the stream to.
|
||||
const uint8_t *SkipAttribute(const uint8_t *start, enum DwarfForm form);
|
||||
|
||||
// Process the actual debug information in a split DWARF file.
|
||||
void ProcessSplitDwarf();
|
||||
|
||||
// Read the debug sections from a .dwo file.
|
||||
void ReadDebugSectionsFromDwo(ElfReader* elf_reader,
|
||||
SectionMap* sections);
|
||||
|
||||
// Path of the file containing the debug information.
|
||||
const string path_;
|
||||
const char* SkipAttribute(const char* start,
|
||||
enum DwarfForm form);
|
||||
|
||||
// Offset from section start is the offset of this compilation unit
|
||||
// from the beginning of the .debug_info section.
|
||||
|
@ -488,9 +297,9 @@ class CompilationUnit {
|
|||
// buffer is the buffer for our CU, starting at .debug_info + offset
|
||||
// passed in from constructor.
|
||||
// after_header points to right after the compilation unit header.
|
||||
const uint8_t *buffer_;
|
||||
const char* buffer_;
|
||||
uint64 buffer_length_;
|
||||
const uint8_t *after_header_;
|
||||
const char* after_header_;
|
||||
|
||||
// The associated ByteReader that handles endianness issues for us
|
||||
ByteReader* reader_;
|
||||
|
@ -509,143 +318,96 @@ class CompilationUnit {
|
|||
// String section buffer and length, if we have a string section.
|
||||
// This is here to avoid doing a section lookup for strings in
|
||||
// ProcessAttribute, which is in the hot path for DWARF2 reading.
|
||||
const uint8_t *string_buffer_;
|
||||
const char* string_buffer_;
|
||||
uint64 string_buffer_length_;
|
||||
|
||||
// String offsets section buffer and length, if we have a string offsets
|
||||
// section (.debug_str_offsets or .debug_str_offsets.dwo).
|
||||
const uint8_t* str_offsets_buffer_;
|
||||
uint64 str_offsets_buffer_length_;
|
||||
|
||||
// Address section buffer and length, if we have an address section
|
||||
// (.debug_addr).
|
||||
const uint8_t* addr_buffer_;
|
||||
uint64 addr_buffer_length_;
|
||||
|
||||
// Flag indicating whether this compilation unit is part of a .dwo
|
||||
// or .dwp file. If true, we are reading this unit because a
|
||||
// skeleton compilation unit in an executable file had a
|
||||
// DW_AT_GNU_dwo_name or DW_AT_GNU_dwo_id attribute.
|
||||
// In a .dwo file, we expect the string offsets section to
|
||||
// have a ".dwo" suffix, and we will use the ".debug_addr" section
|
||||
// associated with the skeleton compilation unit.
|
||||
bool is_split_dwarf_;
|
||||
|
||||
// The value of the DW_AT_GNU_dwo_id attribute, if any.
|
||||
uint64 dwo_id_;
|
||||
|
||||
// The value of the DW_AT_GNU_dwo_name attribute, if any.
|
||||
const char* dwo_name_;
|
||||
|
||||
// If this is a split DWARF CU, the value of the DW_AT_GNU_dwo_id attribute
|
||||
// from the skeleton CU.
|
||||
uint64 skeleton_dwo_id_;
|
||||
|
||||
// The value of the DW_AT_GNU_ranges_base attribute, if any.
|
||||
uint64 ranges_base_;
|
||||
|
||||
// The value of the DW_AT_GNU_addr_base attribute, if any.
|
||||
uint64 addr_base_;
|
||||
|
||||
// True if we have already looked for a .dwp file.
|
||||
bool have_checked_for_dwp_;
|
||||
|
||||
// Path to the .dwp file.
|
||||
string dwp_path_;
|
||||
|
||||
// ByteReader for the DWP file.
|
||||
std::unique_ptr<ByteReader> dwp_byte_reader_;
|
||||
|
||||
// DWP reader.
|
||||
std::unique_ptr<DwpReader> dwp_reader_;
|
||||
};
|
||||
|
||||
// A Reader for a .dwp file. Supports the fetching of DWARF debug
|
||||
// info for a given dwo_id.
|
||||
//
|
||||
// There are two versions of .dwp files. In both versions, the
|
||||
// .dwp file is an ELF file containing only debug sections.
|
||||
// In Version 1, the file contains many copies of each debug
|
||||
// section, one for each .dwo file that is packaged in the .dwp
|
||||
// file, and the .debug_cu_index section maps from the dwo_id
|
||||
// to a set of section indexes. In Version 2, the file contains
|
||||
// one of each debug section, and the .debug_cu_index section
|
||||
// maps from the dwo_id to a set of offsets and lengths that
|
||||
// identify each .dwo file's contribution to the larger sections.
|
||||
// This class is the main interface between the reader and the
|
||||
// client. The virtual functions inside this get called for
|
||||
// interesting events that happen during DWARF2 reading.
|
||||
// The default implementation skips everything.
|
||||
|
||||
class DwpReader {
|
||||
class Dwarf2Handler {
|
||||
public:
|
||||
DwpReader(const ByteReader& byte_reader, ElfReader* elf_reader);
|
||||
Dwarf2Handler() { }
|
||||
|
||||
~DwpReader();
|
||||
virtual ~Dwarf2Handler() { }
|
||||
|
||||
// Read the CU index and initialize data members.
|
||||
void Initialize();
|
||||
// Start to process a compilation unit at OFFSET from the beginning of the
|
||||
// .debug_info section. Return false if you would like to skip this
|
||||
// compilation unit.
|
||||
virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
|
||||
uint8 offset_size, uint64 cu_length,
|
||||
uint8 dwarf_version) { return false; }
|
||||
|
||||
// Read the debug sections for the given dwo_id.
|
||||
void ReadDebugSectionsForCU(uint64 dwo_id, SectionMap* sections);
|
||||
// Start to process a DIE at OFFSET from the beginning of the .debug_info
|
||||
// section. Return false if you would like to skip this DIE.
|
||||
virtual bool StartDIE(uint64 offset, enum DwarfTag tag) { return false; }
|
||||
|
||||
private:
|
||||
// Search a v1 hash table for "dwo_id". Returns the slot index
|
||||
// where the dwo_id was found, or -1 if it was not found.
|
||||
int LookupCU(uint64 dwo_id);
|
||||
// Called when we have an attribute with unsigned data to give to our
|
||||
// handler. The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
||||
// DATA.
|
||||
virtual void ProcessAttributeUnsigned(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) { }
|
||||
|
||||
// Search a v2 hash table for "dwo_id". Returns the row index
|
||||
// in the offsets and sizes tables, or 0 if it was not found.
|
||||
uint32 LookupCUv2(uint64 dwo_id);
|
||||
// Called when we have an attribute with signed data to give to our handler.
|
||||
// The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
||||
// DATA.
|
||||
virtual void ProcessAttributeSigned(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data) { }
|
||||
|
||||
// The ELF reader for the .dwp file.
|
||||
ElfReader* elf_reader_;
|
||||
// Called when we have an attribute whose value is a reference to
|
||||
// another DIE. The attribute belongs to the DIE at OFFSET from the
|
||||
// beginning of the .debug_info section. Its name is ATTR, its form
|
||||
// is FORM, and the offset of the DIE being referred to from the
|
||||
// beginning of the .debug_info section is DATA.
|
||||
virtual void ProcessAttributeReference(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) { }
|
||||
|
||||
// The ByteReader for the .dwp file.
|
||||
const ByteReader& byte_reader_;
|
||||
// Called when we have an attribute with a buffer of data to give to our
|
||||
// handler. The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, DATA points to
|
||||
// the buffer's contents, and its length in bytes is LENGTH. The buffer is
|
||||
// owned by the caller, not the callee, and may not persist for very long.
|
||||
// If you want the data to be available later, it needs to be copied.
|
||||
virtual void ProcessAttributeBuffer(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const char* data,
|
||||
uint64 len) { }
|
||||
|
||||
// Pointer to the .debug_cu_index section.
|
||||
const char* cu_index_;
|
||||
// Called when we have an attribute with string data to give to our handler.
|
||||
// The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, and its value is
|
||||
// DATA.
|
||||
virtual void ProcessAttributeString(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string& data) { }
|
||||
|
||||
// Size of the .debug_cu_index section.
|
||||
size_t cu_index_size_;
|
||||
// Called when we have an attribute whose value is the 64-bit signature
|
||||
// of a type unit in the .debug_types section. OFFSET is the offset of
|
||||
// the DIE whose attribute we're reporting. ATTR and FORM are the
|
||||
// attribute's name and form. SIGNATURE is the type unit's signature.
|
||||
virtual void ProcessAttributeSignature(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 signature) { }
|
||||
|
||||
// Pointer to the .debug_str.dwo section.
|
||||
const char* string_buffer_;
|
||||
// Called when finished processing the DIE at OFFSET.
|
||||
// Because DWARF2/3 specifies a tree of DIEs, you may get starts
|
||||
// before ends of the previous DIE, as we process children before
|
||||
// ending the parent.
|
||||
virtual void EndDIE(uint64 offset) { }
|
||||
|
||||
// Size of the .debug_str.dwo section.
|
||||
size_t string_buffer_size_;
|
||||
|
||||
// Version of the .dwp file. We support versions 1 and 2 currently.
|
||||
int version_;
|
||||
|
||||
// Number of columns in the section tables (version 2).
|
||||
unsigned int ncolumns_;
|
||||
|
||||
// Number of units in the section tables (version 2).
|
||||
unsigned int nunits_;
|
||||
|
||||
// Number of slots in the hash table.
|
||||
unsigned int nslots_;
|
||||
|
||||
// Pointer to the beginning of the hash table.
|
||||
const char* phash_;
|
||||
|
||||
// Pointer to the beginning of the index table.
|
||||
const char* pindex_;
|
||||
|
||||
// Pointer to the beginning of the section index pool (version 1).
|
||||
const char* shndx_pool_;
|
||||
|
||||
// Pointer to the beginning of the section offset table (version 2).
|
||||
const char* offset_table_;
|
||||
|
||||
// Pointer to the beginning of the section size table (version 2).
|
||||
const char* size_table_;
|
||||
|
||||
// Contents of the sections of interest (version 2).
|
||||
const char* abbrev_data_;
|
||||
size_t abbrev_size_;
|
||||
const char* info_data_;
|
||||
size_t info_size_;
|
||||
const char* str_offsets_data_;
|
||||
size_t str_offsets_size_;
|
||||
};
|
||||
|
||||
// This class is a reader for DWARF's Call Frame Information. CFI
|
||||
|
@ -875,7 +637,7 @@ class CallFrameInfo {
|
|||
// The mechanics of C++ exception handling, personality routines,
|
||||
// and language-specific data areas are described here, rather nicely:
|
||||
// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
|
||||
CallFrameInfo(const uint8_t *buffer, size_t buffer_length,
|
||||
CallFrameInfo(const char *buffer, size_t buffer_length,
|
||||
ByteReader *reader, Handler *handler, Reporter *reporter,
|
||||
bool eh_frame = false)
|
||||
: buffer_(buffer), buffer_length_(buffer_length),
|
||||
|
@ -903,7 +665,7 @@ class CallFrameInfo {
|
|||
size_t offset;
|
||||
|
||||
// The start of this entry in the buffer.
|
||||
const uint8_t *start;
|
||||
const char *start;
|
||||
|
||||
// Which kind of entry this is.
|
||||
//
|
||||
|
@ -914,16 +676,16 @@ class CallFrameInfo {
|
|||
|
||||
// The end of this entry's common prologue (initial length and id), and
|
||||
// the start of this entry's kind-specific fields.
|
||||
const uint8_t *fields;
|
||||
const char *fields;
|
||||
|
||||
// The start of this entry's instructions.
|
||||
const uint8_t *instructions;
|
||||
const char *instructions;
|
||||
|
||||
// The address past the entry's last byte in the buffer. (Note that
|
||||
// since offset points to the entry's initial length field, and the
|
||||
// length field is the number of bytes after that field, this is not
|
||||
// simply buffer_ + offset + length.)
|
||||
const uint8_t *end;
|
||||
const char *end;
|
||||
|
||||
// For both DWARF CFI and .eh_frame sections, this is the CIE id in a
|
||||
// CIE, and the offset of the associated CIE in an FDE.
|
||||
|
@ -1000,7 +762,7 @@ class CallFrameInfo {
|
|||
// true. On failure, report the problem, and return false. Even if we
|
||||
// return false, set ENTRY->end to the first byte after the entry if we
|
||||
// were able to figure that out, or NULL if we weren't.
|
||||
bool ReadEntryPrologue(const uint8_t *cursor, Entry *entry);
|
||||
bool ReadEntryPrologue(const char *cursor, Entry *entry);
|
||||
|
||||
// Parse the fields of a CIE after the entry prologue, including any 'z'
|
||||
// augmentation data. Assume that the 'Entry' fields of CIE are
|
||||
|
@ -1028,7 +790,7 @@ class CallFrameInfo {
|
|||
}
|
||||
|
||||
// The contents of the DWARF .debug_info section we're parsing.
|
||||
const uint8_t *buffer_;
|
||||
const char *buffer_;
|
||||
size_t buffer_length_;
|
||||
|
||||
// For reading multi-byte values with the appropriate endianness.
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
|
||||
// dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
|
@ -187,7 +186,7 @@ class CFI: public CFIFixture, public Test { };
|
|||
TEST_F(CFI, EmptyRegion) {
|
||||
EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0);
|
||||
EXPECT_CALL(handler, End()).Times(0);
|
||||
static const uint8_t data[] = { 42 };
|
||||
static const char data[1] = { 42 };
|
||||
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
CallFrameInfo parser(data, 0, &byte_reader, &handler, &reporter);
|
||||
|
@ -214,8 +213,7 @@ TEST_F(CFI, IncompleteLength32) {
|
|||
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size() - 2,
|
||||
CallFrameInfo parser(contents.data(), contents.size() - 2,
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
@ -240,8 +238,7 @@ TEST_F(CFI, IncompleteLength64) {
|
|||
|
||||
ByteReader byte_reader(ENDIANNESS_LITTLE);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size() - 4,
|
||||
CallFrameInfo parser(contents.data(), contents.size() - 4,
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
@ -265,8 +262,7 @@ TEST_F(CFI, IncompleteId32) {
|
|||
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
@ -292,8 +288,7 @@ TEST_F(CFI, BadId32) {
|
|||
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
@ -314,8 +309,7 @@ TEST_F(CFI, SingleCIE) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_LITTLE);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
@ -345,8 +339,7 @@ TEST_F(CFI, OneFDE) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
@ -389,8 +382,7 @@ TEST_F(CFI, TwoFDEsOneCIE) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
@ -439,8 +431,7 @@ TEST_F(CFI, TwoFDEsTwoCIEs) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_LITTLE);
|
||||
byte_reader.SetAddressSize(8);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
@ -484,8 +475,7 @@ TEST_F(CFI, BadVersion) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
@ -529,8 +519,7 @@ TEST_F(CFI, BadAugmentation) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_FALSE(parser.Start());
|
||||
}
|
||||
|
@ -564,8 +553,7 @@ TEST_F(CFI, CIEVersion1ReturnColumn) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
@ -599,8 +587,7 @@ TEST_F(CFI, CIEVersion3ReturnColumn) {
|
|||
EXPECT_TRUE(section.GetContents(&contents));
|
||||
ByteReader byte_reader(ENDIANNESS_BIG);
|
||||
byte_reader.SetAddressSize(4);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
EXPECT_TRUE(parser.Start());
|
||||
}
|
||||
|
@ -681,8 +668,7 @@ struct CFIInsnFixture: public CFIFixture {
|
|||
}
|
||||
ByteReader byte_reader(endianness);
|
||||
byte_reader.SetAddressSize(section->AddressSize());
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter);
|
||||
if (succeeds)
|
||||
EXPECT_TRUE(parser.Start());
|
||||
|
@ -2003,12 +1989,10 @@ struct EHFrameFixture: public CFIInsnFixture {
|
|||
}
|
||||
ByteReader byte_reader(endianness);
|
||||
byte_reader.SetAddressSize(section->AddressSize());
|
||||
byte_reader.SetCFIDataBase(encoded_pointer_bases.cfi,
|
||||
reinterpret_cast<const uint8_t *>(contents.data()));
|
||||
byte_reader.SetCFIDataBase(encoded_pointer_bases.cfi, contents.data());
|
||||
byte_reader.SetTextBase(encoded_pointer_bases.text);
|
||||
byte_reader.SetDataBase(encoded_pointer_bases.data);
|
||||
CallFrameInfo parser(reinterpret_cast<const uint8_t *>(contents.data()),
|
||||
contents.size(),
|
||||
CallFrameInfo parser(contents.data(), contents.size(),
|
||||
&byte_reader, &handler, &reporter, true);
|
||||
if (succeeds)
|
||||
EXPECT_TRUE(parser.Start());
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
|
||||
// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <iostream>
|
||||
|
@ -92,7 +91,7 @@ class MockDwarf2Handler: public Dwarf2Handler {
|
|||
MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const uint8_t *data,
|
||||
const char* data,
|
||||
uint64 len));
|
||||
MOCK_METHOD4(ProcessAttributeString, void(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
|
@ -133,11 +132,9 @@ struct DIEFixture {
|
|||
assert(info.GetContents(&info_contents));
|
||||
assert(abbrevs.GetContents(&abbrevs_contents));
|
||||
section_map.clear();
|
||||
section_map[".debug_info"].first
|
||||
= reinterpret_cast<const uint8_t *>(info_contents.data());
|
||||
section_map[".debug_info"].first = info_contents.data();
|
||||
section_map[".debug_info"].second = info_contents.size();
|
||||
section_map[".debug_abbrev"].first
|
||||
= reinterpret_cast<const uint8_t *>(abbrevs_contents.data());
|
||||
section_map[".debug_abbrev"].first = abbrevs_contents.data();
|
||||
section_map[".debug_abbrev"].second = abbrevs_contents.size();
|
||||
return section_map;
|
||||
}
|
||||
|
@ -199,7 +196,7 @@ TEST_P(DwarfHeader, Header) {
|
|||
|
||||
ByteReader byte_reader(GetParam().endianness == kLittleEndian ?
|
||||
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
|
||||
CompilationUnit parser("", MakeSectionMap(), 0, &byte_reader, &handler);
|
||||
CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler);
|
||||
EXPECT_EQ(parser.Start(), info_contents.size());
|
||||
}
|
||||
|
||||
|
@ -277,7 +274,7 @@ struct DwarfFormsFixture: public DIEFixture {
|
|||
void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) {
|
||||
ByteReader byte_reader(params.endianness == kLittleEndian ?
|
||||
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
|
||||
CompilationUnit parser("", MakeSectionMap(), offset, &byte_reader, &handler);
|
||||
CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler);
|
||||
EXPECT_EQ(offset + parser.Start(), info_contents.size());
|
||||
}
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,166 +0,0 @@
|
|||
// Copyright 2005 Google Inc. All Rights Reserved.
|
||||
// Author: chatham@google.com (Andrew Chatham)
|
||||
// Author: satorux@google.com (Satoru Takabayashi)
|
||||
//
|
||||
// ElfReader handles reading in ELF. It can extract symbols from the
|
||||
// current process, which may be used to symbolize stack traces
|
||||
// without having to make a potentially dangerous call to fork().
|
||||
//
|
||||
// ElfReader dynamically allocates memory, so it is not appropriate to
|
||||
// use once the address space might be corrupted, such as during
|
||||
// process death.
|
||||
//
|
||||
// ElfReader supports both 32-bit and 64-bit ELF binaries.
|
||||
|
||||
#ifndef COMMON_DWARF_ELF_READER_H__
|
||||
#define COMMON_DWARF_ELF_READER_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/types.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::pair;
|
||||
|
||||
namespace dwarf2reader {
|
||||
|
||||
class SymbolMap;
|
||||
class Elf32;
|
||||
class Elf64;
|
||||
template<typename ElfArch>
|
||||
class ElfReaderImpl;
|
||||
|
||||
class ElfReader {
|
||||
public:
|
||||
explicit ElfReader(const string &path);
|
||||
~ElfReader();
|
||||
|
||||
// Parse the ELF prologue of this file and return whether it was
|
||||
// successfully parsed and matches the word size and byte order of
|
||||
// the current process.
|
||||
bool IsNativeElfFile() const;
|
||||
|
||||
// Similar to IsNativeElfFile but checks if it's a 32-bit ELF file.
|
||||
bool IsElf32File() const;
|
||||
|
||||
// Similar to IsNativeElfFile but checks if it's a 64-bit ELF file.
|
||||
bool IsElf64File() const;
|
||||
|
||||
// Checks if it's an ELF file of type ET_DYN (shared object file).
|
||||
bool IsDynamicSharedObject();
|
||||
|
||||
// Add symbols in the given ELF file into the provided SymbolMap,
|
||||
// assuming that the file has been loaded into the specified
|
||||
// offset.
|
||||
//
|
||||
// The remaining arguments are typically taken from a
|
||||
// ProcMapsIterator (base/sysinfo.h) and describe which portions of
|
||||
// the ELF file are mapped into which parts of memory:
|
||||
//
|
||||
// mem_offset - position at which the segment is mapped into memory
|
||||
// file_offset - offset in the file where the mapping begins
|
||||
// length - length of the mapped segment
|
||||
void AddSymbols(SymbolMap *symbols,
|
||||
uint64 mem_offset, uint64 file_offset,
|
||||
uint64 length);
|
||||
|
||||
class SymbolSink {
|
||||
public:
|
||||
virtual ~SymbolSink() {}
|
||||
virtual void AddSymbol(const char *name, uint64 address, uint64 size) = 0;
|
||||
};
|
||||
|
||||
// Like AddSymbols above, but with no address correction.
|
||||
// Processes any SHT_SYMTAB section, followed by any SHT_DYNSYM section.
|
||||
void VisitSymbols(SymbolSink *sink);
|
||||
|
||||
// Like VisitSymbols above, but for a specific symbol binding/type.
|
||||
// A negative value for the binding and type parameters means any
|
||||
// binding or type.
|
||||
void VisitSymbols(SymbolSink *sink, int symbol_binding, int symbol_type);
|
||||
|
||||
// Like VisitSymbols above but can optionally export raw symbol values instead
|
||||
// of adjusted ones.
|
||||
void VisitSymbols(SymbolSink *sink, int symbol_binding, int symbol_type,
|
||||
bool get_raw_symbol_values);
|
||||
|
||||
// p_vaddr of the first PT_LOAD segment (if any), or 0 if no PT_LOAD
|
||||
// segments are present. This is the address an ELF image was linked
|
||||
// (by static linker) to be loaded at. Usually (but not always) 0 for
|
||||
// shared libraries and position-independent executables.
|
||||
uint64 VaddrOfFirstLoadSegment();
|
||||
|
||||
// Return the name of section "shndx". Returns NULL if the section
|
||||
// is not found.
|
||||
const char *GetSectionName(int shndx);
|
||||
|
||||
// Return the number of sections in the given ELF file.
|
||||
uint64 GetNumSections();
|
||||
|
||||
// Get section "shndx" from the given ELF file. On success, return
|
||||
// the pointer to the section and store the size in "size".
|
||||
// On error, return NULL. The returned section data is only valid
|
||||
// until the ElfReader gets destroyed.
|
||||
const char *GetSectionByIndex(int shndx, size_t *size);
|
||||
|
||||
// Get section with "section_name" (ex. ".text", ".symtab") in the
|
||||
// given ELF file. On success, return the pointer to the section
|
||||
// and store the size in "size". On error, return NULL. The
|
||||
// returned section data is only valid until the ElfReader gets
|
||||
// destroyed.
|
||||
const char *GetSectionByName(const string §ion_name, size_t *size);
|
||||
|
||||
// This is like GetSectionByName() but it returns a lot of extra information
|
||||
// about the section. The SectionInfo structure is almost identical to
|
||||
// the typedef struct Elf64_Shdr defined in <elf.h>, but is redefined
|
||||
// here so that the many short macro names in <elf.h> don't have to be
|
||||
// added to our already cluttered namespace.
|
||||
struct SectionInfo {
|
||||
uint32 type; // Section type (SHT_xxx constant from elf.h).
|
||||
uint64 flags; // Section flags (SHF_xxx constants from elf.h).
|
||||
uint64 addr; // Section virtual address at execution.
|
||||
uint64 offset; // Section file offset.
|
||||
uint64 size; // Section size in bytes.
|
||||
uint32 link; // Link to another section.
|
||||
uint32 info; // Additional section information.
|
||||
uint64 addralign; // Section alignment.
|
||||
uint64 entsize; // Entry size if section holds a table.
|
||||
};
|
||||
const char *GetSectionInfoByName(const string §ion_name,
|
||||
SectionInfo *info);
|
||||
|
||||
// Check if "path" is an ELF binary that has not been stripped of symbol
|
||||
// tables. This function supports both 32-bit and 64-bit ELF binaries.
|
||||
static bool IsNonStrippedELFBinary(const string &path);
|
||||
|
||||
// Check if "path" is an ELF binary that has not been stripped of debug
|
||||
// info. Unlike IsNonStrippedELFBinary, this function will return
|
||||
// false for binaries passed through "strip -S".
|
||||
static bool IsNonDebugStrippedELFBinary(const string &path);
|
||||
|
||||
// Match a requested section name with the section name as it
|
||||
// appears in the elf-file, adjusting for compressed debug section
|
||||
// names. For example, returns true if name == ".debug_abbrev" and
|
||||
// sh_name == ".zdebug_abbrev"
|
||||
static bool SectionNamesMatch(const string &name, const string &sh_name);
|
||||
|
||||
private:
|
||||
// Lazily initialize impl32_ and return it.
|
||||
ElfReaderImpl<Elf32> *GetImpl32();
|
||||
// Ditto for impl64_.
|
||||
ElfReaderImpl<Elf64> *GetImpl64();
|
||||
|
||||
// Path of the file we're reading.
|
||||
const string path_;
|
||||
// Read-only file descriptor for the file. May be -1 if there was an
|
||||
// error during open.
|
||||
int fd_;
|
||||
ElfReaderImpl<Elf32> *impl32_;
|
||||
ElfReaderImpl<Elf64> *impl64_;
|
||||
};
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
||||
#endif // COMMON_DWARF_ELF_READER_H__
|
|
@ -9,7 +9,6 @@ HOST_SOURCES += [
|
|||
'bytereader.cc',
|
||||
'dwarf2diehandler.cc',
|
||||
'dwarf2reader.cc',
|
||||
'elf_reader.cc',
|
||||
'functioninfo.cc',
|
||||
]
|
||||
HOST_CXXFLAGS += [
|
||||
|
@ -27,9 +26,4 @@ HOST_CXXFLAGS += [
|
|||
'-funsigned-char',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'Darwin':
|
||||
HOST_CXXFLAGS += [
|
||||
'-stdlib=libc++',
|
||||
]
|
||||
|
||||
include('/toolkit/crashreporter/crashreporter.mozbuild')
|
||||
|
|
|
@ -45,7 +45,11 @@ typedef unsigned short uint16;
|
|||
typedef unsigned int uint32;
|
||||
typedef unsigned long long uint64;
|
||||
|
||||
typedef intptr_t intptr;
|
||||
typedef uintptr_t uintptr;
|
||||
#ifdef __PTRDIFF_TYPE__
|
||||
typedef __PTRDIFF_TYPE__ intptr;
|
||||
typedef unsigned __PTRDIFF_TYPE__ uintptr;
|
||||
#else
|
||||
#error "Can't find pointer-sized integral types."
|
||||
#endif
|
||||
|
||||
#endif // _COMMON_DWARF_TYPES_H__
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
#include <cxxabi.h>
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -141,7 +140,7 @@ DwarfCUToModule::FileContext::~FileContext() {
|
|||
}
|
||||
|
||||
void DwarfCUToModule::FileContext::AddSectionToSectionMap(
|
||||
const string& name, const uint8_t *contents, uint64 length) {
|
||||
const string& name, const char* contents, uint64 length) {
|
||||
section_map_[name] = std::make_pair(contents, length);
|
||||
}
|
||||
|
||||
|
@ -417,8 +416,7 @@ string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
|||
|
||||
// If this DIE was marked as a declaration, record its names in the
|
||||
// specification table.
|
||||
if ((declaration_ && qualified_name) ||
|
||||
(unqualified_name && enclosing_name)) {
|
||||
if (declaration_ && qualified_name || (unqualified_name && enclosing_name)) {
|
||||
Specification spec;
|
||||
if (qualified_name) {
|
||||
spec.qualified_name = *qualified_name;
|
||||
|
@ -816,7 +814,7 @@ void DwarfCUToModule::ReadSourceLines(uint64 offset) {
|
|||
cu_context_->reporter->MissingSection(".debug_line");
|
||||
return;
|
||||
}
|
||||
const uint8_t *section_start = map_entry->second.first;
|
||||
const char *section_start = map_entry->second.first;
|
||||
uint64 section_length = map_entry->second.second;
|
||||
if (offset >= section_length) {
|
||||
cu_context_->reporter->BadLineInfoOffset(offset);
|
||||
|
|
|
@ -39,8 +39,6 @@
|
|||
#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__
|
||||
#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/language.h"
|
||||
|
@ -86,7 +84,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
|||
|
||||
// Add CONTENTS of size LENGTH to the section map as NAME.
|
||||
void AddSectionToSectionMap(const string& name,
|
||||
const uint8_t *contents,
|
||||
const char* contents,
|
||||
uint64 length);
|
||||
|
||||
// Clear the section map for testing.
|
||||
|
@ -142,7 +140,7 @@ class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
|
|||
// mappings, given a pointer to some DWARF line number data
|
||||
// PROGRAM, and an overestimate of its size. Add no zero-length
|
||||
// lines to LINES.
|
||||
virtual void ReadProgram(const uint8_t *program, uint64 length,
|
||||
virtual void ReadProgram(const char *program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines) = 0;
|
||||
};
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
|
||||
// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
@ -67,7 +65,7 @@ using ::testing::ValuesIn;
|
|||
class MockLineToModuleHandler: public DwarfCUToModule::LineToModuleHandler {
|
||||
public:
|
||||
MOCK_METHOD1(StartCompilationUnit, void(const string& compilation_dir));
|
||||
MOCK_METHOD4(ReadProgram, void(const uint8_t *program, uint64 length,
|
||||
MOCK_METHOD4(ReadProgram, void(const char* program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines));
|
||||
};
|
||||
|
||||
|
@ -113,7 +111,7 @@ class CUFixtureBase {
|
|||
public:
|
||||
explicit AppendLinesFunctor(
|
||||
const vector<Module::Line> *lines) : lines_(lines) { }
|
||||
void operator()(const uint8_t *program, uint64 length,
|
||||
void operator()(const char *program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines) {
|
||||
lines->insert(lines->end(), lines_->begin(), lines_->end());
|
||||
}
|
||||
|
@ -287,7 +285,7 @@ class CUFixtureBase {
|
|||
// Mock line program reader.
|
||||
MockLineToModuleHandler line_reader_;
|
||||
AppendLinesFunctor appender_;
|
||||
static const uint8_t dummy_line_program_[];
|
||||
static const char dummy_line_program_[];
|
||||
static const size_t dummy_line_size_;
|
||||
|
||||
MockWarningReporter reporter_;
|
||||
|
@ -304,7 +302,7 @@ class CUFixtureBase {
|
|||
bool functions_filled_;
|
||||
};
|
||||
|
||||
const uint8_t CUFixtureBase::dummy_line_program_[] = "lots of fun data";
|
||||
const char CUFixtureBase::dummy_line_program_[] = "lots of fun data";
|
||||
const size_t CUFixtureBase::dummy_line_size_ =
|
||||
sizeof(CUFixtureBase::dummy_line_program_);
|
||||
|
||||
|
@ -377,7 +375,7 @@ void CUFixtureBase::ProcessStrangeAttributes(
|
|||
handler->ProcessAttributeReference((DwarfAttribute) 0xf7f7480f,
|
||||
(DwarfForm) 0x829e038a,
|
||||
0x50fddef44734fdecULL);
|
||||
static const uint8_t buffer[10] = "frobynode";
|
||||
static const char buffer[10] = "frobynode";
|
||||
handler->ProcessAttributeBuffer((DwarfAttribute) 0xa55ffb51,
|
||||
(DwarfForm) 0x2f43b041,
|
||||
buffer, sizeof(buffer));
|
||||
|
|
|
@ -39,7 +39,6 @@
|
|||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <link.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -65,7 +64,6 @@
|
|||
#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
|
||||
|
@ -89,18 +87,14 @@ 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
|
||||
|
@ -232,7 +226,7 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler {
|
|||
void StartCompilationUnit(const string& compilation_dir) {
|
||||
compilation_dir_ = compilation_dir;
|
||||
}
|
||||
void ReadProgram(const uint8_t *program, uint64 length,
|
||||
void ReadProgram(const char* program, uint64 length,
|
||||
Module* module, std::vector<Module::Line>* lines) {
|
||||
DwarfLineToModule handler(module, compilation_dir_, lines);
|
||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||
|
@ -270,8 +264,8 @@ bool LoadDwarf(const string& dwarf_filename,
|
|||
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);
|
||||
const char* contents = GetOffset<ElfClass, char>(elf_header,
|
||||
section->sh_offset);
|
||||
file_context.AddSectionToSectionMap(name, contents, section->sh_size);
|
||||
}
|
||||
|
||||
|
@ -280,7 +274,7 @@ bool LoadDwarf(const string& dwarf_filename,
|
|||
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 =
|
||||
const std::pair<const char*, uint64>& debug_info_section =
|
||||
debug_info_entry->second;
|
||||
// This should never have been called if the file doesn't have a
|
||||
// .debug_info section.
|
||||
|
@ -294,8 +288,7 @@ bool LoadDwarf(const string& dwarf_filename,
|
|||
// 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(),
|
||||
dwarf2reader::CompilationUnit reader(file_context.section_map(),
|
||||
offset,
|
||||
&byte_reader,
|
||||
&die_dispatcher);
|
||||
|
@ -358,8 +351,8 @@ bool LoadDwarfCFI(const string& dwarf_filename,
|
|||
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);
|
||||
const char* cfi =
|
||||
GetOffset<ElfClass, char>(elf_header, section->sh_offset);
|
||||
size_t cfi_size = section->sh_size;
|
||||
|
||||
// Plug together the parser, handler, and their entourages.
|
||||
|
@ -492,13 +485,12 @@ bool IsSameFile(const char* left_abspath, const string& right_path) {
|
|||
|
||||
// Read the .gnu_debuglink and get the debug file name. If anything goes
|
||||
// wrong, return an empty string.
|
||||
string ReadDebugLink(const uint8_t *debuglink,
|
||||
string ReadDebugLink(const char* 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;
|
||||
size_t debuglink_len = strlen(debuglink) + 5; // Include '\0' + CRC32.
|
||||
debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round up to 4 bytes.
|
||||
|
||||
// Sanity check.
|
||||
|
@ -519,8 +511,7 @@ string ReadDebugLink(const uint8_t *debuglink,
|
|||
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);
|
||||
debuglink_path = debug_dir + "/" + 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,
|
||||
|
@ -742,61 +733,32 @@ bool LoadSymbols(const string& obj_file,
|
|||
}
|
||||
|
||||
// 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 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* symtab =
|
||||
const uint8_t* dynsyms =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header,
|
||||
symtab_section->sh_offset);
|
||||
const uint8_t* strtab =
|
||||
dynsym_section->sh_offset);
|
||||
const uint8_t* dynstrs =
|
||||
GetOffset<ElfClass, uint8_t>(elf_header,
|
||||
strtab_section->sh_offset);
|
||||
dynstr_section->sh_offset);
|
||||
bool result =
|
||||
ELFSymbolsToModule(symtab,
|
||||
symtab_section->sh_size,
|
||||
strtab,
|
||||
strtab_section->sh_size,
|
||||
ELFSymbolsToModule(dynsyms,
|
||||
dynsym_section->sh_size,
|
||||
dynstrs,
|
||||
dynstr_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -882,9 +844,9 @@ bool LoadSymbols(const string& obj_file,
|
|||
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);
|
||||
const char* debuglink_contents =
|
||||
GetOffset<ElfClass, char>(elf_header,
|
||||
gnu_debuglink_section->sh_offset);
|
||||
string debuglink_file =
|
||||
ReadDebugLink(debuglink_contents,
|
||||
gnu_debuglink_section->sh_size,
|
||||
|
@ -935,6 +897,25 @@ const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
|
|||
}
|
||||
}
|
||||
|
||||
// Format the Elf file identifier in IDENTIFIER as a UUID with the
|
||||
// dashes removed.
|
||||
string FormatIdentifier(unsigned char identifier[16]) {
|
||||
char identifier_str[40];
|
||||
google_breakpad::FileID::ConvertIdentifierToString(
|
||||
identifier,
|
||||
identifier_str,
|
||||
sizeof(identifier_str));
|
||||
string id_no_dash;
|
||||
for (int i = 0; identifier_str[i] != '\0'; ++i)
|
||||
if (identifier_str[i] != '-')
|
||||
id_no_dash += identifier_str[i];
|
||||
// Add an extra "0" by the end. PDB files on Windows have an 'age'
|
||||
// number appended to the end of the file identifier; this isn't
|
||||
// really used or necessary on other platforms, but be consistent.
|
||||
id_no_dash += '0';
|
||||
return id_no_dash;
|
||||
}
|
||||
|
||||
// Return the non-directory portion of FILENAME: the portion after the
|
||||
// last slash, or the whole filename if there are no slashes.
|
||||
string BaseFileName(const string &filename) {
|
||||
|
@ -977,12 +958,19 @@ bool SanitizeDebugFile(const typename ElfClass::Ehdr* debug_elf_header,
|
|||
}
|
||||
|
||||
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)) {
|
||||
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;
|
||||
typedef typename ElfClass::Shdr Shdr;
|
||||
|
||||
*out_module = NULL;
|
||||
|
||||
unsigned char identifier[16];
|
||||
if (!google_breakpad::FileID::ElfFileIdentifierFromMappedFile(elf_header,
|
||||
identifier)) {
|
||||
fprintf(stderr, "%s: unable to generate file identifier\n",
|
||||
obj_filename.c_str());
|
||||
return false;
|
||||
|
@ -995,41 +983,17 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
|
|||
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;
|
||||
|
||||
string name = BaseFileName(obj_filename);
|
||||
string os = "Linux";
|
||||
string id = FormatIdentifier(identifier);
|
||||
|
||||
LoadSymbolsInfo<ElfClass> info(debug_dirs);
|
||||
scoped_ptr<Module> module(new Module(name, os, architecture, id));
|
||||
if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header,
|
||||
!debug_dirs.empty(), &info,
|
||||
options, module.get())) {
|
||||
|
@ -1044,9 +1008,7 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
|
|||
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)) {
|
||||
obj_filename, architecture, big_endian)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1104,45 +1066,6 @@ bool WriteSymbolFile(const string &obj_file,
|
|||
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,
|
||||
|
|
|
@ -67,12 +67,6 @@ bool WriteSymbolFile(const string &obj_file,
|
|||
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.
|
||||
|
|
|
@ -40,8 +40,6 @@
|
|||
#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"
|
||||
|
@ -56,7 +54,6 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file,
|
|||
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;
|
||||
|
@ -64,9 +61,7 @@ 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) {
|
||||
|
@ -83,11 +78,7 @@ class DumpSymbols : public Test {
|
|||
uint8_t* elfdata;
|
||||
};
|
||||
|
||||
typedef Types<ElfClass32, ElfClass64> ElfClasses;
|
||||
|
||||
TYPED_TEST_CASE(DumpSymbols, ElfClasses);
|
||||
|
||||
TYPED_TEST(DumpSymbols, Invalid) {
|
||||
TEST_F(DumpSymbols, Invalid) {
|
||||
Elf32_Ehdr header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
Module* module;
|
||||
|
@ -99,8 +90,8 @@ TYPED_TEST(DumpSymbols, Invalid) {
|
|||
&module));
|
||||
}
|
||||
|
||||
TYPED_TEST(DumpSymbols, SimplePublic) {
|
||||
ELF elf(TypeParam::kMachine, TypeParam::kClass, kLittleEndian);
|
||||
TEST_F(DumpSymbols, SimplePublic32) {
|
||||
ELF elf(EM_386, ELFCLASS32, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
|
@ -108,11 +99,8 @@ TYPED_TEST(DumpSymbols, SimplePublic) {
|
|||
|
||||
// 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.
|
||||
SymbolTable syms(kLittleEndian, 4, table);
|
||||
syms.AddSymbol("superfunc", (uint32_t)0x1000, (uint32_t)0x10,
|
||||
ELF32_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
|
@ -121,14 +109,14 @@ TYPED_TEST(DumpSymbols, SimplePublic) {
|
|||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(typename TypeParam::Sym)); // entsize
|
||||
sizeof(Elf32_Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
this->GetElfContents(elf);
|
||||
GetElfContents(elf);
|
||||
|
||||
Module* module;
|
||||
DumpOptions options(ALL_SYMBOL_DATA, true);
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
|
||||
"foo",
|
||||
vector<string>(),
|
||||
options,
|
||||
|
@ -136,40 +124,24 @@ TYPED_TEST(DumpSymbols, SimplePublic) {
|
|||
|
||||
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());
|
||||
EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
delete module;
|
||||
}
|
||||
|
||||
TYPED_TEST(DumpSymbols, SimpleBuildID) {
|
||||
ELF elf(TypeParam::kMachine, TypeParam::kClass, kLittleEndian);
|
||||
TEST_F(DumpSymbols, SimplePublic64) {
|
||||
ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian);
|
||||
// Zero out text section for simplicity.
|
||||
Section text(kLittleEndian);
|
||||
text.Append(4096, 0);
|
||||
elf.AddSection(".text", text, SHT_PROGBITS);
|
||||
|
||||
// Add a 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),
|
||||
SymbolTable syms(kLittleEndian, 8, table);
|
||||
syms.AddSymbol("superfunc", (uint64_t)0x1000, (uint64_t)0x10,
|
||||
ELF64_ST_INFO(STB_GLOBAL, STT_FUNC),
|
||||
SHN_UNDEF + 1);
|
||||
int index = elf.AddSection(".dynstr", table, SHT_STRTAB);
|
||||
elf.AddSection(".dynsym", syms,
|
||||
|
@ -177,14 +149,14 @@ TYPED_TEST(DumpSymbols, SimpleBuildID) {
|
|||
SHF_ALLOC, // flags
|
||||
0, // addr
|
||||
index, // link
|
||||
sizeof(typename TypeParam::Sym)); // entsize
|
||||
sizeof(Elf64_Sym)); // entsize
|
||||
|
||||
elf.Finish();
|
||||
this->GetElfContents(elf);
|
||||
GetElfContents(elf);
|
||||
|
||||
Module* module;
|
||||
DumpOptions options(ALL_SYMBOL_DATA, true);
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
|
||||
EXPECT_TRUE(ReadSymbolDataInternal(elfdata,
|
||||
"foo",
|
||||
vector<string>(),
|
||||
options,
|
||||
|
@ -192,13 +164,9 @@ TYPED_TEST(DumpSymbols, SimpleBuildID) {
|
|||
|
||||
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;
|
||||
EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n"
|
||||
"PUBLIC 1000 0 superfunc\n",
|
||||
s.str());
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -49,13 +49,9 @@ struct ElfClass32 {
|
|||
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 {
|
||||
|
@ -66,13 +62,9 @@ struct ElfClass64 {
|
|||
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);
|
||||
|
|
|
@ -39,20 +39,15 @@
|
|||
#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.
|
||||
|
@ -63,7 +58,7 @@ FileID::FileID(const char* path) : path_(path) {}
|
|||
|
||||
template<typename ElfClass>
|
||||
static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
|
||||
wasteful_vector<uint8_t>& identifier) {
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
typedef typename ElfClass::Nhdr Nhdr;
|
||||
|
||||
const void* section_end = reinterpret_cast<const char*>(section) + length;
|
||||
|
@ -81,19 +76,21 @@ static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
|
|||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) +
|
||||
const char* build_id = reinterpret_cast<const char*>(note_header) +
|
||||
sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz);
|
||||
identifier.insert(identifier.end(),
|
||||
build_id,
|
||||
build_id + note_header->n_descsz);
|
||||
// Copy as many bits of the build ID as will fit
|
||||
// into the GUID space.
|
||||
my_memset(identifier, 0, kMDGUIDSize);
|
||||
memcpy(identifier, build_id,
|
||||
std::min(kMDGUIDSize, (size_t)note_header->n_descsz));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Attempt to locate a .note.gnu.build-id section in an ELF binary
|
||||
// and copy it into |identifier|.
|
||||
static bool FindElfBuildIDNote(const void* elf_mapped_base,
|
||||
wasteful_vector<uint8_t>& identifier) {
|
||||
// and copy as many bytes of it as will fit into |identifier|.
|
||||
static bool FindElfBuildIDNote(const void *elf_mapped_base,
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
void* note_section;
|
||||
size_t note_size;
|
||||
int elfclass;
|
||||
|
@ -119,10 +116,8 @@ static bool FindElfBuildIDNote(const void* elf_mapped_base,
|
|||
|
||||
// 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);
|
||||
|
||||
static bool HashElfTextSection(const void *elf_mapped_base,
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
void* text_section;
|
||||
size_t text_size;
|
||||
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
|
||||
|
@ -131,9 +126,7 @@ static bool HashElfTextSection(const void* elf_mapped_base,
|
|||
return false;
|
||||
}
|
||||
|
||||
// Only provide |kMDGUIDSize| bytes to keep identifiers produced by this
|
||||
// function backwards-compatible.
|
||||
my_memset(&identifier[0], 0, kMDGUIDSize);
|
||||
my_memset(identifier, 0, kMDGUIDSize);
|
||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
|
||||
const uint8_t* ptr_end = ptr + std::min(text_size, static_cast<size_t>(4096));
|
||||
while (ptr < ptr_end) {
|
||||
|
@ -146,7 +139,7 @@ static bool HashElfTextSection(const void* elf_mapped_base,
|
|||
|
||||
// static
|
||||
bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
|
||||
wasteful_vector<uint8_t>& identifier) {
|
||||
uint8_t identifier[kMDGUIDSize]) {
|
||||
// Look for a build id note first.
|
||||
if (FindElfBuildIDNote(base, identifier))
|
||||
return true;
|
||||
|
@ -155,7 +148,7 @@ bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
|
|||
return HashElfTextSection(base, identifier);
|
||||
}
|
||||
|
||||
bool FileID::ElfFileIdentifier(wasteful_vector<uint8_t>& identifier) {
|
||||
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
|
||||
MemoryMappedFile mapped_file(path_.c_str(), 0);
|
||||
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
||||
return false;
|
||||
|
@ -163,26 +156,13 @@ bool FileID::ElfFileIdentifier(wasteful_vector<uint8_t>& identifier) {
|
|||
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 };
|
||||
void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
|
||||
char* buffer, int buffer_length) {
|
||||
uint8_t identifier_swapped[kMDGUIDSize];
|
||||
|
||||
// Endian-ness swap to match dump processor expectation.
|
||||
memcpy(identifier_swapped, &identifier[0],
|
||||
std::min(kMDGUIDSize, identifier.size()));
|
||||
memcpy(identifier_swapped, identifier, kMDGUIDSize);
|
||||
uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped);
|
||||
*data1 = htonl(*data1);
|
||||
uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4);
|
||||
|
@ -190,13 +170,22 @@ string FileID::ConvertIdentifierToUUIDString(
|
|||
uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6);
|
||||
*data3 = htons(*data3);
|
||||
|
||||
return bytes_to_hex_string(identifier_swapped, kMDGUIDSize);
|
||||
}
|
||||
int buffer_idx = 0;
|
||||
for (unsigned int idx = 0;
|
||||
(buffer_idx < buffer_length) && (idx < kMDGUIDSize);
|
||||
++idx) {
|
||||
int hi = (identifier_swapped[idx] >> 4) & 0x0F;
|
||||
int lo = (identifier_swapped[idx]) & 0x0F;
|
||||
|
||||
// static
|
||||
string FileID::ConvertIdentifierToString(
|
||||
const wasteful_vector<uint8_t>& identifier) {
|
||||
return bytes_to_hex_string(&identifier[0], identifier.size());
|
||||
if (idx == 4 || idx == 6 || idx == 8 || idx == 10)
|
||||
buffer[buffer_idx++] = '-';
|
||||
|
||||
buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi;
|
||||
buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo;
|
||||
}
|
||||
|
||||
// NULL terminate
|
||||
buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -37,15 +37,10 @@
|
|||
#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;
|
||||
static const size_t kMDGUIDSize = sizeof(MDGUID);
|
||||
|
||||
class FileID {
|
||||
public:
|
||||
|
@ -53,29 +48,25 @@ class FileID {
|
|||
~FileID() {}
|
||||
|
||||
// Load the identifier for the elf file path specified in the constructor into
|
||||
// |identifier|.
|
||||
//
|
||||
// |identifier|. Return false if the identifier could not be created for the
|
||||
// file.
|
||||
// The current implementation will look for a .note.gnu.build-id
|
||||
// section and use that as the file id, otherwise it falls back to
|
||||
// XORing the first 4096 bytes of the .text section to generate an identifier.
|
||||
bool ElfFileIdentifier(wasteful_vector<uint8_t>& identifier);
|
||||
bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]);
|
||||
|
||||
// Load the identifier for the elf file mapped into memory at |base| into
|
||||
// |identifier|. Return false if the identifier could not be created for this
|
||||
// |identifier|. Return false if the identifier could not be created for the
|
||||
// file.
|
||||
static bool ElfFileIdentifierFromMappedFile(
|
||||
const void* base,
|
||||
wasteful_vector<uint8_t>& identifier);
|
||||
static bool ElfFileIdentifierFromMappedFile(const void* base,
|
||||
uint8_t identifier[kMDGUIDSize]);
|
||||
|
||||
// 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);
|
||||
// Convert the |identifier| data to a NULL terminated string. The string will
|
||||
// be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE).
|
||||
// The |buffer| should be at least 37 bytes long to receive all of the data
|
||||
// and termination. Shorter buffers will contain truncated data.
|
||||
static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize],
|
||||
char* buffer, int buffer_length);
|
||||
|
||||
private:
|
||||
// Storage for the path specified
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/elf_gnu_compat.h"
|
||||
#include "common/linux/elfutils.h"
|
||||
|
@ -46,11 +45,13 @@
|
|||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
using google_breakpad::ElfClass32;
|
||||
using google_breakpad::ElfClass64;
|
||||
using google_breakpad::SafeReadLink;
|
||||
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 {
|
||||
|
@ -63,8 +64,6 @@ void PopulateSection(Section* section, int size, int prime_number) {
|
|||
section->Append(1, (i % prime_number) % 256);
|
||||
}
|
||||
|
||||
typedef wasteful_vector<uint8_t> id_vector;
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
@ -88,20 +87,19 @@ TEST(FileIDStripTest, StripSelf) {
|
|||
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);
|
||||
|
||||
uint8_t identifier1[sizeof(MDGUID)];
|
||||
uint8_t identifier2[sizeof(MDGUID)];
|
||||
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);
|
||||
char identifier_string1[37];
|
||||
char identifier_string2[37];
|
||||
FileID::ConvertIdentifierToString(identifier1, identifier_string1,
|
||||
37);
|
||||
FileID::ConvertIdentifierToString(identifier2, identifier_string2,
|
||||
37);
|
||||
EXPECT_STREQ(identifier_string1, identifier_string2);
|
||||
}
|
||||
#endif // !__ANDROID__
|
||||
|
||||
|
@ -118,22 +116,8 @@ public:
|
|||
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;
|
||||
|
@ -141,8 +125,10 @@ typedef Types<ElfClass32, ElfClass64> ElfClasses;
|
|||
TYPED_TEST_CASE(FileIDTest, ElfClasses);
|
||||
|
||||
TYPED_TEST(FileIDTest, ElfClass) {
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
const char expected_identifier_string[] =
|
||||
"80808080808000000000008080808080";
|
||||
"80808080-8080-0000-0000-008080808080";
|
||||
char identifier_string[sizeof(expected_identifier_string)];
|
||||
const size_t kTextSectionSize = 128;
|
||||
|
||||
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
|
||||
|
@ -154,106 +140,58 @@ TYPED_TEST(FileIDTest, ElfClass) {
|
|||
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);
|
||||
FileID::ConvertIdentifierToString(identifier, identifier_string,
|
||||
sizeof(identifier_string));
|
||||
EXPECT_STREQ(expected_identifier_string, identifier_string);
|
||||
}
|
||||
|
||||
TYPED_TEST(FileIDTest, BuildID) {
|
||||
const uint8_t kExpectedIdentifierBytes[] =
|
||||
const uint8_t kExpectedIdentifier[sizeof(MDGUID)] =
|
||||
{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);
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
|
||||
char expected_identifier_string[] =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
FileID::ConvertIdentifierToString(kExpectedIdentifier,
|
||||
expected_identifier_string,
|
||||
sizeof(expected_identifier_string));
|
||||
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
char identifier_string[sizeof(expected_identifier_string)];
|
||||
|
||||
ELF 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));
|
||||
notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier,
|
||||
sizeof(kExpectedIdentifier));
|
||||
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);
|
||||
FileID::ConvertIdentifierToString(identifier, identifier_string,
|
||||
sizeof(identifier_string));
|
||||
EXPECT_STREQ(expected_identifier_string, identifier_string);
|
||||
}
|
||||
|
||||
TYPED_TEST(FileIDTest, BuildIDPH) {
|
||||
const uint8_t kExpectedIdentifierBytes[] =
|
||||
const uint8_t kExpectedIdentifier[sizeof(MDGUID)] =
|
||||
{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);
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F};
|
||||
char expected_identifier_string[] =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
FileID::ConvertIdentifierToString(kExpectedIdentifier,
|
||||
expected_identifier_string,
|
||||
sizeof(expected_identifier_string));
|
||||
|
||||
uint8_t identifier[sizeof(MDGUID)];
|
||||
char identifier_string[sizeof(expected_identifier_string)];
|
||||
|
||||
ELF elf(EM_386, TypeParam::kClass, kLittleEndian);
|
||||
Section text(kLittleEndian);
|
||||
|
@ -262,25 +200,31 @@ TYPED_TEST(FileIDTest, BuildIDPH) {
|
|||
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));
|
||||
notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier,
|
||||
sizeof(kExpectedIdentifier));
|
||||
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);
|
||||
FileID::ConvertIdentifierToString(identifier, identifier_string,
|
||||
sizeof(identifier_string));
|
||||
EXPECT_STREQ(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) {
|
||||
char identifier_string_1[] =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
char identifier_string_2[] =
|
||||
"00000000-0000-0000-0000-000000000000";
|
||||
uint8_t identifier_1[sizeof(MDGUID)];
|
||||
uint8_t identifier_2[sizeof(MDGUID)];
|
||||
|
||||
{
|
||||
ELF elf1(EM_386, TypeParam::kClass, kLittleEndian);
|
||||
Section foo_1(kLittleEndian);
|
||||
|
@ -293,11 +237,10 @@ TYPED_TEST(FileIDTest, UniqueHashes) {
|
|||
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);
|
||||
FileID::ConvertIdentifierToString(identifier_1, identifier_string_1,
|
||||
sizeof(identifier_string_1));
|
||||
|
||||
{
|
||||
ELF elf2(EM_386, TypeParam::kClass, kLittleEndian);
|
||||
|
@ -311,28 +254,10 @@ TYPED_TEST(FileIDTest, UniqueHashes) {
|
|||
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);
|
||||
FileID::ConvertIdentifierToString(identifier_2, identifier_string_2,
|
||||
sizeof(identifier_string_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));
|
||||
EXPECT_STRNE(identifier_string_1, identifier_string_2);
|
||||
}
|
||||
|
|
|
@ -72,9 +72,7 @@ bool HTTPUpload::SendRequest(const string &url,
|
|||
// 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");
|
||||
if (!curl_lib || dlsym(curl_lib, "curl_easy_init") == NULL) {
|
||||
dlerror(); // Clear dlerror before attempting to open libraries.
|
||||
dlclose(curl_lib);
|
||||
curl_lib = NULL;
|
||||
|
@ -115,10 +113,6 @@ bool HTTPUpload::SendRequest(const string &url,
|
|||
*(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());
|
||||
|
@ -203,13 +197,6 @@ bool HTTPUpload::SendRequest(const string &url,
|
|||
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> ¶meters) {
|
||||
for (map<string, string>::const_iterator pos = parameters.begin();
|
||||
|
|
|
@ -74,9 +74,6 @@ class HTTPUpload {
|
|||
// any quote (") characters. Returns true if so.
|
||||
static bool CheckParameters(const map<string, string> ¶meters);
|
||||
|
||||
// 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();
|
||||
|
|
|
@ -35,6 +35,6 @@
|
|||
// 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)
|
||||
#define IGNORE_RET(x) do { if (x); } while (0)
|
||||
|
||||
#endif // COMMON_LINUX_IGNORE_RET_H_
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace google_breakpad {
|
|||
class LibcurlWrapper {
|
||||
public:
|
||||
LibcurlWrapper();
|
||||
virtual ~LibcurlWrapper();
|
||||
~LibcurlWrapper();
|
||||
virtual bool Init();
|
||||
virtual bool SetProxy(const string& proxy_host,
|
||||
const string& proxy_userpwd);
|
||||
|
|
|
@ -87,7 +87,18 @@ bool MemoryMappedFile::Map(const char* path, size_t offset) {
|
|||
return true;
|
||||
}
|
||||
|
||||
#if defined(__x86_64__) || defined(__aarch64__) || \
|
||||
(defined(__mips__) && _MIPS_SIM == _ABI64)
|
||||
void* data = sys_mmap(NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset);
|
||||
#else
|
||||
if ((offset & 4095) != 0) {
|
||||
// Not page aligned.
|
||||
sys_close(fd);
|
||||
return false;
|
||||
}
|
||||
void* data = sys_mmap2(
|
||||
NULL, file_len, PROT_READ, MAP_PRIVATE, fd, offset >> 12);
|
||||
#endif
|
||||
sys_close(fd);
|
||||
if (data == MAP_FAILED) {
|
||||
return false;
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
// 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
|
|
@ -1,59 +0,0 @@
|
|||
// -*- 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_
|
|
@ -213,10 +213,8 @@ void ELF::Finish() {
|
|||
SymbolTable::SymbolTable(Endianness endianness,
|
||||
size_t addr_size,
|
||||
StringTable& table) : Section(endianness),
|
||||
addr_size_(addr_size),
|
||||
table_(table) {
|
||||
#ifndef NDEBUG
|
||||
addr_size_ = addr_size;
|
||||
#endif
|
||||
assert(addr_size_ == 4 || addr_size_ == 8);
|
||||
}
|
||||
|
||||
|
|
|
@ -173,9 +173,7 @@ class SymbolTable : public Section {
|
|||
uint64_t size, unsigned info, uint16_t shndx);
|
||||
|
||||
private:
|
||||
#ifndef NDEBUG
|
||||
size_t addr_size_;
|
||||
#endif
|
||||
StringTable& table_;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
ARCHS = $(ARCHS_STANDARD_32_64_BIT)
|
||||
SDKROOT = macosx10.5
|
||||
|
||||
GCC_VERSION = 4.2
|
||||
GCC_VERSION[sdk=macosx10.4][arch=*] = 4.0
|
||||
|
||||
GCC_C_LANGUAGE_STANDARD = c99
|
||||
|
||||
GCC_WARN_CHECK_SWITCH_STATEMENTS = YES
|
||||
|
@ -35,10 +41,7 @@ GCC_WARN_64_TO_32_BIT_CONVERSION = NO
|
|||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES
|
||||
GCC_WARN_MISSING_PARENTHESES = YES
|
||||
|
||||
// Once https://bugs.chromium.org/p/google-breakpad/issues/detail?id=697
|
||||
// is fixed this should be reenabled.
|
||||
//GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES
|
||||
GCC_WARN_SIGN_COMPARE = YES
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES
|
||||
|
@ -48,5 +51,6 @@ GCC_WARN_UNUSED_VARIABLE = YES
|
|||
GCC_TREAT_WARNINGS_AS_ERRORS = YES
|
||||
|
||||
DEBUG_INFORMATION_FORMAT = dwarf-with-dsym
|
||||
DEBUG_INFORMATION_FORMAT[sdk=macosx10.4][arch=*] = stabs
|
||||
|
||||
ALWAYS_SEARCH_USER_PATHS = NO
|
||||
|
|
|
@ -30,65 +30,6 @@
|
|||
#import "HTTPMultipartUpload.h"
|
||||
#import "GTMDefines.h"
|
||||
|
||||
// As -[NSString stringByAddingPercentEscapesUsingEncoding:] has been
|
||||
// deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements it
|
||||
// using -[NSString stringByAddingPercentEncodingWithAllowedCharacters:] when
|
||||
// using those SDKs.
|
||||
static NSString *PercentEncodeNSString(NSString *key) {
|
||||
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_9_0) && \
|
||||
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0) || \
|
||||
(defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
|
||||
defined(MAC_OS_X_VERSION_10_11) && \
|
||||
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
|
||||
return [key stringByAddingPercentEncodingWithAllowedCharacters:
|
||||
[NSCharacterSet URLQueryAllowedCharacterSet]];
|
||||
#else
|
||||
return [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
#endif
|
||||
}
|
||||
|
||||
// As -[NSURLConnection sendSynchronousRequest:returningResponse:error:] has
|
||||
// been deprecated with iOS 9.0 / OS X 10.11 SDKs, this function re-implements
|
||||
// it using -[NSURLSession dataTaskWithRequest:completionHandler:] when using
|
||||
// those SDKs.
|
||||
static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
||||
NSURLResponse **out_response,
|
||||
NSError **out_error) {
|
||||
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_9_0) && \
|
||||
__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_9_0) || \
|
||||
(defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
|
||||
defined(MAC_OS_X_VERSION_10_11) && \
|
||||
MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11)
|
||||
__block NSData* result = nil;
|
||||
__block NSError* error = nil;
|
||||
__block NSURLResponse* response = nil;
|
||||
dispatch_semaphore_t wait_semaphone = dispatch_semaphore_create(0);
|
||||
[[[NSURLSession sharedSession]
|
||||
dataTaskWithRequest:req
|
||||
completionHandler:^(NSData *data,
|
||||
NSURLResponse *resp,
|
||||
NSError *err) {
|
||||
if (out_error)
|
||||
error = [err retain];
|
||||
if (out_response)
|
||||
response = [resp retain];
|
||||
if (err == nil)
|
||||
result = [data retain];
|
||||
dispatch_semaphore_signal(wait_semaphone);
|
||||
}] resume];
|
||||
dispatch_semaphore_wait(wait_semaphone, DISPATCH_TIME_FOREVER);
|
||||
dispatch_release(wait_semaphone);
|
||||
if (out_error)
|
||||
*out_error = [error autorelease];
|
||||
if (out_response)
|
||||
*out_response = [response autorelease];
|
||||
return [result autorelease];
|
||||
#else
|
||||
return [NSURLConnection sendSynchronousRequest:req
|
||||
returningResponse:out_response
|
||||
error:out_error];
|
||||
#endif
|
||||
}
|
||||
@interface HTTPMultipartUpload(PrivateMethods)
|
||||
- (NSString *)multipartBoundary;
|
||||
// Each of the following methods will append the starting multipart boundary,
|
||||
|
@ -111,7 +52,8 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
|||
|
||||
//=============================================================================
|
||||
- (NSData *)formDataForKey:(NSString *)key value:(NSString *)value {
|
||||
NSString *escaped = PercentEncodeNSString(key);
|
||||
NSString *escaped =
|
||||
[key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *fmt =
|
||||
@"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n";
|
||||
NSString *form = [NSString stringWithFormat:fmt, boundary_, escaped, value];
|
||||
|
@ -122,7 +64,8 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
|||
//=============================================================================
|
||||
- (NSData *)formDataForFileContents:(NSData *)contents name:(NSString *)name {
|
||||
NSMutableData *data = [NSMutableData data];
|
||||
NSString *escaped = PercentEncodeNSString(name);
|
||||
NSString *escaped =
|
||||
[name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||
NSString *fmt = @"--%@\r\nContent-Disposition: form-data; name=\"%@\"; "
|
||||
"filename=\"minidump.dmp\"\r\nContent-Type: application/octet-stream\r\n\r\n";
|
||||
NSString *pre = [NSString stringWithFormat:fmt, boundary_, escaped];
|
||||
|
@ -253,7 +196,9 @@ static NSData *SendSynchronousNSURLRequest(NSURLRequest *req,
|
|||
[[req HTTPBody] writeToURL:[req URL] options:0 error:error];
|
||||
} else {
|
||||
NSURLResponse *response = nil;
|
||||
data = SendSynchronousNSURLRequest(req, &response, error);
|
||||
data = [NSURLConnection sendSynchronousRequest:req
|
||||
returningResponse:&response
|
||||
error:error];
|
||||
response_ = (NSHTTPURLResponse *)[response retain];
|
||||
}
|
||||
[req release];
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
#include <libgen.h>
|
||||
#include <mach-o/arch.h>
|
||||
#include <mach-o/fat.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -317,7 +316,7 @@ class DumpSymbols::DumperLineToModule:
|
|||
compilation_dir_ = compilation_dir;
|
||||
}
|
||||
|
||||
void ReadProgram(const uint8_t *program, uint64 length,
|
||||
void ReadProgram(const char *program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines) {
|
||||
DwarfLineToModule handler(module, compilation_dir_, lines);
|
||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||
|
@ -328,65 +327,6 @@ class DumpSymbols::DumperLineToModule:
|
|||
dwarf2reader::ByteReader *byte_reader_; // WEAK
|
||||
};
|
||||
|
||||
bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) {
|
||||
// Select an object file, if SetArchitecture hasn't been called to set one
|
||||
// explicitly.
|
||||
if (!selected_object_file_) {
|
||||
// If there's only one architecture, that's the one.
|
||||
if (object_files_.size() == 1)
|
||||
selected_object_file_ = &object_files_[0];
|
||||
else {
|
||||
// Look for an object file whose architecture matches our own.
|
||||
const NXArchInfo *local_arch = NXGetLocalArchInfo();
|
||||
if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) {
|
||||
fprintf(stderr, "%s: object file contains more than one"
|
||||
" architecture, none of which match the current"
|
||||
" architecture; specify an architecture explicitly"
|
||||
" with '-a ARCH' to resolve the ambiguity\n",
|
||||
object_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(selected_object_file_);
|
||||
|
||||
// Find the name of the selected file's architecture, to appear in
|
||||
// the MODULE record and in error messages.
|
||||
const NXArchInfo *selected_arch_info =
|
||||
google_breakpad::BreakpadGetArchInfoFromCpuType(
|
||||
selected_object_file_->cputype, selected_object_file_->cpusubtype);
|
||||
|
||||
const char *selected_arch_name = selected_arch_info->name;
|
||||
if (strcmp(selected_arch_name, "i386") == 0)
|
||||
selected_arch_name = "x86";
|
||||
|
||||
// Produce a name to use in error messages that includes the
|
||||
// filename, and the architecture, if there is more than one.
|
||||
selected_object_name_ = object_filename_;
|
||||
if (object_files_.size() > 1) {
|
||||
selected_object_name_ += ", architecture ";
|
||||
selected_object_name_ + selected_arch_name;
|
||||
}
|
||||
|
||||
// Compute a module name, to appear in the MODULE record.
|
||||
string module_name = object_filename_;
|
||||
module_name = basename(&module_name[0]);
|
||||
|
||||
// Choose an identifier string, to appear in the MODULE record.
|
||||
string identifier = Identifier();
|
||||
if (identifier.empty())
|
||||
return false;
|
||||
identifier += "0";
|
||||
|
||||
// Create a module to hold the debugging information.
|
||||
module.reset(new Module(module_name,
|
||||
"mac",
|
||||
selected_arch_name,
|
||||
identifier));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
|
||||
const mach_o::Reader &macho_reader,
|
||||
const mach_o::SectionMap &dwarf_sections,
|
||||
|
@ -406,7 +346,7 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
|
|||
it != dwarf_sections.end(); ++it) {
|
||||
file_context.AddSectionToSectionMap(
|
||||
it->first,
|
||||
it->second.contents.start,
|
||||
reinterpret_cast<const char *>(it->second.contents.start),
|
||||
it->second.contents.Size());
|
||||
}
|
||||
|
||||
|
@ -414,7 +354,7 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
|
|||
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 =
|
||||
const std::pair<const char*, uint64>& debug_info_section =
|
||||
debug_info_entry->second;
|
||||
// There had better be a __debug_info section!
|
||||
if (!debug_info_section.first) {
|
||||
|
@ -437,8 +377,7 @@ bool DumpSymbols::ReadDwarf(google_breakpad::Module *module,
|
|||
// Make a Dwarf2Handler that drives our DIEHandler.
|
||||
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
|
||||
// Make a DWARF parser for the compilation unit at OFFSET.
|
||||
dwarf2reader::CompilationUnit dwarf_reader(selected_object_name_,
|
||||
file_context.section_map(),
|
||||
dwarf2reader::CompilationUnit dwarf_reader(file_context.section_map(),
|
||||
offset,
|
||||
&byte_reader,
|
||||
&die_dispatcher);
|
||||
|
@ -485,7 +424,7 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module *module,
|
|||
}
|
||||
|
||||
// Find the call frame information and its size.
|
||||
const uint8_t *cfi = section.contents.start;
|
||||
const char *cfi = reinterpret_cast<const char *>(section.contents.start);
|
||||
size_t cfi_size = section.contents.Size();
|
||||
|
||||
// Plug together the parser, handler, and their entourages.
|
||||
|
@ -595,9 +534,61 @@ bool DumpSymbols::LoadCommandDumper::SymtabCommand(const ByteBuffer &entries,
|
|||
}
|
||||
|
||||
bool DumpSymbols::ReadSymbolData(Module** out_module) {
|
||||
scoped_ptr<Module> module;
|
||||
if (!CreateEmptyModule(module))
|
||||
// Select an object file, if SetArchitecture hasn't been called to set one
|
||||
// explicitly.
|
||||
if (!selected_object_file_) {
|
||||
// If there's only one architecture, that's the one.
|
||||
if (object_files_.size() == 1)
|
||||
selected_object_file_ = &object_files_[0];
|
||||
else {
|
||||
// Look for an object file whose architecture matches our own.
|
||||
const NXArchInfo *local_arch = NXGetLocalArchInfo();
|
||||
if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) {
|
||||
fprintf(stderr, "%s: object file contains more than one"
|
||||
" architecture, none of which match the current"
|
||||
" architecture; specify an architecture explicitly"
|
||||
" with '-a ARCH' to resolve the ambiguity\n",
|
||||
object_filename_.c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(selected_object_file_);
|
||||
|
||||
// Find the name of the selected file's architecture, to appear in
|
||||
// the MODULE record and in error messages.
|
||||
const NXArchInfo *selected_arch_info =
|
||||
google_breakpad::BreakpadGetArchInfoFromCpuType(
|
||||
selected_object_file_->cputype, selected_object_file_->cpusubtype);
|
||||
|
||||
const char *selected_arch_name = selected_arch_info->name;
|
||||
if (strcmp(selected_arch_name, "i386") == 0)
|
||||
selected_arch_name = "x86";
|
||||
|
||||
// Produce a name to use in error messages that includes the
|
||||
// filename, and the architecture, if there is more than one.
|
||||
selected_object_name_ = object_filename_;
|
||||
if (object_files_.size() > 1) {
|
||||
selected_object_name_ += ", architecture ";
|
||||
selected_object_name_ + selected_arch_name;
|
||||
}
|
||||
|
||||
// Compute a module name, to appear in the MODULE record.
|
||||
string module_name = object_filename_;
|
||||
module_name = basename(&module_name[0]);
|
||||
|
||||
// Choose an identifier string, to appear in the MODULE record.
|
||||
string identifier = Identifier();
|
||||
if (identifier.empty())
|
||||
return false;
|
||||
identifier += "0";
|
||||
|
||||
// Create a module to hold the debugging information.
|
||||
scoped_ptr<Module> module(new Module(module_name,
|
||||
"mac",
|
||||
selected_arch_name,
|
||||
identifier));
|
||||
|
||||
// Parse the selected object file.
|
||||
mach_o::Reader::Reporter reporter(selected_object_name_);
|
||||
|
@ -632,15 +623,4 @@ bool DumpSymbols::WriteSymbolFile(std::ostream &stream) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 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 DumpSymbols::WriteSymbolFileHeader(std::ostream &stream) {
|
||||
scoped_ptr<Module> module;
|
||||
if (!CreateEmptyModule(module))
|
||||
return false;
|
||||
|
||||
return module->Write(stream, symbol_data_);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -112,11 +112,6 @@ class DumpSymbols {
|
|||
// return false.
|
||||
bool WriteSymbolFile(std::ostream &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(std::ostream &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.
|
||||
|
@ -135,10 +130,6 @@ class DumpSymbols {
|
|||
// Return an identifier string for the file this DumpSymbols is dumping.
|
||||
std::string Identifier();
|
||||
|
||||
|
||||
// Creates an empty module object.
|
||||
bool CreateEmptyModule(scoped_ptr<Module>& module);
|
||||
|
||||
// Read debugging information from |dwarf_sections|, which was taken from
|
||||
// |macho_reader|, and add it to |module|. On success, return true;
|
||||
// on failure, report the problem and return false.
|
||||
|
|
|
@ -181,15 +181,15 @@ void Reader::Reporter::LoadCommandRegionTruncated() {
|
|||
|
||||
void Reader::Reporter::LoadCommandsOverrun(size_t claimed, size_t i,
|
||||
LoadCommandType type) {
|
||||
fprintf(stderr, "%s: file's header claims there are %zu"
|
||||
" load commands, but load command #%zu",
|
||||
fprintf(stderr, "%s: file's header claims there are %ld"
|
||||
" load commands, but load command #%ld",
|
||||
filename_.c_str(), claimed, i);
|
||||
if (type) fprintf(stderr, ", of type %d,", type);
|
||||
fprintf(stderr, " extends beyond the end of the load command region\n");
|
||||
}
|
||||
|
||||
void Reader::Reporter::LoadCommandTooShort(size_t i, LoadCommandType type) {
|
||||
fprintf(stderr, "%s: the contents of load command #%zu, of type %d,"
|
||||
fprintf(stderr, "%s: the contents of load command #%ld, of type %d,"
|
||||
" extend beyond the size given in the load command's header\n",
|
||||
filename_.c_str(), i, type);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ if CONFIG['MOZ_CRASHREPORTER']:
|
|||
HOST_CXXFLAGS += [
|
||||
'-O2',
|
||||
'-g',
|
||||
'-stdlib=libc++',
|
||||
]
|
||||
HostLibrary('host_breakpad_mac_common_s')
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#import "GTMObjC2Runtime.h"
|
||||
#import "GTMUnitTestDevLog.h"
|
||||
|
||||
#if !GTM_IPHONE_SDK
|
||||
#import "GTMGarbageCollection.h"
|
||||
#endif // !GTM_IPHONE_SDK
|
||||
|
||||
#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST
|
||||
#import <stdarg.h>
|
||||
|
||||
|
@ -426,3 +430,71 @@ static int MethodSort(id a, id b, void *context) {
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
// Leak detection
|
||||
#if !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK
|
||||
// Don't want to get leaks on the iPhone Device as the device doesn't
|
||||
// have 'leaks'. The simulator does though.
|
||||
|
||||
// COV_NF_START
|
||||
// We don't have leak checking on by default, so this won't be hit.
|
||||
static void _GTMRunLeaks(void) {
|
||||
// This is an atexit handler. It runs leaks for us to check if we are
|
||||
// leaking anything in our tests.
|
||||
const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE");
|
||||
NSMutableString *exclusions = [NSMutableString string];
|
||||
if (cExclusionsEnv) {
|
||||
NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv];
|
||||
NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","];
|
||||
NSString *exclusion;
|
||||
NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet];
|
||||
GTM_FOREACH_OBJECT(exclusion, exclusionsArray) {
|
||||
exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet];
|
||||
[exclusions appendFormat:@"-exclude \"%@\" ", exclusion];
|
||||
}
|
||||
}
|
||||
// Clearing out DYLD_ROOT_PATH because iPhone Simulator framework libraries
|
||||
// are different from regular OS X libraries and leaks will fail to run
|
||||
// because of missing symbols. Also capturing the output of leaks and then
|
||||
// pipe rather than a direct pipe, because otherwise if leaks failed,
|
||||
// the system() call will still be successful. Bug:
|
||||
// http://code.google.com/p/google-toolbox-for-mac/issues/detail?id=56
|
||||
NSString *string
|
||||
= [NSString stringWithFormat:
|
||||
@"LeakOut=`DYLD_ROOT_PATH='' /usr/bin/leaks %@%d` &&"
|
||||
@"echo \"$LeakOut\"|/usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'",
|
||||
exclusions, getpid()];
|
||||
int ret = system([string UTF8String]);
|
||||
if (ret) {
|
||||
fprintf(stderr,
|
||||
"%s:%d: Error: Unable to run leaks. 'system' returned: %d\n",
|
||||
__FILE__, __LINE__, ret);
|
||||
fflush(stderr);
|
||||
}
|
||||
}
|
||||
// COV_NF_END
|
||||
|
||||
static __attribute__((constructor)) void _GTMInstallLeaks(void) {
|
||||
BOOL checkLeaks = YES;
|
||||
#if !GTM_IPHONE_SDK
|
||||
checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES;
|
||||
#endif // !GTM_IPHONE_SDK
|
||||
if (checkLeaks) {
|
||||
checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO;
|
||||
if (checkLeaks) {
|
||||
// COV_NF_START
|
||||
// We don't have leak checking on by default, so this won't be hit.
|
||||
fprintf(stderr, "Leak Checking Enabled\n");
|
||||
fflush(stderr);
|
||||
int ret = atexit(&_GTMRunLeaks);
|
||||
// To avoid unused variable warning when _GTMDevAssert is stripped.
|
||||
(void)ret;
|
||||
_GTMDevAssert(ret == 0,
|
||||
@"Unable to install _GTMRunLeaks as an atexit handler (%d)",
|
||||
errno);
|
||||
// COV_NF_END
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !GTM_IPHONE_DEVICE && !GTM_SUPPRESS_RUN_LEAKS_HOOK
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
|
||||
#ifdef __APPLE__
|
||||
#define sys_mmap mmap
|
||||
#define sys_mmap2 mmap
|
||||
#define sys_munmap munmap
|
||||
#define MAP_ANONYMOUS MAP_ANON
|
||||
#else
|
||||
|
@ -63,8 +64,7 @@ class PageAllocator {
|
|||
: page_size_(getpagesize()),
|
||||
last_(NULL),
|
||||
current_page_(NULL),
|
||||
page_offset_(0),
|
||||
pages_allocated_(0) {
|
||||
page_offset_(0) {
|
||||
}
|
||||
|
||||
~PageAllocator() {
|
||||
|
@ -112,12 +112,16 @@ class PageAllocator {
|
|||
return false;
|
||||
}
|
||||
|
||||
unsigned long pages_allocated() { return pages_allocated_; }
|
||||
|
||||
private:
|
||||
uint8_t *GetNPages(size_t num_pages) {
|
||||
#if defined(__x86_64__) || defined(__aarch64__) || defined(__aarch64__) || \
|
||||
((defined(__mips__) && _MIPS_SIM == _ABI64))
|
||||
void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
#else
|
||||
void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
#endif
|
||||
if (a == MAP_FAILED)
|
||||
return NULL;
|
||||
|
||||
|
@ -132,8 +136,6 @@ class PageAllocator {
|
|||
header->num_pages = num_pages;
|
||||
last_ = header;
|
||||
|
||||
pages_allocated_ += num_pages;
|
||||
|
||||
return reinterpret_cast<uint8_t*>(a);
|
||||
}
|
||||
|
||||
|
@ -155,7 +157,6 @@ class PageAllocator {
|
|||
PageHeader *last_;
|
||||
uint8_t *current_page_;
|
||||
size_t page_offset_;
|
||||
unsigned long pages_allocated_;
|
||||
};
|
||||
|
||||
// Wrapper to use with STL containers
|
||||
|
@ -164,30 +165,12 @@ struct PageStdAllocator : public std::allocator<T> {
|
|||
typedef typename std::allocator<T>::pointer pointer;
|
||||
typedef typename std::allocator<T>::size_type size_type;
|
||||
|
||||
explicit PageStdAllocator(PageAllocator& allocator) : allocator_(allocator),
|
||||
stackdata_(NULL),
|
||||
stackdata_size_(0)
|
||||
{}
|
||||
|
||||
explicit PageStdAllocator(PageAllocator& allocator): allocator_(allocator) {}
|
||||
template <class Other> PageStdAllocator(const PageStdAllocator<Other>& other)
|
||||
: allocator_(other.allocator_),
|
||||
stackdata_(nullptr),
|
||||
stackdata_size_(0)
|
||||
{}
|
||||
|
||||
explicit PageStdAllocator(PageAllocator& allocator,
|
||||
pointer stackdata,
|
||||
size_type stackdata_size) : allocator_(allocator),
|
||||
stackdata_(stackdata),
|
||||
stackdata_size_(stackdata_size)
|
||||
{}
|
||||
: allocator_(other.allocator_) {}
|
||||
|
||||
inline pointer allocate(size_type n, const void* = 0) {
|
||||
const size_type size = sizeof(T) * n;
|
||||
if (size <= stackdata_size_) {
|
||||
return stackdata_;
|
||||
}
|
||||
return static_cast<pointer>(allocator_.Alloc(size));
|
||||
return static_cast<pointer>(allocator_.Alloc(sizeof(T) * n));
|
||||
}
|
||||
|
||||
inline void deallocate(pointer, size_type) {
|
||||
|
@ -205,8 +188,6 @@ struct PageStdAllocator : public std::allocator<T> {
|
|||
template<typename Other> friend struct PageStdAllocator;
|
||||
|
||||
PageAllocator& allocator_;
|
||||
pointer stackdata_;
|
||||
size_type stackdata_size_;
|
||||
};
|
||||
|
||||
// A wasteful vector is a std::vector, except that it allocates memory from a
|
||||
|
@ -219,24 +200,6 @@ class wasteful_vector : public std::vector<T, PageStdAllocator<T> > {
|
|||
: std::vector<T, PageStdAllocator<T> >(PageStdAllocator<T>(*allocator)) {
|
||||
std::vector<T, PageStdAllocator<T> >::reserve(size_hint);
|
||||
}
|
||||
protected:
|
||||
wasteful_vector(PageStdAllocator<T> allocator)
|
||||
: std::vector<T, PageStdAllocator<T> >(allocator) {}
|
||||
};
|
||||
|
||||
// auto_wasteful_vector allocates space on the stack for N entries to avoid
|
||||
// using the PageAllocator for small data, while still allowing for larger data.
|
||||
template<class T, unsigned int N>
|
||||
class auto_wasteful_vector : public wasteful_vector<T> {
|
||||
T stackdata_[N];
|
||||
public:
|
||||
auto_wasteful_vector(PageAllocator* allocator)
|
||||
: wasteful_vector<T>(
|
||||
PageStdAllocator<T>(*allocator,
|
||||
&stackdata_[0],
|
||||
sizeof(stackdata_))) {
|
||||
std::vector<T, PageStdAllocator<T> >::reserve(N);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -38,13 +38,11 @@ typedef testing::Test PageAllocatorTest;
|
|||
|
||||
TEST(PageAllocatorTest, Setup) {
|
||||
PageAllocator allocator;
|
||||
EXPECT_EQ(0U, allocator.pages_allocated());
|
||||
}
|
||||
|
||||
TEST(PageAllocatorTest, SmallObjects) {
|
||||
PageAllocator allocator;
|
||||
|
||||
EXPECT_EQ(0U, allocator.pages_allocated());
|
||||
for (unsigned i = 1; i < 1024; ++i) {
|
||||
uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i));
|
||||
ASSERT_FALSE(p == NULL);
|
||||
|
@ -55,10 +53,8 @@ TEST(PageAllocatorTest, SmallObjects) {
|
|||
TEST(PageAllocatorTest, LargeObject) {
|
||||
PageAllocator allocator;
|
||||
|
||||
EXPECT_EQ(0U, allocator.pages_allocated());
|
||||
uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(10000));
|
||||
ASSERT_FALSE(p == NULL);
|
||||
EXPECT_EQ(3U, allocator.pages_allocated());
|
||||
for (unsigned i = 1; i < 10; ++i) {
|
||||
uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i));
|
||||
ASSERT_FALSE(p == NULL);
|
||||
|
@ -79,7 +75,6 @@ TEST(WastefulVectorTest, Setup) {
|
|||
|
||||
TEST(WastefulVectorTest, Simple) {
|
||||
PageAllocator allocator_;
|
||||
EXPECT_EQ(0U, allocator_.pages_allocated());
|
||||
wasteful_vector<unsigned> v(&allocator_);
|
||||
|
||||
for (unsigned i = 0; i < 256; ++i) {
|
||||
|
@ -89,7 +84,6 @@ TEST(WastefulVectorTest, Simple) {
|
|||
}
|
||||
ASSERT_FALSE(v.empty());
|
||||
ASSERT_EQ(v.size(), 256u);
|
||||
EXPECT_EQ(1U, allocator_.pages_allocated());
|
||||
for (unsigned i = 0; i < 256; ++i)
|
||||
ASSERT_EQ(v[i], i);
|
||||
}
|
||||
|
@ -97,28 +91,7 @@ TEST(WastefulVectorTest, Simple) {
|
|||
TEST(WastefulVectorTest, UsesPageAllocator) {
|
||||
PageAllocator allocator_;
|
||||
wasteful_vector<unsigned> v(&allocator_);
|
||||
EXPECT_EQ(1U, allocator_.pages_allocated());
|
||||
|
||||
v.push_back(1);
|
||||
ASSERT_TRUE(allocator_.OwnsPointer(&v[0]));
|
||||
}
|
||||
|
||||
TEST(WastefulVectorTest, AutoWastefulVector) {
|
||||
PageAllocator allocator_;
|
||||
EXPECT_EQ(0U, allocator_.pages_allocated());
|
||||
|
||||
auto_wasteful_vector<unsigned, 4> v(&allocator_);
|
||||
EXPECT_EQ(0U, allocator_.pages_allocated());
|
||||
|
||||
v.push_back(1);
|
||||
EXPECT_EQ(0U, allocator_.pages_allocated());
|
||||
EXPECT_FALSE(allocator_.OwnsPointer(&v[0]));
|
||||
|
||||
v.resize(4);
|
||||
EXPECT_EQ(0U, allocator_.pages_allocated());
|
||||
EXPECT_FALSE(allocator_.OwnsPointer(&v[0]));
|
||||
|
||||
v.resize(10);
|
||||
EXPECT_EQ(1U, allocator_.pages_allocated());
|
||||
EXPECT_TRUE(allocator_.OwnsPointer(&v[0]));
|
||||
}
|
||||
|
|
|
@ -49,13 +49,11 @@ using std::hex;
|
|||
|
||||
|
||||
Module::Module(const string &name, const string &os,
|
||||
const string &architecture, const string &id,
|
||||
const string &code_id /* = "" */) :
|
||||
const string &architecture, const string &id) :
|
||||
name_(name),
|
||||
os_(os),
|
||||
architecture_(architecture),
|
||||
id_(id),
|
||||
code_id_(code_id),
|
||||
load_address_(0) { }
|
||||
|
||||
Module::~Module() {
|
||||
|
@ -260,10 +258,6 @@ bool Module::Write(std::ostream &stream, SymbolData symbol_data) {
|
|||
if (!stream.good())
|
||||
return ReportError();
|
||||
|
||||
if (!code_id_.empty()) {
|
||||
stream << "INFO CODE_ID " << code_id_ << endl;
|
||||
}
|
||||
|
||||
if (symbol_data != ONLY_CFI) {
|
||||
AssignSourceIds();
|
||||
|
||||
|
|
|
@ -186,7 +186,7 @@ class Module {
|
|||
// Create a new module with the given name, operating system,
|
||||
// architecture, and ID string.
|
||||
Module(const string &name, const string &os, const string &architecture,
|
||||
const string &id, const string &code_id = "");
|
||||
const string &id);
|
||||
~Module();
|
||||
|
||||
// Set the module's load address to LOAD_ADDRESS; addresses given
|
||||
|
@ -292,7 +292,6 @@ class Module {
|
|||
string os() const { return os_; }
|
||||
string architecture() const { return architecture_; }
|
||||
string identifier() const { return id_; }
|
||||
string code_identifier() const { return code_id_; }
|
||||
|
||||
private:
|
||||
// Report an error that has occurred writing the symbol file, using
|
||||
|
@ -305,7 +304,7 @@ class Module {
|
|||
static bool WriteRuleMap(const RuleMap &rule_map, std::ostream &stream);
|
||||
|
||||
// Module header entries.
|
||||
string name_, os_, architecture_, id_, code_id_;
|
||||
string name_, os_, architecture_, id_;
|
||||
|
||||
// The module's nominal load address. Addresses for functions and
|
||||
// lines are absolute, assuming the module is loaded at this
|
||||
|
|
|
@ -64,7 +64,6 @@ static Module::Function *generate_duplicate_function(const string &name) {
|
|||
#define MODULE_OS "os-name"
|
||||
#define MODULE_ARCH "architecture"
|
||||
#define MODULE_ID "id-string"
|
||||
#define MODULE_CODE_ID "code-id-string"
|
||||
|
||||
TEST(Write, Header) {
|
||||
stringstream s;
|
||||
|
@ -75,16 +74,6 @@ TEST(Write, Header) {
|
|||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, HeaderCodeId) {
|
||||
stringstream s;
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, MODULE_CODE_ID);
|
||||
m.Write(s, ALL_SYMBOL_DATA);
|
||||
string contents = s.str();
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"INFO CODE_ID code-id-string\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, OneLineFunc) {
|
||||
stringstream s;
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
|
|
@ -43,10 +43,6 @@ if CONFIG['OS_ARCH'] != 'WINNT':
|
|||
'module.cc',
|
||||
'string_conversion.cc',
|
||||
]
|
||||
if CONFIG['OS_ARCH'] == 'Darwin':
|
||||
HOST_CXXFLAGS += [
|
||||
'-stdlib=libc++',
|
||||
]
|
||||
HOST_CXXFLAGS += [
|
||||
'-O2',
|
||||
'-g',
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
// Copyright (c) 2016, 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 GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
|
||||
#define GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(_MSC_VER) && MSC_VER < 1900
|
||||
#include <basetsd.h>
|
||||
|
||||
#define snprintf _snprintf
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H
|
|
@ -210,7 +210,7 @@ bool FindAndLoadOmapTable(const wchar_t* name,
|
|||
reinterpret_cast<BYTE*>(&table->at(0)),
|
||||
&count_read))) {
|
||||
fprintf(stderr, "IDiaEnumDebugStreamData::Next failed while reading "
|
||||
"data from stream \"%ws\"\n", name);
|
||||
"data from stream \"%ws\"\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
/* Define to 1 if you have the <a.out.h> header file. */
|
||||
#undef HAVE_A_OUT_H
|
||||
|
||||
/* define if the compiler supports basic C++11 syntax */
|
||||
#undef HAVE_CXX11
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
|
|
|
@ -40,12 +40,6 @@
|
|||
#ifndef GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__
|
||||
#define GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__
|
||||
|
||||
#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && \
|
||||
!defined(__STDC_FORMAT_MACROS)
|
||||
#error "inttypes.h has already been included before this header file, but "
|
||||
#error "without __STDC_FORMAT_MACROS defined."
|
||||
#endif
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif /* __STDC_FORMAT_MACROS */
|
||||
|
|
|
@ -157,20 +157,4 @@ enum MDMIPSRegisterNumbers {
|
|||
MD_CONTEXT_MIPS_FLOATING_POINT \
|
||||
MD_CONTEXT_MIPS_DSP)
|
||||
|
||||
/**
|
||||
* Breakpad defines for MIPS64
|
||||
*/
|
||||
#define MD_CONTEXT_MIPS64 0x00080000
|
||||
#define MD_CONTEXT_MIPS64_INTEGER (MD_CONTEXT_MIPS64 | 0x00000002)
|
||||
#define MD_CONTEXT_MIPS64_FLOATING_POINT (MD_CONTEXT_MIPS64 | 0x00000004)
|
||||
#define MD_CONTEXT_MIPS64_DSP (MD_CONTEXT_MIPS64 | 0x00000008)
|
||||
|
||||
#define MD_CONTEXT_MIPS64_FULL (MD_CONTEXT_MIPS64_INTEGER | \
|
||||
MD_CONTEXT_MIPS64_FLOATING_POINT | \
|
||||
MD_CONTEXT_MIPS64_DSP)
|
||||
|
||||
#define MD_CONTEXT_MIPS64_ALL (MD_CONTEXT_MIPS64_INTEGER | \
|
||||
MD_CONTEXT_MIPS64_FLOATING_POINT \
|
||||
MD_CONTEXT_MIPS64_DSP)
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_MIPS_H__
|
||||
|
|
|
@ -100,9 +100,6 @@ typedef enum {
|
|||
/* STATUS_STACK_BUFFER_OVERRUN */
|
||||
MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION = 0xc0000374,
|
||||
/* STATUS_HEAP_CORRUPTION */
|
||||
MD_EXCEPTION_OUT_OF_MEMORY = 0xe0000008,
|
||||
/* Exception thrown by Chromium allocators to indicate OOM.
|
||||
See base/process/memory.h in Chromium for rationale. */
|
||||
MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363
|
||||
/* Per http://support.microsoft.com/kb/185294,
|
||||
generated by Visual C++ compiler */
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче