Added stripped down google-breakpad repo.

This commit is contained in:
Asher Baker 2013-05-16 07:23:05 +01:00
parent bad8c99a0b
commit 744ac40f3c
204 changed files with 89382 additions and 1 deletions

View File

@ -3,7 +3,7 @@
#define _AUTO_VERSION_INFORMATION_H_
#define SM_BUILD_TAG ""
#define SM_BUILD_UNIQUEID "38:7b77e54e78c1" SM_BUILD_TAG
#define SM_BUILD_UNIQUEID "0:fcdef8474873" SM_BUILD_TAG
#define SM_VERSION "1.0.0"
#define SM_FULL_VERSION SM_VERSION SM_BUILD_TAG
#define SM_FILE_VERSION 1,0,0,0

1
google-breakpad/AUTHORS Normal file
View File

@ -0,0 +1 @@
opensource@google.com

28
google-breakpad/COPYING Normal file
View File

@ -0,0 +1,28 @@
Copyright (c) 2006, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

44
google-breakpad/DEPS Normal file
View File

@ -0,0 +1,44 @@
# Copyright (c) 2010, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# We only use this file to ease the steps of generating projects after
# syncing, if we use gclient. All dependencies are svn:externals instead.
# If you're not using gclient, you need to run the gyp python script to
# generate the projects.
# This can be done by the following command (assuming current directory):
# src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp
hooks = [
{
# A change to a .gyp, .gypi, or to GYP itself should run the generator.
"pattern": ".",
"action": ["python",
"src/src/tools/gyp/gyp",
"src/src/client/windows/breakpad_client.gyp"],
},
]

234
google-breakpad/INSTALL Normal file
View File

@ -0,0 +1,234 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006 Free Software Foundation, Inc.
This file is free documentation; the Free Software Foundation gives
unlimited permission to copy, distribute and modify it.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package.
4. Type `make install' to install the programs and any data files and
documentation.
5. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that the
`configure' script does not know about. Run `./configure --help' for
details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX'.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them.
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Optional Features
=================
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Specifying the System Type
==========================
There may be some features `configure' cannot figure out automatically,
but needs to determine by the type of machine the package will run on.
Usually, assuming the package is built to be run on the _same_
architectures, `configure' can figure that out, but if it prints a
message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share, you
can create a site shell script called `config.site' that gives default
values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it operates.
`--help'
`-h'
Print a summary of the options to `configure', and exit.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

1112
google-breakpad/Makefile.am Normal file

File diff suppressed because it is too large Load Diff

6397
google-breakpad/Makefile.in Normal file

File diff suppressed because it is too large Load Diff

0
google-breakpad/NEWS Normal file
View File

43
google-breakpad/README Normal file
View File

@ -0,0 +1,43 @@
Breakpad is a set of client and server components which implement a
crash-reporting system.
-----
Getting started in 32-bit mode (from trunk)
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 request change review:
0. Get access to a read-write copy of source.
Owners at http://code.google.com/p/google-breakpad/ are able to grant
this access.
1. Check out a read-write copy of source using instructions at
http://code.google.com/p/google-breakpad/source/checkout
2. 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. Download http://codereview.appspot.com/static/upload.py
4. Run upload.py from the 'src' directory:
upload.py --server=breakpad.appspot.com
You will be prompted for credential and a description.
5. At http://breakpad.appspot.com 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
6. When applying code review feedback, specify the '-i' option when running
upload.py again and pass the issue number so it updates the existing issue,
rather than creating a new one.
Be sure to rerun upload.py from the same directory as you did for previous
uploads to allow for proper diff calculations.

1037
google-breakpad/aclocal.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
/usr/share/automake-1.11/compile

1534
google-breakpad/autotools/config.guess vendored Executable file

File diff suppressed because it is too large Load Diff

1782
google-breakpad/autotools/config.sub vendored Executable file

File diff suppressed because it is too large Load Diff

584
google-breakpad/autotools/depcomp Executable file
View File

@ -0,0 +1,584 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
scriptversion=2006-10-15.18
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 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
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>.
case $1 in
'')
echo "$0: No command. Try \`$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
Run PROGRAMS ARGS to compile a file, generating dependencies
as side-effects.
Environment variables:
depmode Dependency tracking mode.
source Source file read by `PROGRAMS ARGS'.
object Object file output by `PROGRAMS ARGS'.
DEPDIR directory where to store dependencies.
depfile Dependency file to output.
tmpdepfile Temporary file to use when outputing dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "depcomp $scriptversion"
exit $?
;;
esac
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
echo "depcomp: Variables source, object and depmode must be set" 1>&2
exit 1
fi
# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po.
depfile=${depfile-`echo "$object" |
sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`}
tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
# Some modes work just like other modes, but use different flags. We
# parameterize here, but still list the modes in the big case below,
# to make depend.m4 easier to write. Note that we *cannot* use a case
# here, because this file can only contain one case statement.
if test "$depmode" = hp; then
# HP compiler uses -M and no extra arg.
gccflag=-M
depmode=gcc
fi
if test "$depmode" = dashXmstdout; then
# This is just like dashmstdout with a different argument.
dashmflag=-xM
depmode=dashmstdout
fi
case "$depmode" in
gcc3)
## gcc 3 implements dependency tracking that does exactly what
## we want. Yay! Note: for some reason libtool 1.4 doesn't like
## it if -MD -MP comes after the -MF stuff. Hmm.
## Unfortunately, FreeBSD c89 acceptance of flags depends upon
## the command line argument order; so add the flags where they
## appear in depend2.am. Note that the slowdown incurred here
## affects only configure: in makefiles, %FASTDEP% shortcuts this.
for arg
do
case $arg in
-c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;;
*) set fnord "$@" "$arg" ;;
esac
shift # fnord
shift # $arg
done
"$@"
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
mv "$tmpdepfile" "$depfile"
;;
gcc)
## There are various ways to get dependency output from gcc. Here's
## why we pick this rather obscure method:
## - Don't want to use -MD because we'd like the dependencies to end
## up in a subdir. Having to rename by hand is ugly.
## (We might end up doing this anyway to support other compilers.)
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
## -MM, not -M (despite what the docs say).
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
gccflag=-MD,
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
echo "$object : \\" > "$depfile"
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
## The second -e expression handles DOS-style file names with drive letters.
sed -e 's/^[^:]*: / /' \
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
## This next piece of magic avoids the `deleted header file' problem.
## The problem is that when a header file which appears in a .P file
## is deleted, the dependency causes make to die (because there is
## typically no way to rebuild the header). We avoid this by adding
## dummy dependencies for each header file. Too bad gcc doesn't do
## this for us directly.
tr ' ' '
' < "$tmpdepfile" |
## Some versions of gcc put a space before the `:'. On the theory
## that the space means something, we add a space to the output as
## well.
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp)
# This case exists only to let depend.m4 do its work. It works by
# looking at the text of this script. This case will never be run,
# since it is checked for above.
exit 1
;;
sgi)
if test "$libtool" = yes; then
"$@" "-Wp,-MDupdate,$tmpdepfile"
else
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
echo "$object : \\" > "$depfile"
# Clip off the initial element (the dependent). Don't try to be
# clever and replace this with sed code, as IRIX sed won't handle
# lines with more than a fixed number of characters (4096 in
# IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines;
# the IRIX cc adds comments like `#:fec' to the end of the
# dependency line.
tr ' ' '
' < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
tr '
' ' ' >> $depfile
echo >> $depfile
# The second pass generates a dummy entry for each header file.
tr ' ' '
' < "$tmpdepfile" \
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
>> $depfile
else
# The sourcefile does not contain any dependencies, so just
# store a dummy comment line, to avoid errors with the Makefile
# "include basename.Plo" scheme.
echo "#dummy" > "$depfile"
fi
rm -f "$tmpdepfile"
;;
aix)
# The C for AIX Compiler uses -M and outputs the dependencies
# in a .u file. In older versions, this file always lives in the
# current directory. Also, the AIX compiler puts `$object:' at the
# start of each line; $object doesn't have directory information.
# Version 6 uses the directory in both cases.
stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
tmpdepfile="$stripped.u"
if test "$libtool" = yes; then
"$@" -Wc,-M
else
"$@" -M
fi
stat=$?
if test -f "$tmpdepfile"; then :
else
stripped=`echo "$stripped" | sed 's,^.*/,,'`
tmpdepfile="$stripped.u"
fi
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
if test -f "$tmpdepfile"; then
outname="$stripped.o"
# Each line is of the form `foo.o: dependent.h'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
else
# The sourcefile does not contain any dependencies, so just
# store a dummy comment line, to avoid errors with the Makefile
# "include basename.Plo" scheme.
echo "#dummy" > "$depfile"
fi
rm -f "$tmpdepfile"
;;
icc)
# Intel's C compiler understands `-MD -MF file'. However on
# icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
# ICC 7.0 will fill foo.d with something like
# foo.o: sub/foo.c
# foo.o: sub/foo.h
# which is wrong. We want:
# sub/foo.o: sub/foo.c
# sub/foo.o: sub/foo.h
# sub/foo.c:
# sub/foo.h:
# ICC 7.1 will output
# foo.o: sub/foo.c sub/foo.h
# and will wrap long lines using \ :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
"$@" -MD -MF "$tmpdepfile"
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile"
exit $stat
fi
rm -f "$depfile"
# Each line is of the form `foo.o: dependent.h',
# or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'.
# Do two passes, one to just change these to
# `$object: dependent.h' and one to simply `dependent.h:'.
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
hp2)
# The "hp" stanza above does not work with aCC (C++) and HP's ia64
# compilers, which have integrated preprocessors. The correct option
# to use with these is +Maked; it writes dependencies to a file named
# 'foo.d', which lands next to the object file, wherever that
# happens to be.
# Much of this is similar to the tru64 case; see comments there.
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
test "x$dir" = "x$object" && dir=
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
"$@" -Wc,+Maked
else
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir$base.d
"$@" +Maked
fi
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
# Add `dependent.h:' lines.
sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile"
else
echo "#dummy" > "$depfile"
fi
rm -f "$tmpdepfile" "$tmpdepfile2"
;;
tru64)
# The Tru64 compiler uses -MD to generate dependencies as a side
# effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
# At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put
# dependencies in `foo.d' instead, so we check for that too.
# Subdirectories are respected.
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
test "x$dir" = "x$object" && dir=
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
if test "$libtool" = yes; then
# With Tru64 cc, shared objects can also be used to make a
# static library. This mechanism is used in libtool 1.4 series to
# handle both shared and static libraries in a single compilation.
# With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d.
#
# With libtool 1.5 this exception was removed, and libtool now
# generates 2 separate objects for the 2 libraries. These two
# compilations output dependencies in $dir.libs/$base.o.d and
# in $dir$base.o.d. We have to check for both files, because
# one of the two compilations can be disabled. We should prefer
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
# automatically cleaned when .libs/ is deleted, while ignoring
# the former would cause a distcleancheck panic.
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
tmpdepfile2=$dir$base.o.d # libtool 1.5
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
"$@" -Wc,-MD
else
tmpdepfile1=$dir$base.o.d
tmpdepfile2=$dir$base.d
tmpdepfile3=$dir$base.d
tmpdepfile4=$dir$base.d
"$@" -MD
fi
stat=$?
if test $stat -eq 0; then :
else
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
exit $stat
fi
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
do
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
# That's a tab and a space in the [].
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
else
echo "#dummy" > "$depfile"
fi
rm -f "$tmpdepfile"
;;
#nosideeffect)
# This comment above is used by automake to tell side-effect
# dependency tracking mechanisms from slower ones.
dashmstdout)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test $1 != '--mode=compile'; do
shift
done
shift
fi
# Remove `-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
test -z "$dashmflag" && dashmflag=-M
# Require at least two characters before searching for `:'
# in the target name. This is to cope with DOS-style filenames:
# a dependency such as `c:/foo/bar' could be seen as target `c' otherwise.
"$@" $dashmflag |
sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
tr ' ' '
' < "$tmpdepfile" | \
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
dashXmstdout)
# This case only exists to satisfy depend.m4. It is never actually
# run, as this mode is specially recognized in the preamble.
exit 1
;;
makedepend)
"$@" || exit $?
# Remove any Libtool call
if test "$libtool" = yes; then
while test $1 != '--mode=compile'; do
shift
done
shift
fi
# X makedepend
shift
cleared=no
for arg in "$@"; do
case $cleared in
no)
set ""; shift
cleared=yes ;;
esac
case "$arg" in
-D*|-I*)
set fnord "$@" "$arg"; shift ;;
# Strip any option that makedepend may not understand. Remove
# the object too, otherwise makedepend will parse it as a source file.
-*|$object)
;;
*)
set fnord "$@" "$arg"; shift ;;
esac
done
obj_suffix="`echo $object | sed 's/^.*\././'`"
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
sed '1,2d' "$tmpdepfile" | tr ' ' '
' | \
## Some versions of the HPUX 10.20 sed can't process this invocation
## correctly. Breaking it into two sed invocations is a workaround.
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
cpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout.
"$@" || exit $?
# Remove the call to Libtool.
if test "$libtool" = yes; then
while test $1 != '--mode=compile'; do
shift
done
shift
fi
# Remove `-o $object'.
IFS=" "
for arg
do
case $arg in
-o)
shift
;;
$object)
shift
;;
*)
set fnord "$@" "$arg"
shift # fnord
shift # $arg
;;
esac
done
"$@" -E |
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
sed '$ s: \\$::' > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
cat < "$tmpdepfile" >> "$depfile"
sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
msvisualcpp)
# Important note: in order to support this mode, a compiler *must*
# always write the preprocessed file to stdout, regardless of -o,
# because we must use -o when running libtool.
"$@" || exit $?
IFS=" "
for arg
do
case "$arg" in
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
set fnord "$@"
shift
shift
;;
*)
set fnord "$@" "$arg"
shift
shift
;;
esac
done
"$@" -E |
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
rm -f "$depfile"
echo "$object : \\" > "$depfile"
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
echo " " >> "$depfile"
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
rm -f "$tmpdepfile"
;;
none)
exec "$@"
;;
*)
echo "Unknown depmode $depmode" 1>&2
exit 1
;;
esac
exit 0
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-end: "$"
# End:

View File

@ -0,0 +1,507 @@
#!/bin/sh
# install - install a program, script, or datafile
scriptversion=2006-10-14.15
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
# following copyright and license.
#
# Copyright (C) 1994 X Consortium
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
# Except as contained in this notice, the name of the X Consortium shall not
# be used in advertising or otherwise to promote the sale, use or other deal-
# ings in this Software without prior written authorization from the X Consor-
# tium.
#
#
# FSF changes to this file are in the public domain.
#
# Calling this script install-sh is preferred over install.sh, to prevent
# `make' implicit rules from creating a file called install from it
# when there is no Makefile.
#
# This script is compatible with the BSD install script, but was written
# from scratch.
nl='
'
IFS=" "" $nl"
# set DOITPROG to echo to test this script
# Don't use :- since 4.3BSD and earlier shells don't like it.
doit="${DOITPROG-}"
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.
mvprog="${MVPROG-mv}"
cpprog="${CPPROG-cp}"
chmodprog="${CHMODPROG-chmod}"
chownprog="${CHOWNPROG-chown}"
chgrpprog="${CHGRPPROG-chgrp}"
stripprog="${STRIPPROG-strip}"
rmprog="${RMPROG-rm}"
mkdirprog="${MKDIRPROG-mkdir}"
posix_glob=
posix_mkdir=
# Desired mode of installed file.
mode=0755
chmodcmd=$chmodprog
chowncmd=
chgrpcmd=
stripcmd=
rmcmd="$rmprog -f"
mvcmd="$mvprog"
src=
dst=
dir_arg=
dstarg=
no_target_directory=
usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
or: $0 [OPTION]... SRCFILES... DIRECTORY
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
or: $0 [OPTION]... -d DIRECTORIES...
In the 1st form, copy SRCFILE to DSTFILE.
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
In the 4th, create DIRECTORIES.
Options:
-c (ignored)
-d create directories instead of installing files.
-g GROUP $chgrpprog installed files to GROUP.
-m MODE $chmodprog installed files to MODE.
-o USER $chownprog installed files to USER.
-s $stripprog installed files.
-t DIRECTORY install into DIRECTORY.
-T report an error if DSTFILE is a directory.
--help display this help and exit.
--version display version info and exit.
Environment variables override the default commands:
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
"
while test $# -ne 0; do
case $1 in
-c) shift
continue;;
-d) dir_arg=true
shift
continue;;
-g) chgrpcmd="$chgrpprog $2"
shift
shift
continue;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
shift
shift
case $mode in
*' '* | *' '* | *'
'* | *'*'* | *'?'* | *'['*)
echo "$0: invalid mode: $mode" >&2
exit 1;;
esac
continue;;
-o) chowncmd="$chownprog $2"
shift
shift
continue;;
-s) stripcmd=$stripprog
shift
continue;;
-t) dstarg=$2
shift
shift
continue;;
-T) no_target_directory=true
shift
continue;;
--version) echo "$0 $scriptversion"; exit $?;;
--) shift
break;;
-*) echo "$0: invalid option: $1" >&2
exit 1;;
*) break;;
esac
done
if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
# When -d is used, all remaining arguments are directories to create.
# When -t is used, the destination is already specified.
# Otherwise, the last argument is the destination. Remove it from $@.
for arg
do
if test -n "$dstarg"; then
# $@ is not empty: it contains at least $arg.
set fnord "$@" "$dstarg"
shift # fnord
fi
shift # arg
dstarg=$arg
done
fi
if test $# -eq 0; then
if test -z "$dir_arg"; then
echo "$0: no input file specified." >&2
exit 1
fi
# It's OK to call `install-sh -d' without argument.
# This can happen when creating conditional directories.
exit 0
fi
if test -z "$dir_arg"; then
trap '(exit $?); exit' 1 2 13 15
# Set umask so as not to create temps with too-generous modes.
# However, 'strip' requires both read and write access to temps.
case $mode in
# Optimize common cases.
*644) cp_umask=133;;
*755) cp_umask=22;;
*[0-7])
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
u_plus_rw=
else
u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
fi
for src
do
# Protect names starting with `-'.
case $src in
-*) src=./$src ;;
esac
if test -n "$dir_arg"; then
dst=$src
dstdir=$dst
test -d "$dstdir"
dstdir_status=$?
else
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command
# might cause directories to be created, which would be especially bad
# if $src (and thus $dsttmp) contains '*'.
if test ! -f "$src" && test ! -d "$src"; then
echo "$0: $src does not exist." >&2
exit 1
fi
if test -z "$dstarg"; then
echo "$0: no destination specified." >&2
exit 1
fi
dst=$dstarg
# Protect names starting with `-'.
case $dst in
-*) dst=./$dst ;;
esac
# 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 -n "$no_target_directory"; then
echo "$0: $dstarg: Is a directory" >&2
exit 1
fi
dstdir=$dst
dst=$dstdir/`basename "$src"`
dstdir_status=0
else
# 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
fi
obsolete_mkdir_used=false
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;;
*[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
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-writeable 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"
)
then :
else
# The umask is ridiculous, or mkdir does not conform to POSIX,
# or it failed possibly due to a race condition. Create the
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
/*) prefix=/ ;;
-*) prefix=./ ;;
*) prefix= ;;
esac
case $posix_glob in
'')
if (set -f) 2>/dev/null; then
posix_glob=true
else
posix_glob=false
fi ;;
esac
oIFS=$IFS
IFS=/
$posix_glob && set -f
set fnord $dstdir
shift
$posix_glob && set +f
IFS=$oIFS
prefixes=
for d
do
test -z "$d" && 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/
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
fi
fi
fi
if test -n "$dir_arg"; then
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
else
# Make a couple of temp file names in the proper directory.
dsttmp=$dstdir/_inst.$$_
rmtmp=$dstdir/_rm.$$_
# Trap to clean up those temp files at exit.
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
# Copy the file name to the temp name.
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") &&
# and set any options; do chmod last to preserve setuid bits.
#
# If any of these fail, we abort the whole thing. If we want to
# ignore errors from any of these, just make sure not to ignore
# errors from the above "$doit $cpprog $src $dsttmp" command.
#
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
&& { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
&& { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
# Now rename the file to the real destination.
{ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \
|| {
# The rename failed, perhaps because mv can't rename something else
# 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.
{
if test -f "$dst"; then
$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
}
else
:
fi
} &&
# Now rename the file to the real destination.
$doit $mvcmd "$dsttmp" "$dst"
}
} || exit 1
trap '' 0
fi
done
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-end: "$"
# End:

File diff suppressed because it is too large Load Diff

367
google-breakpad/autotools/missing Executable file
View File

@ -0,0 +1,367 @@
#! /bin/sh
# Common stub for a few missing GNU programs while installing.
scriptversion=2006-05-10.23
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006
# Free Software Foundation, Inc.
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
# 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
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
if test $# -eq 0; then
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
fi
run=:
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p'
sed_minuso='s/.* -o \([^ ]*\).*/\1/p'
# In the cases where this matters, `missing' is being run in the
# srcdir already.
if test -f configure.ac; then
configure_ac=configure.ac
else
configure_ac=configure.in
fi
msg="missing on your system"
case $1 in
--run)
# Try to run requested program, and just exit if it succeeds.
run=
shift
"$@" && exit 0
# Exit code 63 means version mismatch. This often happens
# when the user try to use an ancient version of a tool on
# a file that requires a minimum version. In this case we
# we should proceed has if the program had been absent, or
# if --run hadn't been passed.
if test $? = 63; then
run=:
msg="probably too old"
fi
;;
-h|--h|--he|--hel|--help)
echo "\
$0 [OPTION]... PROGRAM [ARGUMENT]...
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
error status if there is no known handling for PROGRAM.
Options:
-h, --help display this help and exit
-v, --version output version information and exit
--run try to run the given command, and emulate it if it fails
Supported PROGRAM values:
aclocal touch file \`aclocal.m4'
autoconf touch file \`configure'
autoheader touch file \`config.h.in'
autom4te touch the output file, or create a stub one
automake touch all \`Makefile.in' files
bison create \`y.tab.[ch]', if possible, from existing .[ch]
flex create \`lex.yy.c', if possible, from existing .c
help2man touch the output file
lex create \`lex.yy.c', if possible, from existing .c
makeinfo touch the output file
tar try tar, gnutar, gtar, then tar without non-portable flags
yacc create \`y.tab.[ch]', if possible, from existing .[ch]
Send bug reports to <bug-automake@gnu.org>."
exit $?
;;
-v|--v|--ve|--ver|--vers|--versi|--versio|--version)
echo "missing $scriptversion (GNU Automake)"
exit $?
;;
-*)
echo 1>&2 "$0: Unknown \`$1' option"
echo 1>&2 "Try \`$0 --help' for more information"
exit 1
;;
esac
# Now exit if we have it, but it failed. Also exit now if we
# don't have it and --version was passed (most likely to detect
# the program).
case $1 in
lex|yacc)
# Not GNU programs, they don't have --version.
;;
tar)
if test -n "$run"; then
echo 1>&2 "ERROR: \`tar' requires --run"
exit 1
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
exit 1
fi
;;
*)
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
# We have it, but it failed.
exit 1
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
# Could not run --version or --help. This is probably someone
# running `$TOOL --version' or `$TOOL --help' to check whether
# $TOOL exists and not knowing $TOOL uses missing.
exit 1
fi
;;
esac
# If it does not exist, or fails to run (possibly an outdated version),
# try to emulate it.
case $1 in
aclocal*)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
to install the \`Automake' and \`Perl' packages. Grab them from
any GNU archive site."
touch aclocal.m4
;;
autoconf)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`${configure_ac}'. You might want to install the
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU
archive site."
touch configure
;;
autoheader)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`acconfig.h' or \`${configure_ac}'. You might want
to install the \`Autoconf' and \`GNU m4' packages. Grab them
from any GNU archive site."
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
test -z "$files" && files="config.h"
touch_files=
for f in $files; do
case $f in
*:*) touch_files="$touch_files "`echo "$f" |
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
*) touch_files="$touch_files $f.in";;
esac
done
touch $touch_files
;;
automake*)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
You might want to install the \`Automake' and \`Perl' packages.
Grab them from any GNU archive site."
find . -type f -name Makefile.am -print |
sed 's/\.am$/.in/' |
while read f; do touch "$f"; done
;;
autom4te)
echo 1>&2 "\
WARNING: \`$1' is needed, but is $msg.
You might have modified some files without having the
proper tools for further handling them.
You can get \`$1' as part of \`Autoconf' from any GNU
archive site."
file=`echo "$*" | sed -n "$sed_output"`
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
if test -f "$file"; then
touch $file
else
test -z "$file" || exec >$file
echo "#! /bin/sh"
echo "# Created by GNU Automake missing as a replacement of"
echo "# $ $@"
echo "exit 0"
chmod +x $file
exit 1
fi
;;
bison|yacc)
echo 1>&2 "\
WARNING: \`$1' $msg. You should only need it if
you modified a \`.y' file. You may need the \`Bison' package
in order for those modifications to take effect. You can get
\`Bison' from any GNU archive site."
rm -f y.tab.c y.tab.h
if test $# -ne 1; then
eval LASTARG="\${$#}"
case $LASTARG in
*.y)
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
if test -f "$SRCFILE"; then
cp "$SRCFILE" y.tab.c
fi
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
if test -f "$SRCFILE"; then
cp "$SRCFILE" y.tab.h
fi
;;
esac
fi
if test ! -f y.tab.h; then
echo >y.tab.h
fi
if test ! -f y.tab.c; then
echo 'main() { return 0; }' >y.tab.c
fi
;;
lex|flex)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified a \`.l' file. You may need the \`Flex' package
in order for those modifications to take effect. You can get
\`Flex' from any GNU archive site."
rm -f lex.yy.c
if test $# -ne 1; then
eval LASTARG="\${$#}"
case $LASTARG in
*.l)
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
if test -f "$SRCFILE"; then
cp "$SRCFILE" lex.yy.c
fi
;;
esac
fi
if test ! -f lex.yy.c; then
echo 'main() { return 0; }' >lex.yy.c
fi
;;
help2man)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified a dependency of a manual page. You may need the
\`Help2man' package in order for those modifications to take
effect. You can get \`Help2man' from any GNU archive site."
file=`echo "$*" | sed -n "$sed_output"`
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
if test -f "$file"; then
touch $file
else
test -z "$file" || exec >$file
echo ".ab help2man is required to generate this page"
exit 1
fi
;;
makeinfo)
echo 1>&2 "\
WARNING: \`$1' is $msg. You should only need it if
you modified a \`.texi' or \`.texinfo' file, or any other file
indirectly affecting the aspect of the manual. The spurious
call might also be the consequence of using a buggy \`make' (AIX,
DU, IRIX). You might want to install the \`Texinfo' package or
the \`GNU make' package. Grab either from any GNU archive site."
# The file to touch is that specified with -o ...
file=`echo "$*" | sed -n "$sed_output"`
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"`
if test -z "$file"; then
# ... or it is the one specified with @setfilename ...
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
file=`sed -n '
/^@setfilename/{
s/.* \([^ ]*\) *$/\1/
p
q
}' $infile`
# ... or it is derived from the source name (dir/f.texi becomes f.info)
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info
fi
# If the file does not exist, the user really needs makeinfo;
# let's fail without touching anything.
test -f $file || exit 1
touch $file
;;
tar)
shift
# We have already tried tar in the generic part.
# Look for gnutar/gtar before invocation to avoid ugly error
# messages.
if (gnutar --version > /dev/null 2>&1); then
gnutar "$@" && exit 0
fi
if (gtar --version > /dev/null 2>&1); then
gtar "$@" && exit 0
fi
firstarg="$1"
if shift; then
case $firstarg in
*o*)
firstarg=`echo "$firstarg" | sed s/o//`
tar "$firstarg" "$@" && exit 0
;;
esac
case $firstarg in
*h*)
firstarg=`echo "$firstarg" | sed s/h//`
tar "$firstarg" "$@" && exit 0
;;
esac
fi
echo 1>&2 "\
WARNING: I can't seem to be able to run \`tar' with the given arguments.
You may want to install GNU tar or Free paxutils, or check the
command line arguments."
exit 1
;;
*)
echo 1>&2 "\
WARNING: \`$1' is needed, and is $msg.
You might have modified some files without having the
proper tools for further handling them. Check the \`README' file,
it often tells you about the needed prerequisites for installing
this package. You may also peek at any GNU archive site, in case
some other package would contain this missing \`$1' program."
exit 1
;;
esac
exit 0
# Local variables:
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-end: "$"
# End:

7649
google-breakpad/configure vendored Executable file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,151 @@
# Copyright (c) 2006, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
AC_PREREQ(2.57)
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
dnl Sanity check: the argument is just a file that should exist.
AC_CONFIG_SRCDIR(README)
AC_CONFIG_AUX_DIR(autotools)
AC_CONFIG_MACRO_DIR([m4])
AC_CANONICAL_HOST
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
AM_CONFIG_HEADER(src/config.h)
AM_PROG_AS
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CPP
AC_PROG_CXX
AC_PROG_RANLIB
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
AC_HEADER_STDC
m4_include(m4/ax_pthread.m4)
AX_PTHREAD
AC_CHECK_HEADERS([a.out.h])
# Only build Linux client libs when compiling for Linux
case $host in
*-*-linux* | *-android* )
LINUX_HOST=true
;;
esac
AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue)
# Only use Android support headers when compiling for Android
case $host in
*-android*)
ANDROID_HOST=true
;;
esac
AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue)
AC_ARG_ENABLE(m32,
AS_HELP_STRING([--enable-m32],
[Compile/build with -m32]
[(default is no)]),
[case "${enableval}" in
yes)
CFLAGS="${CFLAGS} -m32"
CXXFLAGS="${CXXFLAGS} -m32"
usem32=true
;;
no)
usem32=false
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --enable-m32)
;;
esac],
[usem32=false])
AC_ARG_ENABLE(processor,
AS_HELP_STRING([--disable-processor],
[Don't build processor library]
[(default is no)]),
[case "${enableval}" in
yes)
disable_processor=false
;;
no)
disable_processor=true
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --disable-processor)
;;
esac],
[disable_processor=false])
AM_CONDITIONAL(DISABLE_PROCESSOR, test x$disable_processor = xtrue)
AC_ARG_ENABLE(tools,
AS_HELP_STRING([--disable-tools],
[Don't build tool binaries]
[(default is no)]),
[case "${enableval}" in
yes)
disable_tools=false
;;
no)
disable_tools=true
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --disable-tools)
;;
esac],
[disable_tools=false])
AM_CONDITIONAL(DISABLE_TOOLS, test x$disable_tools = xtrue)
if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then
AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!])
fi
AC_ARG_ENABLE(selftest,
AS_HELP_STRING([--enable-selftest],
[Run extra tests with "make check" ]
[(may conflict with optimizations) ]
[(default is no)]),
[case "${enableval}" in
yes)
selftest=true
;;
no)
selftest=false
;;
*)
AC_MSG_ERROR(bad value ${enableval} for --enable-selftest)
;;
esac],
[selftest=false])
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
AC_CONFIG_FILES([Makefile])
AC_OUTPUT

View File

@ -0,0 +1,283 @@
# ===========================================================================
# http://www.nongnu.org/autoconf-archive/ax_pthread.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]])
#
# DESCRIPTION
#
# This macro figures out how to build C programs using POSIX threads. It
# sets the PTHREAD_LIBS output variable to the threads library and linker
# flags, and the PTHREAD_CFLAGS output variable to any special C compiler
# flags that are needed. (The user can also force certain compiler
# flags/libs to be tested by setting these environment variables.)
#
# Also sets PTHREAD_CC to any special C compiler that is needed for
# multi-threaded programs (defaults to the value of CC otherwise). (This
# is necessary on AIX to use the special cc_r compiler alias.)
#
# NOTE: You are assumed to not only compile your program with these flags,
# but also link it with them as well. e.g. you should link with
# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS
#
# If you are only building threads programs, you may wish to use these
# variables in your default LIBS, CFLAGS, and CC:
#
# LIBS="$PTHREAD_LIBS $LIBS"
# CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# CC="$PTHREAD_CC"
#
# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant
# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name
# (e.g. PTHREAD_CREATE_UNDETACHED on AIX).
#
# ACTION-IF-FOUND is a list of shell commands to run if a threads library
# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it
# is not found. If ACTION-IF-FOUND is not specified, the default action
# will define HAVE_PTHREAD.
#
# Please let the authors know if this macro fails on any platform, or if
# you have any other suggestions or comments. This macro was based on work
# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help
# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by
# Alejandro Forero Cuervo to the autoconf macro repository. We are also
# grateful for the helpful feedback of numerous users.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
#
# 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 the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 6
AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD])
AC_DEFUN([AX_PTHREAD], [
AC_REQUIRE([AC_CANONICAL_HOST])
AC_LANG_SAVE
AC_LANG_C
ax_pthread_ok=no
# We used to check for pthread.h first, but this fails if pthread.h
# requires special compiler flags (e.g. on True64 or Sequent).
# It gets checked for in the link test anyway.
# First of all, check if the user has set any of the PTHREAD_LIBS,
# etcetera environment variables, and if threads linking works using
# them:
if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS])
AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes)
AC_MSG_RESULT($ax_pthread_ok)
if test x"$ax_pthread_ok" = xno; then
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# We must check for the threads library under a number of different
# names; the ordering is very important because some systems
# (e.g. DEC) have both -lpthread and -lpthreads, where one of the
# libraries is broken (non-POSIX).
# Create a list of thread flags to try. Items starting with a "-" are
# C compiler flags, and other items are library names, except for "none"
# which indicates that we try without any flags at all, and "pthread-config"
# which is a program returning the flags for the Pth emulation library.
ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config"
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# pthreads: AIX (must check this before -lpthread)
# none: in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads)
# -pthreads: Solaris/gcc
# -mthreads: Mingw32/gcc, Lynx/gcc
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# pthread: Linux, etcetera
# --thread-safe: KAI C++
# pthread-config: use pthread-config program (for GNU Pth library)
case "${host_cpu}-${host_os}" in
*solaris*)
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags"
;;
*-darwin*)
acx_pthread_flags="-pthread $acx_pthread_flags"
;;
esac
if test x"$ax_pthread_ok" = xno; then
for flag in $ax_pthread_flags; do
case $flag in
none)
AC_MSG_CHECKING([whether pthreads work without any flags])
;;
-*)
AC_MSG_CHECKING([whether pthreads work with $flag])
PTHREAD_CFLAGS="$flag"
;;
pthread-config)
AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no)
if test x"$ax_pthread_config" = xno; then continue; fi
PTHREAD_CFLAGS="`pthread-config --cflags`"
PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`"
;;
*)
AC_MSG_CHECKING([for the pthreads library -l$flag])
PTHREAD_LIBS="-l$flag"
;;
esac
save_LIBS="$LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Check for various functions. We must include pthread.h,
# since some functions may be macros. (On the Sequent, we
# need a special flag -Kthread to make this header compile.)
# We check for pthread_join because it is in -lpthread on IRIX
# while pthread_create is in libc. We check for pthread_attr_init
# due to DEC craziness with -lpthreads. We check for
# pthread_cleanup_push because it is one of the few pthread
# functions on Solaris that doesn't have a non-functional libc stub.
# We try pthread_create on general principles.
AC_TRY_LINK([#include <pthread.h>
static void routine(void* a) {a=0;}
static void* start_routine(void* a) {return a;}],
[pthread_t th; pthread_attr_t attr;
pthread_join(th, 0);
pthread_attr_init(&attr);
pthread_cleanup_push(routine, 0);
pthread_create(&th,0,start_routine,0);
pthread_cleanup_pop(0); ],
[ax_pthread_ok=yes])
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
AC_MSG_RESULT($ax_pthread_ok)
if test "x$ax_pthread_ok" = xyes; then
break;
fi
PTHREAD_LIBS=""
PTHREAD_CFLAGS=""
done
fi
# Various other checks:
if test "x$ax_pthread_ok" = xyes; then
save_LIBS="$LIBS"
LIBS="$PTHREAD_LIBS $LIBS"
save_CFLAGS="$CFLAGS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
# Detect AIX lossage: JOINABLE attribute is called UNDETACHED.
AC_MSG_CHECKING([for joinable pthread attribute])
attr_name=unknown
for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;],
[attr_name=$attr; break])
done
AC_MSG_RESULT($attr_name)
if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then
AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name,
[Define to necessary symbol if this constant
uses a non-standard name on your system.])
fi
AC_MSG_CHECKING([if more special flags are required for pthreads])
flag=no
case "${host_cpu}-${host_os}" in
*-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";;
*solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";;
esac
AC_MSG_RESULT(${flag})
if test "x$flag" != xno; then
PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS"
fi
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
# More AIX lossage: must compile with xlc_r or cc_r
if test x"$GCC" != xyes; then
AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC})
else
PTHREAD_CC=$CC
fi
else
PTHREAD_CC="$CC"
fi
AC_SUBST(PTHREAD_LIBS)
AC_SUBST(PTHREAD_CFLAGS)
AC_SUBST(PTHREAD_CC)
# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND:
if test x"$ax_pthread_ok" = xyes; then
ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1])
:
else
ax_pthread_ok=no
$2
fi
AC_LANG_RESTORE
])dnl AX_PTHREAD

7377
google-breakpad/m4/libtool.m4 vendored Normal file

File diff suppressed because it is too large Load Diff

368
google-breakpad/m4/ltoptions.m4 vendored Normal file
View File

@ -0,0 +1,368 @@
# Helper functions for option handling. -*- Autoconf -*-
#
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# 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.
# serial 6 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME)
# ------------------------------------------
m4_define([_LT_MANGLE_OPTION],
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])])
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME)
# ---------------------------------------
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a
# matching handler defined, dispatch to it. Other OPTION-NAMEs are
# saved as a flag.
m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
[m4_warning([Unknown $1 option `$2'])])[]dnl
])
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET])
# ------------------------------------------------------------
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise.
m4_define([_LT_IF_OPTION],
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])])
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET)
# -------------------------------------------------------
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME
# are set.
m4_define([_LT_UNLESS_OPTIONS],
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option),
[m4_define([$0_found])])])[]dnl
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3
])[]dnl
])
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST)
# ----------------------------------------
# OPTION-LIST is a space-separated list of Libtool options associated
# with MACRO-NAME. If any OPTION has a matching handler declared with
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about
# the unknown option and exit.
m4_defun([_LT_SET_OPTIONS],
[# Set options
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])),
[_LT_SET_OPTION([$1], _LT_Option)])
m4_if([$1],[LT_INIT],[
dnl
dnl Simply set some default values (i.e off) if boolean options were not
dnl specified:
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no
])
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no
])
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
dnl `shared' nor `disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
[_LT_ENABLE_FAST_INSTALL])
])
])# _LT_SET_OPTIONS
## --------------------------------- ##
## Macros to handle LT_INIT options. ##
## --------------------------------- ##
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME)
# -----------------------------------------
m4_define([_LT_MANGLE_DEFUN],
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])])
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE)
# -----------------------------------------------
m4_define([LT_OPTION_DEFINE],
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl
])# LT_OPTION_DEFINE
# dlopen
# ------
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes
])
AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], [])
# win32-dll
# ---------
# Declare package support for building win32 dll's.
LT_OPTION_DEFINE([LT_INIT], [win32-dll],
[enable_win32_dll=yes
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*)
AC_CHECK_TOOL(AS, as, false)
AC_CHECK_TOOL(DLLTOOL, dlltool, false)
AC_CHECK_TOOL(OBJDUMP, objdump, false)
;;
esac
test -z "$AS" && AS=as
_LT_DECL([], [AS], [0], [Assembler program])dnl
test -z "$DLLTOOL" && DLLTOOL=dlltool
_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl
test -z "$OBJDUMP" && OBJDUMP=objdump
_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl
])# win32-dll
AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
[AC_REQUIRE([AC_CANONICAL_HOST])dnl
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
# implement the --enable-shared flag, and supports the `shared' and
# `disable-shared' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@],
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_shared=yes ;;
no) enable_shared=no ;;
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
_LT_DECL([build_libtool_libs], [enable_shared], [0],
[Whether or not to build shared libraries])
])# _LT_ENABLE_SHARED
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])])
# Old names:
AC_DEFUN([AC_ENABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared])
])
AC_DEFUN([AC_DISABLE_SHARED],
[_LT_SET_OPTION([LT_INIT], [disable-shared])
])
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)])
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_SHARED], [])
dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
# implement the --enable-static flag, and support the `static' and
# `disable-static' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@],
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_static=yes ;;
no) enable_static=no ;;
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
_LT_DECL([build_old_libs], [enable_static], [0],
[Whether or not to build static libraries])
])# _LT_ENABLE_STATIC
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])])
# Old names:
AC_DEFUN([AC_ENABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static])
])
AC_DEFUN([AC_DISABLE_STATIC],
[_LT_SET_OPTION([LT_INIT], [disable-static])
])
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)])
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AM_ENABLE_STATIC], [])
dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
# implement the --enable-fast-install flag, and support the `fast-install'
# and `disable-fast-install' LT_INIT options.
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@],
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])],
[p=${PACKAGE-default}
case $enableval in
yes) enable_fast_install=yes ;;
no) enable_fast_install=no ;;
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
for pkg in $enableval; do
IFS="$lt_save_ifs"
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
IFS="$lt_save_ifs"
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
_LT_DECL([fast_install], [enable_fast_install], [0],
[Whether or not to optimize for fast installation])dnl
])# _LT_ENABLE_FAST_INSTALL
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])])
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])])
# Old names:
AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the `fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
the `disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
# _LT_WITH_PIC([MODE])
# --------------------
# implement the --with-pic flag, and support the `pic-only' and `no-pic'
# LT_INIT options.
# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
[AS_HELP_STRING([--with-pic],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
[pic_mode="$withval"],
[pic_mode=default])
test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])])
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])])
# Old name:
AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
put the `pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], [])
## ----------------- ##
## LTDL_INIT Options ##
## ----------------- ##
m4_define([_LTDL_MODE], [])
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive],
[m4_define([_LTDL_MODE], [nonrecursive])])
LT_OPTION_DEFINE([LTDL_INIT], [recursive],
[m4_define([_LTDL_MODE], [recursive])])
LT_OPTION_DEFINE([LTDL_INIT], [subproject],
[m4_define([_LTDL_MODE], [subproject])])
m4_define([_LTDL_TYPE], [])
LT_OPTION_DEFINE([LTDL_INIT], [installable],
[m4_define([_LTDL_TYPE], [installable])])
LT_OPTION_DEFINE([LTDL_INIT], [convenience],
[m4_define([_LTDL_TYPE], [convenience])])

123
google-breakpad/m4/ltsugar.m4 vendored Normal file
View File

@ -0,0 +1,123 @@
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# 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.
# serial 6 ltsugar.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])])
# lt_join(SEP, ARG1, [ARG2...])
# -----------------------------
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their
# associated separator.
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier
# versions in m4sugar had bugs.
m4_define([lt_join],
[m4_if([$#], [1], [],
[$#], [2], [[$2]],
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])])
m4_define([_lt_join],
[m4_if([$#$2], [2], [],
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])])
# lt_car(LIST)
# lt_cdr(LIST)
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
# Autoconf-2.59 which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
[$#], 1, [],
[m4_dquote(m4_shift($@))])])
m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
# than defined and empty).
#
# This macro is needed until we can rely on Autoconf 2.62, since earlier
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING.
m4_define([lt_append],
[m4_define([$1],
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])])
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...])
# ----------------------------------------------------------
# Produce a SEP delimited list of all paired combinations of elements of
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list
# has the form PREFIXmINFIXSUFFIXn.
# Needed until we can rely on m4_combine added in Autoconf 2.62.
m4_define([lt_combine],
[m4_if(m4_eval([$# > 3]), [1],
[m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl
[[m4_foreach([_Lt_prefix], [$2],
[m4_foreach([_Lt_suffix],
]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[,
[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])])
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ])
# -----------------------------------------------------------------------
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ.
m4_define([lt_if_append_uniq],
[m4_ifdef([$1],
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1],
[lt_append([$1], [$2], [$3])$4],
[$5])],
[lt_append([$1], [$2], [$3])$4])])
# lt_dict_add(DICT, KEY, VALUE)
# -----------------------------
m4_define([lt_dict_add],
[m4_define([$1($2)], [$3])])
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE)
# --------------------------------------------
m4_define([lt_dict_add_subkey],
[m4_define([$1($2:$3)], [$4])])
# lt_dict_fetch(DICT, KEY, [SUBKEY])
# ----------------------------------
m4_define([lt_dict_fetch],
[m4_ifval([$3],
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]),
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))])
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE])
# -----------------------------------------------------------------
m4_define([lt_if_dict_fetch],
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4],
[$5],
[$6])])
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...])
# --------------------------------------------------------------
m4_define([lt_dict_filter],
[m4_if([$5], [], [],
[lt_join(m4_quote(m4_default([$4], [[, ]])),
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]),
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl
])

23
google-breakpad/m4/ltversion.m4 vendored Normal file
View File

@ -0,0 +1,23 @@
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
# Copyright (C) 2004 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# 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.
# Generated from ltversion.in.
# serial 3017 ltversion.m4
# This file is part of GNU Libtool
m4_define([LT_PACKAGE_VERSION], [2.2.6b])
m4_define([LT_PACKAGE_REVISION], [1.3017])
AC_DEFUN([LTVERSION_VERSION],
[macro_version='2.2.6b'
macro_revision='1.3017'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])

92
google-breakpad/m4/lt~obsolete.m4 vendored Normal file
View File

@ -0,0 +1,92 @@
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# 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.
# serial 4 lt~obsolete.m4
# These exist entirely to fool aclocal when bootstrapping libtool.
#
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us
# using a macro with the same name in our local m4/libtool.m4 it'll
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define
# and doesn't know about Autoconf macros at all.)
#
# So we provide this file, which has a silly filename so it's always
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
# we give up compatibility with versions before 1.7, at which point
# we need to keep only those names which we still refer to.
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])])
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])])
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])])
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])])
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])])
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])])
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])])
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])])
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])])
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])])
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])])
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])])
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])])
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])])
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])])
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])])
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])])
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])])
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])])
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])])
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])])
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])])
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])])
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])])
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])])
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])])
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])])
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])])
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])])
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])])
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])])
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])])
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])])
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])])
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])])
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])])
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])])
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])])
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])])
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])])
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])])
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])])
m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])])
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])])
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])])
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])])
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])])
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])])
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])])
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])])
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])])
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])])

View File

@ -0,0 +1,36 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef BREAKPAD_GOOGLETEST_INCLUDES_H__
#define BREAKPAD_GOOGLETEST_INCLUDES_H__
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/include/gmock/gmock.h"
#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__

View File

@ -0,0 +1,53 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
namespace google_breakpad {
class CrashGenerationServer;
class ClientInfo {
public:
ClientInfo(pid_t pid, CrashGenerationServer* crash_server)
: crash_server_(crash_server),
pid_(pid) {}
CrashGenerationServer* crash_server() const { return crash_server_; }
pid_t pid() const { return pid_; }
private:
CrashGenerationServer* crash_server_;
pid_t pid_;
};
}
#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_

View File

@ -0,0 +1,92 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <algorithm>
#include "client/linux/crash_generation/crash_generation_client.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
bool
CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
{
int fds[2];
sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
struct kernel_msghdr msg;
my_memset(&msg, 0, sizeof(struct kernel_msghdr));
struct kernel_iovec iov[1];
iov[0].iov_base = const_cast<void*>(blob);
iov[0].iov_len = blob_size;
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
char cmsg[kControlMsgSize];
my_memset(cmsg, 0, kControlMsgSize);
msg.msg_control = cmsg;
msg.msg_controllen = sizeof(cmsg);
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
hdr->cmsg_level = SOL_SOCKET;
hdr->cmsg_type = SCM_RIGHTS;
hdr->cmsg_len = CMSG_LEN(sizeof(int));
int* p = reinterpret_cast<int*>(CMSG_DATA(hdr));
*p = fds[1];
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
sys_close(fds[1]);
if (ret <= 0)
return false;
// wait for an ACK from the server
char b;
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
return true;
}
//static
CrashGenerationClient*
CrashGenerationClient::TryCreate(int server_fd)
{
if (0 > server_fd)
return NULL;
return new CrashGenerationClient(server_fd);
}
}

View File

@ -0,0 +1,69 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
#include <stddef.h>
namespace google_breakpad {
class CrashGenerationClient {
public:
~CrashGenerationClient()
{
}
// Request the crash server to generate a dump. |blob| is a hack,
// see exception_handler.h and minidump_writer.h
//
// Return true if the dump was successful; false otherwise.
bool RequestDump(const void* blob, size_t blob_size);
// Return a new CrashGenerationClient if |server_fd| is valid and
// connects to a CrashGenerationServer. Otherwise, return NULL.
// The returned CrashGenerationClient* is owned by the caller of
// this function.
static CrashGenerationClient* TryCreate(int server_fd);
private:
CrashGenerationClient(int server_fd) : server_fd_(server_fd)
{
}
int server_fd_;
// prevent copy construction and assignment
CrashGenerationClient(const CrashGenerationClient&);
CrashGenerationClient& operator=(const CrashGenerationClient&);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_

View File

@ -0,0 +1,464 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include "client/linux/crash_generation/crash_generation_server.h"
#include "client/linux/crash_generation/client_info.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/guid_creator.h"
#include "common/linux/safe_readlink.h"
static const char kCommandQuit = 'x';
static bool
GetInodeForFileDescriptor(ino_t* inode_out, int fd)
{
assert(inode_out);
struct stat buf;
if (fstat(fd, &buf) < 0)
return false;
if (!S_ISSOCK(buf.st_mode))
return false;
*inode_out = buf.st_ino;
return true;
}
// expected prefix of the target of the /proc/self/fd/%d link for a socket
static const char kSocketLinkPrefix[] = "socket:[";
// Parse a symlink in /proc/pid/fd/$x and return the inode number of the
// socket.
// inode_out: (output) set to the inode number on success
// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
static bool
GetInodeForProcPath(ino_t* inode_out, const char* path)
{
assert(inode_out);
assert(path);
char buf[PATH_MAX];
if (!google_breakpad::SafeReadLink(path, buf)) {
return false;
}
if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) {
return false;
}
char* endptr;
const uint64_t inode_ul =
strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10);
if (*endptr != ']')
return false;
if (inode_ul == ULLONG_MAX) {
return false;
}
*inode_out = inode_ul;
return true;
}
static bool
FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode)
{
assert(pid_out);
bool already_found = false;
DIR* proc = opendir("/proc");
if (!proc) {
return false;
}
std::vector<pid_t> pids;
struct dirent* dent;
while ((dent = readdir(proc))) {
char* endptr;
const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10);
if (pid_ul == ULONG_MAX || '\0' != *endptr)
continue;
pids.push_back(pid_ul);
}
closedir(proc);
for (std::vector<pid_t>::const_iterator
i = pids.begin(); i != pids.end(); ++i) {
const pid_t current_pid = *i;
char buf[PATH_MAX];
snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid);
DIR* fd = opendir(buf);
if (!fd)
continue;
while ((dent = readdir(fd))) {
if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid,
dent->d_name) >= static_cast<int>(sizeof(buf))) {
continue;
}
ino_t fd_inode;
if (GetInodeForProcPath(&fd_inode, buf)
&& fd_inode == socket_inode) {
if (already_found) {
closedir(fd);
return false;
}
already_found = true;
*pid_out = current_pid;
break;
}
}
closedir(fd);
}
return already_found;
}
namespace google_breakpad {
CrashGenerationServer::CrashGenerationServer(
const int listen_fd,
OnClientDumpRequestCallback dump_callback,
void* dump_context,
OnClientExitingCallback exit_callback,
void* exit_context,
bool generate_dumps,
const string* dump_path) :
server_fd_(listen_fd),
dump_callback_(dump_callback),
dump_context_(dump_context),
exit_callback_(exit_callback),
exit_context_(exit_context),
generate_dumps_(generate_dumps),
started_(false)
{
if (dump_path)
dump_dir_ = *dump_path;
else
dump_dir_ = "/tmp";
}
CrashGenerationServer::~CrashGenerationServer()
{
if (started_)
Stop();
}
bool
CrashGenerationServer::Start()
{
if (started_ || 0 > server_fd_)
return false;
int control_pipe[2];
if (pipe(control_pipe))
return false;
if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC))
return false;
if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC))
return false;
if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK))
return false;
control_pipe_in_ = control_pipe[0];
control_pipe_out_ = control_pipe[1];
if (pthread_create(&thread_, NULL,
ThreadMain, reinterpret_cast<void*>(this)))
return false;
started_ = true;
return true;
}
void
CrashGenerationServer::Stop()
{
assert(pthread_self() != thread_);
if (!started_)
return;
HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1));
void* dummy;
pthread_join(thread_, &dummy);
started_ = false;
}
//static
bool
CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd)
{
int fds[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds))
return false;
static const int on = 1;
// Enable passcred on the server end of the socket
if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)))
return false;
if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
return false;
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC))
return false;
*client_fd = fds[0];
*server_fd = fds[1];
return true;
}
// The following methods/functions execute on the server thread
void
CrashGenerationServer::Run()
{
struct pollfd pollfds[2];
memset(&pollfds, 0, sizeof(pollfds));
pollfds[0].fd = server_fd_;
pollfds[0].events = POLLIN;
pollfds[1].fd = control_pipe_in_;
pollfds[1].events = POLLIN;
while (true) {
// infinite timeout
int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1);
if (-1 == nevents) {
if (EINTR == errno) {
continue;
} else {
return;
}
}
if (pollfds[0].revents && !ClientEvent(pollfds[0].revents))
return;
if (pollfds[1].revents && !ControlEvent(pollfds[1].revents))
return;
}
}
bool
CrashGenerationServer::ClientEvent(short revents)
{
if (POLLHUP & revents)
return false;
assert(POLLIN & revents);
// A process has crashed and has signaled us by writing a datagram
// to the death signal socket. The datagram contains the crash context needed
// for writing the minidump as well as a file descriptor and a credentials
// block so that they can't lie about their pid.
// The length of the control message:
static const unsigned kControlMsgSize =
CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
// The length of the regular payload:
static const unsigned kCrashContextSize =
sizeof(google_breakpad::ExceptionHandler::CrashContext);
struct msghdr msg = {0};
struct iovec iov[1];
char crash_context[kCrashContextSize];
char control[kControlMsgSize];
const ssize_t expected_msg_size = sizeof(crash_context);
iov[0].iov_base = crash_context;
iov[0].iov_len = sizeof(crash_context);
msg.msg_iov = iov;
msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]);
msg.msg_control = control;
msg.msg_controllen = kControlMsgSize;
const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0));
if (msg_size != expected_msg_size)
return true;
if (msg.msg_controllen != kControlMsgSize ||
msg.msg_flags & ~MSG_TRUNC)
return true;
// Walk the control payload and extract the file descriptor and validated pid.
pid_t crashing_pid = -1;
int signal_fd = -1;
for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
hdr = CMSG_NXTHDR(&msg, hdr)) {
if (hdr->cmsg_level != SOL_SOCKET)
continue;
if (hdr->cmsg_type == SCM_RIGHTS) {
const unsigned len = hdr->cmsg_len -
(((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
assert(len % sizeof(int) == 0u);
const unsigned num_fds = len / sizeof(int);
if (num_fds > 1 || num_fds == 0) {
// A nasty process could try and send us too many descriptors and
// force a leak.
for (unsigned i = 0; i < num_fds; ++i)
HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]));
return true;
} else {
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
}
} else if (hdr->cmsg_type == SCM_CREDENTIALS) {
const struct ucred *cred =
reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
crashing_pid = cred->pid;
}
}
if (crashing_pid == -1 || signal_fd == -1) {
if (signal_fd)
HANDLE_EINTR(close(signal_fd));
return true;
}
// Kernel bug workaround (broken in 2.6.30 at least):
// The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
// namespaces. Thus |crashing_pid| might be garbage from our point of view.
// In the future we can remove this workaround, but we have to wait a couple
// of years to be sure that it's worked its way out into the world.
ino_t inode_number;
if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) {
HANDLE_EINTR(close(signal_fd));
return true;
}
if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) {
HANDLE_EINTR(close(signal_fd));
return true;
}
string minidump_filename;
if (!MakeMinidumpFilename(minidump_filename))
return true;
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
crashing_pid, crash_context,
kCrashContextSize)) {
HANDLE_EINTR(close(signal_fd));
return true;
}
if (dump_callback_) {
ClientInfo info(crashing_pid, this);
dump_callback_(dump_context_, &info, &minidump_filename);
}
// Send the done signal to the process: it can exit now.
memset(&msg, 0, sizeof(msg));
struct iovec done_iov;
done_iov.iov_base = const_cast<char*>("\x42");
done_iov.iov_len = 1;
msg.msg_iov = &done_iov;
msg.msg_iovlen = 1;
HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL));
HANDLE_EINTR(close(signal_fd));
return true;
}
bool
CrashGenerationServer::ControlEvent(short revents)
{
if (POLLHUP & revents)
return false;
assert(POLLIN & revents);
char command;
if (read(control_pipe_in_, &command, 1))
return false;
switch (command) {
case kCommandQuit:
return false;
default:
assert(0);
}
return true;
}
bool
CrashGenerationServer::MakeMinidumpFilename(string& outFilename)
{
GUID guid;
char guidString[kGUIDStringLength+1];
if (!(CreateGUID(&guid)
&& GUIDToString(&guid, guidString, sizeof(guidString))))
return false;
char path[PATH_MAX];
snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString);
outFilename = path;
return true;
}
// static
void*
CrashGenerationServer::ThreadMain(void *arg)
{
reinterpret_cast<CrashGenerationServer*>(arg)->Run();
return NULL;
}
} // namespace google_breakpad

View File

@ -0,0 +1,135 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
#include <pthread.h>
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
class ClientInfo;
class CrashGenerationServer {
public:
// WARNING: callbacks may be invoked on a different thread
// than that which creates the CrashGenerationServer. They must
// be thread safe.
typedef void (*OnClientDumpRequestCallback)(void* context,
const ClientInfo* client_info,
const string* file_path);
typedef void (*OnClientExitingCallback)(void* context,
const ClientInfo* client_info);
// Create an instance with the given parameters.
//
// Parameter listen_fd: The server fd created by CreateReportChannel().
// Parameter dump_callback: Callback for a client crash dump request.
// Parameter dump_context: Context for client crash dump request callback.
// Parameter exit_callback: Callback for client process exit.
// Parameter exit_context: Context for client exit callback.
// Parameter generate_dumps: Whether to automatically generate dumps.
// Client code of this class might want to generate dumps explicitly
// in the crash dump request callback. In that case, false can be
// passed for this parameter.
// Parameter dump_path: Path for generating dumps; required only if true is
// passed for generateDumps parameter; NULL can be passed otherwise.
CrashGenerationServer(const int listen_fd,
OnClientDumpRequestCallback dump_callback,
void* dump_context,
OnClientExitingCallback exit_callback,
void* exit_context,
bool generate_dumps,
const string* dump_path);
~CrashGenerationServer();
// Perform initialization steps needed to start listening to clients.
//
// Return true if initialization is successful; false otherwise.
bool Start();
// Stop the server.
void Stop();
// Create a "channel" that can be used by clients to report crashes
// to a CrashGenerationServer. |*server_fd| should be passed to
// this class's constructor, and |*client_fd| should be passed to
// the ExceptionHandler constructor in the client process.
static bool CreateReportChannel(int* server_fd, int* client_fd);
private:
// Run the server's event loop
void Run();
// Invoked when an child process (client) event occurs
// Returning true => "keep running", false => "exit loop"
bool ClientEvent(short revents);
// Invoked when the controlling thread (main) event occurs
// Returning true => "keep running", false => "exit loop"
bool ControlEvent(short revents);
// Return a unique filename at which a minidump can be written
bool MakeMinidumpFilename(string& outFilename);
// Trampoline to |Run()|
static void* ThreadMain(void* arg);
int server_fd_;
OnClientDumpRequestCallback dump_callback_;
void* dump_context_;
OnClientExitingCallback exit_callback_;
void* exit_context_;
bool generate_dumps_;
string dump_dir_;
bool started_;
pthread_t thread_;
int control_pipe_in_;
int control_pipe_out_;
// disable these
CrashGenerationServer(const CrashGenerationServer&);
CrashGenerationServer& operator=(const CrashGenerationServer&);
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_

View File

@ -0,0 +1,3 @@
MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so
PUBLIC 400 0 __kernel_vsyscall
STACK WIN 4 400 100 1 1 0 0 0 0 0 1

View File

@ -0,0 +1,3 @@
MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so
PUBLIC 400 0 __kernel_vsyscall
STACK WIN 4 400 200 3 3 0 0 0 0 0 1

View File

@ -0,0 +1,688 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The ExceptionHandler object installs signal handlers for a number of
// signals. We rely on the signal handler running on the thread which crashed
// in order to identify it. This is true of the synchronous signals (SEGV etc),
// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
// uses ExceptionHandler, you need to use tgkill to direct it to the current
// thread.
//
// The signal flow looks like this:
//
// SignalHandler (uses a global stack of ExceptionHandler objects to find
// | one to handle the signal. If the first rejects it, try
// | the second etc...)
// V
// HandleSignal ----------------------------| (clones a new process which
// | | shares an address space with
// (wait for cloned | the crashed process. This
// process) | allows us to ptrace the crashed
// | | process)
// V V
// (set signal handler to ThreadEntry (static function to bounce
// SIG_DFL and rethrow, | back into the object)
// killing the crashed |
// process) V
// DoDump (writes minidump)
// |
// V
// sys_exit
//
// This code is a little fragmented. Different functions of the ExceptionHandler
// class run in a number of different contexts. Some of them run in a normal
// context and are easy to code, others run in a compromised context and the
// restrictions at the top of minidump_writer.cc apply: no libc and use the
// alternative malloc. Each function should have comment above it detailing the
// context which it runs in.
#include "client/linux/handler/exception_handler.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
#include <sys/signal.h>
#include <sys/ucontext.h>
#include <sys/user.h>
#include <ucontext.h>
#include <algorithm>
#include <utility>
#include <vector>
#include "common/linux/linux_libc_support.h"
#include "common/memory.h"
#include "client/linux/log/log.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/linux/eintr_wrapper.h"
#include "third_party/lss/linux_syscall_support.h"
#if defined(__ANDROID__)
#include "linux/sched.h"
#endif
#ifndef PR_SET_PTRACER
#define PR_SET_PTRACER 0x59616d61
#endif
// A wrapper for the tgkill syscall: send a signal to a specific thread.
static int tgkill(pid_t tgid, pid_t tid, int sig) {
return syscall(__NR_tgkill, tgid, tid, sig);
return 0;
}
namespace google_breakpad {
namespace {
// The list of signals which we consider to be crashes. The default action for
// 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
};
const int kNumHandledSignals =
sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]);
struct sigaction old_handlers[kNumHandledSignals];
bool handlers_installed = false;
// InstallAlternateStackLocked will store the newly installed stack in new_stack
// and (if it exists) the previously installed stack in old_stack.
stack_t old_stack;
stack_t new_stack;
bool stack_installed = false;
// Create an alternative stack to run the signal handlers on. This is done since
// the signal might have been caused by a stack overflow.
// Runs before crashing: normal context.
void InstallAlternateStackLocked() {
if (stack_installed)
return;
memset(&old_stack, 0, sizeof(old_stack));
memset(&new_stack, 0, sizeof(new_stack));
// SIGSTKSZ may be too small to prevent the signal handlers from overrunning
// the alternative stack. Ensure that the size of the alternative stack is
// large enough.
static const unsigned kSigStackSize = std::max(8192, SIGSTKSZ);
// Only set an alternative stack if there isn't already one, or if the current
// one is too small.
if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
old_stack.ss_size < kSigStackSize) {
new_stack.ss_sp = malloc(kSigStackSize);
new_stack.ss_size = kSigStackSize;
if (sys_sigaltstack(&new_stack, NULL) == -1) {
free(new_stack.ss_sp);
return;
}
stack_installed = true;
}
}
// Runs before crashing: normal context.
void RestoreAlternateStackLocked() {
if (!stack_installed)
return;
stack_t current_stack;
if (sys_sigaltstack(NULL, &current_stack) == -1)
return;
// Only restore the old_stack if the current alternative stack is the one
// installed by the call to InstallAlternateStackLocked.
if (current_stack.ss_sp == new_stack.ss_sp) {
if (old_stack.ss_sp) {
if (sys_sigaltstack(&old_stack, NULL) == -1)
return;
} else {
stack_t disable_stack;
disable_stack.ss_flags = SS_DISABLE;
if (sys_sigaltstack(&disable_stack, NULL) == -1)
return;
}
}
free(new_stack.ss_sp);
stack_installed = false;
}
} // namespace
// We can stack multiple exception handlers. In that case, this is the global
// which holds the stack.
std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
PTHREAD_MUTEX_INITIALIZER;
// Runs before crashing: normal context.
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
void* callback_context,
bool install_handler,
const int server_fd)
: filter_(filter),
callback_(callback),
callback_context_(callback_context),
minidump_descriptor_(descriptor),
crash_handler_(NULL) {
if (server_fd >= 0)
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD())
minidump_descriptor_.UpdatePath();
pthread_mutex_lock(&handler_stack_mutex_);
if (!handler_stack_)
handler_stack_ = new std::vector<ExceptionHandler*>;
if (install_handler) {
InstallAlternateStackLocked();
InstallHandlersLocked();
}
handler_stack_->push_back(this);
pthread_mutex_unlock(&handler_stack_mutex_);
}
// Runs before crashing: normal context.
ExceptionHandler::~ExceptionHandler() {
pthread_mutex_lock(&handler_stack_mutex_);
std::vector<ExceptionHandler*>::iterator handler =
std::find(handler_stack_->begin(), handler_stack_->end(), this);
handler_stack_->erase(handler);
if (handler_stack_->empty()) {
RestoreAlternateStackLocked();
RestoreHandlersLocked();
}
pthread_mutex_unlock(&handler_stack_mutex_);
}
// Runs before crashing: normal context.
// static
bool ExceptionHandler::InstallHandlersLocked() {
if (handlers_installed)
return false;
// Fail if unable to store all the old handlers.
for (int i = 0; i < kNumHandledSignals; ++i) {
if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1)
return false;
}
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sigemptyset(&sa.sa_mask);
// Mask all exception signals when we're handling one of them.
for (int i = 0; i < kNumHandledSignals; ++i)
sigaddset(&sa.sa_mask, kExceptionSignals[i]);
sa.sa_sigaction = SignalHandler;
sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
for (int i = 0; i < kNumHandledSignals; ++i) {
if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) {
// At this point it is impractical to back out changes, and so failure to
// install a signal is intentionally ignored.
}
}
handlers_installed = true;
return true;
}
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
// static
void ExceptionHandler::RestoreHandlersLocked() {
if (!handlers_installed)
return;
for (int i = 0; i < kNumHandledSignals; ++i) {
if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) {
signal(kExceptionSignals[i], SIG_DFL);
}
}
handlers_installed = false;
}
// void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
// crash_handler_ = callback;
// }
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
// static
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
// All the exception signals are blocked at this point.
pthread_mutex_lock(&handler_stack_mutex_);
// Sometimes, Breakpad runs inside a process where some other buggy code
// saves and restores signal handlers temporarily with 'signal'
// instead of 'sigaction'. This loses the SA_SIGINFO flag associated
// with this function. As a consequence, the values of 'info' and 'uc'
// become totally bogus, generally inducing a crash.
//
// The following code tries to detect this case. When it does, it
// resets the signal handlers with sigaction + SA_SIGINFO and returns.
// This forces the signal to be thrown again, but this time the kernel
// will call the function with the right arguments.
struct sigaction cur_handler;
if (sigaction(sig, NULL, &cur_handler) == 0 &&
(cur_handler.sa_flags & SA_SIGINFO) == 0) {
// Reset signal handler with the right flags.
sigemptyset(&cur_handler.sa_mask);
sigaddset(&cur_handler.sa_mask, sig);
cur_handler.sa_sigaction = SignalHandler;
cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO;
if (sigaction(sig, &cur_handler, NULL) == -1) {
// When resetting the handler fails, try to reset the
// default one to avoid an infinite loop here.
signal(sig, SIG_DFL);
}
pthread_mutex_unlock(&handler_stack_mutex_);
return;
}
bool handled = false;
for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) {
handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc);
}
// Upon returning from this signal handler, sig will become unmasked and then
// it will be retriggered. If one of the ExceptionHandlers handled it
// successfully, restore the default handler. Otherwise, restore the
// previously installed handler. Then, when the signal is retriggered, it will
// be delivered to the appropriate handler.
if (handled) {
signal(sig, SIG_DFL);
} else {
RestoreHandlersLocked();
}
pthread_mutex_unlock(&handler_stack_mutex_);
if (info->si_pid) {
// This signal was triggered by somebody sending us the signal with kill().
// In order to retrigger it, we have to queue a new signal by calling
// kill() ourselves.
if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
// If we failed to kill ourselves (e.g. because a sandbox disallows us
// to do so), we instead resort to terminating our process. This will
// result in an incorrect exit code.
_exit(1);
}
} else {
// This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
// No need to reissue the signal. It will automatically trigger again,
// when we return from the signal handler.
}
}
struct ThreadArgument {
pid_t pid; // the crashing process
const MinidumpDescriptor* minidump_descriptor;
ExceptionHandler* handler;
const void* context; // a CrashContext structure
size_t context_size;
};
// This is the entry function for the cloned process. We are in a compromised
// context here: see the top of the file.
// static
int ExceptionHandler::ThreadEntry(void *arg) {
const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
// Block here until the crashing process unblocks us when
// we're allowed to use ptrace
thread_arg->handler->WaitForContinueSignal();
return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
thread_arg->context_size) == false;
}
// This function runs in a compromised context: see the top of the file.
// Runs on the crashing thread.
bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
if (filter_ && !filter_(callback_context_))
return false;
// Allow ourselves to be dumped if the signal is trusted.
bool signal_trusted = info->si_code > 0;
bool signal_pid_trusted = info->si_code == SI_USER ||
info->si_code == SI_TKILL;
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
sys_prctl(PR_SET_DUMPABLE, 1);
}
CrashContext context;
memcpy(&context.siginfo, info, sizeof(siginfo_t));
memcpy(&context.context, uc, sizeof(struct ucontext));
#if !defined(__ARM_EABI__)
// FP state is not part of user ABI on ARM Linux.
struct ucontext *uc_ptr = (struct ucontext*)uc;
if (uc_ptr->uc_mcontext.fpregs) {
memcpy(&context.float_state,
uc_ptr->uc_mcontext.fpregs,
sizeof(context.float_state));
}
#endif
context.tid = syscall(__NR_gettid);
if (crash_handler_ != NULL) {
if (crash_handler_(&context, sizeof(context), callback_context_)) {
return true;
}
}
return GenerateDump(&context);
}
// This is a public interface to HandleSignal that allows the client to
// generate a crash dump. This function may run in a compromised context.
bool ExceptionHandler::SimulateSignalDelivery(int sig) {
siginfo_t siginfo = {};
// Mimic a trusted signal to allow tracing the process (see
// ExceptionHandler::HandleSignal().
siginfo.si_code = SI_USER;
siginfo.si_pid = getpid();
struct ucontext context;
getcontext(&context);
return HandleSignal(sig, &siginfo, &context);
}
// This function may run in a compromised context: see the top of the file.
bool ExceptionHandler::GenerateDump(CrashContext *context) {
if (IsOutOfProcess())
return crash_generation_client_->RequestDump(context, sizeof(*context));
static const unsigned kChildStackSize = 8000;
PageAllocator allocator;
uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
if (!stack)
return false;
// clone() needs the top-most address. (scrub just to be safe)
stack += kChildStackSize;
my_memset(stack - 16, 0, 16);
ThreadArgument thread_arg;
thread_arg.handler = this;
thread_arg.minidump_descriptor = &minidump_descriptor_;
thread_arg.pid = getpid();
thread_arg.context = context;
thread_arg.context_size = sizeof(*context);
// We need to explicitly enable ptrace of parent processes on some
// kernels, but we need to know the PID of the cloned process before we
// can do this. Create a pipe here which we can use to block the
// cloned process after creating it, until we have explicitly enabled ptrace
if(sys_pipe(fdes) == -1) {
// Creating the pipe failed. We'll log an error but carry on anyway,
// as we'll probably still get a useful crash report. All that will happen
// is the write() and read() calls will fail with EBADF
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
sys_pipe failed:";
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
const pid_t child = sys_clone(
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
&thread_arg, NULL, NULL, NULL);
int r, status;
// Allow the child to ptrace us
sys_prctl(PR_SET_PTRACER, child);
SendContinueSignalToChild();
do {
r = sys_waitpid(child, &status, __WALL);
} while (r == -1 && errno == EINTR);
sys_close(fdes[0]);
sys_close(fdes[1]);
if (r == -1) {
static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
if (callback_)
success = callback_(minidump_descriptor_, callback_context_, success);
return success;
}
// This function runs in a compromised context: see the top of the file.
void ExceptionHandler::SendContinueSignalToChild() {
static const char okToContinueMessage = 'a';
int r;
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
if(r == -1) {
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
sys_write failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
}
// This function runs in a compromised context: see the top of the file.
// Runs on the cloned process.
void ExceptionHandler::WaitForContinueSignal() {
int r;
char receivedMessage;
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
if(r == -1) {
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
sys_read failed:";
logger::write(msg, sizeof(msg) - 1);
logger::write(strerror(errno), strlen(strerror(errno)));
logger::write("\n", 1);
}
}
// This function runs in a compromised context: see the top of the file.
// Runs on the cloned process.
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
size_t context_size) {
if (minidump_descriptor_.IsFD()) {
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
minidump_descriptor_.size_limit(),
crashing_process,
context,
context_size,
mapping_list_,
app_memory_list_);
}
return google_breakpad::WriteMinidump(minidump_descriptor_.path(),
minidump_descriptor_.size_limit(),
crashing_process,
context,
context_size,
mapping_list_,
app_memory_list_);
}
// static
bool ExceptionHandler::WriteMinidump(const string& dump_path,
MinidumpCallback callback,
void* callback_context) {
MinidumpDescriptor descriptor(dump_path);
ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1);
return eh.WriteMinidump();
}
// In order to making using EBP to calculate the desired value for ESP
// a valid operation, ensure that this function is compiled with a
// frame pointer using the following attribute. This attribute
// is supported on GCC but not on clang.
#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__)
__attribute__((optimize("no-omit-frame-pointer")))
#endif
bool ExceptionHandler::WriteMinidump() {
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) {
// Update the path of the minidump so that this can be called multiple times
// and new files are created for each minidump. This is done before the
// generation happens, as clients may want to access the MinidumpDescriptor
// after this call to find the exact path to the minidump file.
minidump_descriptor_.UpdatePath();
} else if (minidump_descriptor_.IsFD()) {
// Reposition the FD to its beginning and resize it to get rid of the
// previous minidump info.
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
static_cast<void>(ftruncate(minidump_descriptor_.fd(), 0));
}
// Allow this process to be dumped.
sys_prctl(PR_SET_DUMPABLE, 1);
CrashContext context;
int getcontext_result = getcontext(&context.context);
if (getcontext_result)
return false;
#if defined(__i386__)
// In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved
// from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer
// and it only makes sense when running in kernel mode with a different stack
// pointer. When WriteMiniDump is called during normal processing REG_UESP is
// zero which leads to bad minidump files.
if (!context.context.uc_mcontext.gregs[REG_UESP]) {
// If REG_UESP is set to REG_ESP then that includes the stack space for the
// CrashContext object in this function, which is about 128 KB. Since the
// Linux dumper only records 32 KB of stack this would mean that nothing
// useful would be recorded. A better option is to set REG_UESP to REG_EBP,
// perhaps with a small negative offset in case there is any code that
// objects to them being equal.
context.context.uc_mcontext.gregs[REG_UESP] =
context.context.uc_mcontext.gregs[REG_EBP] - 16;
// The stack saving is based off of REG_ESP so it must be set to match the
// new REG_UESP.
context.context.uc_mcontext.gregs[REG_ESP] =
context.context.uc_mcontext.gregs[REG_UESP];
}
#endif
#if !defined(__ARM_EABI__)
// FPU state is not part of ARM EABI ucontext_t.
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
sizeof(context.float_state));
#endif
context.tid = sys_gettid();
// Add an exception stream to the minidump for better reporting.
memset(&context.siginfo, 0, sizeof(context.siginfo));
context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED;
#if defined(__i386__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]);
#elif defined(__x86_64__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]);
#elif defined(__arm__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
#else
#error "This code has not been ported to your platform yet."
#endif
return GenerateDump(&context);
}
void ExceptionHandler::AddMappingInfo(const string& name,
const uint8_t identifier[sizeof(MDGUID)],
uintptr_t start_address,
size_t mapping_size,
size_t file_offset) {
MappingInfo info;
info.start_addr = start_address;
info.size = mapping_size;
info.offset = file_offset;
strncpy(info.name, name.c_str(), sizeof(info.name) - 1);
info.name[sizeof(info.name) - 1] = '\0';
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, identifier, sizeof(MDGUID));
mapping_list_.push_back(mapping);
}
void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) {
AppMemoryList::iterator iter =
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
if (iter != app_memory_list_.end()) {
// Don't allow registering the same pointer twice.
return;
}
AppMemory app_memory;
app_memory.ptr = ptr;
app_memory.length = length;
app_memory_list_.push_back(app_memory);
}
void ExceptionHandler::UnregisterAppMemory(void* ptr) {
AppMemoryList::iterator iter =
std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr);
if (iter != app_memory_list_.end()) {
app_memory_list_.erase(iter);
}
}
// static
bool ExceptionHandler::WriteMinidumpForChild(pid_t child,
pid_t child_blamed_thread,
const string& dump_path,
MinidumpCallback callback,
void* callback_context) {
// This function is not run in a compromised context.
MinidumpDescriptor descriptor(dump_path);
descriptor.UpdatePath();
if (!google_breakpad::WriteMinidump(descriptor.path(),
child,
child_blamed_thread))
return false;
return callback ? callback(descriptor, callback_context, true) : true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,273 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
#include <string>
#include <vector>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/ucontext.h>
#include "client/linux/crash_generation/crash_generation_client.h"
#include "client/linux/handler/minidump_descriptor.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
// ExceptionHandler
//
// ExceptionHandler can write a minidump file when an exception occurs,
// or when WriteMinidump() is called explicitly by your program.
//
// To have the exception handler write minidumps when an uncaught exception
// (crash) occurs, you should create an instance early in the execution
// of your program, and keep it around for the entire time you want to
// have crash handling active (typically, until shutdown).
// (NOTE): There should be only be one this kind of exception handler
// object per process.
//
// If you want to write minidumps without installing the exception handler,
// you can create an ExceptionHandler with install_handler set to false,
// then call WriteMinidump. You can also use this technique if you want to
// use different minidump callbacks for different call sites.
//
// In either case, a callback function is called when a minidump is written,
// which receives the full path or file descriptor of the minidump. The
// caller can collect and write additional application state to that minidump,
// and launch an external crash-reporting application.
//
// Caller should try to make the callbacks as crash-friendly as possible,
// it should avoid use heap memory allocation as much as possible.
class ExceptionHandler {
public:
// A callback function to run before Breakpad performs any substantial
// processing of an exception. A FilterCallback is called before writing
// a minidump. |context| is the parameter supplied by the user as
// callback_context when the handler was created.
//
// If a FilterCallback returns true, Breakpad will continue processing,
// attempting to write a minidump. If a FilterCallback returns false,
// Breakpad will immediately report the exception as unhandled without
// writing a minidump, allowing another handler the opportunity to handle it.
typedef bool (*FilterCallback)(void *context);
// A callback function to run after the minidump has been written.
// |descriptor| contains the file descriptor or file path containing the
// minidump. |context| is the parameter supplied by the user as
// callback_context when the handler was created. |succeeded| indicates
// whether a minidump file was successfully written.
//
// If an exception occurred and the callback returns true, Breakpad will
// treat the exception as fully-handled, suppressing any other handlers from
// being notified of the exception. If the callback returns false, Breakpad
// will treat the exception as unhandled, and allow another handler to handle
// it. If there are no other handlers, Breakpad will report the exception to
// the system as unhandled, allowing a debugger or native crash dialog the
// opportunity to handle the exception. Most callback implementations
// should normally return the value of |succeeded|, or when they wish to
// not report an exception of handled, false. Callbacks will rarely want to
// return true directly (unless |succeeded| is true).
typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor,
void* context,
bool succeeded);
// In certain cases, a user may wish to handle the generation of the minidump
// themselves. In this case, they can install a handler callback which is
// called when a crash has occurred. If this function returns true, no other
// processing of occurs and the process will shortly be crashed. If this
// returns false, the normal processing continues.
typedef bool (*HandlerCallback)(const void* crash_context,
size_t crash_context_size,
void* context);
// Creates a new ExceptionHandler instance to handle writing minidumps.
// Before writing a minidump, the optional |filter| callback will be called.
// Its return value determines whether or not Breakpad should write a
// minidump. The minidump content will be written to the file path or file
// descriptor from |descriptor|, and the optional |callback| is called after
// writing the dump file, as described above.
// If install_handler is true, then a minidump will be written whenever
// an unhandled exception occurs. If it is false, minidumps will only
// be written when WriteMinidump is called.
// If |server_fd| is valid, the minidump is generated out-of-process. If it
// is -1, in-process generation will always be used.
ExceptionHandler(const MinidumpDescriptor& descriptor,
FilterCallback filter,
MinidumpCallback callback,
void *callback_context,
bool install_handler,
const int server_fd);
~ExceptionHandler();
const MinidumpDescriptor& minidump_descriptor() const {
return minidump_descriptor_;
}
void set_minidump_descriptor(const MinidumpDescriptor& descriptor) {
minidump_descriptor_ = descriptor;
}
void set_crash_handler(HandlerCallback callback) {
crash_handler_ = callback;
}
// Writes a minidump immediately. This can be used to capture the execution
// state independently of a crash.
// Returns true on success.
// If the ExceptionHandler has been created with a path, a new file is
// generated for each minidump. The file path can be retrieved in the
// MinidumpDescriptor passed to the MinidumpCallback or by accessing the
// MinidumpDescriptor directly from the ExceptionHandler (with
// minidump_descriptor()).
// If the ExceptionHandler has been created with a file descriptor, the file
// descriptor is repositioned to its beginning and the previous generated
// minidump is overwritten.
// Note that this method is not supposed to be called from a compromised
// context as it uses the heap.
bool WriteMinidump();
// Convenience form of WriteMinidump which does not require an
// ExceptionHandler instance.
static bool WriteMinidump(const string& dump_path,
MinidumpCallback callback,
void* callback_context);
// Write a minidump of |child| immediately. This can be used to
// capture the execution state of |child| independently of a crash.
// Pass a meaningful |child_blamed_thread| to make that thread in
// the child process the one from which a crash signature is
// extracted.
//
// WARNING: the return of this function *must* happen before
// the code that will eventually reap |child| executes.
// Otherwise there's a pernicious race condition in which |child|
// exits, is reaped, another process created with its pid, then that
// new process dumped.
static bool WriteMinidumpForChild(pid_t child,
pid_t child_blamed_thread,
const string& dump_path,
MinidumpCallback callback,
void* callback_context);
// This structure is passed to minidump_writer.h:WriteMinidump via an opaque
// blob. It shouldn't be needed in any user code.
struct CrashContext {
siginfo_t siginfo;
pid_t tid; // the crashing thread.
struct ucontext context;
#if !defined(__ARM_EABI__)
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
struct _libc_fpstate float_state;
#endif
};
// Returns whether out-of-process dump generation is used or not.
bool IsOutOfProcess() const {
return crash_generation_client_.get() != NULL;
}
// Add information about a memory mapping. This can be used if
// a custom library loader is used that maps things in a way
// that the linux dumper can't handle by reading the maps file.
void AddMappingInfo(const string& name,
const uint8_t identifier[sizeof(MDGUID)],
uintptr_t start_address,
size_t mapping_size,
size_t file_offset);
// Register a block of memory of length bytes starting at address ptr
// to be copied to the minidump when a crash happens.
void RegisterAppMemory(void* ptr, size_t length);
// Unregister a block of memory that was registered with RegisterAppMemory.
void UnregisterAppMemory(void* ptr);
// Force signal handling for the specified signal.
bool SimulateSignalDelivery(int sig);
private:
// Save the old signal handlers and install new ones.
static bool InstallHandlersLocked();
// Restore the old signal handlers.
static void RestoreHandlersLocked();
void PreresolveSymbols();
bool GenerateDump(CrashContext *context);
void SendContinueSignalToChild();
void WaitForContinueSignal();
static void SignalHandler(int sig, siginfo_t* info, void* uc);
bool HandleSignal(int sig, siginfo_t* info, void* uc);
static int ThreadEntry(void* arg);
bool DoDump(pid_t crashing_process, const void* context,
size_t context_size);
const FilterCallback filter_;
const MinidumpCallback callback_;
void* const callback_context_;
scoped_ptr<CrashGenerationClient> crash_generation_client_;
MinidumpDescriptor minidump_descriptor_;
HandlerCallback crash_handler_;
// The global exception handler stack. This is need becuase there may exist
// multiple ExceptionHandler instances in a process. Each will have itself
// registered in this stack.
static std::vector<ExceptionHandler*> *handler_stack_;
static pthread_mutex_t handler_stack_mutex_;
// We need to explicitly enable ptrace of parent processes on some
// kernels, but we need to know the PID of the cloned process before we
// can do this. We create a pipe which we can use to block the
// cloned process after creating it, until we have explicitly enabled
// ptrace. This is used to store the file descriptors for the pipe
int fdes[2];
// Callers can add extra info about mappings for cases where the
// dumper code cannot extract enough information from /proc/<pid>/maps.
MappingList mapping_list_;
// Callers can request additional memory regions to be included in
// the dump.
AppMemoryList app_memory_list_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,79 @@
// Copyright (c) 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdio.h>
#include "client/linux/handler/minidump_descriptor.h"
#include "common/linux/guid_creator.h"
namespace google_breakpad {
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
: fd_(descriptor.fd_),
directory_(descriptor.directory_),
c_path_(NULL),
size_limit_(descriptor.size_limit_) {
// The copy constructor is not allowed to be called on a MinidumpDescriptor
// with a valid path_, as getting its c_path_ would require the heap which
// can cause problems in compromised environments.
assert(descriptor.path_.empty());
}
MinidumpDescriptor& MinidumpDescriptor::operator=(
const MinidumpDescriptor& descriptor) {
assert(descriptor.path_.empty());
fd_ = descriptor.fd_;
directory_ = descriptor.directory_;
path_.clear();
if (c_path_) {
// This descriptor already had a path set, so generate a new one.
c_path_ = NULL;
UpdatePath();
}
size_limit_ = descriptor.size_limit_;
return *this;
}
void MinidumpDescriptor::UpdatePath() {
assert(fd_ == -1 && !directory_.empty());
GUID guid;
char guid_str[kGUIDStringLength + 1];
if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) {
assert(false);
}
path_.clear();
path_ = directory_ + "/" + guid_str + ".dmp";
c_path_ = path_.c_str();
}
} // namespace google_breakpad

View File

@ -0,0 +1,100 @@
// Copyright (c) 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
#include <assert.h>
#include <sys/types.h>
#include <string>
#include "common/using_std_string.h"
// The MinidumpDescriptor describes how to access a minidump: it can contain
// either a file descriptor or a path.
// Note that when using files, it is created with the path to a directory.
// The actual path where the minidump is generated is created by this class.
namespace google_breakpad {
class MinidumpDescriptor {
public:
MinidumpDescriptor() : fd_(-1), size_limit_(-1) {}
explicit MinidumpDescriptor(const string& directory)
: fd_(-1),
directory_(directory),
c_path_(NULL),
size_limit_(-1) {
assert(!directory.empty());
}
explicit MinidumpDescriptor(int fd)
: fd_(fd),
c_path_(NULL),
size_limit_(-1) {
assert(fd != -1);
}
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
bool IsFD() const { return fd_ != -1; }
int fd() const { return fd_; }
string directory() const { return directory_; }
const char* path() const { return c_path_; }
// Updates the path so it is unique.
// Should be called from a normal context: this methods uses the heap.
void UpdatePath();
off_t size_limit() const { return size_limit_; }
void set_size_limit(off_t limit) { size_limit_ = limit; }
private:
// The file descriptor where the minidump is generated.
int fd_;
// The directory where the minidump should be generated.
string directory_;
// The full path to the generated minidump.
string path_;
// The C string of |path_|. Precomputed so it can be access from a compromised
// context.
const char* c_path_;
off_t size_limit_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_

View File

@ -0,0 +1,48 @@
// Copyright (c) 2012 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "client/linux/log/log.h"
#if defined(__ANDROID__)
#include <android/log.h>
#else
#include "third_party/lss/linux_syscall_support.h"
#endif
namespace logger {
int write(const char* buf, size_t nbytes) {
#if defined(__ANDROID__)
return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf);
#else
return sys_write(2, buf, nbytes);
#endif
}
} // namespace logger

View File

@ -0,0 +1,41 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_LOG_LOG_H_
#define CLIENT_LINUX_LOG_LOG_H_
#include <stddef.h>
namespace logger {
int write(const char* buf, size_t nbytes);
} // namespace logger
#endif // CLIENT_LINUX_LOG_LOG_H_

View File

@ -0,0 +1,144 @@
// Copyright (c) 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// Helper class used to model a set of CPUs, as read from sysfs
// files like /sys/devices/system/cpu/present
// See See http://www.kernel.org/doc/Documentation/cputopology.txt
class CpuSet {
public:
// The maximum number of supported CPUs.
static const size_t kMaxCpus = 1024;
CpuSet() {
my_memset(mask_, 0, sizeof(mask_));
}
// Parse a sysfs file to extract the corresponding CPU set.
bool ParseSysFile(int fd) {
char buffer[512];
int ret = sys_read(fd, buffer, sizeof(buffer)-1);
if (ret < 0)
return false;
buffer[ret] = '\0';
// Expected format: comma-separated list of items, where each
// item can be a decimal integer, or two decimal integers separated
// by a dash.
// E.g.:
// 0
// 0,1,2,3
// 0-3
// 1,10-23
const char* p = buffer;
const char* p_end = p + ret;
while (p < p_end) {
// Skip leading space, if any
while (p < p_end && my_isspace(*p))
p++;
// Find start and size of current item.
const char* item = p;
size_t item_len = static_cast<size_t>(p_end - p);
const char* item_next =
static_cast<const char*>(my_memchr(p, ',', item_len));
if (item_next != NULL) {
p = item_next + 1;
item_len = static_cast<size_t>(item_next - item);
} else {
p = p_end;
item_next = p_end;
}
// Ignore trailing spaces.
while (item_next > item && my_isspace(item_next[-1]))
item_next--;
// skip empty items.
if (item_next == item)
continue;
// read first decimal value.
uintptr_t start = 0;
const char* next = my_read_decimal_ptr(&start, item);
uintptr_t end = start;
if (*next == '-')
my_read_decimal_ptr(&end, next+1);
while (start <= end)
SetBit(start++);
}
return true;
}
// Intersect this CPU set with another one.
void IntersectWith(const CpuSet& other) {
for (size_t nn = 0; nn < kMaskWordCount; ++nn)
mask_[nn] &= other.mask_[nn];
}
// Return the number of CPUs in this set.
int GetCount() {
int result = 0;
for (size_t nn = 0; nn < kMaskWordCount; ++nn) {
result += __builtin_popcount(mask_[nn]);
}
return result;
}
private:
void SetBit(uintptr_t index) {
size_t nn = static_cast<size_t>(index);
if (nn < kMaxCpus)
mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits));
}
typedef uint32_t MaskWordType;
static const size_t kMaskWordBits = 8*sizeof(MaskWordType);
static const size_t kMaskWordCount =
(kMaxCpus + kMaskWordBits - 1) / kMaskWordBits;
MaskWordType mask_[kMaskWordCount];
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_

View File

@ -0,0 +1,165 @@
// Copyright (c) 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/cpu_set.h"
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_testfile.h"
using namespace google_breakpad;
namespace {
typedef testing::Test CpuSetTest;
// Helper class to write test text file to a temporary file and return
// its file descriptor.
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("cpu_set", text) {
}
};
}
TEST(CpuSetTest, EmptyCount) {
CpuSet set;
ASSERT_EQ(0, set.GetCount());
}
TEST(CpuSetTest, OneCpu) {
ScopedTestFile file("10");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(1, set.GetCount());
}
TEST(CpuSetTest, OneCpuTerminated) {
ScopedTestFile file("10\n");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(1, set.GetCount());
}
TEST(CpuSetTest, TwoCpusWithComma) {
ScopedTestFile file("1,10");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(2, set.GetCount());
}
TEST(CpuSetTest, TwoCpusWithRange) {
ScopedTestFile file("1-2");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(2, set.GetCount());
}
TEST(CpuSetTest, TenCpusWithRange) {
ScopedTestFile file("9-18");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(10, set.GetCount());
}
TEST(CpuSetTest, MultiItems) {
ScopedTestFile file("0, 2-4, 128");
ASSERT_TRUE(file.IsOk());
CpuSet set;
ASSERT_TRUE(set.ParseSysFile(file.GetFd()));
ASSERT_EQ(5, set.GetCount());
}
TEST(CpuSetTest, IntersectWith) {
ScopedTestFile file1("9-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(11, set1.GetCount());
ScopedTestFile file2("16-24");
ASSERT_TRUE(file2.IsOk());
CpuSet set2;
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
ASSERT_EQ(9, set2.GetCount());
set1.IntersectWith(set2);
ASSERT_EQ(4, set1.GetCount());
ASSERT_EQ(9, set2.GetCount());
}
TEST(CpuSetTest, SelfIntersection) {
ScopedTestFile file1("9-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(11, set1.GetCount());
set1.IntersectWith(set1);
ASSERT_EQ(11, set1.GetCount());
}
TEST(CpuSetTest, EmptyIntersection) {
ScopedTestFile file1("0-19");
ASSERT_TRUE(file1.IsOk());
CpuSet set1;
ASSERT_TRUE(set1.ParseSysFile(file1.GetFd()));
ASSERT_EQ(20, set1.GetCount());
ScopedTestFile file2("20-39");
ASSERT_TRUE(file2.IsOk());
CpuSet set2;
ASSERT_TRUE(set2.ParseSysFile(file2.GetFd()));
ASSERT_EQ(20, set2.GetCount());
set1.IntersectWith(set2);
ASSERT_EQ(0, set1.GetCount());
ASSERT_EQ(20, set2.GetCount());
}

View File

@ -0,0 +1,106 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// A class for enumerating a directory without using diropen/readdir or other
// functions which may allocate memory.
class DirectoryReader {
public:
DirectoryReader(int fd)
: fd_(fd),
buf_used_(0) {
}
// Return the next entry from the directory
// name: (output) the NUL terminated entry name
//
// Returns true iff successful (false on EOF).
//
// After calling this, one must call |PopEntry| otherwise you'll get the same
// entry over and over.
bool GetNextEntry(const char** name) {
struct kernel_dirent* const dent =
reinterpret_cast<kernel_dirent*>(buf_);
if (buf_used_ == 0) {
// need to read more entries.
const int n = sys_getdents(fd_, dent, sizeof(buf_));
if (n < 0) {
return false;
} else if (n == 0) {
hit_eof_ = true;
} else {
buf_used_ += n;
}
}
if (buf_used_ == 0 && hit_eof_)
return false;
assert(buf_used_ > 0);
*name = dent->d_name;
return true;
}
void PopEntry() {
if (!buf_used_)
return;
const struct kernel_dirent* const dent =
reinterpret_cast<kernel_dirent*>(buf_);
buf_used_ -= dent->d_reclen;
my_memmove(buf_, buf_ + dent->d_reclen, buf_used_);
}
private:
const int fd_;
bool hit_eof_;
unsigned buf_used_;
uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1];
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_

View File

@ -0,0 +1,78 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <set>
#include <string>
#include <dirent.h>
#include <fcntl.h>
#include <sys/types.h>
#include "client/linux/minidump_writer/directory_reader.h"
#include "common/using_std_string.h"
#include "breakpad_googletest_includes.h"
using namespace google_breakpad;
namespace {
typedef testing::Test DirectoryReaderTest;
}
TEST(DirectoryReaderTest, CompareResults) {
std::set<string> dent_set;
DIR *const dir = opendir("/proc/self");
ASSERT_TRUE(dir != NULL);
struct dirent* dent;
while ((dent = readdir(dir)))
dent_set.insert(dent->d_name);
closedir(dir);
const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY);
ASSERT_GE(fd, 0);
DirectoryReader dir_reader(fd);
unsigned seen = 0;
const char* name;
while (dir_reader.GetNextEntry(&name)) {
ASSERT_TRUE(dent_set.find(name) != dent_set.end());
seen++;
dir_reader.PopEntry();
}
ASSERT_TRUE(dent_set.find("status") != dent_set.end());
ASSERT_TRUE(dent_set.find("stat") != dent_set.end());
ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end());
ASSERT_EQ(dent_set.size(), seen);
close(fd);
}

View File

@ -0,0 +1,131 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// A class for reading a file, line by line, without using fopen/fgets or other
// functions which may allocate memory.
class LineReader {
public:
LineReader(int fd)
: fd_(fd),
hit_eof_(false),
buf_used_(0) {
}
// The maximum length of a line.
static const size_t kMaxLineLen = 512;
// Return the next line from the file.
// line: (output) a pointer to the start of the line. The line is NUL
// terminated.
// len: (output) the length of the line (not inc the NUL byte)
//
// Returns true iff successful (false on EOF).
//
// One must call |PopLine| after this function, otherwise you'll continue to
// get the same line over and over.
bool GetNextLine(const char **line, unsigned *len) {
for (;;) {
if (buf_used_ == 0 && hit_eof_)
return false;
for (unsigned i = 0; i < buf_used_; ++i) {
if (buf_[i] == '\n' || buf_[i] == 0) {
buf_[i] = 0;
*len = i;
*line = buf_;
return true;
}
}
if (buf_used_ == sizeof(buf_)) {
// we scanned the whole buffer and didn't find an end-of-line marker.
// This line is too long to process.
return false;
}
// We didn't find any end-of-line terminators in the buffer. However, if
// this is the last line in the file it might not have one:
if (hit_eof_) {
assert(buf_used_);
// There's room for the NUL because of the buf_used_ == sizeof(buf_)
// check above.
buf_[buf_used_] = 0;
*len = buf_used_;
buf_used_ += 1; // since we appended the NUL.
*line = buf_;
return true;
}
// Otherwise, we should pull in more data from the file
const ssize_t n = sys_read(fd_, buf_ + buf_used_,
sizeof(buf_) - buf_used_);
if (n < 0) {
return false;
} else if (n == 0) {
hit_eof_ = true;
} else {
buf_used_ += n;
}
// At this point, we have either set the hit_eof_ flag, or we have more
// data to process...
}
}
void PopLine(unsigned len) {
// len doesn't include the NUL byte at the end.
assert(buf_used_ >= len + 1);
buf_used_ -= len + 1;
my_memmove(buf_, buf_ + len + 1, buf_used_);
}
private:
const int fd_;
bool hit_eof_;
unsigned buf_used_;
char buf_[kMaxLineLen];
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_

View File

@ -0,0 +1,170 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_testfile.h"
using namespace google_breakpad;
namespace {
typedef testing::Test LineReaderTest;
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("line_reader", text) {
}
ScopedTestFile(const char* text, size_t text_len)
: AutoTestFile("line_reader", text, text_len) {
}
};
}
TEST(LineReaderTest, EmptyFile) {
ScopedTestFile file("");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, OneLineTerminated) {
ScopedTestFile file("a\n");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned int len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned int)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, OneLine) {
ScopedTestFile file("a");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, TwoLinesTerminated) {
ScopedTestFile file("a\nb\n");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, TwoLines) {
ScopedTestFile file("a\nb");
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('a', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ((unsigned)1, len);
ASSERT_EQ('b', line[0]);
ASSERT_EQ('\0', line[1]);
reader.PopLine(len);
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}
TEST(LineReaderTest, MaxLength) {
char l[LineReader::kMaxLineLen-1];
memset(l, 'a', sizeof(l));
ScopedTestFile file(l, sizeof(l));
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_TRUE(reader.GetNextLine(&line, &len));
ASSERT_EQ(sizeof(l), len);
ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
ASSERT_EQ('\0', line[len]);
}
TEST(LineReaderTest, TooLong) {
// Note: this writes kMaxLineLen 'a' chars in the test file.
char l[LineReader::kMaxLineLen];
memset(l, 'a', sizeof(l));
ScopedTestFile file(l, sizeof(l));
ASSERT_TRUE(file.IsOk());
LineReader reader(file.GetFd());
const char *line;
unsigned len;
ASSERT_FALSE(reader.GetNextLine(&line, &len));
}

View File

@ -0,0 +1,233 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
// See linux_core_dumper.h for details.
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <elf.h>
#include <stdio.h>
#include <string.h>
#include <sys/procfs.h>
#include "common/linux/linux_libc_support.h"
namespace google_breakpad {
LinuxCoreDumper::LinuxCoreDumper(pid_t pid,
const char* core_path,
const char* procfs_path)
: LinuxDumper(pid),
core_path_(core_path),
procfs_path_(procfs_path),
thread_infos_(&allocator_, 8) {
assert(core_path_);
}
bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
const char* node) const {
if (!path || !node)
return false;
size_t node_len = my_strlen(node);
if (node_len == 0)
return false;
size_t procfs_path_len = my_strlen(procfs_path_);
size_t total_length = procfs_path_len + 1 + node_len;
if (total_length >= NAME_MAX)
return false;
memcpy(path, procfs_path_, procfs_path_len);
path[procfs_path_len] = '/';
memcpy(path + procfs_path_len + 1, node, node_len);
path[total_length] = '\0';
return true;
}
void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) {
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
// TODO(benchan): Investigate whether the data to be copied could span
// across multiple segments in the core dump file. ElfCoreDump::CopyData
// and this method do not handle that case yet.
if (!core_.CopyData(dest, virtual_address, length)) {
// If the data segment is not found in the core dump, fill the result
// with marker characters.
memset(dest, 0xab, length);
}
}
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= thread_infos_.size())
return false;
*info = thread_infos_[index];
const uint8_t* stack_pointer;
#if defined(__i386)
memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
#elif defined(__x86_64)
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
#elif defined(__ARM_EABI__)
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
#else
#error "This code hasn't been ported to your platform yet."
#endif
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
return true;
}
bool LinuxCoreDumper::IsPostMortem() const {
return true;
}
bool LinuxCoreDumper::ThreadsSuspend() {
return true;
}
bool LinuxCoreDumper::ThreadsResume() {
return true;
}
bool LinuxCoreDumper::EnumerateThreads() {
if (!mapped_core_file_.Map(core_path_)) {
fprintf(stderr, "Could not map core dump file into memory\n");
return false;
}
core_.SetContent(mapped_core_file_.content());
if (!core_.IsValid()) {
fprintf(stderr, "Invalid core dump file\n");
return false;
}
ElfCoreDump::Note note = core_.GetFirstNote();
if (!note.IsValid()) {
fprintf(stderr, "PT_NOTE section not found\n");
return false;
}
bool first_thread = true;
do {
ElfCoreDump::Word type = note.GetType();
MemoryRange name = note.GetName();
MemoryRange description = note.GetDescription();
if (type == 0 || name.IsEmpty() || description.IsEmpty()) {
fprintf(stderr, "Could not found a valid PT_NOTE.\n");
return false;
}
// Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
// ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
// Thread Name Type
// -------------------------------------------------------------------
// 1st thread CORE NT_PRSTATUS
// process-wide CORE NT_PRPSINFO
// process-wide CORE NT_AUXV
// 1st thread CORE NT_FPREGSET
// 1st thread LINUX NT_PRXFPREG
// 1st thread LINUX NT_386_TLS
//
// 2nd thread CORE NT_PRSTATUS
// 2nd thread CORE NT_FPREGSET
// 2nd thread LINUX NT_PRXFPREG
// 2nd thread LINUX NT_386_TLS
//
// 3rd thread CORE NT_PRSTATUS
// 3rd thread CORE NT_FPREGSET
// 3rd thread LINUX NT_PRXFPREG
// 3rd thread LINUX NT_386_TLS
//
// The following code only works if notes are ordered as expected.
switch (type) {
case NT_PRSTATUS: {
if (description.length() != sizeof(elf_prstatus)) {
fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n");
return false;
}
const elf_prstatus* status =
reinterpret_cast<const elf_prstatus*>(description.data());
pid_t pid = status->pr_pid;
ThreadInfo info;
memset(&info, 0, sizeof(ThreadInfo));
info.tgid = status->pr_pgrp;
info.ppid = status->pr_ppid;
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
if (first_thread) {
crash_thread_ = pid;
crash_signal_ = status->pr_info.si_signo;
}
first_thread = false;
threads_.push_back(pid);
thread_infos_.push_back(info);
break;
}
#if defined(__i386) || defined(__x86_64)
case NT_FPREGSET: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpregs)) {
fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpregs, description.data(), sizeof(info->fpregs));
break;
}
#endif
#if defined(__i386)
case NT_PRXFPREG: {
if (thread_infos_.empty())
return false;
ThreadInfo* info = &thread_infos_.back();
if (description.length() != sizeof(info->fpxregs)) {
fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n");
return false;
}
memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs));
break;
}
#endif
}
note = note.GetNextNote();
} while (note.IsValid());
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,122 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
// class, which is derived from google_breakpad::LinuxDumper to extract
// information from a crashed process via its core dump and proc files.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_
#include "client/linux/minidump_writer/linux_dumper.h"
#include "common/linux/elf_core_dump.h"
#include "common/linux/memory_mapped_file.h"
namespace google_breakpad {
class LinuxCoreDumper : public LinuxDumper {
public:
// Constructs a dumper for extracting information of a given process
// with a process ID of |pid| via its core dump file at |core_path| and
// 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
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>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result.|node| is the final node without any slashes. Return true on
// success.
//
// As this dumper performs a post-mortem dump and makes use of a copy
// of the proc files of the crashed process, this derived method does
// not actually make use of |pid| and always returns a subpath of
// |procfs_path_| regardless of whether |pid| corresponds to the main
// process or a thread of the process, i.e. assuming both the main process
// and its threads have the following proc files with the same content:
// auxv, cmdline, environ, exe, maps, status
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
// Implements LinuxDumper::CopyFromProcess().
// Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method extracts the content
// the core dump and fills |dest| with a sequence of marker bytes
// if the expected data is not found in the core dump.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex().
// Reads information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::IsPostMortem().
// Always returns true to indicate that this dumper performs a
// post-mortem dump of a crashed process via a core dump file.
virtual bool IsPostMortem() const;
// Implements LinuxDumper::ThreadsSuspend().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to suspend. This method does nothing and
// always returns true.
virtual bool ThreadsSuspend();
// Implements LinuxDumper::ThreadsResume().
// As the dumper performs a post-mortem dump via a core dump file,
// there is no threads to resume. This method does nothing and
// always returns true.
virtual bool ThreadsResume();
protected:
// Implements LinuxDumper::EnumerateThreads().
// Enumerates all threads of the given process into |threads_|.
virtual bool EnumerateThreads();
private:
// Path of the core dump file.
const char* core_path_;
// Path of the directory containing the proc files of the given process,
// which is usually a copy of /proc/<pid>.
const char* procfs_path_;
// Memory-mapped core dump file at |core_path_|.
MemoryMappedFile mapped_core_file_;
// Content of the core dump file.
ElfCoreDump core_;
// Thread info found in the core dump file.
wasteful_vector<ThreadInfo> thread_infos_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_

View File

@ -0,0 +1,113 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_core_dumper_unittest.cc:
// Unit tests for google_breakpad::LinuxCoreDumoer.
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_core_dumper.h"
#include "common/linux/tests/crash_generator.h"
#include "common/using_std_string.h"
using namespace google_breakpad;
TEST(LinuxCoreDumperTest, BuildProcPath) {
const pid_t pid = getpid();
const char procfs_path[] = "/procfs_copy";
LinuxCoreDumper dumper(getpid(), "core_file", procfs_path);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"%s/maps", procfs_path);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node));
}
TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
CrashGenerator crash_generator;
if (!crash_generator.HasDefaultCorePattern()) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to non-default core pattern\n");
return;
}
const unsigned kNumOfThreads = 3;
const unsigned kCrashThread = 1;
const int kCrashSignal = SIGABRT;
pid_t child_pid;
// TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
// CrashGenerator is identified and fixed.
if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
kCrashSignal, &child_pid)) {
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test "
"is skipped due to no core dump generated\n");
return;
}
const string core_file = crash_generator.GetCoreFilePath();
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
EXPECT_TRUE(dumper.Init());
EXPECT_TRUE(dumper.IsPostMortem());
// These are no-ops and should always return true.
EXPECT_TRUE(dumper.ThreadsSuspend());
EXPECT_TRUE(dumper.ThreadsResume());
// LinuxCoreDumper cannot determine the crash address and thus it always
// sets the crash address to 0.
EXPECT_EQ(0U, dumper.crash_address());
EXPECT_EQ(kCrashSignal, dumper.crash_signal());
EXPECT_EQ(crash_generator.GetThreadId(kCrashThread),
dumper.crash_thread());
EXPECT_EQ(kNumOfThreads, dumper.threads().size());
for (unsigned i = 0; i < kNumOfThreads; ++i) {
ThreadInfo info;
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info));
const void* stack;
size_t stack_len;
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer));
EXPECT_EQ(getpid(), info.ppid);
}
}

View File

@ -0,0 +1,335 @@
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_dumper.cc: Implement google_breakpad::LinuxDumper.
// See linux_dumper.h for details.
// This code deals with the mechanics of getting information about a crashed
// process. Since this code may run in a compromised address space, the same
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
// use the alternative allocator.
#include "client/linux/minidump_writer/linux_dumper.h"
#include <assert.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <string.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/file_id.h"
#include "common/linux/linux_libc_support.h"
#include "common/linux/memory_mapped_file.h"
#include "common/linux/safe_readlink.h"
#include "third_party/lss/linux_syscall_support.h"
static const char kMappedFileUnsafePrefix[] = "/dev/";
static const char kDeletedSuffix[] = " (deleted)";
inline static bool IsMappedFileOpenUnsafe(
const google_breakpad::MappingInfo& mapping) {
// It is unsafe to attempt to open a mapped file that lives under /dev,
// because the semantics of the open may be driver-specific so we'd risk
// hanging the crash dumper. And a file in /dev/ almost certainly has no
// ELF file identifier anyways.
return my_strncmp(mapping.name,
kMappedFileUnsafePrefix,
sizeof(kMappedFileUnsafePrefix) - 1) == 0;
}
namespace google_breakpad {
// All interesting auvx entry types are below AT_SYSINFO_EHDR
#define AT_MAX AT_SYSINFO_EHDR
LinuxDumper::LinuxDumper(pid_t pid)
: pid_(pid),
crash_address_(0),
crash_signal_(0),
crash_thread_(0),
threads_(&allocator_, 8),
mappings_(&allocator_),
auxv_(&allocator_, AT_MAX + 1) {
}
LinuxDumper::~LinuxDumper() {
}
bool LinuxDumper::Init() {
return ReadAuxv() && EnumerateThreads() && EnumerateMappings();
}
bool
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
uint8_t identifier[sizeof(MDGUID)])
{
assert(!member || mapping_id < mappings_.size());
my_memset(identifier, 0, sizeof(MDGUID));
if (IsMappedFileOpenUnsafe(mapping))
return false;
// Special-case linux-gate because it's not a real file.
if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) {
void* linux_gate = NULL;
if (pid_ == sys_getpid()) {
linux_gate = reinterpret_cast<void*>(mapping.start_addr);
} else {
linux_gate = allocator_.Alloc(mapping.size);
CopyFromProcess(linux_gate, pid_,
reinterpret_cast<const void*>(mapping.start_addr),
mapping.size);
}
return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier);
}
char filename[NAME_MAX];
size_t filename_len = my_strlen(mapping.name);
assert(filename_len < NAME_MAX);
if (filename_len >= NAME_MAX)
return false;
my_memcpy(filename, mapping.name, filename_len);
filename[filename_len] = '\0';
bool filename_modified = HandleDeletedFileInMapping(filename);
MemoryMappedFile mapped_file(filename);
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
return false;
bool success =
FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier);
if (success && member && filename_modified) {
mappings_[mapping_id]->name[filename_len -
sizeof(kDeletedSuffix) + 1] = '\0';
}
return success;
}
bool LinuxDumper::ReadAuxv() {
char auxv_path[NAME_MAX];
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
return false;
}
int fd = sys_open(auxv_path, O_RDONLY, 0);
if (fd < 0) {
return false;
}
elf_aux_entry one_aux_entry;
bool res = false;
while (sys_read(fd,
&one_aux_entry,
sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) &&
one_aux_entry.a_type != AT_NULL) {
if (one_aux_entry.a_type <= AT_MAX) {
auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val;
res = true;
}
}
sys_close(fd);
return res;
}
bool LinuxDumper::EnumerateMappings() {
char maps_path[NAME_MAX];
if (!BuildProcPath(maps_path, pid_, "maps"))
return false;
// linux_gate_loc is the beginning of the kernel's mapping of
// linux-gate.so in the process. It doesn't actually show up in the
// maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
// aux vector entry, which gives the information necessary to special
// case its entry when creating the list of mappings.
// See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
// information.
const void* linux_gate_loc =
reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]);
// Although the initial executable is usually the first mapping, it's not
// guaranteed (see http://crosbug.com/25355); therefore, try to use the
// actual entry point to find the mapping.
const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]);
const int fd = sys_open(maps_path, O_RDONLY, 0);
if (fd < 0)
return false;
LineReader* const line_reader = new(allocator_) LineReader(fd);
const char* line;
unsigned line_len;
while (line_reader->GetNextLine(&line, &line_len)) {
uintptr_t start_addr, end_addr, offset;
const char* i1 = my_read_hex_ptr(&start_addr, line);
if (*i1 == '-') {
const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
if (*i2 == ' ') {
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
if (*i3 == ' ') {
const char* name = NULL;
// Only copy name if the name is a valid path name, or if
// it's the VDSO image.
if (((name = my_strchr(line, '/')) == NULL) &&
linux_gate_loc &&
reinterpret_cast<void*>(start_addr) == linux_gate_loc) {
name = kLinuxGateLibraryName;
offset = 0;
}
// Merge adjacent mappings with the same name into one module,
// assuming they're a single library mapped by the dynamic linker
if (name && !mappings_.empty()) {
MappingInfo* module = mappings_.back();
if ((start_addr == module->start_addr + module->size) &&
(my_strlen(name) == my_strlen(module->name)) &&
(my_strncmp(name, module->name, my_strlen(name)) == 0)) {
module->size = end_addr - module->start_addr;
line_reader->PopLine(line_len);
continue;
}
}
MappingInfo* const module = new(allocator_) MappingInfo;
my_memset(module, 0, sizeof(MappingInfo));
module->start_addr = start_addr;
module->size = end_addr - start_addr;
module->offset = offset;
if (name != NULL) {
const unsigned l = my_strlen(name);
if (l < sizeof(module->name))
my_memcpy(module->name, name, l);
}
// If this is the entry-point mapping, and it's not already the
// first one, then we need to make it be first. This is because
// the minidump format assumes the first module is the one that
// corresponds to the main executable (as codified in
// processor/minidump.cc:MinidumpModuleList::GetMainModule()).
if (entry_point_loc &&
(entry_point_loc >=
reinterpret_cast<void*>(module->start_addr)) &&
(entry_point_loc <
reinterpret_cast<void*>(module->start_addr+module->size)) &&
!mappings_.empty()) {
// push the module onto the front of the list.
mappings_.resize(mappings_.size() + 1);
for (size_t idx = mappings_.size() - 1; idx > 0; idx--)
mappings_[idx] = mappings_[idx - 1];
mappings_[0] = module;
} else {
mappings_.push_back(module);
}
}
}
}
line_reader->PopLine(line_len);
}
sys_close(fd);
return !mappings_.empty();
}
// Get information about the stack, given the stack pointer. We don't try to
// walk the stack since we might not have all the information needed to do
// unwind. So we just grab, up to, 32k of stack.
bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
uintptr_t int_stack_pointer) {
// Move the stack pointer to the bottom of the page that it's in.
const uintptr_t page_size = getpagesize();
uint8_t* const stack_pointer =
reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1));
// The number of bytes of stack which we try to capture.
static const ptrdiff_t kStackToCapture = 32 * 1024;
const MappingInfo* mapping = FindMapping(stack_pointer);
if (!mapping)
return false;
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
const ptrdiff_t distance_to_end =
static_cast<ptrdiff_t>(mapping->size) - offset;
*stack_len = distance_to_end > kStackToCapture ?
kStackToCapture : distance_to_end;
*stack = stack_pointer;
return true;
}
// Find the mapping which the given memory address falls in.
const MappingInfo* LinuxDumper::FindMapping(const void* address) const {
const uintptr_t addr = (uintptr_t) address;
for (size_t i = 0; i < mappings_.size(); ++i) {
const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
if (addr >= start && addr - start < mappings_[i]->size)
return mappings_[i];
}
return NULL;
}
bool LinuxDumper::HandleDeletedFileInMapping(char* path) const {
static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1;
// Check for ' (deleted)' in |path|.
// |path| has to be at least as long as "/x (deleted)".
const size_t path_len = my_strlen(path);
if (path_len < kDeletedSuffixLen + 2)
return false;
if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix,
kDeletedSuffixLen) != 0) {
return false;
}
// 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;
if (!SafeReadLink(exe_link, new_path))
return false;
if (my_strcmp(path, new_path) != 0)
return false;
// Check to see if someone actually named their executable 'foo (deleted)'.
struct kernel_stat exe_stat;
struct kernel_stat new_path_stat;
if (sys_stat(exe_link, &exe_stat) == 0 &&
sys_stat(new_path, &new_path_stat) == 0 &&
exe_stat.st_dev == new_path_stat.st_dev &&
exe_stat.st_ino == new_path_stat.st_ino) {
return false;
}
my_memcpy(path, exe_link, NAME_MAX);
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,210 @@
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
// is a base class for extracting information of a crashed process. It
// was originally a complete implementation using the ptrace API, but
// has been refactored to allow derived implementations supporting both
// ptrace and core dump. A portion of the original implementation is now
// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
// details).
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_
#include <elf.h>
#include <linux/limits.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/user.h>
#include "common/memory.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
#if defined(__i386) || defined(__x86_64)
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
#endif
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
#if defined(__i386) || defined(__ARM_EABI__)
typedef Elf32_auxv_t elf_aux_entry;
#elif defined(__x86_64)
typedef Elf64_auxv_t elf_aux_entry;
#endif
typedef typeof(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
// When we find the VDSO mapping in the process's address space, this
// is the name we use for it when writing it to the minidump.
// This should always be less than NAME_MAX!
const char kLinuxGateLibraryName[] = "linux-gate.so";
// We produce one of these structures for each thread in the crashed process.
struct ThreadInfo {
pid_t tgid; // thread group id
pid_t ppid; // parent process
uintptr_t stack_pointer; // thread stack pointer
#if defined(__i386) || defined(__x86_64)
user_regs_struct regs;
user_fpregs_struct fpregs;
static const unsigned kNumDebugRegisters = 8;
debugreg_t dregs[8];
#if defined(__i386)
user_fpxregs_struct fpxregs;
#endif // defined(__i386)
#elif defined(__ARM_EABI__)
// Mimicking how strace does this(see syscall.c, search for GETREGS)
struct user_regs regs;
struct user_fpregs fpregs;
#endif
};
// One of these is produced for each mapping in the process (i.e. line in
// /proc/$x/maps).
struct MappingInfo {
uintptr_t start_addr;
size_t size;
size_t offset; // offset into the backed file.
char name[NAME_MAX];
};
class LinuxDumper {
public:
explicit LinuxDumper(pid_t pid);
virtual ~LinuxDumper();
// Parse the data for |threads| and |mappings|.
virtual bool Init();
// Return true if the dumper performs a post-mortem dump.
virtual bool IsPostMortem() const = 0;
// Suspend/resume all threads in the given process.
virtual bool ThreadsSuspend() = 0;
virtual bool ThreadsResume() = 0;
// Read information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0;
// These are only valid after a call to |Init|.
const wasteful_vector<pid_t> &threads() { return threads_; }
const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
const MappingInfo* FindMapping(const void* address) const;
const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; }
// Find a block of memory to take as the stack given the top of stack pointer.
// stack: (output) the lowest address in the memory area
// stack_len: (output) the length of the memory area
// stack_top: the current top of the stack
bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
PageAllocator* allocator() { return &allocator_; }
// Copy content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) = 0;
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result.|node| is the final node without any slashes. Returns true on
// success.
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
// Generate a File ID from the .text section of a mapped entry.
// If not a member, mapping_id is ignored.
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
bool member,
unsigned int mapping_id,
uint8_t identifier[sizeof(MDGUID)]);
uintptr_t crash_address() const { return crash_address_; }
void set_crash_address(uintptr_t crash_address) {
crash_address_ = crash_address;
}
int crash_signal() const { return crash_signal_; }
void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; }
pid_t crash_thread() const { return crash_thread_; }
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
protected:
bool ReadAuxv();
virtual bool EnumerateMappings();
virtual bool EnumerateThreads() = 0;
// For the case where a running program has been deleted, it'll show up in
// /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
// see if '/path/to/program (deleted)' matches /proc/pid/exe and return
// /proc/pid/exe in |path| so ELF identifier generation works correctly. This
// also checks to see if '/path/to/program (deleted)' exists, so it does not
// get fooled by a poorly named binary.
// For programs that don't end with ' (deleted)', this is a no-op.
// This assumes |path| is a buffer with length NAME_MAX.
// Returns true if |path| is modified.
bool HandleDeletedFileInMapping(char* path) const;
// ID of the crashed process.
const pid_t pid_;
// Virtual address at which the process crashed.
uintptr_t crash_address_;
// Signal that terminated the crashed process.
int crash_signal_;
// ID of the crashed thread.
pid_t crash_thread_;
mutable PageAllocator allocator_;
// IDs of all the threads.
wasteful_vector<pid_t> threads_;
// Info from /proc/<pid>/maps.
wasteful_vector<MappingInfo*> mappings_;
// Info from /proc/<pid>/auxv
wasteful_vector<elf_aux_val_t> auxv_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_

View File

@ -0,0 +1,90 @@
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Helper program for the linux_dumper class, which creates a bunch of
// threads. The first word of each thread's stack is set to the thread
// id.
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
#include "common/scoped_ptr.h"
#include "third_party/lss/linux_syscall_support.h"
#if defined(__ARM_EABI__)
#define TID_PTR_REGISTER "r3"
#elif defined(__i386)
#define TID_PTR_REGISTER "ecx"
#elif defined(__x86_64)
#define TID_PTR_REGISTER "rcx"
#else
#error This test has not been ported to this platform.
#endif
void *thread_function(void *data) {
int pipefd = *static_cast<int *>(data);
volatile pid_t thread_id = syscall(__NR_gettid);
// Signal parent that a thread has started.
uint8_t byte = 1;
if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) {
perror("ERROR: parent notification failed");
return NULL;
}
register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id;
while (true)
asm volatile ("" : : "r" (thread_id_ptr));
return NULL;
}
int main(int argc, char *argv[]) {
if (argc < 3) {
fprintf(stderr,
"usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n");
return 1;
}
int pipefd = atoi(argv[1]);
int num_threads = atoi(argv[2]);
if (num_threads < 1) {
fprintf(stderr, "ERROR: number of threads is 0");
return 1;
}
google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]);
pthread_attr_t thread_attributes;
pthread_attr_init(&thread_attributes);
pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED);
for (int i = 1; i < num_threads; i++) {
pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd);
}
thread_function(&pipefd);
return 0;
}

View File

@ -0,0 +1,291 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
// See linux_ptrace_dumper.h for detals.
// This class was originally splitted from google_breakpad::LinuxDumper.
// This code deals with the mechanics of getting information about a crashed
// process. Since this code may run in a compromised address space, the same
// rules apply as detailed at the top of minidump_writer.h: no libc calls and
// use the alternative allocator.
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include <asm/ptrace.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include "client/linux/minidump_writer/directory_reader.h"
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
// Suspends a thread by attaching to it.
static bool SuspendThread(pid_t pid) {
// This may fail if the thread has just died or debugged.
errno = 0;
if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
errno != 0) {
return false;
}
while (sys_waitpid(pid, NULL, __WALL) < 0) {
if (errno != EINTR) {
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
return false;
}
}
#if defined(__i386) || defined(__x86_64)
// On x86, the stack pointer is NULL or -1, when executing trusted code in
// the seccomp sandbox. Not only does this cause difficulties down the line
// when trying to dump the thread's stack, it also results in the minidumps
// containing information about the trusted threads. This information is
// generally completely meaningless and just pollutes the minidumps.
// We thus test the stack pointer and exclude any threads that are part of
// the seccomp sandbox's trusted code.
user_regs_struct regs;
if (sys_ptrace(PTRACE_GETREGS, pid, NULL, &regs) == -1 ||
#if defined(__i386)
!regs.esp
#elif defined(__x86_64)
!regs.rsp
#endif
) {
sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
return false;
}
#endif
return true;
}
// Resumes a thread by detaching from it.
static bool ResumeThread(pid_t pid) {
return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
}
namespace google_breakpad {
LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid)
: LinuxDumper(pid),
threads_suspended_(false) {
}
bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
const char* node) const {
if (!path || !node || pid <= 0)
return false;
size_t node_len = my_strlen(node);
if (node_len == 0)
return false;
const unsigned pid_len = my_uint_len(pid);
const size_t total_length = 6 + pid_len + 1 + node_len;
if (total_length >= NAME_MAX)
return false;
my_memcpy(path, "/proc/", 6);
my_uitos(path + 6, pid, pid_len);
path[6 + pid_len] = '/';
my_memcpy(path + 6 + pid_len + 1, node, node_len);
path[total_length] = '\0';
return true;
}
void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) {
unsigned long tmp = 55;
size_t done = 0;
static const size_t word_size = sizeof(tmp);
uint8_t* const local = (uint8_t*) dest;
uint8_t* const remote = (uint8_t*) src;
while (done < length) {
const size_t l = (length - done > word_size) ? word_size : (length - done);
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
tmp = 0;
}
my_memcpy(local + done, &tmp, l);
done += l;
}
}
// Read thread info from /proc/$pid/status.
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
// these members are set to -1. Returns true iff all three members are
// available.
bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
if (index >= threads_.size())
return false;
pid_t tid = threads_[index];
assert(info != NULL);
char status_path[NAME_MAX];
if (!BuildProcPath(status_path, tid, "status"))
return false;
const int fd = sys_open(status_path, O_RDONLY, 0);
if (fd < 0)
return false;
LineReader* const line_reader = new(allocator_) LineReader(fd);
const char* line;
unsigned line_len;
info->ppid = info->tgid = -1;
while (line_reader->GetNextLine(&line, &line_len)) {
if (my_strncmp("Tgid:\t", line, 6) == 0) {
my_strtoui(&info->tgid, line + 6);
} else if (my_strncmp("PPid:\t", line, 6) == 0) {
my_strtoui(&info->ppid, line + 6);
}
line_reader->PopLine(line_len);
}
sys_close(fd);
if (info->ppid == -1 || info->tgid == -1)
return false;
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
return false;
}
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
return false;
}
#if defined(__i386)
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
return false;
#endif
#if defined(__i386) || defined(__x86_64)
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
if (sys_ptrace(
PTRACE_PEEKUSER, tid,
reinterpret_cast<void*> (offsetof(struct user,
u_debugreg[0]) + i *
sizeof(debugreg_t)),
&info->dregs[i]) == -1) {
return false;
}
}
#endif
const uint8_t* stack_pointer;
#if defined(__i386)
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
#elif defined(__x86_64)
my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
#elif defined(__ARM_EABI__)
my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
#else
#error "This code hasn't been ported to your platform yet."
#endif
info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer);
return true;
}
bool LinuxPtraceDumper::IsPostMortem() const {
return false;
}
bool LinuxPtraceDumper::ThreadsSuspend() {
if (threads_suspended_)
return true;
for (size_t i = 0; i < threads_.size(); ++i) {
if (!SuspendThread(threads_[i])) {
// If the thread either disappeared before we could attach to it, or if
// it was part of the seccomp sandbox's trusted code, it is OK to
// silently drop it from the minidump.
my_memmove(&threads_[i], &threads_[i+1],
(threads_.size() - i - 1) * sizeof(threads_[i]));
threads_.resize(threads_.size() - 1);
--i;
}
}
threads_suspended_ = true;
return threads_.size() > 0;
}
bool LinuxPtraceDumper::ThreadsResume() {
if (!threads_suspended_)
return false;
bool good = true;
for (size_t i = 0; i < threads_.size(); ++i)
good &= ResumeThread(threads_[i]);
threads_suspended_ = false;
return good;
}
// Parse /proc/$pid/task to list all the threads of the process identified by
// pid.
bool LinuxPtraceDumper::EnumerateThreads() {
char task_path[NAME_MAX];
if (!BuildProcPath(task_path, pid_, "task"))
return false;
const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
if (fd < 0)
return false;
DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd);
// The directory may contain duplicate entries which we filter by assuming
// that they are consecutive.
int last_tid = -1;
const char* dent_name;
while (dir_reader->GetNextEntry(&dent_name)) {
if (my_strcmp(dent_name, ".") &&
my_strcmp(dent_name, "..")) {
int tid = 0;
if (my_strtoui(&tid, dent_name) &&
last_tid != tid) {
last_tid = tid;
threads_.push_back(tid);
}
}
dir_reader->PopEntry();
}
sys_close(fd);
return true;
}
} // namespace google_breakpad

View File

@ -0,0 +1,92 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
// class, which is derived from google_breakpad::LinuxDumper to extract
// information from a crashed process via ptrace.
// This class was originally splitted from google_breakpad::LinuxDumper.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_
#include "client/linux/minidump_writer/linux_dumper.h"
namespace google_breakpad {
class LinuxPtraceDumper : public LinuxDumper {
public:
// Constructs a dumper for extracting information of a given process
// with a process ID of |pid|.
explicit LinuxPtraceDumper(pid_t pid);
// Implements LinuxDumper::BuildProcPath().
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
// |path| is a character array of at least NAME_MAX bytes to return the
// result. |node| is the final node without any slashes. Returns true on
// success.
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const;
// Implements LinuxDumper::CopyFromProcess().
// Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method uses ptrace to extract
// the content from the target process.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex().
// Reads information about the |index|-th thread of |threads_|.
// Returns true on success. One must have called |ThreadsSuspend| first.
virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info);
// Implements LinuxDumper::IsPostMortem().
// Always returns false to indicate this dumper performs a dump of
// a crashed process via ptrace.
virtual bool IsPostMortem() const;
// Implements LinuxDumper::ThreadsSuspend().
// Suspends all threads in the given process. Returns true on success.
virtual bool ThreadsSuspend();
// Implements LinuxDumper::ThreadsResume().
// Resumes all threads in the given process. Returns true on success.
virtual bool ThreadsResume();
protected:
// Implements LinuxDumper::EnumerateThreads().
// Enumerates all threads of the given process into |threads_|.
virtual bool EnumerateThreads();
private:
// Set to true if all threads of the crashed process are suspended.
bool threads_suspended_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_

View File

@ -0,0 +1,440 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// linux_ptrace_dumper_unittest.cc:
// Unit tests for google_breakpad::LinuxPtraceDumoer.
//
// This file was renamed from linux_dumper_unittest.cc and modified due
// to LinuxDumper being splitted into two classes.
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/safe_readlink.h"
#include "common/memory.h"
#include "common/using_std_string.h"
using namespace google_breakpad;
namespace {
typedef testing::Test LinuxPtraceDumperTest;
} // namespace
TEST(LinuxPtraceDumperTest, Setup) {
LinuxPtraceDumper dumper(getpid());
}
TEST(LinuxPtraceDumperTest, FindMappings) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
ASSERT_FALSE(dumper.FindMapping(NULL));
}
TEST(LinuxPtraceDumperTest, ThreadList) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
ASSERT_GE(dumper.threads().size(), (size_t)1);
bool found = false;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
if (dumper.threads()[i] == getpid()) {
ASSERT_FALSE(found);
found = true;
}
}
ASSERT_TRUE(found);
}
// Helper stack class to close a file descriptor and unmap
// a mmap'ed mapping.
class StackHelper {
public:
StackHelper(int fd, char* mapping, size_t size)
: fd_(fd), mapping_(mapping), size_(size) {}
~StackHelper() {
munmap(mapping_, size_);
close(fd_);
}
private:
int fd_;
char* mapping_;
size_t size_;
};
TEST(LinuxPtraceDumperTest, MergedMappings) {
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// mmap two segments out of the helper binary, one
// enclosed in the other, but with different protections.
const size_t kPageSize = sysconf(_SC_PAGESIZE);
const size_t kMappingSize = 3 * kPageSize;
int fd = open(helper_path.c_str(), O_RDONLY);
ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path
<< ", Error: " << strerror(errno);
char* mapping =
reinterpret_cast<char*>(mmap(NULL,
kMappingSize,
PROT_READ,
MAP_SHARED,
fd,
0));
ASSERT_TRUE(mapping);
const uintptr_t kMappingAddress = reinterpret_cast<uintptr_t>(mapping);
// Ensure that things get cleaned up.
StackHelper helper(fd, mapping, kMappingSize);
// Carve a page out of the first mapping with different permissions.
char* inside_mapping = reinterpret_cast<char*>(
mmap(mapping + 2 *kPageSize,
kPageSize,
PROT_NONE,
MAP_SHARED | MAP_FIXED,
fd,
// Map a different offset just to
// better test real-world conditions.
kPageSize));
ASSERT_TRUE(inside_mapping);
// Now check that LinuxPtraceDumper interpreted the mappings properly.
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
int mapping_count = 0;
for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
const MappingInfo& mapping = *dumper.mappings()[i];
if (strcmp(mapping.name, helper_path.c_str()) == 0) {
// This mapping should encompass the entire original mapped
// range.
EXPECT_EQ(kMappingAddress, mapping.start_addr);
EXPECT_EQ(kMappingSize, mapping.size);
EXPECT_EQ(0U, mapping.offset);
mapping_count++;
}
}
EXPECT_EQ(1, mapping_count);
}
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
static const int kNumberOfThreadsInHelperProgram = 5;
char kNumberOfThreadsArgument[2];
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(helper_path.c_str(),
"linux_dumper_unittest_helper",
pipe_fd_string,
kNumberOfThreadsArgument,
NULL);
// Kill if we get here.
printf("Errno from exec: %d", errno);
FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno);
exit(0);
}
close(fds[1]);
// Wait for all child threads to indicate that they have started
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
static_cast<ssize_t>(sizeof(junk)));
}
close(fds[0]);
// There is a race here because we may stop a child thread before
// it is actually running the busy loop. Empirically this sleep
// is sufficient to avoid the race.
usleep(100000);
// Children are ready now.
LinuxPtraceDumper dumper(child_pid);
ASSERT_TRUE(dumper.Init());
EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size());
EXPECT_TRUE(dumper.ThreadsSuspend());
ThreadInfo one_thread;
for (size_t i = 0; i < dumper.threads().size(); ++i) {
EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread));
const void* stack;
size_t stack_len;
EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len,
one_thread.stack_pointer));
// In the helper program, we stored a pointer to the thread id in a
// specific register. Check that we can recover its value.
#if defined(__ARM_EABI__)
pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]);
#elif defined(__i386)
pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx);
#elif defined(__x86_64)
pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx);
#else
#error This test has not been ported to this platform.
#endif
pid_t one_thread_id;
dumper.CopyFromProcess(&one_thread_id,
dumper.threads()[i],
process_tid_location,
4);
EXPECT_EQ(dumper.threads()[i], one_thread_id);
}
EXPECT_TRUE(dumper.ThreadsResume());
kill(child_pid, SIGKILL);
// Reap child
int status;
ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0)));
ASSERT_TRUE(WIFSIGNALED(status));
ASSERT_EQ(SIGKILL, WTERMSIG(status));
}
TEST(LinuxPtraceDumperTest, BuildProcPath) {
const pid_t pid = getpid();
LinuxPtraceDumper dumper(pid);
char maps_path[NAME_MAX] = "";
char maps_path_expected[NAME_MAX];
snprintf(maps_path_expected, sizeof(maps_path_expected),
"/proc/%d/maps", pid);
EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps"));
EXPECT_STREQ(maps_path_expected, maps_path);
EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps"));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, ""));
EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL));
char long_node[NAME_MAX];
size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1;
memset(long_node, 'a', long_node_len);
long_node[long_node_len] = '\0';
EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node));
}
#if !defined(__ARM_EABI__)
// Ensure that the linux-gate VDSO is included in the mapping list.
TEST(LinuxPtraceDumperTest, MappingsIncludeLinuxGate) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
void* linux_gate_loc =
reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]);
ASSERT_TRUE(linux_gate_loc);
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
const MappingInfo* mapping;
for (unsigned i = 0; i < mappings.size(); ++i) {
mapping = mappings[i];
if (!strcmp(mapping->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
break;
}
}
EXPECT_TRUE(found_linux_gate);
EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr));
EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG));
}
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
TEST(LinuxPtraceDumperTest, LinuxGateMappingID) {
LinuxPtraceDumper dumper(getpid());
ASSERT_TRUE(dumper.Init());
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
unsigned index = 0;
for (unsigned i = 0; i < mappings.size(); ++i) {
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
index = i;
break;
}
}
ASSERT_TRUE(found_linux_gate);
uint8_t identifier[sizeof(MDGUID)];
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
true,
index,
identifier));
uint8_t empty_identifier[sizeof(MDGUID)];
memset(empty_identifier, 0, sizeof(empty_identifier));
EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier)));
}
// Ensure that the linux-gate VDSO can generate a non-zeroed File ID
// from a child process.
TEST(LinuxPtraceDumperTest, LinuxGateMappingIDChild) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// Fork a child so ptrace works.
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
// Now wait forever for the parent.
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
LinuxPtraceDumper dumper(child);
ASSERT_TRUE(dumper.Init());
bool found_linux_gate = false;
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
unsigned index = 0;
for (unsigned i = 0; i < mappings.size(); ++i) {
if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) {
found_linux_gate = true;
index = i;
break;
}
}
ASSERT_TRUE(found_linux_gate);
// Need to suspend the child so ptrace actually works.
ASSERT_TRUE(dumper.ThreadsSuspend());
uint8_t identifier[sizeof(MDGUID)];
ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index],
true,
index,
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());
close(fds[1]);
}
#endif
TEST(LinuxPtraceDumperTest, FileIDsMatch) {
// Calculate the File ID of our binary using both
// FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
// and ensure that we get the same result from both.
char exe_name[PATH_MAX];
ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name));
int fds[2];
ASSERT_NE(-1, pipe(fds));
// Fork a child so ptrace works.
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
// Now wait forever for the parent.
char b;
IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b))));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
LinuxPtraceDumper dumper(child);
ASSERT_TRUE(dumper.Init());
const wasteful_vector<MappingInfo*> mappings = dumper.mappings();
bool found_exe = false;
unsigned i;
for (i = 0; i < mappings.size(); ++i) {
const MappingInfo* mapping = mappings[i];
if (!strcmp(mapping->name, exe_name)) {
found_exe = true;
break;
}
}
ASSERT_TRUE(found_exe);
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));
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);
close(fds[1]);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
#include <list>
#include <utility>
#include "client/linux/minidump_writer/linux_dumper.h"
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
class ExceptionHandler;
struct MappingEntry {
MappingInfo first;
uint8_t second[sizeof(MDGUID)];
};
// A list of <MappingInfo, GUID>
typedef std::list<MappingEntry> MappingList;
// These entries store a list of memory regions that the client wants included
// in the minidump.
struct AppMemory {
void* ptr;
size_t length;
bool operator==(const struct AppMemory& other) const {
return ptr == other.ptr;
}
bool operator==(const void* other) const {
return ptr == other;
}
};
typedef std::list<AppMemory> AppMemoryList;
// Writes a minidump to the filesystem. These functions do not malloc nor use
// libc functions which may. Thus, it can be used in contexts where the state
// of the heap may be corrupt.
// minidump_path: the path to the file to write to. This is opened O_EXCL and
// fails open fails.
// crashing_process: the pid of the crashing process. This must be trusted.
// blob: a blob of data from the crashing process. See exception_handler.h
// blob_size: the length of |blob|, in bytes
//
// Returns true iff successful.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size);
// Same as above but takes an open file descriptor instead of a path.
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size);
// Alternate form of WriteMinidump() that works with processes that
// are not expected to have crashed. If |process_blamed_thread| is
// meaningful, it will be the one from which a crash signature is
// extracted. It is not expected that this function will be called
// from a compromised context, but it is safe to do so.
bool WriteMinidump(const char* minidump_path, pid_t process,
pid_t process_blamed_thread);
// These overloads also allow passing a list of known mappings and
// a list of additional memory regions to be included in the minidump.
bool WriteMinidump(const char* minidump_path, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
bool WriteMinidump(int minidump_fd, pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
// These overloads also allow passing a file size limit for the minidump.
bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit,
pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
bool WriteMinidump(int minidump_fd, off_t minidump_size_limit,
pid_t crashing_process,
const void* blob, size_t blob_size,
const MappingList& mappings,
const AppMemoryList& appdata);
bool WriteMinidump(const char* filename,
const MappingList& mappings,
const AppMemoryList& appdata,
LinuxDumper* dumper);
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_

View File

@ -0,0 +1,757 @@
// 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.
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <ucontext.h>
#include <unistd.h>
#include <string>
#include "breakpad_googletest_includes.h"
#include "client/linux/handler/exception_handler.h"
#include "client/linux/minidump_writer/linux_dumper.h"
#include "client/linux/minidump_writer/minidump_writer.h"
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/eintr_wrapper.h"
#include "common/linux/file_id.h"
#include "common/linux/ignore_ret.h"
#include "common/linux/safe_readlink.h"
#include "common/scoped_ptr.h"
#include "common/tests/auto_tempdir.h"
#include "common/tests/file_utils.h"
#include "common/using_std_string.h"
#include "google_breakpad/processor/minidump.h"
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;
const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest";
TEST(MinidumpWriterTest, SetupWithPath) {
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));
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(0, stat(templ.c_str(), &st));
ASSERT_GT(st.st_size, 0);
close(fds[1]);
}
TEST(MinidumpWriterTest, SetupWithFD) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
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));
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
struct stat st;
ASSERT_EQ(0, stat(templ.c_str(), &st));
ASSERT_GT(st.st_size, 0);
close(fds[1]);
}
// Test that mapping info can be specified when writing a minidump,
// and that it ends up in the module list of the minidump.
TEST(MinidumpWriterTest, MappingInfo) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const uint32_t memory_size = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "a fake module";
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
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 =
reinterpret_cast<char*>(mmap(NULL,
memory_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON,
-1,
0));
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
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;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// Add information about the mapped memory.
MappingInfo info;
info.start_addr = kMemoryAddress;
info.size = memory_size;
info.offset = 0;
strcpy(info.name, kMemoryName);
MappingList mappings;
AppMemoryList memory_list;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
mappings, memory_list));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
// and debug ID.
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module =
module_list->GetModuleForAddress(kMemoryAddress);
ASSERT_TRUE(module);
EXPECT_EQ(kMemoryAddress, module->base_address());
EXPECT_EQ(memory_size, module->size());
EXPECT_EQ(kMemoryName, module->code_file());
EXPECT_EQ(module_identifier, module->debug_identifier());
uint32_t len;
// These streams are expected to be there
EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len));
EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len));
close(fds[1]);
}
// Test that mapping info can be specified, and that it overrides
// existing mappings that are wholly contained within the specified
// range.
TEST(MinidumpWriterTest, MappingInfoContained) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const int32_t memory_size = sysconf(_SC_PAGESIZE);
const char* kMemoryName = "a fake module";
const uint8_t kModuleGUID[sizeof(MDGUID)] = {
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
};
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;
string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
ASSERT_NE(-1, fd);
unlink(tempfile.c_str());
// fill with zeros
google_breakpad::scoped_array<char> buffer(new char[memory_size]);
memset(buffer.get(), 0, memory_size);
ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size));
lseek(fd, 0, SEEK_SET);
char* memory =
reinterpret_cast<char*>(mmap(NULL,
memory_size,
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
fd,
0));
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
close(fd);
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));
context.tid = 1;
string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName;
// Add information about the mapped memory. Report it as being larger than
// it actually is.
MappingInfo info;
info.start_addr = kMemoryAddress - memory_size;
info.size = memory_size * 3;
info.offset = 0;
strcpy(info.name, kMemoryName);
MappingList mappings;
AppMemoryList memory_list;
MappingEntry mapping;
mapping.first = info;
memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
mappings.push_back(mapping);
ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
mappings, memory_list));
// Read the minidump. Load the module list, and ensure that
// the mmap'ed |memory| is listed with the given module name
// and debug ID.
Minidump minidump(dumpfile);
ASSERT_TRUE(minidump.Read());
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module =
module_list->GetModuleForAddress(kMemoryAddress);
ASSERT_TRUE(module);
EXPECT_EQ(info.start_addr, module->base_address());
EXPECT_EQ(info.size, module->size());
EXPECT_EQ(kMemoryName, module->code_file());
EXPECT_EQ(module_identifier, module->debug_identifier());
close(fds[1]);
}
TEST(MinidumpWriterTest, DeletedBinary) {
const string kNumberOfThreadsArgument = "1";
const string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
// Copy binary to a temp file.
AutoTempDir temp_dir;
string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str()))
<< "Failed to copy " << helper_path << " to " << binpath;
ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(binpath.c_str(),
binpath.c_str(),
pipe_fd_string,
kNumberOfThreadsArgument.c_str(),
NULL);
}
close(fds[1]);
// Wait for the child process to signal that it's ready.
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr);
close(fds[0]);
// Child is ready now.
// Unlink the test binary.
unlink(binpath.c_str());
ExceptionHandler::CrashContext context;
memset(&context, 0, sizeof(context));
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// Set a non-zero tid to avoid tripping asserts.
context.tid = 1;
ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
sizeof(context)));
kill(child_pid, SIGKILL);
struct stat st;
ASSERT_EQ(0, stat(templ.c_str(), &st));
ASSERT_GT(st.st_size, 0);
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
// Check that the main module filename is correct.
MinidumpModuleList* module_list = minidump.GetModuleList();
ASSERT_TRUE(module_list);
const MinidumpModule* module = module_list->GetMainModule();
EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
// Check that the file ID is correct.
FileID fileid(helper_path.c_str());
uint8_t identifier[sizeof(MDGUID)];
EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
char identifier_string[kGUIDStringSize];
FileID::ConvertIdentifierToString(identifier,
identifier_string,
kGUIDStringSize);
string module_identifier(identifier_string);
// 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";
EXPECT_EQ(module_identifier, module->debug_identifier());
}
// Test that an additional memory region can be added to the minidump.
TEST(MinidumpWriterTest, AdditionalMemory) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
// These are defined here so the parent can use them to check the
// data from the minidump afterwards.
const uint32_t kMemorySize = sysconf(_SC_PAGESIZE);
// Get some heap memory.
uint8_t* memory = new uint8_t[kMemorySize];
const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
ASSERT_TRUE(memory);
// Stick some data into the memory so the contents can be verified.
for (uint32_t i = 0; i < kMemorySize; ++i) {
memory[i] = i % 255;
}
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
// This needs a valid context for minidump writing to work, but getting
// a useful one from the child is too much work, so just use one from
// the parent since the child is just a forked copy anyway.
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
unlink(templ.c_str());
MappingList mappings;
AppMemoryList memory_list;
// Add the memory region to the list of memory to be included.
AppMemory app_memory;
app_memory.ptr = memory;
app_memory.length = kMemorySize;
memory_list.push_back(app_memory);
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
mappings, memory_list));
// Read the minidump. Ensure that the memory region is present
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList();
ASSERT_TRUE(dump_memory_list);
const MinidumpMemoryRegion* region =
dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress);
ASSERT_TRUE(region);
EXPECT_EQ(kMemoryAddress, region->GetBase());
EXPECT_EQ(kMemorySize, region->GetSize());
// Verify memory contents.
EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize));
delete[] memory;
close(fds[1]);
}
// Test that an invalid thread stack pointer still results in a minidump.
TEST(MinidumpWriterTest, InvalidStackPointer) {
int fds[2];
ASSERT_NE(-1, pipe(fds));
const pid_t child = fork();
if (child == 0) {
close(fds[1]);
char b;
HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
close(fds[0]);
syscall(__NR_exit);
}
close(fds[0]);
ExceptionHandler::CrashContext context;
// This needs a valid context for minidump writing to work, but getting
// a useful one from the child is too much work, so just use one from
// the parent since the child is just a forked copy anyway.
ASSERT_EQ(0, getcontext(&context.context));
context.tid = child;
// Fake the child's stack pointer for its crashing thread. NOTE: This must
// be an invalid memory address for the child process (stack or otherwise).
#if defined(__i386)
// Try 1MB below the current stack.
uintptr_t invalid_stack_pointer =
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
#elif defined(__x86_64)
// Try 1MB below the current stack.
uintptr_t invalid_stack_pointer =
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
#elif defined(__ARM_EABI__)
// Try 1MB below the current stack.
uintptr_t invalid_stack_pointer =
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
#else
# error "This code has not been ported to your platform yet."
#endif
AutoTempDir temp_dir;
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
// NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
// presented with an invalid stack pointer.
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
// Read the minidump. Ensure that the memory region is present
Minidump minidump(templ);
ASSERT_TRUE(minidump.Read());
// TODO(ted.mielczarek,mkrebs): Enable this part of the test once
// https://breakpad.appspot.com/413002/ is committed.
#if 0
// Make sure there's a thread without a stack. NOTE: It's okay if
// GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
// region problem".
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
ASSERT_TRUE(dump_thread_list);
bool found_empty_stack = false;
for (int i = 0; i < dump_thread_list->thread_count(); i++) {
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
ASSERT_TRUE(thread->thread() != NULL);
// When the stack size is zero bytes, GetMemory() returns NULL.
if (thread->GetMemory() == NULL) {
found_empty_stack = true;
break;
}
}
// NOTE: If you fail this, first make sure that "invalid_stack_pointer"
// above is indeed set to an invalid address.
ASSERT_TRUE(found_empty_stack);
#endif
close(fds[1]);
}
// Test that limiting the size of the minidump works.
TEST(MinidumpWriterTest, MinidumpSizeLimit) {
static const int kNumberOfThreadsInHelperProgram = 40;
char number_of_threads_arg[3];
sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram);
string helper_path(GetHelperBinary());
if (helper_path.empty()) {
FAIL() << "Couldn't find helper binary";
exit(1);
}
int fds[2];
ASSERT_NE(-1, pipe(fds));
pid_t child_pid = fork();
if (child_pid == 0) {
// In child process.
close(fds[0]);
// Pass the pipe fd and the number of threads as arguments.
char pipe_fd_string[8];
sprintf(pipe_fd_string, "%d", fds[1]);
execl(helper_path.c_str(),
helper_path.c_str(),
pipe_fd_string,
number_of_threads_arg,
NULL);
}
close(fds[1]);
// Wait for all child threads to indicate that they have started
for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) {
struct pollfd pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.fd = fds[0];
pfd.events = POLLIN | POLLERR;
const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
ASSERT_EQ(1, r);
ASSERT_TRUE(pfd.revents & POLLIN);
uint8_t junk;
ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),
static_cast<ssize_t>(sizeof(junk)));
}
close(fds[0]);
// There is a race here because we may stop a child thread before
// it is actually running the busy loop. Empirically this sleep
// is sufficient to avoid the race.
usleep(100000);
// Child and its threads are ready now.
off_t normal_file_size;
int total_normal_stack_size = 0;
AutoTempDir temp_dir;
// First, write a minidump with no size limit.
{
string normal_dump = temp_dir.path() +
"/minidump-writer-unittest.dmp";
ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1,
child_pid, NULL, 0,
MappingList(), AppMemoryList()));
struct stat st;
ASSERT_EQ(0, stat(normal_dump.c_str(), &st));
ASSERT_GT(st.st_size, 0);
normal_file_size = st.st_size;
Minidump minidump(normal_dump);
ASSERT_TRUE(minidump.Read());
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
ASSERT_TRUE(dump_thread_list);
for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
ASSERT_TRUE(thread->thread() != NULL);
// When the stack size is zero bytes, GetMemory() returns NULL.
MinidumpMemoryRegion* memory = thread->GetMemory();
ASSERT_TRUE(memory != NULL);
total_normal_stack_size += memory->GetSize();
}
}
// Second, write a minidump with a size limit big enough to not trigger
// anything.
{
// Set size limit arbitrarily 1MB larger than the normal file size -- such
// that the limiting code will not kick in.
const off_t minidump_size_limit = normal_file_size + 1024*1024;
string same_dump = temp_dir.path() +
"/minidump-writer-unittest-same.dmp";
ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit,
child_pid, NULL, 0,
MappingList(), AppMemoryList()));
struct stat st;
ASSERT_EQ(0, stat(same_dump.c_str(), &st));
// Make sure limiting wasn't actually triggered. NOTE: If you fail this,
// first make sure that "minidump_size_limit" above is indeed set to a
// large enough value -- the limit-checking code in minidump_writer.cc
// does just a rough estimate.
ASSERT_EQ(normal_file_size, st.st_size);
}
// Third, write a minidump with a size limit small enough to be triggered.
{
// Set size limit to some arbitrary amount, such that the limiting code
// will kick in. The equation used to set this value was determined by
// simply reversing the size-limit logic a little bit in order to pick a
// size we know will trigger it. The definition of
// kLimitAverageThreadStackLength here was copied from class
// MinidumpWriter in minidump_writer.cc.
static const unsigned kLimitAverageThreadStackLength = 8 * 1024;
off_t minidump_size_limit = kNumberOfThreadsInHelperProgram *
kLimitAverageThreadStackLength;
// If, in reality, each of the threads' stack is *smaller* than
// kLimitAverageThreadStackLength, the normal file size could very well be
// smaller than the arbitrary limit that was just set. In that case,
// either of these numbers should trigger the size-limiting code, but we
// might as well pick the smallest.
if (normal_file_size < minidump_size_limit)
minidump_size_limit = normal_file_size;
string limit_dump = temp_dir.path() +
"/minidump-writer-unittest-limit.dmp";
ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit,
child_pid, NULL, 0,
MappingList(), AppMemoryList()));
struct stat st;
ASSERT_EQ(0, stat(limit_dump.c_str(), &st));
ASSERT_GT(st.st_size, 0);
// Make sure the file size is at least smaller than the original. If this
// fails because it's the same size, then the size-limit logic didn't kick
// in like it was supposed to.
EXPECT_LT(st.st_size, normal_file_size);
Minidump minidump(limit_dump);
ASSERT_TRUE(minidump.Read());
MinidumpThreadList* dump_thread_list = minidump.GetThreadList();
ASSERT_TRUE(dump_thread_list);
int total_limit_stack_size = 0;
for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) {
MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i);
ASSERT_TRUE(thread->thread() != NULL);
// When the stack size is zero bytes, GetMemory() returns NULL.
MinidumpMemoryRegion* memory = thread->GetMemory();
ASSERT_TRUE(memory != NULL);
total_limit_stack_size += memory->GetSize();
}
// Make sure stack size shrunk by at least 1KB per extra thread. The
// definition of kLimitBaseThreadCount here was copied from class
// MinidumpWriter in minidump_writer.cc.
// Note: The 1KB is arbitrary, and assumes that the thread stacks are big
// enough to shrink by that much. For example, if each thread stack was
// originally only 2KB, the current size-limit logic wouldn't actually
// shrink them because that's the size to which it tries to shrink. If
// you fail this part of the test due to something like that, the test
// logic should probably be improved to account for your situation.
const unsigned kLimitBaseThreadCount = 20;
const unsigned kMinPerExtraThreadStackReduction = 1024;
const int min_expected_reduction = (kNumberOfThreadsInHelperProgram -
kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction;
EXPECT_LT(total_limit_stack_size,
total_normal_stack_size - min_expected_reduction);
}
// Kill the helper program.
kill(child_pid, SIGKILL);
}
} // namespace

View File

@ -0,0 +1,66 @@
// 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.
// minidump_writer_unittest_utils.cc:
// Shared routines used by unittests under client/linux/minidump_writer.
#include <limits.h>
#include <stdlib.h>
#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h"
#include "common/linux/safe_readlink.h"
#include "common/using_std_string.h"
namespace google_breakpad {
string GetHelperBinary() {
string helper_path;
char *bindir = getenv("bindir");
if (bindir) {
helper_path = string(bindir) + "/";
} else {
// Locate helper binary next to the current binary.
char self_path[PATH_MAX];
if (!SafeReadLink("/proc/self/exe", self_path)) {
return "";
}
helper_path = string(self_path);
size_t pos = helper_path.rfind('/');
if (pos == string::npos) {
return "";
}
helper_path.erase(pos + 1);
}
helper_path += "linux_dumper_unittest_helper";
return helper_path;
}
} // namespace google_breakpad

View File

@ -0,0 +1,49 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// minidump_writer_unittest_utils.h:
// Shared routines used by unittests under client/linux/minidump_writer.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
// Returns the full path to linux_dumper_unittest_helper. The full path is
// discovered either by using the environment variable "bindir" or by using
// the location of the main module of the currently running process.
string GetHelperBinary();
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_

View File

@ -0,0 +1,130 @@
// Copyright (c) 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
#define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include "client/linux/minidump_writer/line_reader.h"
#include "common/linux/linux_libc_support.h"
#include "third_party/lss/linux_syscall_support.h"
namespace google_breakpad {
// A class for reading /proc/cpuinfo without using fopen/fgets or other
// functions which may allocate memory.
class ProcCpuInfoReader {
public:
ProcCpuInfoReader(int fd)
: line_reader_(fd), pop_count_(-1) {
}
// Return the next field name, or NULL in case of EOF.
// field: (output) Pointer to zero-terminated field name.
// Returns true on success, or false on EOF or error (line too long).
bool GetNextField(const char** field) {
for (;;) {
const char* line;
unsigned line_len;
// Try to read next line.
if (pop_count_ >= 0) {
line_reader_.PopLine(pop_count_);
pop_count_ = -1;
}
if (!line_reader_.GetNextLine(&line, &line_len))
return false;
pop_count_ = static_cast<int>(line_len);
const char* line_end = line + line_len;
// Expected format: <field-name> <space>+ ':' <space> <value>
// Note that:
// - empty lines happen.
// - <field-name> can contain spaces.
// - some fields have an empty <value>
char* sep = static_cast<char*>(my_memchr(line, ':', line_len));
if (sep == NULL)
continue;
// Record the value. Skip leading space after the column to get
// its start.
const char* val = sep+1;
while (val < line_end && my_isspace(*val))
val++;
value_ = val;
value_len_ = static_cast<size_t>(line_end - val);
// Remove trailing spaces before the column to properly 0-terminate
// the field name.
while (sep > line && my_isspace(sep[-1]))
sep--;
if (sep == line)
continue;
// zero-terminate field name.
*sep = '\0';
*field = line;
return true;
}
}
// Return the field value. This must be called after a succesful
// call to GetNextField().
const char* GetValue() {
assert(value_);
return value_;
}
// Same as GetValue(), but also returns the length in characters of
// the value.
const char* GetValueAndLen(size_t* length) {
assert(value_);
*length = value_len_;
return value_;
}
private:
LineReader line_reader_;
int pop_count_;
const char* value_;
size_t value_len_;
};
} // namespace google_breakpad
#endif // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_

View File

@ -0,0 +1,200 @@
// Copyright (c) 2013, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
#include "breakpad_googletest_includes.h"
#include "common/linux/eintr_wrapper.h"
#include "common/tests/auto_testfile.h"
using namespace google_breakpad;
#if !defined(__ANDROID__)
#define TEMPDIR "/tmp"
#else
#define TEMPDIR "/data/local/tmp"
#endif
namespace {
typedef testing::Test ProcCpuInfoReaderTest;
class ScopedTestFile : public AutoTestFile {
public:
explicit ScopedTestFile(const char* text)
: AutoTestFile("proc_cpuinfo_reader", text) {
}
};
}
TEST(ProcCpuInfoReaderTest, EmptyFile) {
ScopedTestFile file("");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, OneLineTerminated) {
ScopedTestFile file("foo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, OneLine) {
ScopedTestFile file("foo : bar");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char *field;
size_t value_len;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len));
ASSERT_EQ(3U, value_len);
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) {
ScopedTestFile file("foo : bar\nzoo : tut\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("zoo", field);
ASSERT_STREQ("tut", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipMalformedLine) {
ScopedTestFile file("this line should have a column\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) {
ScopedTestFile file("\n\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipEmptyField) {
ScopedTestFile file(" : bar\nzoo : tut\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("zoo", field);
ASSERT_STREQ("tut", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) {
ScopedTestFile file("foo : bar\n\n\nfoo : bar\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
ASSERT_STREQ("bar", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, FieldWithSpaces) {
ScopedTestFile file("foo bar : zoo\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo bar", field);
ASSERT_STREQ("zoo", reader.GetValue());
ASSERT_FALSE(reader.GetNextField(&field));
}
TEST(ProcCpuInfoReaderTest, EmptyValue) {
ScopedTestFile file("foo :\n");
ASSERT_TRUE(file.IsOk());
ProcCpuInfoReader reader(file.GetFd());
const char* field;
ASSERT_TRUE(reader.GetNextField(&field));
ASSERT_STREQ("foo", field);
size_t value_len;
ASSERT_STREQ("", reader.GetValueAndLen(&value_len));
ASSERT_EQ(0U, value_len);
ASSERT_FALSE(reader.GetNextField(&field));
}

View File

@ -0,0 +1,104 @@
// Copyright (c) 2009, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "common/linux/google_crashdump_uploader.h"
#include "third_party/linux/include/gflags/gflags.h"
#include <string>
#include <iostream>
#include "common/using_std_string.h"
DEFINE_string(crash_server, "https://clients2.google.com/cr",
"The crash server to upload minidumps to.");
DEFINE_string(product_name, "",
"The product name that the minidump corresponds to.");
DEFINE_string(product_version, "",
"The version of the product that produced the minidump.");
DEFINE_string(client_id, "",
"The client GUID");
DEFINE_string(minidump_path, "",
"The path of the minidump file.");
DEFINE_string(ptime, "",
"The process uptime in milliseconds.");
DEFINE_string(ctime, "",
"The cumulative process uptime in milliseconds.");
DEFINE_string(email, "",
"The user's email address.");
DEFINE_string(comments, "",
"Extra user comments");
DEFINE_string(proxy_host, "",
"Proxy host");
DEFINE_string(proxy_userpasswd, "",
"Proxy username/password in user:pass format.");
bool CheckForRequiredFlagsOrDie() {
string error_text = "";
if (FLAGS_product_name.empty()) {
error_text.append("\nProduct name must be specified.");
}
if (FLAGS_product_version.empty()) {
error_text.append("\nProduct version must be specified.");
}
if (FLAGS_client_id.empty()) {
error_text.append("\nClient ID must be specified.");
}
if (FLAGS_minidump_path.empty()) {
error_text.append("\nMinidump pathname must be specified.");
}
if (!error_text.empty()) {
std::cout << error_text;
return false;
}
return true;
}
int main(int argc, char *argv[]) {
google::InitGoogleLogging(argv[0]);
google::ParseCommandLineFlags(&argc, &argv, true);
if (!CheckForRequiredFlagsOrDie()) {
return 1;
}
google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name,
FLAGS_product_version,
FLAGS_client_id,
FLAGS_ptime,
FLAGS_ctime,
FLAGS_email,
FLAGS_comments,
FLAGS_minidump_path,
FLAGS_crash_server,
FLAGS_proxy_host,
FLAGS_proxy_userpasswd);
g.Upload();
}

View File

@ -0,0 +1,97 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// minidump_file_writer-inl.h: Minidump file writer implementation.
//
// See minidump_file_writer.h for documentation.
#ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__
#define CLIENT_MINIDUMP_FILE_WRITER_INL_H__
#include <assert.h>
#include "client/minidump_file_writer.h"
#include "google_breakpad/common/minidump_size.h"
namespace google_breakpad {
template<typename MDType>
inline bool TypedMDRVA<MDType>::Allocate() {
allocation_state_ = SINGLE_OBJECT;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size());
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::Allocate(size_t additional) {
allocation_state_ = SINGLE_OBJECT;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + additional);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) {
assert(count);
allocation_state_ = ARRAY;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() * count);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(size_t count,
size_t length) {
assert(count && length);
allocation_state_ = SINGLE_OBJECT_WITH_ARRAY;
return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * length);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) {
assert(allocation_state_ == ARRAY);
return writer_->Copy(
static_cast<MDRVA>(position_ + index * minidump_size<MDType>::size()),
item, minidump_size<MDType>::size());
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index,
const void *src,
size_t length) {
assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY);
return writer_->Copy(
static_cast<MDRVA>(position_ + minidump_size<MDType>::size()
+ index * length),
src, length);
}
template<typename MDType>
inline bool TypedMDRVA<MDType>::Flush() {
return writer_->Copy(position_, &data_, minidump_size<MDType>::size());
}
} // namespace google_breakpad
#endif // CLIENT_MINIDUMP_FILE_WRITER_INL_H__

View File

@ -0,0 +1,284 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// minidump_file_writer.cc: Minidump file writer implementation.
//
// See minidump_file_writer.h for documentation.
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "client/minidump_file_writer-inl.h"
#include "common/linux/linux_libc_support.h"
#include "common/string_conversion.h"
#if __linux__
#include "third_party/lss/linux_syscall_support.h"
#endif
namespace google_breakpad {
const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
MinidumpFileWriter::MinidumpFileWriter()
: file_(-1),
close_file_when_destroyed_(true),
position_(0),
size_(0) {
}
MinidumpFileWriter::~MinidumpFileWriter() {
if (close_file_when_destroyed_)
Close();
}
bool MinidumpFileWriter::Open(const char *path) {
assert(file_ == -1);
#if __linux__
file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
#else
file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
#endif
return file_ != -1;
}
void MinidumpFileWriter::SetFile(const int file) {
assert(file_ == -1);
file_ = file;
close_file_when_destroyed_ = false;
}
bool MinidumpFileWriter::Close() {
bool result = true;
if (file_ != -1) {
if (-1 == ftruncate(file_, position_)) {
return false;
}
#if __linux__
result = (sys_close(file_) == 0);
#else
result = (close(file_) == 0);
#endif
file_ = -1;
}
return result;
}
bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
unsigned int length,
TypedMDRVA<MDString> *mdstring) {
bool result = true;
if (sizeof(wchar_t) == sizeof(uint16_t)) {
// Shortcut if wchar_t is the same size as MDString's buffer
result = mdstring->Copy(str, mdstring->get()->length);
} else {
uint16_t out[2];
int out_idx = 0;
// Copy the string character by character
while (length && result) {
UTF32ToUTF16Char(*str, out);
if (!out[0])
return false;
// Process one character at a time
--length;
++str;
// Append the one or two UTF-16 characters. The first one will be non-
// zero, but the second one may be zero, depending on the conversion from
// UTF-32.
int out_count = out[1] ? 2 : 1;
size_t out_size = sizeof(uint16_t) * out_count;
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
out_idx += out_count;
}
}
return result;
}
bool MinidumpFileWriter::CopyStringToMDString(const char *str,
unsigned int length,
TypedMDRVA<MDString> *mdstring) {
bool result = true;
uint16_t out[2];
int out_idx = 0;
// Copy the string character by character
while (length && result) {
int conversion_count = UTF8ToUTF16Char(str, length, out);
if (!conversion_count)
return false;
// Move the pointer along based on the nubmer of converted characters
length -= conversion_count;
str += conversion_count;
// Append the one or two UTF-16 characters
int out_count = out[1] ? 2 : 1;
size_t out_size = sizeof(uint16_t) * out_count;
result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
out_idx += out_count;
}
return result;
}
template <typename CharType>
bool MinidumpFileWriter::WriteStringCore(const CharType *str,
unsigned int length,
MDLocationDescriptor *location) {
assert(str);
assert(location);
// Calculate the mdstring length by either limiting to |length| as passed in
// or by finding the location of the NULL character.
unsigned int mdstring_length = 0;
if (!length)
length = INT_MAX;
for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
;
// Allocate the string buffer
TypedMDRVA<MDString> mdstring(this);
if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t)))
return false;
// Set length excluding the NULL and copy the string
mdstring.get()->length =
static_cast<uint32_t>(mdstring_length * sizeof(uint16_t));
bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
// NULL terminate
if (result) {
uint16_t ch = 0;
result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
if (result)
*location = mdstring.location();
}
return result;
}
bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
MDLocationDescriptor *location) {
return WriteStringCore(str, length, location);
}
bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
MDLocationDescriptor *location) {
return WriteStringCore(str, length, location);
}
bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
MDMemoryDescriptor *output) {
assert(src);
assert(output);
UntypedMDRVA mem(this);
if (!mem.Allocate(size))
return false;
if (!mem.Copy(src, mem.size()))
return false;
output->start_of_memory_range = reinterpret_cast<uint64_t>(src);
output->memory = mem.location();
return true;
}
MDRVA MinidumpFileWriter::Allocate(size_t size) {
assert(size);
assert(file_ != -1);
size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
if (position_ + aligned_size > size_) {
size_t growth = aligned_size;
size_t minimal_growth = getpagesize();
// Ensure that the file grows by at least the size of a memory page
if (growth < minimal_growth)
growth = minimal_growth;
size_t new_size = size_ + growth;
if (ftruncate(file_, new_size) != 0)
return kInvalidMDRVA;
size_ = new_size;
}
MDRVA current_position = position_;
position_ += static_cast<MDRVA>(aligned_size);
return current_position;
}
bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
assert(src);
assert(size);
assert(file_ != -1);
// Ensure that the data will fit in the allocated space
if (static_cast<size_t>(size + position) > size_)
return false;
// Seek and write the data
#if __linux__
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
if (sys_write(file_, src, size) == size) {
#else
if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
if (write(file_, src, size) == size) {
#endif
return true;
}
}
return false;
}
bool UntypedMDRVA::Allocate(size_t size) {
assert(size_ == 0);
size_ = size;
position_ = writer_->Allocate(size_);
return position_ != MinidumpFileWriter::kInvalidMDRVA;
}
bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) {
assert(src);
assert(size);
assert(pos + size <= position_ + size_);
return writer_->Copy(pos, src, size);
}
} // namespace google_breakpad

View File

@ -0,0 +1,272 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// minidump_file_writer.h: Implements file-based minidump generation. It's
// intended to be used with the Google Breakpad open source crash handling
// project.
#ifndef CLIENT_MINIDUMP_FILE_WRITER_H__
#define CLIENT_MINIDUMP_FILE_WRITER_H__
#include <string>
#include "google_breakpad/common/minidump_format.h"
namespace google_breakpad {
class UntypedMDRVA;
template<typename MDType> class TypedMDRVA;
// The user of this class can Open() a file and add minidump streams, data, and
// strings using the definitions in minidump_format.h. Since this class is
// expected to be used in a situation where the current process may be
// damaged, it will not allocate heap memory.
// Sample usage:
// MinidumpFileWriter writer;
// writer.Open("/tmp/minidump.dmp");
// TypedMDRVA<MDRawHeader> header(&writer_);
// header.Allocate();
// header->get()->signature = MD_HEADER_SIGNATURE;
// :
// writer.Close();
//
// An alternative is to use SetFile and provide a file descriptor:
// MinidumpFileWriter writer;
// writer.SetFile(minidump_fd);
// TypedMDRVA<MDRawHeader> header(&writer_);
// header.Allocate();
// header->get()->signature = MD_HEADER_SIGNATURE;
// :
// writer.Close();
class MinidumpFileWriter {
public:
// Invalid MDRVA (Minidump Relative Virtual Address)
// returned on failed allocation
static const MDRVA kInvalidMDRVA;
MinidumpFileWriter();
~MinidumpFileWriter();
// Open |path| as the destination of the minidump data. Any existing file
// will be overwritten.
// Return true on success, or false on failure.
bool Open(const char *path);
// Sets the file descriptor |file| as the destination of the minidump data.
// Can be used as an alternative to Open() when a file descriptor is
// available.
// Note that |fd| is not closed when the instance of MinidumpFileWriter is
// destroyed.
void SetFile(const int file);
// Close the current file (that was either created when Open was called, or
// specified with SetFile).
// Return true on success, or false on failure.
bool Close();
// Copy the contents of |str| to a MDString and write it to the file.
// |str| is expected to be either UTF-16 or UTF-32 depending on the size
// of wchar_t.
// Maximum |length| of characters to copy from |str|, or specify 0 to use the
// entire NULL terminated string. Copying will stop at the first NULL.
// |location| the allocated location
// Return true on success, or false on failure
bool WriteString(const wchar_t *str, unsigned int length,
MDLocationDescriptor *location);
// Same as above, except with |str| as a UTF-8 string
bool WriteString(const char *str, unsigned int length,
MDLocationDescriptor *location);
// Write |size| bytes starting at |src| into the current position.
// Return true on success and set |output| to position, or false on failure
bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output);
// Copies |size| bytes from |src| to |position|
// Return true on success, or false on failure
bool Copy(MDRVA position, const void *src, ssize_t size);
// Return the current position for writing to the minidump
inline MDRVA position() const { return position_; }
private:
friend class UntypedMDRVA;
// Allocates an area of |size| bytes.
// Returns the position of the allocation, or kInvalidMDRVA if it was
// unable to allocate the bytes.
MDRVA Allocate(size_t size);
// The file descriptor for the output file.
int file_;
// Whether |file_| should be closed when the instance is destroyed.
bool close_file_when_destroyed_;
// Current position in buffer
MDRVA position_;
// Current allocated size
size_t size_;
// Copy |length| characters from |str| to |mdstring|. These are distinct
// because the underlying MDString is a UTF-16 based string. The wchar_t
// variant may need to create a MDString that has more characters than the
// source |str|, whereas the UTF-8 variant may coalesce characters to form
// a single UTF-16 character.
bool CopyStringToMDString(const wchar_t *str, unsigned int length,
TypedMDRVA<MDString> *mdstring);
bool CopyStringToMDString(const char *str, unsigned int length,
TypedMDRVA<MDString> *mdstring);
// The common templated code for writing a string
template <typename CharType>
bool WriteStringCore(const CharType *str, unsigned int length,
MDLocationDescriptor *location);
};
// Represents an untyped allocated chunk
class UntypedMDRVA {
public:
explicit UntypedMDRVA(MinidumpFileWriter *writer)
: writer_(writer),
position_(writer->position()),
size_(0) {}
// Allocates |size| bytes. Must not call more than once.
// Return true on success, or false on failure
bool Allocate(size_t size);
// Returns the current position or kInvalidMDRVA if allocation failed
inline MDRVA position() const { return position_; }
// Number of bytes allocated
inline size_t size() const { return size_; }
// Return size and position
inline MDLocationDescriptor location() const {
MDLocationDescriptor location = { static_cast<uint32_t>(size_),
position_ };
return location;
}
// Copy |size| bytes starting at |src| into the minidump at |position|
// Return true on success, or false on failure
bool Copy(MDRVA position, const void *src, size_t size);
// Copy |size| bytes from |src| to the current position
inline bool Copy(const void *src, size_t size) {
return Copy(position_, src, size);
}
protected:
// Writer we associate with
MinidumpFileWriter *writer_;
// Position of the start of the data
MDRVA position_;
// Allocated size
size_t size_;
};
// Represents a Minidump object chunk. Additional memory can be allocated at
// the end of the object as a:
// - single allocation
// - Array of MDType objects
// - A MDType object followed by an array
template<typename MDType>
class TypedMDRVA : public UntypedMDRVA {
public:
// Constructs an unallocated MDRVA
explicit TypedMDRVA(MinidumpFileWriter *writer)
: UntypedMDRVA(writer),
data_(),
allocation_state_(UNALLOCATED) {}
inline ~TypedMDRVA() {
// Ensure that the data_ object is written out
if (allocation_state_ != ARRAY)
Flush();
}
// Address of object data_ of MDType. This is not declared const as the
// typical usage will be to access the underlying |data_| object as to
// alter its contents.
MDType *get() { return &data_; }
// Allocates minidump_size<MDType>::size() bytes.
// Must not call more than once.
// Return true on success, or false on failure
bool Allocate();
// Allocates minidump_size<MDType>::size() + |additional| bytes.
// Must not call more than once.
// Return true on success, or false on failure
bool Allocate(size_t additional);
// Allocate an array of |count| elements of MDType.
// Must not call more than once.
// Return true on success, or false on failure
bool AllocateArray(size_t count);
// Allocate an array of |count| elements of |size| after object of MDType
// Must not call more than once.
// Return true on success, or false on failure
bool AllocateObjectAndArray(size_t count, size_t size);
// Copy |item| to |index|
// Must have been allocated using AllocateArray().
// Return true on success, or false on failure
bool CopyIndex(unsigned int index, MDType *item);
// Copy |size| bytes starting at |str| to |index|
// Must have been allocated using AllocateObjectAndArray().
// Return true on success, or false on failure
bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size);
// Write data_
bool Flush();
private:
enum AllocationState {
UNALLOCATED = 0,
SINGLE_OBJECT,
ARRAY,
SINGLE_OBJECT_WITH_ARRAY
};
MDType data_;
AllocationState allocation_state_;
};
} // namespace google_breakpad
#endif // CLIENT_MINIDUMP_FILE_WRITER_H__

View File

@ -0,0 +1,179 @@
// Copyright (c) 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Author: waylonis@google.com (Dan Waylonis)
/*
g++ -I../ ../common/convert_UTF.c \
../common/string_conversion.cc \
minidump_file_writer.cc \
minidump_file_writer_unittest.cc \
-o minidump_file_writer_unittest
*/
#include <fcntl.h>
#include <unistd.h>
#include "minidump_file_writer-inl.h"
using google_breakpad::MinidumpFileWriter;
#define ASSERT_TRUE(cond) \
if (!(cond)) { \
fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
return false; \
}
#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2))
#define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2))
struct StringStructure {
unsigned long integer_value;
MDLocationDescriptor first_string;
MDLocationDescriptor second_string;
};
struct ArrayStructure {
unsigned char char_value;
unsigned short short_value;
unsigned long long_value;
};
typedef struct {
unsigned long count;
ArrayStructure array[0];
} ObjectAndArrayStructure;
static bool WriteFile(const char *path) {
MinidumpFileWriter writer;
if (writer.Open(path)) {
// Test a single structure
google_breakpad::TypedMDRVA<StringStructure> strings(&writer);
ASSERT_TRUE(strings.Allocate());
strings.get()->integer_value = 0xBEEF;
const char *first = "First String";
ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string));
const wchar_t *second = L"Second String";
ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string));
// Test an array structure
google_breakpad::TypedMDRVA<ArrayStructure> array(&writer);
unsigned int count = 10;
ASSERT_TRUE(array.AllocateArray(count));
for (unsigned char i = 0; i < count; ++i) {
ArrayStructure local;
local.char_value = i;
local.short_value = i + 1;
local.long_value = i + 2;
ASSERT_TRUE(array.CopyIndex(i, &local));
}
// Test an object followed by an array
google_breakpad::TypedMDRVA<ObjectAndArrayStructure> obj_array(&writer);
ASSERT_TRUE(obj_array.AllocateObjectAndArray(count,
sizeof(ArrayStructure)));
obj_array.get()->count = count;
for (unsigned char i = 0; i < count; ++i) {
ArrayStructure local;
local.char_value = i;
local.short_value = i + 1;
local.long_value = i + 2;
ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local)));
}
}
return writer.Close();
}
static bool CompareFile(const char *path) {
unsigned long expected[] = {
#if defined(__BIG_ENDIAN__)
0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,
0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069,
0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064,
0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002,
0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005,
0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008,
0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b,
0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003,
0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006,
0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009,
0x0000000a, 0x0900000a, 0x0000000b, 0x00000000
#else
0x0000beef, 0x0000001e, 0x00000018, 0x00000020,
0x00000038, 0x00000000, 0x00000018, 0x00690046,
0x00730072, 0x00200074, 0x00740053, 0x00690072,
0x0067006e, 0x00000000, 0x0000001a, 0x00650053,
0x006f0063, 0x0064006e, 0x00530020, 0x00720074,
0x006e0069, 0x00000067, 0x00011e00, 0x00000002,
0x00021e01, 0x00000003, 0x00031e02, 0x00000004,
0x00041e03, 0x00000005, 0x00051e04, 0x00000006,
0x00061e05, 0x00000007, 0x00071e06, 0x00000008,
0x00081e07, 0x00000009, 0x00091e08, 0x0000000a,
0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00,
0x00000002, 0x00021c01, 0x00000003, 0x00031c02,
0x00000004, 0x00041c03, 0x00000005, 0x00051c04,
0x00000006, 0x00061c05, 0x00000007, 0x00071c06,
0x00000008, 0x00081c07, 0x00000009, 0x00091c08,
0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000,
#endif
};
size_t expected_byte_count = sizeof(expected);
int fd = open(path, O_RDONLY, 0600);
void *buffer = malloc(expected_byte_count);
ASSERT_NE(fd, -1);
ASSERT_TRUE(buffer);
ASSERT_EQ(read(fd, buffer, expected_byte_count),
static_cast<ssize_t>(expected_byte_count));
char *b1, *b2;
b1 = reinterpret_cast<char*>(buffer);
b2 = reinterpret_cast<char*>(expected);
while (*b1 == *b2) {
b1++;
b2++;
}
printf("%p\n", reinterpret_cast<void*>(b1 - (char*)buffer));
ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0);
return true;
}
static bool RunTests() {
const char *path = "/tmp/minidump_file_writer_unittest.dmp";
ASSERT_TRUE(WriteFile(path));
ASSERT_TRUE(CompareFile(path));
unlink(path);
return true;
}
extern "C" int main(int argc, const char *argv[]) {
return RunTests() ? 0 : 1;
}

View File

@ -0,0 +1,41 @@
// 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.
#ifndef COMMON_BASICTYPES_H_
#define COMMON_BASICTYPES_H_
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#ifndef DISALLOW_COPY_AND_ASSIGN
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
#endif // DISALLOW_COPY_AND_ASSIGN
#endif // COMMON_BASICTYPES_H_

View File

@ -0,0 +1,265 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// byte_cursor.h: Classes for parsing values from a buffer of bytes.
// The ByteCursor class provides a convenient interface for reading
// fixed-size integers of arbitrary endianness, being thorough about
// checking for buffer overruns.
#ifndef COMMON_BYTE_CURSOR_H_
#define COMMON_BYTE_CURSOR_H_
#include <assert.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "common/using_std_string.h"
namespace google_breakpad {
// A buffer holding a series of bytes.
struct ByteBuffer {
ByteBuffer() : start(0), end(0) { }
ByteBuffer(const uint8_t *set_start, size_t set_size)
: start(set_start), end(set_start + set_size) { }
~ByteBuffer() { };
// Equality operators. Useful in unit tests, and when we're using
// ByteBuffers to refer to regions of a larger buffer.
bool operator==(const ByteBuffer &that) const {
return start == that.start && end == that.end;
}
bool operator!=(const ByteBuffer &that) const {
return start != that.start || end != that.end;
}
// Not C++ style guide compliant, but this definitely belongs here.
size_t Size() const {
assert(start <= end);
return end - start;
}
const uint8_t *start, *end;
};
// A cursor pointing into a ByteBuffer that can parse numbers of various
// widths and representations, strings, and data blocks, advancing through
// the buffer as it goes. All ByteCursor operations check that accesses
// haven't gone beyond the end of the enclosing ByteBuffer.
class ByteCursor {
public:
// Create a cursor reading bytes from the start of BUFFER. By default, the
// cursor reads multi-byte values in little-endian form.
ByteCursor(const ByteBuffer *buffer, bool big_endian = false)
: buffer_(buffer), here_(buffer->start),
big_endian_(big_endian), complete_(true) { }
// Accessor and setter for this cursor's endianness flag.
bool big_endian() const { return big_endian_; }
void set_big_endian(bool big_endian) { big_endian_ = big_endian; }
// Accessor and setter for this cursor's current position. The setter
// returns a reference to this cursor.
const uint8_t *here() const { return here_; }
ByteCursor &set_here(const uint8_t *here) {
assert(buffer_->start <= here && here <= buffer_->end);
here_ = here;
return *this;
}
// Return the number of bytes available to read at the cursor.
size_t Available() const { return size_t(buffer_->end - here_); }
// Return true if this cursor is at the end of its buffer.
bool AtEnd() const { return Available() == 0; }
// When used as a boolean value this cursor converts to true if all
// prior reads have been completed, or false if we ran off the end
// of the buffer.
operator bool() const { return complete_; }
// Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true,
// unsigned otherwise, using the cursor's established endianness, and set
// *RESULT to the number. If we read off the end of our buffer, clear
// this cursor's complete_ flag, and store a dummy value in *RESULT.
// Return a reference to this cursor.
template<typename T>
ByteCursor &Read(size_t size, bool is_signed, T *result) {
if (CheckAvailable(size)) {
T v = 0;
if (big_endian_) {
for (size_t i = 0; i < size; i++)
v = (v << 8) + here_[i];
} else {
// This loop condition looks weird, but size_t is unsigned, so
// decrementing i after it is zero yields the largest size_t value.
for (size_t i = size - 1; i < size; i--)
v = (v << 8) + here_[i];
}
if (is_signed && size < sizeof(T)) {
size_t sign_bit = (T)1 << (size * 8 - 1);
v = (v ^ sign_bit) - sign_bit;
}
here_ += size;
*result = v;
} else {
*result = (T) 0xdeadbeef;
}
return *this;
}
// Read an integer, using the cursor's established endianness and
// *RESULT's size and signedness, and set *RESULT to the number. If we
// read off the end of our buffer, clear this cursor's complete_ flag.
// Return a reference to this cursor.
template<typename T>
ByteCursor &operator>>(T &result) {
bool T_is_signed = (T)-1 < 0;
return Read(sizeof(T), T_is_signed, &result);
}
// Copy the SIZE bytes at the cursor to BUFFER, and advance this
// cursor to the end of them. If we read off the end of our buffer,
// clear this cursor's complete_ flag, and set *POINTER to NULL.
// Return a reference to this cursor.
ByteCursor &Read(uint8_t *buffer, size_t size) {
if (CheckAvailable(size)) {
memcpy(buffer, here_, size);
here_ += size;
}
return *this;
}
// Set STR to a copy of the '\0'-terminated string at the cursor. If the
// byte buffer does not contain a terminating zero, clear this cursor's
// complete_ flag, and set STR to the empty string. Return a reference to
// this cursor.
ByteCursor &CString(string *str) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', Available()));
if (end) {
str->assign(reinterpret_cast<const char *>(here_), end - here_);
here_ = end + 1;
} else {
str->clear();
here_ = buffer_->end;
complete_ = false;
}
return *this;
}
// Like CString(STR), but extract the string from a fixed-width buffer
// LIMIT bytes long, which may or may not contain a terminating '\0'
// byte. Specifically:
//
// - If there are not LIMIT bytes available at the cursor, clear the
// cursor's complete_ flag and set STR to the empty string.
//
// - Otherwise, if the LIMIT bytes at the cursor contain any '\0'
// characters, set *STR to a copy of the bytes before the first '\0',
// and advance the cursor by LIMIT bytes.
//
// - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
// cursor by LIMIT bytes.
ByteCursor &CString(string *str, size_t limit) {
if (CheckAvailable(limit)) {
const uint8_t *end
= static_cast<const uint8_t *>(memchr(here_, '\0', limit));
if (end)
str->assign(reinterpret_cast<const char *>(here_), end - here_);
else
str->assign(reinterpret_cast<const char *>(here_), limit);
here_ += limit;
} else {
str->clear();
}
return *this;
}
// Set *POINTER to point to the SIZE bytes at the cursor, and advance
// this cursor to the end of them. If SIZE is omitted, don't move the
// cursor. If we read off the end of our buffer, clear this cursor's
// complete_ flag, and set *POINTER to NULL. Return a reference to this
// cursor.
ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) {
if (CheckAvailable(size)) {
*pointer = here_;
here_ += size;
} else {
*pointer = NULL;
}
return *this;
}
// Skip SIZE bytes at the cursor. If doing so would advance us off
// the end of our buffer, clear this cursor's complete_ flag, and
// set *POINTER to NULL. Return a reference to this cursor.
ByteCursor &Skip(size_t size) {
if (CheckAvailable(size))
here_ += size;
return *this;
}
private:
// If there are at least SIZE bytes available to read from the buffer,
// return true. Otherwise, set here_ to the end of the buffer, set
// complete_ to false, and return false.
bool CheckAvailable(size_t size) {
if (Available() >= size) {
return true;
} else {
here_ = buffer_->end;
complete_ = false;
return false;
}
}
// The buffer we're reading bytes from.
const ByteBuffer *buffer_;
// The next byte within buffer_ that we'll read.
const uint8_t *here_;
// True if we should read numbers in big-endian form; false if we
// should read in little-endian form.
bool big_endian_;
// True if we've been able to read all we've been asked to.
bool complete_;
};
} // namespace google_breakpad
#endif // COMMON_BYTE_CURSOR_H_

View File

@ -0,0 +1,776 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer
// and google_breakpad::ByteCursor.
#include <string>
#include <string.h>
#include "breakpad_googletest_includes.h"
#include "common/byte_cursor.h"
#include "common/using_std_string.h"
using google_breakpad::ByteBuffer;
using google_breakpad::ByteCursor;
TEST(Buffer, SizeOfNothing) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
EXPECT_EQ(0U, buffer.Size());
}
TEST(Buffer, SizeOfSomething) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
EXPECT_EQ(10U, buffer.Size());
}
TEST(Extent, AvailableEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_EQ(0U, cursor.Available());
}
TEST(Extent, AtEndEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor.AtEnd());
}
TEST(Extent, AsBoolEmpty) {
uint8_t data[1];
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor);
}
TEST(Extent, AvailableSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_EQ(10U, cursor.Available());
}
TEST(Extent, AtEndSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_FALSE(cursor.AtEnd());
EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd());
}
TEST(Extent, AsBoolSome) {
uint8_t data[10];
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
EXPECT_TRUE(cursor);
EXPECT_TRUE(cursor.Skip(sizeof(data)));
EXPECT_FALSE(cursor.Skip(1));
}
TEST(Extent, Cursor) {
uint8_t data[] = { 0xf7,
0x9f, 0xbe,
0x67, 0xfb, 0xd3, 0x58,
0x6f, 0x36, 0xde, 0xd1,
0x2a, 0x2a, 0x2a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a;
uint16_t b;
uint32_t c;
uint32_t d;
uint8_t stars[3];
EXPECT_EQ(data + 0U, cursor.here());
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(data + 1U, cursor.here());
EXPECT_TRUE(cursor >> b);
EXPECT_EQ(data + 3U, cursor.here());
EXPECT_TRUE(cursor >> c);
EXPECT_EQ(data + 7U, cursor.here());
EXPECT_TRUE(cursor.Skip(4));
EXPECT_EQ(data + 11U, cursor.here());
EXPECT_TRUE(cursor.Read(stars, 3));
EXPECT_EQ(data + 14U, cursor.here());
EXPECT_FALSE(cursor >> d);
EXPECT_EQ(data + 14U, cursor.here());
}
TEST(Extent, SetOffset) {
uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a, b, c, d, e;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0x5cU, a);
EXPECT_EQ(data + 1U, cursor.here());
EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1)
>> d >> e);
EXPECT_EQ(0x79U, b);
EXPECT_EQ(0xd5U, c);
EXPECT_EQ(0x79U, d);
EXPECT_EQ(0x8cU, e);
EXPECT_EQ(data + 3U, cursor.here());
}
TEST(BigEndian, Signed1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, true, &a)
.Read(1, true, &b)
.Read(1, true, &c)
.Read(1, true, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(-0x80, c);
EXPECT_EQ(-1, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, true, &e));
}
TEST(BigEndian, Signed2) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, true, &a)
.Read(2, true, &b)
.Read(2, true, &c)
.Read(2, true, &d)
.Read(2, true, &e)
.Read(2, true, &f)
.Read(2, true, &g)
.Read(2, true, &h)
.Read(2, true, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(-0x8000, d);
EXPECT_EQ(-0x7f80, e);
EXPECT_EQ(-1, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(-0x7544, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, true, &j));
}
TEST(BigEndian, Signed4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0xb6, 0xb1, 0xff, 0xef,
0x19, 0x6a, 0xca, 0x46 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, true, &a)
.Read(4, true, &b)
.Read(4, true, &c)
.Read(4, true, &d)
.Read(4, true, &e)
.Read(4, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(-0x80000000LL, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int32_t) 0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, true, &g));
}
TEST(BigEndian, Signed8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, true, &a)
.Read(8, true, &b)
.Read(8, true, &c)
.Read(8, true, &d)
.Read(8, true, &e)
.Read(8, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffffffffffffLL, b);
EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4LL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, true, &g));
}
TEST(BigEndian, Unsigned1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, false, &a)
.Read(1, false, &b)
.Read(1, false, &c)
.Read(1, false, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(0x80, c);
EXPECT_EQ(0xff, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, false, &e));
}
TEST(BigEndian, Unsigned2) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x80, 0x7f, 0xff,
0x80, 0x00, 0x80, 0x80, 0xff, 0xff,
0x39, 0xf1, 0x8a, 0xbc, 0x5a, 0xec };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
int64_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, false, &a)
.Read(2, false, &b)
.Read(2, false, &c)
.Read(2, false, &d)
.Read(2, false, &e)
.Read(2, false, &f)
.Read(2, false, &g)
.Read(2, false, &h)
.Read(2, false, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(0x8000, d);
EXPECT_EQ(0x8080, e);
EXPECT_EQ(0xffff, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(0x8abc, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, false, &j));
}
TEST(BigEndian, Unsigned4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff,
0xb6, 0xb1, 0xff, 0xef,
0x19, 0x6a, 0xca, 0x46 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, false, &a)
.Read(4, false, &b)
.Read(4, false, &c)
.Read(4, false, &d)
.Read(4, false, &e)
.Read(4, false, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(0x80000000, c);
EXPECT_EQ(0xffffffff, d);
EXPECT_EQ(0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, false, &g));
}
TEST(BigEndian, Unsigned8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c,
0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, true);
uint64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, false, &a)
.Read(8, false, &b)
.Read(8, false, &c)
.Read(8, false, &d)
.Read(8, false, &e)
.Read(8, false, &f));
EXPECT_EQ(0U, a);
EXPECT_EQ(0x7fffffffffffffffULL, b);
EXPECT_EQ(0x8000000000000000ULL, c);
EXPECT_EQ(0xffffffffffffffffULL, d);
EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, false, &g));
}
TEST(LittleEndian, Signed1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, true, &a)
.Read(1, true, &b)
.Read(1, true, &c)
.Read(1, true, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(-0x80, c);
EXPECT_EQ(-1, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, true, &e));
}
TEST(LittleEndian, Signed2) {
uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, false);
int32_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, true, &a)
.Read(2, true, &b)
.Read(2, true, &c)
.Read(2, true, &d)
.Read(2, true, &e)
.Read(2, true, &f)
.Read(2, true, &g)
.Read(2, true, &h)
.Read(2, true, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(-0x8000, d);
EXPECT_EQ(-0x7f80, e);
EXPECT_EQ(-1, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(-0x7544, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, true, &j));
}
TEST(LittleEndian, Signed4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff,
0xef, 0xff, 0xb1, 0xb6,
0x46, 0xca, 0x6a, 0x19 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, true, &a)
.Read(4, true, &b)
.Read(4, true, &c)
.Read(4, true, &d)
.Read(4, true, &e)
.Read(4, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(-0x80000000LL, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int32_t) 0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, true, &g));
}
TEST(LittleEndian, Signed8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer, false);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, true, &a)
.Read(8, true, &b)
.Read(8, true, &c)
.Read(8, true, &d)
.Read(8, true, &e)
.Read(8, true, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffffffffffffLL, b);
EXPECT_EQ(-0x7fffffffffffffffLL - 1, c);
EXPECT_EQ(-1, d);
EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4LL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, true, &g));
}
TEST(LittleEndian, Unsigned1) {
uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e;
ASSERT_TRUE(cursor
.Read(1, false, &a)
.Read(1, false, &b)
.Read(1, false, &c)
.Read(1, false, &d));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7f, b);
EXPECT_EQ(0x80, c);
EXPECT_EQ(0xff, d);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(1, false, &e));
}
TEST(LittleEndian, Unsigned2) {
uint8_t data[] = { 0x00, 0x00, 0x80, 0x00, 0xff, 0x7f,
0x00, 0x80, 0x80, 0x80, 0xff, 0xff,
0xf1, 0x39, 0xbc, 0x8a, 0xec, 0x5a };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a, b, c, d, e, f, g, h, i, j;
ASSERT_TRUE(cursor
.Read(2, false, &a)
.Read(2, false, &b)
.Read(2, false, &c)
.Read(2, false, &d)
.Read(2, false, &e)
.Read(2, false, &f)
.Read(2, false, &g)
.Read(2, false, &h)
.Read(2, false, &i));
EXPECT_EQ(0, a);
EXPECT_EQ(0x80, b);
EXPECT_EQ(0x7fff, c);
EXPECT_EQ(0x8000, d);
EXPECT_EQ(0x8080, e);
EXPECT_EQ(0xffff, f);
EXPECT_EQ(0x39f1, g);
EXPECT_EQ(0x8abc, h);
EXPECT_EQ(0x5aec, i);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(2, false, &j));
}
TEST(LittleEndian, Unsigned4) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff,
0xef, 0xff, 0xb1, 0xb6,
0x46, 0xca, 0x6a, 0x19 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(4, false, &a)
.Read(4, false, &b)
.Read(4, false, &c)
.Read(4, false, &d)
.Read(4, false, &e)
.Read(4, false, &f));
EXPECT_EQ(0, a);
EXPECT_EQ(0x7fffffff, b);
EXPECT_EQ(0x80000000, c);
EXPECT_EQ(0xffffffff, d);
EXPECT_EQ(0xb6b1ffef, e);
EXPECT_EQ(0x196aca46, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(4, false, &g));
}
TEST(LittleEndian, Unsigned8) {
uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93,
0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint64_t a, b, c, d, e, f, g;
ASSERT_TRUE(cursor
.Read(8, false, &a)
.Read(8, false, &b)
.Read(8, false, &c)
.Read(8, false, &d)
.Read(8, false, &e)
.Read(8, false, &f));
EXPECT_EQ(0U, a);
EXPECT_EQ(0x7fffffffffffffffULL, b);
EXPECT_EQ(0x8000000000000000ULL, c);
EXPECT_EQ(0xffffffffffffffffULL, d);
EXPECT_EQ(0x9320d5e9d2d5879cULL, e);
EXPECT_EQ(0x4e4249d27f8414a4ULL, f);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor.Read(8, false, &g));
}
TEST(Extractor, Signed1) {
uint8_t data[] = { 0xfd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int8_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-3, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Signed2) {
uint8_t data[] = { 0x13, 0xcd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int16_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-13037, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Signed4) {
uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
int32_t a;
// For some reason, G++ 4.4.1 complains:
// warning: array subscript is above array bounds
// in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
// I'm not able to see how such a reference would occur.
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(-380377902, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned1) {
uint8_t data[] = { 0xfd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xfd, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned2) {
uint8_t data[] = { 0x13, 0xcd };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint16_t a;
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xcd13, a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Unsigned4) {
uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint32_t a;
// For some reason, G++ 4.4.1 complains:
// warning: array subscript is above array bounds
// in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
// I'm not able to see how such a reference would occur.
EXPECT_TRUE(cursor >> a);
EXPECT_EQ(0xe953e4d2, a);
EXPECT_FALSE(cursor >> a);
EXPECT_FALSE(cursor >> a);
}
TEST(Extractor, Mixed) {
uint8_t data[] = { 0x42,
0x25, 0x0b,
0x3d, 0x25, 0xed, 0x2a,
0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf,
0xd8,
0x22, 0xa5,
0x3a, 0x02, 0x6a, 0xd7,
0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
cursor.set_big_endian(true);
uint8_t a;
uint16_t b;
uint32_t c;
uint64_t d;
int8_t e;
int16_t f;
int32_t g;
int64_t h;
int z;
EXPECT_FALSE(cursor.AtEnd());
EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h);
EXPECT_EQ(0x42U, a);
EXPECT_EQ(0x250bU, b);
EXPECT_EQ(0x3d25ed2aU, c);
EXPECT_EQ(0xec169e14615b2ccfULL, d);
EXPECT_EQ(-40, e);
EXPECT_EQ(0x22a5, f);
EXPECT_EQ(0x3a026ad7, g);
EXPECT_EQ(-7842405714468937530LL, h);
EXPECT_TRUE(cursor.AtEnd());
EXPECT_FALSE(cursor >> z);
}
TEST(Strings, Zero) {
uint8_t data[] = { 0xa6 };
ByteBuffer buffer(data, 0);
ByteCursor cursor(&buffer);
uint8_t received[1];
received[0] = 0xc2;
EXPECT_TRUE(cursor.Read(received, 0));
EXPECT_EQ(0xc2U, received[0]);
}
TEST(Strings, Some) {
uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed };
EXPECT_TRUE(cursor.Skip(2).Read(received, 5));
uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed };
EXPECT_TRUE(memcmp(received, expected, 7) == 0);
}
TEST(Strings, TooMuch) {
uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
uint8_t received1[3];
uint8_t received2[3];
uint8_t received3[3];
EXPECT_FALSE(cursor
.Read(received1, 3)
.Read(received2, 3)
.Read(received3, 3));
uint8_t expected1[3] = { 0x5d, 0x31, 0x09 };
uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c };
EXPECT_TRUE(memcmp(received1, expected1, 3) == 0);
EXPECT_TRUE(memcmp(received2, expected2, 3) == 0);
}
TEST(Strings, PointTo) {
uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 };
ByteBuffer buffer(data, sizeof(data));
ByteCursor cursor(&buffer);
const uint8_t *received1;
const uint8_t *received2;
const uint8_t *received3;
const uint8_t *received4;
EXPECT_FALSE(cursor
.PointTo(&received1, 3)
.PointTo(&received2, 3)
.PointTo(&received3)
.PointTo(&received4, 3));
EXPECT_EQ(data + 0, received1);
EXPECT_EQ(data + 3, received2);
EXPECT_EQ(data + 6, received3);
EXPECT_EQ(NULL, received4);
}
TEST(Strings, CString) {
uint8_t data[] = "abc\0\0foo";
ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
ByteCursor cursor(&buffer);
string a, b, c;
EXPECT_TRUE(cursor.CString(&a).CString(&b));
EXPECT_EQ("abc", a);
EXPECT_EQ("", b);
EXPECT_FALSE(cursor.CString(&c));
EXPECT_EQ("", c);
EXPECT_TRUE(cursor.AtEnd());
}
TEST(Strings, CStringLimit) {
uint8_t data[] = "abcdef\0\0foobar";
ByteBuffer buffer(data, sizeof(data) - 1); // don't include terminating '\0'
ByteCursor cursor(&buffer);
string a, b, c, d, e;
EXPECT_TRUE(cursor.CString(&a, 3));
EXPECT_EQ("abc", a);
EXPECT_TRUE(cursor.CString(&b, 0));
EXPECT_EQ("", b);
EXPECT_TRUE(cursor.CString(&c, 6));
EXPECT_EQ("def", c);
EXPECT_TRUE(cursor.CString(&d, 4));
EXPECT_EQ("ooba", d);
EXPECT_FALSE(cursor.CString(&e, 4));
EXPECT_EQ("", e);
EXPECT_TRUE(cursor.AtEnd());
}
// uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 };
// ByteBuffer buffer(data, sizeof(data));

View File

@ -0,0 +1,533 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Sept 2001: fixed const & error conditions per
mods suggested by S. Parent & A. Lillich.
June 2002: Tim Dodd added detection and handling of incomplete
source sequences, enhanced error detection, added casts
to eliminate compiler warnings.
July 2003: slight mods to back out aggressive FFFE detection.
Jan 2004: updated switches in from-UTF8 conversions.
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
See the header file "ConvertUTF.h" for complete documentation.
------------------------------------------------------------------------ */
#include "convert_UTF.h"
#ifdef CVTUTF_DEBUG
#include <stdio.h>
#endif
static const int halfShift = 10; /* used for shifting by 10 bits */
static const UTF32 halfBase = 0x0010000UL;
static const UTF32 halfMask = 0x3FFUL;
#define UNI_SUR_HIGH_START (UTF32)0xD800
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
#define UNI_SUR_LOW_START (UTF32)0xDC00
#define UNI_SUR_LOW_END (UTF32)0xDFFF
#define false 0
#define true 1
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
if (target >= targetEnd) {
result = targetExhausted; break;
}
ch = *source++;
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_LEGAL_UTF32) {
if (flags == strictConversion) {
result = sourceIllegal;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
--source; /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF32* target = *targetStart;
UTF32 ch, ch2;
while (source < sourceEnd) {
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
if (target >= targetEnd) {
source = oldSource; /* Back up source pointer! */
result = targetExhausted; break;
}
*target++ = ch;
}
*sourceStart = source;
*targetStart = target;
#ifdef CVTUTF_DEBUG
if (result == sourceIllegal) {
fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2);
fflush(stderr);
}
#endif
return result;
}
/* --------------------------------------------------------------------- */
/*
* Index into the table below with the first byte of a UTF-8 sequence to
* get the number of trailing bytes that are supposed to follow it.
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
* left as-is for anyone who may want to do such conversion, which was
* allowed in earlier algorithms.
*/
static const char trailingBytesForUTF8[256] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
};
/*
* Magic values subtracted from a buffer value during UTF8 conversion.
* This table contains as many values as there might be trailing bytes
* in a UTF-8 sequence.
*/
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
0x03C82080UL, 0xFA082080UL, 0x82082080UL };
/*
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
* into the first byte, depending on how many bytes follow. There are
* as many entries in this table as there are UTF-8 sequence types.
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
* for *legal* UTF-8 will be 4 or fewer bytes total.
*/
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
/* --------------------------------------------------------------------- */
/* The interface converts a whole buffer to avoid function-call overhead.
* Constants have been gathered. Loops & conditionals have been removed as
* much as possible for efficiency, in favor of drop-through switches.
* (See "Note A" at the bottom of the file for equivalent code.)
* If your compiler supports it, the "isLegalUTF8" call can be turned
* into an inline function.
*/
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF16* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
ch = *source++;
/* If we have a surrogate pair, convert to UTF32 first. */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) {
/* If the 16 bits following the high surrogate are in the source buffer... */
if (source < sourceEnd) {
UTF32 ch2 = *source;
/* If it's a low surrogate, convert to UTF32. */
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ (ch2 - UNI_SUR_LOW_START) + halfBase;
++source;
} else if (flags == strictConversion) { /* it's an unpaired high surrogate */
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
} else { /* We don't have the 16 bits following the high surrogate. */
--source; /* return to the high surrogate */
result = sourceExhausted;
break;
}
} else if (flags == strictConversion) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/* Figure out how many bytes the result will require */
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch < (UTF32)0x110000) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
}
target += bytesToWrite;
if (target > targetEnd) {
source = oldSource; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
/*
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
* This must be called with the length pre-determined by the first byte.
* If not calling this from ConvertUTF8to*, then the length can be set by:
* length = trailingBytesForUTF8[*source]+1;
* and the sequence is illegal right away if there aren't that many bytes
* available.
* If presented with a length > 4, this returns false. The Unicode
* definition of UTF-8 goes up to 4-byte sequences.
*/
static Boolean isLegalUTF8(const UTF8 *source, int length) {
UTF8 a;
const UTF8 *srcptr = source+length;
switch (length) {
default: return false;
/* Everything else falls through when "true"... */
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
switch (*source) {
/* no fall-through in this inner switch */
case 0xE0: if (a < 0xA0) return false; break;
case 0xED: if (a > 0x9F) return false; break;
case 0xF0: if (a < 0x90) return false; break;
case 0xF4: if (a > 0x8F) return false; break;
default: if (a < 0x80) return false;
}
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
}
if (*source > 0xF4) return false;
return true;
}
/* --------------------------------------------------------------------- */
/*
* Exported function to return whether a UTF-8 sequence is legal or not.
* This is not used here; it's just exported.
*/
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
int length = trailingBytesForUTF8[*source]+1;
if (source+length > sourceEnd) {
return false;
}
return isLegalUTF8(source, length);
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF16* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = (UTF16)ch; /* normal case */
}
} else if (ch > UNI_MAX_UTF16) {
if (flags == strictConversion) {
result = sourceIllegal;
source -= (extraBytesToRead+1); /* return to the start */
break; /* Bail out; shouldn't continue */
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
/* target is a character in range 0xFFFF - 0x10FFFF. */
if (target + 1 >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up source pointer! */
result = targetExhausted; break;
}
ch -= halfBase;
*target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START);
*target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START);
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF32* source = *sourceStart;
UTF8* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch;
unsigned short bytesToWrite = 0;
const UTF32 byteMask = 0xBF;
const UTF32 byteMark = 0x80;
ch = *source++;
if (flags == strictConversion ) {
/* UTF-16 surrogate values are illegal in UTF-32 */
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
--source; /* return to the illegal value itself */
result = sourceIllegal;
break;
}
}
/*
* Figure out how many bytes the result will require. Turn any
* illegally large UTF32 things (> Plane 17) into replacement chars.
*/
if (ch < (UTF32)0x80) { bytesToWrite = 1;
} else if (ch < (UTF32)0x800) { bytesToWrite = 2;
} else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
} else if (ch <= UNI_MAX_LEGAL_UTF32) { bytesToWrite = 4;
} else { bytesToWrite = 3;
ch = UNI_REPLACEMENT_CHAR;
result = sourceIllegal;
}
target += bytesToWrite;
if (target > targetEnd) {
--source; /* Back up source pointer! */
target -= bytesToWrite; result = targetExhausted; break;
}
switch (bytesToWrite) { /* note: everything falls through. */
case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6;
case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]);
}
target += bytesToWrite;
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* --------------------------------------------------------------------- */
ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) {
ConversionResult result = conversionOK;
const UTF8* source = *sourceStart;
UTF32* target = *targetStart;
while (source < sourceEnd) {
UTF32 ch = 0;
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
if (source + extraBytesToRead >= sourceEnd) {
result = sourceExhausted; break;
}
/* Do this check whether lenient or strict */
if (! isLegalUTF8(source, extraBytesToRead+1)) {
result = sourceIllegal;
break;
}
/*
* The cases all fall through. See "Note A" below.
*/
switch (extraBytesToRead) {
case 5: ch += *source++; ch <<= 6;
case 4: ch += *source++; ch <<= 6;
case 3: ch += *source++; ch <<= 6;
case 2: ch += *source++; ch <<= 6;
case 1: ch += *source++; ch <<= 6;
case 0: ch += *source++;
}
ch -= offsetsFromUTF8[extraBytesToRead];
if (target >= targetEnd) {
source -= (extraBytesToRead+1); /* Back up the source pointer! */
result = targetExhausted; break;
}
if (ch <= UNI_MAX_LEGAL_UTF32) {
/*
* UTF-16 surrogate values are illegal in UTF-32, and anything
* over Plane 17 (> 0x10FFFF) is illegal.
*/
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) {
if (flags == strictConversion) {
source -= (extraBytesToRead+1); /* return to the illegal value itself */
result = sourceIllegal;
break;
} else {
*target++ = UNI_REPLACEMENT_CHAR;
}
} else {
*target++ = ch;
}
} else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
result = sourceIllegal;
*target++ = UNI_REPLACEMENT_CHAR;
}
}
*sourceStart = source;
*targetStart = target;
return result;
}
/* ---------------------------------------------------------------------
Note A.
The fall-through switches in UTF-8 reading code save a
temp variable, some decrements & conditionals. The switches
are equivalent to the following loop:
{
int tmpBytesToRead = extraBytesToRead+1;
do {
ch += *source++;
--tmpBytesToRead;
if (tmpBytesToRead) ch <<= 6;
} while (tmpBytesToRead > 0);
}
In UTF-8 writing code, the switches on "bytesToWrite" are
similarly unrolled loops.
--------------------------------------------------------------------- */

View File

@ -0,0 +1,143 @@
/*
* Copyright 2001-2004 Unicode, Inc.
*
* Disclaimer
*
* This source code is provided as is by Unicode, Inc. No claims are
* made as to fitness for any particular purpose. No warranties of any
* kind are expressed or implied. The recipient agrees to determine
* applicability of information provided. If this file has been
* purchased on magnetic or optical media from Unicode, Inc., the
* sole remedy for any claim will be exchange of defective media
* within 90 days of receipt.
*
* Limitations on Rights to Redistribute This Code
*
* Unicode, Inc. hereby grants the right to freely use the information
* supplied in this file in the creation of products supporting the
* Unicode Standard, and to make copies of this file in any form
* for internal or external distribution as long as this notice
* remains attached.
*/
/* ---------------------------------------------------------------------
Conversions between UTF32, UTF-16, and UTF-8. Header file.
Several funtions are included here, forming a complete set of
conversions between the three formats. UTF-7 is not included
here, but is handled in a separate source file.
Each of these routines takes pointers to input buffers and output
buffers. The input buffers are const.
Each routine converts the text between *sourceStart and sourceEnd,
putting the result into the buffer between *targetStart and
targetEnd. Note: the end pointers are *after* the last item: e.g.
*(sourceEnd - 1) is the last item.
The return result indicates whether the conversion was successful,
and if not, whether the problem was in the source or target buffers.
(Only the first encountered problem is indicated.)
After the conversion, *sourceStart and *targetStart are both
updated to point to the end of last text successfully converted in
the respective buffers.
Input parameters:
sourceStart - pointer to a pointer to the source buffer.
The contents of this are modified on return so that
it points at the next thing to be converted.
targetStart - similarly, pointer to pointer to the target buffer.
sourceEnd, targetEnd - respectively pointers to the ends of the
two buffers, for overflow checking only.
These conversion functions take a ConversionFlags argument. When this
flag is set to strict, both irregular sequences and isolated surrogates
will cause an error. When the flag is set to lenient, both irregular
sequences and isolated surrogates are converted.
Whether the flag is strict or lenient, all illegal sequences will cause
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
must check for illegal sequences.
When the flag is set to lenient, characters over 0x10FFFF are converted
to the replacement character; otherwise (when the flag is set to strict)
they constitute an error.
Output parameters:
The value "sourceIllegal" is returned from some routines if the input
sequence is malformed. When "sourceIllegal" is returned, the source
value will point to the illegal value that caused the problem. E.g.,
in UTF-8 when a sequence is malformed, it points to the start of the
malformed sequence.
Author: Mark E. Davis, 1994.
Rev History: Rick McGowan, fixes & updates May 2001.
Fixes & updates, Sept 2001.
------------------------------------------------------------------------ */
/* ---------------------------------------------------------------------
The following 4 definitions are compiler-specific.
The C standard does not guarantee that wchar_t has at least
16 bits, so wchar_t is no less portable than unsigned short!
All should be unsigned values to avoid sign extension during
bit mask & shift operations.
------------------------------------------------------------------------ */
typedef unsigned long UTF32; /* at least 32 bits */
typedef unsigned short UTF16; /* at least 16 bits */
typedef unsigned char UTF8; /* typically 8 bits */
typedef unsigned char Boolean; /* 0 or 1 */
/* Some fundamental constants */
#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
#define UNI_MAX_BMP (UTF32)0x0000FFFF
#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF
typedef enum {
conversionOK, /* conversion successful */
sourceExhausted, /* partial character in source, but hit end */
targetExhausted, /* insuff. room in target for conversion */
sourceIllegal /* source sequence is illegal/malformed */
} ConversionResult;
typedef enum {
strictConversion = 0,
lenientConversion
} ConversionFlags;
/* This is for C++ and does no harm in C */
#ifdef __cplusplus
extern "C" {
#endif
ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd,
UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags);
ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd,
UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
#ifdef __cplusplus
}
#endif
/* --------------------------------------------------------------------- */

View File

@ -0,0 +1,175 @@
// Copyright 2006 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__
#define UTIL_DEBUGINFO_BYTEREADER_INL_H__
#include "common/dwarf/bytereader.h"
#include <assert.h>
namespace dwarf2reader {
inline uint8 ByteReader::ReadOneByte(const char* buffer) const {
return buffer[0];
}
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) {
return buffer0 | buffer1 << 8;
} else {
return buffer1 | buffer0 << 8;
}
}
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];
const uint32 buffer3 = buffer[3];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24;
} else {
return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24;
}
}
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];
const uint64 buffer3 = buffer[3];
const uint64 buffer4 = buffer[4];
const uint64 buffer5 = buffer[5];
const uint64 buffer6 = buffer[6];
const uint64 buffer7 = buffer[7];
if (endian_ == ENDIANNESS_LITTLE) {
return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 |
buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56;
} else {
return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 |
buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56;
}
}
// Read an unsigned LEB128 number. Each byte contains 7 bits of
// information, plus one bit saying whether the number continues or
// not.
inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer,
size_t* len) const {
uint64 result = 0;
size_t num_read = 0;
unsigned int shift = 0;
unsigned char byte;
do {
byte = *buffer++;
num_read++;
result |= (static_cast<uint64>(byte & 0x7f)) << shift;
shift += 7;
} while (byte & 0x80);
*len = num_read;
return result;
}
// 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 char* buffer,
size_t* len) const {
int64 result = 0;
unsigned int shift = 0;
size_t num_read = 0;
unsigned char byte;
do {
byte = *buffer++;
num_read++;
result |= (static_cast<uint64>(byte & 0x7f) << shift);
shift += 7;
} while (byte & 0x80);
if ((shift < 8 * sizeof (result)) && (byte & 0x40))
result |= -((static_cast<int64>(1)) << shift);
*len = num_read;
return result;
}
inline uint64 ByteReader::ReadOffset(const char* buffer) const {
assert(this->offset_reader_);
return (this->*offset_reader_)(buffer);
}
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 char *buffer_base) {
section_base_ = section_base;
buffer_base_ = buffer_base;
have_section_base_ = true;
}
inline void ByteReader::SetTextBase(uint64 text_base) {
text_base_ = text_base;
have_text_base_ = true;
}
inline void ByteReader::SetDataBase(uint64 data_base) {
data_base_ = data_base;
have_data_base_ = true;
}
inline void ByteReader::SetFunctionBase(uint64 function_base) {
function_base_ = function_base;
have_function_base_ = true;
}
inline void ByteReader::ClearFunctionBase() {
have_function_base_ = false;
}
} // namespace dwarf2reader
#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__

View File

@ -0,0 +1,245 @@
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assert.h>
#include <stdlib.h>
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/bytereader.h"
namespace dwarf2reader {
ByteReader::ByteReader(enum Endianness endian)
:offset_reader_(NULL), address_reader_(NULL), endian_(endian),
address_size_(0), offset_size_(0),
have_section_base_(), have_text_base_(), have_data_base_(),
have_function_base_() { }
ByteReader::~ByteReader() { }
void ByteReader::SetOffsetSize(uint8 size) {
offset_size_ = size;
assert(size == 4 || size == 8);
if (size == 4) {
this->offset_reader_ = &ByteReader::ReadFourBytes;
} else {
this->offset_reader_ = &ByteReader::ReadEightBytes;
}
}
void ByteReader::SetAddressSize(uint8 size) {
address_size_ = size;
assert(size == 4 || size == 8);
if (size == 4) {
this->address_reader_ = &ByteReader::ReadFourBytes;
} else {
this->address_reader_ = &ByteReader::ReadEightBytes;
}
}
uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
const uint64 initial_length = ReadFourBytes(start);
start += 4;
// In DWARF2/3, if the initial length is all 1 bits, then the offset
// size is 8 and we need to read the next 8 bytes for the real length.
if (initial_length == 0xffffffff) {
SetOffsetSize(8);
*len = 12;
return ReadOffset(start);
} else {
SetOffsetSize(4);
*len = 4;
}
return initial_length;
}
bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const {
if (encoding == DW_EH_PE_omit) return true;
if (encoding == DW_EH_PE_aligned) return true;
if ((encoding & 0x7) > DW_EH_PE_udata8)
return false;
if ((encoding & 0x70) > DW_EH_PE_funcrel)
return false;
return true;
}
bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
switch (encoding & 0x70) {
case DW_EH_PE_absptr: return true;
case DW_EH_PE_pcrel: return have_section_base_;
case DW_EH_PE_textrel: return have_text_base_;
case DW_EH_PE_datarel: return have_data_base_;
case DW_EH_PE_funcrel: return have_function_base_;
default: return false;
}
}
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
// see it here.
assert(encoding != DW_EH_PE_omit);
// The Linux Standards Base 4.0 does not make this clear, but the
// GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
// agree that aligned pointers are always absolute, machine-sized,
// machine-signed pointers.
if (encoding == DW_EH_PE_aligned) {
assert(have_section_base_);
// We don't need to align BUFFER in *our* address space. Rather, we
// need to find the next position in our buffer that would be aligned
// when the .eh_frame section the buffer contains is loaded into the
// program's memory. So align assuming that buffer_base_ gets loaded at
// address section_base_, where section_base_ itself may or may not be
// aligned.
// First, find the offset to START from the closest prior aligned
// address.
uint64 skew = section_base_ & (AddressSize() - 1);
// Now find the offset from that aligned address to buffer.
uint64 offset = skew + (buffer - buffer_base_);
// Round up to the next boundary.
uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
// Convert back to a pointer.
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);
}
// Extract the value first, ignoring whether it's a pointer or an
// offset relative to some base.
uint64 offset;
switch (encoding & 0x0f) {
case DW_EH_PE_absptr:
// DW_EH_PE_absptr is weird, as it is used as a meaningful value for
// both the high and low nybble of encoding bytes. When it appears in
// the high nybble, it means that the pointer is absolute, not an
// offset from some base address. When it appears in the low nybble,
// as here, it means that the pointer is stored as a normal
// machine-sized and machine-signed address. A low nybble of
// DW_EH_PE_absptr does not imply that the pointer is absolute; it is
// correct for us to treat the value as an offset from a base address
// if the upper nybble is not DW_EH_PE_absptr.
offset = ReadAddress(buffer);
*len = AddressSize();
break;
case DW_EH_PE_uleb128:
offset = ReadUnsignedLEB128(buffer, len);
break;
case DW_EH_PE_udata2:
offset = ReadTwoBytes(buffer);
*len = 2;
break;
case DW_EH_PE_udata4:
offset = ReadFourBytes(buffer);
*len = 4;
break;
case DW_EH_PE_udata8:
offset = ReadEightBytes(buffer);
*len = 8;
break;
case DW_EH_PE_sleb128:
offset = ReadSignedLEB128(buffer, len);
break;
case DW_EH_PE_sdata2:
offset = ReadTwoBytes(buffer);
// Sign-extend from 16 bits.
offset = (offset ^ 0x8000) - 0x8000;
*len = 2;
break;
case DW_EH_PE_sdata4:
offset = ReadFourBytes(buffer);
// Sign-extend from 32 bits.
offset = (offset ^ 0x80000000ULL) - 0x80000000ULL;
*len = 4;
break;
case DW_EH_PE_sdata8:
// No need to sign-extend; this is the full width of our type.
offset = ReadEightBytes(buffer);
*len = 8;
break;
default:
abort();
}
// Find the appropriate base address.
uint64 base;
switch (encoding & 0x70) {
case DW_EH_PE_absptr:
base = 0;
break;
case DW_EH_PE_pcrel:
assert(have_section_base_);
base = section_base_ + (buffer - buffer_base_);
break;
case DW_EH_PE_textrel:
assert(have_text_base_);
base = text_base_;
break;
case DW_EH_PE_datarel:
assert(have_data_base_);
base = data_base_;
break;
case DW_EH_PE_funcrel:
assert(have_function_base_);
base = function_base_;
break;
default:
abort();
}
uint64 pointer = base + offset;
// Remove inappropriate upper bits.
if (AddressSize() == 4)
pointer = pointer & 0xffffffff;
else
assert(AddressSize() == sizeof(uint64));
return pointer;
}
} // namespace dwarf2reader

View File

@ -0,0 +1,310 @@
// -*- mode: C++ -*-
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_DWARF_BYTEREADER_H__
#define COMMON_DWARF_BYTEREADER_H__
#include <string>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
namespace dwarf2reader {
// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN
// because it conflicts with a macro
enum Endianness {
ENDIANNESS_BIG,
ENDIANNESS_LITTLE
};
// A ByteReader knows how to read single- and multi-byte values of
// various endiannesses, sizes, and encodings, as used in DWARF
// debugging information and Linux C++ exception handling data.
class ByteReader {
public:
// Construct a ByteReader capable of reading one-, two-, four-, and
// eight-byte values according to ENDIANNESS, absolute machine-sized
// addresses, DWARF-style "initial length" values, signed and
// unsigned LEB128 numbers, and Linux C++ exception handling data's
// encoded pointers.
explicit ByteReader(enum Endianness endianness);
virtual ~ByteReader();
// Read a single byte from BUFFER and return it as an unsigned 8 bit
// number.
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 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 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 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
// the number of bytes read.
//
// The unsigned LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between 0 and 0x7f, then its unsigned LEB128
// representation is a single byte whose value is N.
//
// - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
// 0x80, followed by the unsigned LEB128 representation of N /
// 128, rounded towards negative infinity.
//
// 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 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.
//
// The signed LEB128 representation of an integer N is a variable
// number of bytes:
//
// - If N is between -0x40 and 0x3f, then its signed LEB128
// representation is a single byte whose value is N in two's
// complement.
//
// - Otherwise, its signed LEB128 representation is (N & 0x7f) |
// 0x80, followed by the signed LEB128 representation of N / 128,
// rounded towards negative infinity.
//
// 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 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
// bytes in length from 1 to 255, but we only support 32- and 64-bit
// addresses at the moment.) You must call this before using the
// ReadAddress member function.
//
// For data in a .debug_info section, or something that .debug_info
// refers to like line number or macro data, the compilation unit
// header's address_size field indicates the address size to use. Call
// frame information doesn't indicate its address size (a shortcoming of
// the spec); you must supply the appropriate size based on the
// architecture of the target machine.
void SetAddressSize(uint8 size);
// Return the current address size, in bytes. This is either 4,
// indicating 32-bit addresses, or 8, indicating 64-bit addresses.
uint8 AddressSize() const { return address_size_; }
// 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 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
// addresses on the target machine; it refers only to the size of section
// offsets and data lengths appearing in the DWARF data. One only needs
// 64-bit DWARF when the debugging data itself is larger than 4GiB.
// 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the
// debugging data itself is very large.
//
// DWARF information identifies itself as 32-bit or 64-bit DWARF: each
// compilation unit and call frame information entry begins with an
// "initial length" field, which, in addition to giving the length of the
// data, also indicates the size of section offsets and lengths appearing
// in that data. The ReadInitialLength member function, below, reads an
// initial length and sets the ByteReader's offset size as a side effect.
// Thus, in the normal process of reading DWARF data, the appropriate
// offset size is set automatically. So, you should only need to call
// SetOffsetSize if you are using the same ByteReader to jump from the
// midst of one block of DWARF data into another.
// Read a DWARF "initial length" field from START, and return it as
// an unsigned 64 bit integer, respecting this ByteReader's
// endianness. Set *LEN to the length of the initial length in
// bytes, either four or twelve. As a side effect, set this
// ByteReader's offset size to either 4 (if we see a 32-bit DWARF
// initial length) or 8 (if we see a 64-bit DWARF initial length).
//
// A DWARF initial length is either:
//
// - a byte count stored as an unsigned 32-bit value less than
// 0xffffff00, indicating that the data whose length is being
// measured uses the 32-bit DWARF format, or
//
// - 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 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 char* buffer) const;
// Return the current offset size, in bytes.
// A return value of 4 indicates that we are reading 32-bit DWARF.
// A return value of 8 indicates that we are reading 64-bit DWARF.
uint8 OffsetSize() const { return offset_size_; }
// Indicate that section offsets and lengths are SIZE bytes long. SIZE
// must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF).
// Usually, you should not call this function yourself; instead, let a
// call to ReadInitialLength establish the data's offset size
// automatically.
void SetOffsetSize(uint8 size);
// The Linux C++ ABI uses a variant of DWARF call frame information
// for exception handling. This data is included in the program's
// address space as the ".eh_frame" section, and intepreted at
// runtime to walk the stack, find exception handlers, and run
// cleanup code. The format is mostly the same as DWARF CFI, with
// some adjustments made to provide the additional
// exception-handling data, and to make the data easier to work with
// in memory --- for example, to allow it to be placed in read-only
// memory even when describing position-independent code.
//
// In particular, exception handling data can select a number of
// different encodings for pointers that appear in the data, as
// described by the DwarfPointerEncoding enum. There are actually
// four axes(!) to the encoding:
//
// - The pointer size: pointers can be 2, 4, or 8 bytes long, or use
// the DWARF LEB128 encoding.
//
// - The pointer's signedness: pointers can be signed or unsigned.
//
// - The pointer's base address: the data stored in the exception
// handling data can be the actual address (that is, an absolute
// pointer), or relative to one of a number of different base
// addreses --- including that of the encoded pointer itself, for
// a form of "pc-relative" addressing.
//
// - The pointer may be indirect: it may be the address where the
// true pointer is stored. (This is used to refer to things via
// global offset table entries, program linkage table entries, or
// other tricks used in position-independent code.)
//
// There are also two options that fall outside that matrix
// altogether: the pointer may be omitted, or it may have padding to
// align it on an appropriate address boundary. (That last option
// may seem like it should be just another axis, but it is not.)
// Indicate that the exception handling data is loaded starting at
// SECTION_BASE, and that the start of its buffer in our own memory
// 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 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.
void SetTextBase(uint64 text_base);
// Indicate that the base address for DW_EH_PE_datarel pointers is
// DATA_BASE. The proper value depends on the ABI; it is usually the
// address of the global offset table, held in a designated register in
// position-independent code. You will need to look at the startup code
// for the target system to be sure. I tried; my eyes bled.
void SetDataBase(uint64 data_base);
// Indicate that the base address for the FDE we are processing is
// FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel
// pointers. (This encoding does not seem to be used by the GNU
// toolchain.)
void SetFunctionBase(uint64 function_base);
// Indicate that we are no longer processing any FDE, so any use of
// a DW_EH_PE_funcrel encoding is an error.
void ClearFunctionBase();
// Return true if ENCODING is a valid pointer encoding.
bool ValidEncoding(DwarfPointerEncoding encoding) const;
// Return true if we have all the information we need to read a
// pointer that uses ENCODING. This checks that the appropriate
// SetFooBase function for ENCODING has been called.
bool UsableEncoding(DwarfPointerEncoding encoding) const;
// Read an encoded pointer from BUFFER using ENCODING; return the
// absolute address it represents, and set *LEN to the pointer's
// length in bytes, including any padding for aligned pointers.
//
// This function calls 'abort' if ENCODING is invalid or refers to a
// 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 char *buffer, DwarfPointerEncoding encoding,
size_t *len) const;
private:
// Function pointer type for our address and offset readers.
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,
// generally depending on the amount of DWARF2/3 info present.
// This function pointer gets set by SetOffsetSize.
AddressReader offset_reader_;
// Read an address from BUFFER and return it as an unsigned 64 bit
// integer. DWARF2/3 allow addresses to be any size from 0-255
// bytes currently. Internally we support 4 and 8 byte addresses,
// and will CHECK on anything else.
// This function pointer gets set by SetAddressSize.
AddressReader address_reader_;
Endianness endian_;
uint8 address_size_;
uint8 offset_size_;
// Base addresses for Linux C++ exception handling data's encoded pointers.
bool have_section_base_, have_text_base_, have_data_base_;
bool have_function_base_;
uint64 section_base_, text_base_, data_base_, function_base_;
const char *buffer_base_;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_BYTEREADER_H__

View File

@ -0,0 +1,697 @@
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader
#include <string>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/cfi_assembler.h"
#include "common/using_std_string.h"
using dwarf2reader::ByteReader;
using dwarf2reader::DwarfPointerEncoding;
using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using google_breakpad::CFISection;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Section;
using testing::Test;
struct ReaderFixture {
string contents;
size_t pointer_size;
};
class Reader: public ReaderFixture, public Test { };
class ReaderDeathTest: public ReaderFixture, public Test { };
TEST_F(Reader, SimpleConstructor) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
CFISection section(kBigEndian, 4);
section
.D8(0xc0)
.D16(0xcf0d)
.D32(0x96fdd219)
.D64(0xbbf55fef0825f117ULL)
.ULEB128(0xa0927048ba8121afULL)
.LEB128(-0x4f337badf4483f83LL)
.D32(0xfec319c9);
ASSERT_TRUE(section.GetContents(&contents));
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));
EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7));
size_t leb128_size;
EXPECT_EQ(0xa0927048ba8121afULL,
reader.ReadUnsignedLEB128(data + 15, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(-0x4f337badf4483f83LL,
reader.ReadSignedLEB128(data + 25, &leb128_size));
EXPECT_EQ(10U, leb128_size);
EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35));
}
TEST_F(Reader, ValidEncodings) {
ByteReader reader(ENDIANNESS_LITTLE);
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_pcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_textrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_datarel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_absptr |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_uleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_udata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sleb128 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata2 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata4 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_TRUE(reader.ValidEncoding(
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect |
dwarf2reader::DW_EH_PE_sdata8 |
dwarf2reader::DW_EH_PE_funcrel)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0)));
EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0)));
}
TEST_F(ReaderDeathTest, DW_EH_PE_omit) {
static const char data[1] = { 42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit,
&pointer_size),
"encoding != DW_EH_PE_omit");
}
TEST_F(Reader, DW_EH_PE_absptr4) {
static const char data[] = { 0x27, 0x57, 0xea, 0x40 };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x40ea5727U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_absptr8) {
static const char data[] = {
0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x010598c240ea5727ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_uleb128) {
static const char data[] = { 0x81, 0x84, 0x4c };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x130201U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata2) {
static const char data[] = { 0xf4, 0x8d };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(0xf48dU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata4) {
static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
EXPECT_EQ(0xa5628f8b,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr8) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x8fed199f69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_udata8Addr4) {
static const char data[] = {
0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
EXPECT_EQ(0x69047304ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sleb128) {
static const char data[] = { 0x42, 0xff, 0xfb, 0x73 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
EXPECT_EQ(-0x030201U & 0xffffffff,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128,
&pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata2) {
static const char data[] = { 0xb9, 0xbf };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffffffbfb9ULL,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2,
&pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata4) {
static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0xffffffffadc2b8f2ULL,
reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_sdata8) {
static const char data[] = {
0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87
};
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(8);
EXPECT_EQ(0x87269b0ce0795766ULL,
reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8,
&pointer_size));
EXPECT_EQ(8U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_pcrel) {
static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel
| dwarf2reader::DW_EH_PE_absptr);
reader.SetCFIDataBase(0x89951377, data);
EXPECT_EQ(0x89951377 + 3 + 0x14c8c402,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_textrel) {
static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e };
ByteReader reader(ENDIANNESS_LITTLE);
reader.SetAddressSize(4);
reader.SetTextBase(0xb91beaf0);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel
| dwarf2reader::DW_EH_PE_sdata2);
EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff,
reader.ReadEncodedPointer(data + 3, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_datarel) {
static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(8);
reader.SetDataBase(0xbef308bd25ce74f0ULL);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel
| dwarf2reader::DW_EH_PE_sleb128);
EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL,
reader.ReadEncodedPointer(data + 2, encoding, &pointer_size));
EXPECT_EQ(3U, pointer_size);
}
TEST_F(Reader, DW_EH_PE_funcrel) {
static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetAddressSize(4);
reader.SetFunctionBase(0x823c3520);
DwarfPointerEncoding encoding =
DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel
| dwarf2reader::DW_EH_PE_udata2);
EXPECT_EQ(0x823c3520 + 0xd148,
reader.ReadEncodedPointer(data + 5, encoding, &pointer_size));
EXPECT_EQ(2U, pointer_size);
}
TEST(UsableBase, CFI) {
static const char data[1] = { 0x42 };
ByteReader reader(ENDIANNESS_BIG);
reader.SetCFIDataBase(0xb31cbd20, data);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Text) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetTextBase(0xa899ccb9);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Data) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetDataBase(0xf7b10bcd);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, Function) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
TEST(UsableBase, ClearFunction) {
ByteReader reader(ENDIANNESS_BIG);
reader.SetFunctionBase(0xc2c0ed81);
reader.ClearFunctionBase();
EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel));
EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit));
EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60)));
}
struct AlignedFixture {
AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); }
static const char data[10];
ByteReader reader;
size_t pointer_size;
};
const char AlignedFixture::data[10] = {
0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b
};
class Aligned: public AlignedFixture, public Test { };
TEST_F(Aligned, DW_EH_PE_aligned0) {
reader.SetCFIDataBase(0xb440305c, data);
EXPECT_EQ(0xfe6e93d8U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned1) {
reader.SetCFIDataBase(0xb440305d, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned2) {
reader.SetCFIDataBase(0xb440305e, data);
EXPECT_EQ(0x93d834d5U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned3) {
reader.SetCFIDataBase(0xb440305f, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned11) {
reader.SetCFIDataBase(0xb4403061, data);
EXPECT_EQ(0xd834d51cU,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(6U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned30) {
reader.SetCFIDataBase(0xb4403063, data);
EXPECT_EQ(0x6e93d834U,
reader.ReadEncodedPointer(data + 1,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(4U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned23) {
reader.SetCFIDataBase(0xb4403062, data);
EXPECT_EQ(0x1cd3ac2bU,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(7U, pointer_size);
}
TEST_F(Aligned, DW_EH_PE_aligned03) {
reader.SetCFIDataBase(0xb4403064, data);
EXPECT_EQ(0x34d51cd3U,
reader.ReadEncodedPointer(data + 3,
dwarf2reader::DW_EH_PE_aligned,
&pointer_size));
EXPECT_EQ(5U, pointer_size);
}

View File

@ -0,0 +1,198 @@
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
// See cfi_assembler.h for details.
#include "common/dwarf/cfi_assembler.h"
#include <assert.h>
#include <stdlib.h>
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
uint8_t version,
const string &augmentation,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = false;
if (dwarf64) {
D32(kDwarf64InitialLengthMarker);
D64(entry_length_->length);
entry_length_->start = Here();
D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier);
}
D8(version);
AppendCString(augmentation);
ULEB128(code_alignment_factor);
LEB128(data_alignment_factor);
if (version == 1)
D8(return_address_register);
else
ULEB128(return_address_register);
return *this;
}
CFISection &CFISection::FDEHeader(Label cie_pointer,
uint64_t initial_location,
uint64_t address_range,
bool dwarf64) {
assert(!entry_length_);
entry_length_ = new PendingLength();
in_fde_ = true;
fde_start_address_ = initial_location;
if (dwarf64) {
D32(0xffffffff);
D64(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D64(Here() - cie_pointer);
else
D64(cie_pointer);
} else {
D32(entry_length_->length);
entry_length_->start = Here();
if (eh_frame_)
D32(Here() - cie_pointer);
else
D32(cie_pointer);
}
EncodedPointer(initial_location);
// The FDE length in an .eh_frame section uses the same encoding as the
// initial location, but ignores the base address (selected by the upper
// nybble of the encoding), as it's a length, not an address that can be
// made relative.
EncodedPointer(address_range,
DwarfPointerEncoding(pointer_encoding_ & 0x0f));
return *this;
}
CFISection &CFISection::FinishEntry() {
assert(entry_length_);
Align(address_size_, dwarf2reader::DW_CFA_nop);
entry_length_->length = Here() - entry_length_->start;
delete entry_length_;
entry_length_ = NULL;
in_fde_ = false;
return *this;
}
CFISection &CFISection::EncodedPointer(uint64_t address,
DwarfPointerEncoding encoding,
const EncodedPointerBases &bases) {
// Omitted data is extremely easy to emit.
if (encoding == dwarf2reader::DW_EH_PE_omit)
return *this;
// If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume
// that ADDRESS is the address at which the pointer is stored --- in
// other words, that bit has no effect on how we write the pointer.
encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect);
// Find the base address to which this pointer is relative. The upper
// nybble of the encoding specifies this.
uint64_t base;
switch (encoding & 0xf0) {
case dwarf2reader::DW_EH_PE_absptr: base = 0; break;
case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break;
case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break;
case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break;
case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break;
case dwarf2reader::DW_EH_PE_aligned: base = 0; break;
default: abort();
};
// Make ADDRESS relative. Yes, this is appropriate even for "absptr"
// values; see gcc/unwind-pe.h.
address -= base;
// Align the pointer, if required.
if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned)
Align(AddressSize());
// Append ADDRESS to this section in the appropriate form. For the
// fixed-width forms, we don't need to differentiate between signed and
// unsigned encodings, because ADDRESS has already been extended to 64
// bits before it was passed to us.
switch (encoding & 0x0f) {
case dwarf2reader::DW_EH_PE_absptr:
Address(address);
break;
case dwarf2reader::DW_EH_PE_uleb128:
ULEB128(address);
break;
case dwarf2reader::DW_EH_PE_sleb128:
LEB128(address);
break;
case dwarf2reader::DW_EH_PE_udata2:
case dwarf2reader::DW_EH_PE_sdata2:
D16(address);
break;
case dwarf2reader::DW_EH_PE_udata4:
case dwarf2reader::DW_EH_PE_sdata4:
D32(address);
break;
case dwarf2reader::DW_EH_PE_udata8:
case dwarf2reader::DW_EH_PE_sdata8:
D64(address);
break;
default:
abort();
}
return *this;
};
const uint32_t CFISection::kDwarf64InitialLengthMarker;
const uint32_t CFISection::kDwarf32CIEIdentifier;
const uint64_t CFISection::kDwarf64CIEIdentifier;
const uint32_t CFISection::kEHFrame32CIEIdentifier;
const uint64_t CFISection::kEHFrame64CIEIdentifier;
} // namespace google_breakpad

View File

@ -0,0 +1,269 @@
// -*- mode: C++ -*-
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// cfi_assembler.h: Define CFISection, a class for creating properly
// (and improperly) formatted DWARF CFI data for unit tests.
#ifndef PROCESSOR_CFI_ASSEMBLER_H_
#define PROCESSOR_CFI_ASSEMBLER_H_
#include <string>
#include "common/dwarf/dwarf2enums.h"
#include "common/test_assembler.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
namespace google_breakpad {
using dwarf2reader::DwarfPointerEncoding;
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
class CFISection: public Section {
public:
// CFI augmentation strings beginning with 'z', defined by the
// Linux/IA-64 C++ ABI, can specify interesting encodings for
// addresses appearing in FDE headers and call frame instructions (and
// for additional fields whose presence the augmentation string
// specifies). In particular, pointers can be specified to be relative
// to various base address: the start of the .text section, the
// location holding the address itself, and so on. These allow the
// frame data to be position-independent even when they live in
// write-protected pages. These variants are specified at the
// following two URLs:
//
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
// http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
//
// CFISection leaves the production of well-formed 'z'-augmented CIEs and
// FDEs to the user, but does provide EncodedPointer, to emit
// properly-encoded addresses for a given pointer encoding.
// EncodedPointer uses an instance of this structure to find the base
// addresses it should use; you can establish a default for all encoded
// pointers appended to this section with SetEncodedPointerBases.
struct EncodedPointerBases {
EncodedPointerBases() : cfi(), text(), data() { }
// The starting address of this CFI section in memory, for
// DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
// that has is loaded into the program's address space.
uint64_t cfi;
// The starting address of this file's .text section, for DW_EH_PE_textrel.
uint64_t text;
// The starting address of this file's .got or .eh_frame_hdr section,
// for DW_EH_PE_datarel.
uint64_t data;
};
// Create a CFISection whose endianness is ENDIANNESS, and where
// machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is
// true, use the .eh_frame format, as described by the Linux
// Standards Base Core Specification, instead of the DWARF CFI
// format.
CFISection(Endianness endianness, size_t address_size,
bool eh_frame = false)
: Section(endianness), address_size_(address_size), eh_frame_(eh_frame),
pointer_encoding_(dwarf2reader::DW_EH_PE_absptr),
encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) {
// The 'start', 'Here', and 'Mark' members of a CFISection all refer
// to section offsets.
start() = 0;
}
// Return this CFISection's address size.
size_t AddressSize() const { return address_size_; }
// Return true if this CFISection uses the .eh_frame format, or
// false if it contains ordinary DWARF CFI data.
bool ContainsEHFrame() const { return eh_frame_; }
// Use ENCODING for pointers in calls to FDEHeader and EncodedPointer.
void SetPointerEncoding(DwarfPointerEncoding encoding) {
pointer_encoding_ = encoding;
}
// Use the addresses in BASES as the base addresses for encoded
// pointers in subsequent calls to FDEHeader or EncodedPointer.
// This function makes a copy of BASES.
void SetEncodedPointerBases(const EncodedPointerBases &bases) {
encoded_pointer_bases_ = bases;
}
// Append a Common Information Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// Before calling this function, you will typically want to use Mark
// or Here to make a label to pass to FDEHeader that refers to this
// CIE's position in the section.
CFISection &CIEHeader(uint64_t code_alignment_factor,
int data_alignment_factor,
unsigned return_address_register,
uint8_t version = 3,
const string &augmentation = "",
bool dwarf64 = false);
// Append a Frame Description Entry header to this section with the
// given values. If dwarf64 is true, use the 64-bit DWARF initial
// length format for the CIE's initial length. Return a reference to
// this section. You should call FinishEntry after writing the last
// instruction for the CIE.
//
// This function doesn't support entries that are longer than
// 0xffffff00 bytes. (The "initial length" is always a 32-bit
// value.) Nor does it support .debug_frame sections longer than
// 0xffffff00 bytes.
CFISection &FDEHeader(Label cie_pointer,
uint64_t initial_location,
uint64_t address_range,
bool dwarf64 = false);
// Note the current position as the end of the last CIE or FDE we
// started, after padding with DW_CFA_nops for alignment. This
// defines the label representing the entry's length, cited in the
// entry's header. Return a reference to this section.
CFISection &FinishEntry();
// Append the contents of BLOCK as a DW_FORM_block value: an
// unsigned LEB128 length, followed by that many bytes of data.
CFISection &Block(const string &block) {
ULEB128(block.size());
Append(block);
return *this;
}
// Append ADDRESS to this section, in the appropriate size and
// endianness. Return a reference to this section.
CFISection &Address(uint64_t address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
CFISection &Address(Label address) {
Section::Append(endianness(), address_size_, address);
return *this;
}
// Append ADDRESS to this section, using ENCODING and BASES. ENCODING
// defaults to this section's default encoding, established by
// SetPointerEncoding. BASES defaults to this section's bases, set by
// SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the
// encoding, assume that ADDRESS is where the true address is stored.
// Return a reference to this section.
//
// (C++ doesn't let me use default arguments here, because I want to
// refer to members of *this in the default argument expression.)
CFISection &EncodedPointer(uint64_t address) {
return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
}
CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) {
return EncodedPointer(address, encoding, encoded_pointer_bases_);
}
CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding,
const EncodedPointerBases &bases);
// Restate some member functions, to keep chaining working nicely.
CFISection &Mark(Label *label) { Section::Mark(label); return *this; }
CFISection &D8(uint8_t v) { Section::D8(v); return *this; }
CFISection &D16(uint16_t v) { Section::D16(v); return *this; }
CFISection &D16(Label v) { Section::D16(v); return *this; }
CFISection &D32(uint32_t v) { Section::D32(v); return *this; }
CFISection &D32(const Label &v) { Section::D32(v); return *this; }
CFISection &D64(uint64_t v) { Section::D64(v); return *this; }
CFISection &D64(const Label &v) { Section::D64(v); return *this; }
CFISection &LEB128(long long v) { Section::LEB128(v); return *this; }
CFISection &ULEB128(uint64_t v) { Section::ULEB128(v); return *this; }
private:
// A length value that we've appended to the section, but is not yet
// known. LENGTH is the appended value; START is a label referring
// to the start of the data whose length was cited.
struct PendingLength {
Label length;
Label start;
};
// Constants used in CFI/.eh_frame data:
// If the first four bytes of an "initial length" are this constant, then
// the data uses the 64-bit DWARF format, and the length itself is the
// subsequent eight bytes.
static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU;
// The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0;
static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0;
static const uint32_t kEHFrame32CIEIdentifier = 0;
static const uint64_t kEHFrame64CIEIdentifier = 0;
// The size of a machine address for the data in this section.
size_t address_size_;
// If true, we are generating a Linux .eh_frame section, instead of
// a standard DWARF .debug_frame section.
bool eh_frame_;
// The encoding to use for FDE pointers.
DwarfPointerEncoding pointer_encoding_;
// The base addresses to use when emitting encoded pointers.
EncodedPointerBases encoded_pointer_bases_;
// The length value for the current entry.
//
// Oddly, this must be dynamically allocated. Labels never get new
// values; they only acquire constraints on the value they already
// have, or assert if you assign them something incompatible. So
// each header needs truly fresh Label objects to cite in their
// headers and track their positions. The alternative is explicit
// destructor invocation and a placement new. Ick.
PendingLength *entry_length_;
// True if we are currently emitting an FDE --- that is, we have
// called FDEHeader but have not yet called FinishEntry.
bool in_fde_;
// If in_fde_ is true, this is its starting address. We use this for
// emitting DW_EH_PE_funcrel pointers.
uint64_t fde_start_address_;
};
} // namespace google_breakpad
#endif // PROCESSOR_CFI_ASSEMBLER_H_

View File

@ -0,0 +1,198 @@
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
// See dwarf2diehandler.h for details.
#include <assert.h>
#include <string>
#include "common/dwarf/dwarf2diehandler.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
DIEDispatcher::~DIEDispatcher() {
while (!die_handlers_.empty()) {
HandlerStack &entry = die_handlers_.top();
if (entry.handler_ != root_handler_)
delete entry.handler_;
die_handlers_.pop();
}
}
bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version) {
return root_handler_->StartCompilationUnit(offset, address_size,
offset_size, cu_length,
dwarf_version);
}
bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag) {
// The stack entry for the parent of this DIE, if there is one.
HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top();
// Does this call indicate that we're done receiving the parent's
// attributes' values? If so, call its EndAttributes member function.
if (parent && parent->handler_ && !parent->reported_attributes_end_) {
parent->reported_attributes_end_ = true;
if (!parent->handler_->EndAttributes()) {
// Finish off this handler now. and edit *PARENT to indicate that
// we don't want to visit any of the children.
parent->handler_->Finish();
if (parent->handler_ != root_handler_)
delete parent->handler_;
parent->handler_ = NULL;
return false;
}
}
// Find a handler for this DIE.
DIEHandler *handler;
if (parent) {
if (parent->handler_)
// Ask the parent to find a handler.
handler = parent->handler_->FindChildHandler(offset, tag);
else
// No parent handler means we're not interested in any of our
// children.
handler = NULL;
} else {
// This is the root DIE. For a non-root DIE, the parent's handler
// decides whether to visit it, but the root DIE has no parent
// handler, so we have a special method on the root DIE handler
// itself to decide.
if (root_handler_->StartRootDIE(offset, tag))
handler = root_handler_;
else
handler = NULL;
}
// Push a handler stack entry for this new handler. As an
// optimization, we don't push NULL-handler entries on top of other
// NULL-handler entries; we just let the oldest such entry stand for
// the whole subtree.
if (handler || !parent || parent->handler_) {
HandlerStack entry;
entry.offset_ = offset;
entry.handler_ = handler;
entry.reported_attributes_end_ = false;
die_handlers_.push(entry);
}
return handler != NULL;
}
void DIEDispatcher::EndDIE(uint64 offset) {
assert(!die_handlers_.empty());
HandlerStack *entry = &die_handlers_.top();
if (entry->handler_) {
// This entry had better be the handler for this DIE.
assert(entry->offset_ == offset);
// If a DIE has no children, this EndDIE call indicates that we're
// done receiving its attributes' values.
if (!entry->reported_attributes_end_)
entry->handler_->EndAttributes(); // Ignore return value: no children.
entry->handler_->Finish();
if (entry->handler_ != root_handler_)
delete entry->handler_;
} else {
// If this DIE is within a tree we're ignoring, then don't pop the
// handler stack: that entry stands for the whole tree.
if (entry->offset_ != offset)
return;
}
die_handlers_.pop();
}
void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeUnsigned(attr, form, data);
}
void DIEDispatcher::ProcessAttributeSigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeSigned(attr, form, data);
}
void DIEDispatcher::ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeReference(attr, form, data);
}
void DIEDispatcher::ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeBuffer(attr, form, data, len);
}
void DIEDispatcher::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeString(attr, form, data);
}
void DIEDispatcher::ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signature) {
HandlerStack &current = die_handlers_.top();
// This had better be an attribute of the DIE we were meant to handle.
assert(offset == current.offset_);
current.handler_->ProcessAttributeSignature(attr, form, signature);
}
} // namespace dwarf2reader

View File

@ -0,0 +1,363 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2reader::CompilationUnit is a simple and direct parser for
// DWARF data, but its handler interface is not convenient to use. In
// particular:
//
// - CompilationUnit calls Dwarf2Handler's member functions to report
// every attribute's value, regardless of what sort of DIE it is.
// As a result, the ProcessAttributeX functions end up looking like
// this:
//
// switch (parent_die_tag) {
// case DW_TAG_x:
// switch (attribute_name) {
// case DW_AT_y:
// handle attribute y of DIE type x
// ...
// } break;
// ...
// }
//
// In C++ it's much nicer to use virtual function dispatch to find
// the right code for a given case than to switch on the DIE tag
// like this.
//
// - Processing different kinds of DIEs requires different sets of
// data: lexical block DIEs have start and end addresses, but struct
// type DIEs don't. It would be nice to be able to have separate
// handler classes for separate kinds of DIEs, each with the members
// appropriate to its role, instead of having one handler class that
// needs to hold data for every DIE type.
//
// - There should be a separate instance of the appropriate handler
// class for each DIE, instead of a single object with tables
// tracking all the dies in the compilation unit.
//
// - It's not convenient to take some action after all a DIE's
// attributes have been seen, but before visiting any of its
// children. The only indication you have that a DIE's attribute
// list is complete is that you get either a StartDIE or an EndDIE
// call.
//
// - It's not convenient to make use of the tree structure of the
// DIEs. Skipping all the children of a given die requires
// maintaining state and returning false from StartDIE until we get
// an EndDIE call with the appropriate offset.
//
// This interface tries to take care of all that. (You're shocked, I'm sure.)
//
// Using the classes here, you provide an initial handler for the root
// DIE of the compilation unit. Each handler receives its DIE's
// attributes, and provides fresh handler objects for children of
// interest, if any. The three classes are:
//
// - DIEHandler: the base class for your DIE-type-specific handler
// classes.
//
// - RootDIEHandler: derived from DIEHandler, the base class for your
// root DIE handler class.
//
// - DIEDispatcher: derived from Dwarf2Handler, an instance of this
// invokes your DIE-type-specific handler objects.
//
// In detail:
//
// - Define handler classes specialized for the DIE types you're
// interested in. These handler classes must inherit from
// DIEHandler. Thus:
//
// class My_DW_TAG_X_Handler: public DIEHandler { ... };
// class My_DW_TAG_Y_Handler: public DIEHandler { ... };
//
// DIEHandler subclasses needn't correspond exactly to single DIE
// types, as shown here; the point is that you can have several
// different classes appropriate to different kinds of DIEs.
//
// - In particular, define a handler class for the compilation
// unit's root DIE, that inherits from RootDIEHandler:
//
// class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... };
//
// RootDIEHandler inherits from DIEHandler, adding a few additional
// member functions for examining the compilation unit as a whole,
// and other quirks of rootness.
//
// - Then, create a DIEDispatcher instance, passing it an instance of
// your root DIE handler class, and use that DIEDispatcher as the
// dwarf2reader::CompilationUnit's handler:
//
// My_DW_TAG_compile_unit_Handler root_die_handler(...);
// DIEDispatcher die_dispatcher(&root_die_handler);
// CompilationUnit reader(sections, offset, bytereader, &die_dispatcher);
//
// Here, 'die_dispatcher' acts as a shim between 'reader' and the
// various DIE-specific handlers you have defined.
//
// - When you call reader.Start(), die_dispatcher behaves as follows,
// starting with your root die handler and the compilation unit's
// root DIE:
//
// - It calls the handler's ProcessAttributeX member functions for
// each of the DIE's attributes.
//
// - It calls the handler's EndAttributes member function. This
// should return true if any of the DIE's children should be
// visited, in which case:
//
// - For each of the DIE's children, die_dispatcher calls the
// DIE's handler's FindChildHandler member function. If that
// returns a pointer to a DIEHandler instance, then
// die_dispatcher uses that handler to process the child, using
// this procedure recursively. Alternatively, if
// FindChildHandler returns NULL, die_dispatcher ignores that
// child and its descendants.
//
// - When die_dispatcher has finished processing all the DIE's
// children, it invokes the handler's Finish() member function,
// and destroys the handler. (As a special case, it doesn't
// destroy the root DIE handler.)
//
// This allows the code for handling a particular kind of DIE to be
// gathered together in a single class, makes it easy to skip all the
// children or individual children of a particular DIE, and provides
// appropriate parental context for each die.
#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__
#define COMMON_DWARF_DWARF2DIEHANDLER_H__
#include <stack>
#include <string>
#include "common/dwarf/types.h"
#include "common/dwarf/dwarf2enums.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
// A base class for handlers for specific DIE types. The series of
// calls made on a DIE handler is as follows:
//
// - for each attribute of the DIE:
// - ProcessAttributeX()
// - EndAttributes()
// - if that returned true, then for each child:
// - FindChildHandler()
// - if that returns a non-NULL pointer to a new handler:
// - recurse, with the new handler and the child die
// - Finish()
// - destruction
class DIEHandler {
public:
DIEHandler() { }
virtual ~DIEHandler() { }
// When we visit a DIE, we first use these member functions to
// report the DIE's attributes and their values. These have the
// same restrictions as the corresponding member functions of
// dwarf2reader::Dwarf2Handler.
//
// Since DWARF does not specify in what order attributes must
// appear, avoid making decisions in these functions that would be
// affected by the presence of other attributes. The EndAttributes
// function is a more appropriate place for such work, as all the
// DIE's attributes have been seen at that point.
//
// The default definitions ignore the values they are passed.
virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) { }
virtual void ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form,
int64 data) { }
virtual void ProcessAttributeReference(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) { }
virtual void ProcessAttributeBuffer(enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len) { }
virtual void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string& data) { }
virtual void ProcessAttributeSignature(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signture) { }
// Once we have reported all the DIE's attributes' values, we call
// this member function. If it returns false, we skip all the DIE's
// children. If it returns true, we call FindChildHandler on each
// child. If that returns a handler object, we use that to visit
// the child; otherwise, we skip the child.
//
// This is a good place to make decisions that depend on more than
// one attribute. DWARF does not specify in what order attributes
// must appear, so only when the EndAttributes function is called
// does the handler have a complete picture of the DIE's attributes.
//
// The default definition elects to ignore the DIE's children.
// You'll need to override this if you override FindChildHandler,
// but at least the default behavior isn't to pass the children to
// FindChildHandler, which then ignores them all.
virtual bool EndAttributes() { return false; }
// If EndAttributes returns true to indicate that some of the DIE's
// children might be of interest, then we apply this function to
// each of the DIE's children. If it returns a handler object, then
// we use that to visit the child DIE. If it returns NULL, we skip
// that child DIE (and all its descendants).
//
// OFFSET is the offset of the child; TAG indicates what kind of DIE
// it is.
//
// The default definition skips all children.
virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) {
return NULL;
}
// When we are done processing a DIE, we call this member function.
// This happens after the EndAttributes call, all FindChildHandler
// calls (if any), and all operations on the children themselves (if
// any). We call Finish on every handler --- even if EndAttributes
// returns false.
virtual void Finish() { };
};
// A subclass of DIEHandler, with additional kludges for handling the
// compilation unit's root die.
class RootDIEHandler: public DIEHandler {
public:
RootDIEHandler() { }
virtual ~RootDIEHandler() { }
// We pass the values reported via Dwarf2Handler::StartCompilationUnit
// to this member function, and skip the entire compilation unit if it
// returns false. So the root DIE handler is actually also
// responsible for handling the compilation unit metadata.
// The default definition always visits the compilation unit.
virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version) { return true; }
// For the root DIE handler only, we pass the offset, tag and
// attributes of the compilation unit's root DIE. This is the only
// way the root DIE handler can find the root DIE's tag. If this
// function returns true, we will visit the root DIE using the usual
// DIEHandler methods; otherwise, we skip the entire compilation
// unit.
//
// The default definition elects to visit the root DIE.
virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; }
};
class DIEDispatcher: public Dwarf2Handler {
public:
// Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for
// the compilation unit's root die, as described for the DIEHandler
// class.
DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { }
// Destroying a DIEDispatcher destroys all active handler objects
// except the root handler.
~DIEDispatcher();
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
bool StartDIE(uint64 offset, enum DwarfTag tag);
void ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeSigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data);
void ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeBuffer(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len);
void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data);
void ProcessAttributeSignature(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 signature);
void EndDIE(uint64 offset);
private:
// The type of a handler stack entry. This includes some fields
// which don't really need to be on the stack --- they could just be
// single data members of DIEDispatcher --- but putting them here
// makes it easier to see that the code is correct.
struct HandlerStack {
// The offset of the DIE for this handler stack entry.
uint64 offset_;
// The handler object interested in this DIE's attributes and
// children. If NULL, we're not interested in either.
DIEHandler *handler_;
// Have we reported the end of this DIE's attributes to the handler?
bool reported_attributes_end_;
};
// Stack of DIE attribute handlers. At StartDIE(D), the top of the
// stack is the handler of D's parent, whom we may ask for a handler
// for D itself. At EndDIE(D), the top of the stack is D's handler.
// Special cases:
//
// - Before we've seen the compilation unit's root DIE, the stack is
// empty; we'll call root_handler_'s special member functions, and
// perhaps push root_handler_ on the stack to look at the root's
// immediate children.
//
// - When we decide to ignore a subtree, we only push an entry on
// the stack for the root of the tree being ignored, rather than
// pushing lots of stack entries with handler_ set to NULL.
std::stack<HandlerStack> die_handlers_;
// The root handler. We don't push it on die_handlers_ until we
// actually get the StartDIE call for the root.
RootDIEHandler *root_handler_;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__

View File

@ -0,0 +1,524 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
#include <string>
#include <utility>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/using_std_string.h"
using std::make_pair;
using ::testing::_;
using ::testing::ContainerEq;
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::InSequence;
using ::testing::Return;
using ::testing::Sequence;
using ::testing::StrEq;
using dwarf2reader::DIEDispatcher;
using dwarf2reader::DIEHandler;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfTag;
using dwarf2reader::RootDIEHandler;
class MockDIEHandler: public DIEHandler {
public:
MOCK_METHOD3(ProcessAttributeUnsigned,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD3(ProcessAttributeSigned,
void(DwarfAttribute, DwarfForm, int64));
MOCK_METHOD3(ProcessAttributeReference,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD4(ProcessAttributeBuffer,
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
MOCK_METHOD0(Finish, void());
};
class MockRootDIEHandler: public RootDIEHandler {
public:
MOCK_METHOD3(ProcessAttributeUnsigned,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD3(ProcessAttributeSigned,
void(DwarfAttribute, DwarfForm, int64));
MOCK_METHOD3(ProcessAttributeReference,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD4(ProcessAttributeBuffer,
void(DwarfAttribute, DwarfForm, const char *, uint64));
MOCK_METHOD3(ProcessAttributeString,
void(DwarfAttribute, DwarfForm, const string &));
MOCK_METHOD3(ProcessAttributeSignature,
void(DwarfAttribute, DwarfForm, uint64));
MOCK_METHOD0(EndAttributes, bool());
MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag));
MOCK_METHOD0(Finish, void());
MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag));
};
// If the handler elects to skip the compilation unit, the dispatcher
// should tell the reader so.
TEST(Dwarf2DIEHandler, SkipCompilationUnit) {
Sequence s;
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66))
.InSequence(s)
.WillOnce(Return(false));
EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66));
}
// If the handler elects to skip the root DIE, the dispatcher should
// tell the reader so.
TEST(Dwarf2DIEHandler, SkipRootDIE) {
Sequence s;
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
.InSequence(s)
.WillOnce(Return(false));
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,
0xf4, 0x02,
0xb00febffa76e2b2bLL, 0x5c));
EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6));
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
// If the handler elects to skip the root DIE's children, the
// dispatcher should tell the reader so --- and avoid deleting the
// root handler.
TEST(Dwarf2DIEHandler, SkipRootDIEChildren) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
{
InSequence s;
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0,
0x09f8bf0767f91675LL, 0xdb))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6))
.WillOnce(Return(true));
// Please don't tell me about my children.
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(false));
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL,
0x26, 0xa0,
0x09f8bf0767f91675LL, 0xdb));
EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
(DwarfTag) 0xb4f98da6));
EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
(DwarfTag) 0xc3a17bba));
die_dispatcher.EndDIE(0x435150ceedccda18LL);
die_dispatcher.EndDIE(0x7d08242b4b510cf2LL);
}
// The dispatcher should pass attribute values through to the die
// handler accurately.
TEST(Dwarf2DIEHandler, PassAttributeValues) {
MockRootDIEHandler mock_root_handler;
DIEDispatcher die_dispatcher(&mock_root_handler);
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.
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc,
0x2ecb4dc778a80f21LL, 0x66))
.WillOnce(Return(true));
// We'll like the root DIE.
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c))
.WillOnce(Return(true));
// Expect some attribute values.
EXPECT_CALL(mock_root_handler,
ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed,
(DwarfForm) 0x424f1468,
0xa592571997facda1ULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeSigned((DwarfAttribute) 0x43694dc9,
(DwarfForm) 0xf6f78901L,
0x92602a4e3bf1f446LL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeReference((DwarfAttribute) 0x4033e8cL,
(DwarfForm) 0xf66fbe0bL,
0x50fddef44734fdecULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af,
(DwarfForm) 0xe99a539a,
buffer, sizeof(buffer)))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeString((DwarfAttribute) 0x310ed065,
(DwarfForm) 0x15762fec,
StrEq(str)))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler,
ProcessAttributeSignature((DwarfAttribute) 0x58790d72,
(DwarfForm) 0x4159f138,
0x94682463613e6a5fULL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, FindChildHandler(_, _))
.Times(0);
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
// Drive the dispatcher.
// Report the CU header.
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL,
0x89, 0xdc,
0x2ecb4dc778a80f21LL,
0x66));
// Report the root DIE.
EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL,
(DwarfTag) 0x9829445c));
// Report some attribute values.
die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x1cc0bfed,
(DwarfForm) 0x424f1468,
0xa592571997facda1ULL);
die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x43694dc9,
(DwarfForm) 0xf6f78901,
0x92602a4e3bf1f446LL);
die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x4033e8c,
(DwarfForm) 0xf66fbe0b,
0x50fddef44734fdecULL);
die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x25d7e0af,
(DwarfForm) 0xe99a539a,
buffer, sizeof(buffer));
die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x310ed065,
(DwarfForm) 0x15762fec,
str);
die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL,
(DwarfAttribute) 0x58790d72,
(DwarfForm) 0x4159f138,
0x94682463613e6a5fULL);
// Finish the root DIE (and thus the CU).
die_dispatcher.EndDIE(0xe2222da01e29f2a9LL);
}
TEST(Dwarf2DIEHandler, FindAndSkipChildren) {
MockRootDIEHandler mock_root_handler;
MockDIEHandler *mock_child1_handler = new(MockDIEHandler);
MockDIEHandler *mock_child3_handler = new(MockDIEHandler);
DIEDispatcher die_dispatcher(&mock_root_handler);
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
0x47dd3c764275a216LL, 0xa5))
.WillOnce(Return(true));
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler,
ProcessAttributeSigned((DwarfAttribute) 0xf779a642,
(DwarfForm) 0x2cb63027,
0x18e744661769d08fLL))
.WillOnce(Return());
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
// First child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c))
.WillOnce(Return(mock_child1_handler));
{
EXPECT_CALL(*mock_child1_handler,
ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65,
(DwarfForm) 0xe4f64c41,
0x1b04e5444a55fe67LL))
.WillOnce(Return());
EXPECT_CALL(*mock_child1_handler, EndAttributes())
.WillOnce(Return(false));
// Skip first grandchild DIE and first great-grandchild DIE.
EXPECT_CALL(*mock_child1_handler, Finish())
.WillOnce(Return());
}
// Second child DIE. Root handler will decline to return a handler
// for this child.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b))
.WillOnce(Return((DIEHandler *) NULL));
// Third child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e))
.WillOnce(Return(mock_child3_handler));
{
EXPECT_CALL(*mock_child3_handler,
ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL))
.WillOnce(Return());
EXPECT_CALL(*mock_child3_handler, EndAttributes())
.WillOnce(Return(true));
EXPECT_CALL(*mock_child3_handler, Finish())
.WillOnce(Return());
}
EXPECT_CALL(mock_root_handler, Finish())
.WillOnce(Return());
}
}
// Drive the dispatcher.
// Report the CU header.
EXPECT_TRUE(die_dispatcher
.StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21,
0x47dd3c764275a216LL, 0xa5));
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL,
(DwarfTag) 0xf5d60c59));
die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
(DwarfAttribute) 0xf779a642,
(DwarfForm) 0x2cb63027,
0x18e744661769d08fLL);
// First child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
(DwarfTag) 0xac2cbd8c));
die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
(DwarfAttribute) 0xa6fd6f65,
(DwarfForm) 0xe4f64c41,
0x1b04e5444a55fe67LL);
// First grandchild DIE. Will be skipped.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
(DwarfTag) 0x22f05a15));
// First great-grandchild DIE. Will be skipped without being
// mentioned to any handler.
{
EXPECT_FALSE(die_dispatcher
.StartDIE(0xb3076285d25cac25LL,
(DwarfTag) 0xcff4061b));
die_dispatcher.EndDIE(0xb3076285d25cac25LL);
}
die_dispatcher.EndDIE(0xd68de1ee0bd29419LL);
}
die_dispatcher.EndDIE(0x149f644f8116fe8cLL);
}
// Second child DIE. Root handler will decline to find a handler for it.
{
EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL,
(DwarfTag) 0x505a068b));
die_dispatcher.EndDIE(0x97412be24875de9dLL);
}
// Third child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
(DwarfTag) 0x8c22970e));
die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL);
die_dispatcher.EndDIE(0x753c964c8ab538aeLL);
}
// Finish the root DIE (and thus the CU).
die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL);
}
}
// The DIEDispatcher destructor is supposed to delete all handlers on
// the stack, except for the root.
TEST(Dwarf2DIEHandler, FreeHandlersOnStack) {
MockRootDIEHandler mock_root_handler;
MockDIEHandler *mock_child_handler = new(MockDIEHandler);
MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler);
{
InSequence s;
// We'll like the compilation unit header.
EXPECT_CALL(mock_root_handler,
StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
0x76d392ff393ddda2LL, 0xbf))
.WillOnce(Return(true));
// Root DIE.
{
EXPECT_CALL(mock_root_handler,
StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361))
.WillOnce(Return(true));
EXPECT_CALL(mock_root_handler, EndAttributes())
.WillOnce(Return(true));
// Child DIE.
EXPECT_CALL(mock_root_handler,
FindChildHandler(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0))
.WillOnce(Return(mock_child_handler));
{
EXPECT_CALL(*mock_child_handler, EndAttributes())
.WillOnce(Return(true));
// Grandchild DIE.
EXPECT_CALL(*mock_child_handler,
FindChildHandler(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007))
.WillOnce(Return(mock_grandchild_handler));
{
EXPECT_CALL(*mock_grandchild_handler,
ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL))
.WillOnce(Return());
// At this point, we abandon the traversal, so none of the
// usual stuff should get called.
EXPECT_CALL(*mock_grandchild_handler, EndAttributes())
.Times(0);
EXPECT_CALL(*mock_grandchild_handler, Finish())
.Times(0);
}
EXPECT_CALL(*mock_child_handler, Finish())
.Times(0);
}
EXPECT_CALL(mock_root_handler, Finish())
.Times(0);
}
}
// The dispatcher.
DIEDispatcher die_dispatcher(&mock_root_handler);
// Report the CU header.
EXPECT_TRUE(die_dispatcher
.StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89,
0x76d392ff393ddda2LL, 0xbf));
// Report the root DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL,
(DwarfTag) 0x98980361));
// Child DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
(DwarfTag) 0x898bf0d0));
// Grandchild DIE.
{
EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
(DwarfTag) 0x2802d007));
die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL,
(DwarfAttribute) 0x4e2b7cfb,
(DwarfForm) 0x610b7ae1,
0x3ea5c609d7d7560fLL);
// Stop the traversal abruptly, so that there will still be
// handlers on the stack when the dispatcher is destructed.
// No EndDIE call...
}
// No EndDIE call...
}
// No EndDIE call...
}
}

View File

@ -0,0 +1,650 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_DWARF_DWARF2ENUMS_H__
#define COMMON_DWARF_DWARF2ENUMS_H__
namespace dwarf2reader {
// These enums do not follow the google3 style only because they are
// known universally (specs, other implementations) by the names in
// exactly this capitalization.
// Tag names and codes.
enum DwarfTag {
DW_TAG_padding = 0x00,
DW_TAG_array_type = 0x01,
DW_TAG_class_type = 0x02,
DW_TAG_entry_point = 0x03,
DW_TAG_enumeration_type = 0x04,
DW_TAG_formal_parameter = 0x05,
DW_TAG_imported_declaration = 0x08,
DW_TAG_label = 0x0a,
DW_TAG_lexical_block = 0x0b,
DW_TAG_member = 0x0d,
DW_TAG_pointer_type = 0x0f,
DW_TAG_reference_type = 0x10,
DW_TAG_compile_unit = 0x11,
DW_TAG_string_type = 0x12,
DW_TAG_structure_type = 0x13,
DW_TAG_subroutine_type = 0x15,
DW_TAG_typedef = 0x16,
DW_TAG_union_type = 0x17,
DW_TAG_unspecified_parameters = 0x18,
DW_TAG_variant = 0x19,
DW_TAG_common_block = 0x1a,
DW_TAG_common_inclusion = 0x1b,
DW_TAG_inheritance = 0x1c,
DW_TAG_inlined_subroutine = 0x1d,
DW_TAG_module = 0x1e,
DW_TAG_ptr_to_member_type = 0x1f,
DW_TAG_set_type = 0x20,
DW_TAG_subrange_type = 0x21,
DW_TAG_with_stmt = 0x22,
DW_TAG_access_declaration = 0x23,
DW_TAG_base_type = 0x24,
DW_TAG_catch_block = 0x25,
DW_TAG_const_type = 0x26,
DW_TAG_constant = 0x27,
DW_TAG_enumerator = 0x28,
DW_TAG_file_type = 0x29,
DW_TAG_friend = 0x2a,
DW_TAG_namelist = 0x2b,
DW_TAG_namelist_item = 0x2c,
DW_TAG_packed_type = 0x2d,
DW_TAG_subprogram = 0x2e,
DW_TAG_template_type_param = 0x2f,
DW_TAG_template_value_param = 0x30,
DW_TAG_thrown_type = 0x31,
DW_TAG_try_block = 0x32,
DW_TAG_variant_part = 0x33,
DW_TAG_variable = 0x34,
DW_TAG_volatile_type = 0x35,
// DWARF 3.
DW_TAG_dwarf_procedure = 0x36,
DW_TAG_restrict_type = 0x37,
DW_TAG_interface_type = 0x38,
DW_TAG_namespace = 0x39,
DW_TAG_imported_module = 0x3a,
DW_TAG_unspecified_type = 0x3b,
DW_TAG_partial_unit = 0x3c,
DW_TAG_imported_unit = 0x3d,
// SGI/MIPS Extensions.
DW_TAG_MIPS_loop = 0x4081,
// HP extensions. See:
// ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz
DW_TAG_HP_array_descriptor = 0x4090,
// GNU extensions.
DW_TAG_format_label = 0x4101, // For FORTRAN 77 and Fortran 90.
DW_TAG_function_template = 0x4102, // For C++.
DW_TAG_class_template = 0x4103, // For C++.
DW_TAG_GNU_BINCL = 0x4104,
DW_TAG_GNU_EINCL = 0x4105,
// Extensions for UPC. See: http://upc.gwu.edu/~upc.
DW_TAG_upc_shared_type = 0x8765,
DW_TAG_upc_strict_type = 0x8766,
DW_TAG_upc_relaxed_type = 0x8767,
// PGI (STMicroelectronics) extensions. No documentation available.
DW_TAG_PGI_kanji_type = 0xA000,
DW_TAG_PGI_interface_block = 0xA020
};
enum DwarfHasChild {
DW_children_no = 0,
DW_children_yes = 1
};
// Form names and codes.
enum DwarfForm {
DW_FORM_addr = 0x01,
DW_FORM_block2 = 0x03,
DW_FORM_block4 = 0x04,
DW_FORM_data2 = 0x05,
DW_FORM_data4 = 0x06,
DW_FORM_data8 = 0x07,
DW_FORM_string = 0x08,
DW_FORM_block = 0x09,
DW_FORM_block1 = 0x0a,
DW_FORM_data1 = 0x0b,
DW_FORM_flag = 0x0c,
DW_FORM_sdata = 0x0d,
DW_FORM_strp = 0x0e,
DW_FORM_udata = 0x0f,
DW_FORM_ref_addr = 0x10,
DW_FORM_ref1 = 0x11,
DW_FORM_ref2 = 0x12,
DW_FORM_ref4 = 0x13,
DW_FORM_ref8 = 0x14,
DW_FORM_ref_udata = 0x15,
DW_FORM_indirect = 0x16,
// Added in DWARF 4:
DW_FORM_sec_offset = 0x17,
DW_FORM_exprloc = 0x18,
DW_FORM_flag_present = 0x19,
DW_FORM_ref_sig8 = 0x20
};
// Attribute names and codes
enum DwarfAttribute {
DW_AT_sibling = 0x01,
DW_AT_location = 0x02,
DW_AT_name = 0x03,
DW_AT_ordering = 0x09,
DW_AT_subscr_data = 0x0a,
DW_AT_byte_size = 0x0b,
DW_AT_bit_offset = 0x0c,
DW_AT_bit_size = 0x0d,
DW_AT_element_list = 0x0f,
DW_AT_stmt_list = 0x10,
DW_AT_low_pc = 0x11,
DW_AT_high_pc = 0x12,
DW_AT_language = 0x13,
DW_AT_member = 0x14,
DW_AT_discr = 0x15,
DW_AT_discr_value = 0x16,
DW_AT_visibility = 0x17,
DW_AT_import = 0x18,
DW_AT_string_length = 0x19,
DW_AT_common_reference = 0x1a,
DW_AT_comp_dir = 0x1b,
DW_AT_const_value = 0x1c,
DW_AT_containing_type = 0x1d,
DW_AT_default_value = 0x1e,
DW_AT_inline = 0x20,
DW_AT_is_optional = 0x21,
DW_AT_lower_bound = 0x22,
DW_AT_producer = 0x25,
DW_AT_prototyped = 0x27,
DW_AT_return_addr = 0x2a,
DW_AT_start_scope = 0x2c,
DW_AT_stride_size = 0x2e,
DW_AT_upper_bound = 0x2f,
DW_AT_abstract_origin = 0x31,
DW_AT_accessibility = 0x32,
DW_AT_address_class = 0x33,
DW_AT_artificial = 0x34,
DW_AT_base_types = 0x35,
DW_AT_calling_convention = 0x36,
DW_AT_count = 0x37,
DW_AT_data_member_location = 0x38,
DW_AT_decl_column = 0x39,
DW_AT_decl_file = 0x3a,
DW_AT_decl_line = 0x3b,
DW_AT_declaration = 0x3c,
DW_AT_discr_list = 0x3d,
DW_AT_encoding = 0x3e,
DW_AT_external = 0x3f,
DW_AT_frame_base = 0x40,
DW_AT_friend = 0x41,
DW_AT_identifier_case = 0x42,
DW_AT_macro_info = 0x43,
DW_AT_namelist_items = 0x44,
DW_AT_priority = 0x45,
DW_AT_segment = 0x46,
DW_AT_specification = 0x47,
DW_AT_static_link = 0x48,
DW_AT_type = 0x49,
DW_AT_use_location = 0x4a,
DW_AT_variable_parameter = 0x4b,
DW_AT_virtuality = 0x4c,
DW_AT_vtable_elem_location = 0x4d,
// DWARF 3 values.
DW_AT_allocated = 0x4e,
DW_AT_associated = 0x4f,
DW_AT_data_location = 0x50,
DW_AT_stride = 0x51,
DW_AT_entry_pc = 0x52,
DW_AT_use_UTF8 = 0x53,
DW_AT_extension = 0x54,
DW_AT_ranges = 0x55,
DW_AT_trampoline = 0x56,
DW_AT_call_column = 0x57,
DW_AT_call_file = 0x58,
DW_AT_call_line = 0x59,
// SGI/MIPS extensions.
DW_AT_MIPS_fde = 0x2001,
DW_AT_MIPS_loop_begin = 0x2002,
DW_AT_MIPS_tail_loop_begin = 0x2003,
DW_AT_MIPS_epilog_begin = 0x2004,
DW_AT_MIPS_loop_unroll_factor = 0x2005,
DW_AT_MIPS_software_pipeline_depth = 0x2006,
DW_AT_MIPS_linkage_name = 0x2007,
DW_AT_MIPS_stride = 0x2008,
DW_AT_MIPS_abstract_name = 0x2009,
DW_AT_MIPS_clone_origin = 0x200a,
DW_AT_MIPS_has_inlines = 0x200b,
// HP extensions.
DW_AT_HP_block_index = 0x2000,
DW_AT_HP_unmodifiable = 0x2001, // Same as DW_AT_MIPS_fde.
DW_AT_HP_actuals_stmt_list = 0x2010,
DW_AT_HP_proc_per_section = 0x2011,
DW_AT_HP_raw_data_ptr = 0x2012,
DW_AT_HP_pass_by_reference = 0x2013,
DW_AT_HP_opt_level = 0x2014,
DW_AT_HP_prof_version_id = 0x2015,
DW_AT_HP_opt_flags = 0x2016,
DW_AT_HP_cold_region_low_pc = 0x2017,
DW_AT_HP_cold_region_high_pc = 0x2018,
DW_AT_HP_all_variables_modifiable = 0x2019,
DW_AT_HP_linkage_name = 0x201a,
DW_AT_HP_prof_flags = 0x201b, // In comp unit of procs_info for -g.
// GNU extensions.
DW_AT_sf_names = 0x2101,
DW_AT_src_info = 0x2102,
DW_AT_mac_info = 0x2103,
DW_AT_src_coords = 0x2104,
DW_AT_body_begin = 0x2105,
DW_AT_body_end = 0x2106,
DW_AT_GNU_vector = 0x2107,
// VMS extensions.
DW_AT_VMS_rtnbeg_pd_address = 0x2201,
// UPC extension.
DW_AT_upc_threads_scaled = 0x3210,
// PGI (STMicroelectronics) extensions.
DW_AT_PGI_lbase = 0x3a00,
DW_AT_PGI_soffset = 0x3a01,
DW_AT_PGI_lstride = 0x3a02
};
// Line number opcodes.
enum DwarfLineNumberOps {
DW_LNS_extended_op = 0,
DW_LNS_copy = 1,
DW_LNS_advance_pc = 2,
DW_LNS_advance_line = 3,
DW_LNS_set_file = 4,
DW_LNS_set_column = 5,
DW_LNS_negate_stmt = 6,
DW_LNS_set_basic_block = 7,
DW_LNS_const_add_pc = 8,
DW_LNS_fixed_advance_pc = 9,
// DWARF 3.
DW_LNS_set_prologue_end = 10,
DW_LNS_set_epilogue_begin = 11,
DW_LNS_set_isa = 12
};
// Line number extended opcodes.
enum DwarfLineNumberExtendedOps {
DW_LNE_end_sequence = 1,
DW_LNE_set_address = 2,
DW_LNE_define_file = 3,
// HP extensions.
DW_LNE_HP_negate_is_UV_update = 0x11,
DW_LNE_HP_push_context = 0x12,
DW_LNE_HP_pop_context = 0x13,
DW_LNE_HP_set_file_line_column = 0x14,
DW_LNE_HP_set_routine_name = 0x15,
DW_LNE_HP_set_sequence = 0x16,
DW_LNE_HP_negate_post_semantics = 0x17,
DW_LNE_HP_negate_function_exit = 0x18,
DW_LNE_HP_negate_front_end_logical = 0x19,
DW_LNE_HP_define_proc = 0x20
};
// Type encoding names and codes
enum DwarfEncoding {
DW_ATE_address =0x1,
DW_ATE_boolean =0x2,
DW_ATE_complex_float =0x3,
DW_ATE_float =0x4,
DW_ATE_signed =0x5,
DW_ATE_signed_char =0x6,
DW_ATE_unsigned =0x7,
DW_ATE_unsigned_char =0x8,
// DWARF3/DWARF3f
DW_ATE_imaginary_float =0x9,
DW_ATE_packed_decimal =0xa,
DW_ATE_numeric_string =0xb,
DW_ATE_edited =0xc,
DW_ATE_signed_fixed =0xd,
DW_ATE_unsigned_fixed =0xe,
DW_ATE_decimal_float =0xf,
DW_ATE_lo_user =0x80,
DW_ATE_hi_user =0xff
};
// Location virtual machine opcodes
enum DwarfOpcode {
DW_OP_addr =0x03,
DW_OP_deref =0x06,
DW_OP_const1u =0x08,
DW_OP_const1s =0x09,
DW_OP_const2u =0x0a,
DW_OP_const2s =0x0b,
DW_OP_const4u =0x0c,
DW_OP_const4s =0x0d,
DW_OP_const8u =0x0e,
DW_OP_const8s =0x0f,
DW_OP_constu =0x10,
DW_OP_consts =0x11,
DW_OP_dup =0x12,
DW_OP_drop =0x13,
DW_OP_over =0x14,
DW_OP_pick =0x15,
DW_OP_swap =0x16,
DW_OP_rot =0x17,
DW_OP_xderef =0x18,
DW_OP_abs =0x19,
DW_OP_and =0x1a,
DW_OP_div =0x1b,
DW_OP_minus =0x1c,
DW_OP_mod =0x1d,
DW_OP_mul =0x1e,
DW_OP_neg =0x1f,
DW_OP_not =0x20,
DW_OP_or =0x21,
DW_OP_plus =0x22,
DW_OP_plus_uconst =0x23,
DW_OP_shl =0x24,
DW_OP_shr =0x25,
DW_OP_shra =0x26,
DW_OP_xor =0x27,
DW_OP_bra =0x28,
DW_OP_eq =0x29,
DW_OP_ge =0x2a,
DW_OP_gt =0x2b,
DW_OP_le =0x2c,
DW_OP_lt =0x2d,
DW_OP_ne =0x2e,
DW_OP_skip =0x2f,
DW_OP_lit0 =0x30,
DW_OP_lit1 =0x31,
DW_OP_lit2 =0x32,
DW_OP_lit3 =0x33,
DW_OP_lit4 =0x34,
DW_OP_lit5 =0x35,
DW_OP_lit6 =0x36,
DW_OP_lit7 =0x37,
DW_OP_lit8 =0x38,
DW_OP_lit9 =0x39,
DW_OP_lit10 =0x3a,
DW_OP_lit11 =0x3b,
DW_OP_lit12 =0x3c,
DW_OP_lit13 =0x3d,
DW_OP_lit14 =0x3e,
DW_OP_lit15 =0x3f,
DW_OP_lit16 =0x40,
DW_OP_lit17 =0x41,
DW_OP_lit18 =0x42,
DW_OP_lit19 =0x43,
DW_OP_lit20 =0x44,
DW_OP_lit21 =0x45,
DW_OP_lit22 =0x46,
DW_OP_lit23 =0x47,
DW_OP_lit24 =0x48,
DW_OP_lit25 =0x49,
DW_OP_lit26 =0x4a,
DW_OP_lit27 =0x4b,
DW_OP_lit28 =0x4c,
DW_OP_lit29 =0x4d,
DW_OP_lit30 =0x4e,
DW_OP_lit31 =0x4f,
DW_OP_reg0 =0x50,
DW_OP_reg1 =0x51,
DW_OP_reg2 =0x52,
DW_OP_reg3 =0x53,
DW_OP_reg4 =0x54,
DW_OP_reg5 =0x55,
DW_OP_reg6 =0x56,
DW_OP_reg7 =0x57,
DW_OP_reg8 =0x58,
DW_OP_reg9 =0x59,
DW_OP_reg10 =0x5a,
DW_OP_reg11 =0x5b,
DW_OP_reg12 =0x5c,
DW_OP_reg13 =0x5d,
DW_OP_reg14 =0x5e,
DW_OP_reg15 =0x5f,
DW_OP_reg16 =0x60,
DW_OP_reg17 =0x61,
DW_OP_reg18 =0x62,
DW_OP_reg19 =0x63,
DW_OP_reg20 =0x64,
DW_OP_reg21 =0x65,
DW_OP_reg22 =0x66,
DW_OP_reg23 =0x67,
DW_OP_reg24 =0x68,
DW_OP_reg25 =0x69,
DW_OP_reg26 =0x6a,
DW_OP_reg27 =0x6b,
DW_OP_reg28 =0x6c,
DW_OP_reg29 =0x6d,
DW_OP_reg30 =0x6e,
DW_OP_reg31 =0x6f,
DW_OP_breg0 =0x70,
DW_OP_breg1 =0x71,
DW_OP_breg2 =0x72,
DW_OP_breg3 =0x73,
DW_OP_breg4 =0x74,
DW_OP_breg5 =0x75,
DW_OP_breg6 =0x76,
DW_OP_breg7 =0x77,
DW_OP_breg8 =0x78,
DW_OP_breg9 =0x79,
DW_OP_breg10 =0x7a,
DW_OP_breg11 =0x7b,
DW_OP_breg12 =0x7c,
DW_OP_breg13 =0x7d,
DW_OP_breg14 =0x7e,
DW_OP_breg15 =0x7f,
DW_OP_breg16 =0x80,
DW_OP_breg17 =0x81,
DW_OP_breg18 =0x82,
DW_OP_breg19 =0x83,
DW_OP_breg20 =0x84,
DW_OP_breg21 =0x85,
DW_OP_breg22 =0x86,
DW_OP_breg23 =0x87,
DW_OP_breg24 =0x88,
DW_OP_breg25 =0x89,
DW_OP_breg26 =0x8a,
DW_OP_breg27 =0x8b,
DW_OP_breg28 =0x8c,
DW_OP_breg29 =0x8d,
DW_OP_breg30 =0x8e,
DW_OP_breg31 =0x8f,
DW_OP_regX =0x90,
DW_OP_fbreg =0x91,
DW_OP_bregX =0x92,
DW_OP_piece =0x93,
DW_OP_deref_size =0x94,
DW_OP_xderef_size =0x95,
DW_OP_nop =0x96,
// DWARF3/DWARF3f
DW_OP_push_object_address =0x97,
DW_OP_call2 =0x98,
DW_OP_call4 =0x99,
DW_OP_call_ref =0x9a,
DW_OP_form_tls_address =0x9b,
DW_OP_call_frame_cfa =0x9c,
DW_OP_bit_piece =0x9d,
DW_OP_lo_user =0xe0,
DW_OP_hi_user =0xff,
// GNU extensions
DW_OP_GNU_push_tls_address =0xe0
};
// Source languages. These are values for DW_AT_language.
enum DwarfLanguage
{
DW_LANG_none =0x0000,
DW_LANG_C89 =0x0001,
DW_LANG_C =0x0002,
DW_LANG_Ada83 =0x0003,
DW_LANG_C_plus_plus =0x0004,
DW_LANG_Cobol74 =0x0005,
DW_LANG_Cobol85 =0x0006,
DW_LANG_Fortran77 =0x0007,
DW_LANG_Fortran90 =0x0008,
DW_LANG_Pascal83 =0x0009,
DW_LANG_Modula2 =0x000a,
DW_LANG_Java =0x000b,
DW_LANG_C99 =0x000c,
DW_LANG_Ada95 =0x000d,
DW_LANG_Fortran95 =0x000e,
DW_LANG_PLI =0x000f,
DW_LANG_ObjC =0x0010,
DW_LANG_ObjC_plus_plus =0x0011,
DW_LANG_UPC =0x0012,
DW_LANG_D =0x0013,
// Implementation-defined language code range.
DW_LANG_lo_user = 0x8000,
DW_LANG_hi_user = 0xffff,
// Extensions.
// MIPS assembly language. The GNU toolchain uses this for all
// assembly languages, since there's no generic DW_LANG_ value for that.
// See include/dwarf2.h in the binutils, gdb, or gcc source trees.
DW_LANG_Mips_Assembler =0x8001,
DW_LANG_Upc =0x8765 // Unified Parallel C
};
// Inline codes. These are values for DW_AT_inline.
enum DwarfInline {
DW_INL_not_inlined =0x0,
DW_INL_inlined =0x1,
DW_INL_declared_not_inlined =0x2,
DW_INL_declared_inlined =0x3
};
// Call Frame Info instructions.
enum DwarfCFI
{
DW_CFA_advance_loc = 0x40,
DW_CFA_offset = 0x80,
DW_CFA_restore = 0xc0,
DW_CFA_nop = 0x00,
DW_CFA_set_loc = 0x01,
DW_CFA_advance_loc1 = 0x02,
DW_CFA_advance_loc2 = 0x03,
DW_CFA_advance_loc4 = 0x04,
DW_CFA_offset_extended = 0x05,
DW_CFA_restore_extended = 0x06,
DW_CFA_undefined = 0x07,
DW_CFA_same_value = 0x08,
DW_CFA_register = 0x09,
DW_CFA_remember_state = 0x0a,
DW_CFA_restore_state = 0x0b,
DW_CFA_def_cfa = 0x0c,
DW_CFA_def_cfa_register = 0x0d,
DW_CFA_def_cfa_offset = 0x0e,
DW_CFA_def_cfa_expression = 0x0f,
DW_CFA_expression = 0x10,
DW_CFA_offset_extended_sf = 0x11,
DW_CFA_def_cfa_sf = 0x12,
DW_CFA_def_cfa_offset_sf = 0x13,
DW_CFA_val_offset = 0x14,
DW_CFA_val_offset_sf = 0x15,
DW_CFA_val_expression = 0x16,
// Opcodes in this range are reserved for user extensions.
DW_CFA_lo_user = 0x1c,
DW_CFA_hi_user = 0x3f,
// SGI/MIPS specific.
DW_CFA_MIPS_advance_loc8 = 0x1d,
// GNU extensions.
DW_CFA_GNU_window_save = 0x2d,
DW_CFA_GNU_args_size = 0x2e,
DW_CFA_GNU_negative_offset_extended = 0x2f
};
// Exception handling 'z' augmentation letters.
enum DwarfZAugmentationCodes {
// If the CFI augmentation string begins with 'z', then the CIE and FDE
// have an augmentation data area just before the instructions, whose
// contents are determined by the subsequent augmentation letters.
DW_Z_augmentation_start = 'z',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding, and the FDE
// augmentation data includes a language-specific data area pointer,
// represented using that encoding.
DW_Z_has_LSDA = 'L',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding, followed by a pointer
// to a personality routine, represented using that encoding.
DW_Z_has_personality_routine = 'P',
// If this letter is present in a 'z' augmentation string, the CIE
// augmentation data includes a pointer encoding describing how the FDE's
// initial location, address range, and DW_CFA_set_loc operands are
// encoded.
DW_Z_has_FDE_address_encoding = 'R',
// If this letter is present in a 'z' augmentation string, then code
// addresses covered by FDEs that cite this CIE are signal delivery
// trampolines. Return addresses of frames in trampolines should not be
// adjusted as described in section 6.4.4 of the DWARF 3 spec.
DW_Z_is_signal_trampoline = 'S'
};
// Exception handling frame description pointer formats, as described
// by the Linux Standard Base Core Specification 4.0, section 11.5,
// DWARF Extensions.
enum DwarfPointerEncoding
{
DW_EH_PE_absptr = 0x00,
DW_EH_PE_omit = 0xff,
DW_EH_PE_uleb128 = 0x01,
DW_EH_PE_udata2 = 0x02,
DW_EH_PE_udata4 = 0x03,
DW_EH_PE_udata8 = 0x04,
DW_EH_PE_sleb128 = 0x09,
DW_EH_PE_sdata2 = 0x0A,
DW_EH_PE_sdata4 = 0x0B,
DW_EH_PE_sdata8 = 0x0C,
DW_EH_PE_pcrel = 0x10,
DW_EH_PE_textrel = 0x20,
DW_EH_PE_datarel = 0x30,
DW_EH_PE_funcrel = 0x40,
DW_EH_PE_aligned = 0x50,
// The GNU toolchain sources define this enum value as well,
// simply to help classify the lower nybble values into signed and
// unsigned groups.
DW_EH_PE_signed = 0x08,
// This is not documented in LSB 4.0, but it is used in both the
// Linux and OS X toolchains. It can be added to any other
// encoding (except DW_EH_PE_aligned), and indicates that the
// encoded value represents the address at which the true address
// is stored, not the true address itself.
DW_EH_PE_indirect = 0x80
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_DWARF2ENUMS_H__

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,484 @@
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit
#include <stdlib.h>
#include <iostream>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/dwarf/bytereader-inl.h"
#include "common/dwarf/dwarf2reader_test_common.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
#include "google_breakpad/common/breakpad_types.h"
using google_breakpad::test_assembler::Endianness;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using google_breakpad::test_assembler::kBigEndian;
using google_breakpad::test_assembler::kLittleEndian;
using dwarf2reader::ByteReader;
using dwarf2reader::CompilationUnit;
using dwarf2reader::Dwarf2Handler;
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfHasChild;
using dwarf2reader::DwarfTag;
using dwarf2reader::ENDIANNESS_BIG;
using dwarf2reader::ENDIANNESS_LITTLE;
using dwarf2reader::SectionMap;
using std::vector;
using testing::InSequence;
using testing::Pointee;
using testing::Return;
using testing::Sequence;
using testing::Test;
using testing::TestWithParam;
using testing::_;
class MockDwarf2Handler: public Dwarf2Handler {
public:
MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version));
MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag));
MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
uint64 data));
MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
int64 data));
MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data));
MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const char* data,
uint64 len));
MOCK_METHOD4(ProcessAttributeString, void(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data));
MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset,
DwarfAttribute attr,
enum DwarfForm form,
uint64 signature));
MOCK_METHOD1(EndDIE, void(uint64 offset));
};
struct DIEFixture {
DIEFixture() {
// Fix the initial offset of the .debug_info and .debug_abbrev sections.
info.start() = 0;
abbrevs.start() = 0;
// Default expectations for the data handler.
EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0);
EXPECT_CALL(handler, StartDIE(_, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0);
EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0);
EXPECT_CALL(handler, EndDIE(_)).Times(0);
}
// Return a reference to a section map whose .debug_info section refers
// to |info|, and whose .debug_abbrev section refers to |abbrevs|. This
// function returns a reference to the same SectionMap each time; new
// calls wipe out maps established by earlier calls.
const SectionMap &MakeSectionMap() {
// Copy the sections' contents into strings that will live as long as
// the map itself.
assert(info.GetContents(&info_contents));
assert(abbrevs.GetContents(&abbrevs_contents));
section_map.clear();
section_map[".debug_info"].first = info_contents.data();
section_map[".debug_info"].second = info_contents.size();
section_map[".debug_abbrev"].first = abbrevs_contents.data();
section_map[".debug_abbrev"].second = abbrevs_contents.size();
return section_map;
}
TestCompilationUnit info;
TestAbbrevTable abbrevs;
MockDwarf2Handler handler;
string abbrevs_contents, info_contents;
SectionMap section_map;
};
struct DwarfHeaderParams {
DwarfHeaderParams(Endianness endianness, size_t format_size,
int version, size_t address_size)
: endianness(endianness), format_size(format_size),
version(version), address_size(address_size) { }
Endianness endianness;
size_t format_size; // 4-byte or 8-byte DWARF offsets
int version;
size_t address_size;
};
class DwarfHeader: public DIEFixture,
public TestWithParam<DwarfHeaderParams> { };
TEST_P(DwarfHeader, Header) {
Label abbrev_table = abbrevs.Here();
abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit,
dwarf2reader::DW_children_yes)
.Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string)
.EndAbbrev()
.EndTable();
info.set_format_size(GetParam().format_size);
info.set_endianness(GetParam().endianness);
info.Header(GetParam().version, abbrev_table, GetParam().address_size)
.ULEB128(1) // DW_TAG_compile_unit, with children
.AppendCString("sam") // DW_AT_name, DW_FORM_string
.D8(0); // end of children
info.Finish();
{
InSequence s;
EXPECT_CALL(handler,
StartCompilationUnit(0, GetParam().address_size,
GetParam().format_size, _,
GetParam().version))
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit))
.WillOnce(Return(true));
EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,
dwarf2reader::DW_FORM_string,
"sam"))
.WillOnce(Return());
EXPECT_CALL(handler, EndDIE(_))
.WillOnce(Return());
}
ByteReader byte_reader(GetParam().endianness == kLittleEndian ?
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler);
EXPECT_EQ(parser.Start(), info_contents.size());
}
INSTANTIATE_TEST_CASE_P(
HeaderVariants, DwarfHeader,
::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
DwarfHeaderParams(kLittleEndian, 4, 2, 8),
DwarfHeaderParams(kLittleEndian, 4, 3, 4),
DwarfHeaderParams(kLittleEndian, 4, 3, 8),
DwarfHeaderParams(kLittleEndian, 4, 4, 4),
DwarfHeaderParams(kLittleEndian, 4, 4, 8),
DwarfHeaderParams(kLittleEndian, 8, 2, 4),
DwarfHeaderParams(kLittleEndian, 8, 2, 8),
DwarfHeaderParams(kLittleEndian, 8, 3, 4),
DwarfHeaderParams(kLittleEndian, 8, 3, 8),
DwarfHeaderParams(kLittleEndian, 8, 4, 4),
DwarfHeaderParams(kLittleEndian, 8, 4, 8),
DwarfHeaderParams(kBigEndian, 4, 2, 4),
DwarfHeaderParams(kBigEndian, 4, 2, 8),
DwarfHeaderParams(kBigEndian, 4, 3, 4),
DwarfHeaderParams(kBigEndian, 4, 3, 8),
DwarfHeaderParams(kBigEndian, 4, 4, 4),
DwarfHeaderParams(kBigEndian, 4, 4, 8),
DwarfHeaderParams(kBigEndian, 8, 2, 4),
DwarfHeaderParams(kBigEndian, 8, 2, 8),
DwarfHeaderParams(kBigEndian, 8, 3, 4),
DwarfHeaderParams(kBigEndian, 8, 3, 8),
DwarfHeaderParams(kBigEndian, 8, 4, 4),
DwarfHeaderParams(kBigEndian, 8, 4, 8)));
struct DwarfFormsFixture: public DIEFixture {
// Start a compilation unit, as directed by |params|, containing one
// childless DIE of the given tag, with one attribute of the given name
// and form. The 'info' fixture member is left just after the abbrev
// code, waiting for the attribute value to be appended.
void StartSingleAttributeDIE(const DwarfHeaderParams &params,
DwarfTag tag, DwarfAttribute name,
DwarfForm form) {
// Create the abbreviation table.
Label abbrev_table = abbrevs.Here();
abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no)
.Attribute(name, form)
.EndAbbrev()
.EndTable();
// Create the compilation unit, up to the attribute value.
info.set_format_size(params.format_size);
info.set_endianness(params.endianness);
info.Header(params.version, abbrev_table, params.address_size)
.ULEB128(1); // abbrev code
}
// Set up handler to expect a compilation unit matching |params|,
// containing one childless DIE of the given tag, in the sequence s. Stop
// just before the expectations.
void ExpectBeginCompilationUnit(const DwarfHeaderParams &params,
DwarfTag tag, uint64 offset=0) {
EXPECT_CALL(handler,
StartCompilationUnit(offset, params.address_size,
params.format_size, _,
params.version))
.InSequence(s)
.WillOnce(Return(true));
EXPECT_CALL(handler, StartDIE(_, tag))
.InSequence(s)
.WillOnce(Return(true));
}
void ExpectEndCompilationUnit() {
EXPECT_CALL(handler, EndDIE(_))
.InSequence(s)
.WillOnce(Return());
}
void ParseCompilationUnit(const DwarfHeaderParams &params, uint64 offset=0) {
ByteReader byte_reader(params.endianness == kLittleEndian ?
ENDIANNESS_LITTLE : ENDIANNESS_BIG);
CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler);
EXPECT_EQ(offset + parser.Start(), info_contents.size());
}
// The sequence to which the fixture's methods append expectations.
Sequence s;
};
struct DwarfForms: public DwarfFormsFixture,
public TestWithParam<DwarfHeaderParams> { };
TEST_P(DwarfForms, addr) {
StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit,
dwarf2reader::DW_AT_low_pc,
dwarf2reader::DW_FORM_addr);
uint64_t value;
if (GetParam().address_size == 4) {
value = 0xc8e9ffcc;
info.D32(value);
} else {
value = 0xe942517fc2768564ULL;
info.D64(value);
}
info.Finish();
ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc,
dwarf2reader::DW_FORM_addr,
value))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, block2_empty) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
(DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2);
info.D16(0);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2,
_, 0))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, block2) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7,
(DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2);
unsigned char data[258];
memset(data, '*', sizeof(data));
info.D16(sizeof(data))
.Append(data, sizeof(data));
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463,
dwarf2reader::DW_FORM_block2,
Pointee('*'), 258))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, flag_present) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2,
(DwarfAttribute) 0x359d1972,
dwarf2reader::DW_FORM_flag_present);
// DW_FORM_flag_present occupies no space in the DIE.
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2);
EXPECT_CALL(handler,
ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972,
dwarf2reader::DW_FORM_flag_present,
1))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, sec_offset) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689,
(DwarfAttribute) 0xa060bfd1,
dwarf2reader::DW_FORM_sec_offset);
uint64_t value;
if (GetParam().format_size == 4) {
value = 0xacc9c388;
info.D32(value);
} else {
value = 0xcffe5696ffe3ed0aULL;
info.D64(value);
}
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689);
EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1,
dwarf2reader::DW_FORM_sec_offset,
value))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, exprloc) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb,
(DwarfAttribute) 0xba3ae5cb,
dwarf2reader::DW_FORM_exprloc);
info.ULEB128(29)
.Append(29, 173);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb);
EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb,
dwarf2reader::DW_FORM_exprloc,
Pointee(173), 29))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
TEST_P(DwarfForms, ref_sig8) {
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
(DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8);
info.D64(0xf72fa0cb6ddcf9d6ULL);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b);
EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8,
0xf72fa0cb6ddcf9d6ULL))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam());
}
// A value passed to ProcessAttributeSignature is just an absolute number,
// not an offset within the compilation unit as most of the other
// DW_FORM_ref forms are. Check that the reader doesn't try to apply any
// offset to the signature, by reading it from a compilation unit that does
// not start at the beginning of the section.
TEST_P(DwarfForms, ref_sig8_not_first) {
info.Append(98, '*');
StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b,
(DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8);
info.D64(0xf72fa0cb6ddcf9d6ULL);
info.Finish();
ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98);
EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908,
dwarf2reader::DW_FORM_ref_sig8,
0xf72fa0cb6ddcf9d6ULL))
.InSequence(s)
.WillOnce(Return());
ExpectEndCompilationUnit();
ParseCompilationUnit(GetParam(), 98);
}
// Tests for the other attribute forms could go here.
INSTANTIATE_TEST_CASE_P(
HeaderVariants, DwarfForms,
::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4),
DwarfHeaderParams(kLittleEndian, 4, 2, 8),
DwarfHeaderParams(kLittleEndian, 4, 3, 4),
DwarfHeaderParams(kLittleEndian, 4, 3, 8),
DwarfHeaderParams(kLittleEndian, 4, 4, 4),
DwarfHeaderParams(kLittleEndian, 4, 4, 8),
DwarfHeaderParams(kLittleEndian, 8, 2, 4),
DwarfHeaderParams(kLittleEndian, 8, 2, 8),
DwarfHeaderParams(kLittleEndian, 8, 3, 4),
DwarfHeaderParams(kLittleEndian, 8, 3, 8),
DwarfHeaderParams(kLittleEndian, 8, 4, 4),
DwarfHeaderParams(kLittleEndian, 8, 4, 8),
DwarfHeaderParams(kBigEndian, 4, 2, 4),
DwarfHeaderParams(kBigEndian, 4, 2, 8),
DwarfHeaderParams(kBigEndian, 4, 3, 4),
DwarfHeaderParams(kBigEndian, 4, 3, 8),
DwarfHeaderParams(kBigEndian, 4, 4, 4),
DwarfHeaderParams(kBigEndian, 4, 4, 8),
DwarfHeaderParams(kBigEndian, 8, 2, 4),
DwarfHeaderParams(kBigEndian, 8, 2, 8),
DwarfHeaderParams(kBigEndian, 8, 3, 4),
DwarfHeaderParams(kBigEndian, 8, 3, 8),
DwarfHeaderParams(kBigEndian, 8, 4, 4),
DwarfHeaderParams(kBigEndian, 8, 4, 8)));

View File

@ -0,0 +1,149 @@
// -*- mode: c++ -*-
// Copyright (c) 2012, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf2reader_test_common.h: Define TestCompilationUnit and
// TestAbbrevTable, classes for creating properly (and improperly)
// formatted DWARF compilation unit data for unit tests.
#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
#include "common/test_assembler.h"
#include "common/dwarf/dwarf2enums.h"
// A subclass of test_assembler::Section, specialized for constructing
// DWARF compilation units.
class TestCompilationUnit: public google_breakpad::test_assembler::Section {
public:
typedef dwarf2reader::DwarfTag DwarfTag;
typedef dwarf2reader::DwarfAttribute DwarfAttribute;
typedef dwarf2reader::DwarfForm DwarfForm;
typedef google_breakpad::test_assembler::Label Label;
// Set the section's DWARF format size (the 32-bit DWARF format or the
// 64-bit DWARF format, for lengths and section offsets --- not the
// address size) to format_size.
void set_format_size(size_t format_size) {
assert(format_size == 4 || format_size == 8);
format_size_ = format_size;
}
// Append a DWARF section offset value, of the appropriate size for this
// compilation unit.
template<typename T>
void SectionOffset(T offset) {
if (format_size_ == 4)
D32(offset);
else
D64(offset);
}
// Append a DWARF compilation unit header to the section, with the given
// DWARF version, abbrev table offset, and address size.
TestCompilationUnit &Header(int version, const Label &abbrev_offset,
size_t address_size) {
if (format_size_ == 4) {
D32(length_);
} else {
D32(0xffffffff);
D64(length_);
}
post_length_offset_ = Size();
D16(version);
SectionOffset(abbrev_offset);
D8(address_size);
return *this;
}
// Mark the end of this header's DIEs.
TestCompilationUnit &Finish() {
length_ = Size() - post_length_offset_;
return *this;
}
private:
// The DWARF format size for this compilation unit.
size_t format_size_;
// The offset of the point in the compilation unit header immediately
// after the initial length field.
uint64_t post_length_offset_;
// The length of the compilation unit, not including the initial length field.
Label length_;
};
// A subclass of test_assembler::Section specialized for constructing DWARF
// abbreviation tables.
class TestAbbrevTable: public google_breakpad::test_assembler::Section {
public:
typedef dwarf2reader::DwarfTag DwarfTag;
typedef dwarf2reader::DwarfAttribute DwarfAttribute;
typedef dwarf2reader::DwarfForm DwarfForm;
typedef dwarf2reader::DwarfHasChild DwarfHasChild;
typedef google_breakpad::test_assembler::Label Label;
// Start a new abbreviation table entry for abbreviation code |code|,
// encoding a DIE whose tag is |tag|, and which has children if and only
// if |has_children| is true.
TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) {
assert(code != 0);
ULEB128(code);
ULEB128(static_cast<unsigned>(tag));
D8(static_cast<unsigned>(has_children));
return *this;
};
// Add an attribute to the current abbreviation code whose name is |name|
// and whose form is |form|.
TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) {
ULEB128(static_cast<unsigned>(name));
ULEB128(static_cast<unsigned>(form));
return *this;
}
// Finish the current abbreviation code.
TestAbbrevTable &EndAbbrev() {
ULEB128(0);
ULEB128(0);
return *this;
}
// Finish the current abbreviation table.
TestAbbrevTable &EndTable() {
ULEB128(0);
return *this;
}
};
#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__

View File

@ -0,0 +1,231 @@
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This is a client for the dwarf2reader to extract function and line
// information from the debug info.
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <map>
#include <queue>
#include <vector>
#include "common/dwarf/functioninfo.h"
#include "common/dwarf/bytereader.h"
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
using google_breakpad::scoped_ptr;
namespace dwarf2reader {
CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<string>* dirs,
LineMap* linemap):linemap_(linemap),
files_(files),
dirs_(dirs) {
// The dirs and files are 1 indexed, so just make sure we put
// nothing in the 0 vector.
assert(dirs->size() == 0);
assert(files->size() == 0);
dirs->push_back("");
SourceFileInfo s;
s.name = "";
s.lowpc = ULLONG_MAX;
files->push_back(s);
}
void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) {
// These should never come out of order, actually
assert(dir_num == dirs_->size());
dirs_->push_back(name);
}
void CULineInfoHandler::DefineFile(const string& name,
int32 file_num, uint32 dir_num,
uint64 mod_time, uint64 length) {
assert(dir_num >= 0);
assert(dir_num < dirs_->size());
// These should never come out of order, actually.
if (file_num == (int32)files_->size() || file_num == -1) {
string dir = dirs_->at(dir_num);
SourceFileInfo s;
s.lowpc = ULLONG_MAX;
if (dir == "") {
s.name = name;
} else {
s.name = dir + "/" + name;
}
files_->push_back(s);
} else {
fprintf(stderr, "error in DefineFile");
}
}
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
uint32 line_num, uint32 column_num) {
if (file_num < files_->size()) {
linemap_->insert(
std::make_pair(address,
std::make_pair(files_->at(file_num).name.c_str(),
line_num)));
if (address < files_->at(file_num).lowpc) {
files_->at(file_num).lowpc = address;
}
} else {
fprintf(stderr, "error in AddLine");
}
}
bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset,
uint8 address_size,
uint8 offset_size,
uint64 cu_length,
uint8 dwarf_version) {
current_compilation_unit_offset_ = offset;
return true;
}
// For function info, we only care about subprograms and inlined
// subroutines. For line info, the DW_AT_stmt_list lives in the
// compile unit tag.
bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) {
switch (tag) {
case DW_TAG_subprogram:
case DW_TAG_inlined_subroutine: {
current_function_info_ = new FunctionInfo;
current_function_info_->lowpc = current_function_info_->highpc = 0;
current_function_info_->name = "";
current_function_info_->line = 0;
current_function_info_->file = "";
offset_to_funcinfo_->insert(std::make_pair(offset,
current_function_info_));
};
// FALLTHROUGH
case DW_TAG_compile_unit:
return true;
default:
return false;
}
return false;
}
// Only care about the name attribute for functions
void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string &data) {
if (current_function_info_) {
if (attr == DW_AT_name)
current_function_info_->name = data;
else if (attr == DW_AT_MIPS_linkage_name)
current_function_info_->mangled_name = data;
}
}
void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
if (attr == DW_AT_stmt_list) {
SectionMap::const_iterator iter = sections_.find("__debug_line");
assert(iter != sections_.end());
scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data,
iter->second.second - data,
reader_, linehandler_));
lireader->Start();
} else if (current_function_info_) {
switch (attr) {
case DW_AT_low_pc:
current_function_info_->lowpc = data;
break;
case DW_AT_high_pc:
current_function_info_->highpc = data;
break;
case DW_AT_decl_line:
current_function_info_->line = data;
break;
case DW_AT_decl_file:
current_function_info_->file = files_->at(data).name;
break;
default:
break;
}
}
}
void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data) {
if (current_function_info_) {
switch (attr) {
case DW_AT_specification: {
// Some functions have a "specification" attribute
// which means they were defined elsewhere. The name
// attribute is not repeated, and must be taken from
// the specification DIE. Here we'll assume that
// any DIE referenced in this manner will already have
// been seen, but that's not really required by the spec.
FunctionMap::iterator iter = offset_to_funcinfo_->find(data);
if (iter != offset_to_funcinfo_->end()) {
current_function_info_->name = iter->second->name;
current_function_info_->mangled_name = iter->second->mangled_name;
} else {
// If you hit this, this code probably needs to be rewritten.
fprintf(stderr,
"Error: DW_AT_specification was seen before the referenced "
"DIE! (Looking for DIE at offset %08llx, in DIE at "
"offset %08llx)\n", data, offset);
}
break;
}
default:
break;
}
}
}
void CUFunctionInfoHandler::EndDIE(uint64 offset) {
if (current_function_info_ && current_function_info_->lowpc)
address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc,
current_function_info_));
}
} // namespace dwarf2reader

View File

@ -0,0 +1,188 @@
// Copyright (c) 2010 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file contains the definitions for a DWARF2/3 information
// collector that uses the DWARF2/3 reader interface to build a mapping
// of addresses to files, lines, and functions.
#ifndef COMMON_DWARF_FUNCTIONINFO_H__
#define COMMON_DWARF_FUNCTIONINFO_H__
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace dwarf2reader {
struct FunctionInfo {
// Name of the function
string name;
// Mangled name of the function
string mangled_name;
// File containing this function
string file;
// Line number for start of function.
uint32 line;
// Beginning address for this function
uint64 lowpc;
// End address for this function.
uint64 highpc;
};
struct SourceFileInfo {
// Name of the source file name
string name;
// Low address of source file name
uint64 lowpc;
};
typedef std::map<uint64, FunctionInfo*> FunctionMap;
typedef std::map<uint64, std::pair<string, uint32> > LineMap;
// This class is a basic line info handler that fills in the dirs,
// file, and linemap passed into it with the data produced from the
// LineInfoHandler.
class CULineInfoHandler: public LineInfoHandler {
public:
//
CULineInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<string>* dirs,
LineMap* linemap);
virtual ~CULineInfoHandler() { }
// Called when we define a directory. We just place NAME into dirs_
// at position DIR_NUM.
virtual void DefineDir(const string& name, uint32 dir_num);
// Called when we define a filename. We just place
// concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM.
virtual void DefineFile(const string& name, int32 file_num,
uint32 dir_num, uint64 mod_time, uint64 length);
// Called when the line info reader has a new line, address pair
// ready for us. ADDRESS is the address of the code, LENGTH is the
// length of its machine code in bytes, FILE_NUM is the file number
// containing the code, LINE_NUM is the line number in that file for
// the code, and COLUMN_NUM is the column number the code starts at,
// if we know it (0 otherwise).
virtual void AddLine(uint64 address, uint64 length,
uint32 file_num, uint32 line_num, uint32 column_num);
private:
LineMap* linemap_;
std::vector<SourceFileInfo>* files_;
std::vector<string>* dirs_;
};
class CUFunctionInfoHandler: public Dwarf2Handler {
public:
CUFunctionInfoHandler(std::vector<SourceFileInfo>* files,
std::vector<string>* dirs,
LineMap* linemap,
FunctionMap* offset_to_funcinfo,
FunctionMap* address_to_funcinfo,
CULineInfoHandler* linehandler,
const SectionMap& sections,
ByteReader* reader)
: files_(files), dirs_(dirs), linemap_(linemap),
offset_to_funcinfo_(offset_to_funcinfo),
address_to_funcinfo_(address_to_funcinfo),
linehandler_(linehandler), sections_(sections),
reader_(reader), current_function_info_(NULL) { }
virtual ~CUFunctionInfoHandler() { }
// Start to process a compilation unit at OFFSET from the beginning of the
// .debug_info section. We want to see all compilation units, so we
// always return true.
virtual bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
// Start to process a DIE at OFFSET from the beginning of the
// .debug_info section. We only care about function related DIE's.
virtual bool StartDIE(uint64 offset, enum DwarfTag tag);
// 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, has a name of ATTR, a form of
// FORM, and the actual data of the attribute is in DATA.
virtual void ProcessAttributeUnsigned(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
// Called when we have an attribute with a DIE reference to give to
// our handler. The attribute is for the DIE at OFFSET from the
// beginning of the .debug_info section, has a name of ATTR, a form of
// FORM, and the offset of the referenced DIE from the start of the
// .debug_info section is in DATA.
virtual void ProcessAttributeReference(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
// 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, has a name of ATTR, a form of
// FORM, and the actual data of the attribute is in DATA.
virtual void ProcessAttributeString(uint64 offset,
enum DwarfAttribute attr,
enum DwarfForm form,
const string& data);
// 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);
private:
std::vector<SourceFileInfo>* files_;
std::vector<string>* dirs_;
LineMap* linemap_;
FunctionMap* offset_to_funcinfo_;
FunctionMap* address_to_funcinfo_;
CULineInfoHandler* linehandler_;
const SectionMap& sections_;
ByteReader* reader_;
FunctionInfo* current_function_info_;
uint64 current_compilation_unit_offset_;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_FUNCTIONINFO_H__

View File

@ -0,0 +1,61 @@
// Copyright 2008 Google Inc. All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef COMMON_DWARF_LINE_STATE_MACHINE_H__
#define COMMON_DWARF_LINE_STATE_MACHINE_H__
namespace dwarf2reader {
// This is the format of a DWARF2/3 line state machine that we process
// opcodes using. There is no need for anything outside the lineinfo
// processor to know how this works.
struct LineStateMachine {
void Reset(bool default_is_stmt) {
file_num = 1;
address = 0;
line_num = 1;
column_num = 0;
is_stmt = default_is_stmt;
basic_block = false;
end_sequence = false;
}
uint32 file_num;
uint64 address;
uint32 line_num;
uint32 column_num;
bool is_stmt; // stmt means statement.
bool basic_block;
bool end_sequence;
};
} // namespace dwarf2reader
#endif // COMMON_DWARF_LINE_STATE_MACHINE_H__

View File

@ -0,0 +1,55 @@
// Copyright 2008 Google, Inc. All Rights reserved
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// This file contains some typedefs for basic types
#ifndef _COMMON_DWARF_TYPES_H__
#define _COMMON_DWARF_TYPES_H__
#include <stdint.h>
typedef signed char int8;
typedef short int16;
typedef int int32;
typedef long long int64;
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
typedef unsigned long long uint64;
#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__

View File

@ -0,0 +1,258 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// Implementation of google_breakpad::DwarfCFIToModule.
// See dwarf_cfi_to_module.h for details.
#include <sstream>
#include "common/dwarf_cfi_to_module.h"
namespace google_breakpad {
using std::ostringstream;
vector<string> DwarfCFIToModule::RegisterNames::MakeVector(
const char * const *strings,
size_t size) {
vector<string> names(strings, strings + size);
return names;
}
vector<string> DwarfCFIToModule::RegisterNames::I386() {
static const char *const names[] = {
"$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi",
"$eip", "$eflags", "$unused1",
"$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
"$unused2", "$unused3",
"$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
"$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
"$fcw", "$fsw", "$mxcsr",
"$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5",
"$tr", "$ldtr"
};
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
vector<string> DwarfCFIToModule::RegisterNames::X86_64() {
static const char *const names[] = {
"$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp",
"$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15",
"$rip",
"$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7",
"$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15",
"$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7",
"$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7",
"$rflags",
"$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2",
"$fs.base", "$gs.base", "$unused3", "$unused4",
"$tr", "$ldtr",
"$mxcsr", "$fcw", "$fsw"
};
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
// Per ARM IHI 0040A, section 3.1
vector<string> DwarfCFIToModule::RegisterNames::ARM() {
static const char *const names[] = {
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"fps", "cpsr", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "",
"s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7",
"s8", "s9", "s10", "s11", "s12", "s13", "s14", "s15",
"s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23",
"s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"
};
return MakeVector(names, sizeof(names) / sizeof(names[0]));
}
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address) {
assert(!entry_);
// If dwarf2reader::CallFrameInfo can handle this version and
// augmentation, then we should be okay with that, so there's no
// need to check them here.
// Get ready to collect entries.
entry_ = new Module::StackFrameEntry;
entry_->address = address;
entry_->size = length;
entry_offset_ = offset;
return_address_ = return_address;
// Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
// may not establish any rule for .ra if the return address column
// is an ordinary register, and that register holds the return
// address on entry to the function. So establish an initial .ra
// rule citing the return address register.
if (return_address_ < register_names_.size())
entry_->initial_rules[ra_name_] = register_names_[return_address_];
return true;
}
string DwarfCFIToModule::RegisterName(int i) {
assert(entry_);
if (i < 0) {
assert(i == kCFARegister);
return cfa_name_;
}
unsigned reg = i;
if (reg == return_address_)
return ra_name_;
// Ensure that a non-empty name exists for this register value.
if (reg < register_names_.size() && !register_names_[reg].empty())
return register_names_[reg];
reporter_->UnnamedRegister(entry_offset_, reg);
char buf[30];
sprintf(buf, "unnamed_register%u", reg);
return buf;
}
void DwarfCFIToModule::Record(Module::Address address, int reg,
const string &rule) {
assert(entry_);
// Place the name in our global set of strings, and then use the string
// from the set. Even though the assignment looks like a copy, all the
// major std::string implementations use reference counting internally,
// so the effect is to have all our data structures share copies of rules
// whenever possible. Since register names are drawn from a
// vector<string>, register names are already shared.
string shared_rule = *common_strings_.insert(rule).first;
// Is this one of this entry's initial rules?
if (address == entry_->address)
entry_->initial_rules[RegisterName(reg)] = shared_rule;
// File it under the appropriate address.
else
entry_->rule_changes[address][RegisterName(reg)] = shared_rule;
}
bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) {
reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg));
// Treat this as a non-fatal error.
return true;
}
bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) {
ostringstream s;
s << RegisterName(reg);
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::OffsetRule(uint64 address, int reg,
int base_register, long offset) {
ostringstream s;
s << RegisterName(base_register) << " " << offset << " + ^";
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg,
int base_register, long offset) {
ostringstream s;
s << RegisterName(base_register) << " " << offset << " +";
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::RegisterRule(uint64 address, int reg,
int base_register) {
ostringstream s;
s << RegisterName(base_register);
Record(address, reg, s.str());
return true;
}
bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg,
const string &expression) {
reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
// Treat this as a non-fatal error.
return true;
}
bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg,
const string &expression) {
reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg));
// Treat this as a non-fatal error.
return true;
}
bool DwarfCFIToModule::End() {
module_->AddStackFrameEntry(entry_);
entry_ = NULL;
return true;
}
void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) {
fprintf(stderr, "%s, section '%s': "
"the call frame entry at offset 0x%zx refers to register %d,"
" whose name we don't know\n",
file_.c_str(), section_.c_str(), offset, reg);
}
void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset,
const string &reg) {
fprintf(stderr, "%s, section '%s': "
"the call frame entry at offset 0x%zx sets the rule for "
"register '%s' to 'undefined', but the Breakpad symbol file format"
" cannot express this\n",
file_.c_str(), section_.c_str(), offset, reg.c_str());
}
void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset,
const string &reg) {
fprintf(stderr, "%s, section '%s': "
"the call frame entry at offset 0x%zx uses a DWARF expression to"
" describe how to recover register '%s', "
" but this translator cannot yet translate DWARF expressions to"
" Breakpad postfix expressions\n",
file_.c_str(), section_.c_str(), offset, reg.c_str());
}
} // namespace google_breakpad

View File

@ -0,0 +1,196 @@
// -*- mode: c++ -*-
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which
// accepts parsed DWARF call frame info and adds it to a
// google_breakpad::Module object, which can write that information to
// a Breakpad symbol file.
#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H
#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H
#include <assert.h>
#include <stdio.h>
#include <set>
#include <string>
#include <vector>
#include "common/module.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace google_breakpad {
using dwarf2reader::CallFrameInfo;
using google_breakpad::Module;
using std::set;
using std::vector;
// A class that accepts parsed call frame information from the DWARF
// CFI parser and populates a google_breakpad::Module object with the
// contents.
class DwarfCFIToModule: public CallFrameInfo::Handler {
public:
// DwarfCFIToModule uses an instance of this class to report errors
// detected while converting DWARF CFI to Breakpad STACK CFI records.
class Reporter {
public:
// Create a reporter that writes messages to the standard error
// stream. FILE is the name of the file we're processing, and
// SECTION is the name of the section within that file that we're
// looking at (.debug_frame, .eh_frame, etc.).
Reporter(const string &file, const string &section)
: file_(file), section_(section) { }
virtual ~Reporter() { }
// The DWARF CFI entry at OFFSET cites register REG, but REG is not
// covered by the vector of register names passed to the
// DwarfCFIToModule constructor, nor does it match the return
// address column number for this entry.
virtual void UnnamedRegister(size_t offset, int reg);
// The DWARF CFI entry at OFFSET says that REG is undefined, but the
// Breakpad symbol file format cannot express this.
virtual void UndefinedNotSupported(size_t offset, const string &reg);
// The DWARF CFI entry at OFFSET says that REG uses a DWARF
// expression to find its value, but DwarfCFIToModule is not
// capable of translating DWARF expressions to Breakpad postfix
// expressions.
virtual void ExpressionsNotSupported(size_t offset, const string &reg);
protected:
string file_, section_;
};
// Register name tables. If TABLE is a vector returned by one of these
// functions, then TABLE[R] is the name of the register numbered R in
// DWARF call frame information.
class RegisterNames {
public:
// Intel's "x86" or IA-32.
static vector<string> I386();
// AMD x86_64, AMD64, Intel EM64T, or Intel 64
static vector<string> X86_64();
// ARM.
static vector<string> ARM();
private:
// Given STRINGS, an array of C strings with SIZE elements, return an
// equivalent vector<string>.
static vector<string> MakeVector(const char * const *strings, size_t size);
};
// Create a handler for the dwarf2reader::CallFrameInfo parser that
// records the stack unwinding information it receives in MODULE.
//
// Use REGISTER_NAMES[I] as the name of register number I; *this
// keeps a reference to the vector, so the vector should remain
// alive for as long as the DwarfCFIToModule does.
//
// Use REPORTER for reporting problems encountered in the conversion
// process.
DwarfCFIToModule(Module *module, const vector<string> &register_names,
Reporter *reporter)
: module_(module), register_names_(register_names), reporter_(reporter),
entry_(NULL), return_address_(-1), cfa_name_(".cfa"), ra_name_(".ra") {
}
virtual ~DwarfCFIToModule() { delete entry_; }
virtual bool Entry(size_t offset, uint64 address, uint64 length,
uint8 version, const string &augmentation,
unsigned return_address);
virtual bool UndefinedRule(uint64 address, int reg);
virtual bool SameValueRule(uint64 address, int reg);
virtual bool OffsetRule(uint64 address, int reg,
int base_register, long offset);
virtual bool ValOffsetRule(uint64 address, int reg,
int base_register, long offset);
virtual bool RegisterRule(uint64 address, int reg, int base_register);
virtual bool ExpressionRule(uint64 address, int reg,
const string &expression);
virtual bool ValExpressionRule(uint64 address, int reg,
const string &expression);
virtual bool End();
private:
// Return the name to use for register REG.
string RegisterName(int i);
// Record RULE for register REG at ADDRESS.
void Record(Module::Address address, int reg, const string &rule);
// The module to which we should add entries.
Module *module_;
// Map from register numbers to register names.
const vector<string> &register_names_;
// The reporter to use to report problems.
Reporter *reporter_;
// The current entry we're constructing.
Module::StackFrameEntry *entry_;
// The section offset of the current frame description entry, for
// use in error messages.
size_t entry_offset_;
// The return address column for that entry.
unsigned return_address_;
// The names of the return address and canonical frame address. Putting
// these here instead of using string literals allows us to share their
// texts in reference-counted std::string implementations (all the
// popular ones). Many, many rules cite these strings.
string cfa_name_, ra_name_;
// A set of strings used by this CFI. Before storing a string in one of
// our data structures, insert it into this set, and then use the string
// from the set.
//
// Because std::string uses reference counting internally, simply using
// strings from this set, even if passed by value, assigned, or held
// directly in structures and containers (map<string, ...>, for example),
// causes those strings to share a single instance of each distinct piece
// of text.
set<string> common_strings_;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H

View File

@ -0,0 +1,306 @@
// Copyright (c) 2010, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/dwarf_cfi_to_module.h"
#include "common/using_std_string.h"
using std::vector;
using google_breakpad::Module;
using google_breakpad::DwarfCFIToModule;
using testing::ContainerEq;
using testing::Test;
using testing::_;
struct MockCFIReporter: public DwarfCFIToModule::Reporter {
MockCFIReporter(const string &file, const string &section)
: Reporter(file, section) { }
MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg));
MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string &reg));
MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string &reg));
};
struct DwarfCFIToModuleFixture {
DwarfCFIToModuleFixture()
: module("module name", "module os", "module arch", "module id"),
reporter("reporter file", "reporter section"),
handler(&module, register_names, &reporter) {
register_names.push_back("reg0");
register_names.push_back("reg1");
register_names.push_back("reg2");
register_names.push_back("reg3");
register_names.push_back("reg4");
register_names.push_back("reg5");
register_names.push_back("reg6");
register_names.push_back("reg7");
register_names.push_back("sp");
register_names.push_back("pc");
register_names.push_back("");
EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0);
EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0);
EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0);
}
Module module;
vector<string> register_names;
MockCFIReporter reporter;
DwarfCFIToModule handler;
vector<Module::StackFrameEntry *> entries;
};
class Entry: public DwarfCFIToModuleFixture, public Test { };
TEST_F(Entry, Accept) {
ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL,
0xb440ce248169c8d6ULL, 3, "", 0xea93c106));
ASSERT_TRUE(handler.End());
module.GetStackFrameEntries(&entries);
EXPECT_EQ(1U, entries.size());
EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address);
EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size);
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Entry, AcceptOldVersion) {
ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL,
0xc771f44958d40bbcULL, 1, "", 0x093c945e));
ASSERT_TRUE(handler.End());
module.GetStackFrameEntries(&entries);
EXPECT_EQ(1U, entries.size());
EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address);
EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size);
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
struct RuleFixture: public DwarfCFIToModuleFixture {
RuleFixture() : DwarfCFIToModuleFixture() {
entry_address = 0x89327ebf86b47492ULL;
entry_size = 0x2f8cd573072fe02aULL;
return_reg = 0x7886a346;
}
void StartEntry() {
ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size,
3, "", return_reg));
}
void CheckEntry() {
module.GetStackFrameEntries(&entries);
EXPECT_EQ(1U, entries.size());
EXPECT_EQ(entry_address, entries[0]->address);
EXPECT_EQ(entry_size, entries[0]->size);
}
uint64 entry_address, entry_size;
unsigned return_reg;
};
class Rule: public RuleFixture, public Test { };
TEST_F(Rule, UndefinedRule) {
EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7"));
StartEntry();
ASSERT_TRUE(handler.UndefinedRule(entry_address, 7));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, RegisterWithEmptyName) {
EXPECT_CALL(reporter, UnnamedRegister(_, 10));
EXPECT_CALL(reporter, UndefinedNotSupported(_, "unnamed_register10"));
StartEntry();
ASSERT_TRUE(handler.UndefinedRule(entry_address, 10));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, SameValueRule) {
StartEntry();
ASSERT_TRUE(handler.SameValueRule(entry_address, 6));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial["reg6"] = "reg6";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, OffsetRule) {
StartEntry();
ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg,
DwarfCFIToModule::kCFARegister,
16927065));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST_F(Rule, OffsetRuleNegative) {
StartEntry();
ASSERT_TRUE(handler.OffsetRule(entry_address + 1,
DwarfCFIToModule::kCFARegister, 4, -34530721));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST_F(Rule, ValOffsetRule) {
// Use an unnamed register number, to exercise that branch of RegisterName.
EXPECT_CALL(reporter, UnnamedRegister(_, 11));
StartEntry();
ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
DwarfCFIToModule::kCFARegister,
11, 61812979));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 0x5ab7][".cfa"] =
"unnamed_register11 61812979 +";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST_F(Rule, RegisterRule) {
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg3";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, ExpressionRule) {
EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2"));
StartEntry();
ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2,
"it takes two to tango"));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, ValExpressionRule) {
EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0"));
StartEntry();
ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0,
"bit off more than he could chew"));
ASSERT_TRUE(handler.End());
CheckEntry();
EXPECT_EQ(0U, entries[0]->initial_rules.size());
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, DefaultReturnAddressRule) {
return_reg = 2;
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg2";
expected_initial["reg0"] = "reg1";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, DefaultReturnAddressRuleOverride) {
return_reg = 2;
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg1";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
EXPECT_EQ(0U, entries[0]->rule_changes.size());
}
TEST_F(Rule, DefaultReturnAddressRuleLater) {
return_reg = 2;
StartEntry();
ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1));
ASSERT_TRUE(handler.End());
CheckEntry();
Module::RuleMap expected_initial;
expected_initial[".ra"] = "reg2";
EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial));
Module::RuleChangeMap expected_changes;
expected_changes[entry_address + 1][".ra"] = "reg1";
EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes));
}
TEST(RegisterNames, I386) {
vector<string> names = DwarfCFIToModule::RegisterNames::I386();
EXPECT_EQ("$eax", names[0]);
EXPECT_EQ("$ecx", names[1]);
EXPECT_EQ("$esp", names[4]);
EXPECT_EQ("$eip", names[8]);
}
TEST(RegisterNames, ARM) {
vector<string> names = DwarfCFIToModule::RegisterNames::ARM();
EXPECT_EQ("r0", names[0]);
EXPECT_EQ("r10", names[10]);
EXPECT_EQ("sp", names[13]);
EXPECT_EQ("lr", names[14]);
EXPECT_EQ("pc", names[15]);
}
TEST(RegisterNames, X86_64) {
vector<string> names = DwarfCFIToModule::RegisterNames::X86_64();
EXPECT_EQ("$rax", names[0]);
EXPECT_EQ("$rdx", names[1]);
EXPECT_EQ("$rbp", names[6]);
EXPECT_EQ("$rsp", names[7]);
EXPECT_EQ("$rip", names[16]);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,315 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// Add DWARF debugging information to a Breakpad symbol file. This
// file defines the DwarfCUToModule class, which accepts parsed DWARF
// data and populates a google_breakpad::Module with the results; the
// Module can then write its contents as a Breakpad symbol file.
#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__
#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__
#include <string>
#include "common/language.h"
#include "common/module.h"
#include "common/dwarf/bytereader.h"
#include "common/dwarf/dwarf2diehandler.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/scoped_ptr.h"
#include "common/using_std_string.h"
namespace google_breakpad {
using dwarf2reader::DwarfAttribute;
using dwarf2reader::DwarfForm;
using dwarf2reader::DwarfLanguage;
using dwarf2reader::DwarfTag;
// Populate a google_breakpad::Module with DWARF debugging information.
//
// An instance of this class can be provided as a handler to a
// dwarf2reader::DIEDispatcher, which can in turn be a handler for a
// dwarf2reader::CompilationUnit DWARF parser. The handler uses the results
// of parsing to populate a google_breakpad::Module with source file,
// function, and source line information.
class DwarfCUToModule: public dwarf2reader::RootDIEHandler {
struct FilePrivate;
public:
// Information global to the DWARF-bearing file we are processing,
// for use by DwarfCUToModule. Each DwarfCUToModule instance deals
// with a single compilation unit within the file, but information
// global to the whole file is held here. The client is responsible
// for filling it in appropriately (except for the 'file_private'
// field, which the constructor and destructor take care of), and
// then providing it to the DwarfCUToModule instance for each
// compilation unit we process in that file. Set HANDLE_INTER_CU_REFS
// to true to handle debugging symbols with DW_FORM_ref_addr entries.
class FileContext {
public:
FileContext(const string &filename,
Module *module,
bool handle_inter_cu_refs);
~FileContext();
// Add CONTENTS of size LENGTH to the section map as NAME.
void AddSectionToSectionMap(const string& name,
const char* contents,
uint64 length);
// Clear the section map for testing.
void ClearSectionMapForTest();
const dwarf2reader::SectionMap& section_map() const;
private:
friend class DwarfCUToModule;
// Clears all the Specifications if HANDLE_INTER_CU_REFS_ is false.
void ClearSpecifications();
// Given an OFFSET and a CU that starts at COMPILATION_UNIT_START, returns
// true if this is an inter-compilation unit reference that is not being
// handled.
bool IsUnhandledInterCUReference(uint64 offset,
uint64 compilation_unit_start) const;
// The name of this file, for use in error messages.
const string filename_;
// A map of this file's sections, used for finding other DWARF
// sections that the .debug_info section may refer to.
dwarf2reader::SectionMap section_map_;
// The Module to which we're contributing definitions.
Module *module_;
// True if we are handling references between compilation units.
const bool handle_inter_cu_refs_;
// Inter-compilation unit data used internally by the handlers.
scoped_ptr<FilePrivate> file_private_;
};
// An abstract base class for handlers that handle DWARF line data
// for DwarfCUToModule. DwarfCUToModule could certainly just use
// dwarf2reader::LineInfo itself directly, but decoupling things
// this way makes unit testing a little easier.
class LineToModuleHandler {
public:
LineToModuleHandler() { }
virtual ~LineToModuleHandler() { }
// Called at the beginning of a new compilation unit, prior to calling
// ReadProgram(). compilation_dir will indicate the path that the
// current compilation unit was compiled in, consistent with the
// DW_AT_comp_dir DIE.
virtual void StartCompilationUnit(const string& compilation_dir) = 0;
// Populate MODULE and LINES with source file names and code/line
// 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 char *program, uint64 length,
Module *module, vector<Module::Line> *lines) = 0;
};
// The interface DwarfCUToModule uses to report warnings. The member
// function definitions for this class write messages to stderr, but
// you can override them if you'd like to detect or report these
// conditions yourself.
class WarningReporter {
public:
// Warn about problems in the DWARF file FILENAME, in the
// compilation unit at OFFSET.
WarningReporter(const string &filename, uint64 cu_offset)
: filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false),
printed_unpaired_header_(false),
uncovered_warnings_enabled_(false) { }
virtual ~WarningReporter() { }
// Set the name of the compilation unit we're processing to NAME.
virtual void SetCUName(const string &name) { cu_name_ = name; }
// Accessor and setter for uncovered_warnings_enabled_.
// UncoveredFunction and UncoveredLine only report a problem if that is
// true. By default, these warnings are disabled, because those
// conditions occur occasionally in healthy code.
virtual bool uncovered_warnings_enabled() const {
return uncovered_warnings_enabled_;
}
virtual void set_uncovered_warnings_enabled(bool value) {
uncovered_warnings_enabled_ = value;
}
// A DW_AT_specification in the DIE at OFFSET refers to a DIE we
// haven't processed yet, or that wasn't marked as a declaration,
// at TARGET.
virtual void UnknownSpecification(uint64 offset, uint64 target);
// A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we
// haven't processed yet, or that wasn't marked as inline, at TARGET.
virtual void UnknownAbstractOrigin(uint64 offset, uint64 target);
// We were unable to find the DWARF section named SECTION_NAME.
virtual void MissingSection(const string &section_name);
// The CU's DW_AT_stmt_list offset OFFSET is bogus.
virtual void BadLineInfoOffset(uint64 offset);
// FUNCTION includes code covered by no line number data.
virtual void UncoveredFunction(const Module::Function &function);
// Line number NUMBER in LINE_FILE, of length LENGTH, includes code
// covered by no function.
virtual void UncoveredLine(const Module::Line &line);
// The DW_TAG_subprogram DIE at OFFSET has no name specified directly
// in the DIE, nor via a DW_AT_specification or DW_AT_abstract_origin
// link.
virtual void UnnamedFunction(uint64 offset);
// The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
// FilePrivate did not retain the inter-CU specification data.
virtual void UnhandledInterCUReference(uint64 offset, uint64 target);
uint64 cu_offset() const {
return cu_offset_;
}
protected:
const string filename_;
const uint64 cu_offset_;
string cu_name_;
bool printed_cu_header_;
bool printed_unpaired_header_;
bool uncovered_warnings_enabled_;
private:
// Print a per-CU heading, once.
void CUHeading();
// Print an unpaired function/line heading, once.
void UncoveredHeading();
};
// Create a DWARF debugging info handler for a compilation unit
// within FILE_CONTEXT. This uses information received from the
// dwarf2reader::CompilationUnit DWARF parser to populate
// FILE_CONTEXT->module. Use LINE_READER to handle the compilation
// unit's line number data. Use REPORTER to report problems with the
// data we find.
DwarfCUToModule(FileContext *file_context,
LineToModuleHandler *line_reader,
WarningReporter *reporter);
~DwarfCUToModule();
void ProcessAttributeSigned(enum DwarfAttribute attr,
enum DwarfForm form,
int64 data);
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
enum DwarfForm form,
uint64 data);
void ProcessAttributeString(enum DwarfAttribute attr,
enum DwarfForm form,
const string &data);
bool EndAttributes();
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag);
// Assign all our source Lines to the Functions that cover their
// addresses, and then add them to module_.
void Finish();
bool StartCompilationUnit(uint64 offset, uint8 address_size,
uint8 offset_size, uint64 cu_length,
uint8 dwarf_version);
bool StartRootDIE(uint64 offset, enum DwarfTag tag);
private:
// Used internally by the handler. Full definitions are in
// dwarf_cu_to_module.cc.
struct CUContext;
struct DIEContext;
struct Specification;
class GenericDIEHandler;
class FuncHandler;
class NamedScopeHandler;
// A map from section offsets to specifications.
typedef map<uint64, Specification> SpecificationByOffset;
// Set this compilation unit's source language to LANGUAGE.
void SetLanguage(DwarfLanguage language);
// Read source line information at OFFSET in the .debug_line
// section. Record source files in module_, but record source lines
// in lines_; we apportion them to functions in
// AssignLinesToFunctions.
void ReadSourceLines(uint64 offset);
// Assign the lines in lines_ to the individual line lists of the
// functions in functions_. (DWARF line information maps an entire
// compilation unit at a time, and gives no indication of which
// lines belong to which functions, beyond their addresses.)
void AssignLinesToFunctions();
// The only reason cu_context_ and child_context_ are pointers is
// that we want to keep their definitions private to
// dwarf_cu_to_module.cc, instead of listing them all here. They are
// owned by this DwarfCUToModule: the constructor sets them, and the
// destructor deletes them.
// The handler to use to handle line number data.
LineToModuleHandler *line_reader_;
// This compilation unit's context.
scoped_ptr<CUContext> cu_context_;
// A context for our children.
scoped_ptr<DIEContext> child_context_;
// True if this compilation unit has source line information.
bool has_source_line_info_;
// The offset of this compilation unit's line number information in
// the .debug_line section.
uint64 source_line_offset_;
// The line numbers we have seen thus far. We accumulate these here
// during parsing. Then, in Finish, we call AssignLinesToFunctions
// to dole them out to the appropriate functions.
vector<Module::Line> lines_;
};
} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,143 @@
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
// See dwarf_line_to_module.h for details.
#include <stdio.h>
#include <string>
#include "common/dwarf_line_to_module.h"
#include "common/using_std_string.h"
// Trying to support Windows paths in a reasonable way adds a lot of
// variations to test; it would be better to just put off dealing with
// it until we actually have to deal with DWARF on Windows.
// Return true if PATH is an absolute path, false if it is relative.
static bool PathIsAbsolute(const string &path) {
return (path.size() >= 1 && path[0] == '/');
}
static bool HasTrailingSlash(const string &path) {
return (path.size() >= 1 && path[path.size() - 1] == '/');
}
// If PATH is an absolute path, return PATH. If PATH is a relative path,
// treat it as relative to BASE and return the combined path.
static string ExpandPath(const string &path,
const string &base) {
if (PathIsAbsolute(path) || base.empty())
return path;
return base + (HasTrailingSlash(base) ? "" : "/") + path;
}
namespace google_breakpad {
void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) {
// Directory number zero is reserved to mean the compilation
// directory. Silently ignore attempts to redefine it.
if (dir_num != 0)
directories_[dir_num] = ExpandPath(name, compilation_dir_);
}
void DwarfLineToModule::DefineFile(const string &name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length) {
if (file_num == -1)
file_num = ++highest_file_number_;
else if (file_num > highest_file_number_)
highest_file_number_ = file_num;
string dir_name;
if (dir_num == 0) {
// Directory number zero is the compilation directory, and is stored as
// an attribute on the compilation unit, rather than in the program table.
dir_name = compilation_dir_;
} else {
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
if (directory_it != directories_.end()) {
dir_name = directory_it->second;
} else {
if (!warned_bad_directory_number_) {
fprintf(stderr, "warning: DWARF line number data refers to undefined"
" directory numbers\n");
warned_bad_directory_number_ = true;
}
}
}
string full_name = ExpandPath(name, dir_name);
// Find a Module::File object of the given name, and add it to the
// file table.
files_[file_num] = module_->FindFile(full_name);
}
void DwarfLineToModule::AddLine(uint64 address, uint64 length,
uint32 file_num, uint32 line_num,
uint32 column_num) {
if (length == 0)
return;
// Clip lines not to extend beyond the end of the address space.
if (address + length < address)
length = -address;
// Should we omit this line? (See the comments for omitted_line_end_.)
if (address == 0 || address == omitted_line_end_) {
omitted_line_end_ = address + length;
return;
} else {
omitted_line_end_ = 0;
}
// Find the source file being referred to.
Module::File *file = files_[file_num];
if (!file) {
if (!warned_bad_file_number_) {
fprintf(stderr, "warning: DWARF line number data refers to "
"undefined file numbers\n");
warned_bad_file_number_ = true;
}
return;
}
Module::Line line;
line.address = address;
// We set the size when we get the next line or the EndSequence call.
line.size = length;
line.file = file;
line.number = line_num;
lines_->push_back(line);
}
} // namespace google_breakpad

View File

@ -0,0 +1,188 @@
// -*- mode: c++ -*-
// Copyright (c) 2010 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
// The DwarfLineToModule class accepts line number information from a
// DWARF parser and adds it to a google_breakpad::Module. The Module
// can write that data out as a Breakpad symbol file.
#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H
#include <string>
#include "common/module.h"
#include "common/dwarf/dwarf2reader.h"
#include "common/using_std_string.h"
namespace google_breakpad {
// A class for producing a vector of google_breakpad::Module::Line
// instances from parsed DWARF line number data.
//
// An instance of this class can be provided as a handler to a
// dwarf2reader::LineInfo DWARF line number information parser. The
// handler accepts source location information from the parser and
// uses it to produce a vector of google_breakpad::Module::Line
// objects, referring to google_breakpad::Module::File objects added
// to a particular google_breakpad::Module.
//
// GNU toolchain omitted sections support:
// ======================================
//
// Given the right options, the GNU toolchain will omit unreferenced
// functions from the final executable. Unfortunately, when it does so, it
// does not remove the associated portions of the DWARF line number
// program; instead, it gives the DW_LNE_set_address instructions referring
// to the now-deleted code addresses of zero. Given this input, the DWARF
// line parser will call AddLine with a series of lines starting at address
// zero. For example, here is the output from 'readelf -wl' for a program
// with four functions, the first three of which have been omitted:
//
// Line Number Statements:
// Extended opcode 2: set Address to 0x0
// Advance Line by 14 to 15
// Copy
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
// Advance PC by 2 to 0xd
// Extended opcode 1: End of Sequence
//
// Extended opcode 2: set Address to 0x0
// Advance Line by 14 to 15
// Copy
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
// Advance PC by 2 to 0xd
// Extended opcode 1: End of Sequence
//
// Extended opcode 2: set Address to 0x0
// Advance Line by 19 to 20
// Copy
// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21
// Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22
// Advance PC by 2 to 0xa
// Extended opcode 1: End of Sequence
//
// Extended opcode 2: set Address to 0x80483a4
// Advance Line by 23 to 24
// Copy
// Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25
// Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26
// Advance PC by 6 to 0x80483bd
// Extended opcode 1: End of Sequence
//
// Instead of collecting runs of lines describing code that is not there,
// we try to recognize and drop them. Since the linker doesn't explicitly
// distinguish references to dropped sections from genuine references to
// code at address zero, we must use a heuristic. We have chosen:
//
// - If a line starts at address zero, omit it. (On the platforms
// breakpad targets, it is extremely unlikely that there will be code
// at address zero.)
//
// - If a line starts immediately after an omitted line, omit it too.
class DwarfLineToModule: public dwarf2reader::LineInfoHandler {
public:
// As the DWARF line info parser passes us line records, add source
// files to MODULE, and add all lines to the end of LINES. LINES
// need not be empty. If the parser hands us a zero-length line, we
// omit it. If the parser hands us a line that extends beyond the
// end of the address space, we clip it. It's up to our client to
// sort out which lines belong to which functions; we don't add them
// to any particular function in MODULE ourselves.
DwarfLineToModule(Module *module, const string& compilation_dir,
vector<Module::Line> *lines)
: module_(module),
compilation_dir_(compilation_dir),
lines_(lines),
highest_file_number_(-1),
omitted_line_end_(0),
warned_bad_file_number_(false),
warned_bad_directory_number_(false) { }
~DwarfLineToModule() { }
void DefineDir(const string &name, uint32 dir_num);
void DefineFile(const string &name, int32 file_num,
uint32 dir_num, uint64 mod_time,
uint64 length);
void AddLine(uint64 address, uint64 length,
uint32 file_num, uint32 line_num, uint32 column_num);
private:
typedef std::map<uint32, string> DirectoryTable;
typedef std::map<uint32, Module::File *> FileTable;
// The module we're contributing debugging info to. Owned by our
// client.
Module *module_;
// The compilation directory for the current compilation unit whose
// lines are being accumulated.
string compilation_dir_;
// The vector of lines we're accumulating. Owned by our client.
//
// In a Module, as in a breakpad symbol file, lines belong to
// specific functions, but DWARF simply assigns lines to addresses;
// one must infer the line/function relationship using the
// functions' beginning and ending addresses. So we can't add these
// to the appropriate function from module_ until we've read the
// function info as well. Instead, we accumulate lines here, and let
// whoever constructed this sort it all out.
vector<Module::Line> *lines_;
// A table mapping directory numbers to paths.
DirectoryTable directories_;
// A table mapping file numbers to Module::File pointers.
FileTable files_;
// The highest file number we've seen so far, or -1 if we've seen
// none. Used for dynamically defined file numbers.
int32 highest_file_number_;
// This is the ending address of the last line we omitted, or zero if we
// didn't omit the previous line. It is zero before we have received any
// AddLine calls.
uint64 omitted_line_end_;
// True if we've warned about:
bool warned_bad_file_number_; // bad file numbers
bool warned_bad_directory_number_; // bad directory numbers
};
} // namespace google_breakpad
#endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H

Some files were not shown because too many files have changed in this diff Show More