Updated google-breakpad.
This commit is contained in:
parent
f602743c5a
commit
f652f53a22
45
google-breakpad/.gitignore
vendored
Normal file
45
google-breakpad/.gitignore
vendored
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
# Ignore GYP generated Visual Studio artifacts.
|
||||||
|
*.filters
|
||||||
|
*.sdf
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*.vcxproj
|
||||||
|
|
||||||
|
# Ignore compiled Python files.
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Ignore directories gclient syncs.
|
||||||
|
src/testing
|
||||||
|
src/third_party/glog
|
||||||
|
src/third_party/lss
|
||||||
|
src/third_party/protobuf
|
||||||
|
src/tools/gyp
|
@ -1,5 +1,4 @@
|
|||||||
# Copyright (c) 2010, Google Inc.
|
# Copyright 2010 Google Inc. All rights reserved.
|
||||||
# All rights reserved.
|
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are
|
# modification, are permitted provided that the following conditions are
|
||||||
@ -27,18 +26,41 @@
|
|||||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
# 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
|
# This is used to mimic the svn:externals mechanism for gclient (both Git and
|
||||||
# syncing, if we use gclient. All dependencies are svn:externals instead.
|
# SVN) based checkouts of Breakpad. As such, its use is entirely optional. If
|
||||||
# If you're not using gclient, you need to run the gyp python script to
|
# using a manually managed SVN checkout as opposed to a gclient managed checkout
|
||||||
# generate the projects.
|
# you can still use the hooks mechanism for generating project files by calling
|
||||||
# This can be done by the following command (assuming current directory):
|
# 'gclient runhooks' rather than 'gclient sync'.
|
||||||
# src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp
|
|
||||||
|
deps = {
|
||||||
|
# Logging code.
|
||||||
|
"src/src/third_party/glog":
|
||||||
|
"http://google-glog.googlecode.com/svn/trunk@97",
|
||||||
|
|
||||||
|
# Testing libraries and utilities.
|
||||||
|
"src/src/testing": "http://googlemock.googlecode.com/svn/trunk@408",
|
||||||
|
"src/src/testing/gtest": "http://googletest.googlecode.com/svn/trunk@615",
|
||||||
|
|
||||||
|
# Protobuf.
|
||||||
|
"src/src/third_party/protobuf/protobuf":
|
||||||
|
"http://protobuf.googlecode.com/svn/trunk@407",
|
||||||
|
|
||||||
|
# GYP project generator.
|
||||||
|
"src/src/tools/gyp": "http://gyp.googlecode.com/svn/trunk@1886",
|
||||||
|
|
||||||
|
# Linux syscall support.
|
||||||
|
"src/src/third_party/lss":
|
||||||
|
"http://linux-syscall-support.googlecode.com/svn/trunk/lss@24",
|
||||||
|
}
|
||||||
|
|
||||||
hooks = [
|
hooks = [
|
||||||
{
|
{
|
||||||
# A change to a .gyp, .gypi, or to GYP itself should run the generator.
|
# TODO(chrisha): Fix the GYP files so that they work without
|
||||||
|
# --no-circular-check.
|
||||||
"pattern": ".",
|
"pattern": ".",
|
||||||
"action": ["python",
|
"action": ["python",
|
||||||
"src/src/tools/gyp/gyp",
|
"src/src/tools/gyp/gyp_main.py",
|
||||||
|
"--no-circular-check",
|
||||||
"src/src/client/windows/breakpad_client.gyp"],
|
"src/src/client/windows/breakpad_client.gyp"],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -1,19 +1,25 @@
|
|||||||
Installation Instructions
|
Installation Instructions
|
||||||
*************************
|
*************************
|
||||||
|
|
||||||
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
|
Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation,
|
||||||
2006 Free Software Foundation, Inc.
|
Inc.
|
||||||
|
|
||||||
This file is free documentation; the Free Software Foundation gives
|
Copying and distribution of this file, with or without modification,
|
||||||
unlimited permission to copy, distribute and modify it.
|
are permitted in any medium without royalty provided the copyright
|
||||||
|
notice and this notice are preserved. This file is offered as-is,
|
||||||
|
without warranty of any kind.
|
||||||
|
|
||||||
Basic Installation
|
Basic Installation
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Briefly, the shell commands `./configure; make; make install' should
|
Briefly, the shell commands `./configure; make; make install' should
|
||||||
configure, build, and install this package. The following
|
configure, build, and install this package. The following
|
||||||
more-detailed instructions are generic; see the `README' file for
|
more-detailed instructions are generic; see the `README' file for
|
||||||
instructions specific to this package.
|
instructions specific to this package. Some packages provide this
|
||||||
|
`INSTALL' file but do not implement all of the features documented
|
||||||
|
below. The lack of an optional feature in a given package is not
|
||||||
|
necessarily a bug. More recommendations for GNU packages can be found
|
||||||
|
in *note Makefile Conventions: (standards)Makefile Conventions.
|
||||||
|
|
||||||
The `configure' shell script attempts to guess correct values for
|
The `configure' shell script attempts to guess correct values for
|
||||||
various system-dependent variables used during compilation. It uses
|
various system-dependent variables used during compilation. It uses
|
||||||
@ -42,7 +48,7 @@ may remove or edit it.
|
|||||||
you want to change it or regenerate `configure' using a newer version
|
you want to change it or regenerate `configure' using a newer version
|
||||||
of `autoconf'.
|
of `autoconf'.
|
||||||
|
|
||||||
The simplest way to compile this package is:
|
The simplest way to compile this package is:
|
||||||
|
|
||||||
1. `cd' to the directory containing the package's source code and type
|
1. `cd' to the directory containing the package's source code and type
|
||||||
`./configure' to configure the package for your system.
|
`./configure' to configure the package for your system.
|
||||||
@ -53,12 +59,22 @@ The simplest way to compile this package is:
|
|||||||
2. Type `make' to compile the package.
|
2. Type `make' to compile the package.
|
||||||
|
|
||||||
3. Optionally, type `make check' to run any self-tests that come with
|
3. Optionally, type `make check' to run any self-tests that come with
|
||||||
the package.
|
the package, generally using the just-built uninstalled binaries.
|
||||||
|
|
||||||
4. Type `make install' to install the programs and any data files and
|
4. Type `make install' to install the programs and any data files and
|
||||||
documentation.
|
documentation. When installing into a prefix owned by root, it is
|
||||||
|
recommended that the package be configured and built as a regular
|
||||||
|
user, and only the `make install' phase executed with root
|
||||||
|
privileges.
|
||||||
|
|
||||||
5. You can remove the program binaries and object files from the
|
5. Optionally, type `make installcheck' to repeat any self-tests, but
|
||||||
|
this time using the binaries in their final installed location.
|
||||||
|
This target does not install anything. Running this target as a
|
||||||
|
regular user, particularly if the prior `make install' required
|
||||||
|
root privileges, verifies that the installation completed
|
||||||
|
correctly.
|
||||||
|
|
||||||
|
6. You can remove the program binaries and object files from the
|
||||||
source code directory by typing `make clean'. To also remove the
|
source code directory by typing `make clean'. To also remove the
|
||||||
files that `configure' created (so you can compile the package for
|
files that `configure' created (so you can compile the package for
|
||||||
a different kind of computer), type `make distclean'. There is
|
a different kind of computer), type `make distclean'. There is
|
||||||
@ -67,12 +83,22 @@ The simplest way to compile this package is:
|
|||||||
all sorts of other programs in order to regenerate files that came
|
all sorts of other programs in order to regenerate files that came
|
||||||
with the distribution.
|
with the distribution.
|
||||||
|
|
||||||
|
7. Often, you can also type `make uninstall' to remove the installed
|
||||||
|
files again. In practice, not all packages have tested that
|
||||||
|
uninstallation works correctly, even though it is required by the
|
||||||
|
GNU Coding Standards.
|
||||||
|
|
||||||
|
8. Some packages, particularly those that use Automake, provide `make
|
||||||
|
distcheck', which can by used by developers to test that all other
|
||||||
|
targets like `make install' and `make uninstall' work correctly.
|
||||||
|
This target is generally not run by end users.
|
||||||
|
|
||||||
Compilers and Options
|
Compilers and Options
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
Some systems require unusual options for compilation or linking that the
|
Some systems require unusual options for compilation or linking that
|
||||||
`configure' script does not know about. Run `./configure --help' for
|
the `configure' script does not know about. Run `./configure --help'
|
||||||
details on some of the pertinent environment variables.
|
for details on some of the pertinent environment variables.
|
||||||
|
|
||||||
You can give `configure' initial values for configuration parameters
|
You can give `configure' initial values for configuration parameters
|
||||||
by setting variables in the command line or in the environment. Here
|
by setting variables in the command line or in the environment. Here
|
||||||
@ -85,25 +111,41 @@ is an example:
|
|||||||
Compiling For Multiple Architectures
|
Compiling For Multiple Architectures
|
||||||
====================================
|
====================================
|
||||||
|
|
||||||
You can compile the package for more than one kind of computer at the
|
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
|
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
|
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
|
directory where you want the object files and executables to go and run
|
||||||
the `configure' script. `configure' automatically checks for the
|
the `configure' script. `configure' automatically checks for the
|
||||||
source code in the directory that `configure' is in and in `..'.
|
source code in the directory that `configure' is in and in `..'. This
|
||||||
|
is known as a "VPATH" build.
|
||||||
|
|
||||||
With a non-GNU `make', it is safer to compile the package for one
|
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
|
architecture at a time in the source code directory. After you have
|
||||||
installed the package for one architecture, use `make distclean' before
|
installed the package for one architecture, use `make distclean' before
|
||||||
reconfiguring for another architecture.
|
reconfiguring for another architecture.
|
||||||
|
|
||||||
|
On MacOS X 10.5 and later systems, you can create libraries and
|
||||||
|
executables that work on multiple system types--known as "fat" or
|
||||||
|
"universal" binaries--by specifying multiple `-arch' options to the
|
||||||
|
compiler but only a single `-arch' option to the preprocessor. Like
|
||||||
|
this:
|
||||||
|
|
||||||
|
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
|
||||||
|
CPP="gcc -E" CXXCPP="g++ -E"
|
||||||
|
|
||||||
|
This is not guaranteed to produce working output in all cases, you
|
||||||
|
may have to build one architecture at a time and combine the results
|
||||||
|
using the `lipo' tool if you have problems.
|
||||||
|
|
||||||
Installation Names
|
Installation Names
|
||||||
==================
|
==================
|
||||||
|
|
||||||
By default, `make install' installs the package's commands under
|
By default, `make install' installs the package's commands under
|
||||||
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
`/usr/local/bin', include files under `/usr/local/include', etc. You
|
||||||
can specify an installation prefix other than `/usr/local' by giving
|
can specify an installation prefix other than `/usr/local' by giving
|
||||||
`configure' the option `--prefix=PREFIX'.
|
`configure' the option `--prefix=PREFIX', where PREFIX must be an
|
||||||
|
absolute file name.
|
||||||
|
|
||||||
You can specify separate installation prefixes for
|
You can specify separate installation prefixes for
|
||||||
architecture-specific files and architecture-independent files. If you
|
architecture-specific files and architecture-independent files. If you
|
||||||
@ -114,16 +156,47 @@ Documentation and other data files still use the regular prefix.
|
|||||||
In addition, if you use an unusual directory layout you can give
|
In addition, if you use an unusual directory layout you can give
|
||||||
options like `--bindir=DIR' to specify different values for particular
|
options like `--bindir=DIR' to specify different values for particular
|
||||||
kinds of files. Run `configure --help' for a list of the directories
|
kinds of files. Run `configure --help' for a list of the directories
|
||||||
you can set and what kinds of files go in them.
|
you can set and what kinds of files go in them. In general, the
|
||||||
|
default for these options is expressed in terms of `${prefix}', so that
|
||||||
|
specifying just `--prefix' will affect all of the other directory
|
||||||
|
specifications that were not explicitly provided.
|
||||||
|
|
||||||
|
The most portable way to affect installation locations is to pass the
|
||||||
|
correct locations to `configure'; however, many packages provide one or
|
||||||
|
both of the following shortcuts of passing variable assignments to the
|
||||||
|
`make install' command line to change installation locations without
|
||||||
|
having to reconfigure or recompile.
|
||||||
|
|
||||||
|
The first method involves providing an override variable for each
|
||||||
|
affected directory. For example, `make install
|
||||||
|
prefix=/alternate/directory' will choose an alternate location for all
|
||||||
|
directory configuration variables that were expressed in terms of
|
||||||
|
`${prefix}'. Any directories that were specified during `configure',
|
||||||
|
but not in terms of `${prefix}', must each be overridden at install
|
||||||
|
time for the entire installation to be relocated. The approach of
|
||||||
|
makefile variable overrides for each directory variable is required by
|
||||||
|
the GNU Coding Standards, and ideally causes no recompilation.
|
||||||
|
However, some platforms have known limitations with the semantics of
|
||||||
|
shared libraries that end up requiring recompilation when using this
|
||||||
|
method, particularly noticeable in packages that use GNU Libtool.
|
||||||
|
|
||||||
|
The second method involves providing the `DESTDIR' variable. For
|
||||||
|
example, `make install DESTDIR=/alternate/directory' will prepend
|
||||||
|
`/alternate/directory' before all installation names. The approach of
|
||||||
|
`DESTDIR' overrides is not required by the GNU Coding Standards, and
|
||||||
|
does not work on platforms that have drive letters. On the other hand,
|
||||||
|
it does better at avoiding recompilation issues, and works well even
|
||||||
|
when some directory options were not specified in terms of `${prefix}'
|
||||||
|
at `configure' time.
|
||||||
|
|
||||||
|
Optional Features
|
||||||
|
=================
|
||||||
|
|
||||||
If the package supports it, you can cause programs to be installed
|
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
|
with an extra prefix or suffix on their names by giving `configure' the
|
||||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||||
|
|
||||||
Optional Features
|
Some packages pay attention to `--enable-FEATURE' options to
|
||||||
=================
|
|
||||||
|
|
||||||
Some packages pay attention to `--enable-FEATURE' options to
|
|
||||||
`configure', where FEATURE indicates an optional part of the package.
|
`configure', where FEATURE indicates an optional part of the package.
|
||||||
They may also pay attention to `--with-PACKAGE' options, where 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
|
is something like `gnu-as' or `x' (for the X Window System). The
|
||||||
@ -135,14 +208,58 @@ find the X include and library files automatically, but if it doesn't,
|
|||||||
you can use the `configure' options `--x-includes=DIR' and
|
you can use the `configure' options `--x-includes=DIR' and
|
||||||
`--x-libraries=DIR' to specify their locations.
|
`--x-libraries=DIR' to specify their locations.
|
||||||
|
|
||||||
|
Some packages offer the ability to configure how verbose the
|
||||||
|
execution of `make' will be. For these packages, running `./configure
|
||||||
|
--enable-silent-rules' sets the default to minimal output, which can be
|
||||||
|
overridden with `make V=1'; while running `./configure
|
||||||
|
--disable-silent-rules' sets the default to verbose, which can be
|
||||||
|
overridden with `make V=0'.
|
||||||
|
|
||||||
|
Particular systems
|
||||||
|
==================
|
||||||
|
|
||||||
|
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
|
||||||
|
CC is not installed, it is recommended to use the following options in
|
||||||
|
order to use an ANSI C compiler:
|
||||||
|
|
||||||
|
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
|
||||||
|
|
||||||
|
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
|
||||||
|
|
||||||
|
HP-UX `make' updates targets which have the same time stamps as
|
||||||
|
their prerequisites, which makes it generally unusable when shipped
|
||||||
|
generated files such as `configure' are involved. Use GNU `make'
|
||||||
|
instead.
|
||||||
|
|
||||||
|
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
|
||||||
|
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
|
||||||
|
a workaround. If GNU CC is not installed, it is therefore recommended
|
||||||
|
to try
|
||||||
|
|
||||||
|
./configure CC="cc"
|
||||||
|
|
||||||
|
and if that doesn't work, try
|
||||||
|
|
||||||
|
./configure CC="cc -nodtk"
|
||||||
|
|
||||||
|
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
|
||||||
|
directory contains several dysfunctional programs; working variants of
|
||||||
|
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
|
||||||
|
in your `PATH', put it _after_ `/usr/bin'.
|
||||||
|
|
||||||
|
On Haiku, software installed for all users goes in `/boot/common',
|
||||||
|
not `/usr/local'. It is recommended to use the following options:
|
||||||
|
|
||||||
|
./configure --prefix=/boot/common
|
||||||
|
|
||||||
Specifying the System Type
|
Specifying the System Type
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
There may be some features `configure' cannot figure out automatically,
|
There may be some features `configure' cannot figure out
|
||||||
but needs to determine by the type of machine the package will run on.
|
automatically, but needs to determine by the type of machine the package
|
||||||
Usually, assuming the package is built to be run on the _same_
|
will run on. Usually, assuming the package is built to be run on the
|
||||||
architectures, `configure' can figure that out, but if it prints a
|
_same_ architectures, `configure' can figure that out, but if it prints
|
||||||
message saying it cannot guess the machine type, give it the
|
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
|
`--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:
|
type, such as `sun4', or a canonical name which has the form:
|
||||||
|
|
||||||
@ -150,7 +267,8 @@ type, such as `sun4', or a canonical name which has the form:
|
|||||||
|
|
||||||
where SYSTEM can have one of these forms:
|
where SYSTEM can have one of these forms:
|
||||||
|
|
||||||
OS KERNEL-OS
|
OS
|
||||||
|
KERNEL-OS
|
||||||
|
|
||||||
See the file `config.sub' for the possible values of each field. If
|
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
|
`config.sub' isn't included in this package, then this package doesn't
|
||||||
@ -168,9 +286,9 @@ eventually be run) with `--host=TYPE'.
|
|||||||
Sharing Defaults
|
Sharing Defaults
|
||||||
================
|
================
|
||||||
|
|
||||||
If you want to set default values for `configure' scripts to share, you
|
If you want to set default values for `configure' scripts to share,
|
||||||
can create a site shell script called `config.site' that gives default
|
you can create a site shell script called `config.site' that gives
|
||||||
values for variables like `CC', `cache_file', and `prefix'.
|
default values for variables like `CC', `cache_file', and `prefix'.
|
||||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||||
`CONFIG_SITE' environment variable to the location of the site script.
|
`CONFIG_SITE' environment variable to the location of the site script.
|
||||||
@ -179,7 +297,7 @@ A warning: not all `configure' scripts look for a site script.
|
|||||||
Defining Variables
|
Defining Variables
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Variables not defined in a site shell script can be set in the
|
Variables not defined in a site shell script can be set in the
|
||||||
environment passed to `configure'. However, some packages may run
|
environment passed to `configure'. However, some packages may run
|
||||||
configure again during the build, and the customized values of these
|
configure again during the build, and the customized values of these
|
||||||
variables may be lost. In order to avoid this problem, you should set
|
variables may be lost. In order to avoid this problem, you should set
|
||||||
@ -191,18 +309,27 @@ causes the specified `gcc' to be used as the C compiler (unless it is
|
|||||||
overridden in the site shell script).
|
overridden in the site shell script).
|
||||||
|
|
||||||
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
|
||||||
an Autoconf bug. Until the bug is fixed you can use this workaround:
|
an Autoconf limitation. Until the limitation is lifted, you can use
|
||||||
|
this workaround:
|
||||||
|
|
||||||
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
|
CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash
|
||||||
|
|
||||||
`configure' Invocation
|
`configure' Invocation
|
||||||
======================
|
======================
|
||||||
|
|
||||||
`configure' recognizes the following options to control how it operates.
|
`configure' recognizes the following options to control how it
|
||||||
|
operates.
|
||||||
|
|
||||||
`--help'
|
`--help'
|
||||||
`-h'
|
`-h'
|
||||||
Print a summary of the options to `configure', and exit.
|
Print a summary of all of the options to `configure', and exit.
|
||||||
|
|
||||||
|
`--help=short'
|
||||||
|
`--help=recursive'
|
||||||
|
Print a summary of the options unique to this package's
|
||||||
|
`configure', and exit. The `short' variant lists options used
|
||||||
|
only in the top level, while the `recursive' variant lists options
|
||||||
|
also present in any nested packages.
|
||||||
|
|
||||||
`--version'
|
`--version'
|
||||||
`-V'
|
`-V'
|
||||||
@ -229,6 +356,15 @@ an Autoconf bug. Until the bug is fixed you can use this workaround:
|
|||||||
Look for the package's source code in directory DIR. Usually
|
Look for the package's source code in directory DIR. Usually
|
||||||
`configure' can determine that directory automatically.
|
`configure' can determine that directory automatically.
|
||||||
|
|
||||||
|
`--prefix=DIR'
|
||||||
|
Use DIR as the installation prefix. *note Installation Names::
|
||||||
|
for more details, including other options available for fine-tuning
|
||||||
|
the installation locations.
|
||||||
|
|
||||||
|
`--no-create'
|
||||||
|
`-n'
|
||||||
|
Run the configure checks, but stop before creating any output
|
||||||
|
files.
|
||||||
|
|
||||||
`configure' also accepts some other, not widely useful, options. Run
|
`configure' also accepts some other, not widely useful, options. Run
|
||||||
`configure --help' for more details.
|
`configure --help' for more details.
|
||||||
|
|
||||||
|
@ -26,3 +26,25 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
|
||||||
|
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.
|
@ -64,17 +64,50 @@ endif
|
|||||||
# Specify include paths for ac macros
|
# Specify include paths for ac macros
|
||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
|
|
||||||
|
# License file is called LICENSE not COPYING
|
||||||
|
AUTOMAKE_OPTIONS = foreign
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
|
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
|
||||||
|
|
||||||
dist_doc_DATA = \
|
dist_doc_DATA = \
|
||||||
AUTHORS \
|
AUTHORS \
|
||||||
COPYING \
|
|
||||||
ChangeLog \
|
ChangeLog \
|
||||||
INSTALL \
|
INSTALL \
|
||||||
|
LICENSE \
|
||||||
NEWS \
|
NEWS \
|
||||||
README
|
README
|
||||||
|
|
||||||
|
## Headers
|
||||||
|
if LINUX_HOST
|
||||||
|
includeclhdir = $(includedir)/$(PACKAGE)/client/linux/handler
|
||||||
|
includeclh_HEADERS = $(top_srcdir)/src/client/linux/handler/*.h
|
||||||
|
|
||||||
|
includeclmdir = $(includedir)/$(PACKAGE)/client/linux/minidump_writer
|
||||||
|
includeclm_HEADERS = $(top_srcdir)/src/client/linux/minidump_writer/*.h
|
||||||
|
|
||||||
|
includeclcdir = $(includedir)/$(PACKAGE)/client/linux/crash_generation
|
||||||
|
includeclc_HEADERS = $(top_srcdir)/src/client/linux/crash_generation/*.h
|
||||||
|
|
||||||
|
includelssdir = $(includedir)/$(PACKAGE)/third_party/lss
|
||||||
|
includelss_HEADERS = $(top_srcdir)/src/third_party/lss/*.h
|
||||||
|
|
||||||
|
includecldir = $(includedir)/$(PACKAGE)/common/linux
|
||||||
|
includecl_HEADERS = $(top_srcdir)/src/common/linux/*.h
|
||||||
|
endif
|
||||||
|
|
||||||
|
includegbcdir = $(includedir)/$(PACKAGE)/google_breakpad/common
|
||||||
|
includegbc_HEADERS = $(top_srcdir)/src/google_breakpad/common/*.h
|
||||||
|
|
||||||
|
includecdir = $(includedir)/$(PACKAGE)/common
|
||||||
|
includec_HEADERS = $(top_srcdir)/src/common/*.h
|
||||||
|
|
||||||
|
includepdir = $(includedir)/$(PACKAGE)/processor
|
||||||
|
includep_HEADERS = $(top_srcdir)/src/processor/*.h
|
||||||
|
|
||||||
|
## pkgconfig files
|
||||||
|
pkgconfigdir = $(libdir)/pkgconfig
|
||||||
|
pkgconfig_DATA =
|
||||||
|
|
||||||
## Libraries
|
## Libraries
|
||||||
noinst_LIBRARIES =
|
noinst_LIBRARIES =
|
||||||
@ -84,18 +117,24 @@ check_PROGRAMS =
|
|||||||
|
|
||||||
if !DISABLE_PROCESSOR
|
if !DISABLE_PROCESSOR
|
||||||
lib_LIBRARIES += src/libbreakpad.a
|
lib_LIBRARIES += src/libbreakpad.a
|
||||||
|
pkgconfig_DATA += breakpad.pc
|
||||||
noinst_LIBRARIES += src/third_party/libdisasm/libdisasm.a
|
noinst_LIBRARIES += src/third_party/libdisasm/libdisasm.a
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if LINUX_HOST
|
if LINUX_HOST
|
||||||
lib_LIBRARIES += src/client/linux/libbreakpad_client.a
|
lib_LIBRARIES += src/client/linux/libbreakpad_client.a
|
||||||
|
pkgconfig_DATA += breakpad-client.pc
|
||||||
|
|
||||||
src_client_linux_libbreakpad_client_a_SOURCES = \
|
src_client_linux_libbreakpad_client_a_SOURCES = \
|
||||||
src/client/linux/crash_generation/crash_generation_client.cc \
|
src/client/linux/crash_generation/crash_generation_client.cc \
|
||||||
src/client/linux/crash_generation/crash_generation_server.cc \
|
src/client/linux/crash_generation/crash_generation_server.cc \
|
||||||
|
src/client/linux/dump_writer_common/seccomp_unwinder.cc \
|
||||||
|
src/client/linux/dump_writer_common/thread_info.cc \
|
||||||
|
src/client/linux/dump_writer_common/ucontext_reader.cc \
|
||||||
src/client/linux/handler/exception_handler.cc \
|
src/client/linux/handler/exception_handler.cc \
|
||||||
src/client/linux/handler/minidump_descriptor.cc \
|
src/client/linux/handler/minidump_descriptor.cc \
|
||||||
src/client/linux/log/log.cc \
|
src/client/linux/log/log.cc \
|
||||||
|
src/client/linux/microdump_writer/microdump_writer.cc \
|
||||||
src/client/linux/minidump_writer/linux_dumper.cc \
|
src/client/linux/minidump_writer/linux_dumper.cc \
|
||||||
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
|
src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
|
||||||
src/client/linux/minidump_writer/minidump_writer.cc \
|
src/client/linux/minidump_writer/minidump_writer.cc \
|
||||||
@ -124,11 +163,15 @@ src_libbreakpad_a_SOURCES = \
|
|||||||
src/google_breakpad/processor/call_stack.h \
|
src/google_breakpad/processor/call_stack.h \
|
||||||
src/google_breakpad/processor/code_module.h \
|
src/google_breakpad/processor/code_module.h \
|
||||||
src/google_breakpad/processor/code_modules.h \
|
src/google_breakpad/processor/code_modules.h \
|
||||||
|
src/google_breakpad/processor/dump_context.h \
|
||||||
|
src/google_breakpad/processor/dump_object.h \
|
||||||
src/google_breakpad/processor/exploitability.h \
|
src/google_breakpad/processor/exploitability.h \
|
||||||
src/google_breakpad/processor/fast_source_line_resolver.h \
|
src/google_breakpad/processor/fast_source_line_resolver.h \
|
||||||
src/google_breakpad/processor/memory_region.h \
|
src/google_breakpad/processor/memory_region.h \
|
||||||
|
src/google_breakpad/processor/microdump_processor.h \
|
||||||
src/google_breakpad/processor/minidump.h \
|
src/google_breakpad/processor/minidump.h \
|
||||||
src/google_breakpad/processor/minidump_processor.h \
|
src/google_breakpad/processor/minidump_processor.h \
|
||||||
|
src/google_breakpad/processor/process_result.h \
|
||||||
src/google_breakpad/processor/process_state.h \
|
src/google_breakpad/processor/process_state.h \
|
||||||
src/google_breakpad/processor/source_line_resolver_base.h \
|
src/google_breakpad/processor/source_line_resolver_base.h \
|
||||||
src/google_breakpad/processor/source_line_resolver_interface.h \
|
src/google_breakpad/processor/source_line_resolver_interface.h \
|
||||||
@ -154,7 +197,11 @@ src_libbreakpad_a_SOURCES = \
|
|||||||
src/processor/contained_range_map.h \
|
src/processor/contained_range_map.h \
|
||||||
src/processor/disassembler_x86.h \
|
src/processor/disassembler_x86.h \
|
||||||
src/processor/disassembler_x86.cc \
|
src/processor/disassembler_x86.cc \
|
||||||
|
src/processor/dump_context.cc \
|
||||||
|
src/processor/dump_object.cc \
|
||||||
src/processor/exploitability.cc \
|
src/processor/exploitability.cc \
|
||||||
|
src/processor/exploitability_linux.h \
|
||||||
|
src/processor/exploitability_linux.cc \
|
||||||
src/processor/exploitability_win.h \
|
src/processor/exploitability_win.h \
|
||||||
src/processor/exploitability_win.cc \
|
src/processor/exploitability_win.cc \
|
||||||
src/processor/fast_source_line_resolver_types.h \
|
src/processor/fast_source_line_resolver_types.h \
|
||||||
@ -164,6 +211,7 @@ src_libbreakpad_a_SOURCES = \
|
|||||||
src/processor/logging.cc \
|
src/processor/logging.cc \
|
||||||
src/processor/map_serializers-inl.h \
|
src/processor/map_serializers-inl.h \
|
||||||
src/processor/map_serializers.h \
|
src/processor/map_serializers.h \
|
||||||
|
src/processor/microdump_processor.cc \
|
||||||
src/processor/minidump.cc \
|
src/processor/minidump.cc \
|
||||||
src/processor/minidump_processor.cc \
|
src/processor/minidump_processor.cc \
|
||||||
src/processor/module_comparer.cc \
|
src/processor/module_comparer.cc \
|
||||||
@ -185,12 +233,19 @@ src_libbreakpad_a_SOURCES = \
|
|||||||
src/processor/windows_frame_info.h \
|
src/processor/windows_frame_info.h \
|
||||||
src/processor/source_line_resolver_base_types.h \
|
src/processor/source_line_resolver_base_types.h \
|
||||||
src/processor/source_line_resolver_base.cc \
|
src/processor/source_line_resolver_base.cc \
|
||||||
|
src/processor/stack_frame_cpu.cc \
|
||||||
src/processor/stack_frame_symbolizer.cc \
|
src/processor/stack_frame_symbolizer.cc \
|
||||||
src/processor/stackwalker.cc \
|
src/processor/stackwalker.cc \
|
||||||
src/processor/stackwalker_amd64.cc \
|
src/processor/stackwalker_amd64.cc \
|
||||||
src/processor/stackwalker_amd64.h \
|
src/processor/stackwalker_amd64.h \
|
||||||
src/processor/stackwalker_arm.cc \
|
src/processor/stackwalker_arm.cc \
|
||||||
src/processor/stackwalker_arm.h \
|
src/processor/stackwalker_arm.h \
|
||||||
|
src/processor/stackwalker_arm64.cc \
|
||||||
|
src/processor/stackwalker_arm64.h \
|
||||||
|
src/processor/stackwalker_address_list.cc \
|
||||||
|
src/processor/stackwalker_address_list.h \
|
||||||
|
src/processor/stackwalker_mips.cc \
|
||||||
|
src/processor/stackwalker_mips.h \
|
||||||
src/processor/stackwalker_ppc.cc \
|
src/processor/stackwalker_ppc.cc \
|
||||||
src/processor/stackwalker_ppc.h \
|
src/processor/stackwalker_ppc.h \
|
||||||
src/processor/stackwalker_ppc64.cc \
|
src/processor/stackwalker_ppc64.cc \
|
||||||
@ -276,6 +331,7 @@ check_PROGRAMS += \
|
|||||||
src/processor/exploitability_unittest \
|
src/processor/exploitability_unittest \
|
||||||
src/processor/fast_source_line_resolver_unittest \
|
src/processor/fast_source_line_resolver_unittest \
|
||||||
src/processor/map_serializers_unittest \
|
src/processor/map_serializers_unittest \
|
||||||
|
src/processor/microdump_processor_unittest \
|
||||||
src/processor/minidump_processor_unittest \
|
src/processor/minidump_processor_unittest \
|
||||||
src/processor/minidump_unittest \
|
src/processor/minidump_unittest \
|
||||||
src/processor/static_address_map_unittest \
|
src/processor/static_address_map_unittest \
|
||||||
@ -287,6 +343,9 @@ check_PROGRAMS += \
|
|||||||
src/processor/range_map_unittest \
|
src/processor/range_map_unittest \
|
||||||
src/processor/stackwalker_amd64_unittest \
|
src/processor/stackwalker_amd64_unittest \
|
||||||
src/processor/stackwalker_arm_unittest \
|
src/processor/stackwalker_arm_unittest \
|
||||||
|
src/processor/stackwalker_arm64_unittest \
|
||||||
|
src/processor/stackwalker_address_list_unittest \
|
||||||
|
src/processor/stackwalker_mips_unittest \
|
||||||
src/processor/stackwalker_x86_unittest \
|
src/processor/stackwalker_x86_unittest \
|
||||||
src/processor/synth_minidump_unittest
|
src/processor/synth_minidump_unittest
|
||||||
endif
|
endif
|
||||||
@ -322,18 +381,28 @@ endif
|
|||||||
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
||||||
|
|
||||||
if ANDROID_HOST
|
if ANDROID_HOST
|
||||||
# Wrapper script to run unit test programs on a connected Android device.
|
# Since Autotools 1.2, tests are run through a special "test driver" script.
|
||||||
TESTS_ENVIRONMENT = $(top_srcdir)/android/test-shell.sh
|
# Unfortunately, it's not possible anymore to specify an alternative shell to
|
||||||
|
# run them on connected devices, so use a slightly modified version of the
|
||||||
|
# driver for Android.
|
||||||
|
LOG_DRIVER = $(top_srcdir)/android/test-driver
|
||||||
else
|
else
|
||||||
TESTS_ENVIRONMENT =
|
# The default Autotools test driver script.
|
||||||
|
LOG_DRIVER = $(top_srcdir)/autotools/test-driver
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if LINUX_HOST
|
if LINUX_HOST
|
||||||
src_client_linux_linux_dumper_unittest_helper_SOURCES = \
|
src_client_linux_linux_dumper_unittest_helper_SOURCES = \
|
||||||
src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
|
src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
|
||||||
src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS)
|
|
||||||
src_client_linux_linux_dumper_unittest_helper_LDFLAGS=$(PTHREAD_CFLAGS)
|
src_client_linux_linux_dumper_unittest_helper_LDFLAGS=$(PTHREAD_CFLAGS)
|
||||||
src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC)
|
src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC)
|
||||||
|
if ANDROID_HOST
|
||||||
|
# On Android PTHREAD_CFLAGS is empty, and adding src/common/android/include
|
||||||
|
# to the include path is necessary to build this program.
|
||||||
|
src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(AM_CXXFLAGS)
|
||||||
|
else
|
||||||
|
src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS)
|
||||||
|
endif
|
||||||
|
|
||||||
src_client_linux_linux_client_unittest_shlib_SOURCES = \
|
src_client_linux_linux_client_unittest_shlib_SOURCES = \
|
||||||
src/client/linux/handler/exception_handler_unittest.cc \
|
src/client/linux/handler/exception_handler_unittest.cc \
|
||||||
@ -355,6 +424,8 @@ src_client_linux_linux_client_unittest_shlib_SOURCES = \
|
|||||||
src/testing/gtest/src/gtest_main.cc \
|
src/testing/gtest/src/gtest_main.cc \
|
||||||
src/testing/src/gmock-all.cc \
|
src/testing/src/gmock-all.cc \
|
||||||
src/processor/basic_code_modules.cc \
|
src/processor/basic_code_modules.cc \
|
||||||
|
src/processor/dump_context.cc \
|
||||||
|
src/processor/dump_object.cc \
|
||||||
src/processor/logging.cc \
|
src/processor/logging.cc \
|
||||||
src/processor/minidump.cc \
|
src/processor/minidump.cc \
|
||||||
src/processor/pathname_stripper.cc
|
src/processor/pathname_stripper.cc
|
||||||
@ -373,10 +444,14 @@ src_client_linux_linux_client_unittest_shlib_LDFLAGS = \
|
|||||||
-shared \
|
-shared \
|
||||||
-Wl,-h,linux_client_unittest_shlib
|
-Wl,-h,linux_client_unittest_shlib
|
||||||
src_client_linux_linux_client_unittest_shlib_LDADD = \
|
src_client_linux_linux_client_unittest_shlib_LDADD = \
|
||||||
|
src/client/linux/crash_generation/crash_generation_client.o \
|
||||||
|
src/client/linux/dump_writer_common/seccomp_unwinder.o \
|
||||||
|
src/client/linux/dump_writer_common/thread_info.o \
|
||||||
|
src/client/linux/dump_writer_common/ucontext_reader.o \
|
||||||
src/client/linux/handler/exception_handler.o \
|
src/client/linux/handler/exception_handler.o \
|
||||||
src/client/linux/handler/minidump_descriptor.o \
|
src/client/linux/handler/minidump_descriptor.o \
|
||||||
src/client/linux/log/log.o \
|
src/client/linux/log/log.o \
|
||||||
src/client/linux/crash_generation/crash_generation_client.o \
|
src/client/linux/microdump_writer/microdump_writer.o \
|
||||||
src/client/linux/minidump_writer/linux_dumper.o \
|
src/client/linux/minidump_writer/linux_dumper.o \
|
||||||
src/client/linux/minidump_writer/linux_ptrace_dumper.o \
|
src/client/linux/minidump_writer/linux_ptrace_dumper.o \
|
||||||
src/client/linux/minidump_writer/minidump_writer.o \
|
src/client/linux/minidump_writer/minidump_writer.o \
|
||||||
@ -437,6 +512,7 @@ src_tools_linux_dump_syms_dump_syms_SOURCES = \
|
|||||||
src/common/dwarf/bytereader.cc \
|
src/common/dwarf/bytereader.cc \
|
||||||
src/common/dwarf/dwarf2diehandler.cc \
|
src/common/dwarf/dwarf2diehandler.cc \
|
||||||
src/common/dwarf/dwarf2reader.cc \
|
src/common/dwarf/dwarf2reader.cc \
|
||||||
|
src/common/linux/crc32.cc \
|
||||||
src/common/linux/dump_symbols.cc \
|
src/common/linux/dump_symbols.cc \
|
||||||
src/common/linux/elf_symbols_to_module.cc \
|
src/common/linux/elf_symbols_to_module.cc \
|
||||||
src/common/linux/elfutils.cc \
|
src/common/linux/elfutils.cc \
|
||||||
@ -485,6 +561,7 @@ src_common_dumper_unittest_SOURCES = \
|
|||||||
src/common/dwarf/dwarf2reader.cc \
|
src/common/dwarf/dwarf2reader.cc \
|
||||||
src/common/dwarf/dwarf2reader_cfi_unittest.cc \
|
src/common/dwarf/dwarf2reader_cfi_unittest.cc \
|
||||||
src/common/dwarf/dwarf2reader_die_unittest.cc \
|
src/common/dwarf/dwarf2reader_die_unittest.cc \
|
||||||
|
src/common/linux/crc32.cc \
|
||||||
src/common/linux/dump_symbols.cc \
|
src/common/linux/dump_symbols.cc \
|
||||||
src/common/linux/dump_symbols_unittest.cc \
|
src/common/linux/dump_symbols_unittest.cc \
|
||||||
src/common/linux/elf_core_dump.cc \
|
src/common/linux/elf_core_dump.cc \
|
||||||
@ -611,19 +688,27 @@ src_processor_exploitability_unittest_LDADD = \
|
|||||||
src/processor/process_state.o \
|
src/processor/process_state.o \
|
||||||
src/processor/disassembler_x86.o \
|
src/processor/disassembler_x86.o \
|
||||||
src/processor/exploitability.o \
|
src/processor/exploitability.o \
|
||||||
|
src/processor/exploitability_linux.o \
|
||||||
src/processor/exploitability_win.o \
|
src/processor/exploitability_win.o \
|
||||||
src/processor/basic_code_modules.o \
|
src/processor/basic_code_modules.o \
|
||||||
src/processor/basic_source_line_resolver.o \
|
src/processor/basic_source_line_resolver.o \
|
||||||
src/processor/call_stack.o \
|
src/processor/call_stack.o \
|
||||||
src/processor/cfi_frame_info.o \
|
src/processor/cfi_frame_info.o \
|
||||||
|
src/processor/dump_context.o \
|
||||||
|
src/processor/dump_object.o \
|
||||||
src/processor/logging.o \
|
src/processor/logging.o \
|
||||||
src/processor/minidump.o \
|
src/processor/minidump.o \
|
||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
|
src/processor/simple_symbol_supplier.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
|
src/processor/stackwalker_address_list.o \
|
||||||
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.o \
|
src/processor/stackwalker_ppc.o \
|
||||||
src/processor/stackwalker_ppc64.o \
|
src/processor/stackwalker_ppc64.o \
|
||||||
src/processor/stackwalker_sparc.o \
|
src/processor/stackwalker_sparc.o \
|
||||||
@ -685,6 +770,19 @@ src_processor_map_serializers_unittest_LDADD = \
|
|||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||||
|
|
||||||
|
src_processor_microdump_processor_unittest_SOURCES = \
|
||||||
|
src/processor/microdump_processor_unittest.cc \
|
||||||
|
src/testing/gtest/src/gtest-all.cc \
|
||||||
|
src/testing/src/gmock-all.cc
|
||||||
|
src_processor_microdump_processor_unittest_CPPFLAGS = \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-I$(top_srcdir)/src/testing/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest \
|
||||||
|
-I$(top_srcdir)/src/testing
|
||||||
|
src_processor_microdump_processor_unittest_LDADD = \
|
||||||
|
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||||
|
|
||||||
src_processor_minidump_processor_unittest_SOURCES = \
|
src_processor_minidump_processor_unittest_SOURCES = \
|
||||||
src/processor/minidump_processor_unittest.cc \
|
src/processor/minidump_processor_unittest.cc \
|
||||||
src/testing/gtest/src/gtest-all.cc \
|
src/testing/gtest/src/gtest-all.cc \
|
||||||
@ -701,7 +799,10 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||||||
src/processor/call_stack.o \
|
src/processor/call_stack.o \
|
||||||
src/processor/cfi_frame_info.o \
|
src/processor/cfi_frame_info.o \
|
||||||
src/processor/disassembler_x86.o \
|
src/processor/disassembler_x86.o \
|
||||||
|
src/processor/dump_context.o \
|
||||||
|
src/processor/dump_object.o \
|
||||||
src/processor/exploitability.o \
|
src/processor/exploitability.o \
|
||||||
|
src/processor/exploitability_linux.o \
|
||||||
src/processor/exploitability_win.o \
|
src/processor/exploitability_win.o \
|
||||||
src/processor/logging.o \
|
src/processor/logging.o \
|
||||||
src/processor/minidump_processor.o \
|
src/processor/minidump_processor.o \
|
||||||
@ -709,10 +810,14 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
src/processor/process_state.o \
|
src/processor/process_state.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
|
src/processor/stackwalker_address_list.o \
|
||||||
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.o \
|
src/processor/stackwalker_ppc.o \
|
||||||
src/processor/stackwalker_ppc64.o \
|
src/processor/stackwalker_ppc64.o \
|
||||||
src/processor/stackwalker_sparc.o \
|
src/processor/stackwalker_sparc.o \
|
||||||
@ -736,6 +841,8 @@ src_processor_minidump_unittest_CPPFLAGS = \
|
|||||||
-I$(top_srcdir)/src/testing
|
-I$(top_srcdir)/src/testing
|
||||||
src_processor_minidump_unittest_LDADD = \
|
src_processor_minidump_unittest_LDADD = \
|
||||||
src/processor/basic_code_modules.o \
|
src/processor/basic_code_modules.o \
|
||||||
|
src/processor/dump_context.o \
|
||||||
|
src/processor/dump_object.o \
|
||||||
src/processor/logging.o \
|
src/processor/logging.o \
|
||||||
src/processor/minidump.o \
|
src/processor/minidump.o \
|
||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
@ -829,15 +936,20 @@ src_processor_stackwalker_selftest_LDADD = \
|
|||||||
src/processor/call_stack.o \
|
src/processor/call_stack.o \
|
||||||
src/processor/disassembler_x86.o \
|
src/processor/disassembler_x86.o \
|
||||||
src/processor/exploitability.o \
|
src/processor/exploitability.o \
|
||||||
|
src/processor/exploitability_linux.o \
|
||||||
src/processor/exploitability_win.o \
|
src/processor/exploitability_win.o \
|
||||||
src/processor/logging.o \
|
src/processor/logging.o \
|
||||||
src/processor/minidump.o \
|
src/processor/minidump.o \
|
||||||
src/processor/pathname_stripper.o \
|
src/processor/pathname_stripper.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
|
src/processor/stackwalker_address_list.o \
|
||||||
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.o \
|
src/processor/stackwalker_ppc.o \
|
||||||
src/processor/stackwalker_ppc64.o \
|
src/processor/stackwalker_ppc64.o \
|
||||||
src/processor/stackwalker_sparc.o \
|
src/processor/stackwalker_sparc.o \
|
||||||
@ -877,6 +989,54 @@ src_processor_stackwalker_arm_unittest_CPPFLAGS = \
|
|||||||
-I$(top_srcdir)/src/testing/gtest \
|
-I$(top_srcdir)/src/testing/gtest \
|
||||||
-I$(top_srcdir)/src/testing
|
-I$(top_srcdir)/src/testing
|
||||||
|
|
||||||
|
src_processor_stackwalker_arm64_unittest_SOURCES = \
|
||||||
|
src/common/test_assembler.cc \
|
||||||
|
src/processor/stackwalker_arm64_unittest.cc \
|
||||||
|
src/testing/gtest/src/gtest-all.cc \
|
||||||
|
src/testing/gtest/src/gtest_main.cc \
|
||||||
|
src/testing/src/gmock-all.cc
|
||||||
|
src_processor_stackwalker_arm64_unittest_LDADD = \
|
||||||
|
src/libbreakpad.a \
|
||||||
|
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||||
|
src_processor_stackwalker_arm64_unittest_CPPFLAGS = \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-I$(top_srcdir)/src/testing/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest \
|
||||||
|
-I$(top_srcdir)/src/testing
|
||||||
|
|
||||||
|
src_processor_stackwalker_address_list_unittest_SOURCES = \
|
||||||
|
src/common/test_assembler.cc \
|
||||||
|
src/processor/stackwalker_address_list_unittest.cc \
|
||||||
|
src/testing/gtest/src/gtest-all.cc \
|
||||||
|
src/testing/gtest/src/gtest_main.cc \
|
||||||
|
src/testing/src/gmock-all.cc
|
||||||
|
src_processor_stackwalker_address_list_unittest_LDADD = \
|
||||||
|
src/libbreakpad.a \
|
||||||
|
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||||
|
src_processor_stackwalker_address_list_unittest_CPPFLAGS = \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-I$(top_srcdir)/src/testing/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest \
|
||||||
|
-I$(top_srcdir)/src/testing
|
||||||
|
|
||||||
|
src_processor_stackwalker_mips_unittest_SOURCES = \
|
||||||
|
src/common/test_assembler.cc \
|
||||||
|
src/processor/stackwalker_mips_unittest.cc \
|
||||||
|
src/testing/gtest/src/gtest-all.cc \
|
||||||
|
src/testing/gtest/src/gtest_main.cc \
|
||||||
|
src/testing/src/gmock-all.cc
|
||||||
|
src_processor_stackwalker_mips_unittest_LDADD = \
|
||||||
|
src/libbreakpad.a \
|
||||||
|
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||||
|
src_processor_stackwalker_mips_unittest_CPPFLAGS = \
|
||||||
|
-I$(top_srcdir)/src \
|
||||||
|
-I$(top_srcdir)/src/testing/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest/include \
|
||||||
|
-I$(top_srcdir)/src/testing/gtest \
|
||||||
|
-I$(top_srcdir)/src/testing
|
||||||
|
|
||||||
src_processor_stackwalker_x86_unittest_SOURCES = \
|
src_processor_stackwalker_x86_unittest_SOURCES = \
|
||||||
src/common/test_assembler.cc \
|
src/common/test_assembler.cc \
|
||||||
src/processor/stackwalker_x86_unittest.cc \
|
src/processor/stackwalker_x86_unittest.cc \
|
||||||
@ -933,6 +1093,8 @@ src_processor_minidump_dump_SOURCES = \
|
|||||||
src/processor/minidump_dump.cc
|
src/processor/minidump_dump.cc
|
||||||
src_processor_minidump_dump_LDADD = \
|
src_processor_minidump_dump_LDADD = \
|
||||||
src/processor/basic_code_modules.o \
|
src/processor/basic_code_modules.o \
|
||||||
|
src/processor/dump_context.o \
|
||||||
|
src/processor/dump_object.o \
|
||||||
src/processor/logging.o \
|
src/processor/logging.o \
|
||||||
src/processor/minidump.o \
|
src/processor/minidump.o \
|
||||||
src/processor/pathname_stripper.o
|
src/processor/pathname_stripper.o
|
||||||
@ -946,7 +1108,10 @@ src_processor_minidump_stackwalk_LDADD = \
|
|||||||
src/processor/call_stack.o \
|
src/processor/call_stack.o \
|
||||||
src/processor/cfi_frame_info.o \
|
src/processor/cfi_frame_info.o \
|
||||||
src/processor/disassembler_x86.o \
|
src/processor/disassembler_x86.o \
|
||||||
|
src/processor/dump_context.o \
|
||||||
|
src/processor/dump_object.o \
|
||||||
src/processor/exploitability.o \
|
src/processor/exploitability.o \
|
||||||
|
src/processor/exploitability_linux.o \
|
||||||
src/processor/exploitability_win.o \
|
src/processor/exploitability_win.o \
|
||||||
src/processor/logging.o \
|
src/processor/logging.o \
|
||||||
src/processor/minidump.o \
|
src/processor/minidump.o \
|
||||||
@ -955,10 +1120,14 @@ src_processor_minidump_stackwalk_LDADD = \
|
|||||||
src/processor/process_state.o \
|
src/processor/process_state.o \
|
||||||
src/processor/simple_symbol_supplier.o \
|
src/processor/simple_symbol_supplier.o \
|
||||||
src/processor/source_line_resolver_base.o \
|
src/processor/source_line_resolver_base.o \
|
||||||
|
src/processor/stack_frame_cpu.o \
|
||||||
src/processor/stack_frame_symbolizer.o \
|
src/processor/stack_frame_symbolizer.o \
|
||||||
src/processor/stackwalker.o \
|
src/processor/stackwalker.o \
|
||||||
src/processor/stackwalker_amd64.o \
|
src/processor/stackwalker_amd64.o \
|
||||||
src/processor/stackwalker_arm.o \
|
src/processor/stackwalker_arm.o \
|
||||||
|
src/processor/stackwalker_arm64.o \
|
||||||
|
src/processor/stackwalker_address_list.o \
|
||||||
|
src/processor/stackwalker_mips.o \
|
||||||
src/processor/stackwalker_ppc.o \
|
src/processor/stackwalker_ppc.o \
|
||||||
src/processor/stackwalker_ppc64.o \
|
src/processor/stackwalker_ppc64.o \
|
||||||
src/processor/stackwalker_sparc.o \
|
src/processor/stackwalker_sparc.o \
|
||||||
@ -1021,6 +1190,7 @@ EXTRA_DIST = \
|
|||||||
src/client/windows/sender/crash_report_sender.vcproj \
|
src/client/windows/sender/crash_report_sender.vcproj \
|
||||||
src/common/convert_UTF.c \
|
src/common/convert_UTF.c \
|
||||||
src/common/convert_UTF.h \
|
src/common/convert_UTF.h \
|
||||||
|
src/common/linux/crc32.cc \
|
||||||
src/common/linux/dump_symbols.cc \
|
src/common/linux/dump_symbols.cc \
|
||||||
src/common/linux/dump_symbols.h \
|
src/common/linux/dump_symbols.h \
|
||||||
src/common/linux/elf_symbols_to_module.cc \
|
src/common/linux/elf_symbols_to_module.cc \
|
||||||
|
File diff suppressed because it is too large
Load Diff
661
google-breakpad/aclocal.m4
vendored
661
google-breakpad/aclocal.m4
vendored
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
|||||||
/usr/share/automake-1.11/compile
|
|
347
google-breakpad/autotools/compile
Executable file
347
google-breakpad/autotools/compile
Executable file
@ -0,0 +1,347 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
# Wrapper for compilers which do not understand '-c -o'.
|
||||||
|
|
||||||
|
scriptversion=2012-10-14.11; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||||
|
# Written by Tom Tromey <tromey@cygnus.com>.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# This file is maintained in Automake, please report
|
||||||
|
# bugs to <bug-automake@gnu.org> or send patches to
|
||||||
|
# <automake-patches@gnu.org>.
|
||||||
|
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
|
||||||
|
# We need space, tab and new line, in precisely that order. Quoting is
|
||||||
|
# there to prevent tools from complaining about whitespace usage.
|
||||||
|
IFS=" "" $nl"
|
||||||
|
|
||||||
|
file_conv=
|
||||||
|
|
||||||
|
# func_file_conv build_file lazy
|
||||||
|
# Convert a $build file to $host form and store it in $file
|
||||||
|
# Currently only supports Windows hosts. If the determined conversion
|
||||||
|
# type is listed in (the comma separated) LAZY, no conversion will
|
||||||
|
# take place.
|
||||||
|
func_file_conv ()
|
||||||
|
{
|
||||||
|
file=$1
|
||||||
|
case $file in
|
||||||
|
/ | /[!/]*) # absolute file, and not a UNC file
|
||||||
|
if test -z "$file_conv"; then
|
||||||
|
# lazily determine how to convert abs files
|
||||||
|
case `uname -s` in
|
||||||
|
MINGW*)
|
||||||
|
file_conv=mingw
|
||||||
|
;;
|
||||||
|
CYGWIN*)
|
||||||
|
file_conv=cygwin
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
file_conv=wine
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
case $file_conv/,$2, in
|
||||||
|
*,$file_conv,*)
|
||||||
|
;;
|
||||||
|
mingw/*)
|
||||||
|
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
|
||||||
|
;;
|
||||||
|
cygwin/*)
|
||||||
|
file=`cygpath -m "$file" || echo "$file"`
|
||||||
|
;;
|
||||||
|
wine/*)
|
||||||
|
file=`winepath -w "$file" || echo "$file"`
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_dashL linkdir
|
||||||
|
# Make cl look for libraries in LINKDIR
|
||||||
|
func_cl_dashL ()
|
||||||
|
{
|
||||||
|
func_file_conv "$1"
|
||||||
|
if test -z "$lib_path"; then
|
||||||
|
lib_path=$file
|
||||||
|
else
|
||||||
|
lib_path="$lib_path;$file"
|
||||||
|
fi
|
||||||
|
linker_opts="$linker_opts -LIBPATH:$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_dashl library
|
||||||
|
# Do a library search-path lookup for cl
|
||||||
|
func_cl_dashl ()
|
||||||
|
{
|
||||||
|
lib=$1
|
||||||
|
found=no
|
||||||
|
save_IFS=$IFS
|
||||||
|
IFS=';'
|
||||||
|
for dir in $lib_path $LIB
|
||||||
|
do
|
||||||
|
IFS=$save_IFS
|
||||||
|
if $shared && test -f "$dir/$lib.dll.lib"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/$lib.dll.lib
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if test -f "$dir/$lib.lib"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/$lib.lib
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
if test -f "$dir/lib$lib.a"; then
|
||||||
|
found=yes
|
||||||
|
lib=$dir/lib$lib.a
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
IFS=$save_IFS
|
||||||
|
|
||||||
|
if test "$found" != yes; then
|
||||||
|
lib=$lib.lib
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# func_cl_wrapper cl arg...
|
||||||
|
# Adjust compile command to suit cl
|
||||||
|
func_cl_wrapper ()
|
||||||
|
{
|
||||||
|
# Assume a capable shell
|
||||||
|
lib_path=
|
||||||
|
shared=:
|
||||||
|
linker_opts=
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$eat"; then
|
||||||
|
eat=
|
||||||
|
else
|
||||||
|
case $1 in
|
||||||
|
-o)
|
||||||
|
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||||
|
eat=1
|
||||||
|
case $2 in
|
||||||
|
*.o | *.[oO][bB][jJ])
|
||||||
|
func_file_conv "$2"
|
||||||
|
set x "$@" -Fo"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
func_file_conv "$2"
|
||||||
|
set x "$@" -Fe"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
-I)
|
||||||
|
eat=1
|
||||||
|
func_file_conv "$2" mingw
|
||||||
|
set x "$@" -I"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-I*)
|
||||||
|
func_file_conv "${1#-I}" mingw
|
||||||
|
set x "$@" -I"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-l)
|
||||||
|
eat=1
|
||||||
|
func_cl_dashl "$2"
|
||||||
|
set x "$@" "$lib"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-l*)
|
||||||
|
func_cl_dashl "${1#-l}"
|
||||||
|
set x "$@" "$lib"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-L)
|
||||||
|
eat=1
|
||||||
|
func_cl_dashL "$2"
|
||||||
|
;;
|
||||||
|
-L*)
|
||||||
|
func_cl_dashL "${1#-L}"
|
||||||
|
;;
|
||||||
|
-static)
|
||||||
|
shared=false
|
||||||
|
;;
|
||||||
|
-Wl,*)
|
||||||
|
arg=${1#-Wl,}
|
||||||
|
save_ifs="$IFS"; IFS=','
|
||||||
|
for flag in $arg; do
|
||||||
|
IFS="$save_ifs"
|
||||||
|
linker_opts="$linker_opts $flag"
|
||||||
|
done
|
||||||
|
IFS="$save_ifs"
|
||||||
|
;;
|
||||||
|
-Xlinker)
|
||||||
|
eat=1
|
||||||
|
linker_opts="$linker_opts $2"
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
|
||||||
|
func_file_conv "$1"
|
||||||
|
set x "$@" -Tp"$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
|
||||||
|
func_file_conv "$1" mingw
|
||||||
|
set x "$@" "$file"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
if test -n "$linker_opts"; then
|
||||||
|
linker_opts="-link$linker_opts"
|
||||||
|
fi
|
||||||
|
exec "$@" $linker_opts
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
eat=
|
||||||
|
|
||||||
|
case $1 in
|
||||||
|
'')
|
||||||
|
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||||
|
exit 1;
|
||||||
|
;;
|
||||||
|
-h | --h*)
|
||||||
|
cat <<\EOF
|
||||||
|
Usage: compile [--help] [--version] PROGRAM [ARGS]
|
||||||
|
|
||||||
|
Wrapper for compilers which do not understand '-c -o'.
|
||||||
|
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
|
||||||
|
arguments, and rename the output as expected.
|
||||||
|
|
||||||
|
If you are trying to build a whole package this is not the
|
||||||
|
right script to run: please start by reading the file 'INSTALL'.
|
||||||
|
|
||||||
|
Report bugs to <bug-automake@gnu.org>.
|
||||||
|
EOF
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
-v | --v*)
|
||||||
|
echo "compile $scriptversion"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
|
||||||
|
func_cl_wrapper "$@" # Doesn't return...
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
ofile=
|
||||||
|
cfile=
|
||||||
|
|
||||||
|
for arg
|
||||||
|
do
|
||||||
|
if test -n "$eat"; then
|
||||||
|
eat=
|
||||||
|
else
|
||||||
|
case $1 in
|
||||||
|
-o)
|
||||||
|
# configure might choose to run compile as 'compile cc -o foo foo.c'.
|
||||||
|
# So we strip '-o arg' only if arg is an object.
|
||||||
|
eat=1
|
||||||
|
case $2 in
|
||||||
|
*.o | *.obj)
|
||||||
|
ofile=$2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" -o "$2"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*.c)
|
||||||
|
cfile=$1
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
set x "$@" "$1"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if test -z "$ofile" || test -z "$cfile"; then
|
||||||
|
# If no '-o' option was seen then we might have been invoked from a
|
||||||
|
# pattern rule where we don't need one. That is ok -- this is a
|
||||||
|
# normal compilation that the losing compiler can handle. If no
|
||||||
|
# '.c' file was seen then we are probably linking. That is also
|
||||||
|
# ok.
|
||||||
|
exec "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Name of file we expect compiler to create.
|
||||||
|
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
|
||||||
|
|
||||||
|
# Create the lock directory.
|
||||||
|
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
|
||||||
|
# that we are using for the .o file. Also, base the name on the expected
|
||||||
|
# object file name, since that is what matters with a parallel build.
|
||||||
|
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
|
||||||
|
while true; do
|
||||||
|
if mkdir "$lockdir" >/dev/null 2>&1; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 1
|
||||||
|
done
|
||||||
|
# FIXME: race condition here if user kills between mkdir and trap.
|
||||||
|
trap "rmdir '$lockdir'; exit 1" 1 2 15
|
||||||
|
|
||||||
|
# Run the compile.
|
||||||
|
"$@"
|
||||||
|
ret=$?
|
||||||
|
|
||||||
|
if test -f "$cofile"; then
|
||||||
|
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
|
||||||
|
elif test -f "${cofile}bj"; then
|
||||||
|
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rmdir "$lockdir"
|
||||||
|
exit $ret
|
||||||
|
|
||||||
|
# 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-time-zone: "UTC"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
337
google-breakpad/autotools/config.guess
vendored
337
google-breakpad/autotools/config.guess
vendored
@ -1,14 +1,12 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Attempt to guess a canonical system name.
|
# Attempt to guess a canonical system name.
|
||||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
|
# Copyright 1992-2014 Free Software Foundation, Inc.
|
||||||
# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
|
||||||
# 2011, 2012 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
timestamp='2012-06-17'
|
timestamp='2014-03-23'
|
||||||
|
|
||||||
# This file is free software; you can redistribute it and/or modify it
|
# This file is free software; you can redistribute it and/or modify it
|
||||||
# under the terms of the GNU General Public License as published by
|
# under the terms of the GNU General Public License as published by
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful, but
|
# This program is distributed in the hope that it will be useful, but
|
||||||
@ -22,19 +20,17 @@ timestamp='2012-06-17'
|
|||||||
# As a special exception to the GNU General Public License, if you
|
# As a special exception to the GNU General Public License, if you
|
||||||
# distribute this file as part of a program that contains a
|
# distribute this file as part of a program that contains a
|
||||||
# configuration script generated by Autoconf, you may include it under
|
# configuration script generated by Autoconf, you may include it under
|
||||||
# the same distribution terms that you use for the rest of that program.
|
# the same distribution terms that you use for the rest of that
|
||||||
|
# program. This Exception is an additional permission under section 7
|
||||||
|
# of the GNU General Public License, version 3 ("GPLv3").
|
||||||
# Originally written by Per Bothner. Please send patches (context
|
|
||||||
# diff format) to <config-patches@gnu.org> and include a ChangeLog
|
|
||||||
# entry.
|
|
||||||
#
|
#
|
||||||
# This script attempts to guess a canonical system name similar to
|
# Originally written by Per Bothner.
|
||||||
# config.sub. If it succeeds, it prints the system name on stdout, and
|
|
||||||
# exits with 0. Otherwise, it exits with 1.
|
|
||||||
#
|
#
|
||||||
# You can get the latest version of this script from:
|
# You can get the latest version of this script from:
|
||||||
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
|
# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
|
||||||
|
#
|
||||||
|
# Please send patches with a ChangeLog entry to config-patches@gnu.org.
|
||||||
|
|
||||||
|
|
||||||
me=`echo "$0" | sed -e 's,.*/,,'`
|
me=`echo "$0" | sed -e 's,.*/,,'`
|
||||||
|
|
||||||
@ -54,9 +50,7 @@ version="\
|
|||||||
GNU config.guess ($timestamp)
|
GNU config.guess ($timestamp)
|
||||||
|
|
||||||
Originally written by Per Bothner.
|
Originally written by Per Bothner.
|
||||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
|
Copyright 1992-2014 Free Software Foundation, Inc.
|
||||||
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
|
|
||||||
Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This is free software; see the source for copying conditions. There is NO
|
This is free software; see the source for copying conditions. There is NO
|
||||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||||
@ -138,6 +132,27 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
|
|||||||
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
|
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
|
||||||
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
|
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
|
||||||
|
|
||||||
|
case "${UNAME_SYSTEM}" in
|
||||||
|
Linux|GNU|GNU/*)
|
||||||
|
# If the system lacks a compiler, then just pick glibc.
|
||||||
|
# We could probably try harder.
|
||||||
|
LIBC=gnu
|
||||||
|
|
||||||
|
eval $set_cc_for_build
|
||||||
|
cat <<-EOF > $dummy.c
|
||||||
|
#include <features.h>
|
||||||
|
#if defined(__UCLIBC__)
|
||||||
|
LIBC=uclibc
|
||||||
|
#elif defined(__dietlibc__)
|
||||||
|
LIBC=dietlibc
|
||||||
|
#else
|
||||||
|
LIBC=gnu
|
||||||
|
#endif
|
||||||
|
EOF
|
||||||
|
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
# Note: order is significant - the case branches are not exclusive.
|
# Note: order is significant - the case branches are not exclusive.
|
||||||
|
|
||||||
case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
||||||
@ -306,7 +321,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
|
|||||||
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
|
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
|
||||||
echo arm-acorn-riscix${UNAME_RELEASE}
|
echo arm-acorn-riscix${UNAME_RELEASE}
|
||||||
exit ;;
|
exit ;;
|
||||||
arm:riscos:*:*|arm:RISCOS:*:*)
|
arm*:riscos:*:*|arm*:RISCOS:*:*)
|
||||||
echo arm-unknown-riscos
|
echo arm-unknown-riscos
|
||||||
exit ;;
|
exit ;;
|
||||||
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
|
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
|
||||||
@ -805,10 +820,13 @@ EOF
|
|||||||
i*:CYGWIN*:*)
|
i*:CYGWIN*:*)
|
||||||
echo ${UNAME_MACHINE}-pc-cygwin
|
echo ${UNAME_MACHINE}-pc-cygwin
|
||||||
exit ;;
|
exit ;;
|
||||||
|
*:MINGW64*:*)
|
||||||
|
echo ${UNAME_MACHINE}-pc-mingw64
|
||||||
|
exit ;;
|
||||||
*:MINGW*:*)
|
*:MINGW*:*)
|
||||||
echo ${UNAME_MACHINE}-pc-mingw32
|
echo ${UNAME_MACHINE}-pc-mingw32
|
||||||
exit ;;
|
exit ;;
|
||||||
i*:MSYS*:*)
|
*:MSYS*:*)
|
||||||
echo ${UNAME_MACHINE}-pc-msys
|
echo ${UNAME_MACHINE}-pc-msys
|
||||||
exit ;;
|
exit ;;
|
||||||
i*:windows32*:*)
|
i*:windows32*:*)
|
||||||
@ -856,21 +874,21 @@ EOF
|
|||||||
exit ;;
|
exit ;;
|
||||||
*:GNU:*:*)
|
*:GNU:*:*)
|
||||||
# the GNU system
|
# the GNU system
|
||||||
echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
|
echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
|
||||||
exit ;;
|
exit ;;
|
||||||
*:GNU/*:*:*)
|
*:GNU/*:*:*)
|
||||||
# other systems with GNU libc and userland
|
# other systems with GNU libc and userland
|
||||||
echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
|
echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
i*86:Minix:*:*)
|
i*86:Minix:*:*)
|
||||||
echo ${UNAME_MACHINE}-pc-minix
|
echo ${UNAME_MACHINE}-pc-minix
|
||||||
exit ;;
|
exit ;;
|
||||||
aarch64:Linux:*:*)
|
aarch64:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
aarch64_be:Linux:*:*)
|
aarch64_be:Linux:*:*)
|
||||||
UNAME_MACHINE=aarch64_be
|
UNAME_MACHINE=aarch64_be
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
alpha:Linux:*:*)
|
alpha:Linux:*:*)
|
||||||
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
|
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
|
||||||
@ -883,59 +901,54 @@ EOF
|
|||||||
EV68*) UNAME_MACHINE=alphaev68 ;;
|
EV68*) UNAME_MACHINE=alphaev68 ;;
|
||||||
esac
|
esac
|
||||||
objdump --private-headers /bin/sh | grep -q ld.so.1
|
objdump --private-headers /bin/sh | grep -q ld.so.1
|
||||||
if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
|
if test "$?" = 0 ; then LIBC="gnulibc1" ; fi
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
|
exit ;;
|
||||||
|
arc:Linux:*:* | arceb:Linux:*:*)
|
||||||
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
arm*:Linux:*:*)
|
arm*:Linux:*:*)
|
||||||
eval $set_cc_for_build
|
eval $set_cc_for_build
|
||||||
if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
|
if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
|
||||||
| grep -q __ARM_EABI__
|
| grep -q __ARM_EABI__
|
||||||
then
|
then
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
else
|
else
|
||||||
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
|
if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
|
||||||
| grep -q __ARM_PCS_VFP
|
| grep -q __ARM_PCS_VFP
|
||||||
then
|
then
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnueabi
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
|
||||||
else
|
else
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnueabihf
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
exit ;;
|
exit ;;
|
||||||
avr32*:Linux:*:*)
|
avr32*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
cris:Linux:*:*)
|
cris:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-axis-linux-gnu
|
echo ${UNAME_MACHINE}-axis-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
crisv32:Linux:*:*)
|
crisv32:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-axis-linux-gnu
|
echo ${UNAME_MACHINE}-axis-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
frv:Linux:*:*)
|
frv:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
hexagon:Linux:*:*)
|
hexagon:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
i*86:Linux:*:*)
|
i*86:Linux:*:*)
|
||||||
LIBC=gnu
|
echo ${UNAME_MACHINE}-pc-linux-${LIBC}
|
||||||
eval $set_cc_for_build
|
|
||||||
sed 's/^ //' << EOF >$dummy.c
|
|
||||||
#ifdef __dietlibc__
|
|
||||||
LIBC=dietlibc
|
|
||||||
#endif
|
|
||||||
EOF
|
|
||||||
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
|
|
||||||
echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
|
|
||||||
exit ;;
|
exit ;;
|
||||||
ia64:Linux:*:*)
|
ia64:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
m32r*:Linux:*:*)
|
m32r*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
m68*:Linux:*:*)
|
m68*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
mips:Linux:*:* | mips64:Linux:*:*)
|
mips:Linux:*:* | mips64:Linux:*:*)
|
||||||
eval $set_cc_for_build
|
eval $set_cc_for_build
|
||||||
@ -954,54 +967,74 @@ EOF
|
|||||||
#endif
|
#endif
|
||||||
EOF
|
EOF
|
||||||
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
|
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
|
||||||
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
|
test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
|
||||||
;;
|
;;
|
||||||
or32:Linux:*:*)
|
openrisc*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo or1k-unknown-linux-${LIBC}
|
||||||
|
exit ;;
|
||||||
|
or32:Linux:*:* | or1k*:Linux:*:*)
|
||||||
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
padre:Linux:*:*)
|
padre:Linux:*:*)
|
||||||
echo sparc-unknown-linux-gnu
|
echo sparc-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
parisc64:Linux:*:* | hppa64:Linux:*:*)
|
parisc64:Linux:*:* | hppa64:Linux:*:*)
|
||||||
echo hppa64-unknown-linux-gnu
|
echo hppa64-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
parisc:Linux:*:* | hppa:Linux:*:*)
|
parisc:Linux:*:* | hppa:Linux:*:*)
|
||||||
# Look for CPU level
|
# Look for CPU level
|
||||||
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
|
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
|
||||||
PA7*) echo hppa1.1-unknown-linux-gnu ;;
|
PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
|
||||||
PA8*) echo hppa2.0-unknown-linux-gnu ;;
|
PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
|
||||||
*) echo hppa-unknown-linux-gnu ;;
|
*) echo hppa-unknown-linux-${LIBC} ;;
|
||||||
esac
|
esac
|
||||||
exit ;;
|
exit ;;
|
||||||
ppc64:Linux:*:*)
|
ppc64:Linux:*:*)
|
||||||
echo powerpc64-unknown-linux-gnu
|
echo powerpc64-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
ppc:Linux:*:*)
|
ppc:Linux:*:*)
|
||||||
echo powerpc-unknown-linux-gnu
|
echo powerpc-unknown-linux-${LIBC}
|
||||||
|
exit ;;
|
||||||
|
ppc64le:Linux:*:*)
|
||||||
|
echo powerpc64le-unknown-linux-${LIBC}
|
||||||
|
exit ;;
|
||||||
|
ppcle:Linux:*:*)
|
||||||
|
echo powerpcle-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
s390:Linux:*:* | s390x:Linux:*:*)
|
s390:Linux:*:* | s390x:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-ibm-linux
|
echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
sh64*:Linux:*:*)
|
sh64*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
sh*:Linux:*:*)
|
sh*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
sparc:Linux:*:* | sparc64:Linux:*:*)
|
sparc:Linux:*:* | sparc64:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
tile*:Linux:*:*)
|
tile*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
vax:Linux:*:*)
|
vax:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-dec-linux-gnu
|
echo ${UNAME_MACHINE}-dec-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
x86_64:Linux:*:*)
|
x86_64:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
eval $set_cc_for_build
|
||||||
|
X86_64_ABI=
|
||||||
|
# If there is a compiler, see if it is configured for 32-bit objects.
|
||||||
|
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
|
||||||
|
if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
|
||||||
|
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
|
||||||
|
grep IS_X32 >/dev/null
|
||||||
|
then
|
||||||
|
X86_64_ABI=x32
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
echo x86_64-unknown-linux-gnu${X86_64_ABI}
|
||||||
exit ;;
|
exit ;;
|
||||||
xtensa*:Linux:*:*)
|
xtensa*:Linux:*:*)
|
||||||
echo ${UNAME_MACHINE}-unknown-linux-gnu
|
echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
|
||||||
exit ;;
|
exit ;;
|
||||||
i*86:DYNIX/ptx:4*:*)
|
i*86:DYNIX/ptx:4*:*)
|
||||||
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
|
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
|
||||||
@ -1205,6 +1238,9 @@ EOF
|
|||||||
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
|
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
|
||||||
echo i586-pc-haiku
|
echo i586-pc-haiku
|
||||||
exit ;;
|
exit ;;
|
||||||
|
x86_64:Haiku:*:*)
|
||||||
|
echo x86_64-unknown-haiku
|
||||||
|
exit ;;
|
||||||
SX-4:SUPER-UX:*:*)
|
SX-4:SUPER-UX:*:*)
|
||||||
echo sx4-nec-superux${UNAME_RELEASE}
|
echo sx4-nec-superux${UNAME_RELEASE}
|
||||||
exit ;;
|
exit ;;
|
||||||
@ -1231,19 +1267,31 @@ EOF
|
|||||||
exit ;;
|
exit ;;
|
||||||
*:Darwin:*:*)
|
*:Darwin:*:*)
|
||||||
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
|
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
|
||||||
case $UNAME_PROCESSOR in
|
|
||||||
i386)
|
|
||||||
eval $set_cc_for_build
|
eval $set_cc_for_build
|
||||||
|
if test "$UNAME_PROCESSOR" = unknown ; then
|
||||||
|
UNAME_PROCESSOR=powerpc
|
||||||
|
fi
|
||||||
|
if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
|
||||||
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
|
if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
|
||||||
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
|
if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
|
||||||
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
|
(CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
|
||||||
grep IS_64BIT_ARCH >/dev/null
|
grep IS_64BIT_ARCH >/dev/null
|
||||||
then
|
then
|
||||||
UNAME_PROCESSOR="x86_64"
|
case $UNAME_PROCESSOR in
|
||||||
fi
|
i386) UNAME_PROCESSOR=x86_64 ;;
|
||||||
fi ;;
|
powerpc) UNAME_PROCESSOR=powerpc64 ;;
|
||||||
unknown) UNAME_PROCESSOR=powerpc ;;
|
|
||||||
esac
|
esac
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif test "$UNAME_PROCESSOR" = i386 ; then
|
||||||
|
# Avoid executing cc on OS X 10.9, as it ships with a stub
|
||||||
|
# that puts up a graphical alert prompting to install
|
||||||
|
# developer tools. Any system running Mac OS X 10.7 or
|
||||||
|
# later (Darwin 11 and later) is required to have a 64-bit
|
||||||
|
# processor. This is not true of the ARM version of Darwin
|
||||||
|
# that Apple uses in portable devices.
|
||||||
|
UNAME_PROCESSOR=x86_64
|
||||||
|
fi
|
||||||
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
|
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
|
||||||
exit ;;
|
exit ;;
|
||||||
*:procnto*:*:* | *:QNX:[0123456789]*:*)
|
*:procnto*:*:* | *:QNX:[0123456789]*:*)
|
||||||
@ -1334,157 +1382,6 @@ EOF
|
|||||||
exit ;;
|
exit ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
#echo '(No uname command or uname output not recognized.)' 1>&2
|
|
||||||
#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
|
|
||||||
|
|
||||||
eval $set_cc_for_build
|
|
||||||
cat >$dummy.c <<EOF
|
|
||||||
#ifdef _SEQUENT_
|
|
||||||
# include <sys/types.h>
|
|
||||||
# include <sys/utsname.h>
|
|
||||||
#endif
|
|
||||||
main ()
|
|
||||||
{
|
|
||||||
#if defined (sony)
|
|
||||||
#if defined (MIPSEB)
|
|
||||||
/* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
|
|
||||||
I don't know.... */
|
|
||||||
printf ("mips-sony-bsd\n"); exit (0);
|
|
||||||
#else
|
|
||||||
#include <sys/param.h>
|
|
||||||
printf ("m68k-sony-newsos%s\n",
|
|
||||||
#ifdef NEWSOS4
|
|
||||||
"4"
|
|
||||||
#else
|
|
||||||
""
|
|
||||||
#endif
|
|
||||||
); exit (0);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (__arm) && defined (__acorn) && defined (__unix)
|
|
||||||
printf ("arm-acorn-riscix\n"); exit (0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (hp300) && !defined (hpux)
|
|
||||||
printf ("m68k-hp-bsd\n"); exit (0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (NeXT)
|
|
||||||
#if !defined (__ARCHITECTURE__)
|
|
||||||
#define __ARCHITECTURE__ "m68k"
|
|
||||||
#endif
|
|
||||||
int version;
|
|
||||||
version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
|
|
||||||
if (version < 4)
|
|
||||||
printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
|
|
||||||
else
|
|
||||||
printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
|
|
||||||
exit (0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (MULTIMAX) || defined (n16)
|
|
||||||
#if defined (UMAXV)
|
|
||||||
printf ("ns32k-encore-sysv\n"); exit (0);
|
|
||||||
#else
|
|
||||||
#if defined (CMU)
|
|
||||||
printf ("ns32k-encore-mach\n"); exit (0);
|
|
||||||
#else
|
|
||||||
printf ("ns32k-encore-bsd\n"); exit (0);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (__386BSD__)
|
|
||||||
printf ("i386-pc-bsd\n"); exit (0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (sequent)
|
|
||||||
#if defined (i386)
|
|
||||||
printf ("i386-sequent-dynix\n"); exit (0);
|
|
||||||
#endif
|
|
||||||
#if defined (ns32000)
|
|
||||||
printf ("ns32k-sequent-dynix\n"); exit (0);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (_SEQUENT_)
|
|
||||||
struct utsname un;
|
|
||||||
|
|
||||||
uname(&un);
|
|
||||||
|
|
||||||
if (strncmp(un.version, "V2", 2) == 0) {
|
|
||||||
printf ("i386-sequent-ptx2\n"); exit (0);
|
|
||||||
}
|
|
||||||
if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
|
|
||||||
printf ("i386-sequent-ptx1\n"); exit (0);
|
|
||||||
}
|
|
||||||
printf ("i386-sequent-ptx\n"); exit (0);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (vax)
|
|
||||||
# if !defined (ultrix)
|
|
||||||
# include <sys/param.h>
|
|
||||||
# if defined (BSD)
|
|
||||||
# if BSD == 43
|
|
||||||
printf ("vax-dec-bsd4.3\n"); exit (0);
|
|
||||||
# else
|
|
||||||
# if BSD == 199006
|
|
||||||
printf ("vax-dec-bsd4.3reno\n"); exit (0);
|
|
||||||
# else
|
|
||||||
printf ("vax-dec-bsd\n"); exit (0);
|
|
||||||
# endif
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
printf ("vax-dec-bsd\n"); exit (0);
|
|
||||||
# endif
|
|
||||||
# else
|
|
||||||
printf ("vax-dec-ultrix\n"); exit (0);
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined (alliant) && defined (i860)
|
|
||||||
printf ("i860-alliant-bsd\n"); exit (0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
|
|
||||||
$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
|
|
||||||
{ echo "$SYSTEM_NAME"; exit; }
|
|
||||||
|
|
||||||
# Apollos put the system type in the environment.
|
|
||||||
|
|
||||||
test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
|
|
||||||
|
|
||||||
# Convex versions that predate uname can use getsysinfo(1)
|
|
||||||
|
|
||||||
if [ -x /usr/convex/getsysinfo ]
|
|
||||||
then
|
|
||||||
case `getsysinfo -f cpu_type` in
|
|
||||||
c1*)
|
|
||||||
echo c1-convex-bsd
|
|
||||||
exit ;;
|
|
||||||
c2*)
|
|
||||||
if getsysinfo -f scalar_acc
|
|
||||||
then echo c32-convex-bsd
|
|
||||||
else echo c2-convex-bsd
|
|
||||||
fi
|
|
||||||
exit ;;
|
|
||||||
c34*)
|
|
||||||
echo c34-convex-bsd
|
|
||||||
exit ;;
|
|
||||||
c38*)
|
|
||||||
echo c38-convex-bsd
|
|
||||||
exit ;;
|
|
||||||
c4*)
|
|
||||||
echo c4-convex-bsd
|
|
||||||
exit ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat >&2 <<EOF
|
cat >&2 <<EOF
|
||||||
$0: unable to guess system type
|
$0: unable to guess system type
|
||||||
|
|
||||||
|
130
google-breakpad/autotools/config.sub
vendored
130
google-breakpad/autotools/config.sub
vendored
@ -1,24 +1,18 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Configuration validation subroutine script.
|
# Configuration validation subroutine script.
|
||||||
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
|
# Copyright 1992-2014 Free Software Foundation, Inc.
|
||||||
# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
|
||||||
# 2011, 2012 Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
timestamp='2012-06-17'
|
timestamp='2014-07-28'
|
||||||
|
|
||||||
# This file is (in principle) common to ALL GNU software.
|
# This file is free software; you can redistribute it and/or modify it
|
||||||
# The presence of a machine in this file suggests that SOME GNU software
|
# under the terms of the GNU General Public License as published by
|
||||||
# can handle that machine. It does not imply ALL GNU software can.
|
# the Free Software Foundation; either version 3 of the License, or
|
||||||
#
|
|
||||||
# This file is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
# (at your option) any later version.
|
||||||
#
|
#
|
||||||
# This program is distributed in the hope that it will be useful,
|
# This program is distributed in the hope that it will be useful, but
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
# GNU General Public License for more details.
|
# General Public License for more details.
|
||||||
#
|
#
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
# along with this program; if not, see <http://www.gnu.org/licenses/>.
|
||||||
@ -26,11 +20,12 @@ timestamp='2012-06-17'
|
|||||||
# As a special exception to the GNU General Public License, if you
|
# As a special exception to the GNU General Public License, if you
|
||||||
# distribute this file as part of a program that contains a
|
# distribute this file as part of a program that contains a
|
||||||
# configuration script generated by Autoconf, you may include it under
|
# configuration script generated by Autoconf, you may include it under
|
||||||
# the same distribution terms that you use for the rest of that program.
|
# the same distribution terms that you use for the rest of that
|
||||||
|
# program. This Exception is an additional permission under section 7
|
||||||
|
# of the GNU General Public License, version 3 ("GPLv3").
|
||||||
|
|
||||||
|
|
||||||
# Please send patches to <config-patches@gnu.org>. Submit a context
|
# Please send patches with a ChangeLog entry to config-patches@gnu.org.
|
||||||
# diff and a properly formatted GNU ChangeLog entry.
|
|
||||||
#
|
#
|
||||||
# Configuration subroutine to validate and canonicalize a configuration type.
|
# Configuration subroutine to validate and canonicalize a configuration type.
|
||||||
# Supply the specified configuration type as an argument.
|
# Supply the specified configuration type as an argument.
|
||||||
@ -73,9 +68,7 @@ Report bugs and patches to <config-patches@gnu.org>."
|
|||||||
version="\
|
version="\
|
||||||
GNU config.sub ($timestamp)
|
GNU config.sub ($timestamp)
|
||||||
|
|
||||||
Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
|
Copyright 1992-2014 Free Software Foundation, Inc.
|
||||||
2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
|
|
||||||
Free Software Foundation, Inc.
|
|
||||||
|
|
||||||
This is free software; see the source for copying conditions. There is NO
|
This is free software; see the source for copying conditions. There is NO
|
||||||
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
|
||||||
@ -123,7 +116,7 @@ esac
|
|||||||
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
|
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
|
||||||
case $maybe_os in
|
case $maybe_os in
|
||||||
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
|
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
|
||||||
linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
|
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
|
||||||
knetbsd*-gnu* | netbsd*-gnu* | \
|
knetbsd*-gnu* | netbsd*-gnu* | \
|
||||||
kopensolaris*-gnu* | \
|
kopensolaris*-gnu* | \
|
||||||
storm-chaos* | os2-emx* | rtmk-nova*)
|
storm-chaos* | os2-emx* | rtmk-nova*)
|
||||||
@ -156,7 +149,7 @@ case $os in
|
|||||||
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
|
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
|
||||||
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
|
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
|
||||||
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
|
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
|
||||||
-apple | -axis | -knuth | -cray | -microblaze)
|
-apple | -axis | -knuth | -cray | -microblaze*)
|
||||||
os=
|
os=
|
||||||
basic_machine=$1
|
basic_machine=$1
|
||||||
;;
|
;;
|
||||||
@ -259,21 +252,24 @@ case $basic_machine in
|
|||||||
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
|
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
|
||||||
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
|
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
|
||||||
| am33_2.0 \
|
| am33_2.0 \
|
||||||
| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
|
| arc | arceb \
|
||||||
|
| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
|
||||||
|
| avr | avr32 \
|
||||||
| be32 | be64 \
|
| be32 | be64 \
|
||||||
| bfin \
|
| bfin \
|
||||||
| c4x | clipper \
|
| c4x | c8051 | clipper \
|
||||||
| d10v | d30v | dlx | dsp16xx \
|
| d10v | d30v | dlx | dsp16xx | dvp \
|
||||||
| epiphany \
|
| epiphany \
|
||||||
| fido | fr30 | frv \
|
| fido | fr30 | frv \
|
||||||
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
|
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
|
||||||
| hexagon \
|
| hexagon \
|
||||||
| i370 | i860 | i960 | ia64 \
|
| i370 | i860 | i960 | ia64 \
|
||||||
| ip2k | iq2000 \
|
| ip2k | iq2000 \
|
||||||
|
| k1om \
|
||||||
| le32 | le64 \
|
| le32 | le64 \
|
||||||
| lm32 \
|
| lm32 \
|
||||||
| m32c | m32r | m32rle | m68000 | m68k | m88k \
|
| m32c | m32r | m32rle | m68000 | m68k | m88k \
|
||||||
| maxq | mb | microblaze | mcore | mep | metag \
|
| maxq | mb | microblaze | microblazeel | mcore | mep | metag \
|
||||||
| mips | mipsbe | mipseb | mipsel | mipsle \
|
| mips | mipsbe | mipseb | mipsel | mipsle \
|
||||||
| mips16 \
|
| mips16 \
|
||||||
| mips64 | mips64el \
|
| mips64 | mips64el \
|
||||||
@ -287,20 +283,22 @@ case $basic_machine in
|
|||||||
| mips64vr5900 | mips64vr5900el \
|
| mips64vr5900 | mips64vr5900el \
|
||||||
| mipsisa32 | mipsisa32el \
|
| mipsisa32 | mipsisa32el \
|
||||||
| mipsisa32r2 | mipsisa32r2el \
|
| mipsisa32r2 | mipsisa32r2el \
|
||||||
|
| mipsisa32r6 | mipsisa32r6el \
|
||||||
| mipsisa64 | mipsisa64el \
|
| mipsisa64 | mipsisa64el \
|
||||||
| mipsisa64r2 | mipsisa64r2el \
|
| mipsisa64r2 | mipsisa64r2el \
|
||||||
|
| mipsisa64r6 | mipsisa64r6el \
|
||||||
| mipsisa64sb1 | mipsisa64sb1el \
|
| mipsisa64sb1 | mipsisa64sb1el \
|
||||||
| mipsisa64sr71k | mipsisa64sr71kel \
|
| mipsisa64sr71k | mipsisa64sr71kel \
|
||||||
|
| mipsr5900 | mipsr5900el \
|
||||||
| mipstx39 | mipstx39el \
|
| mipstx39 | mipstx39el \
|
||||||
| mn10200 | mn10300 \
|
| mn10200 | mn10300 \
|
||||||
| moxie \
|
| moxie \
|
||||||
| mt \
|
| mt \
|
||||||
| msp430 \
|
| msp430 \
|
||||||
| nds32 | nds32le | nds32be \
|
| nds32 | nds32le | nds32be \
|
||||||
| nios | nios2 \
|
| nios | nios2 | nios2eb | nios2el \
|
||||||
| ns16k | ns32k \
|
| ns16k | ns32k \
|
||||||
| open8 \
|
| open8 | or1k | or1knd | or32 \
|
||||||
| or32 \
|
|
||||||
| pdp10 | pdp11 | pj | pjl \
|
| pdp10 | pdp11 | pj | pjl \
|
||||||
| powerpc | powerpc64 | powerpc64le | powerpcle \
|
| powerpc | powerpc64 | powerpc64le | powerpcle \
|
||||||
| pyramid \
|
| pyramid \
|
||||||
@ -328,7 +326,7 @@ case $basic_machine in
|
|||||||
c6x)
|
c6x)
|
||||||
basic_machine=tic6x-unknown
|
basic_machine=tic6x-unknown
|
||||||
;;
|
;;
|
||||||
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip)
|
m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
|
||||||
basic_machine=$basic_machine-unknown
|
basic_machine=$basic_machine-unknown
|
||||||
os=-none
|
os=-none
|
||||||
;;
|
;;
|
||||||
@ -370,13 +368,13 @@ case $basic_machine in
|
|||||||
| aarch64-* | aarch64_be-* \
|
| aarch64-* | aarch64_be-* \
|
||||||
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
|
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
|
||||||
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
|
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
|
||||||
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
|
| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
|
||||||
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
|
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
|
||||||
| avr-* | avr32-* \
|
| avr-* | avr32-* \
|
||||||
| be32-* | be64-* \
|
| be32-* | be64-* \
|
||||||
| bfin-* | bs2000-* \
|
| bfin-* | bs2000-* \
|
||||||
| c[123]* | c30-* | [cjt]90-* | c4x-* \
|
| c[123]* | c30-* | [cjt]90-* | c4x-* \
|
||||||
| clipper-* | craynv-* | cydra-* \
|
| c8051-* | clipper-* | craynv-* | cydra-* \
|
||||||
| d10v-* | d30v-* | dlx-* \
|
| d10v-* | d30v-* | dlx-* \
|
||||||
| elxsi-* \
|
| elxsi-* \
|
||||||
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
|
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
|
||||||
@ -385,11 +383,13 @@ case $basic_machine in
|
|||||||
| hexagon-* \
|
| hexagon-* \
|
||||||
| i*86-* | i860-* | i960-* | ia64-* \
|
| i*86-* | i860-* | i960-* | ia64-* \
|
||||||
| ip2k-* | iq2000-* \
|
| ip2k-* | iq2000-* \
|
||||||
|
| k1om-* \
|
||||||
| le32-* | le64-* \
|
| le32-* | le64-* \
|
||||||
| lm32-* \
|
| lm32-* \
|
||||||
| m32c-* | m32r-* | m32rle-* \
|
| m32c-* | m32r-* | m32rle-* \
|
||||||
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
|
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
|
||||||
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
|
| m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
|
||||||
|
| microblaze-* | microblazeel-* \
|
||||||
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
|
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
|
||||||
| mips16-* \
|
| mips16-* \
|
||||||
| mips64-* | mips64el-* \
|
| mips64-* | mips64el-* \
|
||||||
@ -403,18 +403,22 @@ case $basic_machine in
|
|||||||
| mips64vr5900-* | mips64vr5900el-* \
|
| mips64vr5900-* | mips64vr5900el-* \
|
||||||
| mipsisa32-* | mipsisa32el-* \
|
| mipsisa32-* | mipsisa32el-* \
|
||||||
| mipsisa32r2-* | mipsisa32r2el-* \
|
| mipsisa32r2-* | mipsisa32r2el-* \
|
||||||
|
| mipsisa32r6-* | mipsisa32r6el-* \
|
||||||
| mipsisa64-* | mipsisa64el-* \
|
| mipsisa64-* | mipsisa64el-* \
|
||||||
| mipsisa64r2-* | mipsisa64r2el-* \
|
| mipsisa64r2-* | mipsisa64r2el-* \
|
||||||
|
| mipsisa64r6-* | mipsisa64r6el-* \
|
||||||
| mipsisa64sb1-* | mipsisa64sb1el-* \
|
| mipsisa64sb1-* | mipsisa64sb1el-* \
|
||||||
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
|
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
|
||||||
|
| mipsr5900-* | mipsr5900el-* \
|
||||||
| mipstx39-* | mipstx39el-* \
|
| mipstx39-* | mipstx39el-* \
|
||||||
| mmix-* \
|
| mmix-* \
|
||||||
| mt-* \
|
| mt-* \
|
||||||
| msp430-* \
|
| msp430-* \
|
||||||
| nds32-* | nds32le-* | nds32be-* \
|
| nds32-* | nds32le-* | nds32be-* \
|
||||||
| nios-* | nios2-* \
|
| nios-* | nios2-* | nios2eb-* | nios2el-* \
|
||||||
| none-* | np1-* | ns16k-* | ns32k-* \
|
| none-* | np1-* | ns16k-* | ns32k-* \
|
||||||
| open8-* \
|
| open8-* \
|
||||||
|
| or1k*-* \
|
||||||
| orion-* \
|
| orion-* \
|
||||||
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
|
||||||
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
|
||||||
@ -788,11 +792,15 @@ case $basic_machine in
|
|||||||
basic_machine=ns32k-utek
|
basic_machine=ns32k-utek
|
||||||
os=-sysv
|
os=-sysv
|
||||||
;;
|
;;
|
||||||
microblaze)
|
microblaze*)
|
||||||
basic_machine=microblaze-xilinx
|
basic_machine=microblaze-xilinx
|
||||||
;;
|
;;
|
||||||
|
mingw64)
|
||||||
|
basic_machine=x86_64-pc
|
||||||
|
os=-mingw64
|
||||||
|
;;
|
||||||
mingw32)
|
mingw32)
|
||||||
basic_machine=i386-pc
|
basic_machine=i686-pc
|
||||||
os=-mingw32
|
os=-mingw32
|
||||||
;;
|
;;
|
||||||
mingw32ce)
|
mingw32ce)
|
||||||
@ -806,6 +814,24 @@ case $basic_machine in
|
|||||||
basic_machine=m68k-atari
|
basic_machine=m68k-atari
|
||||||
os=-mint
|
os=-mint
|
||||||
;;
|
;;
|
||||||
|
mipsEE* | ee | ps2)
|
||||||
|
basic_machine=mips64r5900el-scei
|
||||||
|
case $os in
|
||||||
|
-linux*)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
os=-elf
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
iop)
|
||||||
|
basic_machine=mipsel-scei
|
||||||
|
os=-irx
|
||||||
|
;;
|
||||||
|
dvp)
|
||||||
|
basic_machine=dvp-scei
|
||||||
|
os=-elf
|
||||||
|
;;
|
||||||
mips3*-*)
|
mips3*-*)
|
||||||
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
|
basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
|
||||||
;;
|
;;
|
||||||
@ -820,6 +846,10 @@ case $basic_machine in
|
|||||||
basic_machine=powerpc-unknown
|
basic_machine=powerpc-unknown
|
||||||
os=-morphos
|
os=-morphos
|
||||||
;;
|
;;
|
||||||
|
moxiebox)
|
||||||
|
basic_machine=moxie-unknown
|
||||||
|
os=-moxiebox
|
||||||
|
;;
|
||||||
msdos)
|
msdos)
|
||||||
basic_machine=i386-pc
|
basic_machine=i386-pc
|
||||||
os=-msdos
|
os=-msdos
|
||||||
@ -828,7 +858,7 @@ case $basic_machine in
|
|||||||
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
|
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
|
||||||
;;
|
;;
|
||||||
msys)
|
msys)
|
||||||
basic_machine=i386-pc
|
basic_machine=i686-pc
|
||||||
os=-msys
|
os=-msys
|
||||||
;;
|
;;
|
||||||
mvs)
|
mvs)
|
||||||
@ -1019,7 +1049,11 @@ case $basic_machine in
|
|||||||
basic_machine=i586-unknown
|
basic_machine=i586-unknown
|
||||||
os=-pw32
|
os=-pw32
|
||||||
;;
|
;;
|
||||||
rdos)
|
rdos | rdos64)
|
||||||
|
basic_machine=x86_64-pc
|
||||||
|
os=-rdos
|
||||||
|
;;
|
||||||
|
rdos32)
|
||||||
basic_machine=i386-pc
|
basic_machine=i386-pc
|
||||||
os=-rdos
|
os=-rdos
|
||||||
;;
|
;;
|
||||||
@ -1346,7 +1380,7 @@ case $os in
|
|||||||
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
|
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
|
||||||
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
|
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
|
||||||
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
|
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
|
||||||
| -sym* | -kopensolaris* \
|
| -sym* | -kopensolaris* | -plan9* \
|
||||||
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
|
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
|
||||||
| -aos* | -aros* \
|
| -aos* | -aros* \
|
||||||
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
|
||||||
@ -1359,16 +1393,16 @@ case $os in
|
|||||||
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
|
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
|
||||||
| -chorusos* | -chorusrdb* | -cegcc* \
|
| -chorusos* | -chorusrdb* | -cegcc* \
|
||||||
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||||
| -mingw32* | -linux-gnu* | -linux-android* \
|
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
|
||||||
| -linux-newlib* | -linux-uclibc* \
|
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
|
||||||
| -uxpv* | -beos* | -mpeix* | -udk* \
|
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
|
||||||
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
||||||
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
|
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
|
||||||
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
|
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -irx* \
|
||||||
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
|
||||||
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
|
||||||
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
|
||||||
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
|
| -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* | -tirtos*)
|
||||||
# Remember, each alternative MUST END IN *, to match a version number.
|
# Remember, each alternative MUST END IN *, to match a version number.
|
||||||
;;
|
;;
|
||||||
-qnx*)
|
-qnx*)
|
||||||
@ -1492,9 +1526,6 @@ case $os in
|
|||||||
-aros*)
|
-aros*)
|
||||||
os=-aros
|
os=-aros
|
||||||
;;
|
;;
|
||||||
-kaos*)
|
|
||||||
os=-kaos
|
|
||||||
;;
|
|
||||||
-zvmoe)
|
-zvmoe)
|
||||||
os=-zvmoe
|
os=-zvmoe
|
||||||
;;
|
;;
|
||||||
@ -1543,6 +1574,9 @@ case $basic_machine in
|
|||||||
c4x-* | tic4x-*)
|
c4x-* | tic4x-*)
|
||||||
os=-coff
|
os=-coff
|
||||||
;;
|
;;
|
||||||
|
c8051-*)
|
||||||
|
os=-elf
|
||||||
|
;;
|
||||||
hexagon-*)
|
hexagon-*)
|
||||||
os=-elf
|
os=-elf
|
||||||
;;
|
;;
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# depcomp - compile a program generating dependencies as side-effects
|
# depcomp - compile a program generating dependencies as side-effects
|
||||||
|
|
||||||
scriptversion=2006-10-15.18
|
scriptversion=2013-05-30.07; # UTC
|
||||||
|
|
||||||
# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 Free Software
|
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
|
||||||
# Foundation, Inc.
|
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -17,9 +16,7 @@ scriptversion=2006-10-15.18
|
|||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
||||||
# 02110-1301, USA.
|
|
||||||
|
|
||||||
# As a special exception to the GNU General Public License, if you
|
# As a special exception to the GNU General Public License, if you
|
||||||
# distribute this file as part of a program that contains a
|
# distribute this file as part of a program that contains a
|
||||||
@ -30,7 +27,7 @@ scriptversion=2006-10-15.18
|
|||||||
|
|
||||||
case $1 in
|
case $1 in
|
||||||
'')
|
'')
|
||||||
echo "$0: No command. Try \`$0 --help' for more information." 1>&2
|
echo "$0: No command. Try '$0 --help' for more information." 1>&2
|
||||||
exit 1;
|
exit 1;
|
||||||
;;
|
;;
|
||||||
-h | --h*)
|
-h | --h*)
|
||||||
@ -42,11 +39,11 @@ as side-effects.
|
|||||||
|
|
||||||
Environment variables:
|
Environment variables:
|
||||||
depmode Dependency tracking mode.
|
depmode Dependency tracking mode.
|
||||||
source Source file read by `PROGRAMS ARGS'.
|
source Source file read by 'PROGRAMS ARGS'.
|
||||||
object Object file output by `PROGRAMS ARGS'.
|
object Object file output by 'PROGRAMS ARGS'.
|
||||||
DEPDIR directory where to store dependencies.
|
DEPDIR directory where to store dependencies.
|
||||||
depfile Dependency file to output.
|
depfile Dependency file to output.
|
||||||
tmpdepfile Temporary file to use when outputing dependencies.
|
tmpdepfile Temporary file to use when outputting dependencies.
|
||||||
libtool Whether libtool is used (yes/no).
|
libtool Whether libtool is used (yes/no).
|
||||||
|
|
||||||
Report bugs to <bug-automake@gnu.org>.
|
Report bugs to <bug-automake@gnu.org>.
|
||||||
@ -59,6 +56,66 @@ EOF
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
# Get the directory component of the given path, and save it in the
|
||||||
|
# global variables '$dir'. Note that this directory component will
|
||||||
|
# be either empty or ending with a '/' character. This is deliberate.
|
||||||
|
set_dir_from ()
|
||||||
|
{
|
||||||
|
case $1 in
|
||||||
|
*/*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
|
||||||
|
*) dir=;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the suffix-stripped basename of the given path, and save it the
|
||||||
|
# global variable '$base'.
|
||||||
|
set_base_from ()
|
||||||
|
{
|
||||||
|
base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
|
||||||
|
}
|
||||||
|
|
||||||
|
# If no dependency file was actually created by the compiler invocation,
|
||||||
|
# we still have to create a dummy depfile, to avoid errors with the
|
||||||
|
# Makefile "include basename.Plo" scheme.
|
||||||
|
make_dummy_depfile ()
|
||||||
|
{
|
||||||
|
echo "#dummy" > "$depfile"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Factor out some common post-processing of the generated depfile.
|
||||||
|
# Requires the auxiliary global variable '$tmpdepfile' to be set.
|
||||||
|
aix_post_process_depfile ()
|
||||||
|
{
|
||||||
|
# If the compiler actually managed to produce a dependency file,
|
||||||
|
# post-process it.
|
||||||
|
if test -f "$tmpdepfile"; then
|
||||||
|
# Each line is of the form 'foo.o: dependency.h'.
|
||||||
|
# Do two passes, one to just change these to
|
||||||
|
# $object: dependency.h
|
||||||
|
# and one to simply output
|
||||||
|
# dependency.h:
|
||||||
|
# which is needed to avoid the deleted-header problem.
|
||||||
|
{ sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
|
||||||
|
sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
|
||||||
|
} > "$depfile"
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
else
|
||||||
|
make_dummy_depfile
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# A tabulation character.
|
||||||
|
tab=' '
|
||||||
|
# A newline character.
|
||||||
|
nl='
|
||||||
|
'
|
||||||
|
# Character ranges might be problematic outside the C locale.
|
||||||
|
# These definitions help.
|
||||||
|
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
|
lower=abcdefghijklmnopqrstuvwxyz
|
||||||
|
digits=0123456789
|
||||||
|
alpha=${upper}${lower}
|
||||||
|
|
||||||
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
if test -z "$depmode" || test -z "$source" || test -z "$object"; then
|
||||||
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
echo "depcomp: Variables source, object and depmode must be set" 1>&2
|
||||||
exit 1
|
exit 1
|
||||||
@ -71,6 +128,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
|
|||||||
|
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
|
|
||||||
|
# Avoid interferences from the environment.
|
||||||
|
gccflag= dashmflag=
|
||||||
|
|
||||||
# Some modes work just like other modes, but use different flags. We
|
# Some modes work just like other modes, but use different flags. We
|
||||||
# parameterize here, but still list the modes in the big case below,
|
# 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
|
# to make depend.m4 easier to write. Note that we *cannot* use a case
|
||||||
@ -87,6 +147,29 @@ if test "$depmode" = dashXmstdout; then
|
|||||||
depmode=dashmstdout
|
depmode=dashmstdout
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
cygpath_u="cygpath -u -f -"
|
||||||
|
if test "$depmode" = msvcmsys; then
|
||||||
|
# This is just like msvisualcpp but w/o cygpath translation.
|
||||||
|
# Just convert the backslash-escaped backslashes to single forward
|
||||||
|
# slashes to satisfy depend.m4
|
||||||
|
cygpath_u='sed s,\\\\,/,g'
|
||||||
|
depmode=msvisualcpp
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = msvc7msys; then
|
||||||
|
# This is just like msvc7 but w/o cygpath translation.
|
||||||
|
# Just convert the backslash-escaped backslashes to single forward
|
||||||
|
# slashes to satisfy depend.m4
|
||||||
|
cygpath_u='sed s,\\\\,/,g'
|
||||||
|
depmode=msvc7
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "$depmode" = xlc; then
|
||||||
|
# IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information.
|
||||||
|
gccflag=-qmakedep=gcc,-MF
|
||||||
|
depmode=gcc
|
||||||
|
fi
|
||||||
|
|
||||||
case "$depmode" in
|
case "$depmode" in
|
||||||
gcc3)
|
gcc3)
|
||||||
## gcc 3 implements dependency tracking that does exactly what
|
## gcc 3 implements dependency tracking that does exactly what
|
||||||
@ -107,8 +190,7 @@ gcc3)
|
|||||||
done
|
done
|
||||||
"$@"
|
"$@"
|
||||||
stat=$?
|
stat=$?
|
||||||
if test $stat -eq 0; then :
|
if test $stat -ne 0; then
|
||||||
else
|
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
exit $stat
|
exit $stat
|
||||||
fi
|
fi
|
||||||
@ -116,13 +198,17 @@ gcc3)
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
gcc)
|
gcc)
|
||||||
|
## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
|
||||||
|
## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
|
||||||
|
## (see the conditional assignment to $gccflag above).
|
||||||
## There are various ways to get dependency output from gcc. Here's
|
## There are various ways to get dependency output from gcc. Here's
|
||||||
## why we pick this rather obscure method:
|
## why we pick this rather obscure method:
|
||||||
## - Don't want to use -MD because we'd like the dependencies to end
|
## - 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.
|
## up in a subdir. Having to rename by hand is ugly.
|
||||||
## (We might end up doing this anyway to support other compilers.)
|
## (We might end up doing this anyway to support other compilers.)
|
||||||
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like
|
||||||
## -MM, not -M (despite what the docs say).
|
## -MM, not -M (despite what the docs say). Also, it might not be
|
||||||
|
## supported by the other compilers which use the 'gcc' depmode.
|
||||||
## - Using -M directly means running the compiler twice (even worse
|
## - Using -M directly means running the compiler twice (even worse
|
||||||
## than renaming).
|
## than renaming).
|
||||||
if test -z "$gccflag"; then
|
if test -z "$gccflag"; then
|
||||||
@ -130,31 +216,31 @@ gcc)
|
|||||||
fi
|
fi
|
||||||
"$@" -Wp,"$gccflag$tmpdepfile"
|
"$@" -Wp,"$gccflag$tmpdepfile"
|
||||||
stat=$?
|
stat=$?
|
||||||
if test $stat -eq 0; then :
|
if test $stat -ne 0; then
|
||||||
else
|
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
exit $stat
|
exit $stat
|
||||||
fi
|
fi
|
||||||
rm -f "$depfile"
|
rm -f "$depfile"
|
||||||
echo "$object : \\" > "$depfile"
|
echo "$object : \\" > "$depfile"
|
||||||
alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
|
# The second -e expression handles DOS-style file names with drive
|
||||||
## The second -e expression handles DOS-style file names with drive letters.
|
# letters.
|
||||||
sed -e 's/^[^:]*: / /' \
|
sed -e 's/^[^:]*: / /' \
|
||||||
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
-e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile"
|
||||||
## This next piece of magic avoids the `deleted header file' problem.
|
## 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
|
## 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
|
## is deleted, the dependency causes make to die (because there is
|
||||||
## typically no way to rebuild the header). We avoid this by adding
|
## typically no way to rebuild the header). We avoid this by adding
|
||||||
## dummy dependencies for each header file. Too bad gcc doesn't do
|
## dummy dependencies for each header file. Too bad gcc doesn't do
|
||||||
## this for us directly.
|
## this for us directly.
|
||||||
tr ' ' '
|
## Some versions of gcc put a space before the ':'. On the theory
|
||||||
' < "$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
|
## that the space means something, we add a space to the output as
|
||||||
## well.
|
## well. hp depmode also adds that space, but also prefixes the VPATH
|
||||||
|
## to the object. Take care to not repeat it in the output.
|
||||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
## Some versions of the HPUX 10.20 sed can't process this invocation
|
||||||
## correctly. Breaking it into two sed invocations is a workaround.
|
## correctly. Breaking it into two sed invocations is a workaround.
|
||||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -172,8 +258,7 @@ sgi)
|
|||||||
"$@" -MDupdate "$tmpdepfile"
|
"$@" -MDupdate "$tmpdepfile"
|
||||||
fi
|
fi
|
||||||
stat=$?
|
stat=$?
|
||||||
if test $stat -eq 0; then :
|
if test $stat -ne 0; then
|
||||||
else
|
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
exit $stat
|
exit $stat
|
||||||
fi
|
fi
|
||||||
@ -181,99 +266,156 @@ sgi)
|
|||||||
|
|
||||||
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files
|
||||||
echo "$object : \\" > "$depfile"
|
echo "$object : \\" > "$depfile"
|
||||||
|
|
||||||
# Clip off the initial element (the dependent). Don't try to be
|
# 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
|
# clever and replace this with sed code, as IRIX sed won't handle
|
||||||
# lines with more than a fixed number of characters (4096 in
|
# 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;
|
# 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
|
# the IRIX cc adds comments like '#:fec' to the end of the
|
||||||
# dependency line.
|
# dependency line.
|
||||||
tr ' ' '
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
' < "$tmpdepfile" \
|
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \
|
||||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
|
| tr "$nl" ' ' >> "$depfile"
|
||||||
tr '
|
echo >> "$depfile"
|
||||||
' ' ' >> $depfile
|
|
||||||
echo >> $depfile
|
|
||||||
|
|
||||||
# The second pass generates a dummy entry for each header file.
|
# The second pass generates a dummy entry for each header file.
|
||||||
tr ' ' '
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
' < "$tmpdepfile" \
|
|
||||||
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
| sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
|
||||||
>> $depfile
|
>> "$depfile"
|
||||||
else
|
else
|
||||||
# The sourcefile does not contain any dependencies, so just
|
make_dummy_depfile
|
||||||
# store a dummy comment line, to avoid errors with the Makefile
|
|
||||||
# "include basename.Plo" scheme.
|
|
||||||
echo "#dummy" > "$depfile"
|
|
||||||
fi
|
fi
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
xlc)
|
||||||
|
# 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
|
||||||
|
;;
|
||||||
|
|
||||||
aix)
|
aix)
|
||||||
# The C for AIX Compiler uses -M and outputs the dependencies
|
# The C for AIX Compiler uses -M and outputs the dependencies
|
||||||
# in a .u file. In older versions, this file always lives in the
|
# in a .u file. In older versions, this file always lives in the
|
||||||
# current directory. Also, the AIX compiler puts `$object:' at the
|
# current directory. Also, the AIX compiler puts '$object:' at the
|
||||||
# start of each line; $object doesn't have directory information.
|
# start of each line; $object doesn't have directory information.
|
||||||
# Version 6 uses the directory in both cases.
|
# Version 6 uses the directory in both cases.
|
||||||
stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'`
|
set_dir_from "$object"
|
||||||
tmpdepfile="$stripped.u"
|
set_base_from "$object"
|
||||||
if test "$libtool" = yes; then
|
if test "$libtool" = yes; then
|
||||||
|
tmpdepfile1=$dir$base.u
|
||||||
|
tmpdepfile2=$base.u
|
||||||
|
tmpdepfile3=$dir.libs/$base.u
|
||||||
"$@" -Wc,-M
|
"$@" -Wc,-M
|
||||||
else
|
else
|
||||||
|
tmpdepfile1=$dir$base.u
|
||||||
|
tmpdepfile2=$dir$base.u
|
||||||
|
tmpdepfile3=$dir$base.u
|
||||||
"$@" -M
|
"$@" -M
|
||||||
fi
|
fi
|
||||||
stat=$?
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
if test -f "$tmpdepfile"; then :
|
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
else
|
|
||||||
stripped=`echo "$stripped" | sed 's,^.*/,,'`
|
|
||||||
tmpdepfile="$stripped.u"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test $stat -eq 0; then :
|
|
||||||
else
|
|
||||||
rm -f "$tmpdepfile"
|
|
||||||
exit $stat
|
exit $stat
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -f "$tmpdepfile"; then
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
outname="$stripped.o"
|
do
|
||||||
# Each line is of the form `foo.o: dependent.h'.
|
test -f "$tmpdepfile" && break
|
||||||
# Do two passes, one to just change these to
|
done
|
||||||
# `$object: dependent.h' and one to simply `dependent.h:'.
|
aix_post_process_depfile
|
||||||
sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile"
|
;;
|
||||||
sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile"
|
|
||||||
else
|
tcc)
|
||||||
# The sourcefile does not contain any dependencies, so just
|
# tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
|
||||||
# store a dummy comment line, to avoid errors with the Makefile
|
# FIXME: That version still under development at the moment of writing.
|
||||||
# "include basename.Plo" scheme.
|
# Make that this statement remains true also for stable, released
|
||||||
echo "#dummy" > "$depfile"
|
# versions.
|
||||||
|
# It will wrap lines (doesn't matter whether long or short) with a
|
||||||
|
# trailing '\', as in:
|
||||||
|
#
|
||||||
|
# foo.o : \
|
||||||
|
# foo.c \
|
||||||
|
# foo.h \
|
||||||
|
#
|
||||||
|
# It will put a trailing '\' even on the last line, and will use leading
|
||||||
|
# spaces rather than leading tabs (at least since its commit 0394caf7
|
||||||
|
# "Emit spaces for -MD").
|
||||||
|
"$@" -MD -MF "$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
if test $stat -ne 0; then
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
fi
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
# Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
|
||||||
|
# We have to change lines of the first kind to '$object: \'.
|
||||||
|
sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
|
||||||
|
# And for each line of the second kind, we have to emit a 'dep.h:'
|
||||||
|
# dummy dependency, to avoid the deleted-header problem.
|
||||||
|
sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
icc)
|
## The order of this option in the case statement is important, since the
|
||||||
# Intel's C compiler understands `-MD -MF file'. However on
|
## shell code in configure will try each of these formats in the order
|
||||||
# icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c
|
## listed in this file. A plain '-MD' option would be understood by many
|
||||||
# ICC 7.0 will fill foo.d with something like
|
## compilers, so we must ensure this comes after the gcc and icc options.
|
||||||
# foo.o: sub/foo.c
|
pgcc)
|
||||||
# foo.o: sub/foo.h
|
# Portland's C compiler understands '-MD'.
|
||||||
# which is wrong. We want:
|
# Will always output deps to 'file.d' where file is the root name of the
|
||||||
# sub/foo.o: sub/foo.c
|
# source file under compilation, even if file resides in a subdirectory.
|
||||||
# sub/foo.o: sub/foo.h
|
# The object file name does not affect the name of the '.d' file.
|
||||||
# sub/foo.c:
|
# pgcc 10.2 will output
|
||||||
# sub/foo.h:
|
|
||||||
# ICC 7.1 will output
|
|
||||||
# foo.o: sub/foo.c sub/foo.h
|
# foo.o: sub/foo.c sub/foo.h
|
||||||
# and will wrap long lines using \ :
|
# and will wrap long lines using '\' :
|
||||||
# foo.o: sub/foo.c ... \
|
# foo.o: sub/foo.c ... \
|
||||||
# sub/foo.h ... \
|
# sub/foo.h ... \
|
||||||
# ...
|
# ...
|
||||||
|
set_dir_from "$object"
|
||||||
|
# Use the source, not the object, to determine the base name, since
|
||||||
|
# that's sadly what pgcc will do too.
|
||||||
|
set_base_from "$source"
|
||||||
|
tmpdepfile=$base.d
|
||||||
|
|
||||||
"$@" -MD -MF "$tmpdepfile"
|
# For projects that build the same source file twice into different object
|
||||||
|
# files, the pgcc approach of using the *source* file root name can cause
|
||||||
|
# problems in parallel builds. Use a locking strategy to avoid stomping on
|
||||||
|
# the same $tmpdepfile.
|
||||||
|
lockdir=$base.d-lock
|
||||||
|
trap "
|
||||||
|
echo '$0: caught signal, cleaning up...' >&2
|
||||||
|
rmdir '$lockdir'
|
||||||
|
exit 1
|
||||||
|
" 1 2 13 15
|
||||||
|
numtries=100
|
||||||
|
i=$numtries
|
||||||
|
while test $i -gt 0; do
|
||||||
|
# mkdir is a portable test-and-set.
|
||||||
|
if mkdir "$lockdir" 2>/dev/null; then
|
||||||
|
# This process acquired the lock.
|
||||||
|
"$@" -MD
|
||||||
stat=$?
|
stat=$?
|
||||||
if test $stat -eq 0; then :
|
# Release the lock.
|
||||||
|
rmdir "$lockdir"
|
||||||
|
break
|
||||||
else
|
else
|
||||||
|
# If the lock is being held by a different process, wait
|
||||||
|
# until the winning process is done or we timeout.
|
||||||
|
while test -d "$lockdir" && test $i -gt 0; do
|
||||||
|
sleep 1
|
||||||
|
i=`expr $i - 1`
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
i=`expr $i - 1`
|
||||||
|
done
|
||||||
|
trap - 1 2 13 15
|
||||||
|
if test $i -le 0; then
|
||||||
|
echo "$0: failed to acquire lock after $numtries attempts" >&2
|
||||||
|
echo "$0: check lockdir '$lockdir'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $stat -ne 0; then
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
exit $stat
|
exit $stat
|
||||||
fi
|
fi
|
||||||
@ -285,8 +427,8 @@ icc)
|
|||||||
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
|
||||||
# Some versions of the HPUX 10.20 sed can't process this invocation
|
# Some versions of the HPUX 10.20 sed can't process this invocation
|
||||||
# correctly. Breaking it into two sed invocations is a workaround.
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
|
sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
|
||||||
sed -e 's/$/ :/' >> "$depfile"
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -297,9 +439,8 @@ hp2)
|
|||||||
# 'foo.d', which lands next to the object file, wherever that
|
# 'foo.d', which lands next to the object file, wherever that
|
||||||
# happens to be.
|
# happens to be.
|
||||||
# Much of this is similar to the tru64 case; see comments there.
|
# Much of this is similar to the tru64 case; see comments there.
|
||||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
set_dir_from "$object"
|
||||||
test "x$dir" = "x$object" && dir=
|
set_base_from "$object"
|
||||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
|
||||||
if test "$libtool" = yes; then
|
if test "$libtool" = yes; then
|
||||||
tmpdepfile1=$dir$base.d
|
tmpdepfile1=$dir$base.d
|
||||||
tmpdepfile2=$dir.libs/$base.d
|
tmpdepfile2=$dir.libs/$base.d
|
||||||
@ -310,8 +451,7 @@ hp2)
|
|||||||
"$@" +Maked
|
"$@" +Maked
|
||||||
fi
|
fi
|
||||||
stat=$?
|
stat=$?
|
||||||
if test $stat -eq 0; then :
|
if test $stat -ne 0; then
|
||||||
else
|
|
||||||
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
rm -f "$tmpdepfile1" "$tmpdepfile2"
|
||||||
exit $stat
|
exit $stat
|
||||||
fi
|
fi
|
||||||
@ -321,71 +461,106 @@ hp2)
|
|||||||
test -f "$tmpdepfile" && break
|
test -f "$tmpdepfile" && break
|
||||||
done
|
done
|
||||||
if test -f "$tmpdepfile"; then
|
if test -f "$tmpdepfile"; then
|
||||||
sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
|
sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
|
||||||
# Add `dependent.h:' lines.
|
# Add 'dependent.h:' lines.
|
||||||
sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile"
|
sed -ne '2,${
|
||||||
|
s/^ *//
|
||||||
|
s/ \\*$//
|
||||||
|
s/$/:/
|
||||||
|
p
|
||||||
|
}' "$tmpdepfile" >> "$depfile"
|
||||||
else
|
else
|
||||||
echo "#dummy" > "$depfile"
|
make_dummy_depfile
|
||||||
fi
|
fi
|
||||||
rm -f "$tmpdepfile" "$tmpdepfile2"
|
rm -f "$tmpdepfile" "$tmpdepfile2"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
tru64)
|
tru64)
|
||||||
# The Tru64 compiler uses -MD to generate dependencies as a side
|
# The Tru64 compiler uses -MD to generate dependencies as a side
|
||||||
# effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'.
|
# 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
|
# 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.
|
# dependencies in 'foo.d' instead, so we check for that too.
|
||||||
# Subdirectories are respected.
|
# Subdirectories are respected.
|
||||||
dir=`echo "$object" | sed -e 's|/[^/]*$|/|'`
|
set_dir_from "$object"
|
||||||
test "x$dir" = "x$object" && dir=
|
set_base_from "$object"
|
||||||
base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'`
|
|
||||||
|
|
||||||
if test "$libtool" = yes; then
|
if test "$libtool" = yes; then
|
||||||
# With Tru64 cc, shared objects can also be used to make a
|
# Libtool generates 2 separate objects for the 2 libraries. These
|
||||||
# static library. This mechanism is used in libtool 1.4 series to
|
# two compilations output dependencies in $dir.libs/$base.o.d and
|
||||||
# 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
|
# in $dir$base.o.d. We have to check for both files, because
|
||||||
# one of the two compilations can be disabled. We should prefer
|
# 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
|
# $dir$base.o.d over $dir.libs/$base.o.d because the latter is
|
||||||
# automatically cleaned when .libs/ is deleted, while ignoring
|
# automatically cleaned when .libs/ is deleted, while ignoring
|
||||||
# the former would cause a distcleancheck panic.
|
# the former would cause a distcleancheck panic.
|
||||||
tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4
|
tmpdepfile1=$dir$base.o.d # libtool 1.5
|
||||||
tmpdepfile2=$dir$base.o.d # libtool 1.5
|
tmpdepfile2=$dir.libs/$base.o.d # Likewise.
|
||||||
tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5
|
tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
|
||||||
tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504
|
|
||||||
"$@" -Wc,-MD
|
"$@" -Wc,-MD
|
||||||
else
|
else
|
||||||
tmpdepfile1=$dir$base.o.d
|
tmpdepfile1=$dir$base.d
|
||||||
tmpdepfile2=$dir$base.d
|
tmpdepfile2=$dir$base.d
|
||||||
tmpdepfile3=$dir$base.d
|
tmpdepfile3=$dir$base.d
|
||||||
tmpdepfile4=$dir$base.d
|
|
||||||
"$@" -MD
|
"$@" -MD
|
||||||
fi
|
fi
|
||||||
|
|
||||||
stat=$?
|
stat=$?
|
||||||
if test $stat -eq 0; then :
|
if test $stat -ne 0; then
|
||||||
else
|
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
|
||||||
exit $stat
|
exit $stat
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4"
|
for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
|
||||||
do
|
do
|
||||||
test -f "$tmpdepfile" && break
|
test -f "$tmpdepfile" && break
|
||||||
done
|
done
|
||||||
if test -f "$tmpdepfile"; then
|
# Same post-processing that is required for AIX mode.
|
||||||
sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile"
|
aix_post_process_depfile
|
||||||
# That's a tab and a space in the [].
|
;;
|
||||||
sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
|
|
||||||
|
msvc7)
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
showIncludes=-Wc,-showIncludes
|
||||||
else
|
else
|
||||||
echo "#dummy" > "$depfile"
|
showIncludes=-showIncludes
|
||||||
fi
|
fi
|
||||||
|
"$@" $showIncludes > "$tmpdepfile"
|
||||||
|
stat=$?
|
||||||
|
grep -v '^Note: including file: ' "$tmpdepfile"
|
||||||
|
if test $stat -ne 0; then
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
|
exit $stat
|
||||||
|
fi
|
||||||
|
rm -f "$depfile"
|
||||||
|
echo "$object : \\" > "$depfile"
|
||||||
|
# The first sed program below extracts the file names and escapes
|
||||||
|
# backslashes for cygpath. The second sed program outputs the file
|
||||||
|
# name when reading, but also accumulates all include files in the
|
||||||
|
# hold buffer in order to output them again at the end. This only
|
||||||
|
# works with sed implementations that can handle large buffers.
|
||||||
|
sed < "$tmpdepfile" -n '
|
||||||
|
/^Note: including file: *\(.*\)/ {
|
||||||
|
s//\1/
|
||||||
|
s/\\/\\\\/g
|
||||||
|
p
|
||||||
|
}' | $cygpath_u | sort -u | sed -n '
|
||||||
|
s/ /\\ /g
|
||||||
|
s/\(.*\)/'"$tab"'\1 \\/p
|
||||||
|
s/.\(.*\) \\/\1:/
|
||||||
|
H
|
||||||
|
$ {
|
||||||
|
s/.*/'"$tab"'/
|
||||||
|
G
|
||||||
|
p
|
||||||
|
}' >> "$depfile"
|
||||||
|
echo >> "$depfile" # make sure the fragment doesn't end with a backslash
|
||||||
|
rm -f "$tmpdepfile"
|
||||||
|
;;
|
||||||
|
|
||||||
|
msvc7msys)
|
||||||
|
# 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
|
||||||
;;
|
;;
|
||||||
|
|
||||||
#nosideeffect)
|
#nosideeffect)
|
||||||
@ -399,13 +574,13 @@ dashmstdout)
|
|||||||
|
|
||||||
# Remove the call to Libtool.
|
# Remove the call to Libtool.
|
||||||
if test "$libtool" = yes; then
|
if test "$libtool" = yes; then
|
||||||
while test $1 != '--mode=compile'; do
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
shift
|
shift
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove `-o $object'.
|
# Remove '-o $object'.
|
||||||
IFS=" "
|
IFS=" "
|
||||||
for arg
|
for arg
|
||||||
do
|
do
|
||||||
@ -425,18 +600,18 @@ dashmstdout)
|
|||||||
done
|
done
|
||||||
|
|
||||||
test -z "$dashmflag" && dashmflag=-M
|
test -z "$dashmflag" && dashmflag=-M
|
||||||
# Require at least two characters before searching for `:'
|
# Require at least two characters before searching for ':'
|
||||||
# in the target name. This is to cope with DOS-style filenames:
|
# 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.
|
# a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
|
||||||
"$@" $dashmflag |
|
"$@" $dashmflag |
|
||||||
sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
|
sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
|
||||||
rm -f "$depfile"
|
rm -f "$depfile"
|
||||||
cat < "$tmpdepfile" > "$depfile"
|
cat < "$tmpdepfile" > "$depfile"
|
||||||
tr ' ' '
|
# Some versions of the HPUX 10.20 sed can't process this sed invocation
|
||||||
' < "$tmpdepfile" | \
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
tr ' ' "$nl" < "$tmpdepfile" \
|
||||||
## correctly. Breaking it into two sed invocations is a workaround.
|
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -450,41 +625,51 @@ makedepend)
|
|||||||
"$@" || exit $?
|
"$@" || exit $?
|
||||||
# Remove any Libtool call
|
# Remove any Libtool call
|
||||||
if test "$libtool" = yes; then
|
if test "$libtool" = yes; then
|
||||||
while test $1 != '--mode=compile'; do
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
shift
|
shift
|
||||||
fi
|
fi
|
||||||
# X makedepend
|
# X makedepend
|
||||||
shift
|
shift
|
||||||
cleared=no
|
cleared=no eat=no
|
||||||
for arg in "$@"; do
|
for arg
|
||||||
|
do
|
||||||
case $cleared in
|
case $cleared in
|
||||||
no)
|
no)
|
||||||
set ""; shift
|
set ""; shift
|
||||||
cleared=yes ;;
|
cleared=yes ;;
|
||||||
esac
|
esac
|
||||||
|
if test $eat = yes; then
|
||||||
|
eat=no
|
||||||
|
continue
|
||||||
|
fi
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
-D*|-I*)
|
-D*|-I*)
|
||||||
set fnord "$@" "$arg"; shift ;;
|
set fnord "$@" "$arg"; shift ;;
|
||||||
# Strip any option that makedepend may not understand. Remove
|
# Strip any option that makedepend may not understand. Remove
|
||||||
# the object too, otherwise makedepend will parse it as a source file.
|
# the object too, otherwise makedepend will parse it as a source file.
|
||||||
|
-arch)
|
||||||
|
eat=yes ;;
|
||||||
-*|$object)
|
-*|$object)
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
set fnord "$@" "$arg"; shift ;;
|
set fnord "$@" "$arg"; shift ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
obj_suffix="`echo $object | sed 's/^.*\././'`"
|
obj_suffix=`echo "$object" | sed 's/^.*\././'`
|
||||||
touch "$tmpdepfile"
|
touch "$tmpdepfile"
|
||||||
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
|
||||||
rm -f "$depfile"
|
rm -f "$depfile"
|
||||||
cat < "$tmpdepfile" > "$depfile"
|
# makedepend may prepend the VPATH from the source file name to the object.
|
||||||
sed '1,2d' "$tmpdepfile" | tr ' ' '
|
# No need to regex-escape $object, excess matching of '.' is harmless.
|
||||||
' | \
|
sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile"
|
||||||
## Some versions of the HPUX 10.20 sed can't process this invocation
|
# Some versions of the HPUX 10.20 sed can't process the last invocation
|
||||||
## correctly. Breaking it into two sed invocations is a workaround.
|
# correctly. Breaking it into two sed invocations is a workaround.
|
||||||
sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
|
sed '1,2d' "$tmpdepfile" \
|
||||||
|
| tr ' ' "$nl" \
|
||||||
|
| sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
|
||||||
|
| sed -e 's/$/ :/' >> "$depfile"
|
||||||
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
rm -f "$tmpdepfile" "$tmpdepfile".bak
|
||||||
;;
|
;;
|
||||||
|
|
||||||
@ -495,13 +680,13 @@ cpp)
|
|||||||
|
|
||||||
# Remove the call to Libtool.
|
# Remove the call to Libtool.
|
||||||
if test "$libtool" = yes; then
|
if test "$libtool" = yes; then
|
||||||
while test $1 != '--mode=compile'; do
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
shift
|
shift
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Remove `-o $object'.
|
# Remove '-o $object'.
|
||||||
IFS=" "
|
IFS=" "
|
||||||
for arg
|
for arg
|
||||||
do
|
do
|
||||||
@ -520,10 +705,10 @@ cpp)
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
"$@" -E |
|
"$@" -E \
|
||||||
sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
| sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||||
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' |
|
-e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \
|
||||||
sed '$ s: \\$::' > "$tmpdepfile"
|
| sed '$ s: \\$::' > "$tmpdepfile"
|
||||||
rm -f "$depfile"
|
rm -f "$depfile"
|
||||||
echo "$object : \\" > "$depfile"
|
echo "$object : \\" > "$depfile"
|
||||||
cat < "$tmpdepfile" >> "$depfile"
|
cat < "$tmpdepfile" >> "$depfile"
|
||||||
@ -533,13 +718,27 @@ cpp)
|
|||||||
|
|
||||||
msvisualcpp)
|
msvisualcpp)
|
||||||
# Important note: in order to support this mode, a compiler *must*
|
# Important note: in order to support this mode, a compiler *must*
|
||||||
# always write the preprocessed file to stdout, regardless of -o,
|
# always write the preprocessed file to stdout.
|
||||||
# because we must use -o when running libtool.
|
|
||||||
"$@" || exit $?
|
"$@" || exit $?
|
||||||
|
|
||||||
|
# Remove the call to Libtool.
|
||||||
|
if test "$libtool" = yes; then
|
||||||
|
while test "X$1" != 'X--mode=compile'; do
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
|
||||||
IFS=" "
|
IFS=" "
|
||||||
for arg
|
for arg
|
||||||
do
|
do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
|
-o)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
$object)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
|
||||||
set fnord "$@"
|
set fnord "$@"
|
||||||
shift
|
shift
|
||||||
@ -552,16 +751,23 @@ msvisualcpp)
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
"$@" -E |
|
"$@" -E 2>/dev/null |
|
||||||
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile"
|
sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile"
|
||||||
rm -f "$depfile"
|
rm -f "$depfile"
|
||||||
echo "$object : \\" > "$depfile"
|
echo "$object : \\" > "$depfile"
|
||||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile"
|
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile"
|
||||||
echo " " >> "$depfile"
|
echo "$tab" >> "$depfile"
|
||||||
. "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile"
|
||||||
rm -f "$tmpdepfile"
|
rm -f "$tmpdepfile"
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
msvcmsys)
|
||||||
|
# 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
|
||||||
|
;;
|
||||||
|
|
||||||
none)
|
none)
|
||||||
exec "$@"
|
exec "$@"
|
||||||
;;
|
;;
|
||||||
@ -580,5 +786,6 @@ exit 0
|
|||||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||||
# time-stamp-start: "scriptversion="
|
# time-stamp-start: "scriptversion="
|
||||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
# time-stamp-end: "$"
|
# time-stamp-time-zone: "UTC"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
# End:
|
# End:
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# install - install a program, script, or datafile
|
# install - install a program, script, or datafile
|
||||||
|
|
||||||
scriptversion=2006-10-14.15
|
scriptversion=2011-11-20.07; # UTC
|
||||||
|
|
||||||
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
# This originates from X11R5 (mit/util/scripts/install.sh), which was
|
||||||
# later released in X11R6 (xc/config/util/install.sh) with the
|
# later released in X11R6 (xc/config/util/install.sh) with the
|
||||||
@ -35,7 +35,7 @@ scriptversion=2006-10-14.15
|
|||||||
# FSF changes to this file are in the public domain.
|
# FSF changes to this file are in the public domain.
|
||||||
#
|
#
|
||||||
# Calling this script install-sh is preferred over install.sh, to prevent
|
# Calling this script install-sh is preferred over install.sh, to prevent
|
||||||
# `make' implicit rules from creating a file called install from it
|
# 'make' implicit rules from creating a file called install from it
|
||||||
# when there is no Makefile.
|
# when there is no Makefile.
|
||||||
#
|
#
|
||||||
# This script is compatible with the BSD install script, but was written
|
# This script is compatible with the BSD install script, but was written
|
||||||
@ -48,7 +48,7 @@ IFS=" "" $nl"
|
|||||||
# set DOITPROG to echo to test this script
|
# set DOITPROG to echo to test this script
|
||||||
|
|
||||||
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
# Don't use :- since 4.3BSD and earlier shells don't like it.
|
||||||
doit="${DOITPROG-}"
|
doit=${DOITPROG-}
|
||||||
if test -z "$doit"; then
|
if test -z "$doit"; then
|
||||||
doit_exec=exec
|
doit_exec=exec
|
||||||
else
|
else
|
||||||
@ -58,34 +58,49 @@ fi
|
|||||||
# Put in absolute file names if you don't have them in your path;
|
# Put in absolute file names if you don't have them in your path;
|
||||||
# or use environment vars.
|
# or use environment vars.
|
||||||
|
|
||||||
mvprog="${MVPROG-mv}"
|
chgrpprog=${CHGRPPROG-chgrp}
|
||||||
cpprog="${CPPROG-cp}"
|
chmodprog=${CHMODPROG-chmod}
|
||||||
chmodprog="${CHMODPROG-chmod}"
|
chownprog=${CHOWNPROG-chown}
|
||||||
chownprog="${CHOWNPROG-chown}"
|
cmpprog=${CMPPROG-cmp}
|
||||||
chgrpprog="${CHGRPPROG-chgrp}"
|
cpprog=${CPPROG-cp}
|
||||||
stripprog="${STRIPPROG-strip}"
|
mkdirprog=${MKDIRPROG-mkdir}
|
||||||
rmprog="${RMPROG-rm}"
|
mvprog=${MVPROG-mv}
|
||||||
mkdirprog="${MKDIRPROG-mkdir}"
|
rmprog=${RMPROG-rm}
|
||||||
|
stripprog=${STRIPPROG-strip}
|
||||||
|
|
||||||
|
posix_glob='?'
|
||||||
|
initialize_posix_glob='
|
||||||
|
test "$posix_glob" != "?" || {
|
||||||
|
if (set -f) 2>/dev/null; then
|
||||||
|
posix_glob=
|
||||||
|
else
|
||||||
|
posix_glob=:
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
'
|
||||||
|
|
||||||
posix_glob=
|
|
||||||
posix_mkdir=
|
posix_mkdir=
|
||||||
|
|
||||||
# Desired mode of installed file.
|
# Desired mode of installed file.
|
||||||
mode=0755
|
mode=0755
|
||||||
|
|
||||||
|
chgrpcmd=
|
||||||
chmodcmd=$chmodprog
|
chmodcmd=$chmodprog
|
||||||
chowncmd=
|
chowncmd=
|
||||||
chgrpcmd=
|
mvcmd=$mvprog
|
||||||
stripcmd=
|
|
||||||
rmcmd="$rmprog -f"
|
rmcmd="$rmprog -f"
|
||||||
mvcmd="$mvprog"
|
stripcmd=
|
||||||
|
|
||||||
src=
|
src=
|
||||||
dst=
|
dst=
|
||||||
dir_arg=
|
dir_arg=
|
||||||
dstarg=
|
dst_arg=
|
||||||
|
|
||||||
|
copy_on_change=false
|
||||||
no_target_directory=
|
no_target_directory=
|
||||||
|
|
||||||
usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
usage="\
|
||||||
|
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
|
||||||
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
or: $0 [OPTION]... SRCFILES... DIRECTORY
|
||||||
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
or: $0 [OPTION]... -t DIRECTORY SRCFILES...
|
||||||
or: $0 [OPTION]... -d DIRECTORIES...
|
or: $0 [OPTION]... -d DIRECTORIES...
|
||||||
@ -95,65 +110,59 @@ In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
|
|||||||
In the 4th, create DIRECTORIES.
|
In the 4th, create DIRECTORIES.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-c (ignored)
|
--help display this help and exit.
|
||||||
-d create directories instead of installing files.
|
--version display version info and exit.
|
||||||
-g GROUP $chgrpprog installed files to GROUP.
|
|
||||||
-m MODE $chmodprog installed files to MODE.
|
-c (ignored)
|
||||||
-o USER $chownprog installed files to USER.
|
-C install only if different (preserve the last data modification time)
|
||||||
-s $stripprog installed files.
|
-d create directories instead of installing files.
|
||||||
-t DIRECTORY install into DIRECTORY.
|
-g GROUP $chgrpprog installed files to GROUP.
|
||||||
-T report an error if DSTFILE is a directory.
|
-m MODE $chmodprog installed files to MODE.
|
||||||
--help display this help and exit.
|
-o USER $chownprog installed files to USER.
|
||||||
--version display version info and exit.
|
-s $stripprog installed files.
|
||||||
|
-t DIRECTORY install into DIRECTORY.
|
||||||
|
-T report an error if DSTFILE is a directory.
|
||||||
|
|
||||||
Environment variables override the default commands:
|
Environment variables override the default commands:
|
||||||
CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG
|
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
|
||||||
|
RMPROG STRIPPROG
|
||||||
"
|
"
|
||||||
|
|
||||||
while test $# -ne 0; do
|
while test $# -ne 0; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-c) shift
|
-c) ;;
|
||||||
continue;;
|
|
||||||
|
|
||||||
-d) dir_arg=true
|
-C) copy_on_change=true;;
|
||||||
shift
|
|
||||||
continue;;
|
-d) dir_arg=true;;
|
||||||
|
|
||||||
-g) chgrpcmd="$chgrpprog $2"
|
-g) chgrpcmd="$chgrpprog $2"
|
||||||
shift
|
shift;;
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
--help) echo "$usage"; exit $?;;
|
--help) echo "$usage"; exit $?;;
|
||||||
|
|
||||||
-m) mode=$2
|
-m) mode=$2
|
||||||
shift
|
|
||||||
shift
|
|
||||||
case $mode in
|
case $mode in
|
||||||
*' '* | *' '* | *'
|
*' '* | *' '* | *'
|
||||||
'* | *'*'* | *'?'* | *'['*)
|
'* | *'*'* | *'?'* | *'['*)
|
||||||
echo "$0: invalid mode: $mode" >&2
|
echo "$0: invalid mode: $mode" >&2
|
||||||
exit 1;;
|
exit 1;;
|
||||||
esac
|
esac
|
||||||
continue;;
|
shift;;
|
||||||
|
|
||||||
-o) chowncmd="$chownprog $2"
|
-o) chowncmd="$chownprog $2"
|
||||||
shift
|
shift;;
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-s) stripcmd=$stripprog
|
-s) stripcmd=$stripprog;;
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
-t) dstarg=$2
|
-t) dst_arg=$2
|
||||||
shift
|
# Protect names problematic for 'test' and other utilities.
|
||||||
shift
|
case $dst_arg in
|
||||||
continue;;
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
|
shift;;
|
||||||
|
|
||||||
-T) no_target_directory=true
|
-T) no_target_directory=true;;
|
||||||
shift
|
|
||||||
continue;;
|
|
||||||
|
|
||||||
--version) echo "$0 $scriptversion"; exit $?;;
|
--version) echo "$0 $scriptversion"; exit $?;;
|
||||||
|
|
||||||
@ -165,21 +174,26 @@ while test $# -ne 0; do
|
|||||||
|
|
||||||
*) break;;
|
*) break;;
|
||||||
esac
|
esac
|
||||||
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
if test $# -ne 0 && test -z "$dir_arg$dstarg"; then
|
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
|
||||||
# When -d is used, all remaining arguments are directories to create.
|
# When -d is used, all remaining arguments are directories to create.
|
||||||
# When -t is used, the destination is already specified.
|
# When -t is used, the destination is already specified.
|
||||||
# Otherwise, the last argument is the destination. Remove it from $@.
|
# Otherwise, the last argument is the destination. Remove it from $@.
|
||||||
for arg
|
for arg
|
||||||
do
|
do
|
||||||
if test -n "$dstarg"; then
|
if test -n "$dst_arg"; then
|
||||||
# $@ is not empty: it contains at least $arg.
|
# $@ is not empty: it contains at least $arg.
|
||||||
set fnord "$@" "$dstarg"
|
set fnord "$@" "$dst_arg"
|
||||||
shift # fnord
|
shift # fnord
|
||||||
fi
|
fi
|
||||||
shift # arg
|
shift # arg
|
||||||
dstarg=$arg
|
dst_arg=$arg
|
||||||
|
# Protect names problematic for 'test' and other utilities.
|
||||||
|
case $dst_arg in
|
||||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;;
|
||||||
|
esac
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -188,13 +202,17 @@ if test $# -eq 0; then
|
|||||||
echo "$0: no input file specified." >&2
|
echo "$0: no input file specified." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
# It's OK to call `install-sh -d' without argument.
|
# It's OK to call 'install-sh -d' without argument.
|
||||||
# This can happen when creating conditional directories.
|
# This can happen when creating conditional directories.
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -z "$dir_arg"; then
|
if test -z "$dir_arg"; then
|
||||||
trap '(exit $?); exit' 1 2 13 15
|
do_exit='(exit $ret); exit $ret'
|
||||||
|
trap "ret=129; $do_exit" 1
|
||||||
|
trap "ret=130; $do_exit" 2
|
||||||
|
trap "ret=141; $do_exit" 13
|
||||||
|
trap "ret=143; $do_exit" 15
|
||||||
|
|
||||||
# Set umask so as not to create temps with too-generous modes.
|
# Set umask so as not to create temps with too-generous modes.
|
||||||
# However, 'strip' requires both read and write access to temps.
|
# However, 'strip' requires both read and write access to temps.
|
||||||
@ -222,9 +240,9 @@ fi
|
|||||||
|
|
||||||
for src
|
for src
|
||||||
do
|
do
|
||||||
# Protect names starting with `-'.
|
# Protect names problematic for 'test' and other utilities.
|
||||||
case $src in
|
case $src in
|
||||||
-*) src=./$src ;;
|
-* | [=\(\)!]) src=./$src;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if test -n "$dir_arg"; then
|
if test -n "$dir_arg"; then
|
||||||
@ -242,22 +260,17 @@ do
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test -z "$dstarg"; then
|
if test -z "$dst_arg"; then
|
||||||
echo "$0: no destination specified." >&2
|
echo "$0: no destination specified." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
dst=$dst_arg
|
||||||
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 destination is a directory, append the input filename; won't work
|
||||||
# if double slashes aren't ignored.
|
# if double slashes aren't ignored.
|
||||||
if test -d "$dst"; then
|
if test -d "$dst"; then
|
||||||
if test -n "$no_target_directory"; then
|
if test -n "$no_target_directory"; then
|
||||||
echo "$0: $dstarg: Is a directory" >&2
|
echo "$0: $dst_arg: Is a directory" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
dstdir=$dst
|
dstdir=$dst
|
||||||
@ -341,7 +354,7 @@ do
|
|||||||
if test -z "$dir_arg" || {
|
if test -z "$dir_arg" || {
|
||||||
# Check for POSIX incompatibilities with -m.
|
# Check for POSIX incompatibilities with -m.
|
||||||
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
|
||||||
# other-writeable bit of parent directory when it shouldn't.
|
# other-writable bit of parent directory when it shouldn't.
|
||||||
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
|
||||||
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
ls_ld_tmpdir=`ls -ld "$tmpdir"`
|
||||||
case $ls_ld_tmpdir in
|
case $ls_ld_tmpdir in
|
||||||
@ -378,33 +391,26 @@ do
|
|||||||
# directory the slow way, step by step, checking for races as we go.
|
# directory the slow way, step by step, checking for races as we go.
|
||||||
|
|
||||||
case $dstdir in
|
case $dstdir in
|
||||||
/*) prefix=/ ;;
|
/*) prefix='/';;
|
||||||
-*) prefix=./ ;;
|
[-=\(\)!]*) prefix='./';;
|
||||||
*) prefix= ;;
|
*) prefix='';;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
case $posix_glob in
|
eval "$initialize_posix_glob"
|
||||||
'')
|
|
||||||
if (set -f) 2>/dev/null; then
|
|
||||||
posix_glob=true
|
|
||||||
else
|
|
||||||
posix_glob=false
|
|
||||||
fi ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
oIFS=$IFS
|
oIFS=$IFS
|
||||||
IFS=/
|
IFS=/
|
||||||
$posix_glob && set -f
|
$posix_glob set -f
|
||||||
set fnord $dstdir
|
set fnord $dstdir
|
||||||
shift
|
shift
|
||||||
$posix_glob && set +f
|
$posix_glob set +f
|
||||||
IFS=$oIFS
|
IFS=$oIFS
|
||||||
|
|
||||||
prefixes=
|
prefixes=
|
||||||
|
|
||||||
for d
|
for d
|
||||||
do
|
do
|
||||||
test -z "$d" && continue
|
test X"$d" = X && continue
|
||||||
|
|
||||||
prefix=$prefix$d
|
prefix=$prefix$d
|
||||||
if test -d "$prefix"; then
|
if test -d "$prefix"; then
|
||||||
@ -459,41 +465,54 @@ do
|
|||||||
# ignore errors from any of these, just make sure not to ignore
|
# ignore errors from any of these, just make sure not to ignore
|
||||||
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
# errors from the above "$doit $cpprog $src $dsttmp" command.
|
||||||
#
|
#
|
||||||
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \
|
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
|
||||||
&& { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
|
||||||
&& { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \
|
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
|
||||||
&& { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
|
||||||
|
|
||||||
|
# If -C, don't bother to copy if it wouldn't change the file.
|
||||||
|
if $copy_on_change &&
|
||||||
|
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
|
||||||
|
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
|
||||||
|
|
||||||
|
eval "$initialize_posix_glob" &&
|
||||||
|
$posix_glob set -f &&
|
||||||
|
set X $old && old=:$2:$4:$5:$6 &&
|
||||||
|
set X $new && new=:$2:$4:$5:$6 &&
|
||||||
|
$posix_glob set +f &&
|
||||||
|
|
||||||
|
test "$old" = "$new" &&
|
||||||
|
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
rm -f "$dsttmp"
|
||||||
|
else
|
||||||
|
# Rename the file to the real destination.
|
||||||
|
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
|
||||||
|
|
||||||
# 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
|
# The rename failed, perhaps because mv can't rename something else
|
||||||
# to itself, or perhaps because mv is so ancient that it does not
|
# to itself, or perhaps because mv is so ancient that it does not
|
||||||
# support -f.
|
# support -f.
|
||||||
|
{
|
||||||
# Now remove or move aside any old file at destination location.
|
# Now remove or move aside any old file at destination location.
|
||||||
# We try this two ways since rm can't unlink itself on some
|
# We try this two ways since rm can't unlink itself on some
|
||||||
# systems and the destination file might be busy for other
|
# systems and the destination file might be busy for other
|
||||||
# reasons. In this case, the final cleanup might fail but the new
|
# reasons. In this case, the final cleanup might fail but the new
|
||||||
# file should still install successfully.
|
# file should still install successfully.
|
||||||
{
|
{
|
||||||
if test -f "$dst"; then
|
test ! -f "$dst" ||
|
||||||
$doit $rmcmd -f "$dst" 2>/dev/null \
|
$doit $rmcmd -f "$dst" 2>/dev/null ||
|
||||||
|| { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \
|
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
|
||||||
&& { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\
|
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }
|
||||||
|| {
|
} ||
|
||||||
echo "$0: cannot unlink or rename $dst" >&2
|
{ echo "$0: cannot unlink or rename $dst" >&2
|
||||||
(exit 1); exit 1
|
(exit 1); exit 1
|
||||||
}
|
}
|
||||||
else
|
|
||||||
:
|
|
||||||
fi
|
|
||||||
} &&
|
} &&
|
||||||
|
|
||||||
# Now rename the file to the real destination.
|
# Now rename the file to the real destination.
|
||||||
$doit $mvcmd "$dsttmp" "$dst"
|
$doit $mvcmd "$dsttmp" "$dst"
|
||||||
}
|
}
|
||||||
} || exit 1
|
fi || exit 1
|
||||||
|
|
||||||
trap '' 0
|
trap '' 0
|
||||||
fi
|
fi
|
||||||
@ -503,5 +522,6 @@ done
|
|||||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||||
# time-stamp-start: "scriptversion="
|
# time-stamp-start: "scriptversion="
|
||||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
# time-stamp-end: "$"
|
# time-stamp-time-zone: "UTC"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
# End:
|
# End:
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
#! /bin/sh
|
#! /bin/sh
|
||||||
# Common stub for a few missing GNU programs while installing.
|
# Common wrapper for a few potentially missing GNU programs.
|
||||||
|
|
||||||
scriptversion=2006-05-10.23
|
scriptversion=2013-10-28.13; # UTC
|
||||||
|
|
||||||
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006
|
# Copyright (C) 1996-2013 Free Software Foundation, Inc.
|
||||||
# Free Software Foundation, Inc.
|
# Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
||||||
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
|
|
||||||
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@ -18,9 +17,7 @@ scriptversion=2006-05-10.23
|
|||||||
# GNU General Public License for more details.
|
# GNU General Public License for more details.
|
||||||
|
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
||||||
# 02110-1301, USA.
|
|
||||||
|
|
||||||
# As a special exception to the GNU General Public License, if you
|
# As a special exception to the GNU General Public License, if you
|
||||||
# distribute this file as part of a program that contains a
|
# distribute this file as part of a program that contains a
|
||||||
@ -28,66 +25,40 @@ scriptversion=2006-05-10.23
|
|||||||
# the same distribution terms that you use for the rest of that program.
|
# the same distribution terms that you use for the rest of that program.
|
||||||
|
|
||||||
if test $# -eq 0; then
|
if test $# -eq 0; then
|
||||||
echo 1>&2 "Try \`$0 --help' for more information"
|
echo 1>&2 "Try '$0 --help' for more information"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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
|
case $1 in
|
||||||
--run)
|
|
||||||
# Try to run requested program, and just exit if it succeeds.
|
--is-lightweight)
|
||||||
run=
|
# Used by our autoconf macros to check whether the available missing
|
||||||
|
# script is modern enough.
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
|
||||||
|
--run)
|
||||||
|
# Back-compat with the calling convention used by older automake.
|
||||||
shift
|
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)
|
-h|--h|--he|--hel|--help)
|
||||||
echo "\
|
echo "\
|
||||||
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
$0 [OPTION]... PROGRAM [ARGUMENT]...
|
||||||
|
|
||||||
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
|
Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
|
||||||
error status if there is no known handling for PROGRAM.
|
to PROGRAM being missing or too old.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-h, --help display this help and exit
|
-h, --help display this help and exit
|
||||||
-v, --version output version information 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:
|
Supported PROGRAM values:
|
||||||
aclocal touch file \`aclocal.m4'
|
aclocal autoconf autoheader autom4te automake makeinfo
|
||||||
autoconf touch file \`configure'
|
bison yacc flex lex help2man
|
||||||
autoheader touch file \`config.h.in'
|
|
||||||
autom4te touch the output file, or create a stub one
|
Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and
|
||||||
automake touch all \`Makefile.in' files
|
'g' are ignored when checking the name.
|
||||||
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>."
|
Send bug reports to <bug-automake@gnu.org>."
|
||||||
exit $?
|
exit $?
|
||||||
@ -99,269 +70,146 @@ Send bug reports to <bug-automake@gnu.org>."
|
|||||||
;;
|
;;
|
||||||
|
|
||||||
-*)
|
-*)
|
||||||
echo 1>&2 "$0: Unknown \`$1' option"
|
echo 1>&2 "$0: unknown '$1' option"
|
||||||
echo 1>&2 "Try \`$0 --help' for more information"
|
echo 1>&2 "Try '$0 --help' for more information"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
|
|
||||||
esac
|
esac
|
||||||
|
|
||||||
# Now exit if we have it, but it failed. Also exit now if we
|
# Run the given program, remember its exit status.
|
||||||
# don't have it and --version was passed (most likely to detect
|
"$@"; st=$?
|
||||||
# the program).
|
|
||||||
case $1 in
|
|
||||||
lex|yacc)
|
|
||||||
# Not GNU programs, they don't have --version.
|
|
||||||
;;
|
|
||||||
|
|
||||||
tar)
|
# If it succeeded, we are done.
|
||||||
if test -n "$run"; then
|
test $st -eq 0 && exit 0
|
||||||
echo 1>&2 "ERROR: \`tar' requires --run"
|
|
||||||
exit 1
|
|
||||||
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
# Also exit now if we it failed (or wasn't found), and '--version' was
|
||||||
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then
|
# passed; such an option is passed most likely to detect whether the
|
||||||
# We have it, but it failed.
|
# program is present and works.
|
||||||
exit 1
|
case $2 in --version|--help) exit $st;; esac
|
||||||
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),
|
# Exit code 63 means version mismatch. This often happens when the user
|
||||||
# try to emulate it.
|
# tries to use an ancient version of a tool on a file that requires a
|
||||||
case $1 in
|
# minimum version.
|
||||||
aclocal*)
|
if test $st -eq 63; then
|
||||||
echo 1>&2 "\
|
msg="probably too old"
|
||||||
WARNING: \`$1' is $msg. You should only need it if
|
elif test $st -eq 127; then
|
||||||
you modified \`acinclude.m4' or \`${configure_ac}'. You might want
|
# Program was missing.
|
||||||
to install the \`Automake' and \`Perl' packages. Grab them from
|
msg="missing on your system"
|
||||||
any GNU archive site."
|
else
|
||||||
touch aclocal.m4
|
# Program was found and executed, but failed. Give up.
|
||||||
;;
|
exit $st
|
||||||
|
fi
|
||||||
|
|
||||||
autoconf)
|
perl_URL=http://www.perl.org/
|
||||||
echo 1>&2 "\
|
flex_URL=http://flex.sourceforge.net/
|
||||||
WARNING: \`$1' is $msg. You should only need it if
|
gnu_software_URL=http://www.gnu.org/software
|
||||||
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)
|
program_details ()
|
||||||
echo 1>&2 "\
|
{
|
||||||
WARNING: \`$1' is $msg. You should only need it if
|
case $1 in
|
||||||
you modified \`acconfig.h' or \`${configure_ac}'. You might want
|
aclocal|automake)
|
||||||
to install the \`Autoconf' and \`GNU m4' packages. Grab them
|
echo "The '$1' program is part of the GNU Automake package:"
|
||||||
from any GNU archive site."
|
echo "<$gnu_software_URL/automake>"
|
||||||
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
|
echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
|
||||||
test -z "$files" && files="config.h"
|
echo "<$gnu_software_URL/autoconf>"
|
||||||
touch_files=
|
echo "<$gnu_software_URL/m4/>"
|
||||||
for f in $files; do
|
echo "<$perl_URL>"
|
||||||
case $f in
|
;;
|
||||||
*:*) touch_files="$touch_files "`echo "$f" |
|
autoconf|autom4te|autoheader)
|
||||||
sed -e 's/^[^:]*://' -e 's/:.*//'`;;
|
echo "The '$1' program is part of the GNU Autoconf package:"
|
||||||
*) touch_files="$touch_files $f.in";;
|
echo "<$gnu_software_URL/autoconf/>"
|
||||||
|
echo "It also requires GNU m4 and Perl in order to run:"
|
||||||
|
echo "<$gnu_software_URL/m4/>"
|
||||||
|
echo "<$perl_URL>"
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
}
|
||||||
touch $touch_files
|
|
||||||
;;
|
|
||||||
|
|
||||||
|
give_advice ()
|
||||||
|
{
|
||||||
|
# Normalize program name to check for.
|
||||||
|
normalized_program=`echo "$1" | sed '
|
||||||
|
s/^gnu-//; t
|
||||||
|
s/^gnu//; t
|
||||||
|
s/^g//; t'`
|
||||||
|
|
||||||
|
printf '%s\n' "'$1' is $msg."
|
||||||
|
|
||||||
|
configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
|
||||||
|
case $normalized_program in
|
||||||
|
autoconf*)
|
||||||
|
echo "You should only need it if you modified 'configure.ac',"
|
||||||
|
echo "or m4 files included by it."
|
||||||
|
program_details 'autoconf'
|
||||||
|
;;
|
||||||
|
autoheader*)
|
||||||
|
echo "You should only need it if you modified 'acconfig.h' or"
|
||||||
|
echo "$configure_deps."
|
||||||
|
program_details 'autoheader'
|
||||||
|
;;
|
||||||
automake*)
|
automake*)
|
||||||
echo 1>&2 "\
|
echo "You should only need it if you modified 'Makefile.am' or"
|
||||||
WARNING: \`$1' is $msg. You should only need it if
|
echo "$configure_deps."
|
||||||
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
|
program_details 'automake'
|
||||||
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
|
|
||||||
;;
|
;;
|
||||||
|
aclocal*)
|
||||||
autom4te)
|
echo "You should only need it if you modified 'acinclude.m4' or"
|
||||||
echo 1>&2 "\
|
echo "$configure_deps."
|
||||||
WARNING: \`$1' is needed, but is $msg.
|
program_details 'aclocal'
|
||||||
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
|
|
||||||
;;
|
;;
|
||||||
|
autom4te*)
|
||||||
bison|yacc)
|
echo "You might have modified some maintainer files that require"
|
||||||
echo 1>&2 "\
|
echo "the 'autom4te' program to be rebuilt."
|
||||||
WARNING: \`$1' $msg. You should only need it if
|
program_details 'autom4te'
|
||||||
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
|
bison*|yacc*)
|
||||||
fi
|
echo "You should only need it if you modified a '.y' file."
|
||||||
if test ! -f y.tab.h; then
|
echo "You may want to install the GNU Bison package:"
|
||||||
echo >y.tab.h
|
echo "<$gnu_software_URL/bison/>"
|
||||||
fi
|
|
||||||
if test ! -f y.tab.c; then
|
|
||||||
echo 'main() { return 0; }' >y.tab.c
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
|
lex*|flex*)
|
||||||
lex|flex)
|
echo "You should only need it if you modified a '.l' file."
|
||||||
echo 1>&2 "\
|
echo "You may want to install the Fast Lexical Analyzer package:"
|
||||||
WARNING: \`$1' is $msg. You should only need it if
|
echo "<$flex_URL>"
|
||||||
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
|
help2man*)
|
||||||
fi
|
echo "You should only need it if you modified a dependency" \
|
||||||
if test ! -f lex.yy.c; then
|
"of a man page."
|
||||||
echo 'main() { return 0; }' >lex.yy.c
|
echo "You may want to install the GNU Help2man package:"
|
||||||
fi
|
echo "<$gnu_software_URL/help2man/>"
|
||||||
;;
|
;;
|
||||||
|
makeinfo*)
|
||||||
help2man)
|
echo "You should only need it if you modified a '.texi' file, or"
|
||||||
echo 1>&2 "\
|
echo "any other file indirectly affecting the aspect of the manual."
|
||||||
WARNING: \`$1' is $msg. You should only need it if
|
echo "You might want to install the Texinfo package:"
|
||||||
you modified a dependency of a manual page. You may need the
|
echo "<$gnu_software_URL/texinfo/>"
|
||||||
\`Help2man' package in order for those modifications to take
|
echo "The spurious makeinfo call might also be the consequence of"
|
||||||
effect. You can get \`Help2man' from any GNU archive site."
|
echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
|
||||||
|
echo "want to install GNU make:"
|
||||||
file=`echo "$*" | sed -n "$sed_output"`
|
echo "<$gnu_software_URL/make/>"
|
||||||
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 "\
|
echo "You might have modified some files without having the proper"
|
||||||
WARNING: \`$1' is needed, and is $msg.
|
echo "tools for further handling them. Check the 'README' file, it"
|
||||||
You might have modified some files without having the
|
echo "often tells you about the needed prerequisites for installing"
|
||||||
proper tools for further handling them. Check the \`README' file,
|
echo "this package. You may also peek at any GNU archive site, in"
|
||||||
it often tells you about the needed prerequisites for installing
|
echo "case some other package contains this missing '$1' program."
|
||||||
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
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
exit 0
|
give_advice "$1" | sed -e '1s/^/WARNING: /' \
|
||||||
|
-e '2,$s/^/ /' >&2
|
||||||
|
|
||||||
|
# Propagate the correct exit status (expected to be 127 for a program
|
||||||
|
# not found, 63 for a program that failed due to version mismatch).
|
||||||
|
exit $st
|
||||||
|
|
||||||
# Local variables:
|
# Local variables:
|
||||||
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
# eval: (add-hook 'write-file-hooks 'time-stamp)
|
||||||
# time-stamp-start: "scriptversion="
|
# time-stamp-start: "scriptversion="
|
||||||
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
# time-stamp-format: "%:y-%02m-%02d.%02H"
|
||||||
# time-stamp-end: "$"
|
# time-stamp-time-zone: "UTC"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
# End:
|
# End:
|
||||||
|
139
google-breakpad/autotools/test-driver
Executable file
139
google-breakpad/autotools/test-driver
Executable file
@ -0,0 +1,139 @@
|
|||||||
|
#! /bin/sh
|
||||||
|
# test-driver - basic testsuite driver script.
|
||||||
|
|
||||||
|
scriptversion=2013-07-13.22; # UTC
|
||||||
|
|
||||||
|
# Copyright (C) 2011-2013 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# 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, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
# This file is maintained in Automake, please report
|
||||||
|
# bugs to <bug-automake@gnu.org> or send patches to
|
||||||
|
# <automake-patches@gnu.org>.
|
||||||
|
|
||||||
|
# Make unconditional expansion of undefined variables an error. This
|
||||||
|
# helps a lot in preventing typo-related bugs.
|
||||||
|
set -u
|
||||||
|
|
||||||
|
usage_error ()
|
||||||
|
{
|
||||||
|
echo "$0: $*" >&2
|
||||||
|
print_usage >&2
|
||||||
|
exit 2
|
||||||
|
}
|
||||||
|
|
||||||
|
print_usage ()
|
||||||
|
{
|
||||||
|
cat <<END
|
||||||
|
Usage:
|
||||||
|
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
|
||||||
|
[--expect-failure={yes|no}] [--color-tests={yes|no}]
|
||||||
|
[--enable-hard-errors={yes|no}] [--]
|
||||||
|
TEST-SCRIPT [TEST-SCRIPT-ARGUMENTS]
|
||||||
|
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
|
test_name= # Used for reporting.
|
||||||
|
log_file= # Where to save the output of the test script.
|
||||||
|
trs_file= # Where to save the metadata of the test run.
|
||||||
|
expect_failure=no
|
||||||
|
color_tests=no
|
||||||
|
enable_hard_errors=yes
|
||||||
|
while test $# -gt 0; do
|
||||||
|
case $1 in
|
||||||
|
--help) print_usage; exit $?;;
|
||||||
|
--version) echo "test-driver $scriptversion"; exit $?;;
|
||||||
|
--test-name) test_name=$2; shift;;
|
||||||
|
--log-file) log_file=$2; shift;;
|
||||||
|
--trs-file) trs_file=$2; shift;;
|
||||||
|
--color-tests) color_tests=$2; shift;;
|
||||||
|
--expect-failure) expect_failure=$2; shift;;
|
||||||
|
--enable-hard-errors) enable_hard_errors=$2; shift;;
|
||||||
|
--) shift; break;;
|
||||||
|
-*) usage_error "invalid option: '$1'";;
|
||||||
|
*) break;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
missing_opts=
|
||||||
|
test x"$test_name" = x && missing_opts="$missing_opts --test-name"
|
||||||
|
test x"$log_file" = x && missing_opts="$missing_opts --log-file"
|
||||||
|
test x"$trs_file" = x && missing_opts="$missing_opts --trs-file"
|
||||||
|
if test x"$missing_opts" != x; then
|
||||||
|
usage_error "the following mandatory options are missing:$missing_opts"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $# -eq 0; then
|
||||||
|
usage_error "missing argument"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test $color_tests = yes; then
|
||||||
|
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
|
||||||
|
red='[0;31m' # Red.
|
||||||
|
grn='[0;32m' # Green.
|
||||||
|
lgn='[1;32m' # Light green.
|
||||||
|
blu='[1;34m' # Blue.
|
||||||
|
mgn='[0;35m' # Magenta.
|
||||||
|
std='[m' # No color.
|
||||||
|
else
|
||||||
|
red= grn= lgn= blu= mgn= std=
|
||||||
|
fi
|
||||||
|
|
||||||
|
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
|
||||||
|
trap "st=129; $do_exit" 1
|
||||||
|
trap "st=130; $do_exit" 2
|
||||||
|
trap "st=141; $do_exit" 13
|
||||||
|
trap "st=143; $do_exit" 15
|
||||||
|
|
||||||
|
# Test script is run here.
|
||||||
|
"$@" >$log_file 2>&1
|
||||||
|
estatus=$?
|
||||||
|
if test $enable_hard_errors = no && test $estatus -eq 99; then
|
||||||
|
estatus=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case $estatus:$expect_failure in
|
||||||
|
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
|
||||||
|
0:*) col=$grn res=PASS recheck=no gcopy=no;;
|
||||||
|
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
|
||||||
|
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
|
||||||
|
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
|
||||||
|
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Report outcome to console.
|
||||||
|
echo "${col}${res}${std}: $test_name"
|
||||||
|
|
||||||
|
# Register the test result, and other relevant metadata.
|
||||||
|
echo ":test-result: $res" > $trs_file
|
||||||
|
echo ":global-test-result: $res" >> $trs_file
|
||||||
|
echo ":recheck: $recheck" >> $trs_file
|
||||||
|
echo ":copy-in-global-log: $gcopy" >> $trs_file
|
||||||
|
|
||||||
|
# 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-time-zone: "UTC"
|
||||||
|
# time-stamp-end: "; # UTC"
|
||||||
|
# End:
|
10
google-breakpad/breakpad-client.pc.in
Normal file
10
google-breakpad/breakpad-client.pc.in
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
prefix=@prefix@
|
||||||
|
exec_prefix=@exec_prefix@
|
||||||
|
libdir=@libdir@
|
||||||
|
includedir=@includedir@/@PACKAGE_NAME@
|
||||||
|
|
||||||
|
Name: google-breakpad-client
|
||||||
|
Description: An open-source multi-platform crash reporting system
|
||||||
|
Version: @PACKAGE_VERSION@
|
||||||
|
Libs: -L${libdir} -lbreakpad_client @PTHREAD_LIBS@
|
||||||
|
Cflags: -I${includedir} @PTHREAD_CFLAGS@
|
10
google-breakpad/breakpad.pc.in
Normal file
10
google-breakpad/breakpad.pc.in
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
prefix=@prefix@
|
||||||
|
exec_prefix=@exec_prefix@
|
||||||
|
libdir=@libdir@
|
||||||
|
includedir=@includedir@/@PACKAGE_NAME@
|
||||||
|
|
||||||
|
Name: google-breakpad
|
||||||
|
Description: An open-source multi-platform crash reporting system
|
||||||
|
Version: @PACKAGE_VERSION@
|
||||||
|
Libs: -L${libdir} -lbreakpad @PTHREAD_LIBS@
|
||||||
|
Cflags: -I${includedir} @PTHREAD_CFLAGS@
|
5
google-breakpad/codereview.settings
Normal file
5
google-breakpad/codereview.settings
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# This file is used by gcl to get repository specific information.
|
||||||
|
CODE_REVIEW_SERVER: breakpad.appspot.com
|
||||||
|
CC_LIST: google-breakpad-dev@googlegroups.com
|
||||||
|
TRY_ON_UPLOAD: False
|
||||||
|
VIEW_VC: http://code.google.com/p/google-breakpad/source/detail?r=
|
1589
google-breakpad/configure
vendored
1589
google-breakpad/configure
vendored
File diff suppressed because it is too large
Load Diff
@ -39,6 +39,7 @@ AC_CANONICAL_HOST
|
|||||||
|
|
||||||
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
|
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
|
||||||
AM_CONFIG_HEADER(src/config.h)
|
AM_CONFIG_HEADER(src/config.h)
|
||||||
|
AM_MAINTAINER_MODE
|
||||||
|
|
||||||
AM_PROG_AS
|
AM_PROG_AS
|
||||||
AC_PROG_CC
|
AC_PROG_CC
|
||||||
@ -48,7 +49,28 @@ AC_PROG_CXX
|
|||||||
AC_PROG_RANLIB
|
AC_PROG_RANLIB
|
||||||
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
|
AM_CONDITIONAL(GCC, test "$GCC" = yes) # let the Makefile know if we're gcc
|
||||||
|
|
||||||
|
dnl This must come before all the feature tests below.
|
||||||
|
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_HEADER_STDC
|
AC_HEADER_STDC
|
||||||
|
AC_SYS_LARGEFILE
|
||||||
m4_include(m4/ax_pthread.m4)
|
m4_include(m4/ax_pthread.m4)
|
||||||
AX_PTHREAD
|
AX_PTHREAD
|
||||||
AC_CHECK_HEADERS([a.out.h])
|
AC_CHECK_HEADERS([a.out.h])
|
||||||
@ -69,25 +91,6 @@ case $host in
|
|||||||
esac
|
esac
|
||||||
AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue)
|
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,
|
AC_ARG_ENABLE(processor,
|
||||||
AS_HELP_STRING([--disable-processor],
|
AS_HELP_STRING([--disable-processor],
|
||||||
[Don't build processor library]
|
[Don't build processor library]
|
||||||
@ -147,5 +150,10 @@ AC_ARG_ENABLE(selftest,
|
|||||||
[selftest=false])
|
[selftest=false])
|
||||||
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue)
|
||||||
|
|
||||||
AC_CONFIG_FILES([Makefile])
|
AC_CONFIG_FILES(m4_flatten([
|
||||||
|
breakpad.pc
|
||||||
|
breakpad-client.pc
|
||||||
|
Makefile
|
||||||
|
]))
|
||||||
|
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
@ -33,4 +33,25 @@
|
|||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
#include "testing/include/gmock/gmock.h"
|
#include "testing/include/gmock/gmock.h"
|
||||||
|
|
||||||
|
// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
|
||||||
|
// (illegal instruction) instead of SIGSEGV (segmentation fault). Also,
|
||||||
|
// the number of memory regions differs, so there is no point in running
|
||||||
|
// this test if AddressSanitizer is used.
|
||||||
|
//
|
||||||
|
// Ideally we'd use this attribute to disable ASAN on a per-func basis,
|
||||||
|
// but this doesn't seem to actually work, and it's changed names over
|
||||||
|
// time. So just stick with disabling the actual tests.
|
||||||
|
// http://crbug.com/304575
|
||||||
|
//#define NO_ASAN __attribute__((no_sanitize_address))
|
||||||
|
#if defined(__clang__) && defined(__has_feature)
|
||||||
|
// Have to keep this check sep from above as newer gcc will barf on it.
|
||||||
|
# if __has_feature(address_sanitizer)
|
||||||
|
# define ADDRESS_SANITIZER
|
||||||
|
# endif
|
||||||
|
#elif defined(__GNUC__) && defined(__SANITIZE_ADDRESS__)
|
||||||
|
# define ADDRESS_SANITIZER
|
||||||
|
#else
|
||||||
|
# undef ADDRESS_SANITIZER
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__
|
#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__
|
||||||
|
41
google-breakpad/src/build/all.gyp
Normal file
41
google-breakpad/src/build/all.gyp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'All',
|
||||||
|
'type': 'none',
|
||||||
|
'dependencies': [
|
||||||
|
'../common/common.gyp:*',
|
||||||
|
'../processor/processor.gyp:*',
|
||||||
|
'../tools/tools.gyp:*',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
# Copyright (c) 2010, Google Inc.
|
# Copyright 2010 Google Inc. All rights reserved.
|
||||||
# All rights reserved.
|
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are
|
# modification, are permitted provided that the following conditions are
|
||||||
@ -32,34 +31,13 @@
|
|||||||
# since gyp_chromium is automatically forcing its inclusion.
|
# since gyp_chromium is automatically forcing its inclusion.
|
||||||
{
|
{
|
||||||
'variables': {
|
'variables': {
|
||||||
# .gyp files or targets should set chromium_code to 1 if they build
|
|
||||||
# Chromium-specific code, as opposed to external code. This variable is
|
|
||||||
# used to control such things as the set of warnings to enable, and
|
|
||||||
# whether warnings are treated as errors.
|
|
||||||
'chromium_code%': 0,
|
|
||||||
|
|
||||||
# Variables expected to be overriden on the GYP command line (-D) or by
|
# Variables expected to be overriden on the GYP command line (-D) or by
|
||||||
# ~/.gyp/include.gypi.
|
# ~/.gyp/include.gypi.
|
||||||
|
|
||||||
# Putting a variables dict inside another variables dict looks kind of
|
# Putting a variables dict inside another variables dict looks kind of
|
||||||
# weird. This is done so that "branding" and "buildtype" are defined as
|
# weird. This is necessary to get these variables defined for the conditions
|
||||||
# variables within the outer variables dict here. This is necessary
|
# within this variables dict that operate on these variables.
|
||||||
# to get these variables defined for the conditions within this variables
|
|
||||||
# dict that operate on these variables.
|
|
||||||
'variables': {
|
'variables': {
|
||||||
# Override branding to select the desired branding flavor.
|
|
||||||
'branding%': 'Chromium',
|
|
||||||
|
|
||||||
# Override buildtype to select the desired build flavor.
|
|
||||||
# Dev - everyday build for development/testing
|
|
||||||
# Official - release build (generally implies additional processing)
|
|
||||||
# TODO(mmoss) Once 'buildtype' is fully supported (e.g. Windows gyp
|
|
||||||
# conversion is done), some of the things which are now controlled by
|
|
||||||
# 'branding', such as symbol generation, will need to be refactored based
|
|
||||||
# on 'buildtype' (i.e. we don't care about saving symbols for non-Official
|
|
||||||
# builds).
|
|
||||||
'buildtype%': 'Dev',
|
|
||||||
|
|
||||||
'variables': {
|
'variables': {
|
||||||
# Compute the architecture that we're building on.
|
# Compute the architecture that we're building on.
|
||||||
'conditions': [
|
'conditions': [
|
||||||
@ -73,49 +51,19 @@
|
|||||||
'host_arch%': 'ia32',
|
'host_arch%': 'ia32',
|
||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
|
|
||||||
# Whether we're building a ChromeOS build. We set the initial
|
|
||||||
# value at this level of nesting so it's available for the
|
|
||||||
# toolkit_views test below.
|
|
||||||
'chromeos%': '0',
|
|
||||||
},
|
},
|
||||||
|
|
||||||
# Set default value of toolkit_views on for Windows and Chrome OS.
|
|
||||||
# We set it at this level of nesting so the value is available for
|
|
||||||
# other conditionals below.
|
|
||||||
'conditions': [
|
|
||||||
['OS=="win" or chromeos==1', {
|
|
||||||
'toolkit_views%': 1,
|
|
||||||
}, {
|
|
||||||
'toolkit_views%': 0,
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
|
|
||||||
'host_arch%': '<(host_arch)',
|
'host_arch%': '<(host_arch)',
|
||||||
|
|
||||||
# Default architecture we're building for is the architecture we're
|
# Default architecture we're building for is the architecture we're
|
||||||
# building on.
|
# building on.
|
||||||
'target_arch%': '<(host_arch)',
|
'target_arch%': '<(host_arch)',
|
||||||
|
|
||||||
# We do want to build Chromium with Breakpad support in certain
|
|
||||||
# situations. I.e. for Chrome bot.
|
|
||||||
'linux_chromium_breakpad%': 0,
|
|
||||||
# And if we want to dump symbols.
|
|
||||||
'linux_chromium_dump_symbols%': 0,
|
|
||||||
# Also see linux_strip_binary below.
|
|
||||||
|
|
||||||
# Copy conditionally-set chromeos variable out one scope.
|
|
||||||
'chromeos%': '<(chromeos)',
|
|
||||||
|
|
||||||
# This variable tells WebCore.gyp and JavaScriptCore.gyp whether they are
|
# This variable tells WebCore.gyp and JavaScriptCore.gyp whether they are
|
||||||
# are built under a chromium full build (1) or a webkit.org chromium
|
# are built under a chromium full build (1) or a webkit.org chromium
|
||||||
# build (0).
|
# build (0).
|
||||||
'inside_chromium_build%': 1,
|
'inside_chromium_build%': 1,
|
||||||
|
|
||||||
# Set to 1 to enable fast builds. It disables debug info for fastest
|
|
||||||
# compilation.
|
|
||||||
'fastbuild%': 0,
|
|
||||||
|
|
||||||
# Set to 1 compile with -fPIC cflag on linux. This is a must for shared
|
# Set to 1 compile with -fPIC cflag on linux. This is a must for shared
|
||||||
# libraries on linux x86-64 and arm.
|
# libraries on linux x86-64 and arm.
|
||||||
'linux_fpic%': 0,
|
'linux_fpic%': 0,
|
||||||
@ -123,10 +71,10 @@
|
|||||||
# Python version.
|
# Python version.
|
||||||
'python_ver%': '2.5',
|
'python_ver%': '2.5',
|
||||||
|
|
||||||
# Set ARM-v7 compilation flags
|
# Determine ARM compilation flags.
|
||||||
'armv7%': 0,
|
'arm_version%': 7,
|
||||||
|
|
||||||
# Set Neon compilation flags (only meaningful if armv7==1).
|
# Set Neon compilation flags (only meaningful if arm_version==7).
|
||||||
'arm_neon%': 1,
|
'arm_neon%': 1,
|
||||||
|
|
||||||
# The system root for cross-compiles. Default: none.
|
# The system root for cross-compiles. Default: none.
|
||||||
@ -136,19 +84,12 @@
|
|||||||
'disable_sse2%': 0,
|
'disable_sse2%': 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
# Define branding and buildtype on the basis of their settings within the
|
|
||||||
# variables sub-dict above, unless overridden.
|
|
||||||
'branding%': '<(branding)',
|
|
||||||
'buildtype%': '<(buildtype)',
|
|
||||||
'target_arch%': '<(target_arch)',
|
'target_arch%': '<(target_arch)',
|
||||||
'host_arch%': '<(host_arch)',
|
'host_arch%': '<(host_arch)',
|
||||||
'toolkit_views%': '<(toolkit_views)',
|
|
||||||
'chromeos%': '<(chromeos)',
|
|
||||||
'inside_chromium_build%': '<(inside_chromium_build)',
|
'inside_chromium_build%': '<(inside_chromium_build)',
|
||||||
'fastbuild%': '<(fastbuild)',
|
|
||||||
'linux_fpic%': '<(linux_fpic)',
|
'linux_fpic%': '<(linux_fpic)',
|
||||||
'python_ver%': '<(python_ver)',
|
'python_ver%': '<(python_ver)',
|
||||||
'armv7%': '<(armv7)',
|
'arm_version%': '<(arm_version)',
|
||||||
'arm_neon%': '<(arm_neon)',
|
'arm_neon%': '<(arm_neon)',
|
||||||
'sysroot%': '<(sysroot)',
|
'sysroot%': '<(sysroot)',
|
||||||
'disable_sse2%': '<(disable_sse2)',
|
'disable_sse2%': '<(disable_sse2)',
|
||||||
@ -245,7 +186,7 @@
|
|||||||
'linux_use_seccomp_sandbox%': 0,
|
'linux_use_seccomp_sandbox%': 0,
|
||||||
|
|
||||||
# Set to select the Title Case versions of strings in GRD files.
|
# Set to select the Title Case versions of strings in GRD files.
|
||||||
'use_titlecase_in_grd_files%': 0,
|
'use_titlecase_in_grd%': 0,
|
||||||
|
|
||||||
# Used to disable Native Client at compile time, for platforms where it
|
# Used to disable Native Client at compile time, for platforms where it
|
||||||
# isn't supported
|
# isn't supported
|
||||||
@ -254,7 +195,7 @@
|
|||||||
# Set Thumb compilation flags.
|
# Set Thumb compilation flags.
|
||||||
'arm_thumb%': 0,
|
'arm_thumb%': 0,
|
||||||
|
|
||||||
# Set ARM fpu compilation flags (only meaningful if armv7==1 and
|
# Set ARM fpu compilation flags (only meaningful if arm_version==7 and
|
||||||
# arm_neon==0).
|
# arm_neon==0).
|
||||||
'arm_fpu%': 'vfpv3',
|
'arm_fpu%': 'vfpv3',
|
||||||
|
|
||||||
@ -262,62 +203,6 @@
|
|||||||
'enable_new_npdevice_api%': 0,
|
'enable_new_npdevice_api%': 0,
|
||||||
|
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['OS=="linux" or OS=="freebsd" or OS=="openbsd"', {
|
|
||||||
# This will set gcc_version to XY if you are running gcc X.Y.*.
|
|
||||||
# This is used to tweak build flags for gcc 4.4.
|
|
||||||
'gcc_version%': '<!(python <(DEPTH)/build/compiler_version.py)',
|
|
||||||
# Figure out the python architecture to decide if we build pyauto.
|
|
||||||
'python_arch%': '<!(<(DEPTH)/build/linux/python_arch.sh <(sysroot)/usr/lib/libpython<(python_ver).so.1.0)',
|
|
||||||
'conditions': [
|
|
||||||
['branding=="Chrome" or linux_chromium_breakpad==1', {
|
|
||||||
'linux_breakpad%': 1,
|
|
||||||
}, {
|
|
||||||
'linux_breakpad%': 0,
|
|
||||||
}],
|
|
||||||
# All Chrome builds have breakpad symbols, but only process the
|
|
||||||
# symbols from official builds.
|
|
||||||
# TODO(mmoss) dump_syms segfaults on x64. Enable once dump_syms and
|
|
||||||
# crash server handle 64-bit symbols.
|
|
||||||
['linux_chromium_dump_symbols==1 or '
|
|
||||||
'(branding=="Chrome" and buildtype=="Official" and '
|
|
||||||
'target_arch=="ia32")', {
|
|
||||||
'linux_dump_symbols%': 1,
|
|
||||||
}, {
|
|
||||||
'linux_dump_symbols%': 0,
|
|
||||||
}],
|
|
||||||
['toolkit_views==0', {
|
|
||||||
# GTK wants Title Case strings
|
|
||||||
'use_titlecase_in_grd_files%': 1,
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
}], # OS=="linux" or OS=="freebsd" or OS=="openbsd"
|
|
||||||
['OS=="mac"', {
|
|
||||||
# Mac wants Title Case strings
|
|
||||||
'use_titlecase_in_grd_files%': 1,
|
|
||||||
'conditions': [
|
|
||||||
# mac_product_name is set to the name of the .app bundle as it should
|
|
||||||
# appear on disk. This duplicates data from
|
|
||||||
# chrome/app/theme/chromium/BRANDING and
|
|
||||||
# chrome/app/theme/google_chrome/BRANDING, but is necessary to get
|
|
||||||
# these names into the build system.
|
|
||||||
['branding=="Chrome"', {
|
|
||||||
'mac_product_name%': 'Google Chrome',
|
|
||||||
}, { # else: branding!="Chrome"
|
|
||||||
'mac_product_name%': 'Chromium',
|
|
||||||
}],
|
|
||||||
|
|
||||||
# Feature variables for enabling Mac Breakpad and Keystone auto-update
|
|
||||||
# support. Both features are on by default in official builds with
|
|
||||||
# Chrome branding.
|
|
||||||
['branding=="Chrome" and buildtype=="Official"', {
|
|
||||||
'mac_breakpad%': 1,
|
|
||||||
'mac_keystone%': 1,
|
|
||||||
}, { # else: branding!="Chrome" or buildtype!="Official"
|
|
||||||
'mac_breakpad%': 0,
|
|
||||||
'mac_keystone%': 0,
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
}], # OS=="mac"
|
|
||||||
# Whether to use multiple cores to compile with visual studio. This is
|
# Whether to use multiple cores to compile with visual studio. This is
|
||||||
# optional because it sometimes causes corruption on VS 2005.
|
# optional because it sometimes causes corruption on VS 2005.
|
||||||
# It is on by default on VS 2008 and off on VS 2005.
|
# It is on by default on VS 2008 and off on VS 2005.
|
||||||
@ -341,24 +226,6 @@
|
|||||||
'NACL_WIN64',
|
'NACL_WIN64',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
# Compute based on OS and target architecture whether the GPU
|
|
||||||
# plugin / process is supported.
|
|
||||||
[ 'OS=="win" or (OS=="linux" and target_arch!="arm") or OS=="mac"', {
|
|
||||||
# Enable a variable used elsewhere throughout the GYP files to determine
|
|
||||||
# whether to compile in the sources for the GPU plugin / process.
|
|
||||||
'enable_gpu%': 1,
|
|
||||||
}, { # GPU plugin not supported
|
|
||||||
'enable_gpu%': 0,
|
|
||||||
}],
|
|
||||||
# Compute based on OS, target architecture and device whether GLES
|
|
||||||
# is supported
|
|
||||||
[ 'OS=="linux" and target_arch=="arm" and chromeos==1', {
|
|
||||||
# Enable a variable used elsewhere throughout the GYP files to determine
|
|
||||||
# whether to compile in the sources for the GLES support.
|
|
||||||
'enable_gles%': 1,
|
|
||||||
}, { # GLES not supported
|
|
||||||
'enable_gles%': 0,
|
|
||||||
}],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
# NOTE: When these end up in the Mac bundle, we need to replace '-' for '_'
|
# NOTE: When these end up in the Mac bundle, we need to replace '-' for '_'
|
||||||
@ -373,18 +240,10 @@
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
'target_defaults': {
|
'target_defaults': {
|
||||||
|
'includes': [
|
||||||
|
'filename_rules.gypi',
|
||||||
|
],
|
||||||
'variables': {
|
'variables': {
|
||||||
# The condition that operates on chromium_code is in a target_conditions
|
|
||||||
# section, and will not have access to the default fallback value of
|
|
||||||
# chromium_code at the top of this file, or to the chromium_code
|
|
||||||
# variable placed at the root variables scope of .gyp files, because
|
|
||||||
# those variables are not set at target scope. As a workaround,
|
|
||||||
# if chromium_code is not set at target scope, define it in target scope
|
|
||||||
# to contain whatever value it has during early variable expansion.
|
|
||||||
# That's enough to make it available during target conditional
|
|
||||||
# processing.
|
|
||||||
'chromium_code%': '<(chromium_code)',
|
|
||||||
|
|
||||||
# See http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Optimize-Options.html
|
# See http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Optimize-Options.html
|
||||||
'mac_release_optimization%': '3', # Use -O3 unless overridden
|
'mac_release_optimization%': '3', # Use -O3 unless overridden
|
||||||
'mac_debug_optimization%': '0', # Use -O0 unless overridden
|
'mac_debug_optimization%': '0', # Use -O0 unless overridden
|
||||||
@ -400,34 +259,6 @@
|
|||||||
'release_valgrind_build%': 0,
|
'release_valgrind_build%': 0,
|
||||||
},
|
},
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['branding=="Chrome"', {
|
|
||||||
'defines': ['GOOGLE_CHROME_BUILD'],
|
|
||||||
}, { # else: branding!="Chrome"
|
|
||||||
'defines': ['CHROMIUM_BUILD'],
|
|
||||||
}],
|
|
||||||
['toolkit_views==1', {
|
|
||||||
'defines': ['TOOLKIT_VIEWS=1'],
|
|
||||||
}],
|
|
||||||
['chromeos==1', {
|
|
||||||
'defines': ['OS_CHROMEOS=1'],
|
|
||||||
}],
|
|
||||||
['fastbuild!=0', {
|
|
||||||
'conditions': [
|
|
||||||
# Finally, for Windows, we simply turn on profiling.
|
|
||||||
['OS=="win"', {
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCLinkerTool': {
|
|
||||||
'GenerateDebugInformation': 'false',
|
|
||||||
},
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
'DebugInformationFormat': '0',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, { # else: OS != "win"
|
|
||||||
'cflags': [ '-g1' ],
|
|
||||||
}],
|
|
||||||
], # conditions for fastbuild.
|
|
||||||
}], # fastbuild!=0
|
|
||||||
['selinux==1', {
|
['selinux==1', {
|
||||||
'defines': ['CHROMIUM_SELINUX=1'],
|
'defines': ['CHROMIUM_SELINUX=1'],
|
||||||
}],
|
}],
|
||||||
@ -438,16 +269,6 @@
|
|||||||
}],
|
}],
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
['enable_gpu==1', {
|
|
||||||
'defines': [
|
|
||||||
'ENABLE_GPU=1',
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
['enable_gles==1', {
|
|
||||||
'defines': [
|
|
||||||
'ENABLE_GLES=1',
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
['coverage!=0', {
|
['coverage!=0', {
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['OS=="mac"', {
|
['OS=="mac"', {
|
||||||
@ -489,8 +310,6 @@
|
|||||||
}], # coverage!=0
|
}], # coverage!=0
|
||||||
], # conditions for 'target_defaults'
|
], # conditions for 'target_defaults'
|
||||||
'target_conditions': [
|
'target_conditions': [
|
||||||
['chromium_code==0', {
|
|
||||||
'conditions': [
|
|
||||||
[ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', {
|
[ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', {
|
||||||
'cflags!': [
|
'cflags!': [
|
||||||
'-Wall',
|
'-Wall',
|
||||||
@ -503,11 +322,13 @@
|
|||||||
'_CRT_SECURE_NO_DEPRECATE',
|
'_CRT_SECURE_NO_DEPRECATE',
|
||||||
'_CRT_NONSTDC_NO_WARNINGS',
|
'_CRT_NONSTDC_NO_WARNINGS',
|
||||||
'_CRT_NONSTDC_NO_DEPRECATE',
|
'_CRT_NONSTDC_NO_DEPRECATE',
|
||||||
|
# This is required for ATL to use XP-safe versions of its functions.
|
||||||
|
'_USING_V110_SDK71_',
|
||||||
],
|
],
|
||||||
'msvs_disabled_warnings': [4800],
|
'msvs_disabled_warnings': [4800],
|
||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'WarnAsError': 'false',
|
'WarnAsError': 'true',
|
||||||
'Detect64BitPortabilityProblems': 'false',
|
'Detect64BitPortabilityProblems': 'false',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -518,54 +339,6 @@
|
|||||||
'WARNING_CFLAGS!': ['-Wall'],
|
'WARNING_CFLAGS!': ['-Wall'],
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
],
|
|
||||||
}, {
|
|
||||||
# In Chromium code, we define __STDC_FORMAT_MACROS in order to get the
|
|
||||||
# C99 macros on Mac and Linux.
|
|
||||||
'defines': [
|
|
||||||
'__STDC_FORMAT_MACROS',
|
|
||||||
],
|
|
||||||
'conditions': [
|
|
||||||
['OS!="win"', {
|
|
||||||
'sources/': [ ['exclude', '_win(_unittest)?\\.cc$'],
|
|
||||||
['exclude', '/win/'],
|
|
||||||
['exclude', '/win_[^/]*\\.cc$'] ],
|
|
||||||
}],
|
|
||||||
['OS!="mac"', {
|
|
||||||
'sources/': [ ['exclude', '_(cocoa|mac)(_unittest)?\\.cc$'],
|
|
||||||
['exclude', '/(cocoa|mac)/'],
|
|
||||||
['exclude', '\.mm?$' ] ],
|
|
||||||
}],
|
|
||||||
['OS!="linux" and OS!="freebsd" and OS!="openbsd"', {
|
|
||||||
'sources/': [
|
|
||||||
['exclude', '_(chromeos|gtk|x|x11|xdg)(_unittest)?\\.cc$'],
|
|
||||||
['exclude', '/gtk/'],
|
|
||||||
['exclude', '/(gtk|x11)_[^/]*\\.cc$'],
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
['OS!="linux"', {
|
|
||||||
'sources/': [
|
|
||||||
['exclude', '_linux(_unittest)?\\.cc$'],
|
|
||||||
['exclude', '/linux/'],
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
# We use "POSIX" to refer to all non-Windows operating systems.
|
|
||||||
['OS=="win"', {
|
|
||||||
'sources/': [ ['exclude', '_posix\\.cc$'] ],
|
|
||||||
}],
|
|
||||||
# Though Skia is conceptually shared by Linux and Windows,
|
|
||||||
# the only _skia files in our tree are Linux-specific.
|
|
||||||
['OS!="linux" and OS!="freebsd" and OS!="openbsd"', {
|
|
||||||
'sources/': [ ['exclude', '_skia\\.cc$'] ],
|
|
||||||
}],
|
|
||||||
['chromeos!=1', {
|
|
||||||
'sources/': [ ['exclude', '_chromeos\\.cc$'] ]
|
|
||||||
}],
|
|
||||||
['toolkit_views==0', {
|
|
||||||
'sources/': [ ['exclude', '_views\\.cc$'] ]
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
], # target_conditions for 'target_defaults'
|
], # target_conditions for 'target_defaults'
|
||||||
'default_configuration': 'Debug',
|
'default_configuration': 'Debug',
|
||||||
'configurations': {
|
'configurations': {
|
||||||
@ -590,6 +363,7 @@
|
|||||||
'abstract': 1,
|
'abstract': 1,
|
||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCLinkerTool': {
|
'VCLinkerTool': {
|
||||||
|
'MinimumRequiredVersion': '5.01', # XP.
|
||||||
'TargetMachine': '1',
|
'TargetMachine': '1',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -715,7 +489,30 @@
|
|||||||
'inherit_from': ['Common_Base', 'x86_Base', 'Release_Base'],
|
'inherit_from': ['Common_Base', 'x86_Base', 'Release_Base'],
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['msvs_use_common_release', {
|
['msvs_use_common_release', {
|
||||||
'includes': ['release.gypi'],
|
'defines': ['OFFICIAL_BUILD'],
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'Optimization': '3',
|
||||||
|
'StringPooling': 'true',
|
||||||
|
'OmitFramePointers': 'true',
|
||||||
|
'InlineFunctionExpansion': '2',
|
||||||
|
'EnableIntrinsicFunctions': 'true',
|
||||||
|
'FavorSizeOrSpeed': '2',
|
||||||
|
'OmitFramePointers': 'true',
|
||||||
|
'EnableFiberSafeOptimizations': 'true',
|
||||||
|
'WholeProgramOptimization': 'true',
|
||||||
|
},
|
||||||
|
'VCLibrarianTool': {
|
||||||
|
'AdditionalOptions': ['/ltcg', '/expectedoutputsize:120000000'],
|
||||||
|
},
|
||||||
|
'VCLinkerTool': {
|
||||||
|
'LinkIncremental': '1',
|
||||||
|
'OptimizeReferences': '2',
|
||||||
|
'EnableCOMDATFolding': '2',
|
||||||
|
'OptimizeForWindows98': '1',
|
||||||
|
'LinkTimeCodeGeneration': '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
}],
|
}],
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -773,7 +570,7 @@
|
|||||||
'-fvisibility=hidden',
|
'-fvisibility=hidden',
|
||||||
],
|
],
|
||||||
'cflags_cc': [
|
'cflags_cc': [
|
||||||
'-fno-rtti',
|
'-frtti',
|
||||||
'-fno-threadsafe-statics',
|
'-fno-threadsafe-statics',
|
||||||
# Make inline functions have hidden visiblity by default.
|
# Make inline functions have hidden visiblity by default.
|
||||||
# Surprisingly, not covered by -fvisibility=hidden.
|
# Surprisingly, not covered by -fvisibility=hidden.
|
||||||
@ -821,12 +618,6 @@
|
|||||||
'$_LIBDIRFLAGS', '$LDMODULEFLAGS', '$SOURCES',
|
'$_LIBDIRFLAGS', '$LDMODULEFLAGS', '$SOURCES',
|
||||||
'-Wl,--start-group', '$_LIBFLAGS', '-Wl,--end-group']],
|
'-Wl,--start-group', '$_LIBFLAGS', '-Wl,--end-group']],
|
||||||
'IMPLICIT_COMMAND_DEPENDENCIES': 0,
|
'IMPLICIT_COMMAND_DEPENDENCIES': 0,
|
||||||
# -rpath is only used when building with shared libraries.
|
|
||||||
'conditions': [
|
|
||||||
[ 'component=="shared_library"', {
|
|
||||||
'RPATH': '$LIB_DIR',
|
|
||||||
}],
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'scons_import_variables': [
|
'scons_import_variables': [
|
||||||
'AS',
|
'AS',
|
||||||
@ -932,22 +723,13 @@
|
|||||||
# compiler optimized the code, since the value is always kept
|
# compiler optimized the code, since the value is always kept
|
||||||
# in its specified precision.
|
# in its specified precision.
|
||||||
'conditions': [
|
'conditions': [
|
||||||
['branding=="Chromium" and disable_sse2==0', {
|
['disable_sse2==0', {
|
||||||
'cflags': [
|
'cflags': [
|
||||||
'-march=pentium4',
|
'-march=pentium4',
|
||||||
'-msse2',
|
'-msse2',
|
||||||
'-mfpmath=sse',
|
'-mfpmath=sse',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
# ChromeOS targets Pinetrail, which is sse3, but most of the
|
|
||||||
# benefit comes from sse2 so this setting allows ChromeOS
|
|
||||||
# to build on other CPUs. In the future -march=atom would help
|
|
||||||
# but requires a newer compiler.
|
|
||||||
['chromeos==1 and disable_sse2==0', {
|
|
||||||
'cflags': [
|
|
||||||
'-msse2',
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
],
|
],
|
||||||
# -mmmx allows mmintrin.h to be used for mmx intrinsics.
|
# -mmmx allows mmintrin.h to be used for mmx intrinsics.
|
||||||
# video playback is mmx and sse2 optimized.
|
# video playback is mmx and sse2 optimized.
|
||||||
@ -980,7 +762,7 @@
|
|||||||
'-Wa,-mimplicit-it=thumb',
|
'-Wa,-mimplicit-it=thumb',
|
||||||
]
|
]
|
||||||
}],
|
}],
|
||||||
['armv7==1', {
|
['arm_version==7', {
|
||||||
'cflags': [
|
'cflags': [
|
||||||
'-march=armv7-a',
|
'-march=armv7-a',
|
||||||
'-mtune=cortex-a8',
|
'-mtune=cortex-a8',
|
||||||
@ -1019,24 +801,6 @@
|
|||||||
'-fno-strict-aliasing',
|
'-fno-strict-aliasing',
|
||||||
],
|
],
|
||||||
}],
|
}],
|
||||||
['linux_breakpad==1', {
|
|
||||||
'cflags': [ '-gstabs' ],
|
|
||||||
'defines': ['USE_LINUX_BREAKPAD'],
|
|
||||||
}],
|
|
||||||
['linux_use_seccomp_sandbox==1 and buildtype!="Official"', {
|
|
||||||
'defines': ['USE_SECCOMP_SANDBOX'],
|
|
||||||
}],
|
|
||||||
['library=="shared_library"', {
|
|
||||||
# When building with shared libraries, remove the visiblity-hiding
|
|
||||||
# flag.
|
|
||||||
'cflags!': [ '-fvisibility=hidden' ],
|
|
||||||
'conditions': [
|
|
||||||
['target_arch=="x64" or target_arch=="arm"', {
|
|
||||||
# Shared libraries need -fPIC on x86-64 and arm
|
|
||||||
'cflags': ['-fPIC']
|
|
||||||
}]
|
|
||||||
],
|
|
||||||
}],
|
|
||||||
['linux_use_heapchecker==1', {
|
['linux_use_heapchecker==1', {
|
||||||
'variables': {'linux_use_tcmalloc%': 1},
|
'variables': {'linux_use_tcmalloc%': 1},
|
||||||
}],
|
}],
|
||||||
@ -1078,7 +842,7 @@
|
|||||||
'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic
|
'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic
|
||||||
# (Equivalent to -fPIC)
|
# (Equivalent to -fPIC)
|
||||||
'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions
|
'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions
|
||||||
'GCC_ENABLE_CPP_RTTI': 'NO', # -fno-rtti
|
'GCC_ENABLE_CPP_RTTI': 'YES', # -frtti
|
||||||
'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings
|
'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings
|
||||||
# GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden
|
# GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden
|
||||||
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
|
'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES',
|
||||||
@ -1106,51 +870,6 @@
|
|||||||
['_mac_bundle', {
|
['_mac_bundle', {
|
||||||
'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-ObjC']},
|
'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-ObjC']},
|
||||||
}],
|
}],
|
||||||
['_type=="executable" or _type=="shared_library"', {
|
|
||||||
'target_conditions': [
|
|
||||||
['mac_real_dsym == 1', {
|
|
||||||
# To get a real .dSYM bundle produced by dsymutil, set the
|
|
||||||
# debug information format to dwarf-with-dsym. Since
|
|
||||||
# strip_from_xcode will not be used, set Xcode to do the
|
|
||||||
# stripping as well.
|
|
||||||
'configurations': {
|
|
||||||
'Release_Base': {
|
|
||||||
'xcode_settings': {
|
|
||||||
'DEBUG_INFORMATION_FORMAT': 'dwarf-with-dsym',
|
|
||||||
'DEPLOYMENT_POSTPROCESSING': 'YES',
|
|
||||||
'STRIP_INSTALLED_PRODUCT': 'YES',
|
|
||||||
'target_conditions': [
|
|
||||||
['_type=="shared_library"', {
|
|
||||||
# The Xcode default is to strip debugging symbols
|
|
||||||
# only (-S). Local symbols should be stripped as
|
|
||||||
# well, which will be handled by -x. Xcode will
|
|
||||||
# continue to insert -S when stripping even when
|
|
||||||
# additional flags are added with STRIPFLAGS.
|
|
||||||
'STRIPFLAGS': '-x',
|
|
||||||
}], # _type=="shared_library"
|
|
||||||
], # target_conditions
|
|
||||||
}, # xcode_settings
|
|
||||||
}, # configuration "Release"
|
|
||||||
}, # configurations
|
|
||||||
}, { # mac_real_dsym != 1
|
|
||||||
# To get a fast fake .dSYM bundle, use a post-build step to
|
|
||||||
# produce the .dSYM and strip the executable. strip_from_xcode
|
|
||||||
# only operates in the Release configuration.
|
|
||||||
'postbuilds': [
|
|
||||||
{
|
|
||||||
'variables': {
|
|
||||||
# Define strip_from_xcode in a variable ending in _path
|
|
||||||
# so that gyp understands it's a path and performs proper
|
|
||||||
# relativization during dict merging.
|
|
||||||
'strip_from_xcode_path': 'mac/strip_from_xcode',
|
|
||||||
},
|
|
||||||
'postbuild_name': 'Strip If Needed',
|
|
||||||
'action': ['<(strip_from_xcode_path)'],
|
|
||||||
},
|
|
||||||
], # postbuilds
|
|
||||||
}], # mac_real_dsym
|
|
||||||
], # target_conditions
|
|
||||||
}], # _type=="executable" or _type=="shared_library"
|
|
||||||
], # target_conditions
|
], # target_conditions
|
||||||
}, # target_defaults
|
}, # target_defaults
|
||||||
}], # OS=="mac"
|
}], # OS=="mac"
|
||||||
@ -1174,7 +893,9 @@
|
|||||||
'$(VSInstallDir)/VC/atlmfc/include',
|
'$(VSInstallDir)/VC/atlmfc/include',
|
||||||
],
|
],
|
||||||
'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'],
|
'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'],
|
||||||
'msvs_disabled_warnings': [4396, 4503, 4819],
|
'msvs_disabled_warnings': [
|
||||||
|
4100, 4127, 4396, 4503, 4512, 4819, 4995, 4702
|
||||||
|
],
|
||||||
'msvs_settings': {
|
'msvs_settings': {
|
||||||
'VCCLCompilerTool': {
|
'VCCLCompilerTool': {
|
||||||
'MinimalRebuild': 'false',
|
'MinimalRebuild': 'false',
|
||||||
@ -1182,7 +903,7 @@
|
|||||||
'BufferSecurityCheck': 'true',
|
'BufferSecurityCheck': 'true',
|
||||||
'EnableFunctionLevelLinking': 'true',
|
'EnableFunctionLevelLinking': 'true',
|
||||||
'RuntimeTypeInfo': 'false',
|
'RuntimeTypeInfo': 'false',
|
||||||
'WarningLevel': '3',
|
'WarningLevel': '4',
|
||||||
'WarnAsError': 'true',
|
'WarnAsError': 'true',
|
||||||
'DebugInformationFormat': '3',
|
'DebugInformationFormat': '3',
|
||||||
'conditions': [
|
'conditions': [
|
||||||
@ -1322,9 +1043,3 @@
|
|||||||
'SYMROOT': '<(DEPTH)/xcodebuild',
|
'SYMROOT': '<(DEPTH)/xcodebuild',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
# Local Variables:
|
|
||||||
# tab-width:2
|
|
||||||
# indent-tabs-mode:nil
|
|
||||||
# End:
|
|
||||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
|
57
google-breakpad/src/build/filename_rules.gypi
Normal file
57
google-breakpad/src/build/filename_rules.gypi
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
# Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
{
|
||||||
|
'target_conditions': [
|
||||||
|
['OS!="win"', {
|
||||||
|
'sources/': [
|
||||||
|
['exclude', '(^|/)windows/'],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['OS!="linux"', {
|
||||||
|
'sources/': [
|
||||||
|
['exclude', '(^|/)linux/'],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['OS!="mac"', {
|
||||||
|
'sources/': [
|
||||||
|
['exclude', '(^|/)mac/'],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['OS!="android"', {
|
||||||
|
'sources/': [
|
||||||
|
['exclude', '(^|/)android/'],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
['OS!="solaris"', {
|
||||||
|
'sources/': [
|
||||||
|
['exclude', '(^|/)solaris/'],
|
||||||
|
],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}
|
67
google-breakpad/src/build/gyp_breakpad
Executable file
67
google-breakpad/src/build/gyp_breakpad
Executable file
@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
|
||||||
|
script_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
breakpad_root = os.path.abspath(os.path.join(script_dir, os.pardir))
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.join(breakpad_root, 'tools', 'gyp', 'pylib'))
|
||||||
|
import gyp
|
||||||
|
|
||||||
|
def run_gyp(args):
|
||||||
|
rc = gyp.main(args)
|
||||||
|
if rc != 0:
|
||||||
|
print 'Error running GYP'
|
||||||
|
sys.exit(rc)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = sys.argv[1:]
|
||||||
|
args.append(os.path.join(script_dir, 'all.gyp'))
|
||||||
|
|
||||||
|
args.append('-I')
|
||||||
|
args.append(os.path.join(breakpad_root, 'build', 'common.gypi'))
|
||||||
|
|
||||||
|
args.extend(['-D', 'gyp_output_dir=out'])
|
||||||
|
|
||||||
|
# Set the GYP DEPTH variable to the root of the project.
|
||||||
|
args.append('--depth=' + os.path.relpath(breakpad_root))
|
||||||
|
|
||||||
|
print 'Updating projects from gyp files...'
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
run_gyp(args)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
90
google-breakpad/src/build/testing.gypi
Normal file
90
google-breakpad/src/build/testing.gypi
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'gtest',
|
||||||
|
'type': 'static_library',
|
||||||
|
'sources': [
|
||||||
|
'../testing/gtest/src/gtest-all.cc',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'../testing/gtest',
|
||||||
|
'../testing/gtest/include',
|
||||||
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'include_dirs': [
|
||||||
|
'../testing/gtest/include',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'gtest_main',
|
||||||
|
'type': 'static_library',
|
||||||
|
'dependencies': [
|
||||||
|
'gtest',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'gtest/src/gtest_main.cc',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'gmock',
|
||||||
|
'type': 'static_library',
|
||||||
|
'dependencies': [
|
||||||
|
'gtest',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'../testing/src/gmock-all.cc',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'../testing',
|
||||||
|
'../testing/include',
|
||||||
|
],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'include_dirs': [
|
||||||
|
'../testing/include',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'export_dependent_settings': [
|
||||||
|
'gtest',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'gmock_main',
|
||||||
|
'type': 'static_library',
|
||||||
|
'dependencies': [
|
||||||
|
'gmock',
|
||||||
|
],
|
||||||
|
'sources': [
|
||||||
|
'../testing/src/gmock_main.cc',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
@ -27,37 +27,41 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
|
||||||
#include "common/linux/eintr_wrapper.h"
|
#include "common/linux/eintr_wrapper.h"
|
||||||
#include "common/linux/ignore_ret.h"
|
#include "common/linux/ignore_ret.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
bool
|
namespace {
|
||||||
CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
|
|
||||||
{
|
class CrashGenerationClientImpl : public CrashGenerationClient {
|
||||||
|
public:
|
||||||
|
explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {}
|
||||||
|
virtual ~CrashGenerationClientImpl() {}
|
||||||
|
|
||||||
|
virtual bool RequestDump(const void* blob, size_t blob_size) {
|
||||||
int fds[2];
|
int fds[2];
|
||||||
sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
if (sys_pipe(fds) < 0)
|
||||||
|
return false;
|
||||||
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
|
static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int));
|
||||||
|
|
||||||
struct kernel_msghdr msg;
|
struct kernel_iovec iov;
|
||||||
my_memset(&msg, 0, sizeof(struct kernel_msghdr));
|
iov.iov_base = const_cast<void*>(blob);
|
||||||
struct kernel_iovec iov[1];
|
iov.iov_len = blob_size;
|
||||||
iov[0].iov_base = const_cast<void*>(blob);
|
|
||||||
iov[0].iov_len = blob_size;
|
|
||||||
|
|
||||||
msg.msg_iov = iov;
|
struct kernel_msghdr msg = { 0 };
|
||||||
msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]);
|
msg.msg_iov = &iov;
|
||||||
char cmsg[kControlMsgSize];
|
msg.msg_iovlen = 1;
|
||||||
my_memset(cmsg, 0, kControlMsgSize);
|
char cmsg[kControlMsgSize] = "";
|
||||||
msg.msg_control = cmsg;
|
msg.msg_control = cmsg;
|
||||||
msg.msg_controllen = sizeof(cmsg);
|
msg.msg_controllen = sizeof(cmsg);
|
||||||
|
|
||||||
@ -70,23 +74,32 @@ CrashGenerationClient::RequestDump(const void* blob, size_t blob_size)
|
|||||||
|
|
||||||
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
|
ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0));
|
||||||
sys_close(fds[1]);
|
sys_close(fds[1]);
|
||||||
if (ret <= 0)
|
if (ret < 0) {
|
||||||
|
sys_close(fds[0]);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// wait for an ACK from the server
|
// Wait for an ACK from the server.
|
||||||
char b;
|
char b;
|
||||||
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
|
IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1)));
|
||||||
|
sys_close(fds[0]);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//static
|
private:
|
||||||
CrashGenerationClient*
|
int server_fd_;
|
||||||
CrashGenerationClient::TryCreate(int server_fd)
|
|
||||||
{
|
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl);
|
||||||
if (0 > server_fd)
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// static
|
||||||
|
CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) {
|
||||||
|
if (server_fd < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
return new CrashGenerationClient(server_fd);
|
return new CrashGenerationClientImpl(server_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace google_breakpad
|
||||||
|
@ -30,38 +30,34 @@
|
|||||||
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||||
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
|
||||||
|
|
||||||
|
#include "common/basictypes.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// CrashGenerationClient is an interface for implementing out-of-process crash
|
||||||
|
// dumping. The default implementation, accessed via the TryCreate() factory,
|
||||||
|
// works in conjunction with the CrashGenerationServer to generate a minidump
|
||||||
|
// via a remote process.
|
||||||
class CrashGenerationClient {
|
class CrashGenerationClient {
|
||||||
public:
|
public:
|
||||||
~CrashGenerationClient()
|
CrashGenerationClient() {}
|
||||||
{
|
virtual ~CrashGenerationClient() {}
|
||||||
}
|
|
||||||
|
|
||||||
// Request the crash server to generate a dump. |blob| is a hack,
|
// Request the crash server to generate a dump. |blob| is an opaque
|
||||||
// see exception_handler.h and minidump_writer.h
|
// CrashContext pointer from exception_handler.h.
|
||||||
//
|
// Returns true if the dump was successful; false otherwise.
|
||||||
// Return true if the dump was successful; false otherwise.
|
virtual bool RequestDump(const void* blob, size_t blob_size) = 0;
|
||||||
bool RequestDump(const void* blob, size_t blob_size);
|
|
||||||
|
|
||||||
// Return a new CrashGenerationClient if |server_fd| is valid and
|
// Returns a new CrashGenerationClient if |server_fd| is valid and
|
||||||
// connects to a CrashGenerationServer. Otherwise, return NULL.
|
// connects to a CrashGenerationServer. Otherwise, return NULL.
|
||||||
// The returned CrashGenerationClient* is owned by the caller of
|
// The returned CrashGenerationClient* is owned by the caller of
|
||||||
// this function.
|
// this function.
|
||||||
static CrashGenerationClient* TryCreate(int server_fd);
|
static CrashGenerationClient* TryCreate(int server_fd);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CrashGenerationClient(int server_fd) : server_fd_(server_fd)
|
DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient);
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int server_fd_;
|
|
||||||
|
|
||||||
// prevent copy construction and assignment
|
|
||||||
CrashGenerationClient(const CrashGenerationClient&);
|
|
||||||
CrashGenerationClient& operator=(const CrashGenerationClient&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
@ -51,116 +51,6 @@
|
|||||||
|
|
||||||
static const char kCommandQuit = 'x';
|
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 {
|
namespace google_breakpad {
|
||||||
|
|
||||||
CrashGenerationServer::CrashGenerationServer(
|
CrashGenerationServer::CrashGenerationServer(
|
||||||
@ -349,7 +239,7 @@ CrashGenerationServer::ClientEvent(short revents)
|
|||||||
// A nasty process could try and send us too many descriptors and
|
// A nasty process could try and send us too many descriptors and
|
||||||
// force a leak.
|
// force a leak.
|
||||||
for (unsigned i = 0; i < num_fds; ++i)
|
for (unsigned i = 0; i < num_fds; ++i)
|
||||||
HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]));
|
close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
|
signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0];
|
||||||
@ -363,24 +253,7 @@ CrashGenerationServer::ClientEvent(short revents)
|
|||||||
|
|
||||||
if (crashing_pid == -1 || signal_fd == -1) {
|
if (crashing_pid == -1 || signal_fd == -1) {
|
||||||
if (signal_fd)
|
if (signal_fd)
|
||||||
HANDLE_EINTR(close(signal_fd));
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +264,7 @@ CrashGenerationServer::ClientEvent(short revents)
|
|||||||
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
|
if (!google_breakpad::WriteMinidump(minidump_filename.c_str(),
|
||||||
crashing_pid, crash_context,
|
crashing_pid, crash_context,
|
||||||
kCrashContextSize)) {
|
kCrashContextSize)) {
|
||||||
HANDLE_EINTR(close(signal_fd));
|
close(signal_fd);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,15 +275,8 @@ CrashGenerationServer::ClientEvent(short revents)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the done signal to the process: it can exit now.
|
// Send the done signal to the process: it can exit now.
|
||||||
memset(&msg, 0, sizeof(msg));
|
// (Closing this will make the child's sys_read unblock and return 0.)
|
||||||
struct iovec done_iov;
|
close(signal_fd);
|
||||||
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
||||||
|
#define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
#include <list>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
bool exec; // true if the mapping has the execute bit set.
|
||||||
|
char name[NAME_MAX];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MappingEntry {
|
||||||
|
MappingInfo first;
|
||||||
|
uint8_t second[sizeof(MDGUID)];
|
||||||
|
};
|
||||||
|
|
||||||
|
// A list of <MappingInfo, GUID>
|
||||||
|
typedef std::list<MappingEntry> MappingList;
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
||||||
|
#define CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
||||||
|
|
||||||
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
typedef MDRawContextX86 RawContextCPU;
|
||||||
|
#elif defined(__x86_64)
|
||||||
|
typedef MDRawContextAMD64 RawContextCPU;
|
||||||
|
#elif defined(__ARM_EABI__)
|
||||||
|
typedef MDRawContextARM RawContextCPU;
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
typedef MDRawContextARM64 RawContextCPU;
|
||||||
|
#elif defined(__mips__)
|
||||||
|
typedef MDRawContextMIPS RawContextCPU;
|
||||||
|
#else
|
||||||
|
#error "This code has not been ported to your platform yet."
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H
|
@ -0,0 +1,154 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/seccomp_unwinder.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
#include "common/linux/linux_libc_support.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
void SeccompUnwinder::PopSeccompStackFrame(RawContextCPU* cpu,
|
||||||
|
const MDRawThread& thread,
|
||||||
|
uint8_t* stack_copy) {
|
||||||
|
#if defined(__x86_64)
|
||||||
|
uint64_t bp = cpu->rbp;
|
||||||
|
uint64_t top = thread.stack.start_of_memory_range;
|
||||||
|
for (int i = 4; i--; ) {
|
||||||
|
if (bp < top ||
|
||||||
|
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||||
|
thread.stack.memory.data_size ||
|
||||||
|
bp & 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint64_t old_top = top;
|
||||||
|
top = bp;
|
||||||
|
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||||
|
my_memcpy(&bp, bp_addr, sizeof(bp));
|
||||||
|
if (bp == 0xDEADBEEFDEADBEEFull) {
|
||||||
|
struct {
|
||||||
|
uint64_t r15;
|
||||||
|
uint64_t r14;
|
||||||
|
uint64_t r13;
|
||||||
|
uint64_t r12;
|
||||||
|
uint64_t r11;
|
||||||
|
uint64_t r10;
|
||||||
|
uint64_t r9;
|
||||||
|
uint64_t r8;
|
||||||
|
uint64_t rdi;
|
||||||
|
uint64_t rsi;
|
||||||
|
uint64_t rdx;
|
||||||
|
uint64_t rcx;
|
||||||
|
uint64_t rbx;
|
||||||
|
uint64_t deadbeef;
|
||||||
|
uint64_t rbp;
|
||||||
|
uint64_t fakeret;
|
||||||
|
uint64_t ret;
|
||||||
|
/* char redzone[128]; */
|
||||||
|
} seccomp_stackframe;
|
||||||
|
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||||
|
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||||
|
sizeof(seccomp_stackframe) >
|
||||||
|
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
my_memcpy(&seccomp_stackframe,
|
||||||
|
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||||
|
sizeof(seccomp_stackframe));
|
||||||
|
cpu->rbx = seccomp_stackframe.rbx;
|
||||||
|
cpu->rcx = seccomp_stackframe.rcx;
|
||||||
|
cpu->rdx = seccomp_stackframe.rdx;
|
||||||
|
cpu->rsi = seccomp_stackframe.rsi;
|
||||||
|
cpu->rdi = seccomp_stackframe.rdi;
|
||||||
|
cpu->rbp = seccomp_stackframe.rbp;
|
||||||
|
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
|
||||||
|
cpu->r8 = seccomp_stackframe.r8;
|
||||||
|
cpu->r9 = seccomp_stackframe.r9;
|
||||||
|
cpu->r10 = seccomp_stackframe.r10;
|
||||||
|
cpu->r11 = seccomp_stackframe.r11;
|
||||||
|
cpu->r12 = seccomp_stackframe.r12;
|
||||||
|
cpu->r13 = seccomp_stackframe.r13;
|
||||||
|
cpu->r14 = seccomp_stackframe.r14;
|
||||||
|
cpu->r15 = seccomp_stackframe.r15;
|
||||||
|
cpu->rip = seccomp_stackframe.fakeret;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#elif defined(__i386__)
|
||||||
|
uint32_t bp = cpu->ebp;
|
||||||
|
uint32_t top = thread.stack.start_of_memory_range;
|
||||||
|
for (int i = 4; i--; ) {
|
||||||
|
if (bp < top ||
|
||||||
|
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
||||||
|
thread.stack.memory.data_size ||
|
||||||
|
bp & 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
uint32_t old_top = top;
|
||||||
|
top = bp;
|
||||||
|
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
||||||
|
my_memcpy(&bp, bp_addr, sizeof(bp));
|
||||||
|
if (bp == 0xDEADBEEFu) {
|
||||||
|
struct {
|
||||||
|
uint32_t edi;
|
||||||
|
uint32_t esi;
|
||||||
|
uint32_t edx;
|
||||||
|
uint32_t ecx;
|
||||||
|
uint32_t ebx;
|
||||||
|
uint32_t deadbeef;
|
||||||
|
uint32_t ebp;
|
||||||
|
uint32_t fakeret;
|
||||||
|
uint32_t ret;
|
||||||
|
} seccomp_stackframe;
|
||||||
|
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
||||||
|
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
||||||
|
sizeof(seccomp_stackframe) >
|
||||||
|
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
my_memcpy(&seccomp_stackframe,
|
||||||
|
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
||||||
|
sizeof(seccomp_stackframe));
|
||||||
|
cpu->ebx = seccomp_stackframe.ebx;
|
||||||
|
cpu->ecx = seccomp_stackframe.ecx;
|
||||||
|
cpu->edx = seccomp_stackframe.edx;
|
||||||
|
cpu->esi = seccomp_stackframe.esi;
|
||||||
|
cpu->edi = seccomp_stackframe.edi;
|
||||||
|
cpu->ebp = seccomp_stackframe.ebp;
|
||||||
|
cpu->esp = top + 4*sizeof(void*);
|
||||||
|
cpu->eip = seccomp_stackframe.fakeret;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
|
||||||
|
#define CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
||||||
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
struct SeccompUnwinder {
|
||||||
|
|
||||||
|
// Check if the top of the stack is part of a system call that has been
|
||||||
|
// redirected by the seccomp sandbox. If so, try to pop the stack frames
|
||||||
|
// all the way back to the point where the interception happened.
|
||||||
|
static void PopSeccompStackFrame(RawContextCPU* cpu,
|
||||||
|
const MDRawThread& thread,
|
||||||
|
uint8_t* stack_copy);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_SECCOMP_UNWINDER_H
|
@ -0,0 +1,268 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/thread_info.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "common/linux/linux_libc_support.h"
|
||||||
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
// Write a uint16_t to memory
|
||||||
|
// out: memory location to write to
|
||||||
|
// v: value to write.
|
||||||
|
void U16(void* out, uint16_t v) {
|
||||||
|
my_memcpy(out, &v, sizeof(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a uint32_t to memory
|
||||||
|
// out: memory location to write to
|
||||||
|
// v: value to write.
|
||||||
|
void U32(void* out, uint32_t v) {
|
||||||
|
my_memcpy(out, &v, sizeof(v));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
|
||||||
|
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||||
|
return regs.eip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||||
|
out->context_flags = MD_CONTEXT_X86_ALL;
|
||||||
|
|
||||||
|
out->dr0 = dregs[0];
|
||||||
|
out->dr1 = dregs[1];
|
||||||
|
out->dr2 = dregs[2];
|
||||||
|
out->dr3 = dregs[3];
|
||||||
|
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
||||||
|
// format.
|
||||||
|
out->dr6 = dregs[6];
|
||||||
|
out->dr7 = dregs[7];
|
||||||
|
|
||||||
|
out->gs = regs.xgs;
|
||||||
|
out->fs = regs.xfs;
|
||||||
|
out->es = regs.xes;
|
||||||
|
out->ds = regs.xds;
|
||||||
|
|
||||||
|
out->edi = regs.edi;
|
||||||
|
out->esi = regs.esi;
|
||||||
|
out->ebx = regs.ebx;
|
||||||
|
out->edx = regs.edx;
|
||||||
|
out->ecx = regs.ecx;
|
||||||
|
out->eax = regs.eax;
|
||||||
|
|
||||||
|
out->ebp = regs.ebp;
|
||||||
|
out->eip = regs.eip;
|
||||||
|
out->cs = regs.xcs;
|
||||||
|
out->eflags = regs.eflags;
|
||||||
|
out->esp = regs.esp;
|
||||||
|
out->ss = regs.xss;
|
||||||
|
|
||||||
|
out->float_save.control_word = fpregs.cwd;
|
||||||
|
out->float_save.status_word = fpregs.swd;
|
||||||
|
out->float_save.tag_word = fpregs.twd;
|
||||||
|
out->float_save.error_offset = fpregs.fip;
|
||||||
|
out->float_save.error_selector = fpregs.fcs;
|
||||||
|
out->float_save.data_offset = fpregs.foo;
|
||||||
|
out->float_save.data_selector = fpregs.fos;
|
||||||
|
|
||||||
|
// 8 registers * 10 bytes per register.
|
||||||
|
my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8);
|
||||||
|
|
||||||
|
// This matches the Intel fpsave format.
|
||||||
|
U16(out->extended_registers + 0, fpregs.cwd);
|
||||||
|
U16(out->extended_registers + 2, fpregs.swd);
|
||||||
|
U16(out->extended_registers + 4, fpregs.twd);
|
||||||
|
U16(out->extended_registers + 6, fpxregs.fop);
|
||||||
|
U32(out->extended_registers + 8, fpxregs.fip);
|
||||||
|
U16(out->extended_registers + 12, fpxregs.fcs);
|
||||||
|
U32(out->extended_registers + 16, fpregs.foo);
|
||||||
|
U16(out->extended_registers + 20, fpregs.fos);
|
||||||
|
U32(out->extended_registers + 24, fpxregs.mxcsr);
|
||||||
|
|
||||||
|
my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128);
|
||||||
|
my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__x86_64)
|
||||||
|
|
||||||
|
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||||
|
return regs.rip;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||||
|
out->context_flags = MD_CONTEXT_AMD64_FULL |
|
||||||
|
MD_CONTEXT_AMD64_SEGMENTS;
|
||||||
|
|
||||||
|
out->cs = regs.cs;
|
||||||
|
|
||||||
|
out->ds = regs.ds;
|
||||||
|
out->es = regs.es;
|
||||||
|
out->fs = regs.fs;
|
||||||
|
out->gs = regs.gs;
|
||||||
|
|
||||||
|
out->ss = regs.ss;
|
||||||
|
out->eflags = regs.eflags;
|
||||||
|
|
||||||
|
out->dr0 = dregs[0];
|
||||||
|
out->dr1 = dregs[1];
|
||||||
|
out->dr2 = dregs[2];
|
||||||
|
out->dr3 = dregs[3];
|
||||||
|
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
||||||
|
// format.
|
||||||
|
out->dr6 = dregs[6];
|
||||||
|
out->dr7 = dregs[7];
|
||||||
|
|
||||||
|
out->rax = regs.rax;
|
||||||
|
out->rcx = regs.rcx;
|
||||||
|
out->rdx = regs.rdx;
|
||||||
|
out->rbx = regs.rbx;
|
||||||
|
|
||||||
|
out->rsp = regs.rsp;
|
||||||
|
|
||||||
|
out->rbp = regs.rbp;
|
||||||
|
out->rsi = regs.rsi;
|
||||||
|
out->rdi = regs.rdi;
|
||||||
|
out->r8 = regs.r8;
|
||||||
|
out->r9 = regs.r9;
|
||||||
|
out->r10 = regs.r10;
|
||||||
|
out->r11 = regs.r11;
|
||||||
|
out->r12 = regs.r12;
|
||||||
|
out->r13 = regs.r13;
|
||||||
|
out->r14 = regs.r14;
|
||||||
|
out->r15 = regs.r15;
|
||||||
|
|
||||||
|
out->rip = regs.rip;
|
||||||
|
|
||||||
|
out->flt_save.control_word = fpregs.cwd;
|
||||||
|
out->flt_save.status_word = fpregs.swd;
|
||||||
|
out->flt_save.tag_word = fpregs.ftw;
|
||||||
|
out->flt_save.error_opcode = fpregs.fop;
|
||||||
|
out->flt_save.error_offset = fpregs.rip;
|
||||||
|
out->flt_save.error_selector = 0; // We don't have this.
|
||||||
|
out->flt_save.data_offset = fpregs.rdp;
|
||||||
|
out->flt_save.data_selector = 0; // We don't have this.
|
||||||
|
out->flt_save.mx_csr = fpregs.mxcsr;
|
||||||
|
#if defined (__ANDROID__)
|
||||||
|
// Internal bug b/18097559
|
||||||
|
out->flt_save.mx_csr_mask = fpregs.mxcsr_mask;
|
||||||
|
#else
|
||||||
|
out->flt_save.mx_csr_mask = fpregs.mxcr_mask;
|
||||||
|
#endif
|
||||||
|
my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16);
|
||||||
|
my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__ARM_EABI__)
|
||||||
|
|
||||||
|
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||||
|
return regs.uregs[15];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||||
|
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
|
||||||
|
out->iregs[i] = regs.uregs[i];
|
||||||
|
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
|
||||||
|
out->cpsr = 0;
|
||||||
|
#if !defined(__ANDROID__)
|
||||||
|
out->float_save.fpscr = fpregs.fpsr |
|
||||||
|
(static_cast<uint64_t>(fpregs.fpcr) << 32);
|
||||||
|
// TODO: sort this out, actually collect floating point registers
|
||||||
|
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||||
|
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
|
||||||
|
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||||
|
return regs.pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||||
|
out->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||||
|
|
||||||
|
out->cpsr = static_cast<uint32_t>(regs.pstate);
|
||||||
|
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
|
||||||
|
out->iregs[i] = regs.regs[i];
|
||||||
|
out->iregs[MD_CONTEXT_ARM64_REG_SP] = regs.sp;
|
||||||
|
out->iregs[MD_CONTEXT_ARM64_REG_PC] = regs.pc;
|
||||||
|
|
||||||
|
out->float_save.fpsr = fpregs.fpsr;
|
||||||
|
out->float_save.fpcr = fpregs.fpcr;
|
||||||
|
my_memcpy(&out->float_save.regs, &fpregs.vregs,
|
||||||
|
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__mips__)
|
||||||
|
|
||||||
|
uintptr_t ThreadInfo::GetInstructionPointer() const {
|
||||||
|
return regs.epc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
|
||||||
|
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||||
|
out->iregs[i] = regs.regs[i];
|
||||||
|
|
||||||
|
out->mdhi = regs.hi;
|
||||||
|
out->mdlo = regs.lo;
|
||||||
|
|
||||||
|
for (int i = 0; i < MD_CONTEXT_MIPS_DSP_COUNT; ++i) {
|
||||||
|
out->hi[i] = hi[i];
|
||||||
|
out->lo[i] = lo[i];
|
||||||
|
}
|
||||||
|
out->dsp_control = dsp_control;
|
||||||
|
|
||||||
|
out->epc = regs.epc;
|
||||||
|
out->badvaddr = regs.badvaddr;
|
||||||
|
out->status = regs.status;
|
||||||
|
out->cause = regs.cause;
|
||||||
|
|
||||||
|
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
||||||
|
out->float_save.regs[i] = fpregs.regs[i];
|
||||||
|
|
||||||
|
out->float_save.fpcsr = fpregs.fpcsr;
|
||||||
|
out->float_save.fir = fpregs.fir;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
@ -0,0 +1,88 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
||||||
|
#define CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
||||||
|
|
||||||
|
#include <sys/ucontext.h>
|
||||||
|
#include <sys/user.h>
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/raw_context_cpu.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
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
// Use the structures defined in <asm/ptrace.h>
|
||||||
|
struct user_pt_regs regs;
|
||||||
|
struct user_fpsimd_state fpregs;
|
||||||
|
#elif defined(__mips__)
|
||||||
|
user_regs_struct regs;
|
||||||
|
user_fpregs_struct fpregs;
|
||||||
|
uint32_t hi[3];
|
||||||
|
uint32_t lo[3];
|
||||||
|
uint32_t dsp_control;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Returns the instruction pointer (platform-dependent impl.).
|
||||||
|
uintptr_t GetInstructionPointer() const;
|
||||||
|
|
||||||
|
// Fills a RawContextCPU using the context in the ThreadInfo object.
|
||||||
|
void FillCPUContext(RawContextCPU* out) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_
|
@ -0,0 +1,251 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||||
|
|
||||||
|
#include "common/linux/linux_libc_support.h"
|
||||||
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// Minidump defines register structures which are different from the raw
|
||||||
|
// structures which we get from the kernel. These are platform specific
|
||||||
|
// functions to juggle the ucontext and user structures into minidump format.
|
||||||
|
|
||||||
|
#if defined(__i386__)
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.gregs[REG_ESP];
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.gregs[REG_EIP];
|
||||||
|
}
|
||||||
|
|
||||||
|
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||||
|
const struct _libc_fpstate* fp) {
|
||||||
|
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||||
|
|
||||||
|
out->context_flags = MD_CONTEXT_X86_FULL |
|
||||||
|
MD_CONTEXT_X86_FLOATING_POINT;
|
||||||
|
|
||||||
|
out->gs = regs[REG_GS];
|
||||||
|
out->fs = regs[REG_FS];
|
||||||
|
out->es = regs[REG_ES];
|
||||||
|
out->ds = regs[REG_DS];
|
||||||
|
|
||||||
|
out->edi = regs[REG_EDI];
|
||||||
|
out->esi = regs[REG_ESI];
|
||||||
|
out->ebx = regs[REG_EBX];
|
||||||
|
out->edx = regs[REG_EDX];
|
||||||
|
out->ecx = regs[REG_ECX];
|
||||||
|
out->eax = regs[REG_EAX];
|
||||||
|
|
||||||
|
out->ebp = regs[REG_EBP];
|
||||||
|
out->eip = regs[REG_EIP];
|
||||||
|
out->cs = regs[REG_CS];
|
||||||
|
out->eflags = regs[REG_EFL];
|
||||||
|
out->esp = regs[REG_UESP];
|
||||||
|
out->ss = regs[REG_SS];
|
||||||
|
|
||||||
|
out->float_save.control_word = fp->cw;
|
||||||
|
out->float_save.status_word = fp->sw;
|
||||||
|
out->float_save.tag_word = fp->tag;
|
||||||
|
out->float_save.error_offset = fp->ipoff;
|
||||||
|
out->float_save.error_selector = fp->cssel;
|
||||||
|
out->float_save.data_offset = fp->dataoff;
|
||||||
|
out->float_save.data_selector = fp->datasel;
|
||||||
|
|
||||||
|
// 8 registers * 10 bytes per register.
|
||||||
|
my_memcpy(out->float_save.register_area, fp->_st, 10 * 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__x86_64)
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.gregs[REG_RSP];
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.gregs[REG_RIP];
|
||||||
|
}
|
||||||
|
|
||||||
|
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||||
|
const struct _libc_fpstate* fpregs) {
|
||||||
|
const greg_t* regs = uc->uc_mcontext.gregs;
|
||||||
|
|
||||||
|
out->context_flags = MD_CONTEXT_AMD64_FULL;
|
||||||
|
|
||||||
|
out->cs = regs[REG_CSGSFS] & 0xffff;
|
||||||
|
|
||||||
|
out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
|
||||||
|
out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
|
||||||
|
|
||||||
|
out->eflags = regs[REG_EFL];
|
||||||
|
|
||||||
|
out->rax = regs[REG_RAX];
|
||||||
|
out->rcx = regs[REG_RCX];
|
||||||
|
out->rdx = regs[REG_RDX];
|
||||||
|
out->rbx = regs[REG_RBX];
|
||||||
|
|
||||||
|
out->rsp = regs[REG_RSP];
|
||||||
|
out->rbp = regs[REG_RBP];
|
||||||
|
out->rsi = regs[REG_RSI];
|
||||||
|
out->rdi = regs[REG_RDI];
|
||||||
|
out->r8 = regs[REG_R8];
|
||||||
|
out->r9 = regs[REG_R9];
|
||||||
|
out->r10 = regs[REG_R10];
|
||||||
|
out->r11 = regs[REG_R11];
|
||||||
|
out->r12 = regs[REG_R12];
|
||||||
|
out->r13 = regs[REG_R13];
|
||||||
|
out->r14 = regs[REG_R14];
|
||||||
|
out->r15 = regs[REG_R15];
|
||||||
|
|
||||||
|
out->rip = regs[REG_RIP];
|
||||||
|
|
||||||
|
out->flt_save.control_word = fpregs->cwd;
|
||||||
|
out->flt_save.status_word = fpregs->swd;
|
||||||
|
out->flt_save.tag_word = fpregs->ftw;
|
||||||
|
out->flt_save.error_opcode = fpregs->fop;
|
||||||
|
out->flt_save.error_offset = fpregs->rip;
|
||||||
|
out->flt_save.data_offset = fpregs->rdp;
|
||||||
|
out->flt_save.error_selector = 0; // We don't have this.
|
||||||
|
out->flt_save.data_selector = 0; // We don't have this.
|
||||||
|
out->flt_save.mx_csr = fpregs->mxcsr;
|
||||||
|
out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
|
||||||
|
my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
|
||||||
|
my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__ARM_EABI__)
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.arm_sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.arm_pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||||
|
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||||
|
|
||||||
|
out->iregs[0] = uc->uc_mcontext.arm_r0;
|
||||||
|
out->iregs[1] = uc->uc_mcontext.arm_r1;
|
||||||
|
out->iregs[2] = uc->uc_mcontext.arm_r2;
|
||||||
|
out->iregs[3] = uc->uc_mcontext.arm_r3;
|
||||||
|
out->iregs[4] = uc->uc_mcontext.arm_r4;
|
||||||
|
out->iregs[5] = uc->uc_mcontext.arm_r5;
|
||||||
|
out->iregs[6] = uc->uc_mcontext.arm_r6;
|
||||||
|
out->iregs[7] = uc->uc_mcontext.arm_r7;
|
||||||
|
out->iregs[8] = uc->uc_mcontext.arm_r8;
|
||||||
|
out->iregs[9] = uc->uc_mcontext.arm_r9;
|
||||||
|
out->iregs[10] = uc->uc_mcontext.arm_r10;
|
||||||
|
|
||||||
|
out->iregs[11] = uc->uc_mcontext.arm_fp;
|
||||||
|
out->iregs[12] = uc->uc_mcontext.arm_ip;
|
||||||
|
out->iregs[13] = uc->uc_mcontext.arm_sp;
|
||||||
|
out->iregs[14] = uc->uc_mcontext.arm_lr;
|
||||||
|
out->iregs[15] = uc->uc_mcontext.arm_pc;
|
||||||
|
|
||||||
|
out->cpsr = uc->uc_mcontext.arm_cpsr;
|
||||||
|
|
||||||
|
// TODO: fix this after fixing ExceptionHandler
|
||||||
|
out->float_save.fpscr = 0;
|
||||||
|
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||||
|
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||||
|
const struct fpsimd_context* fpregs) {
|
||||||
|
out->context_flags = MD_CONTEXT_ARM64_FULL;
|
||||||
|
|
||||||
|
out->cpsr = static_cast<uint32_t>(uc->uc_mcontext.pstate);
|
||||||
|
for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i)
|
||||||
|
out->iregs[i] = uc->uc_mcontext.regs[i];
|
||||||
|
out->iregs[MD_CONTEXT_ARM64_REG_SP] = uc->uc_mcontext.sp;
|
||||||
|
out->iregs[MD_CONTEXT_ARM64_REG_PC] = uc->uc_mcontext.pc;
|
||||||
|
|
||||||
|
out->float_save.fpsr = fpregs->fpsr;
|
||||||
|
out->float_save.fpcr = fpregs->fpcr;
|
||||||
|
my_memcpy(&out->float_save.regs, &fpregs->vregs,
|
||||||
|
MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__mips__)
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetStackPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP];
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t UContextReader::GetInstructionPointer(const struct ucontext* uc) {
|
||||||
|
return uc->uc_mcontext.pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UContextReader::FillCPUContext(RawContextCPU *out, const ucontext *uc) {
|
||||||
|
out->context_flags = MD_CONTEXT_MIPS_FULL;
|
||||||
|
|
||||||
|
for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i)
|
||||||
|
out->iregs[i] = uc->uc_mcontext.gregs[i];
|
||||||
|
|
||||||
|
out->mdhi = uc->uc_mcontext.mdhi;
|
||||||
|
out->mdlo = uc->uc_mcontext.mdlo;
|
||||||
|
|
||||||
|
out->hi[0] = uc->uc_mcontext.hi1;
|
||||||
|
out->hi[1] = uc->uc_mcontext.hi2;
|
||||||
|
out->hi[2] = uc->uc_mcontext.hi3;
|
||||||
|
out->lo[0] = uc->uc_mcontext.lo1;
|
||||||
|
out->lo[1] = uc->uc_mcontext.lo2;
|
||||||
|
out->lo[2] = uc->uc_mcontext.lo3;
|
||||||
|
out->dsp_control = uc->uc_mcontext.dsp;
|
||||||
|
|
||||||
|
out->epc = uc->uc_mcontext.pc;
|
||||||
|
out->badvaddr = 0; // Not reported in signal context.
|
||||||
|
out->status = 0; // Not reported in signal context.
|
||||||
|
out->cause = 0; // Not reported in signal context.
|
||||||
|
|
||||||
|
for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i)
|
||||||
|
out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i];
|
||||||
|
|
||||||
|
out->float_save.fpcsr = uc->uc_mcontext.fpc_csr;
|
||||||
|
out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
||||||
|
#define CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
||||||
|
|
||||||
|
#include <sys/ucontext.h>
|
||||||
|
#include <sys/user.h>
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/raw_context_cpu.h"
|
||||||
|
#include "common/memory.h"
|
||||||
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// Wraps platform-dependent implementations of accessors to ucontext structs.
|
||||||
|
struct UContextReader {
|
||||||
|
static uintptr_t GetStackPointer(const struct ucontext* uc);
|
||||||
|
|
||||||
|
static uintptr_t GetInstructionPointer(const struct ucontext* uc);
|
||||||
|
|
||||||
|
// Juggle a arch-specific ucontext into a minidump format
|
||||||
|
// out: the minidump structure
|
||||||
|
// info: the collection of register structures.
|
||||||
|
#if defined(__i386__) || defined(__x86_64)
|
||||||
|
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||||
|
const struct _libc_fpstate* fp);
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
static void FillCPUContext(RawContextCPU *out, const ucontext *uc,
|
||||||
|
const struct fpsimd_context* fpregs);
|
||||||
|
#else
|
||||||
|
static void FillCPUContext(RawContextCPU *out, const ucontext *uc);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H
|
@ -68,6 +68,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -86,9 +87,11 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/basictypes.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "common/memory.h"
|
#include "common/memory.h"
|
||||||
#include "client/linux/log/log.h"
|
#include "client/linux/log/log.h"
|
||||||
|
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||||
#include "common/linux/eintr_wrapper.h"
|
#include "common/linux/eintr_wrapper.h"
|
||||||
@ -141,13 +144,13 @@ void InstallAlternateStackLocked() {
|
|||||||
// SIGSTKSZ may be too small to prevent the signal handlers from overrunning
|
// SIGSTKSZ may be too small to prevent the signal handlers from overrunning
|
||||||
// the alternative stack. Ensure that the size of the alternative stack is
|
// the alternative stack. Ensure that the size of the alternative stack is
|
||||||
// large enough.
|
// large enough.
|
||||||
static const unsigned kSigStackSize = std::max(8192, SIGSTKSZ);
|
static const unsigned kSigStackSize = std::max(16384, SIGSTKSZ);
|
||||||
|
|
||||||
// Only set an alternative stack if there isn't already one, or if the current
|
// Only set an alternative stack if there isn't already one, or if the current
|
||||||
// one is too small.
|
// one is too small.
|
||||||
if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
|
if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp ||
|
||||||
old_stack.ss_size < kSigStackSize) {
|
old_stack.ss_size < kSigStackSize) {
|
||||||
new_stack.ss_sp = malloc(kSigStackSize);
|
new_stack.ss_sp = calloc(1, kSigStackSize);
|
||||||
new_stack.ss_size = kSigStackSize;
|
new_stack.ss_size = kSigStackSize;
|
||||||
|
|
||||||
if (sys_sigaltstack(&new_stack, NULL) == -1) {
|
if (sys_sigaltstack(&new_stack, NULL) == -1) {
|
||||||
@ -185,13 +188,13 @@ void RestoreAlternateStackLocked() {
|
|||||||
stack_installed = false;
|
stack_installed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
// The global exception handler stack. This is needed because there may exist
|
||||||
|
// multiple ExceptionHandler instances in a process. Each will have itself
|
||||||
|
// registered in this stack.
|
||||||
|
std::vector<ExceptionHandler*>* g_handler_stack_ = NULL;
|
||||||
|
pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
// We can stack multiple exception handlers. In that case, this is the global
|
} // namespace
|
||||||
// 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.
|
// Runs before crashing: normal context.
|
||||||
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||||
@ -208,31 +211,34 @@ ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor,
|
|||||||
if (server_fd >= 0)
|
if (server_fd >= 0)
|
||||||
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
|
crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||||
|
|
||||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD())
|
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
|
||||||
|
!minidump_descriptor_.IsMicrodumpOnConsole())
|
||||||
minidump_descriptor_.UpdatePath();
|
minidump_descriptor_.UpdatePath();
|
||||||
|
|
||||||
pthread_mutex_lock(&handler_stack_mutex_);
|
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||||
if (!handler_stack_)
|
if (!g_handler_stack_)
|
||||||
handler_stack_ = new std::vector<ExceptionHandler*>;
|
g_handler_stack_ = new std::vector<ExceptionHandler*>;
|
||||||
if (install_handler) {
|
if (install_handler) {
|
||||||
InstallAlternateStackLocked();
|
InstallAlternateStackLocked();
|
||||||
InstallHandlersLocked();
|
InstallHandlersLocked();
|
||||||
}
|
}
|
||||||
handler_stack_->push_back(this);
|
g_handler_stack_->push_back(this);
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
// Runs before crashing: normal context.
|
||||||
ExceptionHandler::~ExceptionHandler() {
|
ExceptionHandler::~ExceptionHandler() {
|
||||||
pthread_mutex_lock(&handler_stack_mutex_);
|
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||||
std::vector<ExceptionHandler*>::iterator handler =
|
std::vector<ExceptionHandler*>::iterator handler =
|
||||||
std::find(handler_stack_->begin(), handler_stack_->end(), this);
|
std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this);
|
||||||
handler_stack_->erase(handler);
|
g_handler_stack_->erase(handler);
|
||||||
if (handler_stack_->empty()) {
|
if (g_handler_stack_->empty()) {
|
||||||
|
delete g_handler_stack_;
|
||||||
|
g_handler_stack_ = NULL;
|
||||||
RestoreAlternateStackLocked();
|
RestoreAlternateStackLocked();
|
||||||
RestoreHandlersLocked();
|
RestoreHandlersLocked();
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs before crashing: normal context.
|
// Runs before crashing: normal context.
|
||||||
@ -292,7 +298,7 @@ void ExceptionHandler::RestoreHandlersLocked() {
|
|||||||
// static
|
// static
|
||||||
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
||||||
// All the exception signals are blocked at this point.
|
// All the exception signals are blocked at this point.
|
||||||
pthread_mutex_lock(&handler_stack_mutex_);
|
pthread_mutex_lock(&g_handler_stack_mutex_);
|
||||||
|
|
||||||
// Sometimes, Breakpad runs inside a process where some other buggy code
|
// Sometimes, Breakpad runs inside a process where some other buggy code
|
||||||
// saves and restores signal handlers temporarily with 'signal'
|
// saves and restores signal handlers temporarily with 'signal'
|
||||||
@ -319,13 +325,13 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
|||||||
// default one to avoid an infinite loop here.
|
// default one to avoid an infinite loop here.
|
||||||
signal(sig, SIG_DFL);
|
signal(sig, SIG_DFL);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) {
|
for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) {
|
||||||
handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc);
|
handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upon returning from this signal handler, sig will become unmasked and then
|
// Upon returning from this signal handler, sig will become unmasked and then
|
||||||
@ -339,12 +345,13 @@ void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
|
|||||||
RestoreHandlersLocked();
|
RestoreHandlersLocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
pthread_mutex_unlock(&g_handler_stack_mutex_);
|
||||||
|
|
||||||
if (info->si_pid) {
|
if (info->si_pid || sig == SIGABRT) {
|
||||||
// This signal was triggered by somebody sending us the signal with kill().
|
// 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
|
// In order to retrigger it, we have to queue a new signal by calling
|
||||||
// kill() ourselves.
|
// kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is
|
||||||
|
// due to the kernel sending a SIGABRT from a user request via SysRQ.
|
||||||
if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
|
if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) {
|
||||||
// If we failed to kill ourselves (e.g. because a sandbox disallows us
|
// 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
|
// to do so), we instead resort to terminating our process. This will
|
||||||
@ -391,13 +398,24 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
|||||||
bool signal_pid_trusted = info->si_code == SI_USER ||
|
bool signal_pid_trusted = info->si_code == SI_USER ||
|
||||||
info->si_code == SI_TKILL;
|
info->si_code == SI_TKILL;
|
||||||
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
|
if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) {
|
||||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||||
}
|
}
|
||||||
CrashContext context;
|
CrashContext context;
|
||||||
|
// Fill in all the holes in the struct to make Valgrind happy.
|
||||||
|
memset(&context, 0, sizeof(context));
|
||||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
memcpy(&context.context, uc, sizeof(struct ucontext));
|
||||||
#if !defined(__ARM_EABI__)
|
#if defined(__aarch64__)
|
||||||
|
struct ucontext *uc_ptr = (struct ucontext*)uc;
|
||||||
|
struct fpsimd_context *fp_ptr =
|
||||||
|
(struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved;
|
||||||
|
if (fp_ptr->head.magic == FPSIMD_MAGIC) {
|
||||||
|
memcpy(&context.float_state, fp_ptr, sizeof(context.float_state));
|
||||||
|
}
|
||||||
|
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
// FP state is not part of user ABI on ARM Linux.
|
// FP state is not part of user ABI on ARM Linux.
|
||||||
|
// In case of MIPS Linux FP state is already part of struct ucontext
|
||||||
|
// and 'float_state' is not a member of CrashContext.
|
||||||
struct ucontext *uc_ptr = (struct ucontext*)uc;
|
struct ucontext *uc_ptr = (struct ucontext*)uc;
|
||||||
if (uc_ptr->uc_mcontext.fpregs) {
|
if (uc_ptr->uc_mcontext.fpregs) {
|
||||||
memcpy(&context.float_state,
|
memcpy(&context.float_state,
|
||||||
@ -432,9 +450,11 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
|||||||
if (IsOutOfProcess())
|
if (IsOutOfProcess())
|
||||||
return crash_generation_client_->RequestDump(context, sizeof(*context));
|
return crash_generation_client_->RequestDump(context, sizeof(*context));
|
||||||
|
|
||||||
static const unsigned kChildStackSize = 8000;
|
// Allocating too much stack isn't a problem, and better to err on the side
|
||||||
|
// of caution than smash it into random locations.
|
||||||
|
static const unsigned kChildStackSize = 16000;
|
||||||
PageAllocator allocator;
|
PageAllocator allocator;
|
||||||
uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize);
|
uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize));
|
||||||
if (!stack)
|
if (!stack)
|
||||||
return false;
|
return false;
|
||||||
// clone() needs the top-most address. (scrub just to be safe)
|
// clone() needs the top-most address. (scrub just to be safe)
|
||||||
@ -452,28 +472,34 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) {
|
|||||||
// kernels, but we need to know the PID of the cloned process before we
|
// 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
|
// 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
|
// cloned process after creating it, until we have explicitly enabled ptrace
|
||||||
if(sys_pipe(fdes) == -1) {
|
if (sys_pipe(fdes) == -1) {
|
||||||
// Creating the pipe failed. We'll log an error but carry on anyway,
|
// 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
|
// as we'll probably still get a useful crash report. All that will happen
|
||||||
// is the write() and read() calls will fail with EBADF
|
// is the write() and read() calls will fail with EBADF
|
||||||
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
|
static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump "
|
||||||
sys_pipe failed:";
|
"sys_pipe failed:";
|
||||||
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1);
|
||||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||||
logger::write("\n", 1);
|
logger::write("\n", 1);
|
||||||
|
|
||||||
|
// Ensure fdes[0] and fdes[1] are invalid file descriptors.
|
||||||
|
fdes[0] = fdes[1] = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pid_t child = sys_clone(
|
const pid_t child = sys_clone(
|
||||||
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
|
||||||
&thread_arg, NULL, NULL, NULL);
|
&thread_arg, NULL, NULL, NULL);
|
||||||
|
if (child == -1) {
|
||||||
|
sys_close(fdes[0]);
|
||||||
|
sys_close(fdes[1]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
int r, status;
|
|
||||||
// Allow the child to ptrace us
|
// Allow the child to ptrace us
|
||||||
sys_prctl(PR_SET_PTRACER, child);
|
sys_prctl(PR_SET_PTRACER, child, 0, 0, 0);
|
||||||
SendContinueSignalToChild();
|
SendContinueSignalToChild();
|
||||||
do {
|
int status;
|
||||||
r = sys_waitpid(child, &status, __WALL);
|
const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL));
|
||||||
} while (r == -1 && errno == EINTR);
|
|
||||||
|
|
||||||
sys_close(fdes[0]);
|
sys_close(fdes[0]);
|
||||||
sys_close(fdes[1]);
|
sys_close(fdes[1]);
|
||||||
@ -496,9 +522,9 @@ void ExceptionHandler::SendContinueSignalToChild() {
|
|||||||
static const char okToContinueMessage = 'a';
|
static const char okToContinueMessage = 'a';
|
||||||
int r;
|
int r;
|
||||||
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
|
r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char)));
|
||||||
if(r == -1) {
|
if (r == -1) {
|
||||||
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
|
static const char msg[] = "ExceptionHandler::SendContinueSignalToChild "
|
||||||
sys_write failed:";
|
"sys_write failed:";
|
||||||
logger::write(msg, sizeof(msg) - 1);
|
logger::write(msg, sizeof(msg) - 1);
|
||||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||||
logger::write("\n", 1);
|
logger::write("\n", 1);
|
||||||
@ -511,9 +537,9 @@ void ExceptionHandler::WaitForContinueSignal() {
|
|||||||
int r;
|
int r;
|
||||||
char receivedMessage;
|
char receivedMessage;
|
||||||
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
|
r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char)));
|
||||||
if(r == -1) {
|
if (r == -1) {
|
||||||
static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
|
static const char msg[] = "ExceptionHandler::WaitForContinueSignal "
|
||||||
sys_read failed:";
|
"sys_read failed:";
|
||||||
logger::write(msg, sizeof(msg) - 1);
|
logger::write(msg, sizeof(msg) - 1);
|
||||||
logger::write(strerror(errno), strlen(strerror(errno)));
|
logger::write(strerror(errno), strlen(strerror(errno)));
|
||||||
logger::write("\n", 1);
|
logger::write("\n", 1);
|
||||||
@ -524,6 +550,12 @@ void ExceptionHandler::WaitForContinueSignal() {
|
|||||||
// Runs on the cloned process.
|
// Runs on the cloned process.
|
||||||
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
|
||||||
size_t context_size) {
|
size_t context_size) {
|
||||||
|
if (minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||||
|
return google_breakpad::WriteMicrodump(crashing_process,
|
||||||
|
context,
|
||||||
|
context_size,
|
||||||
|
mapping_list_);
|
||||||
|
}
|
||||||
if (minidump_descriptor_.IsFD()) {
|
if (minidump_descriptor_.IsFD()) {
|
||||||
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
|
return google_breakpad::WriteMinidump(minidump_descriptor_.fd(),
|
||||||
minidump_descriptor_.size_limit(),
|
minidump_descriptor_.size_limit(),
|
||||||
@ -559,7 +591,8 @@ bool ExceptionHandler::WriteMinidump(const string& dump_path,
|
|||||||
__attribute__((optimize("no-omit-frame-pointer")))
|
__attribute__((optimize("no-omit-frame-pointer")))
|
||||||
#endif
|
#endif
|
||||||
bool ExceptionHandler::WriteMinidump() {
|
bool ExceptionHandler::WriteMinidump() {
|
||||||
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) {
|
if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() &&
|
||||||
|
!minidump_descriptor_.IsMicrodumpOnConsole()) {
|
||||||
// Update the path of the minidump so that this can be called multiple times
|
// 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
|
// and new files are created for each minidump. This is done before the
|
||||||
// generation happens, as clients may want to access the MinidumpDescriptor
|
// generation happens, as clients may want to access the MinidumpDescriptor
|
||||||
@ -569,11 +602,11 @@ bool ExceptionHandler::WriteMinidump() {
|
|||||||
// Reposition the FD to its beginning and resize it to get rid of the
|
// Reposition the FD to its beginning and resize it to get rid of the
|
||||||
// previous minidump info.
|
// previous minidump info.
|
||||||
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
|
lseek(minidump_descriptor_.fd(), 0, SEEK_SET);
|
||||||
static_cast<void>(ftruncate(minidump_descriptor_.fd(), 0));
|
ignore_result(ftruncate(minidump_descriptor_.fd(), 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow this process to be dumped.
|
// Allow this process to be dumped.
|
||||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
|
||||||
|
|
||||||
CrashContext context;
|
CrashContext context;
|
||||||
int getcontext_result = getcontext(&context.context);
|
int getcontext_result = getcontext(&context.context);
|
||||||
@ -602,7 +635,7 @@ bool ExceptionHandler::WriteMinidump() {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(__ARM_EABI__)
|
#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__)
|
||||||
// FPU state is not part of ARM EABI ucontext_t.
|
// FPU state is not part of ARM EABI ucontext_t.
|
||||||
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
|
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
|
||||||
sizeof(context.float_state));
|
sizeof(context.float_state));
|
||||||
@ -621,6 +654,12 @@ bool ExceptionHandler::WriteMinidump() {
|
|||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
context.siginfo.si_addr =
|
context.siginfo.si_addr =
|
||||||
reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
|
reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc);
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
context.siginfo.si_addr =
|
||||||
|
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
|
||||||
|
#elif defined(__mips__)
|
||||||
|
context.siginfo.si_addr =
|
||||||
|
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
|
||||||
#else
|
#else
|
||||||
#error "This code has not been ported to your platform yet."
|
#error "This code has not been ported to your platform yet."
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,15 +30,13 @@
|
|||||||
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||||
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||||
#include "client/linux/handler/minidump_descriptor.h"
|
#include "client/linux/handler/minidump_descriptor.h"
|
||||||
#include "client/linux/minidump_writer/minidump_writer.h"
|
#include "client/linux/minidump_writer/minidump_writer.h"
|
||||||
@ -129,7 +127,7 @@ class ExceptionHandler {
|
|||||||
ExceptionHandler(const MinidumpDescriptor& descriptor,
|
ExceptionHandler(const MinidumpDescriptor& descriptor,
|
||||||
FilterCallback filter,
|
FilterCallback filter,
|
||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void *callback_context,
|
void* callback_context,
|
||||||
bool install_handler,
|
bool install_handler,
|
||||||
const int server_fd);
|
const int server_fd);
|
||||||
~ExceptionHandler();
|
~ExceptionHandler();
|
||||||
@ -146,6 +144,10 @@ class ExceptionHandler {
|
|||||||
crash_handler_ = callback;
|
crash_handler_ = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_crash_generation_client(CrashGenerationClient* client) {
|
||||||
|
crash_generation_client_.reset(client);
|
||||||
|
}
|
||||||
|
|
||||||
// Writes a minidump immediately. This can be used to capture the execution
|
// Writes a minidump immediately. This can be used to capture the execution
|
||||||
// state independently of a crash.
|
// state independently of a crash.
|
||||||
// Returns true on success.
|
// Returns true on success.
|
||||||
@ -190,9 +192,11 @@ class ExceptionHandler {
|
|||||||
siginfo_t siginfo;
|
siginfo_t siginfo;
|
||||||
pid_t tid; // the crashing thread.
|
pid_t tid; // the crashing thread.
|
||||||
struct ucontext context;
|
struct ucontext context;
|
||||||
#if !defined(__ARM_EABI__)
|
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
|
||||||
struct _libc_fpstate float_state;
|
// In case of MIPS Linux FP state is already part of struct
|
||||||
|
// ucontext so 'float_state' is not required.
|
||||||
|
fpstate_t float_state;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -219,6 +223,10 @@ class ExceptionHandler {
|
|||||||
|
|
||||||
// Force signal handling for the specified signal.
|
// Force signal handling for the specified signal.
|
||||||
bool SimulateSignalDelivery(int sig);
|
bool SimulateSignalDelivery(int sig);
|
||||||
|
|
||||||
|
// Report a crash signal from an SA_SIGINFO signal handler.
|
||||||
|
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Save the old signal handlers and install new ones.
|
// Save the old signal handlers and install new ones.
|
||||||
static bool InstallHandlersLocked();
|
static bool InstallHandlersLocked();
|
||||||
@ -231,7 +239,6 @@ class ExceptionHandler {
|
|||||||
void WaitForContinueSignal();
|
void WaitForContinueSignal();
|
||||||
|
|
||||||
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
static void SignalHandler(int sig, siginfo_t* info, void* uc);
|
||||||
bool HandleSignal(int sig, siginfo_t* info, void* uc);
|
|
||||||
static int ThreadEntry(void* arg);
|
static int ThreadEntry(void* arg);
|
||||||
bool DoDump(pid_t crashing_process, const void* context,
|
bool DoDump(pid_t crashing_process, const void* context,
|
||||||
size_t context_size);
|
size_t context_size);
|
||||||
@ -244,13 +251,11 @@ class ExceptionHandler {
|
|||||||
|
|
||||||
MinidumpDescriptor minidump_descriptor_;
|
MinidumpDescriptor minidump_descriptor_;
|
||||||
|
|
||||||
HandlerCallback crash_handler_;
|
// Must be volatile. The compiler is unaware of the code which runs in
|
||||||
|
// the signal handler which reads this variable. Without volatile the
|
||||||
// The global exception handler stack. This is need becuase there may exist
|
// compiler is free to optimise away writes to this variable which it
|
||||||
// multiple ExceptionHandler instances in a process. Each will have itself
|
// believes are never read.
|
||||||
// registered in this stack.
|
volatile HandlerCallback crash_handler_;
|
||||||
static std::vector<ExceptionHandler*> *handler_stack_;
|
|
||||||
static pthread_mutex_t handler_stack_mutex_;
|
|
||||||
|
|
||||||
// We need to explicitly enable ptrace of parent processes on some
|
// 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
|
// kernels, but we need to know the PID of the cloned process before we
|
||||||
|
@ -35,6 +35,9 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#if defined(__mips__)
|
||||||
|
#include <sys/cachectl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ using namespace google_breakpad;
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// Flush the instruction cache for a given memory range.
|
// Flush the instruction cache for a given memory range.
|
||||||
// Only required on ARM.
|
// Only required on ARM and mips.
|
||||||
void FlushInstructionCache(const char* memory, uint32_t memory_size) {
|
void FlushInstructionCache(const char* memory, uint32_t memory_size) {
|
||||||
#if defined(__arm__)
|
#if defined(__arm__)
|
||||||
long begin = reinterpret_cast<long>(memory);
|
long begin = reinterpret_cast<long>(memory);
|
||||||
@ -72,6 +75,18 @@ void FlushInstructionCache(const char* memory, uint32_t memory_size) {
|
|||||||
# else
|
# else
|
||||||
# error "Your operating system is not supported yet"
|
# error "Your operating system is not supported yet"
|
||||||
# endif
|
# endif
|
||||||
|
#elif defined(__mips__)
|
||||||
|
# if defined(__ANDROID__)
|
||||||
|
// Provided by Android's <unistd.h>
|
||||||
|
long begin = reinterpret_cast<long>(memory);
|
||||||
|
long end = begin + static_cast<long>(memory_size);
|
||||||
|
cacheflush(begin, end, 0);
|
||||||
|
# elif defined(__linux__)
|
||||||
|
// See http://www.linux-mips.org/wiki/Cacheflush_Syscall.
|
||||||
|
cacheflush(const_cast<char*>(memory), memory_size, ICACHE);
|
||||||
|
# else
|
||||||
|
# error "Your operating system is not supported yet"
|
||||||
|
# endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +191,22 @@ static bool DoneCallback(const MinidumpDescriptor& descriptor,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ADDRESS_SANITIZER
|
||||||
|
|
||||||
|
// This is a replacement for "*reinterpret_cast<volatile int*>(NULL) = 0;"
|
||||||
|
// It is needed because GCC is allowed to assume that the program will
|
||||||
|
// not execute any undefined behavior (UB) operation. Further, when GCC
|
||||||
|
// observes that UB statement is reached, it can assume that all statements
|
||||||
|
// leading to the UB one are never executed either, and can completely
|
||||||
|
// optimize them out. In the case of ExceptionHandlerTest::ExternalDumper,
|
||||||
|
// GCC-4.9 optimized out the entire set up of ExceptionHandler, causing
|
||||||
|
// test failure.
|
||||||
|
volatile int *p_null; // external linkage, so GCC can't tell that it
|
||||||
|
// remains NULL. Volatile just for a good measure.
|
||||||
|
static void DoNullPointerDereference() {
|
||||||
|
*p_null = 1;
|
||||||
|
}
|
||||||
|
|
||||||
void ChildCrash(bool use_fd) {
|
void ChildCrash(bool use_fd) {
|
||||||
AutoTempDir temp_dir;
|
AutoTempDir temp_dir;
|
||||||
int fds[2] = {0};
|
int fds[2] = {0};
|
||||||
@ -202,7 +233,7 @@ void ChildCrash(bool use_fd) {
|
|||||||
true, -1));
|
true, -1));
|
||||||
}
|
}
|
||||||
// Crash with the exception handler in scope.
|
// Crash with the exception handler in scope.
|
||||||
*reinterpret_cast<volatile int*>(NULL) = 0;
|
DoNullPointerDereference();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!use_fd)
|
if (!use_fd)
|
||||||
@ -227,6 +258,8 @@ TEST(ExceptionHandlerTest, ChildCrashWithFD) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
|
ASSERT_NO_FATAL_FAILURE(ChildCrash(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // !ADDRESS_SANITIZER
|
||||||
|
|
||||||
static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
|
static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor,
|
||||||
void* context,
|
void* context,
|
||||||
bool succeeded) {
|
bool succeeded) {
|
||||||
@ -268,13 +301,15 @@ static bool InstallRaiseSIGKILL() {
|
|||||||
return sigaction(SIGSEGV, &sa, NULL) != -1;
|
return sigaction(SIGSEGV, &sa, NULL) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ADDRESS_SANITIZER
|
||||||
|
|
||||||
static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter,
|
static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter,
|
||||||
ExceptionHandler::MinidumpCallback done,
|
ExceptionHandler::MinidumpCallback done,
|
||||||
string path) {
|
string path) {
|
||||||
ExceptionHandler handler(
|
ExceptionHandler handler(
|
||||||
MinidumpDescriptor(path), filter, done, NULL, true, -1);
|
MinidumpDescriptor(path), filter, done, NULL, true, -1);
|
||||||
// Crash with the exception handler in scope.
|
// Crash with the exception handler in scope.
|
||||||
*reinterpret_cast<volatile int*>(NULL) = 0;
|
DoNullPointerDereference();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ExceptionHandlerTest, RedeliveryOnFilterCallbackFalse) {
|
TEST(ExceptionHandlerTest, RedeliveryOnFilterCallbackFalse) {
|
||||||
@ -365,7 +400,7 @@ TEST(ExceptionHandlerTest, RedeliveryOnBadSignalHandlerFlag) {
|
|||||||
reinterpret_cast<void*>(SIG_ERR));
|
reinterpret_cast<void*>(SIG_ERR));
|
||||||
|
|
||||||
// Crash with the exception handler in scope.
|
// Crash with the exception handler in scope.
|
||||||
*reinterpret_cast<volatile int*>(NULL) = 0;
|
DoNullPointerDereference();
|
||||||
}
|
}
|
||||||
// SIGKILL means Breakpad's signal handler didn't crash.
|
// SIGKILL means Breakpad's signal handler didn't crash.
|
||||||
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
|
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
|
||||||
@ -435,6 +470,18 @@ TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) {
|
|||||||
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
|
ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // !ADDRESS_SANITIZER
|
||||||
|
|
||||||
|
const unsigned char kIllegalInstruction[] = {
|
||||||
|
#if defined(__mips__)
|
||||||
|
// mfc2 zero,Impl - usually illegal in userspace.
|
||||||
|
0x48, 0x00, 0x00, 0x48
|
||||||
|
#else
|
||||||
|
// This crashes with SIGILL on x86/x86-64/arm.
|
||||||
|
0xff, 0xff, 0xff, 0xff
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
// Test that memory around the instruction pointer is written
|
// Test that memory around the instruction pointer is written
|
||||||
// to the dump as a MinidumpMemoryRegion.
|
// to the dump as a MinidumpMemoryRegion.
|
||||||
TEST(ExceptionHandlerTest, InstructionPointerMemory) {
|
TEST(ExceptionHandlerTest, InstructionPointerMemory) {
|
||||||
@ -446,8 +493,6 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) {
|
|||||||
// data from the minidump afterwards.
|
// data from the minidump afterwards.
|
||||||
const uint32_t kMemorySize = 256; // bytes
|
const uint32_t kMemorySize = 256; // bytes
|
||||||
const int kOffset = kMemorySize / 2;
|
const int kOffset = kMemorySize / 2;
|
||||||
// This crashes with SIGILL on x86/x86-64/arm.
|
|
||||||
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
|
|
||||||
|
|
||||||
const pid_t child = fork();
|
const pid_t child = fork();
|
||||||
if (child == 0) {
|
if (child == 0) {
|
||||||
@ -469,7 +514,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) {
|
|||||||
// Write some instructions that will crash. Put them in the middle
|
// Write some instructions that will crash. Put them in the middle
|
||||||
// of the block of memory, because the minidump should contain 128
|
// of the block of memory, because the minidump should contain 128
|
||||||
// bytes on either side of the instruction pointer.
|
// bytes on either side of the instruction pointer.
|
||||||
memcpy(memory + kOffset, instructions, sizeof(instructions));
|
memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
|
||||||
FlushInstructionCache(memory, kMemorySize);
|
FlushInstructionCache(memory, kMemorySize);
|
||||||
|
|
||||||
// Now execute the instructions, which should crash.
|
// Now execute the instructions, which should crash.
|
||||||
@ -517,12 +562,13 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) {
|
|||||||
ASSERT_TRUE(bytes);
|
ASSERT_TRUE(bytes);
|
||||||
|
|
||||||
uint8_t prefix_bytes[kOffset];
|
uint8_t prefix_bytes[kOffset];
|
||||||
uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)];
|
uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(kIllegalInstruction)];
|
||||||
memset(prefix_bytes, 0, sizeof(prefix_bytes));
|
memset(prefix_bytes, 0, sizeof(prefix_bytes));
|
||||||
memset(suffix_bytes, 0, sizeof(suffix_bytes));
|
memset(suffix_bytes, 0, sizeof(suffix_bytes));
|
||||||
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
|
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
|
EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
|
sizeof(kIllegalInstruction)) == 0);
|
||||||
|
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction),
|
||||||
suffix_bytes, sizeof(suffix_bytes)) == 0);
|
suffix_bytes, sizeof(suffix_bytes)) == 0);
|
||||||
|
|
||||||
unlink(minidump_path.c_str());
|
unlink(minidump_path.c_str());
|
||||||
@ -539,8 +585,6 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
|
|||||||
// data from the minidump afterwards.
|
// data from the minidump afterwards.
|
||||||
const uint32_t kMemorySize = 256; // bytes
|
const uint32_t kMemorySize = 256; // bytes
|
||||||
const int kOffset = 0;
|
const int kOffset = 0;
|
||||||
// This crashes with SIGILL on x86/x86-64/arm.
|
|
||||||
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
|
|
||||||
|
|
||||||
const pid_t child = fork();
|
const pid_t child = fork();
|
||||||
if (child == 0) {
|
if (child == 0) {
|
||||||
@ -562,7 +606,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
|
|||||||
// Write some instructions that will crash. Put them in the middle
|
// Write some instructions that will crash. Put them in the middle
|
||||||
// of the block of memory, because the minidump should contain 128
|
// of the block of memory, because the minidump should contain 128
|
||||||
// bytes on either side of the instruction pointer.
|
// bytes on either side of the instruction pointer.
|
||||||
memcpy(memory + kOffset, instructions, sizeof(instructions));
|
memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
|
||||||
FlushInstructionCache(memory, kMemorySize);
|
FlushInstructionCache(memory, kMemorySize);
|
||||||
|
|
||||||
// Now execute the instructions, which should crash.
|
// Now execute the instructions, which should crash.
|
||||||
@ -609,10 +653,11 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) {
|
|||||||
const uint8_t* bytes = region->GetMemory();
|
const uint8_t* bytes = region->GetMemory();
|
||||||
ASSERT_TRUE(bytes);
|
ASSERT_TRUE(bytes);
|
||||||
|
|
||||||
uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)];
|
uint8_t suffix_bytes[kMemorySize / 2 - sizeof(kIllegalInstruction)];
|
||||||
memset(suffix_bytes, 0, sizeof(suffix_bytes));
|
memset(suffix_bytes, 0, sizeof(suffix_bytes));
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0);
|
EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,
|
||||||
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions),
|
sizeof(kIllegalInstruction)) == 0);
|
||||||
|
EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction),
|
||||||
suffix_bytes, sizeof(suffix_bytes)) == 0);
|
suffix_bytes, sizeof(suffix_bytes)) == 0);
|
||||||
unlink(minidump_path.c_str());
|
unlink(minidump_path.c_str());
|
||||||
}
|
}
|
||||||
@ -630,9 +675,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
|||||||
// if a smaller size is requested, and this test wants to
|
// if a smaller size is requested, and this test wants to
|
||||||
// test the upper bound of the memory range.
|
// test the upper bound of the memory range.
|
||||||
const uint32_t kMemorySize = 4096; // bytes
|
const uint32_t kMemorySize = 4096; // bytes
|
||||||
// This crashes with SIGILL on x86/x86-64/arm.
|
const int kOffset = kMemorySize - sizeof(kIllegalInstruction);
|
||||||
const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff };
|
|
||||||
const int kOffset = kMemorySize - sizeof(instructions);
|
|
||||||
|
|
||||||
const pid_t child = fork();
|
const pid_t child = fork();
|
||||||
if (child == 0) {
|
if (child == 0) {
|
||||||
@ -654,7 +697,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
|||||||
// Write some instructions that will crash. Put them in the middle
|
// Write some instructions that will crash. Put them in the middle
|
||||||
// of the block of memory, because the minidump should contain 128
|
// of the block of memory, because the minidump should contain 128
|
||||||
// bytes on either side of the instruction pointer.
|
// bytes on either side of the instruction pointer.
|
||||||
memcpy(memory + kOffset, instructions, sizeof(instructions));
|
memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction));
|
||||||
FlushInstructionCache(memory, kMemorySize);
|
FlushInstructionCache(memory, kMemorySize);
|
||||||
|
|
||||||
// Now execute the instructions, which should crash.
|
// Now execute the instructions, which should crash.
|
||||||
@ -697,7 +740,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
|||||||
ASSERT_TRUE(region);
|
ASSERT_TRUE(region);
|
||||||
|
|
||||||
const size_t kPrefixSize = 128; // bytes
|
const size_t kPrefixSize = 128; // bytes
|
||||||
EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize());
|
EXPECT_EQ(kPrefixSize + sizeof(kIllegalInstruction), region->GetSize());
|
||||||
const uint8_t* bytes = region->GetMemory();
|
const uint8_t* bytes = region->GetMemory();
|
||||||
ASSERT_TRUE(bytes);
|
ASSERT_TRUE(bytes);
|
||||||
|
|
||||||
@ -705,15 +748,11 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) {
|
|||||||
memset(prefix_bytes, 0, sizeof(prefix_bytes));
|
memset(prefix_bytes, 0, sizeof(prefix_bytes));
|
||||||
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
|
EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0);
|
||||||
EXPECT_TRUE(memcmp(bytes + kPrefixSize,
|
EXPECT_TRUE(memcmp(bytes + kPrefixSize,
|
||||||
instructions, sizeof(instructions)) == 0);
|
kIllegalInstruction, sizeof(kIllegalInstruction)) == 0);
|
||||||
|
|
||||||
unlink(minidump_path.c_str());
|
unlink(minidump_path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
|
|
||||||
// (illegal instruction) instead of SIGSEGV (segmentation fault). Also,
|
|
||||||
// the number of memory regions differs, so there is no point in running
|
|
||||||
// this test if AddressSanitizer is used.
|
|
||||||
#ifndef ADDRESS_SANITIZER
|
#ifndef ADDRESS_SANITIZER
|
||||||
|
|
||||||
// Ensure that an extra memory block doesn't get added when the instruction
|
// Ensure that an extra memory block doesn't get added when the instruction
|
||||||
@ -760,6 +799,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) {
|
|||||||
|
|
||||||
unlink(minidump_path.c_str());
|
unlink(minidump_path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // !ADDRESS_SANITIZER
|
#endif // !ADDRESS_SANITIZER
|
||||||
|
|
||||||
// Test that anonymous memory maps can be annotated with names and IDs.
|
// Test that anonymous memory maps can be annotated with names and IDs.
|
||||||
@ -881,6 +921,8 @@ CrashHandler(const void* crash_context, size_t crash_context_size,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef ADDRESS_SANITIZER
|
||||||
|
|
||||||
TEST(ExceptionHandlerTest, ExternalDumper) {
|
TEST(ExceptionHandlerTest, ExternalDumper) {
|
||||||
int fds[2];
|
int fds[2];
|
||||||
ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
|
ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
|
||||||
@ -894,7 +936,7 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
|
|||||||
ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL,
|
ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL,
|
||||||
reinterpret_cast<void*>(fds[1]), true, -1);
|
reinterpret_cast<void*>(fds[1]), true, -1);
|
||||||
handler.set_crash_handler(CrashHandler);
|
handler.set_crash_handler(CrashHandler);
|
||||||
*reinterpret_cast<volatile int*>(NULL) = 0;
|
DoNullPointerDereference();
|
||||||
}
|
}
|
||||||
close(fds[1]);
|
close(fds[1]);
|
||||||
struct msghdr msg = {0};
|
struct msghdr msg = {0};
|
||||||
@ -953,6 +995,8 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
|
|||||||
unlink(templ.c_str());
|
unlink(templ.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // !ADDRESS_SANITIZER
|
||||||
|
|
||||||
TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) {
|
TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) {
|
||||||
AutoTempDir temp_dir;
|
AutoTempDir temp_dir;
|
||||||
ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
|
ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL,
|
||||||
|
@ -35,8 +35,12 @@
|
|||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
//static
|
||||||
|
const MinidumpDescriptor::MicrodumpOnConsole kMicrodumpOnConsole = {};
|
||||||
|
|
||||||
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor)
|
||||||
: fd_(descriptor.fd_),
|
: mode_(descriptor.mode_),
|
||||||
|
fd_(descriptor.fd_),
|
||||||
directory_(descriptor.directory_),
|
directory_(descriptor.directory_),
|
||||||
c_path_(NULL),
|
c_path_(NULL),
|
||||||
size_limit_(descriptor.size_limit_) {
|
size_limit_(descriptor.size_limit_) {
|
||||||
@ -50,6 +54,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=(
|
|||||||
const MinidumpDescriptor& descriptor) {
|
const MinidumpDescriptor& descriptor) {
|
||||||
assert(descriptor.path_.empty());
|
assert(descriptor.path_.empty());
|
||||||
|
|
||||||
|
mode_ = descriptor.mode_;
|
||||||
fd_ = descriptor.fd_;
|
fd_ = descriptor.fd_;
|
||||||
directory_ = descriptor.directory_;
|
directory_ = descriptor.directory_;
|
||||||
path_.clear();
|
path_.clear();
|
||||||
@ -63,7 +68,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MinidumpDescriptor::UpdatePath() {
|
void MinidumpDescriptor::UpdatePath() {
|
||||||
assert(fd_ == -1 && !directory_.empty());
|
assert(mode_ == kWriteMinidumpToFile && !directory_.empty());
|
||||||
|
|
||||||
GUID guid;
|
GUID guid;
|
||||||
char guid_str[kGUIDStringLength + 1];
|
char guid_str[kGUIDStringLength + 1];
|
||||||
|
@ -37,18 +37,25 @@
|
|||||||
|
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
// The MinidumpDescriptor describes how to access a minidump: it can contain
|
// This class describes how a crash dump should be generated, either:
|
||||||
// either a file descriptor or a path.
|
// - Writing a full minidump to a file in a given directory (the actual path,
|
||||||
// Note that when using files, it is created with the path to a directory.
|
// inside the directory, is determined by this class).
|
||||||
// The actual path where the minidump is generated is created by this class.
|
// - Writing a full minidump to a given fd.
|
||||||
|
// - Writing a reduced microdump to the console (logcat on Android).
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
class MinidumpDescriptor {
|
class MinidumpDescriptor {
|
||||||
public:
|
public:
|
||||||
MinidumpDescriptor() : fd_(-1), size_limit_(-1) {}
|
struct MicrodumpOnConsole {};
|
||||||
|
static const MicrodumpOnConsole kMicrodumpOnConsole;
|
||||||
|
|
||||||
|
MinidumpDescriptor() : mode_(kUninitialized),
|
||||||
|
fd_(-1),
|
||||||
|
size_limit_(-1) {}
|
||||||
|
|
||||||
explicit MinidumpDescriptor(const string& directory)
|
explicit MinidumpDescriptor(const string& directory)
|
||||||
: fd_(-1),
|
: mode_(kWriteMinidumpToFile),
|
||||||
|
fd_(-1),
|
||||||
directory_(directory),
|
directory_(directory),
|
||||||
c_path_(NULL),
|
c_path_(NULL),
|
||||||
size_limit_(-1) {
|
size_limit_(-1) {
|
||||||
@ -56,16 +63,24 @@ class MinidumpDescriptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
explicit MinidumpDescriptor(int fd)
|
explicit MinidumpDescriptor(int fd)
|
||||||
: fd_(fd),
|
: mode_(kWriteMinidumpToFd),
|
||||||
|
fd_(fd),
|
||||||
c_path_(NULL),
|
c_path_(NULL),
|
||||||
size_limit_(-1) {
|
size_limit_(-1) {
|
||||||
assert(fd != -1);
|
assert(fd != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explicit MinidumpDescriptor(const MicrodumpOnConsole&)
|
||||||
|
: mode_(kWriteMicrodumpToConsole),
|
||||||
|
fd_(-1),
|
||||||
|
size_limit_(-1) {}
|
||||||
|
|
||||||
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
|
explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor);
|
||||||
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
|
MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor);
|
||||||
|
|
||||||
bool IsFD() const { return fd_ != -1; }
|
static MinidumpDescriptor getMicrodumpDescriptor();
|
||||||
|
|
||||||
|
bool IsFD() const { return mode_ == kWriteMinidumpToFd; }
|
||||||
|
|
||||||
int fd() const { return fd_; }
|
int fd() const { return fd_; }
|
||||||
|
|
||||||
@ -73,6 +88,10 @@ class MinidumpDescriptor {
|
|||||||
|
|
||||||
const char* path() const { return c_path_; }
|
const char* path() const { return c_path_; }
|
||||||
|
|
||||||
|
bool IsMicrodumpOnConsole() const {
|
||||||
|
return mode_ == kWriteMicrodumpToConsole;
|
||||||
|
}
|
||||||
|
|
||||||
// Updates the path so it is unique.
|
// Updates the path so it is unique.
|
||||||
// Should be called from a normal context: this methods uses the heap.
|
// Should be called from a normal context: this methods uses the heap.
|
||||||
void UpdatePath();
|
void UpdatePath();
|
||||||
@ -81,6 +100,16 @@ class MinidumpDescriptor {
|
|||||||
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
void set_size_limit(off_t limit) { size_limit_ = limit; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum DumpMode {
|
||||||
|
kUninitialized = 0,
|
||||||
|
kWriteMinidumpToFile,
|
||||||
|
kWriteMinidumpToFd,
|
||||||
|
kWriteMicrodumpToConsole
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specifies the dump mode (see DumpMode).
|
||||||
|
DumpMode mode_;
|
||||||
|
|
||||||
// The file descriptor where the minidump is generated.
|
// The file descriptor where the minidump is generated.
|
||||||
int fd_;
|
int fd_;
|
||||||
|
|
||||||
|
@ -0,0 +1,364 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
// This translation unit generates microdumps into the console (logcat on
|
||||||
|
// Android). See crbug.com/410294 for more info and design docs.
|
||||||
|
|
||||||
|
#include "client/linux/microdump_writer/microdump_writer.h"
|
||||||
|
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/seccomp_unwinder.h"
|
||||||
|
#include "client/linux/dump_writer_common/thread_info.h"
|
||||||
|
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||||
|
#include "client/linux/handler/exception_handler.h"
|
||||||
|
#include "client/linux/minidump_writer/linux_ptrace_dumper.h"
|
||||||
|
#include "common/linux/linux_libc_support.h"
|
||||||
|
#include "client/linux/log/log.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using google_breakpad::ExceptionHandler;
|
||||||
|
using google_breakpad::LinuxDumper;
|
||||||
|
using google_breakpad::LinuxPtraceDumper;
|
||||||
|
using google_breakpad::MappingInfo;
|
||||||
|
using google_breakpad::MappingList;
|
||||||
|
using google_breakpad::RawContextCPU;
|
||||||
|
using google_breakpad::SeccompUnwinder;
|
||||||
|
using google_breakpad::ThreadInfo;
|
||||||
|
using google_breakpad::UContextReader;
|
||||||
|
|
||||||
|
class MicrodumpWriter {
|
||||||
|
public:
|
||||||
|
MicrodumpWriter(const ExceptionHandler::CrashContext* context,
|
||||||
|
const MappingList& mappings,
|
||||||
|
LinuxDumper* dumper)
|
||||||
|
: ucontext_(context ? &context->context : NULL),
|
||||||
|
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
|
float_state_(context ? &context->float_state : NULL),
|
||||||
|
#endif
|
||||||
|
dumper_(dumper),
|
||||||
|
mapping_list_(mappings) { }
|
||||||
|
|
||||||
|
~MicrodumpWriter() { dumper_->ThreadsResume(); }
|
||||||
|
|
||||||
|
bool Init() {
|
||||||
|
if (!dumper_->Init())
|
||||||
|
return false;
|
||||||
|
return dumper_->ThreadsSuspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Dump() {
|
||||||
|
bool success;
|
||||||
|
LogLine("-----BEGIN BREAKPAD MICRODUMP-----");
|
||||||
|
success = DumpOSInformation();
|
||||||
|
if (success)
|
||||||
|
success = DumpCrashingThread();
|
||||||
|
if (success)
|
||||||
|
success = DumpMappings();
|
||||||
|
LogLine("-----END BREAKPAD MICRODUMP-----");
|
||||||
|
dumper_->ThreadsResume();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Writes one line to the system log.
|
||||||
|
void LogLine(const char* msg) {
|
||||||
|
logger::write(msg, my_strlen(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stages the given string in the current line buffer.
|
||||||
|
void LogAppend(const char* str) {
|
||||||
|
my_strlcat(log_line_, str, sizeof(log_line_));
|
||||||
|
}
|
||||||
|
|
||||||
|
// As above (required to take precedence over template specialization below).
|
||||||
|
void LogAppend(char* str) {
|
||||||
|
LogAppend(const_cast<const char*>(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stages the hex repr. of the given int type in the current line buffer.
|
||||||
|
template<typename T>
|
||||||
|
void LogAppend(T value) {
|
||||||
|
// Make enough room to hex encode the largest int type + NUL.
|
||||||
|
static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F'};
|
||||||
|
char hexstr[sizeof(T) * 2 + 1];
|
||||||
|
for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4)
|
||||||
|
hexstr[i] = HEX[static_cast<uint8_t>(value) & 0x0F];
|
||||||
|
hexstr[sizeof(T) * 2] = '\0';
|
||||||
|
LogAppend(hexstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stages the buffer content hex-encoded in the current line buffer.
|
||||||
|
void LogAppend(const void* buf, size_t length) {
|
||||||
|
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(buf);
|
||||||
|
for (size_t i = 0; i < length; ++i, ++ptr)
|
||||||
|
LogAppend(*ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes out the current line buffer on the system log.
|
||||||
|
void LogCommitLine() {
|
||||||
|
logger::write(log_line_, my_strlen(log_line_));
|
||||||
|
my_strlcpy(log_line_, "", sizeof(log_line_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DumpOSInformation() {
|
||||||
|
struct utsname uts;
|
||||||
|
if (uname(&uts))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
const char kOSId[] = "A";
|
||||||
|
#else
|
||||||
|
const char kOSId[] = "L";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LogAppend("O ");
|
||||||
|
LogAppend(kOSId);
|
||||||
|
LogAppend(" \"");
|
||||||
|
LogAppend(uts.machine);
|
||||||
|
LogAppend("\" \"");
|
||||||
|
LogAppend(uts.release);
|
||||||
|
LogAppend(" \"");
|
||||||
|
LogAppend(uts.version);
|
||||||
|
LogAppend("\"");
|
||||||
|
LogCommitLine();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DumpThreadStack(uint32_t thread_id,
|
||||||
|
uintptr_t stack_pointer,
|
||||||
|
int max_stack_len,
|
||||||
|
uint8_t** stack_copy) {
|
||||||
|
*stack_copy = NULL;
|
||||||
|
const void* stack;
|
||||||
|
size_t stack_len;
|
||||||
|
|
||||||
|
if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) {
|
||||||
|
assert(false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogAppend("S 0 ");
|
||||||
|
LogAppend(stack_pointer);
|
||||||
|
LogAppend(" ");
|
||||||
|
LogAppend(reinterpret_cast<uintptr_t>(stack));
|
||||||
|
LogAppend(" ");
|
||||||
|
LogAppend(stack_len);
|
||||||
|
LogCommitLine();
|
||||||
|
|
||||||
|
if (max_stack_len >= 0 &&
|
||||||
|
stack_len > static_cast<unsigned int>(max_stack_len)) {
|
||||||
|
stack_len = max_stack_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
*stack_copy = reinterpret_cast<uint8_t*>(Alloc(stack_len));
|
||||||
|
dumper_->CopyFromProcess(*stack_copy, thread_id, stack, stack_len);
|
||||||
|
|
||||||
|
// Dump the content of the stack, splicing it into chunks which size is
|
||||||
|
// compatible with the max logcat line size (see LOGGER_ENTRY_MAX_PAYLOAD).
|
||||||
|
const size_t STACK_DUMP_CHUNK_SIZE = 384;
|
||||||
|
for (size_t stack_off = 0; stack_off < stack_len;
|
||||||
|
stack_off += STACK_DUMP_CHUNK_SIZE) {
|
||||||
|
LogAppend("S ");
|
||||||
|
LogAppend(reinterpret_cast<uintptr_t>(stack) + stack_off);
|
||||||
|
LogAppend(" ");
|
||||||
|
LogAppend(*stack_copy + stack_off,
|
||||||
|
std::min(STACK_DUMP_CHUNK_SIZE, stack_len - stack_off));
|
||||||
|
LogCommitLine();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write information about the crashing thread.
|
||||||
|
bool DumpCrashingThread() {
|
||||||
|
const unsigned num_threads = dumper_->threads().size();
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < num_threads; ++i) {
|
||||||
|
MDRawThread thread;
|
||||||
|
my_memset(&thread, 0, sizeof(thread));
|
||||||
|
thread.thread_id = dumper_->threads()[i];
|
||||||
|
|
||||||
|
// Dump only the crashing thread.
|
||||||
|
if (static_cast<pid_t>(thread.thread_id) != dumper_->crash_thread())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
assert(ucontext_);
|
||||||
|
assert(!dumper_->IsPostMortem());
|
||||||
|
|
||||||
|
uint8_t* stack_copy;
|
||||||
|
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||||
|
if (!DumpThreadStack(thread.thread_id, stack_ptr, -1, &stack_copy))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RawContextCPU cpu;
|
||||||
|
my_memset(&cpu, 0, sizeof(RawContextCPU));
|
||||||
|
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
|
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
|
||||||
|
#else
|
||||||
|
UContextReader::FillCPUContext(&cpu, ucontext_);
|
||||||
|
#endif
|
||||||
|
if (stack_copy)
|
||||||
|
SeccompUnwinder::PopSeccompStackFrame(&cpu, thread, stack_copy);
|
||||||
|
DumpCPUState(&cpu);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpCPUState(RawContextCPU* cpu) {
|
||||||
|
LogAppend("C ");
|
||||||
|
LogAppend(cpu, sizeof(*cpu));
|
||||||
|
LogCommitLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there is caller-provided information about this mapping
|
||||||
|
// in the mapping_list_ list, return true. Otherwise, return false.
|
||||||
|
bool HaveMappingInfo(const MappingInfo& mapping) {
|
||||||
|
for (MappingList::const_iterator iter = mapping_list_.begin();
|
||||||
|
iter != mapping_list_.end();
|
||||||
|
++iter) {
|
||||||
|
// Ignore any mappings that are wholly contained within
|
||||||
|
// mappings in the mapping_info_ list.
|
||||||
|
if (mapping.start_addr >= iter->first.start_addr &&
|
||||||
|
(mapping.start_addr + mapping.size) <=
|
||||||
|
(iter->first.start_addr + iter->first.size)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dump information about the provided |mapping|. If |identifier| is non-NULL,
|
||||||
|
// use it instead of calculating a file ID from the mapping.
|
||||||
|
void DumpModule(const MappingInfo& mapping,
|
||||||
|
bool member,
|
||||||
|
unsigned int mapping_id,
|
||||||
|
const uint8_t* identifier) {
|
||||||
|
MDGUID module_identifier;
|
||||||
|
if (identifier) {
|
||||||
|
// GUID was provided by caller.
|
||||||
|
my_memcpy(&module_identifier, identifier, sizeof(MDGUID));
|
||||||
|
} else {
|
||||||
|
dumper_->ElfFileIdentifierForMapping(
|
||||||
|
mapping,
|
||||||
|
member,
|
||||||
|
mapping_id,
|
||||||
|
reinterpret_cast<uint8_t*>(&module_identifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
char file_name[NAME_MAX];
|
||||||
|
char file_path[NAME_MAX];
|
||||||
|
LinuxDumper::GetMappingEffectiveNameAndPath(
|
||||||
|
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||||
|
|
||||||
|
LogAppend("M ");
|
||||||
|
LogAppend(static_cast<uintptr_t>(mapping.start_addr));
|
||||||
|
LogAppend(" ");
|
||||||
|
LogAppend(mapping.offset);
|
||||||
|
LogAppend(" ");
|
||||||
|
LogAppend(mapping.size);
|
||||||
|
LogAppend(" ");
|
||||||
|
LogAppend(module_identifier.data1);
|
||||||
|
LogAppend(module_identifier.data2);
|
||||||
|
LogAppend(module_identifier.data3);
|
||||||
|
LogAppend(module_identifier.data4[0]);
|
||||||
|
LogAppend(module_identifier.data4[1]);
|
||||||
|
LogAppend(module_identifier.data4[2]);
|
||||||
|
LogAppend(module_identifier.data4[3]);
|
||||||
|
LogAppend(module_identifier.data4[4]);
|
||||||
|
LogAppend(module_identifier.data4[5]);
|
||||||
|
LogAppend(module_identifier.data4[6]);
|
||||||
|
LogAppend(module_identifier.data4[7]);
|
||||||
|
LogAppend(" ");
|
||||||
|
LogAppend(file_name);
|
||||||
|
LogCommitLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write information about the mappings in effect.
|
||||||
|
bool DumpMappings() {
|
||||||
|
// First write all the mappings from the dumper
|
||||||
|
for (unsigned i = 0; i < dumper_->mappings().size(); ++i) {
|
||||||
|
const MappingInfo& mapping = *dumper_->mappings()[i];
|
||||||
|
// Skip mappings which don't look like libraries.
|
||||||
|
if (!strstr(mapping.name, ".so") || // dump only libs (skip fonts, apks).
|
||||||
|
mapping.size < 4096) { // too small to get a signature for.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HaveMappingInfo(mapping))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DumpModule(mapping, true, i, NULL);
|
||||||
|
}
|
||||||
|
// Next write all the mappings provided by the caller
|
||||||
|
for (MappingList::const_iterator iter = mapping_list_.begin();
|
||||||
|
iter != mapping_list_.end();
|
||||||
|
++iter) {
|
||||||
|
DumpModule(iter->first, false, 0, iter->second);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
|
||||||
|
|
||||||
|
const struct ucontext* const ucontext_;
|
||||||
|
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
|
const google_breakpad::fpstate_t* const float_state_;
|
||||||
|
#endif
|
||||||
|
LinuxDumper* dumper_;
|
||||||
|
const MappingList& mapping_list_;
|
||||||
|
char log_line_[512];
|
||||||
|
};
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
bool WriteMicrodump(pid_t crashing_process,
|
||||||
|
const void* blob,
|
||||||
|
size_t blob_size,
|
||||||
|
const MappingList& mappings) {
|
||||||
|
LinuxPtraceDumper dumper(crashing_process);
|
||||||
|
const ExceptionHandler::CrashContext* context = NULL;
|
||||||
|
if (blob) {
|
||||||
|
if (blob_size != sizeof(ExceptionHandler::CrashContext))
|
||||||
|
return false;
|
||||||
|
context = reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
|
||||||
|
dumper.set_crash_address(
|
||||||
|
reinterpret_cast<uintptr_t>(context->siginfo.si_addr));
|
||||||
|
dumper.set_crash_signal(context->siginfo.si_signo);
|
||||||
|
dumper.set_crash_thread(context->tid);
|
||||||
|
}
|
||||||
|
MicrodumpWriter writer(context, mappings, &dumper);
|
||||||
|
if (!writer.Init())
|
||||||
|
return false;
|
||||||
|
return writer.Dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
@ -0,0 +1,58 @@
|
|||||||
|
// Copyright (c) 2014, Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
||||||
|
#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/mapping_info.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// Writes a microdump (a reduced dump containing only the state of the crashing
|
||||||
|
// thread) on the console (logcat on Android). 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.
|
||||||
|
// Args:
|
||||||
|
// 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.
|
||||||
|
// mappings: a list of additional mappings provided by the application.
|
||||||
|
//
|
||||||
|
// Returns true iff successful.
|
||||||
|
bool WriteMicrodump(pid_t crashing_process,
|
||||||
|
const void* blob,
|
||||||
|
size_t blob_size,
|
||||||
|
const MappingList& mappings);
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_
|
@ -36,8 +36,7 @@
|
|||||||
|
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
#include "client/linux/minidump_writer/cpu_set.h"
|
#include "client/linux/minidump_writer/cpu_set.h"
|
||||||
#include "common/linux/eintr_wrapper.h"
|
#include "common/linux/tests/auto_testfile.h"
|
||||||
#include "common/tests/auto_testfile.h"
|
|
||||||
|
|
||||||
using namespace google_breakpad;
|
using namespace google_breakpad;
|
||||||
|
|
||||||
|
@ -33,8 +33,7 @@
|
|||||||
|
|
||||||
#include "client/linux/minidump_writer/line_reader.h"
|
#include "client/linux/minidump_writer/line_reader.h"
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
#include "common/linux/eintr_wrapper.h"
|
#include "common/linux/tests/auto_testfile.h"
|
||||||
#include "common/tests/auto_testfile.h"
|
|
||||||
|
|
||||||
using namespace google_breakpad;
|
using namespace google_breakpad;
|
||||||
|
|
||||||
|
@ -99,6 +99,11 @@ bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
|||||||
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||||
#elif defined(__ARM_EABI__)
|
#elif defined(__ARM_EABI__)
|
||||||
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
||||||
|
#elif defined(__mips__)
|
||||||
|
stack_pointer =
|
||||||
|
reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]);
|
||||||
#else
|
#else
|
||||||
#error "This code hasn't been ported to your platform yet."
|
#error "This code hasn't been ported to your platform yet."
|
||||||
#endif
|
#endif
|
||||||
@ -119,7 +124,7 @@ bool LinuxCoreDumper::ThreadsResume() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool LinuxCoreDumper::EnumerateThreads() {
|
bool LinuxCoreDumper::EnumerateThreads() {
|
||||||
if (!mapped_core_file_.Map(core_path_)) {
|
if (!mapped_core_file_.Map(core_path_, 0)) {
|
||||||
fprintf(stderr, "Could not map core dump file into memory\n");
|
fprintf(stderr, "Could not map core dump file into memory\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -183,7 +188,19 @@ bool LinuxCoreDumper::EnumerateThreads() {
|
|||||||
memset(&info, 0, sizeof(ThreadInfo));
|
memset(&info, 0, sizeof(ThreadInfo));
|
||||||
info.tgid = status->pr_pgrp;
|
info.tgid = status->pr_pgrp;
|
||||||
info.ppid = status->pr_ppid;
|
info.ppid = status->pr_ppid;
|
||||||
|
#if defined(__mips__)
|
||||||
|
for (int i = EF_REG0; i <= EF_REG31; i++)
|
||||||
|
info.regs.regs[i - EF_REG0] = status->pr_reg[i];
|
||||||
|
|
||||||
|
info.regs.lo = status->pr_reg[EF_LO];
|
||||||
|
info.regs.hi = status->pr_reg[EF_HI];
|
||||||
|
info.regs.epc = status->pr_reg[EF_CP0_EPC];
|
||||||
|
info.regs.badvaddr = status->pr_reg[EF_CP0_BADVADDR];
|
||||||
|
info.regs.status = status->pr_reg[EF_CP0_STATUS];
|
||||||
|
info.regs.cause = status->pr_reg[EF_CP0_CAUSE];
|
||||||
|
#else
|
||||||
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
|
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
|
||||||
|
#endif
|
||||||
if (first_thread) {
|
if (first_thread) {
|
||||||
crash_thread_ = pid;
|
crash_thread_ = pid;
|
||||||
crash_signal_ = status->pr_info.si_signo;
|
crash_signal_ = status->pr_info.si_signo;
|
||||||
|
@ -74,18 +74,23 @@ TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) {
|
|||||||
const unsigned kCrashThread = 1;
|
const unsigned kCrashThread = 1;
|
||||||
const int kCrashSignal = SIGABRT;
|
const int kCrashSignal = SIGABRT;
|
||||||
pid_t child_pid;
|
pid_t child_pid;
|
||||||
// TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
|
ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
|
||||||
// CrashGenerator is identified and fixed.
|
kCrashSignal, &child_pid));
|
||||||
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 core_file = crash_generator.GetCoreFilePath();
|
||||||
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
|
const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy();
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
struct stat st;
|
||||||
|
if (stat(core_file.c_str(), &st) != 0) {
|
||||||
|
fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is "
|
||||||
|
"skipped due to no core file being generated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
|
LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str());
|
||||||
|
|
||||||
EXPECT_TRUE(dumper.Init());
|
EXPECT_TRUE(dumper.Init());
|
||||||
|
|
||||||
EXPECT_TRUE(dumper.IsPostMortem());
|
EXPECT_TRUE(dumper.IsPostMortem());
|
||||||
|
@ -38,12 +38,14 @@
|
|||||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <elf.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/line_reader.h"
|
#include "client/linux/minidump_writer/line_reader.h"
|
||||||
|
#include "common/linux/elfutils.h"
|
||||||
#include "common/linux/file_id.h"
|
#include "common/linux/file_id.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "common/linux/memory_mapped_file.h"
|
#include "common/linux/memory_mapped_file.h"
|
||||||
@ -52,6 +54,7 @@
|
|||||||
|
|
||||||
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
static const char kMappedFileUnsafePrefix[] = "/dev/";
|
||||||
static const char kDeletedSuffix[] = " (deleted)";
|
static const char kDeletedSuffix[] = " (deleted)";
|
||||||
|
static const char kReservedFlags[] = " ---p";
|
||||||
|
|
||||||
inline static bool IsMappedFileOpenUnsafe(
|
inline static bool IsMappedFileOpenUnsafe(
|
||||||
const google_breakpad::MappingInfo& mapping) {
|
const google_breakpad::MappingInfo& mapping) {
|
||||||
@ -73,10 +76,13 @@ LinuxDumper::LinuxDumper(pid_t pid)
|
|||||||
: pid_(pid),
|
: pid_(pid),
|
||||||
crash_address_(0),
|
crash_address_(0),
|
||||||
crash_signal_(0),
|
crash_signal_(0),
|
||||||
crash_thread_(0),
|
crash_thread_(pid),
|
||||||
threads_(&allocator_, 8),
|
threads_(&allocator_, 8),
|
||||||
mappings_(&allocator_),
|
mappings_(&allocator_),
|
||||||
auxv_(&allocator_, AT_MAX + 1) {
|
auxv_(&allocator_, AT_MAX + 1) {
|
||||||
|
// The passed-in size to the constructor (above) is only a hint.
|
||||||
|
// Must call .resize() to do actual initialization of the elements.
|
||||||
|
auxv_.resize(AT_MAX + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
LinuxDumper::~LinuxDumper() {
|
LinuxDumper::~LinuxDumper() {
|
||||||
@ -90,8 +96,7 @@ bool
|
|||||||
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||||
bool member,
|
bool member,
|
||||||
unsigned int mapping_id,
|
unsigned int mapping_id,
|
||||||
uint8_t identifier[sizeof(MDGUID)])
|
uint8_t identifier[sizeof(MDGUID)]) {
|
||||||
{
|
|
||||||
assert(!member || mapping_id < mappings_.size());
|
assert(!member || mapping_id < mappings_.size());
|
||||||
my_memset(identifier, 0, sizeof(MDGUID));
|
my_memset(identifier, 0, sizeof(MDGUID));
|
||||||
if (IsMappedFileOpenUnsafe(mapping))
|
if (IsMappedFileOpenUnsafe(mapping))
|
||||||
@ -113,15 +118,16 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
|||||||
|
|
||||||
char filename[NAME_MAX];
|
char filename[NAME_MAX];
|
||||||
size_t filename_len = my_strlen(mapping.name);
|
size_t filename_len = my_strlen(mapping.name);
|
||||||
assert(filename_len < NAME_MAX);
|
if (filename_len >= NAME_MAX) {
|
||||||
if (filename_len >= NAME_MAX)
|
assert(false);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
my_memcpy(filename, mapping.name, filename_len);
|
my_memcpy(filename, mapping.name, filename_len);
|
||||||
filename[filename_len] = '\0';
|
filename[filename_len] = '\0';
|
||||||
bool filename_modified = HandleDeletedFileInMapping(filename);
|
bool filename_modified = HandleDeletedFileInMapping(filename);
|
||||||
|
|
||||||
MemoryMappedFile mapped_file(filename);
|
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||||
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
if (!mapped_file.data() || mapped_file.size() < SELFMAG)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
bool success =
|
bool success =
|
||||||
@ -134,6 +140,120 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
bool ElfFileSoNameFromMappedFile(
|
||||||
|
const void* elf_base, char* soname, size_t soname_size) {
|
||||||
|
if (!IsValidElf(elf_base)) {
|
||||||
|
// Not ELF
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* segment_start;
|
||||||
|
size_t segment_size;
|
||||||
|
int elf_class;
|
||||||
|
if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC,
|
||||||
|
&segment_start, &segment_size, &elf_class)) {
|
||||||
|
// No dynamic section
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* dynstr_start;
|
||||||
|
size_t dynstr_size;
|
||||||
|
if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB,
|
||||||
|
&dynstr_start, &dynstr_size, &elf_class)) {
|
||||||
|
// No dynstr section
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ElfW(Dyn)* dynamic = static_cast<const ElfW(Dyn)*>(segment_start);
|
||||||
|
size_t dcount = segment_size / sizeof(ElfW(Dyn));
|
||||||
|
for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) {
|
||||||
|
if (dyn->d_tag == DT_SONAME) {
|
||||||
|
const char* dynstr = static_cast<const char*>(dynstr_start);
|
||||||
|
if (dyn->d_un.d_val >= dynstr_size) {
|
||||||
|
// Beyond the end of the dynstr section
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const char* str = dynstr + dyn->d_un.d_val;
|
||||||
|
const size_t maxsize = dynstr_size - dyn->d_un.d_val;
|
||||||
|
my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did not find SONAME
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the shared object name (SONAME) by examining the ELF information
|
||||||
|
// for |mapping|. If the SONAME is found copy it into the passed buffer
|
||||||
|
// |soname| and return true. The size of the buffer is |soname_size|.
|
||||||
|
// The SONAME will be truncated if it is too long to fit in the buffer.
|
||||||
|
bool ElfFileSoName(
|
||||||
|
const MappingInfo& mapping, char* soname, size_t soname_size) {
|
||||||
|
if (IsMappedFileOpenUnsafe(mapping)) {
|
||||||
|
// Not safe
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char filename[NAME_MAX];
|
||||||
|
size_t filename_len = my_strlen(mapping.name);
|
||||||
|
if (filename_len >= NAME_MAX) {
|
||||||
|
assert(false);
|
||||||
|
// name too long
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_memcpy(filename, mapping.name, filename_len);
|
||||||
|
filename[filename_len] = '\0';
|
||||||
|
|
||||||
|
MemoryMappedFile mapped_file(filename, mapping.offset);
|
||||||
|
if (!mapped_file.data() || mapped_file.size() < SELFMAG) {
|
||||||
|
// mmap failed
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
// static
|
||||||
|
void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||||
|
char* file_path,
|
||||||
|
size_t file_path_size,
|
||||||
|
char* file_name,
|
||||||
|
size_t file_name_size) {
|
||||||
|
my_strlcpy(file_path, mapping.name, file_path_size);
|
||||||
|
|
||||||
|
// If an executable is mapped from a non-zero offset, this is likely because
|
||||||
|
// the executable was loaded directly from inside an archive file (e.g., an
|
||||||
|
// apk on Android). We try to find the name of the shared object (SONAME) by
|
||||||
|
// looking in the file for ELF sections.
|
||||||
|
bool mapped_from_archive = false;
|
||||||
|
if (mapping.exec && mapping.offset != 0)
|
||||||
|
mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size);
|
||||||
|
|
||||||
|
if (mapped_from_archive) {
|
||||||
|
// Some tools (e.g., stackwalk) extract the basename from the pathname. In
|
||||||
|
// this case, we append the file_name to the mapped archive path as follows:
|
||||||
|
// file_name := libname.so
|
||||||
|
// file_path := /path/to/ARCHIVE.APK/libname.so
|
||||||
|
if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) {
|
||||||
|
my_strlcat(file_path, "/", file_path_size);
|
||||||
|
my_strlcat(file_path, file_name, file_path_size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Common case:
|
||||||
|
// file_path := /path/to/libname.so
|
||||||
|
// file_name := libname.so
|
||||||
|
const char* basename = my_strrchr(file_path, '/');
|
||||||
|
basename = basename == NULL ? file_path : (basename + 1);
|
||||||
|
my_strlcpy(file_name, basename, file_name_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool LinuxDumper::ReadAuxv() {
|
bool LinuxDumper::ReadAuxv() {
|
||||||
char auxv_path[NAME_MAX];
|
char auxv_path[NAME_MAX];
|
||||||
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
|
if (!BuildProcPath(auxv_path, pid_, "auxv")) {
|
||||||
@ -193,6 +313,7 @@ bool LinuxDumper::EnumerateMappings() {
|
|||||||
if (*i1 == '-') {
|
if (*i1 == '-') {
|
||||||
const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
|
const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1);
|
||||||
if (*i2 == ' ') {
|
if (*i2 == ' ') {
|
||||||
|
bool exec = (*(i2 + 3) == 'x');
|
||||||
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
|
const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */);
|
||||||
if (*i3 == ' ') {
|
if (*i3 == ' ') {
|
||||||
const char* name = NULL;
|
const char* name = NULL;
|
||||||
@ -216,11 +337,29 @@ bool LinuxDumper::EnumerateMappings() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Also merge mappings that result from address ranges that the
|
||||||
|
// linker reserved but which a loaded library did not use. These
|
||||||
|
// appear as an anonymous private mapping with no access flags set
|
||||||
|
// and which directly follow an executable mapping.
|
||||||
|
if (!name && !mappings_.empty()) {
|
||||||
|
MappingInfo* module = mappings_.back();
|
||||||
|
if ((start_addr == module->start_addr + module->size) &&
|
||||||
|
module->exec &&
|
||||||
|
module->name[0] == '/' &&
|
||||||
|
offset == 0 && my_strncmp(i2,
|
||||||
|
kReservedFlags,
|
||||||
|
sizeof(kReservedFlags) - 1) == 0) {
|
||||||
|
module->size = end_addr - module->start_addr;
|
||||||
|
line_reader->PopLine(line_len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
MappingInfo* const module = new(allocator_) MappingInfo;
|
MappingInfo* const module = new(allocator_) MappingInfo;
|
||||||
my_memset(module, 0, sizeof(MappingInfo));
|
my_memset(module, 0, sizeof(MappingInfo));
|
||||||
module->start_addr = start_addr;
|
module->start_addr = start_addr;
|
||||||
module->size = end_addr - start_addr;
|
module->size = end_addr - start_addr;
|
||||||
module->offset = offset;
|
module->offset = offset;
|
||||||
|
module->exec = exec;
|
||||||
if (name != NULL) {
|
if (name != NULL) {
|
||||||
const unsigned l = my_strlen(name);
|
const unsigned l = my_strlen(name);
|
||||||
if (l < sizeof(module->name))
|
if (l < sizeof(module->name))
|
||||||
@ -273,7 +412,8 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
|||||||
const MappingInfo* mapping = FindMapping(stack_pointer);
|
const MappingInfo* mapping = FindMapping(stack_pointer);
|
||||||
if (!mapping)
|
if (!mapping)
|
||||||
return false;
|
return false;
|
||||||
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
|
const ptrdiff_t offset = stack_pointer -
|
||||||
|
reinterpret_cast<uint8_t*>(mapping->start_addr);
|
||||||
const ptrdiff_t distance_to_end =
|
const ptrdiff_t distance_to_end =
|
||||||
static_cast<ptrdiff_t>(mapping->size) - offset;
|
static_cast<ptrdiff_t>(mapping->size) - offset;
|
||||||
*stack_len = distance_to_end > kStackToCapture ?
|
*stack_len = distance_to_end > kStackToCapture ?
|
||||||
|
@ -44,19 +44,17 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/user.h>
|
#include <sys/user.h>
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/mapping_info.h"
|
||||||
|
#include "client/linux/dump_writer_common/thread_info.h"
|
||||||
#include "common/memory.h"
|
#include "common/memory.h"
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
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.
|
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||||
#if defined(__i386) || defined(__ARM_EABI__)
|
#if defined(__i386) || defined(__ARM_EABI__) || defined(__mips__)
|
||||||
typedef Elf32_auxv_t elf_aux_entry;
|
typedef Elf32_auxv_t elf_aux_entry;
|
||||||
#elif defined(__x86_64)
|
#elif defined(__x86_64) || defined(__aarch64__)
|
||||||
typedef Elf64_auxv_t elf_aux_entry;
|
typedef Elf64_auxv_t elf_aux_entry;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -67,39 +65,6 @@ typedef typeof(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t;
|
|||||||
// This should always be less than NAME_MAX!
|
// This should always be less than NAME_MAX!
|
||||||
const char kLinuxGateLibraryName[] = "linux-gate.so";
|
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 {
|
class LinuxDumper {
|
||||||
public:
|
public:
|
||||||
explicit LinuxDumper(pid_t pid);
|
explicit LinuxDumper(pid_t pid);
|
||||||
@ -146,7 +111,8 @@ class LinuxDumper {
|
|||||||
virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0;
|
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.
|
// Generate a File ID from the .text section of a mapped entry.
|
||||||
// If not a member, mapping_id is ignored.
|
// If not a member, mapping_id is ignored. This method can also manipulate the
|
||||||
|
// |mapping|.name to truncate "(deleted)" from the file name if necessary.
|
||||||
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
bool ElfFileIdentifierForMapping(const MappingInfo& mapping,
|
||||||
bool member,
|
bool member,
|
||||||
unsigned int mapping_id,
|
unsigned int mapping_id,
|
||||||
@ -163,6 +129,17 @@ class LinuxDumper {
|
|||||||
pid_t crash_thread() const { return crash_thread_; }
|
pid_t crash_thread() const { return crash_thread_; }
|
||||||
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
|
void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; }
|
||||||
|
|
||||||
|
// Extracts the effective path and file name of from |mapping|. In most cases
|
||||||
|
// the effective name/path are just the mapping's path and basename. In some
|
||||||
|
// other cases, however, a library can be mapped from an archive (e.g., when
|
||||||
|
// loading .so libs from an apk on Android) and this method is able to
|
||||||
|
// reconstruct the original file name.
|
||||||
|
static void GetMappingEffectiveNameAndPath(const MappingInfo& mapping,
|
||||||
|
char* file_path,
|
||||||
|
size_t file_path_size,
|
||||||
|
char* file_name,
|
||||||
|
size_t file_name_size);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool ReadAuxv();
|
bool ReadAuxv();
|
||||||
|
|
||||||
|
@ -43,10 +43,14 @@
|
|||||||
|
|
||||||
#if defined(__ARM_EABI__)
|
#if defined(__ARM_EABI__)
|
||||||
#define TID_PTR_REGISTER "r3"
|
#define TID_PTR_REGISTER "r3"
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
#define TID_PTR_REGISTER "x3"
|
||||||
#elif defined(__i386)
|
#elif defined(__i386)
|
||||||
#define TID_PTR_REGISTER "ecx"
|
#define TID_PTR_REGISTER "ecx"
|
||||||
#elif defined(__x86_64)
|
#elif defined(__x86_64)
|
||||||
#define TID_PTR_REGISTER "rcx"
|
#define TID_PTR_REGISTER "rcx"
|
||||||
|
#elif defined(__mips__)
|
||||||
|
#define TID_PTR_REGISTER "$1"
|
||||||
#else
|
#else
|
||||||
#error This test has not been ported to this platform.
|
#error This test has not been ported to this platform.
|
||||||
#endif
|
#endif
|
||||||
|
@ -47,8 +47,13 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/ptrace.h>
|
#include <sys/ptrace.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#if defined(__i386)
|
||||||
|
#include <cpuid.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "client/linux/minidump_writer/directory_reader.h"
|
#include "client/linux/minidump_writer/directory_reader.h"
|
||||||
#include "client/linux/minidump_writer/line_reader.h"
|
#include "client/linux/minidump_writer/line_reader.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
@ -182,6 +187,20 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
|||||||
if (info->ppid == -1 || info->tgid == -1)
|
if (info->ppid == -1 || info->tgid == -1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#ifdef PTRACE_GETREGSET
|
||||||
|
struct iovec io;
|
||||||
|
io.iov_base = &info->regs;
|
||||||
|
io.iov_len = sizeof(info->regs);
|
||||||
|
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
io.iov_base = &info->fpregs;
|
||||||
|
io.iov_len = sizeof(info->fpregs);
|
||||||
|
if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
|
if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -189,11 +208,23 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
|||||||
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
|
if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__i386)
|
#if defined(__i386)
|
||||||
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
|
#if !defined(bit_FXSAVE) // e.g. Clang
|
||||||
return false;
|
#define bit_FXSAVE bit_FXSR
|
||||||
#endif
|
#endif
|
||||||
|
// Detect if the CPU supports the FXSAVE/FXRSTOR instructions
|
||||||
|
int eax, ebx, ecx, edx;
|
||||||
|
__cpuid(1, eax, ebx, ecx, edx);
|
||||||
|
if (edx & bit_FXSAVE) {
|
||||||
|
if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memset(&info->fpxregs, 0, sizeof(info->fpxregs));
|
||||||
|
}
|
||||||
|
#endif // defined(__i386)
|
||||||
|
|
||||||
#if defined(__i386) || defined(__x86_64)
|
#if defined(__i386) || defined(__x86_64)
|
||||||
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
|
for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
|
||||||
@ -208,6 +239,17 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__mips__)
|
||||||
|
for (int i = 0; i < 3; ++i) {
|
||||||
|
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||||
|
reinterpret_cast<void*>(DSP_BASE + (i * 2)), &info->hi[i]);
|
||||||
|
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||||
|
reinterpret_cast<void*>(DSP_BASE + (i * 2) + 1), &info->lo[i]);
|
||||||
|
}
|
||||||
|
sys_ptrace(PTRACE_PEEKUSER, tid,
|
||||||
|
reinterpret_cast<void*>(DSP_CONTROL), &info->dsp_control);
|
||||||
|
#endif
|
||||||
|
|
||||||
const uint8_t* stack_pointer;
|
const uint8_t* stack_pointer;
|
||||||
#if defined(__i386)
|
#if defined(__i386)
|
||||||
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
|
||||||
@ -215,6 +257,11 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
|
|||||||
my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
|
||||||
#elif defined(__ARM_EABI__)
|
#elif defined(__ARM_EABI__)
|
||||||
my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp));
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp));
|
||||||
|
#elif defined(__mips__)
|
||||||
|
stack_pointer =
|
||||||
|
reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]);
|
||||||
#else
|
#else
|
||||||
#error "This code hasn't been ported to your platform yet."
|
#error "This code hasn't been ported to your platform yet."
|
||||||
#endif
|
#endif
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// linux_ptrace_dumper_unittest.cc:
|
// linux_ptrace_dumper_unittest.cc:
|
||||||
// Unit tests for google_breakpad::LinuxPtraceDumoer.
|
// Unit tests for google_breakpad::LinuxPtraceDumper.
|
||||||
//
|
//
|
||||||
// This file was renamed from linux_dumper_unittest.cc and modified due
|
// This file was renamed from linux_dumper_unittest.cc and modified due
|
||||||
// to LinuxDumper being splitted into two classes.
|
// to LinuxDumper being splitted into two classes.
|
||||||
@ -41,6 +41,7 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
#include <sys/poll.h>
|
#include <sys/poll.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
@ -57,20 +58,72 @@
|
|||||||
#include "common/memory.h"
|
#include "common/memory.h"
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
|
#ifndef PR_SET_PTRACER
|
||||||
|
#define PR_SET_PTRACER 0x59616d61
|
||||||
|
#endif
|
||||||
|
|
||||||
using namespace google_breakpad;
|
using namespace google_breakpad;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
typedef testing::Test LinuxPtraceDumperTest;
|
typedef testing::Test LinuxPtraceDumperTest;
|
||||||
|
|
||||||
|
/* Fixture for running tests in a child process. */
|
||||||
|
class LinuxPtraceDumperChildTest : public testing::Test {
|
||||||
|
protected:
|
||||||
|
virtual void SetUp() {
|
||||||
|
child_pid_ = fork();
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
prctl(PR_SET_PTRACER, child_pid_);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gtest is calling TestBody from this class, which sets up a child
|
||||||
|
* process in which the RealTestBody virtual member is called.
|
||||||
|
* As such, TestBody is not supposed to be overridden in derived classes.
|
||||||
|
*/
|
||||||
|
virtual void TestBody() /* final */ {
|
||||||
|
if (child_pid_ == 0) {
|
||||||
|
// child process
|
||||||
|
RealTestBody();
|
||||||
|
exit(HasFatalFailure() ? kFatalFailure :
|
||||||
|
(HasNonfatalFailure() ? kNonFatalFailure : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT_TRUE(child_pid_ > 0);
|
||||||
|
int status;
|
||||||
|
waitpid(child_pid_, &status, 0);
|
||||||
|
if (WEXITSTATUS(status) == kFatalFailure) {
|
||||||
|
GTEST_FATAL_FAILURE_("Test failed in child process");
|
||||||
|
} else if (WEXITSTATUS(status) == kNonFatalFailure) {
|
||||||
|
GTEST_NONFATAL_FAILURE_("Test failed in child process");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Gtest defines TestBody functions through its macros, but classes
|
||||||
|
* derived from this one need to define RealTestBody instead.
|
||||||
|
* This is achieved by defining a TestBody macro further below.
|
||||||
|
*/
|
||||||
|
virtual void RealTestBody() = 0;
|
||||||
|
private:
|
||||||
|
static const int kFatalFailure = 1;
|
||||||
|
static const int kNonFatalFailure = 2;
|
||||||
|
|
||||||
|
pid_t child_pid_;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(LinuxPtraceDumperTest, Setup) {
|
/* Replace TestBody declarations within TEST*() with RealTestBody
|
||||||
LinuxPtraceDumper dumper(getpid());
|
* declarations */
|
||||||
|
#define TestBody RealTestBody
|
||||||
|
|
||||||
|
TEST_F(LinuxPtraceDumperChildTest, Setup) {
|
||||||
|
LinuxPtraceDumper dumper(getppid());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LinuxPtraceDumperTest, FindMappings) {
|
TEST_F(LinuxPtraceDumperChildTest, FindMappings) {
|
||||||
LinuxPtraceDumper dumper(getpid());
|
LinuxPtraceDumper dumper(getppid());
|
||||||
ASSERT_TRUE(dumper.Init());
|
ASSERT_TRUE(dumper.Init());
|
||||||
|
|
||||||
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
|
ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
|
||||||
@ -78,14 +131,14 @@ TEST(LinuxPtraceDumperTest, FindMappings) {
|
|||||||
ASSERT_FALSE(dumper.FindMapping(NULL));
|
ASSERT_FALSE(dumper.FindMapping(NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(LinuxPtraceDumperTest, ThreadList) {
|
TEST_F(LinuxPtraceDumperChildTest, ThreadList) {
|
||||||
LinuxPtraceDumper dumper(getpid());
|
LinuxPtraceDumper dumper(getppid());
|
||||||
ASSERT_TRUE(dumper.Init());
|
ASSERT_TRUE(dumper.Init());
|
||||||
|
|
||||||
ASSERT_GE(dumper.threads().size(), (size_t)1);
|
ASSERT_GE(dumper.threads().size(), (size_t)1);
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (size_t i = 0; i < dumper.threads().size(); ++i) {
|
for (size_t i = 0; i < dumper.threads().size(); ++i) {
|
||||||
if (dumper.threads()[i] == getpid()) {
|
if (dumper.threads()[i] == getppid()) {
|
||||||
ASSERT_FALSE(found);
|
ASSERT_FALSE(found);
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@ -97,12 +150,22 @@ TEST(LinuxPtraceDumperTest, ThreadList) {
|
|||||||
// a mmap'ed mapping.
|
// a mmap'ed mapping.
|
||||||
class StackHelper {
|
class StackHelper {
|
||||||
public:
|
public:
|
||||||
StackHelper(int fd, char* mapping, size_t size)
|
StackHelper()
|
||||||
: fd_(fd), mapping_(mapping), size_(size) {}
|
: fd_(-1), mapping_(NULL), size_(0) {}
|
||||||
~StackHelper() {
|
~StackHelper() {
|
||||||
|
if (size_)
|
||||||
munmap(mapping_, size_);
|
munmap(mapping_, size_);
|
||||||
|
if (fd_ >= 0)
|
||||||
close(fd_);
|
close(fd_);
|
||||||
}
|
}
|
||||||
|
void Init(int fd, char* mapping, size_t size) {
|
||||||
|
fd_ = fd;
|
||||||
|
mapping_ = mapping;
|
||||||
|
size_ = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* mapping() const { return mapping_; }
|
||||||
|
size_t size() const { return size_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int fd_;
|
int fd_;
|
||||||
@ -110,19 +173,28 @@ class StackHelper {
|
|||||||
size_t size_;
|
size_t size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST(LinuxPtraceDumperTest, MergedMappings) {
|
class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest {
|
||||||
string helper_path(GetHelperBinary());
|
protected:
|
||||||
if (helper_path.empty()) {
|
virtual void SetUp();
|
||||||
|
|
||||||
|
string helper_path_;
|
||||||
|
size_t page_size_;
|
||||||
|
StackHelper helper_;
|
||||||
|
};
|
||||||
|
|
||||||
|
void LinuxPtraceDumperMappingsTest::SetUp() {
|
||||||
|
helper_path_ = GetHelperBinary();
|
||||||
|
if (helper_path_.empty()) {
|
||||||
FAIL() << "Couldn't find helper binary";
|
FAIL() << "Couldn't find helper binary";
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// mmap two segments out of the helper binary, one
|
// mmap two segments out of the helper binary, one
|
||||||
// enclosed in the other, but with different protections.
|
// enclosed in the other, but with different protections.
|
||||||
const size_t kPageSize = sysconf(_SC_PAGESIZE);
|
page_size_ = sysconf(_SC_PAGESIZE);
|
||||||
const size_t kMappingSize = 3 * kPageSize;
|
const size_t kMappingSize = 3 * page_size_;
|
||||||
int fd = open(helper_path.c_str(), O_RDONLY);
|
int fd = open(helper_path_.c_str(), O_RDONLY);
|
||||||
ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path
|
ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_
|
||||||
<< ", Error: " << strerror(errno);
|
<< ", Error: " << strerror(errno);
|
||||||
char* mapping =
|
char* mapping =
|
||||||
reinterpret_cast<char*>(mmap(NULL,
|
reinterpret_cast<char*>(mmap(NULL,
|
||||||
@ -133,34 +205,37 @@ TEST(LinuxPtraceDumperTest, MergedMappings) {
|
|||||||
0));
|
0));
|
||||||
ASSERT_TRUE(mapping);
|
ASSERT_TRUE(mapping);
|
||||||
|
|
||||||
const uintptr_t kMappingAddress = reinterpret_cast<uintptr_t>(mapping);
|
|
||||||
|
|
||||||
// Ensure that things get cleaned up.
|
// Ensure that things get cleaned up.
|
||||||
StackHelper helper(fd, mapping, kMappingSize);
|
helper_.Init(fd, mapping, kMappingSize);
|
||||||
|
|
||||||
// Carve a page out of the first mapping with different permissions.
|
// Carve a page out of the first mapping with different permissions.
|
||||||
char* inside_mapping = reinterpret_cast<char*>(
|
char* inside_mapping = reinterpret_cast<char*>(
|
||||||
mmap(mapping + 2 *kPageSize,
|
mmap(mapping + 2 * page_size_,
|
||||||
kPageSize,
|
page_size_,
|
||||||
PROT_NONE,
|
PROT_NONE,
|
||||||
MAP_SHARED | MAP_FIXED,
|
MAP_SHARED | MAP_FIXED,
|
||||||
fd,
|
fd,
|
||||||
// Map a different offset just to
|
// Map a different offset just to
|
||||||
// better test real-world conditions.
|
// better test real-world conditions.
|
||||||
kPageSize));
|
page_size_));
|
||||||
ASSERT_TRUE(inside_mapping);
|
ASSERT_TRUE(inside_mapping);
|
||||||
|
|
||||||
|
LinuxPtraceDumperChildTest::SetUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) {
|
||||||
// Now check that LinuxPtraceDumper interpreted the mappings properly.
|
// Now check that LinuxPtraceDumper interpreted the mappings properly.
|
||||||
LinuxPtraceDumper dumper(getpid());
|
LinuxPtraceDumper dumper(getppid());
|
||||||
ASSERT_TRUE(dumper.Init());
|
ASSERT_TRUE(dumper.Init());
|
||||||
int mapping_count = 0;
|
int mapping_count = 0;
|
||||||
for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
|
for (unsigned i = 0; i < dumper.mappings().size(); ++i) {
|
||||||
const MappingInfo& mapping = *dumper.mappings()[i];
|
const MappingInfo& mapping = *dumper.mappings()[i];
|
||||||
if (strcmp(mapping.name, helper_path.c_str()) == 0) {
|
if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) {
|
||||||
// This mapping should encompass the entire original mapped
|
// This mapping should encompass the entire original mapped
|
||||||
// range.
|
// range.
|
||||||
EXPECT_EQ(kMappingAddress, mapping.start_addr);
|
EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()),
|
||||||
EXPECT_EQ(kMappingSize, mapping.size);
|
mapping.start_addr);
|
||||||
|
EXPECT_EQ(this->helper_.size(), mapping.size);
|
||||||
EXPECT_EQ(0U, mapping.offset);
|
EXPECT_EQ(0U, mapping.offset);
|
||||||
mapping_count++;
|
mapping_count++;
|
||||||
}
|
}
|
||||||
@ -168,6 +243,124 @@ TEST(LinuxPtraceDumperTest, MergedMappings) {
|
|||||||
EXPECT_EQ(1, mapping_count);
|
EXPECT_EQ(1, mapping_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) {
|
||||||
|
const pid_t pid = getppid();
|
||||||
|
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__) && !defined(__mips__)
|
||||||
|
// Ensure that the linux-gate VDSO is included in the mapping list.
|
||||||
|
TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) {
|
||||||
|
LinuxPtraceDumper dumper(getppid());
|
||||||
|
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_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) {
|
||||||
|
LinuxPtraceDumper dumper(getppid());
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_F(LinuxPtraceDumperChildTest, 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));
|
||||||
|
|
||||||
|
LinuxPtraceDumper dumper(getppid());
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get back to normal behavior of TEST*() macros wrt TestBody. */
|
||||||
|
#undef TestBody
|
||||||
|
|
||||||
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||||
static const int kNumberOfThreadsInHelperProgram = 5;
|
static const int kNumberOfThreadsInHelperProgram = 5;
|
||||||
char kNumberOfThreadsArgument[2];
|
char kNumberOfThreadsArgument[2];
|
||||||
@ -239,11 +432,16 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
|||||||
// In the helper program, we stored a pointer to the thread id in a
|
// In the helper program, we stored a pointer to the thread id in a
|
||||||
// specific register. Check that we can recover its value.
|
// specific register. Check that we can recover its value.
|
||||||
#if defined(__ARM_EABI__)
|
#if defined(__ARM_EABI__)
|
||||||
pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]);
|
pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]);
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]);
|
||||||
#elif defined(__i386)
|
#elif defined(__i386)
|
||||||
pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx);
|
pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx);
|
||||||
#elif defined(__x86_64)
|
#elif defined(__x86_64)
|
||||||
pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx);
|
pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx);
|
||||||
|
#elif defined(__mips__)
|
||||||
|
pid_t* process_tid_location =
|
||||||
|
reinterpret_cast<pid_t*>(one_thread.regs.regs[1]);
|
||||||
#else
|
#else
|
||||||
#error This test has not been ported to this platform.
|
#error This test has not been ported to this platform.
|
||||||
#endif
|
#endif
|
||||||
@ -263,178 +461,3 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
|
|||||||
ASSERT_TRUE(WIFSIGNALED(status));
|
ASSERT_TRUE(WIFSIGNALED(status));
|
||||||
ASSERT_EQ(SIGKILL, WTERMSIG(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]);
|
|
||||||
}
|
|
||||||
|
@ -59,10 +59,14 @@
|
|||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
#include <sys/user.h>
|
#include <sys/user.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "client/linux/dump_writer_common/seccomp_unwinder.h"
|
||||||
|
#include "client/linux/dump_writer_common/thread_info.h"
|
||||||
|
#include "client/linux/dump_writer_common/ucontext_reader.h"
|
||||||
#include "client/linux/handler/exception_handler.h"
|
#include "client/linux/handler/exception_handler.h"
|
||||||
#include "client/linux/minidump_writer/cpu_set.h"
|
#include "client/linux/minidump_writer/cpu_set.h"
|
||||||
#include "client/linux/minidump_writer/line_reader.h"
|
#include "client/linux/minidump_writer/line_reader.h"
|
||||||
@ -88,295 +92,14 @@ using google_breakpad::MappingList;
|
|||||||
using google_breakpad::MinidumpFileWriter;
|
using google_breakpad::MinidumpFileWriter;
|
||||||
using google_breakpad::PageAllocator;
|
using google_breakpad::PageAllocator;
|
||||||
using google_breakpad::ProcCpuInfoReader;
|
using google_breakpad::ProcCpuInfoReader;
|
||||||
|
using google_breakpad::RawContextCPU;
|
||||||
|
using google_breakpad::SeccompUnwinder;
|
||||||
using google_breakpad::ThreadInfo;
|
using google_breakpad::ThreadInfo;
|
||||||
using google_breakpad::TypedMDRVA;
|
using google_breakpad::TypedMDRVA;
|
||||||
|
using google_breakpad::UContextReader;
|
||||||
using google_breakpad::UntypedMDRVA;
|
using google_breakpad::UntypedMDRVA;
|
||||||
using google_breakpad::wasteful_vector;
|
using google_breakpad::wasteful_vector;
|
||||||
|
|
||||||
// Minidump defines register structures which are different from the raw
|
|
||||||
// structures which we get from the kernel. These are platform specific
|
|
||||||
// functions to juggle the ucontext and user structures into minidump format.
|
|
||||||
#if defined(__i386__)
|
|
||||||
typedef MDRawContextX86 RawContextCPU;
|
|
||||||
|
|
||||||
// Write a uint16_t to memory
|
|
||||||
// out: memory location to write to
|
|
||||||
// v: value to write.
|
|
||||||
void U16(void* out, uint16_t v) {
|
|
||||||
my_memcpy(out, &v, sizeof(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a uint32_t to memory
|
|
||||||
// out: memory location to write to
|
|
||||||
// v: value to write.
|
|
||||||
void U32(void* out, uint32_t v) {
|
|
||||||
my_memcpy(out, &v, sizeof(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format
|
|
||||||
// out: the minidump structure
|
|
||||||
// info: the collection of register structures.
|
|
||||||
void CPUFillFromThreadInfo(MDRawContextX86 *out,
|
|
||||||
const google_breakpad::ThreadInfo &info) {
|
|
||||||
out->context_flags = MD_CONTEXT_X86_ALL;
|
|
||||||
|
|
||||||
out->dr0 = info.dregs[0];
|
|
||||||
out->dr1 = info.dregs[1];
|
|
||||||
out->dr2 = info.dregs[2];
|
|
||||||
out->dr3 = info.dregs[3];
|
|
||||||
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
|
||||||
// format.
|
|
||||||
out->dr6 = info.dregs[6];
|
|
||||||
out->dr7 = info.dregs[7];
|
|
||||||
|
|
||||||
out->gs = info.regs.xgs;
|
|
||||||
out->fs = info.regs.xfs;
|
|
||||||
out->es = info.regs.xes;
|
|
||||||
out->ds = info.regs.xds;
|
|
||||||
|
|
||||||
out->edi = info.regs.edi;
|
|
||||||
out->esi = info.regs.esi;
|
|
||||||
out->ebx = info.regs.ebx;
|
|
||||||
out->edx = info.regs.edx;
|
|
||||||
out->ecx = info.regs.ecx;
|
|
||||||
out->eax = info.regs.eax;
|
|
||||||
|
|
||||||
out->ebp = info.regs.ebp;
|
|
||||||
out->eip = info.regs.eip;
|
|
||||||
out->cs = info.regs.xcs;
|
|
||||||
out->eflags = info.regs.eflags;
|
|
||||||
out->esp = info.regs.esp;
|
|
||||||
out->ss = info.regs.xss;
|
|
||||||
|
|
||||||
out->float_save.control_word = info.fpregs.cwd;
|
|
||||||
out->float_save.status_word = info.fpregs.swd;
|
|
||||||
out->float_save.tag_word = info.fpregs.twd;
|
|
||||||
out->float_save.error_offset = info.fpregs.fip;
|
|
||||||
out->float_save.error_selector = info.fpregs.fcs;
|
|
||||||
out->float_save.data_offset = info.fpregs.foo;
|
|
||||||
out->float_save.data_selector = info.fpregs.fos;
|
|
||||||
|
|
||||||
// 8 registers * 10 bytes per register.
|
|
||||||
my_memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8);
|
|
||||||
|
|
||||||
// This matches the Intel fpsave format.
|
|
||||||
U16(out->extended_registers + 0, info.fpregs.cwd);
|
|
||||||
U16(out->extended_registers + 2, info.fpregs.swd);
|
|
||||||
U16(out->extended_registers + 4, info.fpregs.twd);
|
|
||||||
U16(out->extended_registers + 6, info.fpxregs.fop);
|
|
||||||
U32(out->extended_registers + 8, info.fpxregs.fip);
|
|
||||||
U16(out->extended_registers + 12, info.fpxregs.fcs);
|
|
||||||
U32(out->extended_registers + 16, info.fpregs.foo);
|
|
||||||
U16(out->extended_registers + 20, info.fpregs.fos);
|
|
||||||
U32(out->extended_registers + 24, info.fpxregs.mxcsr);
|
|
||||||
|
|
||||||
my_memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128);
|
|
||||||
my_memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Juggle an x86 ucontext into minidump format
|
|
||||||
// out: the minidump structure
|
|
||||||
// info: the collection of register structures.
|
|
||||||
void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc,
|
|
||||||
const struct _libc_fpstate* fp) {
|
|
||||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
|
||||||
|
|
||||||
out->context_flags = MD_CONTEXT_X86_FULL |
|
|
||||||
MD_CONTEXT_X86_FLOATING_POINT;
|
|
||||||
|
|
||||||
out->gs = regs[REG_GS];
|
|
||||||
out->fs = regs[REG_FS];
|
|
||||||
out->es = regs[REG_ES];
|
|
||||||
out->ds = regs[REG_DS];
|
|
||||||
|
|
||||||
out->edi = regs[REG_EDI];
|
|
||||||
out->esi = regs[REG_ESI];
|
|
||||||
out->ebx = regs[REG_EBX];
|
|
||||||
out->edx = regs[REG_EDX];
|
|
||||||
out->ecx = regs[REG_ECX];
|
|
||||||
out->eax = regs[REG_EAX];
|
|
||||||
|
|
||||||
out->ebp = regs[REG_EBP];
|
|
||||||
out->eip = regs[REG_EIP];
|
|
||||||
out->cs = regs[REG_CS];
|
|
||||||
out->eflags = regs[REG_EFL];
|
|
||||||
out->esp = regs[REG_UESP];
|
|
||||||
out->ss = regs[REG_SS];
|
|
||||||
|
|
||||||
out->float_save.control_word = fp->cw;
|
|
||||||
out->float_save.status_word = fp->sw;
|
|
||||||
out->float_save.tag_word = fp->tag;
|
|
||||||
out->float_save.error_offset = fp->ipoff;
|
|
||||||
out->float_save.error_selector = fp->cssel;
|
|
||||||
out->float_save.data_offset = fp->dataoff;
|
|
||||||
out->float_save.data_selector = fp->datasel;
|
|
||||||
|
|
||||||
// 8 registers * 10 bytes per register.
|
|
||||||
my_memcpy(out->float_save.register_area, fp->_st, 10 * 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__x86_64)
|
|
||||||
typedef MDRawContextAMD64 RawContextCPU;
|
|
||||||
|
|
||||||
void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
|
|
||||||
const google_breakpad::ThreadInfo &info) {
|
|
||||||
out->context_flags = MD_CONTEXT_AMD64_FULL |
|
|
||||||
MD_CONTEXT_AMD64_SEGMENTS;
|
|
||||||
|
|
||||||
out->cs = info.regs.cs;
|
|
||||||
|
|
||||||
out->ds = info.regs.ds;
|
|
||||||
out->es = info.regs.es;
|
|
||||||
out->fs = info.regs.fs;
|
|
||||||
out->gs = info.regs.gs;
|
|
||||||
|
|
||||||
out->ss = info.regs.ss;
|
|
||||||
out->eflags = info.regs.eflags;
|
|
||||||
|
|
||||||
out->dr0 = info.dregs[0];
|
|
||||||
out->dr1 = info.dregs[1];
|
|
||||||
out->dr2 = info.dregs[2];
|
|
||||||
out->dr3 = info.dregs[3];
|
|
||||||
// 4 and 5 deliberatly omitted because they aren't included in the minidump
|
|
||||||
// format.
|
|
||||||
out->dr6 = info.dregs[6];
|
|
||||||
out->dr7 = info.dregs[7];
|
|
||||||
|
|
||||||
out->rax = info.regs.rax;
|
|
||||||
out->rcx = info.regs.rcx;
|
|
||||||
out->rdx = info.regs.rdx;
|
|
||||||
out->rbx = info.regs.rbx;
|
|
||||||
|
|
||||||
out->rsp = info.regs.rsp;
|
|
||||||
|
|
||||||
out->rbp = info.regs.rbp;
|
|
||||||
out->rsi = info.regs.rsi;
|
|
||||||
out->rdi = info.regs.rdi;
|
|
||||||
out->r8 = info.regs.r8;
|
|
||||||
out->r9 = info.regs.r9;
|
|
||||||
out->r10 = info.regs.r10;
|
|
||||||
out->r11 = info.regs.r11;
|
|
||||||
out->r12 = info.regs.r12;
|
|
||||||
out->r13 = info.regs.r13;
|
|
||||||
out->r14 = info.regs.r14;
|
|
||||||
out->r15 = info.regs.r15;
|
|
||||||
|
|
||||||
out->rip = info.regs.rip;
|
|
||||||
|
|
||||||
out->flt_save.control_word = info.fpregs.cwd;
|
|
||||||
out->flt_save.status_word = info.fpregs.swd;
|
|
||||||
out->flt_save.tag_word = info.fpregs.ftw;
|
|
||||||
out->flt_save.error_opcode = info.fpregs.fop;
|
|
||||||
out->flt_save.error_offset = info.fpregs.rip;
|
|
||||||
out->flt_save.error_selector = 0; // We don't have this.
|
|
||||||
out->flt_save.data_offset = info.fpregs.rdp;
|
|
||||||
out->flt_save.data_selector = 0; // We don't have this.
|
|
||||||
out->flt_save.mx_csr = info.fpregs.mxcsr;
|
|
||||||
out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask;
|
|
||||||
my_memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
|
|
||||||
my_memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
|
|
||||||
const struct _libc_fpstate* fpregs) {
|
|
||||||
const greg_t* regs = uc->uc_mcontext.gregs;
|
|
||||||
|
|
||||||
out->context_flags = MD_CONTEXT_AMD64_FULL;
|
|
||||||
|
|
||||||
out->cs = regs[REG_CSGSFS] & 0xffff;
|
|
||||||
|
|
||||||
out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
|
|
||||||
out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
|
|
||||||
|
|
||||||
out->eflags = regs[REG_EFL];
|
|
||||||
|
|
||||||
out->rax = regs[REG_RAX];
|
|
||||||
out->rcx = regs[REG_RCX];
|
|
||||||
out->rdx = regs[REG_RDX];
|
|
||||||
out->rbx = regs[REG_RBX];
|
|
||||||
|
|
||||||
out->rsp = regs[REG_RSP];
|
|
||||||
out->rbp = regs[REG_RBP];
|
|
||||||
out->rsi = regs[REG_RSI];
|
|
||||||
out->rdi = regs[REG_RDI];
|
|
||||||
out->r8 = regs[REG_R8];
|
|
||||||
out->r9 = regs[REG_R9];
|
|
||||||
out->r10 = regs[REG_R10];
|
|
||||||
out->r11 = regs[REG_R11];
|
|
||||||
out->r12 = regs[REG_R12];
|
|
||||||
out->r13 = regs[REG_R13];
|
|
||||||
out->r14 = regs[REG_R14];
|
|
||||||
out->r15 = regs[REG_R15];
|
|
||||||
|
|
||||||
out->rip = regs[REG_RIP];
|
|
||||||
|
|
||||||
out->flt_save.control_word = fpregs->cwd;
|
|
||||||
out->flt_save.status_word = fpregs->swd;
|
|
||||||
out->flt_save.tag_word = fpregs->ftw;
|
|
||||||
out->flt_save.error_opcode = fpregs->fop;
|
|
||||||
out->flt_save.error_offset = fpregs->rip;
|
|
||||||
out->flt_save.data_offset = fpregs->rdp;
|
|
||||||
out->flt_save.error_selector = 0; // We don't have this.
|
|
||||||
out->flt_save.data_selector = 0; // We don't have this.
|
|
||||||
out->flt_save.mx_csr = fpregs->mxcsr;
|
|
||||||
out->flt_save.mx_csr_mask = fpregs->mxcr_mask;
|
|
||||||
my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
|
|
||||||
my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(__ARMEL__)
|
|
||||||
typedef MDRawContextARM RawContextCPU;
|
|
||||||
|
|
||||||
void CPUFillFromThreadInfo(MDRawContextARM* out,
|
|
||||||
const google_breakpad::ThreadInfo& info) {
|
|
||||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
|
||||||
|
|
||||||
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
|
|
||||||
out->iregs[i] = info.regs.uregs[i];
|
|
||||||
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
|
|
||||||
out->cpsr = 0;
|
|
||||||
#if !defined(__ANDROID__)
|
|
||||||
out->float_save.fpscr = info.fpregs.fpsr |
|
|
||||||
(static_cast<uint64_t>(info.fpregs.fpcr) << 32);
|
|
||||||
// TODO: sort this out, actually collect floating point registers
|
|
||||||
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
|
||||||
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPUFillFromUContext(MDRawContextARM* out, const ucontext* uc,
|
|
||||||
const struct _libc_fpstate* fpregs) {
|
|
||||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
|
||||||
|
|
||||||
out->iregs[0] = uc->uc_mcontext.arm_r0;
|
|
||||||
out->iregs[1] = uc->uc_mcontext.arm_r1;
|
|
||||||
out->iregs[2] = uc->uc_mcontext.arm_r2;
|
|
||||||
out->iregs[3] = uc->uc_mcontext.arm_r3;
|
|
||||||
out->iregs[4] = uc->uc_mcontext.arm_r4;
|
|
||||||
out->iregs[5] = uc->uc_mcontext.arm_r5;
|
|
||||||
out->iregs[6] = uc->uc_mcontext.arm_r6;
|
|
||||||
out->iregs[7] = uc->uc_mcontext.arm_r7;
|
|
||||||
out->iregs[8] = uc->uc_mcontext.arm_r8;
|
|
||||||
out->iregs[9] = uc->uc_mcontext.arm_r9;
|
|
||||||
out->iregs[10] = uc->uc_mcontext.arm_r10;
|
|
||||||
|
|
||||||
out->iregs[11] = uc->uc_mcontext.arm_fp;
|
|
||||||
out->iregs[12] = uc->uc_mcontext.arm_ip;
|
|
||||||
out->iregs[13] = uc->uc_mcontext.arm_sp;
|
|
||||||
out->iregs[14] = uc->uc_mcontext.arm_lr;
|
|
||||||
out->iregs[15] = uc->uc_mcontext.arm_pc;
|
|
||||||
|
|
||||||
out->cpsr = uc->uc_mcontext.arm_cpsr;
|
|
||||||
|
|
||||||
// TODO: fix this after fixing ExceptionHandler
|
|
||||||
out->float_save.fpscr = 0;
|
|
||||||
my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
|
||||||
my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
#error "This code has not been ported to your platform yet."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class MinidumpWriter {
|
class MinidumpWriter {
|
||||||
public:
|
public:
|
||||||
@ -405,11 +128,8 @@ class MinidumpWriter {
|
|||||||
: fd_(minidump_fd),
|
: fd_(minidump_fd),
|
||||||
path_(minidump_path),
|
path_(minidump_path),
|
||||||
ucontext_(context ? &context->context : NULL),
|
ucontext_(context ? &context->context : NULL),
|
||||||
#if !defined(__ARM_EABI__)
|
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
float_state_(context ? &context->float_state : NULL),
|
float_state_(context ? &context->float_state : NULL),
|
||||||
#else
|
|
||||||
// TODO: fix this after fixing ExceptionHandler
|
|
||||||
float_state_(NULL),
|
|
||||||
#endif
|
#endif
|
||||||
dumper_(dumper),
|
dumper_(dumper),
|
||||||
minidump_size_limit_(-1),
|
minidump_size_limit_(-1),
|
||||||
@ -533,123 +253,6 @@ class MinidumpWriter {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the top of the stack is part of a system call that has been
|
|
||||||
// redirected by the seccomp sandbox. If so, try to pop the stack frames
|
|
||||||
// all the way back to the point where the interception happened.
|
|
||||||
void PopSeccompStackFrame(RawContextCPU* cpu, const MDRawThread& thread,
|
|
||||||
uint8_t* stack_copy) {
|
|
||||||
#if defined(__x86_64)
|
|
||||||
uint64_t bp = cpu->rbp;
|
|
||||||
uint64_t top = thread.stack.start_of_memory_range;
|
|
||||||
for (int i = 4; i--; ) {
|
|
||||||
if (bp < top ||
|
|
||||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
|
||||||
thread.stack.memory.data_size ||
|
|
||||||
bp & 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uint64_t old_top = top;
|
|
||||||
top = bp;
|
|
||||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
|
||||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
|
||||||
if (bp == 0xDEADBEEFDEADBEEFull) {
|
|
||||||
struct {
|
|
||||||
uint64_t r15;
|
|
||||||
uint64_t r14;
|
|
||||||
uint64_t r13;
|
|
||||||
uint64_t r12;
|
|
||||||
uint64_t r11;
|
|
||||||
uint64_t r10;
|
|
||||||
uint64_t r9;
|
|
||||||
uint64_t r8;
|
|
||||||
uint64_t rdi;
|
|
||||||
uint64_t rsi;
|
|
||||||
uint64_t rdx;
|
|
||||||
uint64_t rcx;
|
|
||||||
uint64_t rbx;
|
|
||||||
uint64_t deadbeef;
|
|
||||||
uint64_t rbp;
|
|
||||||
uint64_t fakeret;
|
|
||||||
uint64_t ret;
|
|
||||||
/* char redzone[128]; */
|
|
||||||
} seccomp_stackframe;
|
|
||||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
|
||||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
|
||||||
sizeof(seccomp_stackframe) >
|
|
||||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
my_memcpy(&seccomp_stackframe,
|
|
||||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
|
||||||
sizeof(seccomp_stackframe));
|
|
||||||
cpu->rbx = seccomp_stackframe.rbx;
|
|
||||||
cpu->rcx = seccomp_stackframe.rcx;
|
|
||||||
cpu->rdx = seccomp_stackframe.rdx;
|
|
||||||
cpu->rsi = seccomp_stackframe.rsi;
|
|
||||||
cpu->rdi = seccomp_stackframe.rdi;
|
|
||||||
cpu->rbp = seccomp_stackframe.rbp;
|
|
||||||
cpu->rsp = top + 4*sizeof(uint64_t) + 128;
|
|
||||||
cpu->r8 = seccomp_stackframe.r8;
|
|
||||||
cpu->r9 = seccomp_stackframe.r9;
|
|
||||||
cpu->r10 = seccomp_stackframe.r10;
|
|
||||||
cpu->r11 = seccomp_stackframe.r11;
|
|
||||||
cpu->r12 = seccomp_stackframe.r12;
|
|
||||||
cpu->r13 = seccomp_stackframe.r13;
|
|
||||||
cpu->r14 = seccomp_stackframe.r14;
|
|
||||||
cpu->r15 = seccomp_stackframe.r15;
|
|
||||||
cpu->rip = seccomp_stackframe.fakeret;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#elif defined(__i386__)
|
|
||||||
uint32_t bp = cpu->ebp;
|
|
||||||
uint32_t top = thread.stack.start_of_memory_range;
|
|
||||||
for (int i = 4; i--; ) {
|
|
||||||
if (bp < top ||
|
|
||||||
bp + sizeof(bp) > thread.stack.start_of_memory_range +
|
|
||||||
thread.stack.memory.data_size ||
|
|
||||||
bp & 1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
uint32_t old_top = top;
|
|
||||||
top = bp;
|
|
||||||
uint8_t* bp_addr = stack_copy + bp - thread.stack.start_of_memory_range;
|
|
||||||
my_memcpy(&bp, bp_addr, sizeof(bp));
|
|
||||||
if (bp == 0xDEADBEEFu) {
|
|
||||||
struct {
|
|
||||||
uint32_t edi;
|
|
||||||
uint32_t esi;
|
|
||||||
uint32_t edx;
|
|
||||||
uint32_t ecx;
|
|
||||||
uint32_t ebx;
|
|
||||||
uint32_t deadbeef;
|
|
||||||
uint32_t ebp;
|
|
||||||
uint32_t fakeret;
|
|
||||||
uint32_t ret;
|
|
||||||
} seccomp_stackframe;
|
|
||||||
if (top - offsetof(typeof(seccomp_stackframe), deadbeef) < old_top ||
|
|
||||||
top - offsetof(typeof(seccomp_stackframe), deadbeef) +
|
|
||||||
sizeof(seccomp_stackframe) >
|
|
||||||
thread.stack.start_of_memory_range+thread.stack.memory.data_size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
my_memcpy(&seccomp_stackframe,
|
|
||||||
bp_addr - offsetof(typeof(seccomp_stackframe), deadbeef),
|
|
||||||
sizeof(seccomp_stackframe));
|
|
||||||
cpu->ebx = seccomp_stackframe.ebx;
|
|
||||||
cpu->ecx = seccomp_stackframe.ecx;
|
|
||||||
cpu->edx = seccomp_stackframe.edx;
|
|
||||||
cpu->esi = seccomp_stackframe.esi;
|
|
||||||
cpu->edi = seccomp_stackframe.edi;
|
|
||||||
cpu->ebp = seccomp_stackframe.ebp;
|
|
||||||
cpu->esp = top + 4*sizeof(void*);
|
|
||||||
cpu->eip = seccomp_stackframe.fakeret;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer,
|
bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer,
|
||||||
int max_stack_len, uint8_t** stack_copy) {
|
int max_stack_len, uint8_t** stack_copy) {
|
||||||
*stack_copy = NULL;
|
*stack_copy = NULL;
|
||||||
@ -720,12 +323,13 @@ class MinidumpWriter {
|
|||||||
ucontext_ &&
|
ucontext_ &&
|
||||||
!dumper_->IsPostMortem()) {
|
!dumper_->IsPostMortem()) {
|
||||||
uint8_t* stack_copy;
|
uint8_t* stack_copy;
|
||||||
if (!FillThreadStack(&thread, GetStackPointer(), -1, &stack_copy))
|
const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_);
|
||||||
|
if (!FillThreadStack(&thread, stack_ptr, -1, &stack_copy))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Copy 256 bytes around crashing instruction pointer to minidump.
|
// Copy 256 bytes around crashing instruction pointer to minidump.
|
||||||
const size_t kIPMemorySize = 256;
|
const size_t kIPMemorySize = 256;
|
||||||
uint64_t ip = GetInstructionPointer();
|
uint64_t ip = UContextReader::GetInstructionPointer(ucontext_);
|
||||||
// Bound it to the upper and lower bounds of the memory map
|
// Bound it to the upper and lower bounds of the memory map
|
||||||
// it's contained within. If it's not in mapped memory,
|
// it's contained within. If it's not in mapped memory,
|
||||||
// don't bother trying to write it.
|
// don't bother trying to write it.
|
||||||
@ -770,9 +374,13 @@ class MinidumpWriter {
|
|||||||
if (!cpu.Allocate())
|
if (!cpu.Allocate())
|
||||||
return false;
|
return false;
|
||||||
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
||||||
CPUFillFromUContext(cpu.get(), ucontext_, float_state_);
|
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
|
UContextReader::FillCPUContext(cpu.get(), ucontext_, float_state_);
|
||||||
|
#else
|
||||||
|
UContextReader::FillCPUContext(cpu.get(), ucontext_);
|
||||||
|
#endif
|
||||||
if (stack_copy)
|
if (stack_copy)
|
||||||
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
SeccompUnwinder::PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||||
thread.thread_context = cpu.location();
|
thread.thread_context = cpu.location();
|
||||||
crashing_thread_context_ = cpu.location();
|
crashing_thread_context_ = cpu.location();
|
||||||
} else {
|
} else {
|
||||||
@ -792,9 +400,9 @@ class MinidumpWriter {
|
|||||||
if (!cpu.Allocate())
|
if (!cpu.Allocate())
|
||||||
return false;
|
return false;
|
||||||
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
|
||||||
CPUFillFromThreadInfo(cpu.get(), info);
|
info.FillCPUContext(cpu.get());
|
||||||
if (stack_copy)
|
if (stack_copy)
|
||||||
PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
SeccompUnwinder::PopSeccompStackFrame(cpu.get(), thread, stack_copy);
|
||||||
thread.thread_context = cpu.location();
|
thread.thread_context = cpu.location();
|
||||||
if (dumper_->threads()[i] == GetCrashThread()) {
|
if (dumper_->threads()[i] == GetCrashThread()) {
|
||||||
crashing_thread_context_ = cpu.location();
|
crashing_thread_context_ = cpu.location();
|
||||||
@ -802,7 +410,7 @@ class MinidumpWriter {
|
|||||||
// This is the crashing thread of a live process, but
|
// This is the crashing thread of a live process, but
|
||||||
// no context was provided, so set the crash address
|
// no context was provided, so set the crash address
|
||||||
// while the instruction pointer is already here.
|
// while the instruction pointer is already here.
|
||||||
dumper_->set_crash_address(GetInstructionPointer(info));
|
dumper_->set_crash_address(info.GetInstructionPointer());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -839,7 +447,9 @@ class MinidumpWriter {
|
|||||||
|
|
||||||
static bool ShouldIncludeMapping(const MappingInfo& mapping) {
|
static bool ShouldIncludeMapping(const MappingInfo& mapping) {
|
||||||
if (mapping.name[0] == 0 || // only want modules with filenames.
|
if (mapping.name[0] == 0 || // only want modules with filenames.
|
||||||
mapping.offset || // only want to include one mapping per shared lib.
|
// Only want to include one mapping per shared lib.
|
||||||
|
// Avoid filtering executable mappings.
|
||||||
|
(mapping.offset != 0 && !mapping.exec) ||
|
||||||
mapping.size < 4096) { // too small to get a signature for.
|
mapping.size < 4096) { // too small to get a signature for.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -930,24 +540,9 @@ class MinidumpWriter {
|
|||||||
|
|
||||||
mod.base_of_image = mapping.start_addr;
|
mod.base_of_image = mapping.start_addr;
|
||||||
mod.size_of_image = mapping.size;
|
mod.size_of_image = mapping.size;
|
||||||
const size_t filepath_len = my_strlen(mapping.name);
|
|
||||||
|
|
||||||
// Figure out file name from path
|
|
||||||
const char* filename_ptr = mapping.name + filepath_len - 1;
|
|
||||||
while (filename_ptr >= mapping.name) {
|
|
||||||
if (*filename_ptr == '/')
|
|
||||||
break;
|
|
||||||
filename_ptr--;
|
|
||||||
}
|
|
||||||
filename_ptr++;
|
|
||||||
|
|
||||||
const size_t filename_len = mapping.name + filepath_len - filename_ptr;
|
|
||||||
|
|
||||||
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
|
uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX];
|
||||||
uint8_t* cv_ptr = cv_buf;
|
uint8_t* cv_ptr = cv_buf;
|
||||||
UntypedMDRVA cv(&minidump_writer_);
|
|
||||||
if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
|
const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE;
|
||||||
my_memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
|
my_memcpy(cv_ptr, &cv_signature, sizeof(cv_signature));
|
||||||
@ -958,20 +553,31 @@ class MinidumpWriter {
|
|||||||
// GUID was provided by caller.
|
// GUID was provided by caller.
|
||||||
my_memcpy(signature, identifier, sizeof(MDGUID));
|
my_memcpy(signature, identifier, sizeof(MDGUID));
|
||||||
} else {
|
} else {
|
||||||
|
// Note: ElfFileIdentifierForMapping() can manipulate the |mapping.name|.
|
||||||
dumper_->ElfFileIdentifierForMapping(mapping, member,
|
dumper_->ElfFileIdentifierForMapping(mapping, member,
|
||||||
mapping_id, signature);
|
mapping_id, signature);
|
||||||
}
|
}
|
||||||
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
|
my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux.
|
||||||
cv_ptr += sizeof(uint32_t);
|
cv_ptr += sizeof(uint32_t);
|
||||||
|
|
||||||
|
char file_name[NAME_MAX];
|
||||||
|
char file_path[NAME_MAX];
|
||||||
|
LinuxDumper::GetMappingEffectiveNameAndPath(
|
||||||
|
mapping, file_path, sizeof(file_path), file_name, sizeof(file_name));
|
||||||
|
|
||||||
|
const size_t file_name_len = my_strlen(file_name);
|
||||||
|
UntypedMDRVA cv(&minidump_writer_);
|
||||||
|
if (!cv.Allocate(MDCVInfoPDB70_minsize + file_name_len + 1))
|
||||||
|
return false;
|
||||||
|
|
||||||
// Write pdb_file_name
|
// Write pdb_file_name
|
||||||
my_memcpy(cv_ptr, filename_ptr, filename_len + 1);
|
my_memcpy(cv_ptr, file_name, file_name_len + 1);
|
||||||
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1);
|
cv.Copy(cv_buf, MDCVInfoPDB70_minsize + file_name_len + 1);
|
||||||
|
|
||||||
mod.cv_record = cv.location();
|
mod.cv_record = cv.location();
|
||||||
|
|
||||||
MDLocationDescriptor ld;
|
MDLocationDescriptor ld;
|
||||||
if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld))
|
if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld))
|
||||||
return false;
|
return false;
|
||||||
mod.module_name_rva = ld.rva;
|
mod.module_name_rva = ld.rva;
|
||||||
return true;
|
return true;
|
||||||
@ -1127,8 +733,8 @@ class MinidumpWriter {
|
|||||||
return false;
|
return false;
|
||||||
MDRawLinkMap entry;
|
MDRawLinkMap entry;
|
||||||
entry.name = location.rva;
|
entry.name = location.rva;
|
||||||
entry.addr = (void*)map.l_addr;
|
entry.addr = reinterpret_cast<void*>(map.l_addr);
|
||||||
entry.ld = (void*)map.l_ld;
|
entry.ld = reinterpret_cast<void*>(map.l_ld);
|
||||||
linkmap.CopyIndex(idx++, &entry);
|
linkmap.CopyIndex(idx++, &entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1144,11 +750,14 @@ class MinidumpWriter {
|
|||||||
debug.get()->version = debug_entry.r_version;
|
debug.get()->version = debug_entry.r_version;
|
||||||
debug.get()->map = linkmap_rva;
|
debug.get()->map = linkmap_rva;
|
||||||
debug.get()->dso_count = dso_count;
|
debug.get()->dso_count = dso_count;
|
||||||
debug.get()->brk = (void*)debug_entry.r_brk;
|
debug.get()->brk = reinterpret_cast<void*>(debug_entry.r_brk);
|
||||||
debug.get()->ldbase = (void*)debug_entry.r_ldbase;
|
debug.get()->ldbase = reinterpret_cast<void*>(debug_entry.r_ldbase);
|
||||||
debug.get()->dynamic = dynamic;
|
debug.get()->dynamic = dynamic;
|
||||||
|
|
||||||
wasteful_vector<char> dso_debug_data(dumper_->allocator(), dynamic_length);
|
wasteful_vector<char> dso_debug_data(dumper_->allocator(), dynamic_length);
|
||||||
|
// The passed-in size to the constructor (above) is only a hint.
|
||||||
|
// Must call .resize() to do actual initialization of the elements.
|
||||||
|
dso_debug_data.resize(dynamic_length);
|
||||||
dumper_->CopyFromProcess(&dso_debug_data[0], GetCrashThread(), dynamic,
|
dumper_->CopyFromProcess(&dso_debug_data[0], GetCrashThread(), dynamic,
|
||||||
dynamic_length);
|
dynamic_length);
|
||||||
debug.CopyIndexAfterObject(0, &dso_debug_data[0], dynamic_length);
|
debug.CopyIndexAfterObject(0, &dso_debug_data[0], dynamic_length);
|
||||||
@ -1167,53 +776,13 @@ class MinidumpWriter {
|
|||||||
return dumper_->crash_thread();
|
return dumper_->crash_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__i386__)
|
|
||||||
uintptr_t GetStackPointer() {
|
|
||||||
return ucontext_->uc_mcontext.gregs[REG_ESP];
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t GetInstructionPointer() {
|
|
||||||
return ucontext_->uc_mcontext.gregs[REG_EIP];
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t GetInstructionPointer(const ThreadInfo& info) {
|
|
||||||
return info.regs.eip;
|
|
||||||
}
|
|
||||||
#elif defined(__x86_64)
|
|
||||||
uintptr_t GetStackPointer() {
|
|
||||||
return ucontext_->uc_mcontext.gregs[REG_RSP];
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t GetInstructionPointer() {
|
|
||||||
return ucontext_->uc_mcontext.gregs[REG_RIP];
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t GetInstructionPointer(const ThreadInfo& info) {
|
|
||||||
return info.regs.rip;
|
|
||||||
}
|
|
||||||
#elif defined(__ARM_EABI__)
|
|
||||||
uintptr_t GetStackPointer() {
|
|
||||||
return ucontext_->uc_mcontext.arm_sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t GetInstructionPointer() {
|
|
||||||
return ucontext_->uc_mcontext.arm_pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
uintptr_t GetInstructionPointer(const ThreadInfo& info) {
|
|
||||||
return info.regs.uregs[15];
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#error "This code has not been ported to your platform yet."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void NullifyDirectoryEntry(MDRawDirectory* dirent) {
|
void NullifyDirectoryEntry(MDRawDirectory* dirent) {
|
||||||
dirent->stream_type = 0;
|
dirent->stream_type = 0;
|
||||||
dirent->location.data_size = 0;
|
dirent->location.data_size = 0;
|
||||||
dirent->location.rva = 0;
|
dirent->location.rva = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__i386__) || defined(__x86_64__)
|
#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
|
||||||
bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
|
bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
|
||||||
char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
|
char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
|
||||||
static const char vendor_id_name[] = "vendor_id";
|
static const char vendor_id_name[] = "vendor_id";
|
||||||
@ -1224,14 +793,18 @@ class MinidumpWriter {
|
|||||||
bool found;
|
bool found;
|
||||||
} cpu_info_table[] = {
|
} cpu_info_table[] = {
|
||||||
{ "processor", -1, false },
|
{ "processor", -1, false },
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
{ "model", 0, false },
|
{ "model", 0, false },
|
||||||
{ "stepping", 0, false },
|
{ "stepping", 0, false },
|
||||||
{ "cpu family", 0, false },
|
{ "cpu family", 0, false },
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// processor_architecture should always be set, do this first
|
// processor_architecture should always be set, do this first
|
||||||
sys_info->processor_architecture =
|
sys_info->processor_architecture =
|
||||||
#if defined(__i386__)
|
#if defined(__mips__)
|
||||||
|
MD_CPU_ARCHITECTURE_MIPS;
|
||||||
|
#elif defined(__i386__)
|
||||||
MD_CPU_ARCHITECTURE_X86;
|
MD_CPU_ARCHITECTURE_X86;
|
||||||
#else
|
#else
|
||||||
MD_CPU_ARCHITECTURE_AMD64;
|
MD_CPU_ARCHITECTURE_AMD64;
|
||||||
@ -1294,9 +867,11 @@ class MinidumpWriter {
|
|||||||
cpu_info_table[0].value++;
|
cpu_info_table[0].value++;
|
||||||
|
|
||||||
sys_info->number_of_processors = cpu_info_table[0].value;
|
sys_info->number_of_processors = cpu_info_table[0].value;
|
||||||
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
sys_info->processor_level = cpu_info_table[3].value;
|
sys_info->processor_level = cpu_info_table[3].value;
|
||||||
sys_info->processor_revision = cpu_info_table[1].value << 8 |
|
sys_info->processor_revision = cpu_info_table[1].value << 8 |
|
||||||
cpu_info_table[2].value;
|
cpu_info_table[2].value;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (vendor_id[0] != '\0') {
|
if (vendor_id[0] != '\0') {
|
||||||
my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
|
my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id,
|
||||||
@ -1304,7 +879,7 @@ class MinidumpWriter {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__) || defined(__aarch64__)
|
||||||
bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
|
bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
|
||||||
// The CPUID value is broken up in several entries in /proc/cpuinfo.
|
// The CPUID value is broken up in several entries in /proc/cpuinfo.
|
||||||
// This table is used to rebuild it from the entries.
|
// This table is used to rebuild it from the entries.
|
||||||
@ -1326,6 +901,7 @@ class MinidumpWriter {
|
|||||||
const char* tag;
|
const char* tag;
|
||||||
uint32_t hwcaps;
|
uint32_t hwcaps;
|
||||||
} cpu_features_entries[] = {
|
} cpu_features_entries[] = {
|
||||||
|
#if defined(__arm__)
|
||||||
{ "swp", MD_CPU_ARM_ELF_HWCAP_SWP },
|
{ "swp", MD_CPU_ARM_ELF_HWCAP_SWP },
|
||||||
{ "half", MD_CPU_ARM_ELF_HWCAP_HALF },
|
{ "half", MD_CPU_ARM_ELF_HWCAP_HALF },
|
||||||
{ "thumb", MD_CPU_ARM_ELF_HWCAP_THUMB },
|
{ "thumb", MD_CPU_ARM_ELF_HWCAP_THUMB },
|
||||||
@ -1346,10 +922,18 @@ class MinidumpWriter {
|
|||||||
{ "idiva", MD_CPU_ARM_ELF_HWCAP_IDIVA },
|
{ "idiva", MD_CPU_ARM_ELF_HWCAP_IDIVA },
|
||||||
{ "idivt", MD_CPU_ARM_ELF_HWCAP_IDIVT },
|
{ "idivt", MD_CPU_ARM_ELF_HWCAP_IDIVT },
|
||||||
{ "idiv", MD_CPU_ARM_ELF_HWCAP_IDIVA | MD_CPU_ARM_ELF_HWCAP_IDIVT },
|
{ "idiv", MD_CPU_ARM_ELF_HWCAP_IDIVA | MD_CPU_ARM_ELF_HWCAP_IDIVT },
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
// No hwcaps on aarch64.
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
// processor_architecture should always be set, do this first
|
// processor_architecture should always be set, do this first
|
||||||
sys_info->processor_architecture = MD_CPU_ARCHITECTURE_ARM;
|
sys_info->processor_architecture =
|
||||||
|
#if defined(__aarch64__)
|
||||||
|
MD_CPU_ARCHITECTURE_ARM64;
|
||||||
|
#else
|
||||||
|
MD_CPU_ARCHITECTURE_ARM;
|
||||||
|
#endif
|
||||||
|
|
||||||
// /proc/cpuinfo is not readable under various sandboxed environments
|
// /proc/cpuinfo is not readable under various sandboxed environments
|
||||||
// (e.g. Android services with the android:isolatedProcess attribute)
|
// (e.g. Android services with the android:isolatedProcess attribute)
|
||||||
@ -1420,10 +1004,11 @@ class MinidumpWriter {
|
|||||||
const char* p = value;
|
const char* p = value;
|
||||||
if (value[0] == '0' && value[1] == 'x') {
|
if (value[0] == '0' && value[1] == 'x') {
|
||||||
p = my_read_hex_ptr(&result, value+2);
|
p = my_read_hex_ptr(&result, value+2);
|
||||||
} else if (entry->format == 'x')
|
} else if (entry->format == 'x') {
|
||||||
p = my_read_hex_ptr(&result, value);
|
p = my_read_hex_ptr(&result, value);
|
||||||
else
|
} else {
|
||||||
p = my_read_decimal_ptr(&result, value);
|
p = my_read_decimal_ptr(&result, value);
|
||||||
|
}
|
||||||
if (p == value)
|
if (p == value)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@ -1432,13 +1017,14 @@ class MinidumpWriter {
|
|||||||
sys_info->cpu.arm_cpu_info.cpuid |=
|
sys_info->cpu.arm_cpu_info.cpuid |=
|
||||||
static_cast<uint32_t>(result);
|
static_cast<uint32_t>(result);
|
||||||
}
|
}
|
||||||
|
#if defined(__arm__)
|
||||||
// Get the architecture version from the "Processor" field.
|
// Get the architecture version from the "Processor" field.
|
||||||
// Note that it is also available in the "CPU architecture" field,
|
// Note that it is also available in the "CPU architecture" field,
|
||||||
// however, some existing kernels are misconfigured and will report
|
// however, some existing kernels are misconfigured and will report
|
||||||
// invalid values here (e.g. 6, while the CPU is ARMv7-A based).
|
// invalid values here (e.g. 6, while the CPU is ARMv7-A based).
|
||||||
// The "Processor" field doesn't have this issue.
|
// The "Processor" field doesn't have this issue.
|
||||||
if (!my_strcmp(field, "Processor")) {
|
if (!my_strcmp(field, "Processor")) {
|
||||||
unsigned value_len;
|
size_t value_len;
|
||||||
const char* value = reader->GetValueAndLen(&value_len);
|
const char* value = reader->GetValueAndLen(&value_len);
|
||||||
// Expected format: <text> (v<level><endian>)
|
// Expected format: <text> (v<level><endian>)
|
||||||
// Where <text> is some text like "ARMv7 Processor rev 2"
|
// Where <text> is some text like "ARMv7 Processor rev 2"
|
||||||
@ -1457,9 +1043,23 @@ class MinidumpWriter {
|
|||||||
sys_info->processor_level = static_cast<uint16_t>(arch_level);
|
sys_info->processor_level = static_cast<uint16_t>(arch_level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
// The aarch64 architecture does not provide the architecture level
|
||||||
|
// in the Processor field, so we instead check the "CPU architecture"
|
||||||
|
// field.
|
||||||
|
if (!my_strcmp(field, "CPU architecture")) {
|
||||||
|
uintptr_t arch_level = 0;
|
||||||
|
const char* value = reader->GetValue();
|
||||||
|
const char* p = value;
|
||||||
|
p = my_read_decimal_ptr(&arch_level, value);
|
||||||
|
if (p == value)
|
||||||
|
continue;
|
||||||
|
sys_info->processor_level = static_cast<uint16_t>(arch_level);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
// Rebuild the ELF hwcaps from the 'Features' field.
|
// Rebuild the ELF hwcaps from the 'Features' field.
|
||||||
if (!my_strcmp(field, "Features")) {
|
if (!my_strcmp(field, "Features")) {
|
||||||
unsigned value_len;
|
size_t value_len;
|
||||||
const char* value = reader->GetValueAndLen(&value_len);
|
const char* value = reader->GetValueAndLen(&value_len);
|
||||||
|
|
||||||
// Parse each space-separated tag.
|
// Parse each space-separated tag.
|
||||||
@ -1601,23 +1201,6 @@ class MinidumpWriter {
|
|||||||
space_left -= info_len;
|
space_left -= info_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef __ANDROID__
|
|
||||||
// On Android, try to get the build fingerprint and append it.
|
|
||||||
// Fail gracefully because there is no guarantee that the system
|
|
||||||
// property will always be available or accessible.
|
|
||||||
char fingerprint[PROP_VALUE_MAX];
|
|
||||||
int fingerprint_len = __system_property_get("ro.build.fingerprint",
|
|
||||||
fingerprint);
|
|
||||||
// System property values shall always be zero-terminated.
|
|
||||||
// Be paranoid and don't trust the system.
|
|
||||||
if (fingerprint_len > 0 && fingerprint_len < PROP_VALUE_MAX) {
|
|
||||||
const char* separator = " ";
|
|
||||||
if (!first_item)
|
|
||||||
my_strlcat(buf, separator, sizeof(buf));
|
|
||||||
my_strlcat(buf, fingerprint, sizeof(buf));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MDLocationDescriptor location;
|
MDLocationDescriptor location;
|
||||||
if (!minidump_writer_.WriteString(buf, 0, &location))
|
if (!minidump_writer_.WriteString(buf, 0, &location))
|
||||||
return false;
|
return false;
|
||||||
@ -1639,7 +1222,9 @@ class MinidumpWriter {
|
|||||||
const char* path_; // Path to the file where the minidum should be written.
|
const char* path_; // Path to the file where the minidum should be written.
|
||||||
|
|
||||||
const struct ucontext* const ucontext_; // also from the signal handler
|
const struct ucontext* const ucontext_; // also from the signal handler
|
||||||
const struct _libc_fpstate* const float_state_; // ditto
|
#if !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
|
const google_breakpad::fpstate_t* const float_state_; // ditto
|
||||||
|
#endif
|
||||||
LinuxDumper* dumper_;
|
LinuxDumper* dumper_;
|
||||||
MinidumpFileWriter minidump_writer_;
|
MinidumpFileWriter minidump_writer_;
|
||||||
off_t minidump_size_limit_;
|
off_t minidump_size_limit_;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/ucontext.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
@ -44,13 +45,11 @@ namespace google_breakpad {
|
|||||||
|
|
||||||
class ExceptionHandler;
|
class ExceptionHandler;
|
||||||
|
|
||||||
struct MappingEntry {
|
#if defined(__aarch64__)
|
||||||
MappingInfo first;
|
typedef struct fpsimd_context fpstate_t;
|
||||||
uint8_t second[sizeof(MDGUID)];
|
#elif !defined(__ARM_EABI__) && !defined(__mips__)
|
||||||
};
|
typedef struct _libc_fpstate fpstate_t;
|
||||||
|
#endif
|
||||||
// A list of <MappingInfo, GUID>
|
|
||||||
typedef std::list<MappingEntry> MappingList;
|
|
||||||
|
|
||||||
// These entries store a list of memory regions that the client wants included
|
// These entries store a list of memory regions that the client wants included
|
||||||
// in the minidump.
|
// in the minidump.
|
||||||
|
@ -84,7 +84,7 @@ TEST(MinidumpWriterTest, SetupWithPath) {
|
|||||||
AutoTempDir temp_dir;
|
AutoTempDir temp_dir;
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
// Set a non-zero tid to avoid tripping asserts.
|
||||||
context.tid = 1;
|
context.tid = child;
|
||||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
|
ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
|
||||||
struct stat st;
|
struct stat st;
|
||||||
ASSERT_EQ(0, stat(templ.c_str(), &st));
|
ASSERT_EQ(0, stat(templ.c_str(), &st));
|
||||||
@ -114,7 +114,7 @@ TEST(MinidumpWriterTest, SetupWithFD) {
|
|||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||||
int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
|
int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU);
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
// Set a non-zero tid to avoid tripping asserts.
|
||||||
context.tid = 1;
|
context.tid = child;
|
||||||
ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
|
ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context)));
|
||||||
struct stat st;
|
struct stat st;
|
||||||
ASSERT_EQ(0, stat(templ.c_str(), &st));
|
ASSERT_EQ(0, stat(templ.c_str(), &st));
|
||||||
@ -391,7 +391,7 @@ TEST(MinidumpWriterTest, DeletedBinary) {
|
|||||||
|
|
||||||
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
string templ = temp_dir.path() + kMDWriterUnitTestFileName;
|
||||||
// Set a non-zero tid to avoid tripping asserts.
|
// Set a non-zero tid to avoid tripping asserts.
|
||||||
context.tid = 1;
|
context.tid = child_pid;
|
||||||
ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
|
ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
|
||||||
sizeof(context)));
|
sizeof(context)));
|
||||||
kill(child_pid, SIGKILL);
|
kill(child_pid, SIGKILL);
|
||||||
@ -525,21 +525,20 @@ TEST(MinidumpWriterTest, InvalidStackPointer) {
|
|||||||
|
|
||||||
// Fake the child's stack pointer for its crashing thread. NOTE: This must
|
// 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).
|
// be an invalid memory address for the child process (stack or otherwise).
|
||||||
#if defined(__i386)
|
|
||||||
// Try 1MB below the current stack.
|
// Try 1MB below the current stack.
|
||||||
uintptr_t invalid_stack_pointer =
|
uintptr_t invalid_stack_pointer =
|
||||||
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
|
reinterpret_cast<uintptr_t>(&context) - 1024*1024;
|
||||||
|
#if defined(__i386)
|
||||||
context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
|
context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer;
|
||||||
#elif defined(__x86_64)
|
#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;
|
context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer;
|
||||||
#elif defined(__ARM_EABI__)
|
#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;
|
context.context.uc_mcontext.arm_sp = invalid_stack_pointer;
|
||||||
|
#elif defined(__aarch64__)
|
||||||
|
context.context.uc_mcontext.sp = invalid_stack_pointer;
|
||||||
|
#elif defined(__mips__)
|
||||||
|
context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
|
||||||
|
invalid_stack_pointer;
|
||||||
#else
|
#else
|
||||||
# error "This code has not been ported to your platform yet."
|
# error "This code has not been ported to your platform yet."
|
||||||
#endif
|
#endif
|
||||||
|
@ -36,8 +36,7 @@
|
|||||||
|
|
||||||
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
|
#include "client/linux/minidump_writer/proc_cpuinfo_reader.h"
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
#include "common/linux/eintr_wrapper.h"
|
#include "common/linux/tests/auto_testfile.h"
|
||||||
#include "common/tests/auto_testfile.h"
|
|
||||||
|
|
||||||
using namespace google_breakpad;
|
using namespace google_breakpad;
|
||||||
|
|
||||||
|
@ -100,5 +100,5 @@ int main(int argc, char *argv[]) {
|
|||||||
FLAGS_crash_server,
|
FLAGS_crash_server,
|
||||||
FLAGS_proxy_host,
|
FLAGS_proxy_host,
|
||||||
FLAGS_proxy_userpasswd);
|
FLAGS_proxy_userpasswd);
|
||||||
g.Upload();
|
g.Upload(NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
#include "client/minidump_file_writer-inl.h"
|
#include "client/minidump_file_writer-inl.h"
|
||||||
#include "common/linux/linux_libc_support.h"
|
#include "common/linux/linux_libc_support.h"
|
||||||
#include "common/string_conversion.h"
|
#include "common/string_conversion.h"
|
||||||
#if __linux__
|
#if defined(__linux__) && __linux__
|
||||||
#include "third_party/lss/linux_syscall_support.h"
|
#include "third_party/lss/linux_syscall_support.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ MinidumpFileWriter::~MinidumpFileWriter() {
|
|||||||
|
|
||||||
bool MinidumpFileWriter::Open(const char *path) {
|
bool MinidumpFileWriter::Open(const char *path) {
|
||||||
assert(file_ == -1);
|
assert(file_ == -1);
|
||||||
#if __linux__
|
#if defined(__linux__) && __linux__
|
||||||
file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||||
#else
|
#else
|
||||||
file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
||||||
@ -84,7 +84,7 @@ bool MinidumpFileWriter::Close() {
|
|||||||
if (-1 == ftruncate(file_, position_)) {
|
if (-1 == ftruncate(file_, position_)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#if __linux__
|
#if defined(__linux__) && __linux__
|
||||||
result = (sys_close(file_) == 0);
|
result = (sys_close(file_) == 0);
|
||||||
#else
|
#else
|
||||||
result = (close(file_) == 0);
|
result = (close(file_) == 0);
|
||||||
@ -253,7 +253,7 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Seek and write the data
|
// Seek and write the data
|
||||||
#if __linux__
|
#if defined(__linux__) && __linux__
|
||||||
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
|
||||||
if (sys_write(file_, src, size) == size) {
|
if (sys_write(file_, src, size) == size) {
|
||||||
#else
|
#else
|
||||||
|
@ -74,8 +74,8 @@ public:
|
|||||||
MinidumpFileWriter();
|
MinidumpFileWriter();
|
||||||
~MinidumpFileWriter();
|
~MinidumpFileWriter();
|
||||||
|
|
||||||
// Open |path| as the destination of the minidump data. Any existing file
|
// Open |path| as the destination of the minidump data. If |path| already
|
||||||
// will be overwritten.
|
// exists, then Open() will fail.
|
||||||
// Return true on success, or false on failure.
|
// Return true on success, or false on failure.
|
||||||
bool Open(const char *path);
|
bool Open(const char *path);
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Copyright (c) 2010, Google Inc.
|
# Copyright 2010 Google Inc. All rights reserved.
|
||||||
# All rights reserved.
|
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are
|
# modification, are permitted provided that the following conditions are
|
||||||
@ -29,9 +28,21 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'includes': [
|
'includes': [
|
||||||
'build/common.gypi',
|
'../../build/common.gypi',
|
||||||
],
|
],
|
||||||
'targets': [
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'build_all',
|
||||||
|
'type': 'none',
|
||||||
|
'dependencies': [
|
||||||
|
'./crash_generation/crash_generation.gyp:*',
|
||||||
|
'./handler/exception_handler.gyp:*',
|
||||||
|
'./sender/crash_report_sender.gyp:*',
|
||||||
|
'./unittests/client_tests.gyp:*',
|
||||||
|
'./unittests/testing.gyp:*',
|
||||||
|
'./tests/crash_generation_app/crash_generation_app.gyp:*',
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'target_name': 'common',
|
'target_name': 'common',
|
||||||
'type': 'static_library',
|
'type': 'static_library',
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
'Optimization': '2',
|
|
||||||
'StringPooling': 'true',
|
|
||||||
'OmitFramePointers': 'true',
|
|
||||||
},
|
|
||||||
'VCLinkerTool': {
|
|
||||||
'LinkIncremental': '1',
|
|
||||||
'OptimizeReferences': '2',
|
|
||||||
'EnableCOMDATFolding': '2',
|
|
||||||
'OptimizeForWindows98': '1',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
'includes': ['release_defaults.gypi'],
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
'includes': ['release_defaults.gypi'],
|
|
||||||
'defines': ['OFFICIAL_BUILD'],
|
|
||||||
'msvs_settings': {
|
|
||||||
'VCCLCompilerTool': {
|
|
||||||
'Optimization': '3',
|
|
||||||
'InlineFunctionExpansion': '2',
|
|
||||||
'EnableIntrinsicFunctions': 'true',
|
|
||||||
'FavorSizeOrSpeed': '2',
|
|
||||||
'OmitFramePointers': 'true',
|
|
||||||
'EnableFiberSafeOptimizations': 'true',
|
|
||||||
'WholeProgramOptimization': 'true',
|
|
||||||
},
|
|
||||||
'VCLibrarianTool': {
|
|
||||||
'AdditionalOptions': ['/ltcg', '/expectedoutputsize:120000000'],
|
|
||||||
},
|
|
||||||
'VCLinkerTool': {
|
|
||||||
'LinkTimeCodeGeneration': '1',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
'conditions': [
|
|
||||||
# Handle build types.
|
|
||||||
['buildtype=="Dev"', {
|
|
||||||
'includes': ['internal/release_impl.gypi'],
|
|
||||||
}],
|
|
||||||
['buildtype=="Official"', {
|
|
||||||
'includes': ['internal/release_impl_official.gypi'],
|
|
||||||
}],
|
|
||||||
# TODO(bradnelson): may also need:
|
|
||||||
# checksenabled
|
|
||||||
# coverage
|
|
||||||
# dom_stats
|
|
||||||
# pgo_instrument
|
|
||||||
# pgo_optimize
|
|
||||||
# purify
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
|||||||
#ifndef CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
|
#ifndef CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
|
||||||
#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
|
#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@
|
|||||||
#ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
|
#ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
|
||||||
#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
|
#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <DbgHelp.h>
|
#include <dbghelp.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include "common/windows/string_utils-inl.h"
|
#include "common/windows/string_utils-inl.h"
|
||||||
|
@ -67,8 +67,10 @@ bool ClientInfo::Initialize() {
|
|||||||
// The crash_id will be the low order word of the process creation time.
|
// The crash_id will be the low order word of the process creation time.
|
||||||
FILETIME creation_time, exit_time, kernel_time, user_time;
|
FILETIME creation_time, exit_time, kernel_time, user_time;
|
||||||
if (GetProcessTimes(process_handle_, &creation_time, &exit_time,
|
if (GetProcessTimes(process_handle_, &creation_time, &exit_time,
|
||||||
&kernel_time, &user_time))
|
&kernel_time, &user_time)) {
|
||||||
crash_id_ = creation_time.dwLowDateTime;
|
start_time_ = creation_time;
|
||||||
|
}
|
||||||
|
crash_id_ = start_time_.dwLowDateTime;
|
||||||
|
|
||||||
dump_requested_handle_ = CreateEvent(NULL, // Security attributes.
|
dump_requested_handle_ = CreateEvent(NULL, // Security attributes.
|
||||||
TRUE, // Manual reset.
|
TRUE, // Manual reset.
|
||||||
@ -206,7 +208,7 @@ bool ClientInfo::PopulateCustomInfo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetProcessUptime();
|
SetProcessUptime();
|
||||||
return (bytes_count != read_count);
|
return (bytes_count == read_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomClientInfo ClientInfo::GetCustomInfo() const {
|
CustomClientInfo ClientInfo::GetCustomInfo() const {
|
||||||
|
@ -30,8 +30,8 @@
|
|||||||
#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
|
#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
|
||||||
#define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
|
#define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <DbgHelp.h>
|
#include <dbghelp.h>
|
||||||
#include "client/windows/common/ipc_protocol.h"
|
#include "client/windows/common/ipc_protocol.h"
|
||||||
#include "common/scoped_ptr.h"
|
#include "common/scoped_ptr.h"
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
@ -66,6 +66,9 @@ class ClientInfo {
|
|||||||
HANDLE dump_requested_handle() const { return dump_requested_handle_; }
|
HANDLE dump_requested_handle() const { return dump_requested_handle_; }
|
||||||
HANDLE dump_generated_handle() const { return dump_generated_handle_; }
|
HANDLE dump_generated_handle() const { return dump_generated_handle_; }
|
||||||
DWORD crash_id() const { return crash_id_; }
|
DWORD crash_id() const { return crash_id_; }
|
||||||
|
const CustomClientInfo& custom_client_info() const {
|
||||||
|
return custom_client_info_;
|
||||||
|
}
|
||||||
|
|
||||||
void set_dump_request_wait_handle(HANDLE value) {
|
void set_dump_request_wait_handle(HANDLE value) {
|
||||||
dump_request_wait_handle_ = value;
|
dump_request_wait_handle_ = value;
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Copyright (c) 2010, Google Inc.
|
# Copyright 2010 Google Inc. All rights reserved.
|
||||||
# All rights reserved.
|
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are
|
# modification, are permitted provided that the following conditions are
|
||||||
@ -29,7 +28,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'includes': [
|
'includes': [
|
||||||
'../build/common.gypi',
|
'../../../build/common.gypi',
|
||||||
],
|
],
|
||||||
'targets': [
|
'targets': [
|
||||||
{
|
{
|
||||||
|
@ -85,6 +85,15 @@ static bool IsClientRequestValid(const ProtocolMessage& msg) {
|
|||||||
msg.assert_info != NULL);
|
msg.assert_info != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef _DEBUG
|
||||||
|
static bool CheckForIOIncomplete(bool success) {
|
||||||
|
// We should never get an I/O incomplete since we should not execute this
|
||||||
|
// unless the operation has finished and the overlapped event is signaled. If
|
||||||
|
// we do get INCOMPLETE, we have a bug in our code.
|
||||||
|
return success ? false : (GetLastError() == ERROR_IO_INCOMPLETE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
CrashGenerationServer::CrashGenerationServer(
|
CrashGenerationServer::CrashGenerationServer(
|
||||||
const std::wstring& pipe_name,
|
const std::wstring& pipe_name,
|
||||||
SECURITY_ATTRIBUTES* pipe_sec_attrs,
|
SECURITY_ATTRIBUTES* pipe_sec_attrs,
|
||||||
@ -112,16 +121,13 @@ CrashGenerationServer::CrashGenerationServer(
|
|||||||
upload_request_callback_(upload_request_callback),
|
upload_request_callback_(upload_request_callback),
|
||||||
upload_context_(upload_context),
|
upload_context_(upload_context),
|
||||||
generate_dumps_(generate_dumps),
|
generate_dumps_(generate_dumps),
|
||||||
dump_generator_(NULL),
|
dump_path_(dump_path ? *dump_path : L""),
|
||||||
server_state_(IPC_SERVER_STATE_UNINITIALIZED),
|
server_state_(IPC_SERVER_STATE_UNINITIALIZED),
|
||||||
shutting_down_(false),
|
shutting_down_(false),
|
||||||
overlapped_(),
|
overlapped_(),
|
||||||
client_info_(NULL) {
|
client_info_(NULL),
|
||||||
|
pre_fetch_custom_info_(true) {
|
||||||
InitializeCriticalSection(&sync_);
|
InitializeCriticalSection(&sync_);
|
||||||
|
|
||||||
if (dump_path) {
|
|
||||||
dump_generator_.reset(new MinidumpGenerator(*dump_path));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This should never be called from the OnPipeConnected callback.
|
// This should never be called from the OnPipeConnected callback.
|
||||||
@ -387,18 +393,13 @@ void CrashGenerationServer::HandleReadingState() {
|
|||||||
&overlapped_,
|
&overlapped_,
|
||||||
&bytes_count,
|
&bytes_count,
|
||||||
FALSE) != FALSE;
|
FALSE) != FALSE;
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
if (success && bytes_count == sizeof(ProtocolMessage)) {
|
if (success && bytes_count == sizeof(ProtocolMessage)) {
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_READ_DONE);
|
EnterStateImmediately(IPC_SERVER_STATE_READ_DONE);
|
||||||
} else {
|
return;
|
||||||
// We should never get an I/O incomplete since we should not execute this
|
|
||||||
// unless the Read has finished and the overlapped event is signaled. If
|
|
||||||
// we do get INCOMPLETE, we have a bug in our code.
|
|
||||||
assert(error_code != ERROR_IO_INCOMPLETE);
|
|
||||||
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(!CheckForIOIncomplete(success));
|
||||||
|
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When the server thread serving the client is in the READ_DONE state,
|
// When the server thread serving the client is in the READ_DONE state,
|
||||||
@ -467,18 +468,12 @@ void CrashGenerationServer::HandleWritingState() {
|
|||||||
&overlapped_,
|
&overlapped_,
|
||||||
&bytes_count,
|
&bytes_count,
|
||||||
FALSE) != FALSE;
|
FALSE) != FALSE;
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE);
|
EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should never get an I/O incomplete since we should not execute this
|
assert(!CheckForIOIncomplete(success));
|
||||||
// unless the Write has finished and the overlapped event is signaled. If
|
|
||||||
// we do get INCOMPLETE, we have a bug in our code.
|
|
||||||
assert(error_code != ERROR_IO_INCOMPLETE);
|
|
||||||
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,8 +511,6 @@ void CrashGenerationServer::HandleReadingAckState() {
|
|||||||
&overlapped_,
|
&overlapped_,
|
||||||
&bytes_count,
|
&bytes_count,
|
||||||
FALSE) != FALSE;
|
FALSE) != FALSE;
|
||||||
DWORD error_code = success ? ERROR_SUCCESS : GetLastError();
|
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
// The connection handshake with the client is now complete; perform
|
// The connection handshake with the client is now complete; perform
|
||||||
// the callback.
|
// the callback.
|
||||||
@ -550,10 +543,7 @@ void CrashGenerationServer::HandleReadingAckState() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We should never get an I/O incomplete since we should not execute this
|
assert(!CheckForIOIncomplete(success));
|
||||||
// unless the Read has finished and the overlapped event is signaled. If
|
|
||||||
// we do get INCOMPLETE, we have a bug in our code.
|
|
||||||
assert(error_code != ERROR_IO_INCOMPLETE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING);
|
||||||
@ -831,10 +821,12 @@ void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) {
|
|||||||
void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
|
void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) {
|
||||||
assert(context);
|
assert(context);
|
||||||
ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
|
ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context);
|
||||||
client_info->PopulateCustomInfo();
|
|
||||||
|
|
||||||
CrashGenerationServer* crash_server = client_info->crash_server();
|
CrashGenerationServer* crash_server = client_info->crash_server();
|
||||||
assert(crash_server);
|
assert(crash_server);
|
||||||
|
if (crash_server->pre_fetch_custom_info_) {
|
||||||
|
client_info->PopulateCustomInfo();
|
||||||
|
}
|
||||||
crash_server->HandleDumpRequest(*client_info);
|
crash_server->HandleDumpRequest(*client_info);
|
||||||
|
|
||||||
ResetEvent(client_info->dump_requested_handle());
|
ResetEvent(client_info->dump_requested_handle());
|
||||||
@ -921,15 +913,19 @@ bool CrashGenerationServer::GenerateDump(const ClientInfo& client,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return dump_generator_->WriteMinidump(client.process_handle(),
|
MinidumpGenerator dump_generator(dump_path_,
|
||||||
|
client.process_handle(),
|
||||||
client.pid(),
|
client.pid(),
|
||||||
client_thread_id,
|
client_thread_id,
|
||||||
GetCurrentThreadId(),
|
GetCurrentThreadId(),
|
||||||
client_ex_info,
|
client_ex_info,
|
||||||
client.assert_info(),
|
client.assert_info(),
|
||||||
client.dump_type(),
|
client.dump_type(),
|
||||||
true,
|
true);
|
||||||
dump_path);
|
if (!dump_generator.GenerateDumpFile(dump_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return dump_generator.WriteMinidump();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
@ -102,6 +102,10 @@ class CrashGenerationServer {
|
|||||||
// Returns true if initialization is successful; false otherwise.
|
// Returns true if initialization is successful; false otherwise.
|
||||||
bool Start();
|
bool Start();
|
||||||
|
|
||||||
|
void pre_fetch_custom_info(bool do_pre_fetch) {
|
||||||
|
pre_fetch_custom_info_ = do_pre_fetch;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Various states the client can be in during the handshake with
|
// Various states the client can be in during the handshake with
|
||||||
// the server.
|
// the server.
|
||||||
@ -261,8 +265,11 @@ class CrashGenerationServer {
|
|||||||
// Whether to generate dumps.
|
// Whether to generate dumps.
|
||||||
bool generate_dumps_;
|
bool generate_dumps_;
|
||||||
|
|
||||||
// Instance of a mini dump generator.
|
// Wether to populate custom information up-front.
|
||||||
scoped_ptr<MinidumpGenerator> dump_generator_;
|
bool pre_fetch_custom_info_;
|
||||||
|
|
||||||
|
// The dump path for the server.
|
||||||
|
const std::wstring dump_path_;
|
||||||
|
|
||||||
// State of the server in performing the IPC with the client.
|
// State of the server in performing the IPC with the client.
|
||||||
// Note that since we restrict the pipe to one instance, we
|
// Note that since we restrict the pipe to one instance, we
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "client/windows/common/auto_critical_section.h"
|
#include "client/windows/common/auto_critical_section.h"
|
||||||
|
#include "common/scoped_ptr.h"
|
||||||
#include "common/windows/guid_string.h"
|
#include "common/windows/guid_string.h"
|
||||||
|
|
||||||
using std::wstring;
|
using std::wstring;
|
||||||
@ -175,9 +176,14 @@ bool HandleTraceData::CollectHandleData(
|
|||||||
stream_data->Reserved = 0;
|
stream_data->Reserved = 0;
|
||||||
std::copy(operations_.begin(),
|
std::copy(operations_.begin(),
|
||||||
operations_.end(),
|
operations_.end(),
|
||||||
|
#ifdef _MSC_VER
|
||||||
stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>(
|
stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>(
|
||||||
reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1),
|
reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1),
|
||||||
operations_.size()));
|
operations_.size())
|
||||||
|
#else
|
||||||
|
reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -242,10 +248,33 @@ ULONG CALLBACK HandleTraceData::RecordHandleOperations(
|
|||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
MinidumpGenerator::MinidumpGenerator(const wstring& dump_path)
|
MinidumpGenerator::MinidumpGenerator(
|
||||||
|
const std::wstring& dump_path,
|
||||||
|
const HANDLE process_handle,
|
||||||
|
const DWORD process_id,
|
||||||
|
const DWORD thread_id,
|
||||||
|
const DWORD requesting_thread_id,
|
||||||
|
EXCEPTION_POINTERS* exception_pointers,
|
||||||
|
MDRawAssertionInfo* assert_info,
|
||||||
|
const MINIDUMP_TYPE dump_type,
|
||||||
|
const bool is_client_pointers)
|
||||||
: dbghelp_module_(NULL),
|
: dbghelp_module_(NULL),
|
||||||
rpcrt4_module_(NULL),
|
rpcrt4_module_(NULL),
|
||||||
dump_path_(dump_path),
|
dump_path_(dump_path),
|
||||||
|
process_handle_(process_handle),
|
||||||
|
process_id_(process_id),
|
||||||
|
thread_id_(thread_id),
|
||||||
|
requesting_thread_id_(requesting_thread_id),
|
||||||
|
exception_pointers_(exception_pointers),
|
||||||
|
assert_info_(assert_info),
|
||||||
|
dump_type_(dump_type),
|
||||||
|
is_client_pointers_(is_client_pointers),
|
||||||
|
dump_file_(INVALID_HANDLE_VALUE),
|
||||||
|
full_dump_file_(INVALID_HANDLE_VALUE),
|
||||||
|
dump_file_is_internal_(false),
|
||||||
|
full_dump_file_is_internal_(false),
|
||||||
|
additional_streams_(NULL),
|
||||||
|
callback_info_(NULL),
|
||||||
write_dump_(NULL),
|
write_dump_(NULL),
|
||||||
create_uuid_(NULL) {
|
create_uuid_(NULL) {
|
||||||
InitializeCriticalSection(&module_load_sync_);
|
InitializeCriticalSection(&module_load_sync_);
|
||||||
@ -253,6 +282,14 @@ MinidumpGenerator::MinidumpGenerator(const wstring& dump_path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MinidumpGenerator::~MinidumpGenerator() {
|
MinidumpGenerator::~MinidumpGenerator() {
|
||||||
|
if (dump_file_is_internal_ && dump_file_ != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(dump_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_dump_file_is_internal_ && full_dump_file_ != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(full_dump_file_);
|
||||||
|
}
|
||||||
|
|
||||||
if (dbghelp_module_) {
|
if (dbghelp_module_) {
|
||||||
FreeLibrary(dbghelp_module_);
|
FreeLibrary(dbghelp_module_);
|
||||||
}
|
}
|
||||||
@ -265,91 +302,28 @@ MinidumpGenerator::~MinidumpGenerator() {
|
|||||||
DeleteCriticalSection(&module_load_sync_);
|
DeleteCriticalSection(&module_load_sync_);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
|
bool MinidumpGenerator::WriteMinidump() {
|
||||||
DWORD process_id,
|
bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0;
|
||||||
DWORD thread_id,
|
if (dump_file_ == INVALID_HANDLE_VALUE ||
|
||||||
DWORD requesting_thread_id,
|
(full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) {
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
return false;
|
||||||
MDRawAssertionInfo* assert_info,
|
}
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
bool is_client_pointers,
|
|
||||||
wstring* dump_path) {
|
|
||||||
// Just call the full WriteMinidump with NULL as the full_dump_path.
|
|
||||||
return this->WriteMinidump(process_handle, process_id, thread_id,
|
|
||||||
requesting_thread_id, exception_pointers,
|
|
||||||
assert_info, dump_type, is_client_pointers,
|
|
||||||
dump_path, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
|
|
||||||
DWORD process_id,
|
|
||||||
DWORD thread_id,
|
|
||||||
DWORD requesting_thread_id,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
|
||||||
MDRawAssertionInfo* assert_info,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
bool is_client_pointers,
|
|
||||||
wstring* dump_path,
|
|
||||||
wstring* full_dump_path) {
|
|
||||||
MiniDumpWriteDumpType write_dump = GetWriteDump();
|
MiniDumpWriteDumpType write_dump = GetWriteDump();
|
||||||
if (!write_dump) {
|
if (!write_dump) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
wstring dump_file_path;
|
|
||||||
if (!GenerateDumpFilePath(&dump_file_path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the client requests a full memory dump, we will write a normal mini
|
|
||||||
// dump and a full memory dump. Both dump files use the same uuid as file
|
|
||||||
// name prefix.
|
|
||||||
bool full_memory_dump = (dump_type & MiniDumpWithFullMemory) != 0;
|
|
||||||
wstring full_dump_file_path;
|
|
||||||
if (full_memory_dump) {
|
|
||||||
full_dump_file_path.assign(dump_file_path);
|
|
||||||
full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp
|
|
||||||
full_dump_file_path.append(TEXT("-full.dmp"));
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE dump_file = CreateFile(dump_file_path.c_str(),
|
|
||||||
GENERIC_WRITE,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
CREATE_NEW,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (dump_file == INVALID_HANDLE_VALUE) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE full_dump_file = INVALID_HANDLE_VALUE;
|
|
||||||
if (full_memory_dump) {
|
|
||||||
full_dump_file = CreateFile(full_dump_file_path.c_str(),
|
|
||||||
GENERIC_WRITE,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
CREATE_NEW,
|
|
||||||
FILE_ATTRIBUTE_NORMAL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (full_dump_file == INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(dump_file);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL;
|
MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL;
|
||||||
MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
|
MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
|
||||||
|
|
||||||
// Setup the exception information object only if it's a dump
|
// Setup the exception information object only if it's a dump
|
||||||
// due to an exception.
|
// due to an exception.
|
||||||
if (exception_pointers) {
|
if (exception_pointers_) {
|
||||||
dump_exception_pointers = &dump_exception_info;
|
dump_exception_pointers = &dump_exception_info;
|
||||||
dump_exception_info.ThreadId = thread_id;
|
dump_exception_info.ThreadId = thread_id_;
|
||||||
dump_exception_info.ExceptionPointers = exception_pointers;
|
dump_exception_info.ExceptionPointers = exception_pointers_;
|
||||||
dump_exception_info.ClientPointers = is_client_pointers;
|
dump_exception_info.ClientPointers = is_client_pointers_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add an MDRawBreakpadInfo stream to the minidump, to provide additional
|
// Add an MDRawBreakpadInfo stream to the minidump, to provide additional
|
||||||
@ -359,49 +333,54 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
|
|||||||
// can function better with Breakpad-generated dumps when it is present.
|
// can function better with Breakpad-generated dumps when it is present.
|
||||||
// The native debugger is not harmed by the presence of this information.
|
// The native debugger is not harmed by the presence of this information.
|
||||||
MDRawBreakpadInfo breakpad_info = {0};
|
MDRawBreakpadInfo breakpad_info = {0};
|
||||||
if (!is_client_pointers) {
|
if (!is_client_pointers_) {
|
||||||
// Set the dump thread id and requesting thread id only in case of
|
// Set the dump thread id and requesting thread id only in case of
|
||||||
// in-process dump generation.
|
// in-process dump generation.
|
||||||
breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
|
breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
|
||||||
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
|
MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
|
||||||
breakpad_info.dump_thread_id = thread_id;
|
breakpad_info.dump_thread_id = thread_id_;
|
||||||
breakpad_info.requesting_thread_id = requesting_thread_id;
|
breakpad_info.requesting_thread_id = requesting_thread_id_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leave room in user_stream_array for possible assertion info and handle
|
int additional_streams_count = additional_streams_ ?
|
||||||
// operations streams.
|
additional_streams_->UserStreamCount : 0;
|
||||||
MINIDUMP_USER_STREAM user_stream_array[3];
|
scoped_array<MINIDUMP_USER_STREAM> user_stream_array(
|
||||||
|
new MINIDUMP_USER_STREAM[3 + additional_streams_count]);
|
||||||
user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
|
user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
|
||||||
user_stream_array[0].BufferSize = sizeof(breakpad_info);
|
user_stream_array[0].BufferSize = sizeof(breakpad_info);
|
||||||
user_stream_array[0].Buffer = &breakpad_info;
|
user_stream_array[0].Buffer = &breakpad_info;
|
||||||
|
|
||||||
MINIDUMP_USER_STREAM_INFORMATION user_streams;
|
MINIDUMP_USER_STREAM_INFORMATION user_streams;
|
||||||
user_streams.UserStreamCount = 1;
|
user_streams.UserStreamCount = 1;
|
||||||
user_streams.UserStreamArray = user_stream_array;
|
user_streams.UserStreamArray = user_stream_array.get();
|
||||||
|
|
||||||
MDRawAssertionInfo* actual_assert_info = assert_info;
|
MDRawAssertionInfo* actual_assert_info = assert_info_;
|
||||||
MDRawAssertionInfo client_assert_info = {0};
|
MDRawAssertionInfo client_assert_info = {{0}};
|
||||||
|
|
||||||
if (assert_info) {
|
if (assert_info_) {
|
||||||
// If the assertion info object lives in the client process,
|
// If the assertion info object lives in the client process,
|
||||||
// read the memory of the client process.
|
// read the memory of the client process.
|
||||||
if (is_client_pointers) {
|
if (is_client_pointers_) {
|
||||||
SIZE_T bytes_read = 0;
|
SIZE_T bytes_read = 0;
|
||||||
if (!ReadProcessMemory(process_handle,
|
if (!ReadProcessMemory(process_handle_,
|
||||||
assert_info,
|
assert_info_,
|
||||||
&client_assert_info,
|
&client_assert_info,
|
||||||
sizeof(client_assert_info),
|
sizeof(client_assert_info),
|
||||||
&bytes_read)) {
|
&bytes_read)) {
|
||||||
CloseHandle(dump_file);
|
if (dump_file_is_internal_)
|
||||||
if (full_dump_file != INVALID_HANDLE_VALUE)
|
CloseHandle(dump_file_);
|
||||||
CloseHandle(full_dump_file);
|
if (full_dump_file_is_internal_ &&
|
||||||
|
full_dump_file_ != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(full_dump_file_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bytes_read != sizeof(client_assert_info)) {
|
if (bytes_read != sizeof(client_assert_info)) {
|
||||||
CloseHandle(dump_file);
|
if (dump_file_is_internal_)
|
||||||
if (full_dump_file != INVALID_HANDLE_VALUE)
|
CloseHandle(dump_file_);
|
||||||
CloseHandle(full_dump_file);
|
if (full_dump_file_is_internal_ &&
|
||||||
|
full_dump_file_ != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(full_dump_file_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,16 +393,31 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
|
|||||||
++user_streams.UserStreamCount;
|
++user_streams.UserStreamCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (additional_streams_) {
|
||||||
|
for (size_t i = 0;
|
||||||
|
i < additional_streams_->UserStreamCount;
|
||||||
|
i++, user_streams.UserStreamCount++) {
|
||||||
|
user_stream_array[user_streams.UserStreamCount].Type =
|
||||||
|
additional_streams_->UserStreamArray[i].Type;
|
||||||
|
user_stream_array[user_streams.UserStreamCount].BufferSize =
|
||||||
|
additional_streams_->UserStreamArray[i].BufferSize;
|
||||||
|
user_stream_array[user_streams.UserStreamCount].Buffer =
|
||||||
|
additional_streams_->UserStreamArray[i].Buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the process is terminated by STATUS_INVALID_HANDLE exception store
|
// If the process is terminated by STATUS_INVALID_HANDLE exception store
|
||||||
// the trace of operatios for the offending handle value. Do nothing special
|
// the trace of operations for the offending handle value. Do nothing special
|
||||||
// if the client already requested the handle trace to be stored in the dump.
|
// if the client already requested the handle trace to be stored in the dump.
|
||||||
HandleTraceData handle_trace_data;
|
HandleTraceData handle_trace_data;
|
||||||
if (exception_pointers && (dump_type & MiniDumpWithHandleData) == 0) {
|
if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) {
|
||||||
if (!handle_trace_data.CollectHandleData(process_handle,
|
if (!handle_trace_data.CollectHandleData(process_handle_,
|
||||||
exception_pointers)) {
|
exception_pointers_)) {
|
||||||
CloseHandle(dump_file);
|
if (dump_file_is_internal_)
|
||||||
if (full_dump_file != INVALID_HANDLE_VALUE)
|
CloseHandle(dump_file_);
|
||||||
CloseHandle(full_dump_file);
|
if (full_dump_file_is_internal_ &&
|
||||||
|
full_dump_file_ != INVALID_HANDLE_VALUE)
|
||||||
|
CloseHandle(full_dump_file_);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -431,12 +425,12 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
|
|||||||
bool result_full_memory = true;
|
bool result_full_memory = true;
|
||||||
if (full_memory_dump) {
|
if (full_memory_dump) {
|
||||||
result_full_memory = write_dump(
|
result_full_memory = write_dump(
|
||||||
process_handle,
|
process_handle_,
|
||||||
process_id,
|
process_id_,
|
||||||
full_dump_file,
|
full_dump_file_,
|
||||||
static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpNormal))
|
static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpNormal))
|
||||||
| MiniDumpWithHandleData),
|
| MiniDumpWithHandleData),
|
||||||
exception_pointers ? &dump_exception_info : NULL,
|
exception_pointers_ ? &dump_exception_info : NULL,
|
||||||
&user_streams,
|
&user_streams,
|
||||||
NULL) != FALSE;
|
NULL) != FALSE;
|
||||||
}
|
}
|
||||||
@ -448,31 +442,79 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool result_minidump = write_dump(
|
bool result_minidump = write_dump(
|
||||||
process_handle,
|
process_handle_,
|
||||||
process_id,
|
process_id_,
|
||||||
dump_file,
|
dump_file_,
|
||||||
static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpWithFullMemory))
|
static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpWithFullMemory))
|
||||||
| MiniDumpNormal),
|
| MiniDumpNormal),
|
||||||
exception_pointers ? &dump_exception_info : NULL,
|
exception_pointers_ ? &dump_exception_info : NULL,
|
||||||
&user_streams,
|
&user_streams,
|
||||||
NULL) != FALSE;
|
callback_info_) != FALSE;
|
||||||
|
|
||||||
bool result = result_minidump && result_full_memory;
|
return result_minidump && result_full_memory;
|
||||||
|
}
|
||||||
|
|
||||||
CloseHandle(dump_file);
|
bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) {
|
||||||
if (full_dump_file != INVALID_HANDLE_VALUE)
|
// The dump file was already set by handle or this function was previously
|
||||||
CloseHandle(full_dump_file);
|
// called.
|
||||||
|
if (dump_file_ != INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Store the path of the dump file in the out parameter if dump generation
|
wstring dump_file_path;
|
||||||
// succeeded.
|
if (!GenerateDumpFilePath(&dump_file_path)) {
|
||||||
if (result && dump_path) {
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_file_ = CreateFile(dump_file_path.c_str(),
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
CREATE_NEW,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL);
|
||||||
|
if (dump_file_ == INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_file_is_internal_ = true;
|
||||||
*dump_path = dump_file_path;
|
*dump_path = dump_file_path;
|
||||||
}
|
return true;
|
||||||
if (result && full_memory_dump && full_dump_path) {
|
}
|
||||||
*full_dump_path = full_dump_file_path;
|
|
||||||
|
bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) {
|
||||||
|
// A full minidump was not requested.
|
||||||
|
if ((dump_type_ & MiniDumpWithFullMemory) == 0) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
// The dump file was already set by handle or this function was previously
|
||||||
|
// called.
|
||||||
|
if (full_dump_file_ != INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wstring full_dump_file_path;
|
||||||
|
if (!GenerateDumpFilePath(&full_dump_file_path)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp
|
||||||
|
full_dump_file_path.append(TEXT("-full.dmp"));
|
||||||
|
|
||||||
|
full_dump_file_ = CreateFile(full_dump_file_path.c_str(),
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
CREATE_NEW,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL);
|
||||||
|
if (full_dump_file_ == INVALID_HANDLE_VALUE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
full_dump_file_is_internal_ = true;
|
||||||
|
*full_dump_path = full_dump_file_path;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
HMODULE MinidumpGenerator::GetDbghelpModule() {
|
HMODULE MinidumpGenerator::GetDbghelpModule() {
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#include <dbghelp.h>
|
#include <dbghelp.h>
|
||||||
#include <rpc.h>
|
#include <rpc.h>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <string>
|
||||||
#include "google_breakpad/common/minidump_format.h"
|
#include "google_breakpad/common/minidump_format.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
@ -44,37 +45,55 @@ namespace google_breakpad {
|
|||||||
// the clients to generate minidumps.
|
// the clients to generate minidumps.
|
||||||
class MinidumpGenerator {
|
class MinidumpGenerator {
|
||||||
public:
|
public:
|
||||||
// Creates an instance with the given dump path.
|
// Creates an instance with the given parameters.
|
||||||
explicit MinidumpGenerator(const std::wstring& dump_path);
|
// is_client_pointers specifies whether the exception_pointers and
|
||||||
|
// assert_info point into the process that is being dumped.
|
||||||
|
// Before calling WriteMinidump on the returned instance a dump file muct be
|
||||||
|
// specified by a call to either SetDumpFile() or GenerateDumpFile().
|
||||||
|
// If a full dump file will be requested via a subsequent call to either
|
||||||
|
// SetFullDumpFile or GenerateFullDumpFile() dump_type must include
|
||||||
|
// MiniDumpWithFullMemory.
|
||||||
|
MinidumpGenerator(const std::wstring& dump_path,
|
||||||
|
const HANDLE process_handle,
|
||||||
|
const DWORD process_id,
|
||||||
|
const DWORD thread_id,
|
||||||
|
const DWORD requesting_thread_id,
|
||||||
|
EXCEPTION_POINTERS* exception_pointers,
|
||||||
|
MDRawAssertionInfo* assert_info,
|
||||||
|
const MINIDUMP_TYPE dump_type,
|
||||||
|
const bool is_client_pointers);
|
||||||
|
|
||||||
~MinidumpGenerator();
|
~MinidumpGenerator();
|
||||||
|
|
||||||
|
void SetDumpFile(const HANDLE dump_file) { dump_file_ = dump_file; }
|
||||||
|
void SetFullDumpFile(const HANDLE full_dump_file) {
|
||||||
|
full_dump_file_ = full_dump_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate the name for the dump file that will be written to once
|
||||||
|
// WriteMinidump() is called. Can only be called once and cannot be called
|
||||||
|
// if the dump file is set via SetDumpFile().
|
||||||
|
bool GenerateDumpFile(std::wstring* dump_path);
|
||||||
|
|
||||||
|
// Generate the name for the full dump file that will be written to once
|
||||||
|
// WriteMinidump() is called. Cannot be called unless the minidump type
|
||||||
|
// includes MiniDumpWithFullMemory. Can only be called once and cannot be
|
||||||
|
// called if the dump file is set via SetFullDumpFile().
|
||||||
|
bool GenerateFullDumpFile(std::wstring* full_dump_path);
|
||||||
|
|
||||||
|
void SetAdditionalStreams(
|
||||||
|
MINIDUMP_USER_STREAM_INFORMATION* additional_streams) {
|
||||||
|
additional_streams_ = additional_streams;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCallback(MINIDUMP_CALLBACK_INFORMATION* callback_info) {
|
||||||
|
callback_info_ = callback_info;
|
||||||
|
}
|
||||||
|
|
||||||
// Writes the minidump with the given parameters. Stores the
|
// Writes the minidump with the given parameters. Stores the
|
||||||
// dump file path in the dump_path parameter if dump generation
|
// dump file path in the dump_path parameter if dump generation
|
||||||
// succeeds.
|
// succeeds.
|
||||||
bool WriteMinidump(HANDLE process_handle,
|
bool WriteMinidump();
|
||||||
DWORD process_id,
|
|
||||||
DWORD thread_id,
|
|
||||||
DWORD requesting_thread_id,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
|
||||||
MDRawAssertionInfo* assert_info,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
bool is_client_pointers,
|
|
||||||
std::wstring* dump_path);
|
|
||||||
|
|
||||||
// Writes the minidump with the given parameters. Stores the dump file
|
|
||||||
// path in the dump_path (and full_dump_path) parameter if dump
|
|
||||||
// generation succeeds. full_dump_path and dump_path can be NULL.
|
|
||||||
bool WriteMinidump(HANDLE process_handle,
|
|
||||||
DWORD process_id,
|
|
||||||
DWORD thread_id,
|
|
||||||
DWORD requesting_thread_id,
|
|
||||||
EXCEPTION_POINTERS* exception_pointers,
|
|
||||||
MDRawAssertionInfo* assert_info,
|
|
||||||
MINIDUMP_TYPE dump_type,
|
|
||||||
bool is_client_pointers,
|
|
||||||
std::wstring* dump_path,
|
|
||||||
std::wstring* full_dump_path);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Function pointer type for MiniDumpWriteDump, which is looked up
|
// Function pointer type for MiniDumpWriteDump, which is looked up
|
||||||
@ -120,9 +139,53 @@ class MinidumpGenerator {
|
|||||||
// Pointer to the UuidCreate function.
|
// Pointer to the UuidCreate function.
|
||||||
UuidCreateType create_uuid_;
|
UuidCreateType create_uuid_;
|
||||||
|
|
||||||
|
// Handle for the process to dump.
|
||||||
|
HANDLE process_handle_;
|
||||||
|
|
||||||
|
// Process ID for the process to dump.
|
||||||
|
DWORD process_id_;
|
||||||
|
|
||||||
|
// The crashing thread ID.
|
||||||
|
DWORD thread_id_;
|
||||||
|
|
||||||
|
// The thread ID which is requesting the dump.
|
||||||
|
DWORD requesting_thread_id_;
|
||||||
|
|
||||||
|
// Pointer to the exception information for the crash. This may point to an
|
||||||
|
// address in the crashing process so it should not be dereferenced.
|
||||||
|
EXCEPTION_POINTERS* exception_pointers_;
|
||||||
|
|
||||||
|
// Assertion info for the report.
|
||||||
|
MDRawAssertionInfo* assert_info_;
|
||||||
|
|
||||||
|
// Type of minidump to generate.
|
||||||
|
MINIDUMP_TYPE dump_type_;
|
||||||
|
|
||||||
|
// Specifies whether the exception_pointers_ reference memory in the crashing
|
||||||
|
// process.
|
||||||
|
bool is_client_pointers_;
|
||||||
|
|
||||||
// Folder path to store dump files.
|
// Folder path to store dump files.
|
||||||
std::wstring dump_path_;
|
std::wstring dump_path_;
|
||||||
|
|
||||||
|
// The file where the dump will be written.
|
||||||
|
HANDLE dump_file_;
|
||||||
|
|
||||||
|
// The file where the full dump will be written.
|
||||||
|
HANDLE full_dump_file_;
|
||||||
|
|
||||||
|
// Tracks whether the dump file handle is managed externally.
|
||||||
|
bool dump_file_is_internal_;
|
||||||
|
|
||||||
|
// Tracks whether the full dump file handle is managed externally.
|
||||||
|
bool full_dump_file_is_internal_;
|
||||||
|
|
||||||
|
// Additional streams to be written to the dump.
|
||||||
|
MINIDUMP_USER_STREAM_INFORMATION* additional_streams_;
|
||||||
|
|
||||||
|
// The user defined callback for the various stages of the dump process.
|
||||||
|
MINIDUMP_CALLBACK_INFORMATION* callback_info_;
|
||||||
|
|
||||||
// Critical section to sychronize action of loading modules dynamically.
|
// Critical section to sychronize action of loading modules dynamically.
|
||||||
CRITICAL_SECTION module_load_sync_;
|
CRITICAL_SECTION module_load_sync_;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
#include <ObjBase.h>
|
#include <objbase.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@ -104,19 +104,19 @@ ExceptionHandler::ExceptionHandler(
|
|||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void* callback_context,
|
void* callback_context,
|
||||||
int handler_types,
|
int handler_types,
|
||||||
MINIDUMP_TYPE dump_type,
|
CrashGenerationClient* crash_generation_client) {
|
||||||
CrashGenerationClient* crash_generation_client,
|
// The dump_type, pipe_name and custom_info that are passed in to Initialize()
|
||||||
const CustomClientInfo* custom_info) {
|
// are not used. The ones set in crash_generation_client are used instead.
|
||||||
Initialize(dump_path,
|
Initialize(dump_path,
|
||||||
filter,
|
filter,
|
||||||
callback,
|
callback,
|
||||||
callback_context,
|
callback_context,
|
||||||
handler_types,
|
handler_types,
|
||||||
MiniDumpNormal,
|
MiniDumpNormal, // dump_type - not used
|
||||||
NULL, // pipe_name
|
NULL, // pipe_name - not used
|
||||||
NULL, // pipe_handle
|
NULL, // pipe_handle
|
||||||
crash_generation_client,
|
crash_generation_client,
|
||||||
custom_info);
|
NULL); // custom_info - not used
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionHandler::ExceptionHandler(const wstring &dump_path,
|
ExceptionHandler::ExceptionHandler(const wstring &dump_path,
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
# Copyright (c) 2010, Google Inc.
|
# Copyright 2010 Google Inc. All rights reserved.
|
||||||
# All rights reserved.
|
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without
|
# Redistribution and use in source and binary forms, with or without
|
||||||
# modification, are permitted provided that the following conditions are
|
# modification, are permitted provided that the following conditions are
|
||||||
@ -29,7 +28,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'includes': [
|
'includes': [
|
||||||
'../build/common.gypi',
|
'../../../build/common.gypi',
|
||||||
],
|
],
|
||||||
'targets': [
|
'targets': [
|
||||||
{
|
{
|
||||||
@ -41,7 +40,7 @@
|
|||||||
],
|
],
|
||||||
'dependencies': [
|
'dependencies': [
|
||||||
'../breakpad_client.gyp:common',
|
'../breakpad_client.gyp:common',
|
||||||
'../crash_generation/crash_generation.gyp:crash_generation_client',
|
'../crash_generation/crash_generation.gyp:crash_generation_server',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -57,13 +57,13 @@
|
|||||||
#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <Windows.h>
|
#include <windows.h>
|
||||||
#include <DbgHelp.h>
|
#include <dbghelp.h>
|
||||||
#include <rpc.h>
|
#include <rpc.h>
|
||||||
|
|
||||||
#pragma warning( push )
|
#pragma warning(push)
|
||||||
// Disable exception handler warnings.
|
// Disable exception handler warnings.
|
||||||
#pragma warning( disable : 4530 )
|
#pragma warning(disable:4530)
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -212,9 +212,7 @@ class ExceptionHandler {
|
|||||||
MinidumpCallback callback,
|
MinidumpCallback callback,
|
||||||
void* callback_context,
|
void* callback_context,
|
||||||
int handler_types,
|
int handler_types,
|
||||||
MINIDUMP_TYPE dump_type,
|
CrashGenerationClient* crash_generation_client);
|
||||||
CrashGenerationClient* crash_generation_client,
|
|
||||||
const CustomClientInfo* custom_info);
|
|
||||||
|
|
||||||
~ExceptionHandler();
|
~ExceptionHandler();
|
||||||
|
|
||||||
@ -497,7 +495,7 @@ class ExceptionHandler {
|
|||||||
static CRITICAL_SECTION handler_stack_critical_section_;
|
static CRITICAL_SECTION handler_stack_critical_section_;
|
||||||
|
|
||||||
// The number of instances of this class.
|
// The number of instances of this class.
|
||||||
volatile static LONG instance_count_;
|
static volatile LONG instance_count_;
|
||||||
|
|
||||||
// disallow copy ctor and operator=
|
// disallow copy ctor and operator=
|
||||||
explicit ExceptionHandler(const ExceptionHandler &);
|
explicit ExceptionHandler(const ExceptionHandler &);
|
||||||
@ -506,6 +504,6 @@ class ExceptionHandler {
|
|||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
||||||
#pragma warning( pop )
|
#pragma warning(pop)
|
||||||
|
|
||||||
#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
|
||||||
|
@ -38,4 +38,21 @@
|
|||||||
void operator=(const TypeName&)
|
void operator=(const TypeName&)
|
||||||
#endif // DISALLOW_COPY_AND_ASSIGN
|
#endif // DISALLOW_COPY_AND_ASSIGN
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// Used to explicitly mark the return value of a function as unused. If you are
|
||||||
|
// really sure you don't want to do anything with the return value of a function
|
||||||
|
// that has been marked with __attribute__((warn_unused_result)), wrap it with
|
||||||
|
// this. Example:
|
||||||
|
//
|
||||||
|
// scoped_ptr<MyType> my_var = ...;
|
||||||
|
// if (TakeOwnership(my_var.get()) == SUCCESS)
|
||||||
|
// ignore_result(my_var.release());
|
||||||
|
//
|
||||||
|
template<typename T>
|
||||||
|
inline void ignore_result(const T&) {
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
#endif // COMMON_BASICTYPES_H_
|
#endif // COMMON_BASICTYPES_H_
|
||||||
|
242
google-breakpad/src/common/common.gyp
Normal file
242
google-breakpad/src/common/common.gyp
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
# Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without
|
||||||
|
# modification, are permitted provided that the following conditions are
|
||||||
|
# met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright
|
||||||
|
# notice, this list of conditions and the following disclaimer.
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# copyright notice, this list of conditions and the following disclaimer
|
||||||
|
# in the documentation and/or other materials provided with the
|
||||||
|
# distribution.
|
||||||
|
# * Neither the name of Google Inc. nor the names of its
|
||||||
|
# contributors may be used to endorse or promote products derived from
|
||||||
|
# this software without specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
{
|
||||||
|
'target_defaults': {
|
||||||
|
'target_conditions': [
|
||||||
|
['OS=="mac"', {
|
||||||
|
'defines': ['HAVE_MACH_O_NLIST_H'],
|
||||||
|
}],
|
||||||
|
['OS=="linux"', {
|
||||||
|
'defines': ['HAVE_A_OUT_H'],
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'common',
|
||||||
|
'type': 'static_library',
|
||||||
|
'sources': [
|
||||||
|
'android/breakpad_getcontext.S',
|
||||||
|
'android/include/elf.h',
|
||||||
|
'android/include/link.h',
|
||||||
|
'android/include/sgidefs.h',
|
||||||
|
'android/include/stab.h',
|
||||||
|
'android/include/sys/procfs.h',
|
||||||
|
'android/include/sys/signal.h',
|
||||||
|
'android/include/sys/user.h',
|
||||||
|
'android/include/ucontext.h',
|
||||||
|
'android/testing/include/wchar.h',
|
||||||
|
'android/testing/mkdtemp.h',
|
||||||
|
'android/testing/pthread_fixes.h',
|
||||||
|
'android/ucontext_constants.h',
|
||||||
|
'basictypes.h',
|
||||||
|
'byte_cursor.h',
|
||||||
|
'convert_UTF.c',
|
||||||
|
'convert_UTF.h',
|
||||||
|
'dwarf/bytereader-inl.h',
|
||||||
|
'dwarf/bytereader.cc',
|
||||||
|
'dwarf/bytereader.h',
|
||||||
|
'dwarf/cfi_assembler.cc',
|
||||||
|
'dwarf/cfi_assembler.h',
|
||||||
|
'dwarf/dwarf2diehandler.cc',
|
||||||
|
'dwarf/dwarf2diehandler.h',
|
||||||
|
'dwarf/dwarf2enums.h',
|
||||||
|
'dwarf/dwarf2reader.cc',
|
||||||
|
'dwarf/dwarf2reader.h',
|
||||||
|
'dwarf/dwarf2reader_test_common.h',
|
||||||
|
'dwarf/functioninfo.cc',
|
||||||
|
'dwarf/functioninfo.h',
|
||||||
|
'dwarf/line_state_machine.h',
|
||||||
|
'dwarf/types.h',
|
||||||
|
'dwarf_cfi_to_module.cc',
|
||||||
|
'dwarf_cfi_to_module.h',
|
||||||
|
'dwarf_cu_to_module.cc',
|
||||||
|
'dwarf_cu_to_module.h',
|
||||||
|
'dwarf_line_to_module.cc',
|
||||||
|
'dwarf_line_to_module.h',
|
||||||
|
'language.cc',
|
||||||
|
'language.h',
|
||||||
|
'linux/crc32.cc',
|
||||||
|
'linux/crc32.h',
|
||||||
|
'linux/dump_symbols.cc',
|
||||||
|
'linux/dump_symbols.h',
|
||||||
|
'linux/eintr_wrapper.h',
|
||||||
|
'linux/elf_core_dump.cc',
|
||||||
|
'linux/elf_core_dump.h',
|
||||||
|
'linux/elf_gnu_compat.h',
|
||||||
|
'linux/elf_symbols_to_module.cc',
|
||||||
|
'linux/elf_symbols_to_module.h',
|
||||||
|
'linux/elfutils-inl.h',
|
||||||
|
'linux/elfutils.cc',
|
||||||
|
'linux/elfutils.h',
|
||||||
|
'linux/file_id.cc',
|
||||||
|
'linux/file_id.h',
|
||||||
|
'linux/google_crashdump_uploader.cc',
|
||||||
|
'linux/google_crashdump_uploader.h',
|
||||||
|
'linux/guid_creator.cc',
|
||||||
|
'linux/guid_creator.h',
|
||||||
|
'linux/http_upload.cc',
|
||||||
|
'linux/http_upload.h',
|
||||||
|
'linux/ignore_ret.h',
|
||||||
|
'linux/libcurl_wrapper.cc',
|
||||||
|
'linux/libcurl_wrapper.h',
|
||||||
|
'linux/linux_libc_support.cc',
|
||||||
|
'linux/linux_libc_support.h',
|
||||||
|
'linux/memory_mapped_file.cc',
|
||||||
|
'linux/memory_mapped_file.h',
|
||||||
|
'linux/safe_readlink.cc',
|
||||||
|
'linux/safe_readlink.h',
|
||||||
|
'linux/synth_elf.cc',
|
||||||
|
'linux/synth_elf.h',
|
||||||
|
'mac/arch_utilities.cc',
|
||||||
|
'mac/arch_utilities.h',
|
||||||
|
'mac/bootstrap_compat.cc',
|
||||||
|
'mac/bootstrap_compat.h',
|
||||||
|
'mac/byteswap.h',
|
||||||
|
'mac/dump_syms.h',
|
||||||
|
'mac/dump_syms.mm',
|
||||||
|
'mac/file_id.cc',
|
||||||
|
'mac/file_id.h',
|
||||||
|
'mac/GTMDefines.h',
|
||||||
|
'mac/GTMLogger.h',
|
||||||
|
'mac/GTMLogger.m',
|
||||||
|
'mac/HTTPMultipartUpload.h',
|
||||||
|
'mac/HTTPMultipartUpload.m',
|
||||||
|
'mac/MachIPC.h',
|
||||||
|
'mac/MachIPC.mm',
|
||||||
|
'mac/macho_id.cc',
|
||||||
|
'mac/macho_id.h',
|
||||||
|
'mac/macho_reader.cc',
|
||||||
|
'mac/macho_reader.h',
|
||||||
|
'mac/macho_utilities.cc',
|
||||||
|
'mac/macho_utilities.h',
|
||||||
|
'mac/macho_walker.cc',
|
||||||
|
'mac/macho_walker.h',
|
||||||
|
'mac/scoped_task_suspend-inl.h',
|
||||||
|
'mac/string_utilities.cc',
|
||||||
|
'mac/string_utilities.h',
|
||||||
|
'md5.cc',
|
||||||
|
'md5.h',
|
||||||
|
'memory.h',
|
||||||
|
'memory_range.h',
|
||||||
|
'module.cc',
|
||||||
|
'module.h',
|
||||||
|
'scoped_ptr.h',
|
||||||
|
'simple_string_dictionary.cc',
|
||||||
|
'simple_string_dictionary.h',
|
||||||
|
'solaris/dump_symbols.cc',
|
||||||
|
'solaris/dump_symbols.h',
|
||||||
|
'solaris/file_id.cc',
|
||||||
|
'solaris/file_id.h',
|
||||||
|
'solaris/guid_creator.cc',
|
||||||
|
'solaris/guid_creator.h',
|
||||||
|
'solaris/message_output.h',
|
||||||
|
'stabs_reader.cc',
|
||||||
|
'stabs_reader.h',
|
||||||
|
'stabs_to_module.cc',
|
||||||
|
'stabs_to_module.h',
|
||||||
|
'string_conversion.cc',
|
||||||
|
'string_conversion.h',
|
||||||
|
'symbol_data.h',
|
||||||
|
'test_assembler.cc',
|
||||||
|
'test_assembler.h',
|
||||||
|
'unordered.h',
|
||||||
|
'using_std_string.h',
|
||||||
|
'windows/common_windows.gyp',
|
||||||
|
'windows/dia_util.cc',
|
||||||
|
'windows/dia_util.h',
|
||||||
|
'windows/guid_string.cc',
|
||||||
|
'windows/guid_string.h',
|
||||||
|
'windows/http_upload.cc',
|
||||||
|
'windows/http_upload.h',
|
||||||
|
'windows/omap.cc',
|
||||||
|
'windows/omap.h',
|
||||||
|
'windows/omap_internal.h',
|
||||||
|
'windows/pdb_source_line_writer.cc',
|
||||||
|
'windows/pdb_source_line_writer.h',
|
||||||
|
'windows/string_utils-inl.h',
|
||||||
|
'windows/string_utils.cc',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'..',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'common_unittests',
|
||||||
|
'type': 'executable',
|
||||||
|
'sources': [
|
||||||
|
'android/breakpad_getcontext_unittest.cc',
|
||||||
|
'byte_cursor_unittest.cc',
|
||||||
|
'dwarf/bytereader_unittest.cc',
|
||||||
|
'dwarf/dwarf2diehandler_unittest.cc',
|
||||||
|
'dwarf/dwarf2reader_cfi_unittest.cc',
|
||||||
|
'dwarf/dwarf2reader_die_unittest.cc',
|
||||||
|
'dwarf_cfi_to_module_unittest.cc',
|
||||||
|
'dwarf_cu_to_module_unittest.cc',
|
||||||
|
'dwarf_line_to_module_unittest.cc',
|
||||||
|
'linux/dump_symbols_unittest.cc',
|
||||||
|
'linux/elf_core_dump_unittest.cc',
|
||||||
|
'linux/elf_symbols_to_module_unittest.cc',
|
||||||
|
'linux/file_id_unittest.cc',
|
||||||
|
'linux/google_crashdump_uploader_test.cc',
|
||||||
|
'linux/linux_libc_support_unittest.cc',
|
||||||
|
'linux/memory_mapped_file_unittest.cc',
|
||||||
|
'linux/safe_readlink_unittest.cc',
|
||||||
|
'linux/synth_elf_unittest.cc',
|
||||||
|
'linux/tests/auto_testfile.h',
|
||||||
|
'linux/tests/crash_generator.cc',
|
||||||
|
'linux/tests/crash_generator.h',
|
||||||
|
'mac/macho_reader_unittest.cc',
|
||||||
|
'memory_range_unittest.cc',
|
||||||
|
'memory_unittest.cc',
|
||||||
|
'module_unittest.cc',
|
||||||
|
'simple_string_dictionary_unittest.cc',
|
||||||
|
'stabs_reader_unittest.cc',
|
||||||
|
'stabs_to_module_unittest.cc',
|
||||||
|
'test_assembler_unittest.cc',
|
||||||
|
'tests/auto_tempdir.h',
|
||||||
|
'tests/file_utils.cc',
|
||||||
|
'tests/file_utils.h',
|
||||||
|
'windows/omap_unittest.cc',
|
||||||
|
],
|
||||||
|
'include_dirs': [
|
||||||
|
'..',
|
||||||
|
],
|
||||||
|
'dependencies': [
|
||||||
|
'common',
|
||||||
|
'../build/testing.gypi:gmock_main',
|
||||||
|
'../build/testing.gypi:gmock',
|
||||||
|
'../build/testing.gypi:gtest',
|
||||||
|
],
|
||||||
|
'libraries': [
|
||||||
|
'-ldl',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
@ -53,8 +53,13 @@ static const UTF32 halfMask = 0x3FFUL;
|
|||||||
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
#define UNI_SUR_HIGH_END (UTF32)0xDBFF
|
||||||
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
#define UNI_SUR_LOW_START (UTF32)0xDC00
|
||||||
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
#define UNI_SUR_LOW_END (UTF32)0xDFFF
|
||||||
|
|
||||||
|
#ifndef false
|
||||||
#define false 0
|
#define false 0
|
||||||
|
#endif
|
||||||
|
#ifndef true
|
||||||
#define true 1
|
#define true 1
|
||||||
|
#endif
|
||||||
|
|
||||||
/* --------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
@ -20,6 +20,9 @@
|
|||||||
* remains attached.
|
* remains attached.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef COMMON_CONVERT_UTF_H_
|
||||||
|
#define COMMON_CONVERT_UTF_H_
|
||||||
|
|
||||||
/* ---------------------------------------------------------------------
|
/* ---------------------------------------------------------------------
|
||||||
|
|
||||||
Conversions between UTF32, UTF-16, and UTF-8. Header file.
|
Conversions between UTF32, UTF-16, and UTF-8. Header file.
|
||||||
@ -141,3 +144,5 @@ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* --------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
#endif // COMMON_CONVERT_UTF_H_
|
||||||
|
@ -1512,16 +1512,19 @@ bool CallFrameInfo::State::DoInstruction() {
|
|||||||
|
|
||||||
// Change the base register used to compute the CFA.
|
// Change the base register used to compute the CFA.
|
||||||
case DW_CFA_def_cfa_register: {
|
case DW_CFA_def_cfa_register: {
|
||||||
|
if (!ParseOperands("r", &ops)) return false;
|
||||||
Rule *cfa_rule = rules_.CFARule();
|
Rule *cfa_rule = rules_.CFARule();
|
||||||
if (!cfa_rule) {
|
if (!cfa_rule) {
|
||||||
|
if (!DoDefCFA(ops.register_number, ops.offset)) {
|
||||||
reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
|
reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!ParseOperands("r", &ops)) return false;
|
} else {
|
||||||
cfa_rule->SetBaseRegister(ops.register_number);
|
cfa_rule->SetBaseRegister(ops.register_number);
|
||||||
if (!cfa_rule->Handle(handler_, address_,
|
if (!cfa_rule->Handle(handler_, address_,
|
||||||
Handler::kCFARegister))
|
Handler::kCFARegister))
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,6 +105,43 @@ vector<string> DwarfCFIToModule::RegisterNames::ARM() {
|
|||||||
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Per ARM IHI 0057A, section 3.1
|
||||||
|
vector<string> DwarfCFIToModule::RegisterNames::ARM64() {
|
||||||
|
static const char *const names[] = {
|
||||||
|
"x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
|
||||||
|
"x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
|
||||||
|
"x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23",
|
||||||
|
"x24", "x25", "x26", "x27", "x28", "x29", "x30", "sp",
|
||||||
|
"", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "",
|
||||||
|
"", "", "", "", "", "", "", "",
|
||||||
|
"v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
|
||||||
|
"v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15",
|
||||||
|
"v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
|
||||||
|
"v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31"
|
||||||
|
};
|
||||||
|
|
||||||
|
return MakeVector(names, sizeof(names) / sizeof(names[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> DwarfCFIToModule::RegisterNames::MIPS() {
|
||||||
|
static const char* const kRegisterNames[] = {
|
||||||
|
"$zero", "$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
|
||||||
|
"$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
|
||||||
|
"$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
|
||||||
|
"$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
|
||||||
|
"$lo", "$hi", "$pc", "$f0", "$f2", "$f3", "$f4", "$f5",
|
||||||
|
"$f6", "$f7", "$f8", "$f9", "$f10", "$f11", "$f12", "$f13",
|
||||||
|
"$f14", "$f15", "$f16", "$f17", "$f18", "$f19", "$f20",
|
||||||
|
"$f21", "$f22", "$f23", "$f24", "$f25", "$f26", "$f27",
|
||||||
|
"$f28", "$f29", "$f30", "$f31", "$fcsr", "$fir"
|
||||||
|
};
|
||||||
|
|
||||||
|
return MakeVector(kRegisterNames,
|
||||||
|
sizeof(kRegisterNames) / sizeof(kRegisterNames[0]));
|
||||||
|
}
|
||||||
|
|
||||||
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
|
bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length,
|
||||||
uint8 version, const string &augmentation,
|
uint8 version, const string &augmentation,
|
||||||
unsigned return_address) {
|
unsigned return_address) {
|
||||||
|
@ -109,6 +109,12 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
|
|||||||
// ARM.
|
// ARM.
|
||||||
static vector<string> ARM();
|
static vector<string> ARM();
|
||||||
|
|
||||||
|
// ARM64, aka AARCH64.
|
||||||
|
static vector<string> ARM64();
|
||||||
|
|
||||||
|
// MIPS.
|
||||||
|
static vector<string> MIPS();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Given STRINGS, an array of C strings with SIZE elements, return an
|
// Given STRINGS, an array of C strings with SIZE elements, return an
|
||||||
// equivalent vector<string>.
|
// equivalent vector<string>.
|
||||||
|
@ -46,16 +46,15 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <set>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "common/dwarf_line_to_module.h"
|
#include "common/dwarf_line_to_module.h"
|
||||||
|
#include "common/unordered.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
using std::map;
|
using std::map;
|
||||||
using std::pair;
|
using std::pair;
|
||||||
using std::set;
|
|
||||||
using std::sort;
|
using std::sort;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
@ -118,7 +117,7 @@ struct DwarfCUToModule::FilePrivate {
|
|||||||
// so this set will actually hold yet another copy of the string (although
|
// so this set will actually hold yet another copy of the string (although
|
||||||
// everything will still work). To improve memory consumption portably,
|
// everything will still work). To improve memory consumption portably,
|
||||||
// we will probably need to use pointers to strings held in this set.
|
// we will probably need to use pointers to strings held in this set.
|
||||||
set<string> common_strings;
|
unordered_set<string> common_strings;
|
||||||
|
|
||||||
// A map from offsets of DIEs within the .debug_info section to
|
// A map from offsets of DIEs within the .debug_info section to
|
||||||
// Specifications describing those DIEs. Specification references can
|
// Specifications describing those DIEs. Specification references can
|
||||||
@ -337,7 +336,7 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
|
|||||||
}
|
}
|
||||||
|
|
||||||
string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) {
|
string DwarfCUToModule::GenericDIEHandler::AddStringToPool(const string &str) {
|
||||||
pair<set<string>::iterator, bool> result =
|
pair<unordered_set<string>::iterator, bool> result =
|
||||||
cu_context_->file_context->file_private_->common_strings.insert(str);
|
cu_context_->file_context->file_private_->common_strings.insert(str);
|
||||||
return *result.first;
|
return *result.first;
|
||||||
}
|
}
|
||||||
@ -531,7 +530,7 @@ void DwarfCUToModule::FuncHandler::Finish() {
|
|||||||
if (low_pc_ < high_pc_) {
|
if (low_pc_ < high_pc_) {
|
||||||
// Create a Module::Function based on the data we've gathered, and
|
// Create a Module::Function based on the data we've gathered, and
|
||||||
// add it to the functions_ list.
|
// add it to the functions_ list.
|
||||||
Module::Function *func = new Module::Function;
|
scoped_ptr<Module::Function> func(new Module::Function);
|
||||||
// Malformed DWARF may omit the name, but all Module::Functions must
|
// Malformed DWARF may omit the name, but all Module::Functions must
|
||||||
// have names.
|
// have names.
|
||||||
if (!name_.empty()) {
|
if (!name_.empty()) {
|
||||||
@ -546,7 +545,7 @@ void DwarfCUToModule::FuncHandler::Finish() {
|
|||||||
if (func->address) {
|
if (func->address) {
|
||||||
// If the function address is zero this is a sign that this function
|
// If the function address is zero this is a sign that this function
|
||||||
// description is just empty debug data and should just be discarded.
|
// description is just empty debug data and should just be discarded.
|
||||||
cu_context_->functions.push_back(func);
|
cu_context_->functions.push_back(func.release());
|
||||||
}
|
}
|
||||||
} else if (inline_) {
|
} else if (inline_) {
|
||||||
AbstractOrigin origin(name_);
|
AbstractOrigin origin(name_);
|
||||||
|
70
google-breakpad/src/common/linux/crc32.cc
Normal file
70
google-breakpad/src/common/linux/crc32.cc
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
// Copyright 2014 Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#include "common/linux/crc32.h"
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// This implementation is based on the sample implementation in RFC 1952.
|
||||||
|
|
||||||
|
// CRC32 polynomial, in reversed form.
|
||||||
|
// See RFC 1952, or http://en.wikipedia.org/wiki/Cyclic_redundancy_check
|
||||||
|
static const uint32_t kCrc32Polynomial = 0xEDB88320;
|
||||||
|
static uint32_t kCrc32Table[256] = { 0 };
|
||||||
|
|
||||||
|
#define arraysize(f) (sizeof(f) / sizeof(*f))
|
||||||
|
|
||||||
|
static void EnsureCrc32TableInited() {
|
||||||
|
if (kCrc32Table[arraysize(kCrc32Table) - 1])
|
||||||
|
return; // already inited
|
||||||
|
for (uint32_t i = 0; i < arraysize(kCrc32Table); ++i) {
|
||||||
|
uint32_t c = i;
|
||||||
|
for (size_t j = 0; j < 8; ++j) {
|
||||||
|
if (c & 1) {
|
||||||
|
c = kCrc32Polynomial ^ (c >> 1);
|
||||||
|
} else {
|
||||||
|
c >>= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kCrc32Table[i] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t UpdateCrc32(uint32_t start, const void* buf, size_t len) {
|
||||||
|
EnsureCrc32TableInited();
|
||||||
|
|
||||||
|
uint32_t c = start ^ 0xFFFFFFFF;
|
||||||
|
const uint8_t* u = static_cast<const uint8_t*>(buf);
|
||||||
|
for (size_t i = 0; i < len; ++i) {
|
||||||
|
c = kCrc32Table[(c ^ u[i]) & 0xFF] ^ (c >> 8);
|
||||||
|
}
|
||||||
|
return c ^ 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
53
google-breakpad/src/common/linux/crc32.h
Normal file
53
google-breakpad/src/common/linux/crc32.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright 2014 Google Inc.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions are
|
||||||
|
// met:
|
||||||
|
//
|
||||||
|
// * Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// * Redistributions in binary form must reproduce the above
|
||||||
|
// copyright notice, this list of conditions and the following disclaimer
|
||||||
|
// in the documentation and/or other materials provided with the
|
||||||
|
// distribution.
|
||||||
|
// * Neither the name of Google Inc. nor the names of its
|
||||||
|
// contributors may be used to endorse or promote products derived from
|
||||||
|
// this software without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#ifndef COMMON_LINUX_CRC32_H_
|
||||||
|
#define COMMON_LINUX_CRC32_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace google_breakpad {
|
||||||
|
|
||||||
|
// Updates a CRC32 checksum with |len| bytes from |buf|. |initial| holds the
|
||||||
|
// checksum result from the previous update; for the first call, it should be 0.
|
||||||
|
uint32_t UpdateCrc32(uint32_t initial, const void* buf, size_t len);
|
||||||
|
|
||||||
|
// Computes a CRC32 checksum using |len| bytes from |buf|.
|
||||||
|
inline uint32_t ComputeCrc32(const void* buf, size_t len) {
|
||||||
|
return UpdateCrc32(0, buf, len);
|
||||||
|
}
|
||||||
|
inline uint32_t ComputeCrc32(const std::string& str) {
|
||||||
|
return ComputeCrc32(str.c_str(), str.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace google_breakpad
|
||||||
|
|
||||||
|
#endif // COMMON_LINUX_CRC32_H_
|
@ -57,6 +57,8 @@
|
|||||||
#include "common/dwarf_cfi_to_module.h"
|
#include "common/dwarf_cfi_to_module.h"
|
||||||
#include "common/dwarf_cu_to_module.h"
|
#include "common/dwarf_cu_to_module.h"
|
||||||
#include "common/dwarf_line_to_module.h"
|
#include "common/dwarf_line_to_module.h"
|
||||||
|
#include "common/linux/crc32.h"
|
||||||
|
#include "common/linux/eintr_wrapper.h"
|
||||||
#include "common/linux/elfutils.h"
|
#include "common/linux/elfutils.h"
|
||||||
#include "common/linux/elfutils-inl.h"
|
#include "common/linux/elfutils-inl.h"
|
||||||
#include "common/linux/elf_symbols_to_module.h"
|
#include "common/linux/elf_symbols_to_module.h"
|
||||||
@ -88,6 +90,11 @@ using google_breakpad::StabsToModule;
|
|||||||
#endif
|
#endif
|
||||||
using google_breakpad::scoped_ptr;
|
using google_breakpad::scoped_ptr;
|
||||||
|
|
||||||
|
// Define AARCH64 ELF architecture if host machine does not include this define.
|
||||||
|
#ifndef EM_AARCH64
|
||||||
|
#define EM_AARCH64 183
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// FDWrapper
|
// FDWrapper
|
||||||
//
|
//
|
||||||
@ -141,7 +148,7 @@ class MmapWrapper {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool is_set_;
|
bool is_set_;
|
||||||
void *base_;
|
void* base_;
|
||||||
size_t size_;
|
size_t size_;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -204,8 +211,8 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler {
|
|||||||
void StartCompilationUnit(const string& compilation_dir) {
|
void StartCompilationUnit(const string& compilation_dir) {
|
||||||
compilation_dir_ = compilation_dir;
|
compilation_dir_ = compilation_dir;
|
||||||
}
|
}
|
||||||
void ReadProgram(const char *program, uint64 length,
|
void ReadProgram(const char* program, uint64 length,
|
||||||
Module *module, std::vector<Module::Line> *lines) {
|
Module* module, std::vector<Module::Line>* lines) {
|
||||||
DwarfLineToModule handler(module, compilation_dir_, lines);
|
DwarfLineToModule handler(module, compilation_dir_, lines);
|
||||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||||
parser.Start();
|
parser.Start();
|
||||||
@ -291,6 +298,12 @@ bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
|
|||||||
case EM_ARM:
|
case EM_ARM:
|
||||||
*register_names = DwarfCFIToModule::RegisterNames::ARM();
|
*register_names = DwarfCFIToModule::RegisterNames::ARM();
|
||||||
return true;
|
return true;
|
||||||
|
case EM_AARCH64:
|
||||||
|
*register_names = DwarfCFIToModule::RegisterNames::ARM64();
|
||||||
|
return true;
|
||||||
|
case EM_MIPS:
|
||||||
|
*register_names = DwarfCFIToModule::RegisterNames::MIPS();
|
||||||
|
return true;
|
||||||
case EM_X86_64:
|
case EM_X86_64:
|
||||||
*register_names = DwarfCFIToModule::RegisterNames::X86_64();
|
*register_names = DwarfCFIToModule::RegisterNames::X86_64();
|
||||||
return true;
|
return true;
|
||||||
@ -366,7 +379,7 @@ bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper,
|
|||||||
obj_file.c_str(), strerror(errno));
|
obj_file.c_str(), strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void *obj_base = mmap(NULL, st.st_size,
|
void* obj_base = mmap(NULL, st.st_size,
|
||||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
|
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
|
||||||
if (obj_base == MAP_FAILED) {
|
if (obj_base == MAP_FAILED) {
|
||||||
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
|
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
|
||||||
@ -402,19 +415,19 @@ bool ElfEndianness(const typename ElfClass::Ehdr* elf_header,
|
|||||||
|
|
||||||
// Read the .gnu_debuglink and get the debug file name. If anything goes
|
// Read the .gnu_debuglink and get the debug file name. If anything goes
|
||||||
// wrong, return an empty string.
|
// wrong, return an empty string.
|
||||||
template<typename ElfClass>
|
|
||||||
string ReadDebugLink(const char* debuglink,
|
string ReadDebugLink(const char* debuglink,
|
||||||
size_t debuglink_size,
|
const size_t debuglink_size,
|
||||||
|
const bool big_endian,
|
||||||
const string& obj_file,
|
const string& obj_file,
|
||||||
const std::vector<string>& debug_dirs) {
|
const std::vector<string>& debug_dirs) {
|
||||||
size_t debuglink_len = strlen(debuglink) + 5; // '\0' + CRC32.
|
size_t debuglink_len = strlen(debuglink) + 5; // Include '\0' + CRC32.
|
||||||
debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round to nearest 4 bytes.
|
debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round up to 4 bytes.
|
||||||
|
|
||||||
// Sanity check.
|
// Sanity check.
|
||||||
if (debuglink_len != debuglink_size) {
|
if (debuglink_len != debuglink_size) {
|
||||||
fprintf(stderr, "Mismatched .gnu_debuglink string / section size: "
|
fprintf(stderr, "Mismatched .gnu_debuglink string / section size: "
|
||||||
"%zx %zx\n", debuglink_len, debuglink_size);
|
"%zx %zx\n", debuglink_len, debuglink_size);
|
||||||
return "";
|
return string();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@ -425,11 +438,40 @@ string ReadDebugLink(const char* debuglink,
|
|||||||
const string& debug_dir = *it;
|
const string& debug_dir = *it;
|
||||||
debuglink_path = debug_dir + "/" + debuglink;
|
debuglink_path = debug_dir + "/" + debuglink;
|
||||||
debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
|
debuglink_fd = open(debuglink_path.c_str(), O_RDONLY);
|
||||||
if (debuglink_fd >= 0) {
|
if (debuglink_fd < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
FDWrapper debuglink_fd_wrapper(debuglink_fd);
|
||||||
|
|
||||||
|
// The CRC is the last 4 bytes in |debuglink|.
|
||||||
|
const dwarf2reader::Endianness endianness = big_endian ?
|
||||||
|
dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
|
||||||
|
dwarf2reader::ByteReader byte_reader(endianness);
|
||||||
|
uint32_t expected_crc =
|
||||||
|
byte_reader.ReadFourBytes(&debuglink[debuglink_size - 4]);
|
||||||
|
|
||||||
|
uint32_t actual_crc = 0;
|
||||||
|
while (true) {
|
||||||
|
const size_t kReadSize = 4096;
|
||||||
|
char buf[kReadSize];
|
||||||
|
ssize_t bytes_read = HANDLE_EINTR(read(debuglink_fd, &buf, kReadSize));
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
fprintf(stderr, "Error reading debug ELF file %s.\n",
|
||||||
|
debuglink_path.c_str());
|
||||||
|
return string();
|
||||||
|
}
|
||||||
|
if (bytes_read == 0)
|
||||||
|
break;
|
||||||
|
actual_crc = google_breakpad::UpdateCrc32(actual_crc, buf, bytes_read);
|
||||||
|
}
|
||||||
|
if (actual_crc != expected_crc) {
|
||||||
|
fprintf(stderr, "Error reading debug ELF file - CRC32 mismatch: %s\n",
|
||||||
|
debuglink_path.c_str());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n",
|
fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n",
|
||||||
@ -438,13 +480,9 @@ string ReadDebugLink(const char* debuglink,
|
|||||||
const string debug_dir = *it;
|
const string debug_dir = *it;
|
||||||
fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink);
|
fprintf(stderr, " %s/%s\n", debug_dir.c_str(), debuglink);
|
||||||
}
|
}
|
||||||
return "";
|
return string();
|
||||||
}
|
}
|
||||||
|
|
||||||
FDWrapper debuglink_fd_wrapper(debuglink_fd);
|
|
||||||
// TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink
|
|
||||||
// section.
|
|
||||||
|
|
||||||
return debuglink_path;
|
return debuglink_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,6 +572,7 @@ bool LoadSymbols(const string& obj_file,
|
|||||||
typedef typename ElfClass::Addr Addr;
|
typedef typename ElfClass::Addr Addr;
|
||||||
typedef typename ElfClass::Phdr Phdr;
|
typedef typename ElfClass::Phdr Phdr;
|
||||||
typedef typename ElfClass::Shdr Shdr;
|
typedef typename ElfClass::Shdr Shdr;
|
||||||
|
typedef typename ElfClass::Word Word;
|
||||||
|
|
||||||
Addr loading_addr = GetLoadingAddress<ElfClass>(
|
Addr loading_addr = GetLoadingAddress<ElfClass>(
|
||||||
GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
|
GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff),
|
||||||
@ -541,6 +580,8 @@ bool LoadSymbols(const string& obj_file,
|
|||||||
module->SetLoadAddress(loading_addr);
|
module->SetLoadAddress(loading_addr);
|
||||||
info->set_loading_addr(loading_addr, obj_file);
|
info->set_loading_addr(loading_addr, obj_file);
|
||||||
|
|
||||||
|
Word debug_section_type =
|
||||||
|
elf_header->e_machine == EM_MIPS ? SHT_MIPS_DWARF : SHT_PROGBITS;
|
||||||
const Shdr* sections =
|
const Shdr* sections =
|
||||||
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
|
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
|
||||||
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
||||||
@ -574,7 +615,7 @@ bool LoadSymbols(const string& obj_file,
|
|||||||
|
|
||||||
// Look for DWARF debugging information, and load it if present.
|
// Look for DWARF debugging information, and load it if present.
|
||||||
const Shdr* dwarf_section =
|
const Shdr* dwarf_section =
|
||||||
FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS,
|
FindElfSectionByName<ElfClass>(".debug_info", debug_section_type,
|
||||||
sections, names, names_end,
|
sections, names, names_end,
|
||||||
elf_header->e_shnum);
|
elf_header->e_shnum);
|
||||||
if (dwarf_section) {
|
if (dwarf_section) {
|
||||||
@ -593,7 +634,7 @@ bool LoadSymbols(const string& obj_file,
|
|||||||
// Dwarf Call Frame Information (CFI) is actually independent from
|
// Dwarf Call Frame Information (CFI) is actually independent from
|
||||||
// the other DWARF debugging information, and can be used alone.
|
// the other DWARF debugging information, and can be used alone.
|
||||||
const Shdr* dwarf_cfi_section =
|
const Shdr* dwarf_cfi_section =
|
||||||
FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS,
|
FindElfSectionByName<ElfClass>(".debug_frame", debug_section_type,
|
||||||
sections, names, names_end,
|
sections, names, names_end,
|
||||||
elf_header->e_shnum);
|
elf_header->e_shnum);
|
||||||
if (dwarf_cfi_section) {
|
if (dwarf_cfi_section) {
|
||||||
@ -648,13 +689,17 @@ bool LoadSymbols(const string& obj_file,
|
|||||||
names_end, elf_header->e_shnum);
|
names_end, elf_header->e_shnum);
|
||||||
if (gnu_debuglink_section) {
|
if (gnu_debuglink_section) {
|
||||||
if (!info->debug_dirs().empty()) {
|
if (!info->debug_dirs().empty()) {
|
||||||
|
found_debug_info_section = true;
|
||||||
|
|
||||||
const char* debuglink_contents =
|
const char* debuglink_contents =
|
||||||
GetOffset<ElfClass, char>(elf_header,
|
GetOffset<ElfClass, char>(elf_header,
|
||||||
gnu_debuglink_section->sh_offset);
|
gnu_debuglink_section->sh_offset);
|
||||||
string debuglink_file
|
string debuglink_file =
|
||||||
= ReadDebugLink<ElfClass>(debuglink_contents,
|
ReadDebugLink(debuglink_contents,
|
||||||
gnu_debuglink_section->sh_size,
|
gnu_debuglink_section->sh_size,
|
||||||
obj_file, info->debug_dirs());
|
big_endian,
|
||||||
|
obj_file,
|
||||||
|
info->debug_dirs());
|
||||||
info->set_debuglink_file(debuglink_file);
|
info->set_debuglink_file(debuglink_file);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, ".gnu_debuglink section found in '%s', "
|
fprintf(stderr, ".gnu_debuglink section found in '%s', "
|
||||||
@ -664,10 +709,10 @@ bool LoadSymbols(const string& obj_file,
|
|||||||
fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n",
|
fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n",
|
||||||
obj_file.c_str());
|
obj_file.c_str());
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.symbol_data != ONLY_CFI) {
|
if (options.symbol_data != ONLY_CFI) {
|
||||||
// The caller doesn't want to consult .gnu_debuglink.
|
|
||||||
// See if there are export symbols available.
|
|
||||||
const Shdr* dynsym_section =
|
const Shdr* dynsym_section =
|
||||||
FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM,
|
FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM,
|
||||||
sections, names, names_end,
|
sections, names, names_end,
|
||||||
@ -697,17 +742,12 @@ bool LoadSymbols(const string& obj_file,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if some usable information was found, since
|
if (read_gnu_debug_link) {
|
||||||
// the caller doesn't want to use .gnu_debuglink.
|
return found_debug_info_section;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if some usable information was found
|
||||||
return found_usable_info;
|
return found_usable_info;
|
||||||
}
|
|
||||||
|
|
||||||
// No debug info was found, let the user try again with .gnu_debuglink
|
|
||||||
// if present.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the breakpad symbol file identifier for the architecture of
|
// Return the breakpad symbol file identifier for the architecture of
|
||||||
@ -719,6 +759,7 @@ const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
|
|||||||
switch (arch) {
|
switch (arch) {
|
||||||
case EM_386: return "x86";
|
case EM_386: return "x86";
|
||||||
case EM_ARM: return "arm";
|
case EM_ARM: return "arm";
|
||||||
|
case EM_AARCH64: return "arm64";
|
||||||
case EM_MIPS: return "mips";
|
case EM_MIPS: return "mips";
|
||||||
case EM_PPC64: return "ppc64";
|
case EM_PPC64: return "ppc64";
|
||||||
case EM_PPC: return "ppc";
|
case EM_PPC: return "ppc";
|
||||||
@ -753,7 +794,7 @@ string FormatIdentifier(unsigned char identifier[16]) {
|
|||||||
// last slash, or the whole filename if there are no slashes.
|
// last slash, or the whole filename if there are no slashes.
|
||||||
string BaseFileName(const string &filename) {
|
string BaseFileName(const string &filename) {
|
||||||
// Lots of copies! basename's behavior is less than ideal.
|
// Lots of copies! basename's behavior is less than ideal.
|
||||||
char *c_filename = strdup(filename.c_str());
|
char* c_filename = strdup(filename.c_str());
|
||||||
string base = basename(c_filename);
|
string base = basename(c_filename);
|
||||||
free(c_filename);
|
free(c_filename);
|
||||||
return base;
|
return base;
|
||||||
|
@ -37,11 +37,22 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#define HANDLE_EINTR(x) ({ \
|
#define HANDLE_EINTR(x) ({ \
|
||||||
typeof(x) __eintr_result__; \
|
typeof(x) eintr_wrapper_result; \
|
||||||
do { \
|
do { \
|
||||||
__eintr_result__ = x; \
|
eintr_wrapper_result = (x); \
|
||||||
} while (__eintr_result__ == -1 && errno == EINTR); \
|
} while (eintr_wrapper_result == -1 && errno == EINTR); \
|
||||||
__eintr_result__;\
|
eintr_wrapper_result; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define IGNORE_EINTR(x) ({ \
|
||||||
|
typeof(x) eintr_wrapper_result; \
|
||||||
|
do { \
|
||||||
|
eintr_wrapper_result = (x); \
|
||||||
|
if (eintr_wrapper_result == -1 && errno == EINTR) { \
|
||||||
|
eintr_wrapper_result = 0; \
|
||||||
|
} \
|
||||||
|
} while (0); \
|
||||||
|
eintr_wrapper_result; \
|
||||||
})
|
})
|
||||||
|
|
||||||
#endif // COMMON_LINUX_EINTR_WRAPPER_H_
|
#endif // COMMON_LINUX_EINTR_WRAPPER_H_
|
||||||
|
@ -70,7 +70,7 @@ TEST(ElfCoreDumpTest, TestElfHeader) {
|
|||||||
ElfCoreDump core;
|
ElfCoreDump core;
|
||||||
|
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
EXPECT_EQ(NULL, core.GetHeader());
|
EXPECT_EQ(NULL, core.GetHeader());
|
||||||
@ -80,49 +80,49 @@ TEST(ElfCoreDumpTest, TestElfHeader) {
|
|||||||
EXPECT_FALSE(core.GetFirstNote().IsValid());
|
EXPECT_FALSE(core.GetFirstNote().IsValid());
|
||||||
|
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
|
|
||||||
header.e_ident[0] = ELFMAG0;
|
header.e_ident[0] = ELFMAG0;
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
|
|
||||||
header.e_ident[1] = ELFMAG1;
|
header.e_ident[1] = ELFMAG1;
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
|
|
||||||
header.e_ident[2] = ELFMAG2;
|
header.e_ident[2] = ELFMAG2;
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
|
|
||||||
header.e_ident[3] = ELFMAG3;
|
header.e_ident[3] = ELFMAG3;
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
|
|
||||||
header.e_ident[4] = ElfCoreDump::kClass;
|
header.e_ident[4] = ElfCoreDump::kClass;
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
|
|
||||||
header.e_version = EV_CURRENT;
|
header.e_version = EV_CURRENT;
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_FALSE(core.IsValid());
|
EXPECT_FALSE(core.IsValid());
|
||||||
|
|
||||||
header.e_type = ET_CORE;
|
header.e_type = ET_CORE;
|
||||||
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header)));
|
||||||
ASSERT_TRUE(mapped_core_file.Map(core_file));
|
ASSERT_TRUE(mapped_core_file.Map(core_file, 0));
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
EXPECT_TRUE(core.IsValid());
|
EXPECT_TRUE(core.IsValid());
|
||||||
}
|
}
|
||||||
@ -138,22 +138,26 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
|
|||||||
const unsigned kNumOfThreads = 3;
|
const unsigned kNumOfThreads = 3;
|
||||||
const unsigned kCrashThread = 1;
|
const unsigned kCrashThread = 1;
|
||||||
const int kCrashSignal = SIGABRT;
|
const int kCrashSignal = SIGABRT;
|
||||||
// TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
|
ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
|
||||||
// CrashGenerator is identified and fixed.
|
kCrashSignal, NULL));
|
||||||
if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread,
|
|
||||||
kCrashSignal, NULL)) {
|
|
||||||
fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped "
|
|
||||||
"due to no core dump generated");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread);
|
pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread);
|
||||||
set<pid_t> expected_thread_ids;
|
set<pid_t> expected_thread_ids;
|
||||||
for (unsigned i = 0; i < kNumOfThreads; ++i) {
|
for (unsigned i = 0; i < kNumOfThreads; ++i) {
|
||||||
expected_thread_ids.insert(crash_generator.GetThreadId(i));
|
expected_thread_ids.insert(crash_generator.GetThreadId(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
struct stat st;
|
||||||
|
if (stat(crash_generator.GetCoreFilePath().c_str(), &st) != 0) {
|
||||||
|
fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped "
|
||||||
|
"due to no core file being generated");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
MemoryMappedFile mapped_core_file;
|
MemoryMappedFile mapped_core_file;
|
||||||
ASSERT_TRUE(mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str()));
|
ASSERT_TRUE(
|
||||||
|
mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str(), 0));
|
||||||
|
|
||||||
ElfCoreDump core;
|
ElfCoreDump core;
|
||||||
core.SetContent(mapped_core_file.content());
|
core.SetContent(mapped_core_file.content());
|
||||||
@ -182,6 +186,7 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
|
|||||||
|
|
||||||
size_t num_nt_prpsinfo = 0;
|
size_t num_nt_prpsinfo = 0;
|
||||||
size_t num_nt_prstatus = 0;
|
size_t num_nt_prstatus = 0;
|
||||||
|
size_t num_pr_fpvalid = 0;
|
||||||
#if defined(__i386__) || defined(__x86_64__)
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
size_t num_nt_fpregset = 0;
|
size_t num_nt_fpregset = 0;
|
||||||
#endif
|
#endif
|
||||||
@ -213,6 +218,8 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
|
|||||||
EXPECT_EQ(kCrashSignal, status->pr_info.si_signo);
|
EXPECT_EQ(kCrashSignal, status->pr_info.si_signo);
|
||||||
}
|
}
|
||||||
++num_nt_prstatus;
|
++num_nt_prstatus;
|
||||||
|
if (status->pr_fpvalid)
|
||||||
|
++num_pr_fpvalid;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#if defined(__i386__) || defined(__x86_64__)
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
@ -241,9 +248,9 @@ TEST(ElfCoreDumpTest, ValidCoreFile) {
|
|||||||
EXPECT_EQ(1U, num_nt_prpsinfo);
|
EXPECT_EQ(1U, num_nt_prpsinfo);
|
||||||
EXPECT_EQ(kNumOfThreads, num_nt_prstatus);
|
EXPECT_EQ(kNumOfThreads, num_nt_prstatus);
|
||||||
#if defined(__i386__) || defined(__x86_64__)
|
#if defined(__i386__) || defined(__x86_64__)
|
||||||
EXPECT_EQ(kNumOfThreads, num_nt_fpregset);
|
EXPECT_EQ(num_pr_fpvalid, num_nt_fpregset);
|
||||||
#endif
|
#endif
|
||||||
#if defined(__i386__)
|
#if defined(__i386__)
|
||||||
EXPECT_EQ(kNumOfThreads, num_nt_prxfpreg);
|
EXPECT_EQ(num_pr_fpvalid, num_nt_prxfpreg);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ void FindElfClassSection(const char *elf_base,
|
|||||||
const char *section_name,
|
const char *section_name,
|
||||||
typename ElfClass::Word section_type,
|
typename ElfClass::Word section_type,
|
||||||
const void **section_start,
|
const void **section_start,
|
||||||
int *section_size) {
|
size_t *section_size) {
|
||||||
typedef typename ElfClass::Ehdr Ehdr;
|
typedef typename ElfClass::Ehdr Ehdr;
|
||||||
typedef typename ElfClass::Shdr Shdr;
|
typedef typename ElfClass::Shdr Shdr;
|
||||||
|
|
||||||
@ -58,10 +58,10 @@ void FindElfClassSection(const char *elf_base,
|
|||||||
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
|
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
|
||||||
|
|
||||||
const Shdr* sections =
|
const Shdr* sections =
|
||||||
GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff);
|
GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff);
|
||||||
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
const Shdr* section_names = sections + elf_header->e_shstrndx;
|
||||||
const char* names =
|
const char* names =
|
||||||
GetOffset<ElfClass,char>(elf_header, section_names->sh_offset);
|
GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
|
||||||
const char *names_end = names + section_names->sh_size;
|
const char *names_end = names + section_names->sh_size;
|
||||||
|
|
||||||
const Shdr* section =
|
const Shdr* section =
|
||||||
@ -79,7 +79,7 @@ template<typename ElfClass>
|
|||||||
void FindElfClassSegment(const char *elf_base,
|
void FindElfClassSegment(const char *elf_base,
|
||||||
typename ElfClass::Word segment_type,
|
typename ElfClass::Word segment_type,
|
||||||
const void **segment_start,
|
const void **segment_start,
|
||||||
int *segment_size) {
|
size_t *segment_size) {
|
||||||
typedef typename ElfClass::Ehdr Ehdr;
|
typedef typename ElfClass::Ehdr Ehdr;
|
||||||
typedef typename ElfClass::Phdr Phdr;
|
typedef typename ElfClass::Phdr Phdr;
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ void FindElfClassSegment(const char *elf_base,
|
|||||||
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
|
assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
|
||||||
|
|
||||||
const Phdr* phdrs =
|
const Phdr* phdrs =
|
||||||
GetOffset<ElfClass,Phdr>(elf_header, elf_header->e_phoff);
|
GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff);
|
||||||
|
|
||||||
for (int i = 0; i < elf_header->e_phnum; ++i) {
|
for (int i = 0; i < elf_header->e_phnum; ++i) {
|
||||||
if (phdrs[i].p_type == segment_type) {
|
if (phdrs[i].p_type == segment_type) {
|
||||||
@ -122,7 +122,7 @@ bool FindElfSection(const void *elf_mapped_base,
|
|||||||
const char *section_name,
|
const char *section_name,
|
||||||
uint32_t section_type,
|
uint32_t section_type,
|
||||||
const void **section_start,
|
const void **section_start,
|
||||||
int *section_size,
|
size_t *section_size,
|
||||||
int *elfclass) {
|
int *elfclass) {
|
||||||
assert(elf_mapped_base);
|
assert(elf_mapped_base);
|
||||||
assert(section_start);
|
assert(section_start);
|
||||||
@ -158,7 +158,7 @@ bool FindElfSection(const void *elf_mapped_base,
|
|||||||
bool FindElfSegment(const void *elf_mapped_base,
|
bool FindElfSegment(const void *elf_mapped_base,
|
||||||
uint32_t segment_type,
|
uint32_t segment_type,
|
||||||
const void **segment_start,
|
const void **segment_start,
|
||||||
int *segment_size,
|
size_t *segment_size,
|
||||||
int *elfclass) {
|
int *elfclass) {
|
||||||
assert(elf_mapped_base);
|
assert(elf_mapped_base);
|
||||||
assert(segment_start);
|
assert(segment_start);
|
||||||
|
@ -30,8 +30,8 @@
|
|||||||
// elfutils.h: Utilities for dealing with ELF files.
|
// elfutils.h: Utilities for dealing with ELF files.
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef COMMON_LINUX_ELFUTILS_H__
|
#ifndef COMMON_LINUX_ELFUTILS_H_
|
||||||
#define COMMON_LINUX_ELFUTILS_H__
|
#define COMMON_LINUX_ELFUTILS_H_
|
||||||
|
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <link.h>
|
#include <link.h>
|
||||||
@ -79,7 +79,7 @@ bool FindElfSection(const void *elf_mapped_base,
|
|||||||
const char *section_name,
|
const char *section_name,
|
||||||
uint32_t section_type,
|
uint32_t section_type,
|
||||||
const void **section_start,
|
const void **section_start,
|
||||||
int *section_size,
|
size_t *section_size,
|
||||||
int *elfclass);
|
int *elfclass);
|
||||||
|
|
||||||
// Internal helper method, exposed for convenience for callers
|
// Internal helper method, exposed for convenience for callers
|
||||||
@ -101,7 +101,7 @@ FindElfSectionByName(const char* name,
|
|||||||
bool FindElfSegment(const void *elf_mapped_base,
|
bool FindElfSegment(const void *elf_mapped_base,
|
||||||
uint32_t segment_type,
|
uint32_t segment_type,
|
||||||
const void **segment_start,
|
const void **segment_start,
|
||||||
int *segment_size,
|
size_t *segment_size,
|
||||||
int *elfclass);
|
int *elfclass);
|
||||||
|
|
||||||
// Convert an offset from an Elf header into a pointer to the mapped
|
// Convert an offset from an Elf header into a pointer to the mapped
|
||||||
@ -115,4 +115,4 @@ GetOffset(const typename ElfClass::Ehdr* elf_header,
|
|||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
||||||
#endif // COMMON_LINUX_ELFUTILS_H__
|
#endif // COMMON_LINUX_ELFUTILS_H_
|
||||||
|
@ -48,9 +48,7 @@
|
|||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
FileID::FileID(const char* path) {
|
FileID::FileID(const char* path) : path_(path) {}
|
||||||
strncpy(path_, path, sizeof(path_));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ELF note name and desc are 32-bits word padded.
|
// ELF note name and desc are 32-bits word padded.
|
||||||
#define NOTE_PADDING(a) ((a + 3) & ~3)
|
#define NOTE_PADDING(a) ((a + 3) & ~3)
|
||||||
@ -59,7 +57,7 @@ FileID::FileID(const char* path) {
|
|||||||
// and use the syscall/libc wrappers instead of direct syscalls or libc.
|
// and use the syscall/libc wrappers instead of direct syscalls or libc.
|
||||||
|
|
||||||
template<typename ElfClass>
|
template<typename ElfClass>
|
||||||
static bool ElfClassBuildIDNoteIdentifier(const void *section, int length,
|
static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length,
|
||||||
uint8_t identifier[kMDGUIDSize]) {
|
uint8_t identifier[kMDGUIDSize]) {
|
||||||
typedef typename ElfClass::Nhdr Nhdr;
|
typedef typename ElfClass::Nhdr Nhdr;
|
||||||
|
|
||||||
@ -94,7 +92,8 @@ static bool ElfClassBuildIDNoteIdentifier(const void *section, int length,
|
|||||||
static bool FindElfBuildIDNote(const void *elf_mapped_base,
|
static bool FindElfBuildIDNote(const void *elf_mapped_base,
|
||||||
uint8_t identifier[kMDGUIDSize]) {
|
uint8_t identifier[kMDGUIDSize]) {
|
||||||
void* note_section;
|
void* note_section;
|
||||||
int note_size, elfclass;
|
size_t note_size;
|
||||||
|
int elfclass;
|
||||||
if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
|
if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
|
||||||
(const void**)¬e_section, ¬e_size, &elfclass) ||
|
(const void**)¬e_section, ¬e_size, &elfclass) ||
|
||||||
note_size == 0) &&
|
note_size == 0) &&
|
||||||
@ -120,7 +119,7 @@ static bool FindElfBuildIDNote(const void *elf_mapped_base,
|
|||||||
static bool HashElfTextSection(const void *elf_mapped_base,
|
static bool HashElfTextSection(const void *elf_mapped_base,
|
||||||
uint8_t identifier[kMDGUIDSize]) {
|
uint8_t identifier[kMDGUIDSize]) {
|
||||||
void* text_section;
|
void* text_section;
|
||||||
int text_size;
|
size_t text_size;
|
||||||
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
|
if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS,
|
||||||
(const void**)&text_section, &text_size, NULL) ||
|
(const void**)&text_section, &text_size, NULL) ||
|
||||||
text_size == 0) {
|
text_size == 0) {
|
||||||
@ -129,7 +128,7 @@ static bool HashElfTextSection(const void *elf_mapped_base,
|
|||||||
|
|
||||||
my_memset(identifier, 0, kMDGUIDSize);
|
my_memset(identifier, 0, kMDGUIDSize);
|
||||||
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
|
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section);
|
||||||
const uint8_t* ptr_end = ptr + std::min(text_size, 4096);
|
const uint8_t* ptr_end = ptr + std::min(text_size, static_cast<size_t>(4096));
|
||||||
while (ptr < ptr_end) {
|
while (ptr < ptr_end) {
|
||||||
for (unsigned i = 0; i < kMDGUIDSize; i++)
|
for (unsigned i = 0; i < kMDGUIDSize; i++)
|
||||||
identifier[i] ^= ptr[i];
|
identifier[i] ^= ptr[i];
|
||||||
@ -150,7 +149,7 @@ bool FileID::ElfFileIdentifierFromMappedFile(const void* base,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
|
bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) {
|
||||||
MemoryMappedFile mapped_file(path_);
|
MemoryMappedFile mapped_file(path_.c_str(), 0);
|
||||||
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)?
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
#define COMMON_LINUX_FILE_ID_H__
|
#define COMMON_LINUX_FILE_ID_H__
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "common/linux/guid_creator.h"
|
#include "common/linux/guid_creator.h"
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ class FileID {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
// Storage for the path specified
|
// Storage for the path specified
|
||||||
char path_[PATH_MAX];
|
std::string path_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
@ -66,6 +66,9 @@ void PopulateSection(Section* section, int size, int prime_number) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
#ifndef __ANDROID__
|
||||||
|
// This test is disabled on Android: It will always fail, since there is no
|
||||||
|
// 'strip' binary installed on test devices.
|
||||||
TEST(FileIDStripTest, StripSelf) {
|
TEST(FileIDStripTest, StripSelf) {
|
||||||
// Calculate the File ID of this binary using
|
// Calculate the File ID of this binary using
|
||||||
// FileID::ElfFileIdentifier, then make a copy of this binary,
|
// FileID::ElfFileIdentifier, then make a copy of this binary,
|
||||||
@ -98,6 +101,7 @@ TEST(FileIDStripTest, StripSelf) {
|
|||||||
37);
|
37);
|
||||||
EXPECT_STREQ(identifier_string1, identifier_string2);
|
EXPECT_STREQ(identifier_string1, identifier_string2);
|
||||||
}
|
}
|
||||||
|
#endif // !__ANDROID__
|
||||||
|
|
||||||
template<typename ElfClass>
|
template<typename ElfClass>
|
||||||
class FileIDTest : public testing::Test {
|
class FileIDTest : public testing::Test {
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "common/linux/google_crashdump_uploader.h"
|
#include "common/linux/google_crashdump_uploader.h"
|
||||||
#include "common/linux/libcurl_wrapper.h"
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@ -112,7 +111,7 @@ void GoogleCrashdumpUploader::Init(const string& product,
|
|||||||
ctime_ = ctime;
|
ctime_ = ctime;
|
||||||
email_ = email;
|
email_ = email;
|
||||||
comments_ = comments;
|
comments_ = comments;
|
||||||
http_layer_ = http_layer;
|
http_layer_.reset(http_layer);
|
||||||
|
|
||||||
crash_server_ = crash_server;
|
crash_server_ = crash_server;
|
||||||
proxy_host_ = proxy_host;
|
proxy_host_ = proxy_host;
|
||||||
@ -162,7 +161,9 @@ bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GoogleCrashdumpUploader::Upload() {
|
bool GoogleCrashdumpUploader::Upload(int* http_status_code,
|
||||||
|
string* http_response_header,
|
||||||
|
string* http_response_body) {
|
||||||
bool ok = http_layer_->Init();
|
bool ok = http_layer_->Init();
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
std::cout << "http layer init failed";
|
std::cout << "http layer init failed";
|
||||||
@ -194,6 +195,8 @@ bool GoogleCrashdumpUploader::Upload() {
|
|||||||
std::cout << "Sending request to " << crash_server_;
|
std::cout << "Sending request to " << crash_server_;
|
||||||
return http_layer_->SendRequest(crash_server_,
|
return http_layer_->SendRequest(crash_server_,
|
||||||
parameters_,
|
parameters_,
|
||||||
NULL);
|
http_status_code,
|
||||||
|
http_response_header,
|
||||||
|
http_response_body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,15 +28,18 @@
|
|||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_
|
||||||
|
#define COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "common/linux/libcurl_wrapper.h"
|
||||||
|
#include "common/scoped_ptr.h"
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
namespace google_breakpad {
|
namespace google_breakpad {
|
||||||
|
|
||||||
class LibcurlWrapper;
|
|
||||||
|
|
||||||
class GoogleCrashdumpUploader {
|
class GoogleCrashdumpUploader {
|
||||||
public:
|
public:
|
||||||
GoogleCrashdumpUploader(const string& product,
|
GoogleCrashdumpUploader(const string& product,
|
||||||
@ -76,12 +79,14 @@ class GoogleCrashdumpUploader {
|
|||||||
const string& proxy_host,
|
const string& proxy_host,
|
||||||
const string& proxy_userpassword,
|
const string& proxy_userpassword,
|
||||||
LibcurlWrapper* http_layer);
|
LibcurlWrapper* http_layer);
|
||||||
bool Upload();
|
bool Upload(int* http_status_code,
|
||||||
|
string* http_response_header,
|
||||||
|
string* http_response_body);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool CheckRequiredParametersArePresent();
|
bool CheckRequiredParametersArePresent();
|
||||||
|
|
||||||
LibcurlWrapper* http_layer_;
|
scoped_ptr<LibcurlWrapper> http_layer_;
|
||||||
string product_;
|
string product_;
|
||||||
string version_;
|
string version_;
|
||||||
string guid_;
|
string guid_;
|
||||||
@ -98,3 +103,5 @@ class GoogleCrashdumpUploader {
|
|||||||
std::map<string, string> parameters_;
|
std::map<string, string> parameters_;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "common/linux/google_crashdump_uploader.h"
|
#include "common/linux/google_crashdump_uploader.h"
|
||||||
#include "common/linux/libcurl_wrapper.h"
|
|
||||||
#include "breakpad_googletest_includes.h"
|
#include "breakpad_googletest_includes.h"
|
||||||
#include "common/using_std_string.h"
|
#include "common/using_std_string.h"
|
||||||
|
|
||||||
@ -48,10 +47,12 @@ class MockLibcurlWrapper : public LibcurlWrapper {
|
|||||||
const string& proxy_userpwd));
|
const string& proxy_userpwd));
|
||||||
MOCK_METHOD2(AddFile, bool(const string& upload_file_path,
|
MOCK_METHOD2(AddFile, bool(const string& upload_file_path,
|
||||||
const string& basename));
|
const string& basename));
|
||||||
MOCK_METHOD3(SendRequest,
|
MOCK_METHOD5(SendRequest,
|
||||||
bool(const string& url,
|
bool(const string& url,
|
||||||
const std::map<string, string>& parameters,
|
const std::map<string, string>& parameters,
|
||||||
string* server_response));
|
int* http_status_code,
|
||||||
|
string* http_header_data,
|
||||||
|
string* http_response_data));
|
||||||
};
|
};
|
||||||
|
|
||||||
class GoogleCrashdumpUploaderTest : public ::testing::Test {
|
class GoogleCrashdumpUploaderTest : public ::testing::Test {
|
||||||
@ -72,7 +73,7 @@ TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) {
|
|||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
&m);
|
&m);
|
||||||
ASSERT_FALSE(uploader->Upload());
|
ASSERT_FALSE(uploader->Upload(NULL, NULL, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) {
|
TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) {
|
||||||
@ -86,7 +87,7 @@ TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) {
|
|||||||
EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true));
|
EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true));
|
||||||
EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true));
|
EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true));
|
||||||
EXPECT_CALL(m,
|
EXPECT_CALL(m,
|
||||||
SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true));
|
SendRequest("http://foo.com",_,_,_,_)).Times(1).WillOnce(Return(true));
|
||||||
GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar",
|
GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar",
|
||||||
"1.0",
|
"1.0",
|
||||||
"AAA-BBB",
|
"AAA-BBB",
|
||||||
@ -99,14 +100,14 @@ TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) {
|
|||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
&m);
|
&m);
|
||||||
ASSERT_TRUE(uploader->Upload());
|
ASSERT_TRUE(uploader->Upload(NULL, NULL, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) {
|
TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) {
|
||||||
MockLibcurlWrapper m;
|
MockLibcurlWrapper m;
|
||||||
EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true));
|
EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true));
|
||||||
EXPECT_CALL(m, SendRequest(_,_,_)).Times(0);
|
EXPECT_CALL(m, SendRequest(_,_,_,_,_)).Times(0);
|
||||||
GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar",
|
GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar",
|
||||||
"1.0",
|
"1.0",
|
||||||
"AAA-BBB",
|
"AAA-BBB",
|
||||||
@ -119,7 +120,7 @@ TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) {
|
|||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
&m);
|
&m);
|
||||||
ASSERT_FALSE(uploader->Upload());
|
ASSERT_FALSE(uploader->Upload(NULL, NULL, NULL));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) {
|
TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) {
|
||||||
@ -135,7 +136,7 @@ TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) {
|
|||||||
"http://foo.com",
|
"http://foo.com",
|
||||||
"",
|
"",
|
||||||
"");
|
"");
|
||||||
ASSERT_FALSE(uploader.Upload());
|
ASSERT_FALSE(uploader.Upload(NULL, NULL, NULL));
|
||||||
|
|
||||||
// Test with empty product version.
|
// Test with empty product version.
|
||||||
GoogleCrashdumpUploader uploader1("product",
|
GoogleCrashdumpUploader uploader1("product",
|
||||||
@ -150,7 +151,7 @@ TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) {
|
|||||||
"",
|
"",
|
||||||
"");
|
"");
|
||||||
|
|
||||||
ASSERT_FALSE(uploader1.Upload());
|
ASSERT_FALSE(uploader1.Upload(NULL, NULL, NULL));
|
||||||
|
|
||||||
// Test with empty client GUID.
|
// Test with empty client GUID.
|
||||||
GoogleCrashdumpUploader uploader2("product",
|
GoogleCrashdumpUploader uploader2("product",
|
||||||
@ -164,6 +165,6 @@ TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) {
|
|||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"");
|
"");
|
||||||
ASSERT_FALSE(uploader2.Upload());
|
ASSERT_FALSE(uploader2.Upload(NULL, NULL, NULL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,17 @@ bool HTTPUpload::SendRequest(const string &url,
|
|||||||
if (!CheckParameters(parameters))
|
if (!CheckParameters(parameters))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
void *curl_lib = dlopen("libcurl.so", RTLD_NOW);
|
// We may have been linked statically; if curl_easy_init is in the
|
||||||
|
// current binary, no need to search for a dynamic version.
|
||||||
|
void* curl_lib = dlopen(NULL, RTLD_NOW);
|
||||||
|
if (!curl_lib || dlsym(curl_lib, "curl_easy_init") == NULL) {
|
||||||
|
dlerror(); // Clear dlerror before attempting to open libraries.
|
||||||
|
dlclose(curl_lib);
|
||||||
|
curl_lib = NULL;
|
||||||
|
}
|
||||||
|
if (!curl_lib) {
|
||||||
|
curl_lib = dlopen("libcurl.so", RTLD_NOW);
|
||||||
|
}
|
||||||
if (!curl_lib) {
|
if (!curl_lib) {
|
||||||
if (error_description != NULL)
|
if (error_description != NULL)
|
||||||
*error_description = dlerror();
|
*error_description = dlerror();
|
||||||
|
@ -57,6 +57,8 @@ LibcurlWrapper::LibcurlWrapper()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LibcurlWrapper::~LibcurlWrapper() {}
|
||||||
|
|
||||||
bool LibcurlWrapper::SetProxy(const string& proxy_host,
|
bool LibcurlWrapper::SetProxy(const string& proxy_host,
|
||||||
const string& proxy_userpwd) {
|
const string& proxy_userpwd) {
|
||||||
if (!init_ok_) {
|
if (!init_ok_) {
|
||||||
@ -108,7 +110,9 @@ static size_t WriteCallback(void *ptr, size_t size,
|
|||||||
|
|
||||||
bool LibcurlWrapper::SendRequest(const string& url,
|
bool LibcurlWrapper::SendRequest(const string& url,
|
||||||
const std::map<string, string>& parameters,
|
const std::map<string, string>& parameters,
|
||||||
string* server_response) {
|
int* http_status_code,
|
||||||
|
string* http_header_data,
|
||||||
|
string* http_response_data) {
|
||||||
(*easy_setopt_)(curl_, CURLOPT_URL, url.c_str());
|
(*easy_setopt_)(curl_, CURLOPT_URL, url.c_str());
|
||||||
std::map<string, string>::const_iterator iter = parameters.begin();
|
std::map<string, string>::const_iterator iter = parameters.begin();
|
||||||
for (; iter != parameters.end(); ++iter)
|
for (; iter != parameters.end(); ++iter)
|
||||||
@ -118,10 +122,17 @@ bool LibcurlWrapper::SendRequest(const string& url,
|
|||||||
CURLFORM_END);
|
CURLFORM_END);
|
||||||
|
|
||||||
(*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_);
|
(*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_);
|
||||||
if (server_response != NULL) {
|
if (http_response_data != NULL) {
|
||||||
|
http_response_data->clear();
|
||||||
(*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
|
(*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||||
(*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
|
(*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
|
||||||
reinterpret_cast<void *>(server_response));
|
reinterpret_cast<void *>(http_response_data));
|
||||||
|
}
|
||||||
|
if (http_header_data != NULL) {
|
||||||
|
http_header_data->clear();
|
||||||
|
(*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback);
|
||||||
|
(*easy_setopt_)(curl_, CURLOPT_HEADERDATA,
|
||||||
|
reinterpret_cast<void *>(http_header_data));
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLcode err_code = CURLE_OK;
|
CURLcode err_code = CURLE_OK;
|
||||||
@ -129,6 +140,10 @@ bool LibcurlWrapper::SendRequest(const string& url,
|
|||||||
easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
|
easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
|
||||||
(dlsym(curl_lib_, "curl_easy_strerror"));
|
(dlsym(curl_lib_, "curl_easy_strerror"));
|
||||||
|
|
||||||
|
if (http_status_code != NULL) {
|
||||||
|
(*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (err_code != CURLE_OK)
|
if (err_code != CURLE_OK)
|
||||||
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
|
fprintf(stderr, "Failed to send http request to %s, error: %s\n",
|
||||||
@ -209,6 +224,10 @@ bool LibcurlWrapper::SetFunctionPointers() {
|
|||||||
"curl_easy_cleanup",
|
"curl_easy_cleanup",
|
||||||
void(*)(CURL*));
|
void(*)(CURL*));
|
||||||
|
|
||||||
|
SET_AND_CHECK_FUNCTION_POINTER(easy_getinfo_,
|
||||||
|
"curl_easy_getinfo",
|
||||||
|
CURLcode(*)(CURL *, CURLINFO info, ...));
|
||||||
|
|
||||||
SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_,
|
SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_,
|
||||||
"curl_slist_free_all",
|
"curl_slist_free_all",
|
||||||
void(*)(curl_slist*));
|
void(*)(curl_slist*));
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user