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. | ||||
| # All rights reserved. | ||||
| # Copyright 2010 Google Inc. All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are | ||||
| @ -27,18 +26,41 @@ | ||||
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| 
 | ||||
| # We only use this file to ease the steps of generating projects after | ||||
| # syncing, if we use gclient. All dependencies are svn:externals instead. | ||||
| # If you're not using gclient, you need to run the gyp python script to | ||||
| # generate the projects. | ||||
| # This can be done by the following command (assuming current directory): | ||||
| #   src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp | ||||
| # This is used to mimic the svn:externals mechanism for gclient (both Git and | ||||
| # SVN) based checkouts of Breakpad. As such, its use is entirely optional. If | ||||
| # using a manually managed SVN checkout as opposed to a gclient managed checkout | ||||
| # you can still use the hooks mechanism for generating project files by calling | ||||
| # 'gclient runhooks' rather than 'gclient sync'. | ||||
| 
 | ||||
| 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 = [ | ||||
|   { | ||||
|     # 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": ".", | ||||
|     "action": ["python", | ||||
|                "src/src/tools/gyp/gyp", | ||||
|                "src/src/tools/gyp/gyp_main.py", | ||||
|                "--no-circular-check", | ||||
|                "src/src/client/windows/breakpad_client.gyp"], | ||||
|   }, | ||||
| ] | ||||
|  | ||||
| @ -1,19 +1,25 @@ | ||||
| Installation Instructions | ||||
| ************************* | ||||
| 
 | ||||
| Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, | ||||
| 2006 Free Software Foundation, Inc. | ||||
| Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, | ||||
| Inc. | ||||
| 
 | ||||
| This file is free documentation; the Free Software Foundation gives | ||||
| unlimited permission to copy, distribute and modify it. | ||||
|    Copying and distribution of this file, with or without modification, | ||||
| 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 | ||||
| ================== | ||||
| 
 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
|      `./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. | ||||
| 
 | ||||
|   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 | ||||
|      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 | ||||
|      files that `configure' created (so you can compile the package for | ||||
|      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 | ||||
|      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 | ||||
| ===================== | ||||
| 
 | ||||
| Some systems require unusual options for compilation or linking that the | ||||
| `configure' script does not know about.  Run `./configure --help' for | ||||
| details on some of the pertinent environment variables. | ||||
|    Some systems require unusual options for compilation or linking that | ||||
| the `configure' script does not know about.  Run `./configure --help' | ||||
| for details on some of the pertinent environment variables. | ||||
| 
 | ||||
|    You can give `configure' initial values for configuration parameters | ||||
| by setting variables in the command line or in the environment.  Here | ||||
| @ -85,25 +111,41 @@ is an example: | ||||
| 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 | ||||
| own directory.  To do this, you can use GNU `make'.  `cd' to the | ||||
| directory where you want the object files and executables to go and run | ||||
| the `configure' script.  `configure' automatically checks for the | ||||
| source code in the directory that `configure' is in and in `..'. | ||||
| 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 | ||||
| architecture at a time in the source code directory.  After you have | ||||
| installed the package for one architecture, use `make distclean' before | ||||
| reconfiguring for another architecture. | ||||
| 
 | ||||
|    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 | ||||
| ================== | ||||
| 
 | ||||
| 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 | ||||
| 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 | ||||
| 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 | ||||
| options like `--bindir=DIR' to specify different values for particular | ||||
| kinds of files.  Run `configure --help' for a list of the directories | ||||
| you can set and what kinds of files go in them. | ||||
| 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 | ||||
| with an extra prefix or suffix on their names by giving `configure' the | ||||
| option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. | ||||
| 
 | ||||
| Optional Features | ||||
| ================= | ||||
| 
 | ||||
| Some packages pay attention to `--enable-FEATURE' options to | ||||
|    Some packages pay attention to `--enable-FEATURE' options to | ||||
| `configure', where FEATURE indicates an optional part of the package. | ||||
| They may also pay attention to `--with-PACKAGE' options, where PACKAGE | ||||
| is something like `gnu-as' or `x' (for the X Window System).  The | ||||
| @ -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 | ||||
| `--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 | ||||
| ========================== | ||||
| 
 | ||||
| There may be some features `configure' cannot figure out automatically, | ||||
| but needs to determine by the type of machine the package will run on. | ||||
| Usually, assuming the package is built to be run on the _same_ | ||||
| architectures, `configure' can figure that out, but if it prints a | ||||
| message saying it cannot guess the machine type, give it the | ||||
|    There may be some features `configure' cannot figure out | ||||
| automatically, but needs to determine by the type of machine the package | ||||
| will run on.  Usually, assuming the package is built to be run on the | ||||
| _same_ architectures, `configure' can figure that out, but if it prints | ||||
| a message saying it cannot guess the machine type, give it the | ||||
| `--build=TYPE' option.  TYPE can either be a short name for the system | ||||
| type, such as `sun4', or a canonical name which has the form: | ||||
| 
 | ||||
| @ -150,7 +267,8 @@ type, such as `sun4', or a canonical name which has the form: | ||||
| 
 | ||||
| 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 | ||||
| `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 | ||||
| ================ | ||||
| 
 | ||||
| If you want to set default values for `configure' scripts to share, you | ||||
| can create a site shell script called `config.site' that gives default | ||||
| values for variables like `CC', `cache_file', and `prefix'. | ||||
|    If you want to set default values for `configure' scripts to share, | ||||
| you can create a site shell script called `config.site' that gives | ||||
| default values for variables like `CC', `cache_file', and `prefix'. | ||||
| `configure' looks for `PREFIX/share/config.site' if it exists, then | ||||
| `PREFIX/etc/config.site' if it exists.  Or, you can set the | ||||
| `CONFIG_SITE' environment variable to the location of the site script. | ||||
| @ -179,7 +297,7 @@ A warning: not all `configure' scripts look for a site script. | ||||
| Defining Variables | ||||
| ================== | ||||
| 
 | ||||
| Variables not defined in a site shell script can be set in the | ||||
|    Variables not defined in a site shell script can be set in the | ||||
| environment passed to `configure'.  However, some packages may run | ||||
| configure again during the build, and the customized values of these | ||||
| variables may be lost.  In order to avoid this problem, you should set | ||||
| @ -191,18 +309,27 @@ causes the specified `gcc' to be used as the C compiler (unless it is | ||||
| overridden in the site shell script). | ||||
| 
 | ||||
| Unfortunately, this technique does not work for `CONFIG_SHELL' due to | ||||
| an Autoconf bug.  Until the bug is fixed you can use this workaround: | ||||
| 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' recognizes the following options to control how it operates. | ||||
|    `configure' recognizes the following options to control how it | ||||
| operates. | ||||
| 
 | ||||
| `--help' | ||||
| `-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' | ||||
| `-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 | ||||
|      `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 --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 | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| 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
 | ||||
| ACLOCAL_AMFLAGS = -I m4 | ||||
| 
 | ||||
| # License file is called LICENSE not COPYING
 | ||||
| AUTOMAKE_OPTIONS = foreign | ||||
| 
 | ||||
| ## Documentation
 | ||||
| docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION) | ||||
| 
 | ||||
| dist_doc_DATA = \
 | ||||
| 	AUTHORS \
 | ||||
| 	COPYING \
 | ||||
| 	ChangeLog \
 | ||||
| 	INSTALL \
 | ||||
| 	LICENSE \
 | ||||
| 	NEWS \
 | ||||
| 	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
 | ||||
| noinst_LIBRARIES = | ||||
| @ -84,18 +117,24 @@ check_PROGRAMS = | ||||
| 
 | ||||
| if !DISABLE_PROCESSOR | ||||
| lib_LIBRARIES += src/libbreakpad.a | ||||
| pkgconfig_DATA += breakpad.pc | ||||
| noinst_LIBRARIES += src/third_party/libdisasm/libdisasm.a | ||||
| endif | ||||
| 
 | ||||
| if LINUX_HOST | ||||
| lib_LIBRARIES += src/client/linux/libbreakpad_client.a | ||||
| pkgconfig_DATA += breakpad-client.pc | ||||
| 
 | ||||
| src_client_linux_libbreakpad_client_a_SOURCES = \
 | ||||
| 	src/client/linux/crash_generation/crash_generation_client.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/minidump_descriptor.cc \
 | ||||
| 	src/client/linux/log/log.cc \
 | ||||
| 	src/client/linux/microdump_writer/microdump_writer.cc \
 | ||||
| 	src/client/linux/minidump_writer/linux_dumper.cc \
 | ||||
| 	src/client/linux/minidump_writer/linux_ptrace_dumper.cc \
 | ||||
| 	src/client/linux/minidump_writer/minidump_writer.cc \
 | ||||
| @ -124,11 +163,15 @@ src_libbreakpad_a_SOURCES = \ | ||||
| 	src/google_breakpad/processor/call_stack.h \
 | ||||
| 	src/google_breakpad/processor/code_module.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/fast_source_line_resolver.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_processor.h \
 | ||||
| 	src/google_breakpad/processor/process_result.h \
 | ||||
| 	src/google_breakpad/processor/process_state.h \
 | ||||
| 	src/google_breakpad/processor/source_line_resolver_base.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/disassembler_x86.h \
 | ||||
| 	src/processor/disassembler_x86.cc \
 | ||||
| 	src/processor/dump_context.cc \
 | ||||
| 	src/processor/dump_object.cc \
 | ||||
| 	src/processor/exploitability.cc \
 | ||||
| 	src/processor/exploitability_linux.h \
 | ||||
| 	src/processor/exploitability_linux.cc \
 | ||||
| 	src/processor/exploitability_win.h \
 | ||||
| 	src/processor/exploitability_win.cc \
 | ||||
| 	src/processor/fast_source_line_resolver_types.h \
 | ||||
| @ -164,6 +211,7 @@ src_libbreakpad_a_SOURCES = \ | ||||
| 	src/processor/logging.cc \
 | ||||
| 	src/processor/map_serializers-inl.h \
 | ||||
| 	src/processor/map_serializers.h \
 | ||||
|         src/processor/microdump_processor.cc \
 | ||||
| 	src/processor/minidump.cc \
 | ||||
| 	src/processor/minidump_processor.cc \
 | ||||
| 	src/processor/module_comparer.cc \
 | ||||
| @ -185,12 +233,19 @@ src_libbreakpad_a_SOURCES = \ | ||||
| 	src/processor/windows_frame_info.h \
 | ||||
| 	src/processor/source_line_resolver_base_types.h \
 | ||||
| 	src/processor/source_line_resolver_base.cc \
 | ||||
| 	src/processor/stack_frame_cpu.cc \
 | ||||
| 	src/processor/stack_frame_symbolizer.cc \
 | ||||
| 	src/processor/stackwalker.cc \
 | ||||
| 	src/processor/stackwalker_amd64.cc \
 | ||||
| 	src/processor/stackwalker_amd64.h \
 | ||||
| 	src/processor/stackwalker_arm.cc \
 | ||||
| 	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.h \
 | ||||
| 	src/processor/stackwalker_ppc64.cc \
 | ||||
| @ -276,6 +331,7 @@ check_PROGRAMS += \ | ||||
| 	src/processor/exploitability_unittest \
 | ||||
| 	src/processor/fast_source_line_resolver_unittest \
 | ||||
| 	src/processor/map_serializers_unittest \
 | ||||
|         src/processor/microdump_processor_unittest \
 | ||||
| 	src/processor/minidump_processor_unittest \
 | ||||
| 	src/processor/minidump_unittest \
 | ||||
| 	src/processor/static_address_map_unittest \
 | ||||
| @ -287,6 +343,9 @@ check_PROGRAMS += \ | ||||
| 	src/processor/range_map_unittest \
 | ||||
| 	src/processor/stackwalker_amd64_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/synth_minidump_unittest | ||||
| endif | ||||
| @ -322,18 +381,28 @@ endif | ||||
| TESTS = $(check_PROGRAMS) $(check_SCRIPTS) | ||||
| 
 | ||||
| if ANDROID_HOST | ||||
| # Wrapper script to run unit test programs on a connected Android device.
 | ||||
| TESTS_ENVIRONMENT = $(top_srcdir)/android/test-shell.sh | ||||
| # Since Autotools 1.2, tests are run through a special "test driver" script.
 | ||||
| # 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 | ||||
| TESTS_ENVIRONMENT = | ||||
| # The default Autotools test driver script.
 | ||||
| LOG_DRIVER = $(top_srcdir)/autotools/test-driver | ||||
| endif | ||||
| 
 | ||||
| if LINUX_HOST | ||||
| src_client_linux_linux_dumper_unittest_helper_SOURCES = \
 | ||||
| 	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_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/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/src/gmock-all.cc \
 | ||||
| 	src/processor/basic_code_modules.cc \
 | ||||
| 	src/processor/dump_context.cc \
 | ||||
| 	src/processor/dump_object.cc \
 | ||||
| 	src/processor/logging.cc \
 | ||||
| 	src/processor/minidump.cc \
 | ||||
| 	src/processor/pathname_stripper.cc | ||||
| @ -373,10 +444,14 @@ src_client_linux_linux_client_unittest_shlib_LDFLAGS = \ | ||||
| 	-shared \
 | ||||
| 	-Wl,-h,linux_client_unittest_shlib | ||||
| 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/minidump_descriptor.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_ptrace_dumper.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/dwarf2diehandler.cc \
 | ||||
| 	src/common/dwarf/dwarf2reader.cc \
 | ||||
| 	src/common/linux/crc32.cc \
 | ||||
| 	src/common/linux/dump_symbols.cc \
 | ||||
| 	src/common/linux/elf_symbols_to_module.cc \
 | ||||
| 	src/common/linux/elfutils.cc \
 | ||||
| @ -485,6 +561,7 @@ src_common_dumper_unittest_SOURCES = \ | ||||
| 	src/common/dwarf/dwarf2reader.cc \
 | ||||
| 	src/common/dwarf/dwarf2reader_cfi_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_unittest.cc \
 | ||||
| 	src/common/linux/elf_core_dump.cc \
 | ||||
| @ -611,19 +688,27 @@ src_processor_exploitability_unittest_LDADD = \ | ||||
| 	src/processor/process_state.o \
 | ||||
| 	src/processor/disassembler_x86.o \
 | ||||
| 	src/processor/exploitability.o \
 | ||||
| 	src/processor/exploitability_linux.o \
 | ||||
| 	src/processor/exploitability_win.o \
 | ||||
| 	src/processor/basic_code_modules.o \
 | ||||
| 	src/processor/basic_source_line_resolver.o \
 | ||||
| 	src/processor/call_stack.o \
 | ||||
| 	src/processor/cfi_frame_info.o \
 | ||||
| 	src/processor/dump_context.o \
 | ||||
| 	src/processor/dump_object.o \
 | ||||
| 	src/processor/logging.o \
 | ||||
| 	src/processor/minidump.o \
 | ||||
| 	src/processor/pathname_stripper.o \
 | ||||
| 	src/processor/simple_symbol_supplier.o \
 | ||||
| 	src/processor/source_line_resolver_base.o \
 | ||||
| 	src/processor/stack_frame_cpu.o \
 | ||||
| 	src/processor/stack_frame_symbolizer.o \
 | ||||
| 	src/processor/stackwalker.o \
 | ||||
| 	src/processor/stackwalker_amd64.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_ppc64.o \
 | ||||
| 	src/processor/stackwalker_sparc.o \
 | ||||
| @ -685,6 +770,19 @@ src_processor_map_serializers_unittest_LDADD = \ | ||||
| 	src/processor/pathname_stripper.o \
 | ||||
| 	$(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.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/cfi_frame_info.o \
 | ||||
| 	src/processor/disassembler_x86.o \
 | ||||
| 	src/processor/dump_context.o \
 | ||||
| 	src/processor/dump_object.o \
 | ||||
| 	src/processor/exploitability.o \
 | ||||
| 	src/processor/exploitability_linux.o \
 | ||||
| 	src/processor/exploitability_win.o \
 | ||||
| 	src/processor/logging.o \
 | ||||
| 	src/processor/minidump_processor.o \
 | ||||
| @ -709,10 +810,14 @@ src_processor_minidump_processor_unittest_LDADD = \ | ||||
| 	src/processor/pathname_stripper.o \
 | ||||
| 	src/processor/process_state.o \
 | ||||
| 	src/processor/source_line_resolver_base.o \
 | ||||
| 	src/processor/stack_frame_cpu.o \
 | ||||
| 	src/processor/stack_frame_symbolizer.o \
 | ||||
| 	src/processor/stackwalker.o \
 | ||||
| 	src/processor/stackwalker_amd64.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_ppc64.o \
 | ||||
| 	src/processor/stackwalker_sparc.o \
 | ||||
| @ -736,6 +841,8 @@ src_processor_minidump_unittest_CPPFLAGS = \ | ||||
| 	-I$(top_srcdir)/src/testing | ||||
| src_processor_minidump_unittest_LDADD = \
 | ||||
| 	src/processor/basic_code_modules.o \
 | ||||
| 	src/processor/dump_context.o \
 | ||||
| 	src/processor/dump_object.o \
 | ||||
| 	src/processor/logging.o \
 | ||||
| 	src/processor/minidump.o \
 | ||||
| 	src/processor/pathname_stripper.o \
 | ||||
| @ -829,15 +936,20 @@ src_processor_stackwalker_selftest_LDADD = \ | ||||
| 	src/processor/call_stack.o \
 | ||||
| 	src/processor/disassembler_x86.o \
 | ||||
| 	src/processor/exploitability.o \
 | ||||
| 	src/processor/exploitability_linux.o \
 | ||||
| 	src/processor/exploitability_win.o \
 | ||||
| 	src/processor/logging.o \
 | ||||
| 	src/processor/minidump.o \
 | ||||
| 	src/processor/pathname_stripper.o \
 | ||||
| 	src/processor/source_line_resolver_base.o \
 | ||||
| 	src/processor/stack_frame_cpu.o \
 | ||||
| 	src/processor/stack_frame_symbolizer.o \
 | ||||
| 	src/processor/stackwalker.o \
 | ||||
| 	src/processor/stackwalker_amd64.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_ppc64.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 | ||||
| 
 | ||||
| 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/common/test_assembler.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_LDADD = \
 | ||||
| 	src/processor/basic_code_modules.o \
 | ||||
| 	src/processor/dump_context.o \
 | ||||
| 	src/processor/dump_object.o \
 | ||||
| 	src/processor/logging.o \
 | ||||
| 	src/processor/minidump.o \
 | ||||
| 	src/processor/pathname_stripper.o | ||||
| @ -946,7 +1108,10 @@ src_processor_minidump_stackwalk_LDADD = \ | ||||
| 	src/processor/call_stack.o \
 | ||||
| 	src/processor/cfi_frame_info.o \
 | ||||
| 	src/processor/disassembler_x86.o \
 | ||||
| 	src/processor/dump_context.o \
 | ||||
| 	src/processor/dump_object.o \
 | ||||
| 	src/processor/exploitability.o \
 | ||||
| 	src/processor/exploitability_linux.o \
 | ||||
| 	src/processor/exploitability_win.o \
 | ||||
| 	src/processor/logging.o \
 | ||||
| 	src/processor/minidump.o \
 | ||||
| @ -955,10 +1120,14 @@ src_processor_minidump_stackwalk_LDADD = \ | ||||
| 	src/processor/process_state.o \
 | ||||
| 	src/processor/simple_symbol_supplier.o \
 | ||||
| 	src/processor/source_line_resolver_base.o \
 | ||||
| 	src/processor/stack_frame_cpu.o \
 | ||||
| 	src/processor/stack_frame_symbolizer.o \
 | ||||
| 	src/processor/stackwalker.o \
 | ||||
| 	src/processor/stackwalker_amd64.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_ppc64.o \
 | ||||
| 	src/processor/stackwalker_sparc.o \
 | ||||
| @ -1021,6 +1190,7 @@ EXTRA_DIST = \ | ||||
| 	src/client/windows/sender/crash_report_sender.vcproj \
 | ||||
| 	src/common/convert_UTF.c \
 | ||||
| 	src/common/convert_UTF.h \
 | ||||
| 	src/common/linux/crc32.cc \
 | ||||
| 	src/common/linux/dump_symbols.cc \
 | ||||
| 	src/common/linux/dump_symbols.h \
 | ||||
| 	src/common/linux/elf_symbols_to_module.cc \
 | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										777
									
								
								google-breakpad/aclocal.m4
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										777
									
								
								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: | ||||
							
								
								
									
										351
									
								
								google-breakpad/autotools/config.guess
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										351
									
								
								google-breakpad/autotools/config.guess
									
									
									
									
										vendored
									
									
								
							| @ -1,14 +1,12 @@ | ||||
| #! /bin/sh | ||||
| # Attempt to guess a canonical system name. | ||||
| #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, | ||||
| #   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, | ||||
| #   2011, 2012 Free Software Foundation, Inc. | ||||
| #   Copyright 1992-2014 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 | ||||
| # 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. | ||||
| # | ||||
| # 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 | ||||
| # distribute this file as part of a program that contains a | ||||
| # configuration script generated by Autoconf, you may include it under | ||||
| # the same distribution terms that you use for the rest of that program. | ||||
| 
 | ||||
| 
 | ||||
| # Originally written by Per Bothner.  Please send patches (context | ||||
| # diff format) to <config-patches@gnu.org> and include a ChangeLog | ||||
| # entry. | ||||
| # 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"). | ||||
| # | ||||
| # This script attempts to guess a canonical system name similar to | ||||
| # config.sub.  If it succeeds, it prints the system name on stdout, and | ||||
| # exits with 0.  Otherwise, it exits with 1. | ||||
| # Originally written by Per Bothner. | ||||
| # | ||||
| # 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 | ||||
| # | ||||
| # Please send patches with a ChangeLog entry to config-patches@gnu.org. | ||||
| 
 | ||||
| 
 | ||||
| me=`echo "$0" | sed -e 's,.*/,,'` | ||||
| 
 | ||||
| @ -54,9 +50,7 @@ version="\ | ||||
| GNU config.guess ($timestamp) | ||||
| 
 | ||||
| Originally written by Per Bothner. | ||||
| Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, | ||||
| 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 | ||||
| Free Software Foundation, Inc. | ||||
| Copyright 1992-2014 Free Software Foundation, Inc. | ||||
| 
 | ||||
| This is free software; see the source for copying conditions.  There is NO | ||||
| warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." | ||||
| @ -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_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. | ||||
| 
 | ||||
| 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]*:*) | ||||
| 	echo arm-acorn-riscix${UNAME_RELEASE} | ||||
| 	exit ;; | ||||
|     arm:riscos:*:*|arm:RISCOS:*:*) | ||||
|     arm*:riscos:*:*|arm*:RISCOS:*:*) | ||||
| 	echo arm-unknown-riscos | ||||
| 	exit ;; | ||||
|     SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) | ||||
| @ -805,10 +820,13 @@ EOF | ||||
|     i*:CYGWIN*:*) | ||||
| 	echo ${UNAME_MACHINE}-pc-cygwin | ||||
| 	exit ;; | ||||
|     *:MINGW64*:*) | ||||
| 	echo ${UNAME_MACHINE}-pc-mingw64 | ||||
| 	exit ;; | ||||
|     *:MINGW*:*) | ||||
| 	echo ${UNAME_MACHINE}-pc-mingw32 | ||||
| 	exit ;; | ||||
|     i*:MSYS*:*) | ||||
|     *:MSYS*:*) | ||||
| 	echo ${UNAME_MACHINE}-pc-msys | ||||
| 	exit ;; | ||||
|     i*:windows32*:*) | ||||
| @ -856,21 +874,21 @@ EOF | ||||
| 	exit ;; | ||||
|     *:GNU:*:*) | ||||
| 	# 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 ;; | ||||
|     *:GNU/*:*:*) | ||||
| 	# 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 ;; | ||||
|     i*86:Minix:*:*) | ||||
| 	echo ${UNAME_MACHINE}-pc-minix | ||||
| 	exit ;; | ||||
|     aarch64:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     aarch64_be:Linux:*:*) | ||||
| 	UNAME_MACHINE=aarch64_be | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     alpha:Linux:*:*) | ||||
| 	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in | ||||
| @ -883,59 +901,54 @@ EOF | ||||
| 	  EV68*) UNAME_MACHINE=alphaev68 ;; | ||||
| 	esac | ||||
| 	objdump --private-headers /bin/sh | grep -q ld.so.1 | ||||
| 	if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} | ||||
| 	if test "$?" = 0 ; then LIBC="gnulibc1" ; fi | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     arc:Linux:*:* | arceb:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     arm*:Linux:*:*) | ||||
| 	eval $set_cc_for_build | ||||
| 	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ | ||||
| 	    | grep -q __ARM_EABI__ | ||||
| 	then | ||||
| 	    echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	    echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	else | ||||
| 	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ | ||||
| 		| grep -q __ARM_PCS_VFP | ||||
| 	    then | ||||
| 		echo ${UNAME_MACHINE}-unknown-linux-gnueabi | ||||
| 		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi | ||||
| 	    else | ||||
| 		echo ${UNAME_MACHINE}-unknown-linux-gnueabihf | ||||
| 		echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf | ||||
| 	    fi | ||||
| 	fi | ||||
| 	exit ;; | ||||
|     avr32*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     cris:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-axis-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-axis-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     crisv32:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-axis-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-axis-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     frv:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     hexagon:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     i*86:Linux:*:*) | ||||
| 	LIBC=gnu | ||||
| 	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}" | ||||
| 	echo ${UNAME_MACHINE}-pc-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     ia64:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     m32r*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     m68*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     mips:Linux:*:* | mips64:Linux:*:*) | ||||
| 	eval $set_cc_for_build | ||||
| @ -954,54 +967,74 @@ EOF | ||||
| 	#endif | ||||
| EOF | ||||
| 	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:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
|     openrisc*:Linux:*:*) | ||||
| 	echo or1k-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     or32:Linux:*:* | or1k*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     padre:Linux:*:*) | ||||
| 	echo sparc-unknown-linux-gnu | ||||
| 	echo sparc-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     parisc64:Linux:*:* | hppa64:Linux:*:*) | ||||
| 	echo hppa64-unknown-linux-gnu | ||||
| 	echo hppa64-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     parisc:Linux:*:* | hppa:Linux:*:*) | ||||
| 	# Look for CPU level | ||||
| 	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in | ||||
| 	  PA7*) echo hppa1.1-unknown-linux-gnu ;; | ||||
| 	  PA8*) echo hppa2.0-unknown-linux-gnu ;; | ||||
| 	  *)    echo hppa-unknown-linux-gnu ;; | ||||
| 	  PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; | ||||
| 	  PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; | ||||
| 	  *)    echo hppa-unknown-linux-${LIBC} ;; | ||||
| 	esac | ||||
| 	exit ;; | ||||
|     ppc64:Linux:*:*) | ||||
| 	echo powerpc64-unknown-linux-gnu | ||||
| 	echo powerpc64-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     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 ;; | ||||
|     s390:Linux:*:* | s390x:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-ibm-linux | ||||
| 	echo ${UNAME_MACHINE}-ibm-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     sh64*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     sh*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     sparc:Linux:*:* | sparc64:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     tile*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     vax:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-dec-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-dec-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     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 ;; | ||||
|     xtensa*:Linux:*:*) | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-gnu | ||||
| 	echo ${UNAME_MACHINE}-unknown-linux-${LIBC} | ||||
| 	exit ;; | ||||
|     i*86:DYNIX/ptx:4*:*) | ||||
| 	# 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. | ||||
| 	echo i586-pc-haiku | ||||
| 	exit ;; | ||||
|     x86_64:Haiku:*:*) | ||||
| 	echo x86_64-unknown-haiku | ||||
| 	exit ;; | ||||
|     SX-4:SUPER-UX:*:*) | ||||
| 	echo sx4-nec-superux${UNAME_RELEASE} | ||||
| 	exit ;; | ||||
| @ -1231,19 +1267,31 @@ EOF | ||||
| 	exit ;; | ||||
|     *:Darwin:*:*) | ||||
| 	UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown | ||||
| 	case $UNAME_PROCESSOR in | ||||
| 	    i386) | ||||
| 		eval $set_cc_for_build | ||||
| 		if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then | ||||
| 		  if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ | ||||
| 		      (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ | ||||
| 		      grep IS_64BIT_ARCH >/dev/null | ||||
| 		  then | ||||
| 		      UNAME_PROCESSOR="x86_64" | ||||
| 		  fi | ||||
| 		fi ;; | ||||
| 	    unknown) UNAME_PROCESSOR=powerpc ;; | ||||
| 	esac | ||||
| 	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 (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ | ||||
| 		    (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ | ||||
| 		    grep IS_64BIT_ARCH >/dev/null | ||||
| 		then | ||||
| 		    case $UNAME_PROCESSOR in | ||||
| 			i386) UNAME_PROCESSOR=x86_64 ;; | ||||
| 			powerpc) UNAME_PROCESSOR=powerpc64 ;; | ||||
| 		    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} | ||||
| 	exit ;; | ||||
|     *:procnto*:*:* | *:QNX:[0123456789]*:*) | ||||
| @ -1334,157 +1382,6 @@ EOF | ||||
| 	exit ;; | ||||
| 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 | ||||
| $0: unable to guess system type | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										132
									
								
								google-breakpad/autotools/config.sub
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										132
									
								
								google-breakpad/autotools/config.sub
									
									
									
									
										vendored
									
									
								
							| @ -1,24 +1,18 @@ | ||||
| #! /bin/sh | ||||
| # Configuration validation subroutine script. | ||||
| #   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, | ||||
| #   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, | ||||
| #   2011, 2012 Free Software Foundation, Inc. | ||||
| #   Copyright 1992-2014 Free Software Foundation, Inc. | ||||
| 
 | ||||
| timestamp='2012-06-17' | ||||
| timestamp='2014-07-28' | ||||
| 
 | ||||
| # This file is (in principle) common to ALL GNU software. | ||||
| # The presence of a machine in this file suggests that SOME GNU software | ||||
| # can handle that machine.  It does not imply ALL GNU software can. | ||||
| # | ||||
| # 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 | ||||
| # 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 3 of the License, or | ||||
| # (at your option) any later version. | ||||
| # | ||||
| # This program is distributed in the hope that it will be useful, | ||||
| # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
| # GNU General Public License for more details. | ||||
| # 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/>. | ||||
| @ -26,11 +20,12 @@ timestamp='2012-06-17' | ||||
| # 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. | ||||
| # 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 | ||||
| # diff and a properly formatted GNU ChangeLog entry. | ||||
| # Please send patches with a ChangeLog entry to config-patches@gnu.org. | ||||
| # | ||||
| # Configuration subroutine to validate and canonicalize a configuration type. | ||||
| # Supply the specified configuration type as an argument. | ||||
| @ -73,9 +68,7 @@ Report bugs and patches to <config-patches@gnu.org>." | ||||
| version="\ | ||||
| GNU config.sub ($timestamp) | ||||
| 
 | ||||
| Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, | ||||
| 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 | ||||
| Free Software Foundation, Inc. | ||||
| Copyright 1992-2014 Free Software Foundation, Inc. | ||||
| 
 | ||||
| This is free software; see the source for copying conditions.  There is NO | ||||
| warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." | ||||
| @ -123,7 +116,7 @@ esac | ||||
| maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` | ||||
| case $maybe_os in | ||||
|   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* | \ | ||||
|   kopensolaris*-gnu* | \ | ||||
|   storm-chaos* | os2-emx* | rtmk-nova*) | ||||
| @ -156,7 +149,7 @@ case $os in | ||||
| 	-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ | ||||
| 	-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ | ||||
| 	-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ | ||||
| 	-apple | -axis | -knuth | -cray | -microblaze) | ||||
| 	-apple | -axis | -knuth | -cray | -microblaze*) | ||||
| 		os= | ||||
| 		basic_machine=$1 | ||||
| 		;; | ||||
| @ -259,21 +252,24 @@ case $basic_machine in | ||||
| 	| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | ||||
| 	| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | ||||
| 	| am33_2.0 \ | ||||
| 	| arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ | ||||
|         | be32 | be64 \ | ||||
| 	| arc | arceb \ | ||||
| 	| arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ | ||||
| 	| avr | avr32 \ | ||||
| 	| be32 | be64 \ | ||||
| 	| bfin \ | ||||
| 	| c4x | clipper \ | ||||
| 	| d10v | d30v | dlx | dsp16xx \ | ||||
| 	| c4x | c8051 | clipper \ | ||||
| 	| d10v | d30v | dlx | dsp16xx | dvp \ | ||||
| 	| epiphany \ | ||||
| 	| fido | fr30 | frv \ | ||||
| 	| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | ||||
| 	| hexagon \ | ||||
| 	| i370 | i860 | i960 | ia64 \ | ||||
| 	| ip2k | iq2000 \ | ||||
| 	| k1om \ | ||||
| 	| le32 | le64 \ | ||||
| 	| lm32 \ | ||||
| 	| m32c | m32r | m32rle | m68000 | m68k | m88k \ | ||||
| 	| maxq | mb | microblaze | mcore | mep | metag \ | ||||
| 	| maxq | mb | microblaze | microblazeel | mcore | mep | metag \ | ||||
| 	| mips | mipsbe | mipseb | mipsel | mipsle \ | ||||
| 	| mips16 \ | ||||
| 	| mips64 | mips64el \ | ||||
| @ -287,20 +283,22 @@ case $basic_machine in | ||||
| 	| mips64vr5900 | mips64vr5900el \ | ||||
| 	| mipsisa32 | mipsisa32el \ | ||||
| 	| mipsisa32r2 | mipsisa32r2el \ | ||||
| 	| mipsisa32r6 | mipsisa32r6el \ | ||||
| 	| mipsisa64 | mipsisa64el \ | ||||
| 	| mipsisa64r2 | mipsisa64r2el \ | ||||
| 	| mipsisa64r6 | mipsisa64r6el \ | ||||
| 	| mipsisa64sb1 | mipsisa64sb1el \ | ||||
| 	| mipsisa64sr71k | mipsisa64sr71kel \ | ||||
| 	| mipsr5900 | mipsr5900el \ | ||||
| 	| mipstx39 | mipstx39el \ | ||||
| 	| mn10200 | mn10300 \ | ||||
| 	| moxie \ | ||||
| 	| mt \ | ||||
| 	| msp430 \ | ||||
| 	| nds32 | nds32le | nds32be \ | ||||
| 	| nios | nios2 \ | ||||
| 	| nios | nios2 | nios2eb | nios2el \ | ||||
| 	| ns16k | ns32k \ | ||||
| 	| open8 \ | ||||
| 	| or32 \ | ||||
| 	| open8 | or1k | or1knd | or32 \ | ||||
| 	| pdp10 | pdp11 | pj | pjl \ | ||||
| 	| powerpc | powerpc64 | powerpc64le | powerpcle \ | ||||
| 	| pyramid \ | ||||
| @ -328,7 +326,7 @@ case $basic_machine in | ||||
| 	c6x) | ||||
| 		basic_machine=tic6x-unknown | ||||
| 		;; | ||||
| 	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | picochip) | ||||
| 	m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) | ||||
| 		basic_machine=$basic_machine-unknown | ||||
| 		os=-none | ||||
| 		;; | ||||
| @ -370,13 +368,13 @@ case $basic_machine in | ||||
| 	| aarch64-* | aarch64_be-* \ | ||||
| 	| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | ||||
| 	| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | ||||
| 	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ | ||||
| 	| alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ | ||||
| 	| arm-*  | armbe-* | armle-* | armeb-* | armv*-* \ | ||||
| 	| avr-* | avr32-* \ | ||||
| 	| be32-* | be64-* \ | ||||
| 	| bfin-* | bs2000-* \ | ||||
| 	| c[123]* | c30-* | [cjt]90-* | c4x-* \ | ||||
| 	| clipper-* | craynv-* | cydra-* \ | ||||
| 	| c8051-* | clipper-* | craynv-* | cydra-* \ | ||||
| 	| d10v-* | d30v-* | dlx-* \ | ||||
| 	| elxsi-* \ | ||||
| 	| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ | ||||
| @ -385,11 +383,13 @@ case $basic_machine in | ||||
| 	| hexagon-* \ | ||||
| 	| i*86-* | i860-* | i960-* | ia64-* \ | ||||
| 	| ip2k-* | iq2000-* \ | ||||
| 	| k1om-* \ | ||||
| 	| le32-* | le64-* \ | ||||
| 	| lm32-* \ | ||||
| 	| m32c-* | m32r-* | m32rle-* \ | ||||
| 	| 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-* \ | ||||
| 	| mips16-* \ | ||||
| 	| mips64-* | mips64el-* \ | ||||
| @ -403,18 +403,22 @@ case $basic_machine in | ||||
| 	| mips64vr5900-* | mips64vr5900el-* \ | ||||
| 	| mipsisa32-* | mipsisa32el-* \ | ||||
| 	| mipsisa32r2-* | mipsisa32r2el-* \ | ||||
| 	| mipsisa32r6-* | mipsisa32r6el-* \ | ||||
| 	| mipsisa64-* | mipsisa64el-* \ | ||||
| 	| mipsisa64r2-* | mipsisa64r2el-* \ | ||||
| 	| mipsisa64r6-* | mipsisa64r6el-* \ | ||||
| 	| mipsisa64sb1-* | mipsisa64sb1el-* \ | ||||
| 	| mipsisa64sr71k-* | mipsisa64sr71kel-* \ | ||||
| 	| mipsr5900-* | mipsr5900el-* \ | ||||
| 	| mipstx39-* | mipstx39el-* \ | ||||
| 	| mmix-* \ | ||||
| 	| mt-* \ | ||||
| 	| msp430-* \ | ||||
| 	| nds32-* | nds32le-* | nds32be-* \ | ||||
| 	| nios-* | nios2-* \ | ||||
| 	| nios-* | nios2-* | nios2eb-* | nios2el-* \ | ||||
| 	| none-* | np1-* | ns16k-* | ns32k-* \ | ||||
| 	| open8-* \ | ||||
| 	| or1k*-* \ | ||||
| 	| orion-* \ | ||||
| 	| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | ||||
| 	| powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ | ||||
| @ -788,11 +792,15 @@ case $basic_machine in | ||||
| 		basic_machine=ns32k-utek | ||||
| 		os=-sysv | ||||
| 		;; | ||||
| 	microblaze) | ||||
| 	microblaze*) | ||||
| 		basic_machine=microblaze-xilinx | ||||
| 		;; | ||||
| 	mingw64) | ||||
| 		basic_machine=x86_64-pc | ||||
| 		os=-mingw64 | ||||
| 		;; | ||||
| 	mingw32) | ||||
| 		basic_machine=i386-pc | ||||
| 		basic_machine=i686-pc | ||||
| 		os=-mingw32 | ||||
| 		;; | ||||
| 	mingw32ce) | ||||
| @ -806,6 +814,24 @@ case $basic_machine in | ||||
| 		basic_machine=m68k-atari | ||||
| 		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*-*) | ||||
| 		basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` | ||||
| 		;; | ||||
| @ -820,6 +846,10 @@ case $basic_machine in | ||||
| 		basic_machine=powerpc-unknown | ||||
| 		os=-morphos | ||||
| 		;; | ||||
| 	moxiebox) | ||||
| 		basic_machine=moxie-unknown | ||||
| 		os=-moxiebox | ||||
| 		;; | ||||
| 	msdos) | ||||
| 		basic_machine=i386-pc | ||||
| 		os=-msdos | ||||
| @ -828,7 +858,7 @@ case $basic_machine in | ||||
| 		basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` | ||||
| 		;; | ||||
| 	msys) | ||||
| 		basic_machine=i386-pc | ||||
| 		basic_machine=i686-pc | ||||
| 		os=-msys | ||||
| 		;; | ||||
| 	mvs) | ||||
| @ -1019,7 +1049,11 @@ case $basic_machine in | ||||
| 		basic_machine=i586-unknown | ||||
| 		os=-pw32 | ||||
| 		;; | ||||
| 	rdos) | ||||
| 	rdos | rdos64) | ||||
| 		basic_machine=x86_64-pc | ||||
| 		os=-rdos | ||||
| 		;; | ||||
| 	rdos32) | ||||
| 		basic_machine=i386-pc | ||||
| 		os=-rdos | ||||
| 		;; | ||||
| @ -1346,7 +1380,7 @@ case $os in | ||||
| 	-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | ||||
| 	      | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ | ||||
| 	      | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ | ||||
| 	      | -sym* | -kopensolaris* \ | ||||
| 	      | -sym* | -kopensolaris* | -plan9* \ | ||||
| 	      | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | ||||
| 	      | -aos* | -aros* \ | ||||
| 	      | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | ||||
| @ -1359,16 +1393,16 @@ case $os in | ||||
| 	      | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | ||||
| 	      | -chorusos* | -chorusrdb* | -cegcc* \ | ||||
| 	      | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | ||||
| 	      | -mingw32* | -linux-gnu* | -linux-android* \ | ||||
| 	      | -linux-newlib* | -linux-uclibc* \ | ||||
| 	      | -uxpv* | -beos* | -mpeix* | -udk* \ | ||||
| 	      | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ | ||||
| 	      | -linux-newlib* | -linux-musl* | -linux-uclibc* \ | ||||
| 	      | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ | ||||
| 	      | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | ||||
| 	      | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | ||||
| 	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | ||||
| 	      | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* | -irx* \ | ||||
| 	      | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | ||||
| 	      | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | ||||
| 	      | -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. | ||||
| 		;; | ||||
| 	-qnx*) | ||||
| @ -1492,9 +1526,6 @@ case $os in | ||||
| 	-aros*) | ||||
| 		os=-aros | ||||
| 		;; | ||||
| 	-kaos*) | ||||
| 		os=-kaos | ||||
| 		;; | ||||
| 	-zvmoe) | ||||
| 		os=-zvmoe | ||||
| 		;; | ||||
| @ -1543,6 +1574,9 @@ case $basic_machine in | ||||
| 	c4x-* | tic4x-*) | ||||
| 		os=-coff | ||||
| 		;; | ||||
| 	c8051-*) | ||||
| 		os=-elf | ||||
| 		;; | ||||
| 	hexagon-*) | ||||
| 		os=-elf | ||||
| 		;; | ||||
|  | ||||
| @ -1,10 +1,9 @@ | ||||
| #! /bin/sh | ||||
| # 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 | ||||
| # Foundation, Inc. | ||||
| # Copyright (C) 1999-2013 Free Software Foundation, Inc. | ||||
| 
 | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # it under the terms of the GNU General Public License as published by | ||||
| @ -17,9 +16,7 @@ scriptversion=2006-10-15.18 | ||||
| # GNU General Public License for more details. | ||||
| 
 | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
| # 02110-1301, USA. | ||||
| # 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 | ||||
| @ -30,9 +27,9 @@ scriptversion=2006-10-15.18 | ||||
| 
 | ||||
| case $1 in | ||||
|   '') | ||||
|      echo "$0: No command.  Try \`$0 --help' for more information." 1>&2 | ||||
|      exit 1; | ||||
|      ;; | ||||
|     echo "$0: No command.  Try '$0 --help' for more information." 1>&2 | ||||
|     exit 1; | ||||
|     ;; | ||||
|   -h | --h*) | ||||
|     cat <<\EOF | ||||
| Usage: depcomp [--help] [--version] PROGRAM [ARGS] | ||||
| @ -42,11 +39,11 @@ as side-effects. | ||||
| 
 | ||||
| Environment variables: | ||||
|   depmode     Dependency tracking mode. | ||||
|   source      Source file read by `PROGRAMS ARGS'. | ||||
|   object      Object file output by `PROGRAMS ARGS'. | ||||
|   source      Source file read by 'PROGRAMS ARGS'. | ||||
|   object      Object file output by 'PROGRAMS ARGS'. | ||||
|   DEPDIR      directory where to store dependencies. | ||||
|   depfile     Dependency file to output. | ||||
|   tmpdepfile  Temporary file to use when outputing dependencies. | ||||
|   tmpdepfile  Temporary file to use when outputting dependencies. | ||||
|   libtool     Whether libtool is used (yes/no). | ||||
| 
 | ||||
| Report bugs to <bug-automake@gnu.org>. | ||||
| @ -59,6 +56,66 @@ EOF | ||||
|     ;; | ||||
| 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 | ||||
|   echo "depcomp: Variables source, object and depmode must be set" 1>&2 | ||||
|   exit 1 | ||||
| @ -71,6 +128,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} | ||||
| 
 | ||||
| rm -f "$tmpdepfile" | ||||
| 
 | ||||
| # Avoid interferences from the environment. | ||||
| gccflag= dashmflag= | ||||
| 
 | ||||
| # Some modes work just like other modes, but use different flags.  We | ||||
| # parameterize here, but still list the modes in the big case below, | ||||
| # to make depend.m4 easier to write.  Note that we *cannot* use a case | ||||
| @ -82,9 +142,32 @@ if test "$depmode" = hp; then | ||||
| fi | ||||
| 
 | ||||
| if test "$depmode" = dashXmstdout; then | ||||
|    # This is just like dashmstdout with a different argument. | ||||
|    dashmflag=-xM | ||||
|    depmode=dashmstdout | ||||
|   # This is just like dashmstdout with a different argument. | ||||
|   dashmflag=-xM | ||||
|   depmode=dashmstdout | ||||
| 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 | ||||
| @ -107,8 +190,7 @@ gcc3) | ||||
|   done | ||||
|   "$@" | ||||
|   stat=$? | ||||
|   if test $stat -eq 0; then : | ||||
|   else | ||||
|   if test $stat -ne 0; then | ||||
|     rm -f "$tmpdepfile" | ||||
|     exit $stat | ||||
|   fi | ||||
| @ -116,13 +198,17 @@ gcc3) | ||||
|   ;; | ||||
| 
 | ||||
| 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 | ||||
| ## why we pick this rather obscure method: | ||||
| ## - Don't want to use -MD because we'd like the dependencies to end | ||||
| ##   up in a subdir.  Having to rename by hand is ugly. | ||||
| ##   (We might end up doing this anyway to support other compilers.) | ||||
| ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like | ||||
| ##   -MM, not -M (despite what the docs say). | ||||
| ##   -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 | ||||
| ##   than renaming). | ||||
|   if test -z "$gccflag"; then | ||||
| @ -130,31 +216,31 @@ gcc) | ||||
|   fi | ||||
|   "$@" -Wp,"$gccflag$tmpdepfile" | ||||
|   stat=$? | ||||
|   if test $stat -eq 0; then : | ||||
|   else | ||||
|   if test $stat -ne 0; then | ||||
|     rm -f "$tmpdepfile" | ||||
|     exit $stat | ||||
|   fi | ||||
|   rm -f "$depfile" | ||||
|   echo "$object : \\" > "$depfile" | ||||
|   alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz | ||||
| ## The second -e expression handles DOS-style file names with drive letters. | ||||
|   # The second -e expression handles DOS-style file names with drive | ||||
|   # letters. | ||||
|   sed -e 's/^[^:]*: / /' \ | ||||
|       -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" | ||||
| ## This next piece of magic avoids the `deleted header file' problem. | ||||
| ## This next piece of magic avoids the "deleted header file" problem. | ||||
| ## The problem is that when a header file which appears in a .P file | ||||
| ## is deleted, the dependency causes make to die (because there is | ||||
| ## typically no way to rebuild the header).  We avoid this by adding | ||||
| ## dummy dependencies for each header file.  Too bad gcc doesn't do | ||||
| ## this for us directly. | ||||
|   tr ' ' ' | ||||
| ' < "$tmpdepfile" | | ||||
| ## Some versions of gcc put a space before the `:'.  On the theory | ||||
| ## Some versions of gcc put a space before the ':'.  On the theory | ||||
| ## that the space means something, we add a space to the output as | ||||
| ## well. | ||||
| ## 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 | ||||
| ## 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" | ||||
|   ;; | ||||
| 
 | ||||
| @ -172,8 +258,7 @@ sgi) | ||||
|     "$@" -MDupdate "$tmpdepfile" | ||||
|   fi | ||||
|   stat=$? | ||||
|   if test $stat -eq 0; then : | ||||
|   else | ||||
|   if test $stat -ne 0; then | ||||
|     rm -f "$tmpdepfile" | ||||
|     exit $stat | ||||
|   fi | ||||
| @ -181,99 +266,156 @@ sgi) | ||||
| 
 | ||||
|   if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files | ||||
|     echo "$object : \\" > "$depfile" | ||||
| 
 | ||||
|     # Clip off the initial element (the dependent).  Don't try to be | ||||
|     # clever and replace this with sed code, as IRIX sed won't handle | ||||
|     # lines with more than a fixed number of characters (4096 in | ||||
|     # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines; | ||||
|     # the IRIX cc adds comments like `#:fec' to the end of the | ||||
|     # the IRIX cc adds comments like '#:fec' to the end of the | ||||
|     # dependency line. | ||||
|     tr ' ' ' | ||||
| ' < "$tmpdepfile" \ | ||||
|     | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ | ||||
|     tr ' | ||||
| ' ' ' >> $depfile | ||||
|     echo >> $depfile | ||||
| 
 | ||||
|     tr ' ' "$nl" < "$tmpdepfile" \ | ||||
|       | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | ||||
|       | tr "$nl" ' ' >> "$depfile" | ||||
|     echo >> "$depfile" | ||||
|     # The second pass generates a dummy entry for each header file. | ||||
|     tr ' ' ' | ||||
| ' < "$tmpdepfile" \ | ||||
|    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ | ||||
|    >> $depfile | ||||
|     tr ' ' "$nl" < "$tmpdepfile" \ | ||||
|       | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ | ||||
|       >> "$depfile" | ||||
|   else | ||||
|     # The sourcefile does not contain any dependencies, so just | ||||
|     # store a dummy comment line, to avoid errors with the Makefile | ||||
|     # "include basename.Plo" scheme. | ||||
|     echo "#dummy" > "$depfile" | ||||
|     make_dummy_depfile | ||||
|   fi | ||||
|   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) | ||||
|   # The C for AIX Compiler uses -M and outputs the dependencies | ||||
|   # in a .u file.  In older versions, this file always lives in the | ||||
|   # current directory.  Also, the AIX compiler puts `$object:' at the | ||||
|   # current directory.  Also, the AIX compiler puts '$object:' at the | ||||
|   # start of each line; $object doesn't have directory information. | ||||
|   # Version 6 uses the directory in both cases. | ||||
|   stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` | ||||
|   tmpdepfile="$stripped.u" | ||||
|   set_dir_from "$object" | ||||
|   set_base_from "$object" | ||||
|   if test "$libtool" = yes; then | ||||
|     tmpdepfile1=$dir$base.u | ||||
|     tmpdepfile2=$base.u | ||||
|     tmpdepfile3=$dir.libs/$base.u | ||||
|     "$@" -Wc,-M | ||||
|   else | ||||
|     tmpdepfile1=$dir$base.u | ||||
|     tmpdepfile2=$dir$base.u | ||||
|     tmpdepfile3=$dir$base.u | ||||
|     "$@" -M | ||||
|   fi | ||||
|   stat=$? | ||||
| 
 | ||||
|   if test -f "$tmpdepfile"; then : | ||||
|   else | ||||
|     stripped=`echo "$stripped" | sed 's,^.*/,,'` | ||||
|     tmpdepfile="$stripped.u" | ||||
|   fi | ||||
| 
 | ||||
|   if test $stat -eq 0; then : | ||||
|   else | ||||
|     rm -f "$tmpdepfile" | ||||
|   if test $stat -ne 0; then | ||||
|     rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" | ||||
|     exit $stat | ||||
|   fi | ||||
| 
 | ||||
|   if test -f "$tmpdepfile"; then | ||||
|     outname="$stripped.o" | ||||
|     # Each line is of the form `foo.o: dependent.h'. | ||||
|     # Do two passes, one to just change these to | ||||
|     # `$object: dependent.h' and one to simply `dependent.h:'. | ||||
|     sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" | ||||
|     sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" | ||||
|   else | ||||
|     # The sourcefile does not contain any dependencies, so just | ||||
|     # store a dummy comment line, to avoid errors with the Makefile | ||||
|     # "include basename.Plo" scheme. | ||||
|     echo "#dummy" > "$depfile" | ||||
|   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" | ||||
|   do | ||||
|     test -f "$tmpdepfile" && break | ||||
|   done | ||||
|   aix_post_process_depfile | ||||
|   ;; | ||||
| 
 | ||||
| tcc) | ||||
|   # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 | ||||
|   # FIXME: That version still under development at the moment of writing. | ||||
|   #        Make that this statement remains true also for stable, released | ||||
|   #        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 | ||||
|   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" | ||||
|   ;; | ||||
| 
 | ||||
| icc) | ||||
|   # Intel's C compiler understands `-MD -MF file'.  However on | ||||
|   #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c | ||||
|   # ICC 7.0 will fill foo.d with something like | ||||
|   #    foo.o: sub/foo.c | ||||
|   #    foo.o: sub/foo.h | ||||
|   # which is wrong.  We want: | ||||
|   #    sub/foo.o: sub/foo.c | ||||
|   #    sub/foo.o: sub/foo.h | ||||
|   #    sub/foo.c: | ||||
|   #    sub/foo.h: | ||||
|   # ICC 7.1 will output | ||||
| ## The order of this option in the case statement is important, since the | ||||
| ## shell code in configure will try each of these formats in the order | ||||
| ## listed in this file.  A plain '-MD' option would be understood by many | ||||
| ## compilers, so we must ensure this comes after the gcc and icc options. | ||||
| pgcc) | ||||
|   # Portland's C compiler understands '-MD'. | ||||
|   # Will always output deps to 'file.d' where file is the root name of the | ||||
|   # source file under compilation, even if file resides in a subdirectory. | ||||
|   # The object file name does not affect the name of the '.d' file. | ||||
|   # pgcc 10.2 will output | ||||
|   #    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 ... \ | ||||
|   #     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" | ||||
|   stat=$? | ||||
|   if test $stat -eq 0; then : | ||||
|   else | ||||
|   # 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=$? | ||||
|       # Release the lock. | ||||
|       rmdir "$lockdir" | ||||
|       break | ||||
|     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" | ||||
|     exit $stat | ||||
|   fi | ||||
| @ -285,8 +427,8 @@ icc) | ||||
|   sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" | ||||
|   # Some versions of the HPUX 10.20 sed can't process this invocation | ||||
|   # correctly.  Breaking it into two sed invocations is a workaround. | ||||
|   sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | | ||||
|     sed -e 's/$/ :/' >> "$depfile" | ||||
|   sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | ||||
|     | sed -e 's/$/ :/' >> "$depfile" | ||||
|   rm -f "$tmpdepfile" | ||||
|   ;; | ||||
| 
 | ||||
| @ -297,9 +439,8 @@ hp2) | ||||
|   # 'foo.d', which lands next to the object file, wherever that | ||||
|   # happens to be. | ||||
|   # Much of this is similar to the tru64 case; see comments there. | ||||
|   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` | ||||
|   test "x$dir" = "x$object" && dir= | ||||
|   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` | ||||
|   set_dir_from  "$object" | ||||
|   set_base_from "$object" | ||||
|   if test "$libtool" = yes; then | ||||
|     tmpdepfile1=$dir$base.d | ||||
|     tmpdepfile2=$dir.libs/$base.d | ||||
| @ -310,8 +451,7 @@ hp2) | ||||
|     "$@" +Maked | ||||
|   fi | ||||
|   stat=$? | ||||
|   if test $stat -eq 0; then : | ||||
|   else | ||||
|   if test $stat -ne 0; then | ||||
|      rm -f "$tmpdepfile1" "$tmpdepfile2" | ||||
|      exit $stat | ||||
|   fi | ||||
| @ -321,72 +461,107 @@ hp2) | ||||
|     test -f "$tmpdepfile" && break | ||||
|   done | ||||
|   if test -f "$tmpdepfile"; then | ||||
|     sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" | ||||
|     # Add `dependent.h:' lines. | ||||
|     sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile" | ||||
|     sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" | ||||
|     # Add 'dependent.h:' lines. | ||||
|     sed -ne '2,${ | ||||
|                s/^ *// | ||||
|                s/ \\*$// | ||||
|                s/$/:/ | ||||
|                p | ||||
|              }' "$tmpdepfile" >> "$depfile" | ||||
|   else | ||||
|     echo "#dummy" > "$depfile" | ||||
|     make_dummy_depfile | ||||
|   fi | ||||
|   rm -f "$tmpdepfile" "$tmpdepfile2" | ||||
|   ;; | ||||
| 
 | ||||
| tru64) | ||||
|    # The Tru64 compiler uses -MD to generate dependencies as a side | ||||
|    # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. | ||||
|    # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put | ||||
|    # dependencies in `foo.d' instead, so we check for that too. | ||||
|    # Subdirectories are respected. | ||||
|    dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` | ||||
|    test "x$dir" = "x$object" && dir= | ||||
|    base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` | ||||
|   # The Tru64 compiler uses -MD to generate dependencies as a side | ||||
|   # effect.  'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. | ||||
|   # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put | ||||
|   # dependencies in 'foo.d' instead, so we check for that too. | ||||
|   # Subdirectories are respected. | ||||
|   set_dir_from  "$object" | ||||
|   set_base_from "$object" | ||||
| 
 | ||||
|    if test "$libtool" = yes; then | ||||
|       # With Tru64 cc, shared objects can also be used to make a | ||||
|       # static library.  This mechanism is used in libtool 1.4 series to | ||||
|       # handle both shared and static libraries in a single compilation. | ||||
|       # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. | ||||
|       # | ||||
|       # With libtool 1.5 this exception was removed, and libtool now | ||||
|       # generates 2 separate objects for the 2 libraries.  These two | ||||
|       # compilations output dependencies in $dir.libs/$base.o.d and | ||||
|       # in $dir$base.o.d.  We have to check for both files, because | ||||
|       # one of the two compilations can be disabled.  We should prefer | ||||
|       # $dir$base.o.d over $dir.libs/$base.o.d because the latter is | ||||
|       # automatically cleaned when .libs/ is deleted, while ignoring | ||||
|       # the former would cause a distcleancheck panic. | ||||
|       tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4 | ||||
|       tmpdepfile2=$dir$base.o.d          # libtool 1.5 | ||||
|       tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5 | ||||
|       tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504 | ||||
|       "$@" -Wc,-MD | ||||
|    else | ||||
|       tmpdepfile1=$dir$base.o.d | ||||
|       tmpdepfile2=$dir$base.d | ||||
|       tmpdepfile3=$dir$base.d | ||||
|       tmpdepfile4=$dir$base.d | ||||
|       "$@" -MD | ||||
|    fi | ||||
|   if test "$libtool" = yes; then | ||||
|     # Libtool generates 2 separate objects for the 2 libraries.  These | ||||
|     # two compilations output dependencies in $dir.libs/$base.o.d and | ||||
|     # in $dir$base.o.d.  We have to check for both files, because | ||||
|     # one of the two compilations can be disabled.  We should prefer | ||||
|     # $dir$base.o.d over $dir.libs/$base.o.d because the latter is | ||||
|     # automatically cleaned when .libs/ is deleted, while ignoring | ||||
|     # the former would cause a distcleancheck panic. | ||||
|     tmpdepfile1=$dir$base.o.d          # libtool 1.5 | ||||
|     tmpdepfile2=$dir.libs/$base.o.d    # Likewise. | ||||
|     tmpdepfile3=$dir.libs/$base.d      # Compaq CCC V6.2-504 | ||||
|     "$@" -Wc,-MD | ||||
|   else | ||||
|     tmpdepfile1=$dir$base.d | ||||
|     tmpdepfile2=$dir$base.d | ||||
|     tmpdepfile3=$dir$base.d | ||||
|     "$@" -MD | ||||
|   fi | ||||
| 
 | ||||
|    stat=$? | ||||
|    if test $stat -eq 0; then : | ||||
|    else | ||||
|       rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" | ||||
|       exit $stat | ||||
|    fi | ||||
|   stat=$? | ||||
|   if test $stat -ne 0; then | ||||
|     rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" | ||||
|     exit $stat | ||||
|   fi | ||||
| 
 | ||||
|    for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" | ||||
|    do | ||||
|      test -f "$tmpdepfile" && break | ||||
|    done | ||||
|    if test -f "$tmpdepfile"; then | ||||
|       sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" | ||||
|       # That's a tab and a space in the []. | ||||
|       sed -e 's,^.*\.[a-z]*:[	 ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" | ||||
|    else | ||||
|       echo "#dummy" > "$depfile" | ||||
|    fi | ||||
|    rm -f "$tmpdepfile" | ||||
|    ;; | ||||
|   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" | ||||
|   do | ||||
|     test -f "$tmpdepfile" && break | ||||
|   done | ||||
|   # Same post-processing that is required for AIX mode. | ||||
|   aix_post_process_depfile | ||||
|   ;; | ||||
| 
 | ||||
| msvc7) | ||||
|   if test "$libtool" = yes; then | ||||
|     showIncludes=-Wc,-showIncludes | ||||
|   else | ||||
|     showIncludes=-showIncludes | ||||
|   fi | ||||
|   "$@" $showIncludes > "$tmpdepfile" | ||||
|   stat=$? | ||||
|   grep -v '^Note: including file: ' "$tmpdepfile" | ||||
|   if test $stat -ne 0; then | ||||
|     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) | ||||
|   # This comment above is used by automake to tell side-effect | ||||
| @ -399,13 +574,13 @@ dashmstdout) | ||||
| 
 | ||||
|   # Remove the call to Libtool. | ||||
|   if test "$libtool" = yes; then | ||||
|     while test $1 != '--mode=compile'; do | ||||
|     while test "X$1" != 'X--mode=compile'; do | ||||
|       shift | ||||
|     done | ||||
|     shift | ||||
|   fi | ||||
| 
 | ||||
|   # Remove `-o $object'. | ||||
|   # Remove '-o $object'. | ||||
|   IFS=" " | ||||
|   for arg | ||||
|   do | ||||
| @ -425,18 +600,18 @@ dashmstdout) | ||||
|   done | ||||
| 
 | ||||
|   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: | ||||
|   # 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 | | ||||
|     sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile" | ||||
|     sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" | ||||
|   rm -f "$depfile" | ||||
|   cat < "$tmpdepfile" > "$depfile" | ||||
|   tr ' ' ' | ||||
| ' < "$tmpdepfile" | \ | ||||
| ## Some versions of the HPUX 10.20 sed can't process this invocation | ||||
| ## correctly.  Breaking it into two sed invocations is a workaround. | ||||
|     sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" | ||||
|   # Some versions of the HPUX 10.20 sed can't process this sed invocation | ||||
|   # correctly.  Breaking it into two sed invocations is a workaround. | ||||
|   tr ' ' "$nl" < "$tmpdepfile" \ | ||||
|     | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | ||||
|     | sed -e 's/$/ :/' >> "$depfile" | ||||
|   rm -f "$tmpdepfile" | ||||
|   ;; | ||||
| 
 | ||||
| @ -450,41 +625,51 @@ makedepend) | ||||
|   "$@" || exit $? | ||||
|   # Remove any Libtool call | ||||
|   if test "$libtool" = yes; then | ||||
|     while test $1 != '--mode=compile'; do | ||||
|     while test "X$1" != 'X--mode=compile'; do | ||||
|       shift | ||||
|     done | ||||
|     shift | ||||
|   fi | ||||
|   # X makedepend | ||||
|   shift | ||||
|   cleared=no | ||||
|   for arg in "$@"; do | ||||
|   cleared=no eat=no | ||||
|   for arg | ||||
|   do | ||||
|     case $cleared in | ||||
|     no) | ||||
|       set ""; shift | ||||
|       cleared=yes ;; | ||||
|     esac | ||||
|     if test $eat = yes; then | ||||
|       eat=no | ||||
|       continue | ||||
|     fi | ||||
|     case "$arg" in | ||||
|     -D*|-I*) | ||||
|       set fnord "$@" "$arg"; shift ;; | ||||
|     # Strip any option that makedepend may not understand.  Remove | ||||
|     # the object too, otherwise makedepend will parse it as a source file. | ||||
|     -arch) | ||||
|       eat=yes ;; | ||||
|     -*|$object) | ||||
|       ;; | ||||
|     *) | ||||
|       set fnord "$@" "$arg"; shift ;; | ||||
|     esac | ||||
|   done | ||||
|   obj_suffix="`echo $object | sed 's/^.*\././'`" | ||||
|   obj_suffix=`echo "$object" | sed 's/^.*\././'` | ||||
|   touch "$tmpdepfile" | ||||
|   ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" | ||||
|   rm -f "$depfile" | ||||
|   cat < "$tmpdepfile" > "$depfile" | ||||
|   sed '1,2d' "$tmpdepfile" | tr ' ' ' | ||||
| ' | \ | ||||
| ## Some versions of the HPUX 10.20 sed can't process this invocation | ||||
| ## correctly.  Breaking it into two sed invocations is a workaround. | ||||
|     sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" | ||||
|   # makedepend may prepend the VPATH from the source file name to the object. | ||||
|   # 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 the last invocation | ||||
|   # correctly.  Breaking it into two sed invocations is a workaround. | ||||
|   sed '1,2d' "$tmpdepfile" \ | ||||
|     | tr ' ' "$nl" \ | ||||
|     | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | ||||
|     | sed -e 's/$/ :/' >> "$depfile" | ||||
|   rm -f "$tmpdepfile" "$tmpdepfile".bak | ||||
|   ;; | ||||
| 
 | ||||
| @ -495,13 +680,13 @@ cpp) | ||||
| 
 | ||||
|   # Remove the call to Libtool. | ||||
|   if test "$libtool" = yes; then | ||||
|     while test $1 != '--mode=compile'; do | ||||
|     while test "X$1" != 'X--mode=compile'; do | ||||
|       shift | ||||
|     done | ||||
|     shift | ||||
|   fi | ||||
| 
 | ||||
|   # Remove `-o $object'. | ||||
|   # Remove '-o $object'. | ||||
|   IFS=" " | ||||
|   for arg | ||||
|   do | ||||
| @ -520,10 +705,10 @@ cpp) | ||||
|     esac | ||||
|   done | ||||
| 
 | ||||
|   "$@" -E | | ||||
|     sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | ||||
|        -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | | ||||
|     sed '$ s: \\$::' > "$tmpdepfile" | ||||
|   "$@" -E \ | ||||
|     | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | ||||
|              -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | ||||
|     | sed '$ s: \\$::' > "$tmpdepfile" | ||||
|   rm -f "$depfile" | ||||
|   echo "$object : \\" > "$depfile" | ||||
|   cat < "$tmpdepfile" >> "$depfile" | ||||
| @ -533,35 +718,56 @@ cpp) | ||||
| 
 | ||||
| msvisualcpp) | ||||
|   # Important note: in order to support this mode, a compiler *must* | ||||
|   # always write the preprocessed file to stdout, regardless of -o, | ||||
|   # because we must use -o when running libtool. | ||||
|   # always write the preprocessed file to stdout. | ||||
|   "$@" || exit $? | ||||
| 
 | ||||
|   # Remove the call to Libtool. | ||||
|   if test "$libtool" = yes; then | ||||
|     while test "X$1" != 'X--mode=compile'; do | ||||
|       shift | ||||
|     done | ||||
|     shift | ||||
|   fi | ||||
| 
 | ||||
|   IFS=" " | ||||
|   for arg | ||||
|   do | ||||
|     case "$arg" in | ||||
|     -o) | ||||
|       shift | ||||
|       ;; | ||||
|     $object) | ||||
|       shift | ||||
|       ;; | ||||
|     "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") | ||||
| 	set fnord "$@" | ||||
| 	shift | ||||
| 	shift | ||||
| 	;; | ||||
|         set fnord "$@" | ||||
|         shift | ||||
|         shift | ||||
|         ;; | ||||
|     *) | ||||
| 	set fnord "$@" "$arg" | ||||
| 	shift | ||||
| 	shift | ||||
| 	;; | ||||
|         set fnord "$@" "$arg" | ||||
|         shift | ||||
|         shift | ||||
|         ;; | ||||
|     esac | ||||
|   done | ||||
|   "$@" -E | | ||||
|   sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" | ||||
|   "$@" -E 2>/dev/null | | ||||
|   sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" | ||||
|   rm -f "$depfile" | ||||
|   echo "$object : \\" > "$depfile" | ||||
|   . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::	\1 \\:p' >> "$depfile" | ||||
|   echo "	" >> "$depfile" | ||||
|   . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" | ||||
|   sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" | ||||
|   echo "$tab" >> "$depfile" | ||||
|   sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" | ||||
|   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) | ||||
|   exec "$@" | ||||
|   ;; | ||||
| @ -580,5 +786,6 @@ exit 0 | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "scriptversion=" | ||||
| # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||
| # time-stamp-end: "$" | ||||
| # time-stamp-time-zone: "UTC" | ||||
| # time-stamp-end: "; # UTC" | ||||
| # End: | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| #!/bin/sh | ||||
| # 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 | ||||
| # 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. | ||||
| # | ||||
| # 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. | ||||
| # | ||||
| # 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 | ||||
| 
 | ||||
| # Don't use :- since 4.3BSD and earlier shells don't like it. | ||||
| doit="${DOITPROG-}" | ||||
| doit=${DOITPROG-} | ||||
| if test -z "$doit"; then | ||||
|   doit_exec=exec | ||||
| else | ||||
| @ -58,34 +58,49 @@ fi | ||||
| # Put in absolute file names if you don't have them in your path; | ||||
| # or use environment vars. | ||||
| 
 | ||||
| mvprog="${MVPROG-mv}" | ||||
| cpprog="${CPPROG-cp}" | ||||
| chmodprog="${CHMODPROG-chmod}" | ||||
| chownprog="${CHOWNPROG-chown}" | ||||
| chgrpprog="${CHGRPPROG-chgrp}" | ||||
| stripprog="${STRIPPROG-strip}" | ||||
| rmprog="${RMPROG-rm}" | ||||
| mkdirprog="${MKDIRPROG-mkdir}" | ||||
| chgrpprog=${CHGRPPROG-chgrp} | ||||
| chmodprog=${CHMODPROG-chmod} | ||||
| chownprog=${CHOWNPROG-chown} | ||||
| cmpprog=${CMPPROG-cmp} | ||||
| cpprog=${CPPROG-cp} | ||||
| mkdirprog=${MKDIRPROG-mkdir} | ||||
| mvprog=${MVPROG-mv} | ||||
| rmprog=${RMPROG-rm} | ||||
| stripprog=${STRIPPROG-strip} | ||||
| 
 | ||||
| posix_glob='?' | ||||
| initialize_posix_glob=' | ||||
|   test "$posix_glob" != "?" || { | ||||
|     if (set -f) 2>/dev/null; then | ||||
|       posix_glob= | ||||
|     else | ||||
|       posix_glob=: | ||||
|     fi | ||||
|   } | ||||
| ' | ||||
| 
 | ||||
| posix_glob= | ||||
| posix_mkdir= | ||||
| 
 | ||||
| # Desired mode of installed file. | ||||
| mode=0755 | ||||
| 
 | ||||
| chgrpcmd= | ||||
| chmodcmd=$chmodprog | ||||
| chowncmd= | ||||
| chgrpcmd= | ||||
| stripcmd= | ||||
| mvcmd=$mvprog | ||||
| rmcmd="$rmprog -f" | ||||
| mvcmd="$mvprog" | ||||
| stripcmd= | ||||
| 
 | ||||
| src= | ||||
| dst= | ||||
| dir_arg= | ||||
| dstarg= | ||||
| dst_arg= | ||||
| 
 | ||||
| copy_on_change=false | ||||
| 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]... -t DIRECTORY SRCFILES... | ||||
|    or: $0 [OPTION]... -d DIRECTORIES... | ||||
| @ -95,65 +110,59 @@ In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. | ||||
| In the 4th, create DIRECTORIES. | ||||
| 
 | ||||
| Options: | ||||
| -c         (ignored) | ||||
| -d         create directories instead of installing files. | ||||
| -g GROUP   $chgrpprog installed files to GROUP. | ||||
| -m MODE    $chmodprog installed files to MODE. | ||||
| -o USER    $chownprog installed files to USER. | ||||
| -s         $stripprog installed files. | ||||
| -t DIRECTORY  install into DIRECTORY. | ||||
| -T         report an error if DSTFILE is a directory. | ||||
| --help     display this help and exit. | ||||
| --version  display version info and exit. | ||||
|      --help     display this help and exit. | ||||
|      --version  display version info and exit. | ||||
| 
 | ||||
|   -c            (ignored) | ||||
|   -C            install only if different (preserve the last data modification time) | ||||
|   -d            create directories instead of installing files. | ||||
|   -g GROUP      $chgrpprog installed files to GROUP. | ||||
|   -m MODE       $chmodprog installed files to MODE. | ||||
|   -o USER       $chownprog installed files to USER. | ||||
|   -s            $stripprog installed files. | ||||
|   -t DIRECTORY  install into DIRECTORY. | ||||
|   -T            report an error if DSTFILE is a directory. | ||||
| 
 | ||||
| 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 | ||||
|   case $1 in | ||||
|     -c) shift | ||||
|         continue;; | ||||
|     -c) ;; | ||||
| 
 | ||||
|     -d) dir_arg=true | ||||
|         shift | ||||
|         continue;; | ||||
|     -C) copy_on_change=true;; | ||||
| 
 | ||||
|     -d) dir_arg=true;; | ||||
| 
 | ||||
|     -g) chgrpcmd="$chgrpprog $2" | ||||
|         shift | ||||
|         shift | ||||
|         continue;; | ||||
| 	shift;; | ||||
| 
 | ||||
|     --help) echo "$usage"; exit $?;; | ||||
| 
 | ||||
|     -m) mode=$2 | ||||
|         shift | ||||
|         shift | ||||
| 	case $mode in | ||||
| 	  *' '* | *'	'* | *' | ||||
| '*	  | *'*'* | *'?'* | *'['*) | ||||
| 	    echo "$0: invalid mode: $mode" >&2 | ||||
| 	    exit 1;; | ||||
| 	esac | ||||
|         continue;; | ||||
| 	shift;; | ||||
| 
 | ||||
|     -o) chowncmd="$chownprog $2" | ||||
|         shift | ||||
|         shift | ||||
|         continue;; | ||||
| 	shift;; | ||||
| 
 | ||||
|     -s) stripcmd=$stripprog | ||||
|         shift | ||||
|         continue;; | ||||
|     -s) stripcmd=$stripprog;; | ||||
| 
 | ||||
|     -t) dstarg=$2 | ||||
| 	shift | ||||
| 	shift | ||||
| 	continue;; | ||||
|     -t) dst_arg=$2 | ||||
| 	# Protect names problematic for 'test' and other utilities. | ||||
| 	case $dst_arg in | ||||
| 	  -* | [=\(\)!]) dst_arg=./$dst_arg;; | ||||
| 	esac | ||||
| 	shift;; | ||||
| 
 | ||||
|     -T) no_target_directory=true | ||||
| 	shift | ||||
| 	continue;; | ||||
|     -T) no_target_directory=true;; | ||||
| 
 | ||||
|     --version) echo "$0 $scriptversion"; exit $?;; | ||||
| 
 | ||||
| @ -165,21 +174,26 @@ while test $# -ne 0; do | ||||
| 
 | ||||
|     *)  break;; | ||||
|   esac | ||||
|   shift | ||||
| 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 -t is used, the destination is already specified. | ||||
|   # Otherwise, the last argument is the destination.  Remove it from $@. | ||||
|   for arg | ||||
|   do | ||||
|     if test -n "$dstarg"; then | ||||
|     if test -n "$dst_arg"; then | ||||
|       # $@ is not empty: it contains at least $arg. | ||||
|       set fnord "$@" "$dstarg" | ||||
|       set fnord "$@" "$dst_arg" | ||||
|       shift # fnord | ||||
|     fi | ||||
|     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 | ||||
| fi | ||||
| 
 | ||||
| @ -188,13 +202,17 @@ if test $# -eq 0; then | ||||
|     echo "$0: no input file specified." >&2 | ||||
|     exit 1 | ||||
|   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. | ||||
|   exit 0 | ||||
| fi | ||||
| 
 | ||||
| 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. | ||||
|   # However, 'strip' requires both read and write access to temps. | ||||
| @ -222,9 +240,9 @@ fi | ||||
| 
 | ||||
| for src | ||||
| do | ||||
|   # Protect names starting with `-'. | ||||
|   # Protect names problematic for 'test' and other utilities. | ||||
|   case $src in | ||||
|     -*) src=./$src ;; | ||||
|     -* | [=\(\)!]) src=./$src;; | ||||
|   esac | ||||
| 
 | ||||
|   if test -n "$dir_arg"; then | ||||
| @ -242,22 +260,17 @@ do | ||||
|       exit 1 | ||||
|     fi | ||||
| 
 | ||||
|     if test -z "$dstarg"; then | ||||
|     if test -z "$dst_arg"; then | ||||
|       echo "$0: no destination specified." >&2 | ||||
|       exit 1 | ||||
|     fi | ||||
| 
 | ||||
|     dst=$dstarg | ||||
|     # Protect names starting with `-'. | ||||
|     case $dst in | ||||
|       -*) dst=./$dst ;; | ||||
|     esac | ||||
|     dst=$dst_arg | ||||
| 
 | ||||
|     # If destination is a directory, append the input filename; won't work | ||||
|     # if double slashes aren't ignored. | ||||
|     if test -d "$dst"; then | ||||
|       if test -n "$no_target_directory"; then | ||||
| 	echo "$0: $dstarg: Is a directory" >&2 | ||||
| 	echo "$0: $dst_arg: Is a directory" >&2 | ||||
| 	exit 1 | ||||
|       fi | ||||
|       dstdir=$dst | ||||
| @ -341,7 +354,7 @@ do | ||||
| 	      if test -z "$dir_arg" || { | ||||
| 		   # Check for POSIX incompatibilities with -m. | ||||
| 		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or | ||||
| 		   # other-writeable bit of parent directory when it shouldn't. | ||||
| 		   # other-writable bit of parent directory when it shouldn't. | ||||
| 		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. | ||||
| 		   ls_ld_tmpdir=`ls -ld "$tmpdir"` | ||||
| 		   case $ls_ld_tmpdir in | ||||
| @ -378,33 +391,26 @@ do | ||||
|       # directory the slow way, step by step, checking for races as we go. | ||||
| 
 | ||||
|       case $dstdir in | ||||
| 	/*) prefix=/ ;; | ||||
| 	-*) prefix=./ ;; | ||||
| 	*)  prefix= ;; | ||||
| 	/*) prefix='/';; | ||||
| 	[-=\(\)!]*) prefix='./';; | ||||
| 	*)  prefix='';; | ||||
|       esac | ||||
| 
 | ||||
|       case $posix_glob in | ||||
|         '') | ||||
| 	  if (set -f) 2>/dev/null; then | ||||
| 	    posix_glob=true | ||||
| 	  else | ||||
| 	    posix_glob=false | ||||
| 	  fi ;; | ||||
|       esac | ||||
|       eval "$initialize_posix_glob" | ||||
| 
 | ||||
|       oIFS=$IFS | ||||
|       IFS=/ | ||||
|       $posix_glob && set -f | ||||
|       $posix_glob set -f | ||||
|       set fnord $dstdir | ||||
|       shift | ||||
|       $posix_glob && set +f | ||||
|       $posix_glob set +f | ||||
|       IFS=$oIFS | ||||
| 
 | ||||
|       prefixes= | ||||
| 
 | ||||
|       for d | ||||
|       do | ||||
| 	test -z "$d" && continue | ||||
| 	test X"$d" = X && continue | ||||
| 
 | ||||
| 	prefix=$prefix$d | ||||
| 	if test -d "$prefix"; then | ||||
| @ -459,41 +465,54 @@ do | ||||
|     # ignore errors from any of these, just make sure not to ignore | ||||
|     # errors from the above "$doit $cpprog $src $dsttmp" command. | ||||
|     # | ||||
|     { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ | ||||
|       && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ | ||||
|       && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ | ||||
|       && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && | ||||
|     { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && | ||||
|     { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && | ||||
|     { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && | ||||
|     { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && | ||||
| 
 | ||||
|     # Now rename the file to the real destination. | ||||
|     { $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \ | ||||
|       || { | ||||
| 	   # The rename failed, perhaps because mv can't rename something else | ||||
| 	   # to itself, or perhaps because mv is so ancient that it does not | ||||
| 	   # support -f. | ||||
|     # 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` && | ||||
| 
 | ||||
| 	   # Now remove or move aside any old file at destination location. | ||||
| 	   # We try this two ways since rm can't unlink itself on some | ||||
| 	   # systems and the destination file might be busy for other | ||||
| 	   # reasons.  In this case, the final cleanup might fail but the new | ||||
| 	   # file should still install successfully. | ||||
| 	   { | ||||
| 	     if test -f "$dst"; then | ||||
| 	       $doit $rmcmd -f "$dst" 2>/dev/null \ | ||||
| 	       || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \ | ||||
| 		     && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\ | ||||
| 	       || { | ||||
| 		 echo "$0: cannot unlink or rename $dst" >&2 | ||||
| 		 (exit 1); exit 1 | ||||
| 	       } | ||||
| 	     else | ||||
| 	       : | ||||
| 	     fi | ||||
| 	   } && | ||||
|        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 && | ||||
| 
 | ||||
| 	   # Now rename the file to the real destination. | ||||
| 	   $doit $mvcmd "$dsttmp" "$dst" | ||||
| 	 } | ||||
|     } || exit 1 | ||||
|        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 || | ||||
| 
 | ||||
|       # The rename failed, perhaps because mv can't rename something else | ||||
|       # to itself, or perhaps because mv is so ancient that it does not | ||||
|       # support -f. | ||||
|       { | ||||
| 	# Now remove or move aside any old file at destination location. | ||||
| 	# We try this two ways since rm can't unlink itself on some | ||||
| 	# systems and the destination file might be busy for other | ||||
| 	# reasons.  In this case, the final cleanup might fail but the new | ||||
| 	# file should still install successfully. | ||||
| 	{ | ||||
| 	  test ! -f "$dst" || | ||||
| 	  $doit $rmcmd -f "$dst" 2>/dev/null || | ||||
| 	  { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && | ||||
| 	    { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } | ||||
| 	  } || | ||||
| 	  { echo "$0: cannot unlink or rename $dst" >&2 | ||||
| 	    (exit 1); exit 1 | ||||
| 	  } | ||||
| 	} && | ||||
| 
 | ||||
| 	# Now rename the file to the real destination. | ||||
| 	$doit $mvcmd "$dsttmp" "$dst" | ||||
|       } | ||||
|     fi || exit 1 | ||||
| 
 | ||||
|     trap '' 0 | ||||
|   fi | ||||
| @ -503,5 +522,6 @@ done | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "scriptversion=" | ||||
| # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||
| # time-stamp-end: "$" | ||||
| # time-stamp-time-zone: "UTC" | ||||
| # time-stamp-end: "; # UTC" | ||||
| # End: | ||||
|  | ||||
| @ -1,11 +1,10 @@ | ||||
| #! /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 | ||||
| #   Free Software Foundation, Inc. | ||||
| # Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. | ||||
| # Copyright (C) 1996-2013 Free Software Foundation, Inc. | ||||
| # Originally written by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. | ||||
| 
 | ||||
| # This program is free software; you can redistribute it and/or modify | ||||
| # 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. | ||||
| 
 | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program; if not, write to the Free Software | ||||
| # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||
| # 02110-1301, USA. | ||||
| # 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 | ||||
| @ -28,66 +25,40 @@ scriptversion=2006-05-10.23 | ||||
| # the same distribution terms that you use for the rest of that program. | ||||
| 
 | ||||
| if test $# -eq 0; then | ||||
|   echo 1>&2 "Try \`$0 --help' for more information" | ||||
|   echo 1>&2 "Try '$0 --help' for more information" | ||||
|   exit 1 | ||||
| fi | ||||
| 
 | ||||
| run=: | ||||
| sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' | ||||
| sed_minuso='s/.* -o \([^ ]*\).*/\1/p' | ||||
| 
 | ||||
| # In the cases where this matters, `missing' is being run in the | ||||
| # srcdir already. | ||||
| if test -f configure.ac; then | ||||
|   configure_ac=configure.ac | ||||
| else | ||||
|   configure_ac=configure.in | ||||
| fi | ||||
| 
 | ||||
| msg="missing on your system" | ||||
| 
 | ||||
| case $1 in | ||||
| --run) | ||||
|   # Try to run requested program, and just exit if it succeeds. | ||||
|   run= | ||||
|   shift | ||||
|   "$@" && exit 0 | ||||
|   # Exit code 63 means version mismatch.  This often happens | ||||
|   # when the user try to use an ancient version of a tool on | ||||
|   # a file that requires a minimum version.  In this case we | ||||
|   # we should proceed has if the program had been absent, or | ||||
|   # if --run hadn't been passed. | ||||
|   if test $? = 63; then | ||||
|     run=: | ||||
|     msg="probably too old" | ||||
|   fi | ||||
|   ;; | ||||
| 
 | ||||
|   --is-lightweight) | ||||
|     # 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 | ||||
|     ;; | ||||
| 
 | ||||
|   -h|--h|--he|--hel|--help) | ||||
|     echo "\ | ||||
| $0 [OPTION]... PROGRAM [ARGUMENT]... | ||||
| 
 | ||||
| Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an | ||||
| error status if there is no known handling for PROGRAM. | ||||
| Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due | ||||
| to PROGRAM being missing or too old. | ||||
| 
 | ||||
| Options: | ||||
|   -h, --help      display this help and exit | ||||
|   -v, --version   output version information and exit | ||||
|   --run           try to run the given command, and emulate it if it fails | ||||
| 
 | ||||
| Supported PROGRAM values: | ||||
|   aclocal      touch file \`aclocal.m4' | ||||
|   autoconf     touch file \`configure' | ||||
|   autoheader   touch file \`config.h.in' | ||||
|   autom4te     touch the output file, or create a stub one | ||||
|   automake     touch all \`Makefile.in' files | ||||
|   bison        create \`y.tab.[ch]', if possible, from existing .[ch] | ||||
|   flex         create \`lex.yy.c', if possible, from existing .c | ||||
|   help2man     touch the output file | ||||
|   lex          create \`lex.yy.c', if possible, from existing .c | ||||
|   makeinfo     touch the output file | ||||
|   tar          try tar, gnutar, gtar, then tar without non-portable flags | ||||
|   yacc         create \`y.tab.[ch]', if possible, from existing .[ch] | ||||
|   aclocal   autoconf  autoheader   autom4te  automake  makeinfo | ||||
|   bison     yacc      flex         lex       help2man | ||||
| 
 | ||||
| Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and | ||||
| 'g' are ignored when checking the name. | ||||
| 
 | ||||
| Send bug reports to <bug-automake@gnu.org>." | ||||
|     exit $? | ||||
| @ -99,269 +70,146 @@ Send bug reports to <bug-automake@gnu.org>." | ||||
|     ;; | ||||
| 
 | ||||
|   -*) | ||||
|     echo 1>&2 "$0: Unknown \`$1' option" | ||||
|     echo 1>&2 "Try \`$0 --help' for more information" | ||||
|     echo 1>&2 "$0: unknown '$1' option" | ||||
|     echo 1>&2 "Try '$0 --help' for more information" | ||||
|     exit 1 | ||||
|     ;; | ||||
| 
 | ||||
| esac | ||||
| 
 | ||||
| # Now exit if we have it, but it failed.  Also exit now if we | ||||
| # don't have it and --version was passed (most likely to detect | ||||
| # the program). | ||||
| case $1 in | ||||
|   lex|yacc) | ||||
|     # Not GNU programs, they don't have --version. | ||||
| # Run the given program, remember its exit status. | ||||
| "$@"; st=$? | ||||
| 
 | ||||
| # If it succeeded, we are done. | ||||
| test $st -eq 0 && exit 0 | ||||
| 
 | ||||
| # Also exit now if we it failed (or wasn't found), and '--version' was | ||||
| # passed; such an option is passed most likely to detect whether the | ||||
| # program is present and works. | ||||
| case $2 in --version|--help) exit $st;; esac | ||||
| 
 | ||||
| # Exit code 63 means version mismatch.  This often happens when the user | ||||
| # tries to use an ancient version of a tool on a file that requires a | ||||
| # minimum version. | ||||
| if test $st -eq 63; then | ||||
|   msg="probably too old" | ||||
| elif test $st -eq 127; then | ||||
|   # Program was missing. | ||||
|   msg="missing on your system" | ||||
| else | ||||
|   # Program was found and executed, but failed.  Give up. | ||||
|   exit $st | ||||
| fi | ||||
| 
 | ||||
| perl_URL=http://www.perl.org/ | ||||
| flex_URL=http://flex.sourceforge.net/ | ||||
| gnu_software_URL=http://www.gnu.org/software | ||||
| 
 | ||||
| program_details () | ||||
| { | ||||
|   case $1 in | ||||
|     aclocal|automake) | ||||
|       echo "The '$1' program is part of the GNU Automake package:" | ||||
|       echo "<$gnu_software_URL/automake>" | ||||
|       echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" | ||||
|       echo "<$gnu_software_URL/autoconf>" | ||||
|       echo "<$gnu_software_URL/m4/>" | ||||
|       echo "<$perl_URL>" | ||||
|       ;; | ||||
|     autoconf|autom4te|autoheader) | ||||
|       echo "The '$1' program is part of the GNU Autoconf package:" | ||||
|       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 | ||||
| } | ||||
| 
 | ||||
| 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*) | ||||
|       echo "You should only need it if you modified 'Makefile.am' or" | ||||
|       echo "$configure_deps." | ||||
|       program_details 'automake' | ||||
|       ;; | ||||
|     aclocal*) | ||||
|       echo "You should only need it if you modified 'acinclude.m4' or" | ||||
|       echo "$configure_deps." | ||||
|       program_details 'aclocal' | ||||
|       ;; | ||||
|    autom4te*) | ||||
|       echo "You might have modified some maintainer files that require" | ||||
|       echo "the 'autom4te' program to be rebuilt." | ||||
|       program_details 'autom4te' | ||||
|       ;; | ||||
|     bison*|yacc*) | ||||
|       echo "You should only need it if you modified a '.y' file." | ||||
|       echo "You may want to install the GNU Bison package:" | ||||
|       echo "<$gnu_software_URL/bison/>" | ||||
|       ;; | ||||
|     lex*|flex*) | ||||
|       echo "You should only need it if you modified a '.l' file." | ||||
|       echo "You may want to install the Fast Lexical Analyzer package:" | ||||
|       echo "<$flex_URL>" | ||||
|       ;; | ||||
|     help2man*) | ||||
|       echo "You should only need it if you modified a dependency" \ | ||||
|            "of a man page." | ||||
|       echo "You may want to install the GNU Help2man package:" | ||||
|       echo "<$gnu_software_URL/help2man/>" | ||||
|     ;; | ||||
|     makeinfo*) | ||||
|       echo "You should only need it if you modified a '.texi' file, or" | ||||
|       echo "any other file indirectly affecting the aspect of the manual." | ||||
|       echo "You might want to install the Texinfo package:" | ||||
|       echo "<$gnu_software_URL/texinfo/>" | ||||
|       echo "The spurious makeinfo call might also be the consequence of" | ||||
|       echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" | ||||
|       echo "want to install GNU make:" | ||||
|       echo "<$gnu_software_URL/make/>" | ||||
|       ;; | ||||
|     *) | ||||
|       echo "You might have modified some files without having the proper" | ||||
|       echo "tools for further handling them.  Check the 'README' file, it" | ||||
|       echo "often tells you about the needed prerequisites for installing" | ||||
|       echo "this package.  You may also peek at any GNU archive site, in" | ||||
|       echo "case some other package contains this missing '$1' program." | ||||
|       ;; | ||||
|   esac | ||||
| } | ||||
| 
 | ||||
|   tar) | ||||
|     if test -n "$run"; then | ||||
|        echo 1>&2 "ERROR: \`tar' requires --run" | ||||
|        exit 1 | ||||
|     elif test "x$2" = "x--version" || test "x$2" = "x--help"; then | ||||
|        exit 1 | ||||
|     fi | ||||
|     ;; | ||||
| give_advice "$1" | sed -e '1s/^/WARNING: /' \ | ||||
|                        -e '2,$s/^/         /' >&2 | ||||
| 
 | ||||
|   *) | ||||
|     if test -z "$run" && ($1 --version) > /dev/null 2>&1; then | ||||
|        # We have it, but it failed. | ||||
|        exit 1 | ||||
|     elif test "x$2" = "x--version" || test "x$2" = "x--help"; then | ||||
|        # Could not run --version or --help.  This is probably someone | ||||
|        # running `$TOOL --version' or `$TOOL --help' to check whether | ||||
|        # $TOOL exists and not knowing $TOOL uses missing. | ||||
|        exit 1 | ||||
|     fi | ||||
|     ;; | ||||
| esac | ||||
| 
 | ||||
| # If it does not exist, or fails to run (possibly an outdated version), | ||||
| # try to emulate it. | ||||
| case $1 in | ||||
|   aclocal*) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is $msg.  You should only need it if | ||||
|          you modified \`acinclude.m4' or \`${configure_ac}'.  You might want | ||||
|          to install the \`Automake' and \`Perl' packages.  Grab them from | ||||
|          any GNU archive site." | ||||
|     touch aclocal.m4 | ||||
|     ;; | ||||
| 
 | ||||
|   autoconf) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is $msg.  You should only need it if | ||||
|          you modified \`${configure_ac}'.  You might want to install the | ||||
|          \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU | ||||
|          archive site." | ||||
|     touch configure | ||||
|     ;; | ||||
| 
 | ||||
|   autoheader) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is $msg.  You should only need it if | ||||
|          you modified \`acconfig.h' or \`${configure_ac}'.  You might want | ||||
|          to install the \`Autoconf' and \`GNU m4' packages.  Grab them | ||||
|          from any GNU archive site." | ||||
|     files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` | ||||
|     test -z "$files" && files="config.h" | ||||
|     touch_files= | ||||
|     for f in $files; do | ||||
|       case $f in | ||||
|       *:*) touch_files="$touch_files "`echo "$f" | | ||||
| 				       sed -e 's/^[^:]*://' -e 's/:.*//'`;; | ||||
|       *) touch_files="$touch_files $f.in";; | ||||
|       esac | ||||
|     done | ||||
|     touch $touch_files | ||||
|     ;; | ||||
| 
 | ||||
|   automake*) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is $msg.  You should only need it if | ||||
|          you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. | ||||
|          You might want to install the \`Automake' and \`Perl' packages. | ||||
|          Grab them from any GNU archive site." | ||||
|     find . -type f -name Makefile.am -print | | ||||
| 	   sed 's/\.am$/.in/' | | ||||
| 	   while read f; do touch "$f"; done | ||||
|     ;; | ||||
| 
 | ||||
|   autom4te) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is needed, but is $msg. | ||||
|          You might have modified some files without having the | ||||
|          proper tools for further handling them. | ||||
|          You can get \`$1' as part of \`Autoconf' from any GNU | ||||
|          archive site." | ||||
| 
 | ||||
|     file=`echo "$*" | sed -n "$sed_output"` | ||||
|     test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` | ||||
|     if test -f "$file"; then | ||||
| 	touch $file | ||||
|     else | ||||
| 	test -z "$file" || exec >$file | ||||
| 	echo "#! /bin/sh" | ||||
| 	echo "# Created by GNU Automake missing as a replacement of" | ||||
| 	echo "#  $ $@" | ||||
| 	echo "exit 0" | ||||
| 	chmod +x $file | ||||
| 	exit 1 | ||||
|     fi | ||||
|     ;; | ||||
| 
 | ||||
|   bison|yacc) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' $msg.  You should only need it if | ||||
|          you modified a \`.y' file.  You may need the \`Bison' package | ||||
|          in order for those modifications to take effect.  You can get | ||||
|          \`Bison' from any GNU archive site." | ||||
|     rm -f y.tab.c y.tab.h | ||||
|     if test $# -ne 1; then | ||||
|         eval LASTARG="\${$#}" | ||||
| 	case $LASTARG in | ||||
| 	*.y) | ||||
| 	    SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` | ||||
| 	    if test -f "$SRCFILE"; then | ||||
| 	         cp "$SRCFILE" y.tab.c | ||||
| 	    fi | ||||
| 	    SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` | ||||
| 	    if test -f "$SRCFILE"; then | ||||
| 	         cp "$SRCFILE" y.tab.h | ||||
| 	    fi | ||||
| 	  ;; | ||||
| 	esac | ||||
|     fi | ||||
|     if test ! -f y.tab.h; then | ||||
| 	echo >y.tab.h | ||||
|     fi | ||||
|     if test ! -f y.tab.c; then | ||||
| 	echo 'main() { return 0; }' >y.tab.c | ||||
|     fi | ||||
|     ;; | ||||
| 
 | ||||
|   lex|flex) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is $msg.  You should only need it if | ||||
|          you modified a \`.l' file.  You may need the \`Flex' package | ||||
|          in order for those modifications to take effect.  You can get | ||||
|          \`Flex' from any GNU archive site." | ||||
|     rm -f lex.yy.c | ||||
|     if test $# -ne 1; then | ||||
|         eval LASTARG="\${$#}" | ||||
| 	case $LASTARG in | ||||
| 	*.l) | ||||
| 	    SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` | ||||
| 	    if test -f "$SRCFILE"; then | ||||
| 	         cp "$SRCFILE" lex.yy.c | ||||
| 	    fi | ||||
| 	  ;; | ||||
| 	esac | ||||
|     fi | ||||
|     if test ! -f lex.yy.c; then | ||||
| 	echo 'main() { return 0; }' >lex.yy.c | ||||
|     fi | ||||
|     ;; | ||||
| 
 | ||||
|   help2man) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is $msg.  You should only need it if | ||||
| 	 you modified a dependency of a manual page.  You may need the | ||||
| 	 \`Help2man' package in order for those modifications to take | ||||
| 	 effect.  You can get \`Help2man' from any GNU archive site." | ||||
| 
 | ||||
|     file=`echo "$*" | sed -n "$sed_output"` | ||||
|     test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` | ||||
|     if test -f "$file"; then | ||||
| 	touch $file | ||||
|     else | ||||
| 	test -z "$file" || exec >$file | ||||
| 	echo ".ab help2man is required to generate this page" | ||||
| 	exit 1 | ||||
|     fi | ||||
|     ;; | ||||
| 
 | ||||
|   makeinfo) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is $msg.  You should only need it if | ||||
|          you modified a \`.texi' or \`.texinfo' file, or any other file | ||||
|          indirectly affecting the aspect of the manual.  The spurious | ||||
|          call might also be the consequence of using a buggy \`make' (AIX, | ||||
|          DU, IRIX).  You might want to install the \`Texinfo' package or | ||||
|          the \`GNU make' package.  Grab either from any GNU archive site." | ||||
|     # The file to touch is that specified with -o ... | ||||
|     file=`echo "$*" | sed -n "$sed_output"` | ||||
|     test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` | ||||
|     if test -z "$file"; then | ||||
|       # ... or it is the one specified with @setfilename ... | ||||
|       infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` | ||||
|       file=`sed -n ' | ||||
| 	/^@setfilename/{ | ||||
| 	  s/.* \([^ ]*\) *$/\1/ | ||||
| 	  p | ||||
| 	  q | ||||
| 	}' $infile` | ||||
|       # ... or it is derived from the source name (dir/f.texi becomes f.info) | ||||
|       test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info | ||||
|     fi | ||||
|     # If the file does not exist, the user really needs makeinfo; | ||||
|     # let's fail without touching anything. | ||||
|     test -f $file || exit 1 | ||||
|     touch $file | ||||
|     ;; | ||||
| 
 | ||||
|   tar) | ||||
|     shift | ||||
| 
 | ||||
|     # We have already tried tar in the generic part. | ||||
|     # Look for gnutar/gtar before invocation to avoid ugly error | ||||
|     # messages. | ||||
|     if (gnutar --version > /dev/null 2>&1); then | ||||
|        gnutar "$@" && exit 0 | ||||
|     fi | ||||
|     if (gtar --version > /dev/null 2>&1); then | ||||
|        gtar "$@" && exit 0 | ||||
|     fi | ||||
|     firstarg="$1" | ||||
|     if shift; then | ||||
| 	case $firstarg in | ||||
| 	*o*) | ||||
| 	    firstarg=`echo "$firstarg" | sed s/o//` | ||||
| 	    tar "$firstarg" "$@" && exit 0 | ||||
| 	    ;; | ||||
| 	esac | ||||
| 	case $firstarg in | ||||
| 	*h*) | ||||
| 	    firstarg=`echo "$firstarg" | sed s/h//` | ||||
| 	    tar "$firstarg" "$@" && exit 0 | ||||
| 	    ;; | ||||
| 	esac | ||||
|     fi | ||||
| 
 | ||||
|     echo 1>&2 "\ | ||||
| WARNING: I can't seem to be able to run \`tar' with the given arguments. | ||||
|          You may want to install GNU tar or Free paxutils, or check the | ||||
|          command line arguments." | ||||
|     exit 1 | ||||
|     ;; | ||||
| 
 | ||||
|   *) | ||||
|     echo 1>&2 "\ | ||||
| WARNING: \`$1' is needed, and is $msg. | ||||
|          You might have modified some files without having the | ||||
|          proper tools for further handling them.  Check the \`README' file, | ||||
|          it often tells you about the needed prerequisites for installing | ||||
|          this package.  You may also peek at any GNU archive site, in case | ||||
|          some other package would contain this missing \`$1' program." | ||||
|     exit 1 | ||||
|     ;; | ||||
| esac | ||||
| 
 | ||||
| exit 0 | ||||
| # 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: | ||||
| # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||
| # time-stamp-start: "scriptversion=" | ||||
| # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||
| # time-stamp-end: "$" | ||||
| # time-stamp-time-zone: "UTC" | ||||
| # time-stamp-end: "; # UTC" | ||||
| # 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= | ||||
							
								
								
									
										1709
									
								
								google-breakpad/configure
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1709
									
								
								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_CONFIG_HEADER(src/config.h) | ||||
| AM_MAINTAINER_MODE | ||||
| 
 | ||||
| AM_PROG_AS | ||||
| AC_PROG_CC | ||||
| @ -48,7 +49,28 @@ AC_PROG_CXX | ||||
| AC_PROG_RANLIB | ||||
| 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_SYS_LARGEFILE | ||||
| m4_include(m4/ax_pthread.m4) | ||||
| AX_PTHREAD | ||||
| AC_CHECK_HEADERS([a.out.h]) | ||||
| @ -69,25 +91,6 @@ case $host in | ||||
| esac | ||||
| AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue) | ||||
| 
 | ||||
| AC_ARG_ENABLE(m32, | ||||
|               AS_HELP_STRING([--enable-m32], | ||||
|                              [Compile/build with -m32] | ||||
|                              [(default is no)]), | ||||
|               [case "${enableval}" in | ||||
|                  yes) | ||||
|                    CFLAGS="${CFLAGS} -m32" | ||||
|                    CXXFLAGS="${CXXFLAGS} -m32" | ||||
|                    usem32=true | ||||
|                    ;; | ||||
|                  no) | ||||
|                    usem32=false | ||||
|                    ;; | ||||
|                  *) | ||||
|                    AC_MSG_ERROR(bad value ${enableval} for --enable-m32) | ||||
|                    ;; | ||||
|                esac], | ||||
|               [usem32=false]) | ||||
| 
 | ||||
| AC_ARG_ENABLE(processor, | ||||
|               AS_HELP_STRING([--disable-processor], | ||||
|                              [Don't build processor library] | ||||
| @ -147,5 +150,10 @@ AC_ARG_ENABLE(selftest, | ||||
|               [selftest=false]) | ||||
| AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue) | ||||
| 
 | ||||
| AC_CONFIG_FILES([Makefile]) | ||||
| AC_CONFIG_FILES(m4_flatten([ | ||||
|   breakpad.pc | ||||
|   breakpad-client.pc | ||||
|   Makefile | ||||
| ])) | ||||
| 
 | ||||
| AC_OUTPUT | ||||
|  | ||||
| @ -33,4 +33,25 @@ | ||||
| #include "testing/gtest/include/gtest/gtest.h" | ||||
| #include "testing/include/gmock/gmock.h" | ||||
| 
 | ||||
| // If AddressSanitizer is used, NULL pointer dereferences generate SIGILL
 | ||||
| // (illegal instruction) instead of SIGSEGV (segmentation fault).  Also,
 | ||||
| // 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__
 | ||||
|  | ||||
							
								
								
									
										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. | ||||
| # All rights reserved. | ||||
| # Copyright 2010 Google Inc. All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are | ||||
| @ -32,34 +31,13 @@ | ||||
| # since gyp_chromium is automatically forcing its inclusion. | ||||
| { | ||||
|   '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 | ||||
|     # ~/.gyp/include.gypi. | ||||
| 
 | ||||
|     # Putting a variables dict inside another variables dict looks kind of | ||||
|     # weird.  This is done so that "branding" and "buildtype" are defined as | ||||
|     # variables within the outer variables dict here.  This is necessary | ||||
|     # to get these variables defined for the conditions within this variables | ||||
|     # dict that operate on these variables. | ||||
|     # weird. This is necessary to get these variables defined for the conditions | ||||
|     # within this variables dict that operate on these 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': { | ||||
|         # Compute the architecture that we're building on. | ||||
|         'conditions': [ | ||||
| @ -73,49 +51,19 @@ | ||||
|             '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)', | ||||
| 
 | ||||
|       # Default architecture we're building for is the architecture we're | ||||
|       # building on. | ||||
|       '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 | ||||
|       # are built under a chromium full build (1) or a webkit.org chromium | ||||
|       # build (0). | ||||
|       '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 | ||||
|       # libraries on linux x86-64 and arm. | ||||
|       'linux_fpic%': 0, | ||||
| @ -123,10 +71,10 @@ | ||||
|       # Python version. | ||||
|       'python_ver%': '2.5', | ||||
| 
 | ||||
|       # Set ARM-v7 compilation flags | ||||
|       'armv7%': 0, | ||||
|       # Determine ARM compilation flags. | ||||
|       '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, | ||||
| 
 | ||||
|       # The system root for cross-compiles. Default: none. | ||||
| @ -136,19 +84,12 @@ | ||||
|       '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)', | ||||
|     'host_arch%': '<(host_arch)', | ||||
|     'toolkit_views%': '<(toolkit_views)', | ||||
|     'chromeos%': '<(chromeos)', | ||||
|     'inside_chromium_build%': '<(inside_chromium_build)', | ||||
|     'fastbuild%': '<(fastbuild)', | ||||
|     'linux_fpic%': '<(linux_fpic)', | ||||
|     'python_ver%': '<(python_ver)', | ||||
|     'armv7%': '<(armv7)', | ||||
|     'arm_version%': '<(arm_version)', | ||||
|     'arm_neon%': '<(arm_neon)', | ||||
|     'sysroot%': '<(sysroot)', | ||||
|     'disable_sse2%': '<(disable_sse2)', | ||||
| @ -245,7 +186,7 @@ | ||||
|     'linux_use_seccomp_sandbox%': 0, | ||||
| 
 | ||||
|     # 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 | ||||
|     # isn't supported | ||||
| @ -254,7 +195,7 @@ | ||||
|     # Set Thumb compilation flags. | ||||
|     '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_fpu%': 'vfpv3', | ||||
| 
 | ||||
| @ -262,62 +203,6 @@ | ||||
|     'enable_new_npdevice_api%': 0, | ||||
| 
 | ||||
|     '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 | ||||
|       # optional because it sometimes causes corruption on VS 2005. | ||||
|       # It is on by default on VS 2008 and off on VS 2005. | ||||
| @ -341,24 +226,6 @@ | ||||
|           '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 '_' | ||||
| @ -373,18 +240,10 @@ | ||||
|     ], | ||||
|   }, | ||||
|   'target_defaults': { | ||||
|     'includes': [ | ||||
|       'filename_rules.gypi', | ||||
|     ], | ||||
|     '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 | ||||
|       'mac_release_optimization%': '3', # Use -O3 unless overridden | ||||
|       'mac_debug_optimization%': '0',   # Use -O0 unless overridden | ||||
| @ -400,34 +259,6 @@ | ||||
|       'release_valgrind_build%': 0, | ||||
|     }, | ||||
|     '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', { | ||||
|         'defines': ['CHROMIUM_SELINUX=1'], | ||||
|       }], | ||||
| @ -438,16 +269,6 @@ | ||||
|           }], | ||||
|         ], | ||||
|       }], | ||||
|       ['enable_gpu==1', { | ||||
|         'defines': [ | ||||
|           'ENABLE_GPU=1', | ||||
|         ], | ||||
|       }], | ||||
|       ['enable_gles==1', { | ||||
|         'defines': [ | ||||
|           'ENABLE_GLES=1', | ||||
|         ], | ||||
|       }], | ||||
|       ['coverage!=0', { | ||||
|         'conditions': [ | ||||
|           ['OS=="mac"', { | ||||
| @ -489,82 +310,34 @@ | ||||
|       }],  # coverage!=0 | ||||
|     ],  # conditions for 'target_defaults' | ||||
|     'target_conditions': [ | ||||
|       ['chromium_code==0', { | ||||
|         'conditions': [ | ||||
|           [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { | ||||
|             'cflags!': [ | ||||
|               '-Wall', | ||||
|               '-Wextra', | ||||
|               '-Werror', | ||||
|             ], | ||||
|           }], | ||||
|           [ 'OS=="win"', { | ||||
|             'defines': [ | ||||
|               '_CRT_SECURE_NO_DEPRECATE', | ||||
|               '_CRT_NONSTDC_NO_WARNINGS', | ||||
|               '_CRT_NONSTDC_NO_DEPRECATE', | ||||
|             ], | ||||
|             'msvs_disabled_warnings': [4800], | ||||
|             'msvs_settings': { | ||||
|               'VCCLCompilerTool': { | ||||
|                 'WarnAsError': 'false', | ||||
|                 'Detect64BitPortabilityProblems': 'false', | ||||
|               }, | ||||
|             }, | ||||
|           }], | ||||
|           [ 'OS=="mac"', { | ||||
|             'xcode_settings': { | ||||
|               'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO', | ||||
|               'WARNING_CFLAGS!': ['-Wall'], | ||||
|             }, | ||||
|           }], | ||||
|       [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { | ||||
|         'cflags!': [ | ||||
|           '-Wall', | ||||
|           '-Wextra', | ||||
|           '-Werror', | ||||
|         ], | ||||
|       }, { | ||||
|         # In Chromium code, we define __STDC_FORMAT_MACROS in order to get the | ||||
|         # C99 macros on Mac and Linux. | ||||
|       }], | ||||
|       [ 'OS=="win"', { | ||||
|         '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$'] ] | ||||
|           }], | ||||
|           '_CRT_SECURE_NO_DEPRECATE', | ||||
|           '_CRT_NONSTDC_NO_WARNINGS', | ||||
|           '_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_settings': { | ||||
|           'VCCLCompilerTool': { | ||||
|             'WarnAsError': 'true', | ||||
|             'Detect64BitPortabilityProblems': 'false', | ||||
|           }, | ||||
|         }, | ||||
|       }], | ||||
|       [ 'OS=="mac"', { | ||||
|         'xcode_settings': { | ||||
|           'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO', | ||||
|           'WARNING_CFLAGS!': ['-Wall'], | ||||
|         }, | ||||
|       }], | ||||
|     ],  # target_conditions for 'target_defaults' | ||||
|     'default_configuration': 'Debug', | ||||
| @ -590,6 +363,7 @@ | ||||
|         'abstract': 1, | ||||
|         'msvs_settings': { | ||||
|           'VCLinkerTool': { | ||||
|             'MinimumRequiredVersion': '5.01',  # XP. | ||||
|             'TargetMachine': '1', | ||||
|           }, | ||||
|         }, | ||||
| @ -715,7 +489,30 @@ | ||||
|         'inherit_from': ['Common_Base', 'x86_Base', 'Release_Base'], | ||||
|         'conditions': [ | ||||
|           ['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', | ||||
|         ], | ||||
|         'cflags_cc': [ | ||||
|           '-fno-rtti', | ||||
|           '-frtti', | ||||
|           '-fno-threadsafe-statics', | ||||
|           # Make inline functions have hidden visiblity by default. | ||||
|           # Surprisingly, not covered by -fvisibility=hidden. | ||||
| @ -821,12 +618,6 @@ | ||||
|                            '$_LIBDIRFLAGS', '$LDMODULEFLAGS', '$SOURCES', | ||||
|                            '-Wl,--start-group', '$_LIBFLAGS', '-Wl,--end-group']], | ||||
|           'IMPLICIT_COMMAND_DEPENDENCIES': 0, | ||||
|           # -rpath is only used when building with shared libraries. | ||||
|           'conditions': [ | ||||
|             [ 'component=="shared_library"', { | ||||
|               'RPATH': '$LIB_DIR', | ||||
|             }], | ||||
|           ], | ||||
|         }, | ||||
|         'scons_import_variables': [ | ||||
|           'AS', | ||||
| @ -932,22 +723,13 @@ | ||||
|             # compiler optimized the code, since the value is always kept | ||||
|             # in its specified precision. | ||||
|             'conditions': [ | ||||
|               ['branding=="Chromium" and disable_sse2==0', { | ||||
|               ['disable_sse2==0', { | ||||
|                 'cflags': [ | ||||
|                   '-march=pentium4', | ||||
|                   '-msse2', | ||||
|                   '-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. | ||||
|             # video playback is mmx and sse2 optimized. | ||||
| @ -980,7 +762,7 @@ | ||||
|                     '-Wa,-mimplicit-it=thumb', | ||||
|                     ] | ||||
|                   }], | ||||
|                   ['armv7==1', { | ||||
|                   ['arm_version==7', { | ||||
|                     'cflags': [ | ||||
|                       '-march=armv7-a', | ||||
|                       '-mtune=cortex-a8', | ||||
| @ -1019,24 +801,6 @@ | ||||
|               '-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', { | ||||
|             'variables': {'linux_use_tcmalloc%': 1}, | ||||
|           }], | ||||
| @ -1078,7 +842,7 @@ | ||||
|           'GCC_DYNAMIC_NO_PIC': 'NO',               # No -mdynamic-no-pic | ||||
|                                                     # (Equivalent to -fPIC) | ||||
|           '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_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden | ||||
|           'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', | ||||
| @ -1106,51 +870,6 @@ | ||||
|           ['_mac_bundle', { | ||||
|             '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_defaults | ||||
|     }],  # OS=="mac" | ||||
| @ -1174,7 +893,9 @@ | ||||
|           '$(VSInstallDir)/VC/atlmfc/include', | ||||
|         ], | ||||
|         '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': { | ||||
|           'VCCLCompilerTool': { | ||||
|             'MinimalRebuild': 'false', | ||||
| @ -1182,7 +903,7 @@ | ||||
|             'BufferSecurityCheck': 'true', | ||||
|             'EnableFunctionLevelLinking': 'true', | ||||
|             'RuntimeTypeInfo': 'false', | ||||
|             'WarningLevel': '3', | ||||
|             'WarningLevel': '4', | ||||
|             'WarnAsError': 'true', | ||||
|             'DebugInformationFormat': '3', | ||||
|             'conditions': [ | ||||
| @ -1322,9 +1043,3 @@ | ||||
|     '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,66 +27,79 @@ | ||||
| // (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/crash_generation/crash_generation_client.h" | ||||
| 
 | ||||
| #include <stdio.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/types.h> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| 
 | ||||
| #include "client/linux/crash_generation/crash_generation_client.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/linux/ignore_ret.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| bool | ||||
| CrashGenerationClient::RequestDump(const void* blob, size_t blob_size) | ||||
| { | ||||
|   int fds[2]; | ||||
|   sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds); | ||||
|   static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); | ||||
| namespace { | ||||
| 
 | ||||
|   struct kernel_msghdr msg; | ||||
|   my_memset(&msg, 0, sizeof(struct kernel_msghdr)); | ||||
|   struct kernel_iovec iov[1]; | ||||
|   iov[0].iov_base = const_cast<void*>(blob); | ||||
|   iov[0].iov_len = blob_size; | ||||
| class CrashGenerationClientImpl : public CrashGenerationClient { | ||||
|  public: | ||||
|   explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {} | ||||
|   virtual ~CrashGenerationClientImpl() {} | ||||
| 
 | ||||
|   msg.msg_iov = iov; | ||||
|   msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); | ||||
|   char cmsg[kControlMsgSize]; | ||||
|   my_memset(cmsg, 0, kControlMsgSize); | ||||
|   msg.msg_control = cmsg; | ||||
|   msg.msg_controllen = sizeof(cmsg); | ||||
|   virtual bool RequestDump(const void* blob, size_t blob_size) { | ||||
|     int fds[2]; | ||||
|     if (sys_pipe(fds) < 0) | ||||
|       return false; | ||||
|     static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); | ||||
| 
 | ||||
|   struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); | ||||
|   hdr->cmsg_level = SOL_SOCKET; | ||||
|   hdr->cmsg_type = SCM_RIGHTS; | ||||
|   hdr->cmsg_len = CMSG_LEN(sizeof(int)); | ||||
|   int* p = reinterpret_cast<int*>(CMSG_DATA(hdr)); | ||||
|   *p = fds[1]; | ||||
|     struct kernel_iovec iov; | ||||
|     iov.iov_base = const_cast<void*>(blob); | ||||
|     iov.iov_len = blob_size; | ||||
| 
 | ||||
|   ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); | ||||
|   sys_close(fds[1]); | ||||
|   if (ret <= 0) | ||||
|     return false; | ||||
|     struct kernel_msghdr msg = { 0 }; | ||||
|     msg.msg_iov = &iov; | ||||
|     msg.msg_iovlen = 1; | ||||
|     char cmsg[kControlMsgSize] = ""; | ||||
|     msg.msg_control = cmsg; | ||||
|     msg.msg_controllen = sizeof(cmsg); | ||||
| 
 | ||||
|   // wait for an ACK from the server
 | ||||
|   char b; | ||||
|   IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); | ||||
|     struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); | ||||
|     hdr->cmsg_level = SOL_SOCKET; | ||||
|     hdr->cmsg_type = SCM_RIGHTS; | ||||
|     hdr->cmsg_len = CMSG_LEN(sizeof(int)); | ||||
|     int* p = reinterpret_cast<int*>(CMSG_DATA(hdr)); | ||||
|     *p = fds[1]; | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
|     ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); | ||||
|     sys_close(fds[1]); | ||||
|     if (ret < 0) { | ||||
|       sys_close(fds[0]); | ||||
|       return false; | ||||
|     } | ||||
| 
 | ||||
| //static
 | ||||
| CrashGenerationClient* | ||||
| CrashGenerationClient::TryCreate(int server_fd) | ||||
| { | ||||
|   if (0 > server_fd) | ||||
|     // Wait for an ACK from the server.
 | ||||
|     char b; | ||||
|     IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); | ||||
|     sys_close(fds[0]); | ||||
| 
 | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|  private: | ||||
|   int server_fd_; | ||||
| 
 | ||||
|   DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl); | ||||
| }; | ||||
| 
 | ||||
| }  // namespace
 | ||||
| 
 | ||||
| // static
 | ||||
| CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) { | ||||
|   if (server_fd < 0) | ||||
|     return NULL; | ||||
|   return new CrashGenerationClient(server_fd); | ||||
|   return new CrashGenerationClientImpl(server_fd); | ||||
| } | ||||
| 
 | ||||
| } | ||||
| }  // namespace google_breakpad
 | ||||
|  | ||||
| @ -30,40 +30,36 @@ | ||||
| #ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ | ||||
| #define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ | ||||
| 
 | ||||
| #include "common/basictypes.h" | ||||
| 
 | ||||
| #include <stddef.h> | ||||
| 
 | ||||
| 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 { | ||||
| public: | ||||
|   ~CrashGenerationClient() | ||||
|   { | ||||
|   } | ||||
|  public: | ||||
|   CrashGenerationClient() {} | ||||
|   virtual ~CrashGenerationClient() {} | ||||
| 
 | ||||
|   // Request the crash server to generate a dump.  |blob| is a hack,
 | ||||
|   // see exception_handler.h and minidump_writer.h
 | ||||
|   //
 | ||||
|   // Return true if the dump was successful; false otherwise.
 | ||||
|   bool RequestDump(const void* blob, size_t blob_size); | ||||
|   // Request the crash server to generate a dump.  |blob| is an opaque
 | ||||
|   // CrashContext pointer from exception_handler.h.
 | ||||
|   // Returns true if the dump was successful; false otherwise.
 | ||||
|   virtual bool RequestDump(const void* blob, size_t blob_size) = 0; | ||||
| 
 | ||||
|   // 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.
 | ||||
|   // The returned CrashGenerationClient* is owned by the caller of
 | ||||
|   // this function.
 | ||||
|   static CrashGenerationClient* TryCreate(int server_fd); | ||||
| 
 | ||||
| private: | ||||
|   CrashGenerationClient(int server_fd) : server_fd_(server_fd) | ||||
|   { | ||||
|   } | ||||
| 
 | ||||
|   int server_fd_; | ||||
| 
 | ||||
|   // prevent copy construction and assignment
 | ||||
|   CrashGenerationClient(const CrashGenerationClient&); | ||||
|   CrashGenerationClient& operator=(const CrashGenerationClient&); | ||||
|  private: | ||||
|   DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient); | ||||
| }; | ||||
| 
 | ||||
| } // namespace google_breakpad
 | ||||
| }  // namespace google_breakpad
 | ||||
| 
 | ||||
| #endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
 | ||||
| #endif  // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
 | ||||
|  | ||||
| @ -51,116 +51,6 @@ | ||||
| 
 | ||||
| static const char kCommandQuit = 'x'; | ||||
| 
 | ||||
| static bool | ||||
| GetInodeForFileDescriptor(ino_t* inode_out, int fd) | ||||
| { | ||||
|   assert(inode_out); | ||||
| 
 | ||||
|   struct stat buf; | ||||
|   if (fstat(fd, &buf) < 0) | ||||
|     return false; | ||||
| 
 | ||||
|   if (!S_ISSOCK(buf.st_mode)) | ||||
|     return false; | ||||
| 
 | ||||
|   *inode_out = buf.st_ino; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| // expected prefix of the target of the /proc/self/fd/%d link for a socket
 | ||||
| static const char kSocketLinkPrefix[] = "socket:["; | ||||
| 
 | ||||
| // Parse a symlink in /proc/pid/fd/$x and return the inode number of the
 | ||||
| // socket.
 | ||||
| //   inode_out: (output) set to the inode number on success
 | ||||
| //   path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
 | ||||
| static bool | ||||
| GetInodeForProcPath(ino_t* inode_out, const char* path) | ||||
| { | ||||
|   assert(inode_out); | ||||
|   assert(path); | ||||
| 
 | ||||
|   char buf[PATH_MAX]; | ||||
|   if (!google_breakpad::SafeReadLink(path, buf)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   char* endptr; | ||||
|   const uint64_t inode_ul = | ||||
|       strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); | ||||
|   if (*endptr != ']') | ||||
|     return false; | ||||
| 
 | ||||
|   if (inode_ul == ULLONG_MAX) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   *inode_out = inode_ul; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) | ||||
| { | ||||
|   assert(pid_out); | ||||
|   bool already_found = false; | ||||
| 
 | ||||
|   DIR* proc = opendir("/proc"); | ||||
|   if (!proc) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   std::vector<pid_t> pids; | ||||
| 
 | ||||
|   struct dirent* dent; | ||||
|   while ((dent = readdir(proc))) { | ||||
|     char* endptr; | ||||
|     const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); | ||||
|     if (pid_ul == ULONG_MAX || '\0' != *endptr) | ||||
|       continue; | ||||
|     pids.push_back(pid_ul); | ||||
|   } | ||||
|   closedir(proc); | ||||
| 
 | ||||
|   for (std::vector<pid_t>::const_iterator | ||||
|        i = pids.begin(); i != pids.end(); ++i) { | ||||
|     const pid_t current_pid = *i; | ||||
|     char buf[PATH_MAX]; | ||||
|     snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); | ||||
|     DIR* fd = opendir(buf); | ||||
|     if (!fd) | ||||
|       continue; | ||||
| 
 | ||||
|     while ((dent = readdir(fd))) { | ||||
|       if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, | ||||
|                    dent->d_name) >= static_cast<int>(sizeof(buf))) { | ||||
|         continue; | ||||
|       } | ||||
| 
 | ||||
|       ino_t fd_inode; | ||||
|       if (GetInodeForProcPath(&fd_inode, buf) | ||||
|           && fd_inode == socket_inode) { | ||||
|         if (already_found) { | ||||
|           closedir(fd); | ||||
|           return false; | ||||
|         } | ||||
| 
 | ||||
|         already_found = true; | ||||
|         *pid_out = current_pid; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     closedir(fd); | ||||
|   } | ||||
| 
 | ||||
|   return already_found; | ||||
| } | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| CrashGenerationServer::CrashGenerationServer( | ||||
| @ -349,7 +239,7 @@ CrashGenerationServer::ClientEvent(short revents) | ||||
|         // A nasty process could try and send us too many descriptors and
 | ||||
|         // force a leak.
 | ||||
|         for (unsigned i = 0; i < num_fds; ++i) | ||||
|           HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i])); | ||||
|           close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]); | ||||
|         return true; | ||||
|       } else { | ||||
|         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 (signal_fd) | ||||
|       HANDLE_EINTR(close(signal_fd)); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   // Kernel bug workaround (broken in 2.6.30 at least):
 | ||||
|   // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
 | ||||
|   // namespaces. Thus |crashing_pid| might be garbage from our point of view.
 | ||||
|   // In the future we can remove this workaround, but we have to wait a couple
 | ||||
|   // of years to be sure that it's worked its way out into the world.
 | ||||
| 
 | ||||
|   ino_t inode_number; | ||||
|   if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) { | ||||
|     HANDLE_EINTR(close(signal_fd)); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { | ||||
|     HANDLE_EINTR(close(signal_fd)); | ||||
|       close(signal_fd); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
| @ -391,7 +264,7 @@ CrashGenerationServer::ClientEvent(short revents) | ||||
|   if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), | ||||
|                                       crashing_pid, crash_context, | ||||
|                                       kCrashContextSize)) { | ||||
|     HANDLE_EINTR(close(signal_fd)); | ||||
|     close(signal_fd); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
| @ -402,15 +275,8 @@ CrashGenerationServer::ClientEvent(short revents) | ||||
|   } | ||||
| 
 | ||||
|   // Send the done signal to the process: it can exit now.
 | ||||
|   memset(&msg, 0, sizeof(msg)); | ||||
|   struct iovec done_iov; | ||||
|   done_iov.iov_base = const_cast<char*>("\x42"); | ||||
|   done_iov.iov_len = 1; | ||||
|   msg.msg_iov = &done_iov; | ||||
|   msg.msg_iovlen = 1; | ||||
| 
 | ||||
|   HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); | ||||
|   HANDLE_EINTR(close(signal_fd)); | ||||
|   // (Closing this will make the child's sys_read unblock and return 0.)
 | ||||
|   close(signal_fd); | ||||
| 
 | ||||
|   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 <fcntl.h> | ||||
| #include <linux/limits.h> | ||||
| #include <pthread.h> | ||||
| #include <sched.h> | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| @ -86,9 +87,11 @@ | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/basictypes.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/memory.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/minidump_writer.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
 | ||||
|   // the alternative stack. Ensure that the size of the alternative stack is
 | ||||
|   // 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
 | ||||
|   // one is too small.
 | ||||
|   if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || | ||||
|       old_stack.ss_size < kSigStackSize) { | ||||
|     new_stack.ss_sp = malloc(kSigStackSize); | ||||
|     new_stack.ss_sp = calloc(1, kSigStackSize); | ||||
|     new_stack.ss_size = kSigStackSize; | ||||
| 
 | ||||
|     if (sys_sigaltstack(&new_stack, NULL) == -1) { | ||||
| @ -185,13 +188,13 @@ void RestoreAlternateStackLocked() { | ||||
|   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
 | ||||
| // which holds the stack.
 | ||||
| std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL; | ||||
| pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = | ||||
|     PTHREAD_MUTEX_INITIALIZER; | ||||
| }  // namespace
 | ||||
| 
 | ||||
| // Runs before crashing: normal context.
 | ||||
| ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, | ||||
| @ -208,31 +211,34 @@ ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, | ||||
|   if (server_fd >= 0) | ||||
|     crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); | ||||
| 
 | ||||
|   if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) | ||||
|   if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && | ||||
|       !minidump_descriptor_.IsMicrodumpOnConsole()) | ||||
|     minidump_descriptor_.UpdatePath(); | ||||
| 
 | ||||
|   pthread_mutex_lock(&handler_stack_mutex_); | ||||
|   if (!handler_stack_) | ||||
|     handler_stack_ = new std::vector<ExceptionHandler*>; | ||||
|   pthread_mutex_lock(&g_handler_stack_mutex_); | ||||
|   if (!g_handler_stack_) | ||||
|     g_handler_stack_ = new std::vector<ExceptionHandler*>; | ||||
|   if (install_handler) { | ||||
|     InstallAlternateStackLocked(); | ||||
|     InstallHandlersLocked(); | ||||
|   } | ||||
|   handler_stack_->push_back(this); | ||||
|   pthread_mutex_unlock(&handler_stack_mutex_); | ||||
|   g_handler_stack_->push_back(this); | ||||
|   pthread_mutex_unlock(&g_handler_stack_mutex_); | ||||
| } | ||||
| 
 | ||||
| // Runs before crashing: normal context.
 | ||||
| ExceptionHandler::~ExceptionHandler() { | ||||
|   pthread_mutex_lock(&handler_stack_mutex_); | ||||
|   pthread_mutex_lock(&g_handler_stack_mutex_); | ||||
|   std::vector<ExceptionHandler*>::iterator handler = | ||||
|       std::find(handler_stack_->begin(), handler_stack_->end(), this); | ||||
|   handler_stack_->erase(handler); | ||||
|   if (handler_stack_->empty()) { | ||||
|       std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this); | ||||
|   g_handler_stack_->erase(handler); | ||||
|   if (g_handler_stack_->empty()) { | ||||
|     delete g_handler_stack_; | ||||
|     g_handler_stack_ = NULL; | ||||
|     RestoreAlternateStackLocked(); | ||||
|     RestoreHandlersLocked(); | ||||
|   } | ||||
|   pthread_mutex_unlock(&handler_stack_mutex_); | ||||
|   pthread_mutex_unlock(&g_handler_stack_mutex_); | ||||
| } | ||||
| 
 | ||||
| // Runs before crashing: normal context.
 | ||||
| @ -292,7 +298,7 @@ void ExceptionHandler::RestoreHandlersLocked() { | ||||
| // static
 | ||||
| void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { | ||||
|   // All the exception signals are blocked at this point.
 | ||||
|   pthread_mutex_lock(&handler_stack_mutex_); | ||||
|   pthread_mutex_lock(&g_handler_stack_mutex_); | ||||
| 
 | ||||
|   // Sometimes, Breakpad runs inside a process where some other buggy code
 | ||||
|   // 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.
 | ||||
|       signal(sig, SIG_DFL); | ||||
|     } | ||||
|     pthread_mutex_unlock(&handler_stack_mutex_); | ||||
|     pthread_mutex_unlock(&g_handler_stack_mutex_); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   bool handled = false; | ||||
|   for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) { | ||||
|     handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc); | ||||
|   for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) { | ||||
|     handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc); | ||||
|   } | ||||
| 
 | ||||
|   // 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(); | ||||
|   } | ||||
| 
 | ||||
|   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().
 | ||||
|     // 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 we failed to kill ourselves (e.g. because a sandbox disallows us
 | ||||
|       // 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 || | ||||
|       info->si_code == SI_TKILL; | ||||
|   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; | ||||
|   // 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.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.
 | ||||
|   // 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; | ||||
|   if (uc_ptr->uc_mcontext.fpregs) { | ||||
|     memcpy(&context.float_state, | ||||
| @ -432,9 +450,11 @@ bool ExceptionHandler::GenerateDump(CrashContext *context) { | ||||
|   if (IsOutOfProcess()) | ||||
|     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; | ||||
|   uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); | ||||
|   uint8_t* stack = reinterpret_cast<uint8_t*>(allocator.Alloc(kChildStackSize)); | ||||
|   if (!stack) | ||||
|     return false; | ||||
|   // 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
 | ||||
|   // can do this. Create a pipe here which we can use to block the
 | ||||
|   // cloned process after creating it, until we have explicitly enabled ptrace
 | ||||
|   if(sys_pipe(fdes) == -1) { | ||||
|   if (sys_pipe(fdes) == -1) { | ||||
|     // Creating the pipe failed. We'll log an error but carry on anyway,
 | ||||
|     // as we'll probably still get a useful crash report. All that will happen
 | ||||
|     // is the write() and read() calls will fail with EBADF
 | ||||
|     static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
 | ||||
|                                        sys_pipe failed:"; | ||||
|     static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump " | ||||
|                                       "sys_pipe failed:"; | ||||
|     logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); | ||||
|     logger::write(strerror(errno), strlen(strerror(errno))); | ||||
|     logger::write("\n", 1); | ||||
| 
 | ||||
|     // Ensure fdes[0] and fdes[1] are invalid file descriptors.
 | ||||
|     fdes[0] = fdes[1] = -1; | ||||
|   } | ||||
| 
 | ||||
|   const pid_t child = sys_clone( | ||||
|       ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, | ||||
|       &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
 | ||||
|   sys_prctl(PR_SET_PTRACER, child); | ||||
|   sys_prctl(PR_SET_PTRACER, child, 0, 0, 0); | ||||
|   SendContinueSignalToChild(); | ||||
|   do { | ||||
|     r = sys_waitpid(child, &status, __WALL); | ||||
|   } while (r == -1 && errno == EINTR); | ||||
|   int status; | ||||
|   const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL)); | ||||
| 
 | ||||
|   sys_close(fdes[0]); | ||||
|   sys_close(fdes[1]); | ||||
| @ -496,9 +522,9 @@ void ExceptionHandler::SendContinueSignalToChild() { | ||||
|   static const char okToContinueMessage = 'a'; | ||||
|   int r; | ||||
|   r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); | ||||
|   if(r == -1) { | ||||
|     static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
 | ||||
|                                sys_write failed:"; | ||||
|   if (r == -1) { | ||||
|     static const char msg[] = "ExceptionHandler::SendContinueSignalToChild " | ||||
|                               "sys_write failed:"; | ||||
|     logger::write(msg, sizeof(msg) - 1); | ||||
|     logger::write(strerror(errno), strlen(strerror(errno))); | ||||
|     logger::write("\n", 1); | ||||
| @ -511,9 +537,9 @@ void ExceptionHandler::WaitForContinueSignal() { | ||||
|   int r; | ||||
|   char receivedMessage; | ||||
|   r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); | ||||
|   if(r == -1) { | ||||
|     static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
 | ||||
|                                sys_read failed:"; | ||||
|   if (r == -1) { | ||||
|     static const char msg[] = "ExceptionHandler::WaitForContinueSignal " | ||||
|                               "sys_read failed:"; | ||||
|     logger::write(msg, sizeof(msg) - 1); | ||||
|     logger::write(strerror(errno), strlen(strerror(errno))); | ||||
|     logger::write("\n", 1); | ||||
| @ -524,6 +550,12 @@ void ExceptionHandler::WaitForContinueSignal() { | ||||
| // Runs on the cloned process.
 | ||||
| bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, | ||||
|                               size_t context_size) { | ||||
|   if (minidump_descriptor_.IsMicrodumpOnConsole()) { | ||||
|     return google_breakpad::WriteMicrodump(crashing_process, | ||||
|                                            context, | ||||
|                                            context_size, | ||||
|                                            mapping_list_); | ||||
|   } | ||||
|   if (minidump_descriptor_.IsFD()) { | ||||
|     return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), | ||||
|                                           minidump_descriptor_.size_limit(), | ||||
| @ -559,7 +591,8 @@ bool ExceptionHandler::WriteMinidump(const string& dump_path, | ||||
| __attribute__((optimize("no-omit-frame-pointer"))) | ||||
| #endif | ||||
| 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
 | ||||
|     // and new files are created for each minidump.  This is done before the
 | ||||
|     // 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
 | ||||
|     // previous minidump info.
 | ||||
|     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.
 | ||||
|   sys_prctl(PR_SET_DUMPABLE, 1); | ||||
|   sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); | ||||
| 
 | ||||
|   CrashContext context; | ||||
|   int getcontext_result = getcontext(&context.context); | ||||
| @ -602,7 +635,7 @@ bool ExceptionHandler::WriteMinidump() { | ||||
|   } | ||||
| #endif | ||||
| 
 | ||||
| #if !defined(__ARM_EABI__) | ||||
| #if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__) | ||||
|   // FPU state is not part of ARM EABI ucontext_t.
 | ||||
|   memcpy(&context.float_state, context.context.uc_mcontext.fpregs, | ||||
|          sizeof(context.float_state)); | ||||
| @ -621,6 +654,12 @@ bool ExceptionHandler::WriteMinidump() { | ||||
| #elif defined(__arm__) | ||||
|   context.siginfo.si_addr = | ||||
|       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 | ||||
| #error "This code has not been ported to your platform yet." | ||||
| #endif | ||||
|  | ||||
| @ -30,15 +30,13 @@ | ||||
| #ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ | ||||
| #define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ | ||||
| 
 | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include <pthread.h> | ||||
| #include <signal.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/ucontext.h> | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #include "client/linux/crash_generation/crash_generation_client.h" | ||||
| #include "client/linux/handler/minidump_descriptor.h" | ||||
| #include "client/linux/minidump_writer/minidump_writer.h" | ||||
| @ -129,7 +127,7 @@ class ExceptionHandler { | ||||
|   ExceptionHandler(const MinidumpDescriptor& descriptor, | ||||
|                    FilterCallback filter, | ||||
|                    MinidumpCallback callback, | ||||
|                    void *callback_context, | ||||
|                    void* callback_context, | ||||
|                    bool install_handler, | ||||
|                    const int server_fd); | ||||
|   ~ExceptionHandler(); | ||||
| @ -146,6 +144,10 @@ class ExceptionHandler { | ||||
|     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
 | ||||
|   // state independently of a crash.
 | ||||
|   // Returns true on success.
 | ||||
| @ -190,15 +192,17 @@ class ExceptionHandler { | ||||
|     siginfo_t siginfo; | ||||
|     pid_t tid;  // the crashing thread.
 | ||||
|     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.
 | ||||
|     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 | ||||
|   }; | ||||
| 
 | ||||
|   // Returns whether out-of-process dump generation is used or not.
 | ||||
|   bool IsOutOfProcess() const { | ||||
|       return crash_generation_client_.get() != NULL; | ||||
|     return crash_generation_client_.get() != NULL; | ||||
|   } | ||||
| 
 | ||||
|   // Add information about a memory mapping. This can be used if
 | ||||
| @ -219,6 +223,10 @@ class ExceptionHandler { | ||||
| 
 | ||||
|   // Force signal handling for the specified signal.
 | ||||
|   bool SimulateSignalDelivery(int sig); | ||||
| 
 | ||||
|   // Report a crash signal from an SA_SIGINFO signal handler.
 | ||||
|   bool HandleSignal(int sig, siginfo_t* info, void* uc); | ||||
| 
 | ||||
|  private: | ||||
|   // Save the old signal handlers and install new ones.
 | ||||
|   static bool InstallHandlersLocked(); | ||||
| @ -231,7 +239,6 @@ class ExceptionHandler { | ||||
|   void WaitForContinueSignal(); | ||||
| 
 | ||||
|   static void SignalHandler(int sig, siginfo_t* info, void* uc); | ||||
|   bool HandleSignal(int sig, siginfo_t* info, void* uc); | ||||
|   static int ThreadEntry(void* arg); | ||||
|   bool DoDump(pid_t crashing_process, const void* context, | ||||
|               size_t context_size); | ||||
| @ -244,13 +251,11 @@ class ExceptionHandler { | ||||
| 
 | ||||
|   MinidumpDescriptor minidump_descriptor_; | ||||
| 
 | ||||
|   HandlerCallback crash_handler_; | ||||
| 
 | ||||
|   // The global exception handler stack. This is need becuase there may exist
 | ||||
|   // multiple ExceptionHandler instances in a process. Each will have itself
 | ||||
|   // registered in this stack.
 | ||||
|   static std::vector<ExceptionHandler*> *handler_stack_; | ||||
|   static pthread_mutex_t handler_stack_mutex_; | ||||
|   // Must be volatile. The compiler is unaware of the code which runs in
 | ||||
|   // the signal handler which reads this variable. Without volatile the
 | ||||
|   // compiler is free to optimise away writes to this variable which it
 | ||||
|   // believes are never read.
 | ||||
|   volatile HandlerCallback crash_handler_; | ||||
| 
 | ||||
|   // 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
 | ||||
|  | ||||
| @ -35,6 +35,9 @@ | ||||
| #include <sys/socket.h> | ||||
| #include <sys/uio.h> | ||||
| #include <sys/wait.h> | ||||
| #if defined(__mips__) | ||||
| #include <sys/cachectl.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| @ -55,7 +58,7 @@ using namespace google_breakpad; | ||||
| namespace { | ||||
| 
 | ||||
| // 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) { | ||||
| #if defined(__arm__) | ||||
|   long begin = reinterpret_cast<long>(memory); | ||||
| @ -72,6 +75,18 @@ void FlushInstructionCache(const char* memory, uint32_t memory_size) { | ||||
| # else | ||||
| #   error "Your operating system is not supported yet" | ||||
| # 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 | ||||
| } | ||||
| 
 | ||||
| @ -176,6 +191,22 @@ static bool DoneCallback(const MinidumpDescriptor& descriptor, | ||||
|   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) { | ||||
|   AutoTempDir temp_dir; | ||||
|   int fds[2] = {0}; | ||||
| @ -202,7 +233,7 @@ void ChildCrash(bool use_fd) { | ||||
|                                            true, -1)); | ||||
|       } | ||||
|       // Crash with the exception handler in scope.
 | ||||
|       *reinterpret_cast<volatile int*>(NULL) = 0; | ||||
|       DoNullPointerDereference(); | ||||
|     } | ||||
|   } | ||||
|   if (!use_fd) | ||||
| @ -227,6 +258,8 @@ TEST(ExceptionHandlerTest, ChildCrashWithFD) { | ||||
|   ASSERT_NO_FATAL_FAILURE(ChildCrash(true)); | ||||
| } | ||||
| 
 | ||||
| #endif  // !ADDRESS_SANITIZER
 | ||||
| 
 | ||||
| static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor, | ||||
|                                     void* context, | ||||
|                                     bool succeeded) { | ||||
| @ -268,13 +301,15 @@ static bool InstallRaiseSIGKILL() { | ||||
|   return sigaction(SIGSEGV, &sa, NULL) != -1; | ||||
| } | ||||
| 
 | ||||
| #ifndef ADDRESS_SANITIZER | ||||
| 
 | ||||
| static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter, | ||||
|                                ExceptionHandler::MinidumpCallback done, | ||||
|                                string path) { | ||||
|   ExceptionHandler handler( | ||||
|       MinidumpDescriptor(path), filter, done, NULL, true, -1); | ||||
|   // Crash with the exception handler in scope.
 | ||||
|   *reinterpret_cast<volatile int*>(NULL) = 0; | ||||
|   DoNullPointerDereference(); | ||||
| } | ||||
| 
 | ||||
| TEST(ExceptionHandlerTest, RedeliveryOnFilterCallbackFalse) { | ||||
| @ -365,7 +400,7 @@ TEST(ExceptionHandlerTest, RedeliveryOnBadSignalHandlerFlag) { | ||||
|               reinterpret_cast<void*>(SIG_ERR)); | ||||
| 
 | ||||
|     // Crash with the exception handler in scope.
 | ||||
|     *reinterpret_cast<volatile int*>(NULL) = 0; | ||||
|     DoNullPointerDereference(); | ||||
|   } | ||||
|   // SIGKILL means Breakpad's signal handler didn't crash.
 | ||||
|   ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); | ||||
| @ -435,6 +470,18 @@ TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) { | ||||
|   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
 | ||||
| // to the dump as a MinidumpMemoryRegion.
 | ||||
| TEST(ExceptionHandlerTest, InstructionPointerMemory) { | ||||
| @ -446,8 +493,6 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { | ||||
|   // data from the minidump afterwards.
 | ||||
|   const uint32_t kMemorySize = 256;  // bytes
 | ||||
|   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(); | ||||
|   if (child == 0) { | ||||
| @ -469,7 +514,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { | ||||
|     // Write some instructions that will crash. Put them in the middle
 | ||||
|     // of the block of memory, because the minidump should contain 128
 | ||||
|     // bytes on either side of the instruction pointer.
 | ||||
|     memcpy(memory + kOffset, instructions, sizeof(instructions)); | ||||
|     memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); | ||||
|     FlushInstructionCache(memory, kMemorySize); | ||||
| 
 | ||||
|     // Now execute the instructions, which should crash.
 | ||||
| @ -517,12 +562,13 @@ TEST(ExceptionHandlerTest, InstructionPointerMemory) { | ||||
|   ASSERT_TRUE(bytes); | ||||
| 
 | ||||
|   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(suffix_bytes, 0, sizeof(suffix_bytes)); | ||||
|   EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,  | ||||
|                      sizeof(kIllegalInstruction)) == 0); | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), | ||||
|                      suffix_bytes, sizeof(suffix_bytes)) == 0); | ||||
| 
 | ||||
|   unlink(minidump_path.c_str()); | ||||
| @ -539,8 +585,6 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { | ||||
|   // data from the minidump afterwards.
 | ||||
|   const uint32_t kMemorySize = 256;  // bytes
 | ||||
|   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(); | ||||
|   if (child == 0) { | ||||
| @ -562,7 +606,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { | ||||
|     // Write some instructions that will crash. Put them in the middle
 | ||||
|     // of the block of memory, because the minidump should contain 128
 | ||||
|     // bytes on either side of the instruction pointer.
 | ||||
|     memcpy(memory + kOffset, instructions, sizeof(instructions)); | ||||
|     memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); | ||||
|     FlushInstructionCache(memory, kMemorySize); | ||||
| 
 | ||||
|     // Now execute the instructions, which should crash.
 | ||||
| @ -609,10 +653,11 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { | ||||
|   const uint8_t* bytes = region->GetMemory(); | ||||
|   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)); | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction,  | ||||
|                      sizeof(kIllegalInstruction)) == 0); | ||||
|   EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), | ||||
|                      suffix_bytes, sizeof(suffix_bytes)) == 0); | ||||
|   unlink(minidump_path.c_str()); | ||||
| } | ||||
| @ -630,9 +675,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { | ||||
|   // if a smaller size is requested, and this test wants to
 | ||||
|   // test the upper bound of the memory range.
 | ||||
|   const uint32_t kMemorySize = 4096;  // bytes
 | ||||
|   // This crashes with SIGILL on x86/x86-64/arm.
 | ||||
|   const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; | ||||
|   const int kOffset = kMemorySize - sizeof(instructions); | ||||
|   const int kOffset = kMemorySize - sizeof(kIllegalInstruction); | ||||
| 
 | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
| @ -654,7 +697,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { | ||||
|     // Write some instructions that will crash. Put them in the middle
 | ||||
|     // of the block of memory, because the minidump should contain 128
 | ||||
|     // bytes on either side of the instruction pointer.
 | ||||
|     memcpy(memory + kOffset, instructions, sizeof(instructions)); | ||||
|     memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); | ||||
|     FlushInstructionCache(memory, kMemorySize); | ||||
| 
 | ||||
|     // Now execute the instructions, which should crash.
 | ||||
| @ -697,7 +740,7 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { | ||||
|   ASSERT_TRUE(region); | ||||
| 
 | ||||
|   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(); | ||||
|   ASSERT_TRUE(bytes); | ||||
| 
 | ||||
| @ -705,15 +748,11 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { | ||||
|   memset(prefix_bytes, 0, sizeof(prefix_bytes)); | ||||
|   EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); | ||||
|   EXPECT_TRUE(memcmp(bytes + kPrefixSize, | ||||
|                      instructions, sizeof(instructions)) == 0); | ||||
|                      kIllegalInstruction, sizeof(kIllegalInstruction)) == 0); | ||||
| 
 | ||||
|   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 | ||||
| 
 | ||||
| // Ensure that an extra memory block doesn't get added when the instruction
 | ||||
| @ -760,7 +799,8 @@ TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { | ||||
| 
 | ||||
|   unlink(minidump_path.c_str()); | ||||
| } | ||||
| #endif // !ADDRESS_SANITIZER
 | ||||
| 
 | ||||
| #endif  // !ADDRESS_SANITIZER
 | ||||
| 
 | ||||
| // Test that anonymous memory maps can be annotated with names and IDs.
 | ||||
| TEST(ExceptionHandlerTest, ModuleInfo) { | ||||
| @ -881,6 +921,8 @@ CrashHandler(const void* crash_context, size_t crash_context_size, | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| #ifndef ADDRESS_SANITIZER | ||||
| 
 | ||||
| TEST(ExceptionHandlerTest, ExternalDumper) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); | ||||
| @ -894,7 +936,7 @@ TEST(ExceptionHandlerTest, ExternalDumper) { | ||||
|     ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL, | ||||
|                              reinterpret_cast<void*>(fds[1]), true, -1); | ||||
|     handler.set_crash_handler(CrashHandler); | ||||
|     *reinterpret_cast<volatile int*>(NULL) = 0; | ||||
|     DoNullPointerDereference(); | ||||
|   } | ||||
|   close(fds[1]); | ||||
|   struct msghdr msg = {0}; | ||||
| @ -953,6 +995,8 @@ TEST(ExceptionHandlerTest, ExternalDumper) { | ||||
|   unlink(templ.c_str()); | ||||
| } | ||||
| 
 | ||||
| #endif  // !ADDRESS_SANITIZER
 | ||||
| 
 | ||||
| TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) { | ||||
|   AutoTempDir temp_dir; | ||||
|   ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, | ||||
|  | ||||
| @ -35,8 +35,12 @@ | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| //static
 | ||||
| const MinidumpDescriptor::MicrodumpOnConsole kMicrodumpOnConsole = {}; | ||||
| 
 | ||||
| MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor) | ||||
|     : fd_(descriptor.fd_), | ||||
|     : mode_(descriptor.mode_), | ||||
|       fd_(descriptor.fd_), | ||||
|       directory_(descriptor.directory_), | ||||
|       c_path_(NULL), | ||||
|       size_limit_(descriptor.size_limit_) { | ||||
| @ -50,6 +54,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=( | ||||
|     const MinidumpDescriptor& descriptor) { | ||||
|   assert(descriptor.path_.empty()); | ||||
| 
 | ||||
|   mode_ = descriptor.mode_; | ||||
|   fd_ = descriptor.fd_; | ||||
|   directory_ = descriptor.directory_; | ||||
|   path_.clear(); | ||||
| @ -63,7 +68,7 @@ MinidumpDescriptor& MinidumpDescriptor::operator=( | ||||
| } | ||||
| 
 | ||||
| void MinidumpDescriptor::UpdatePath() { | ||||
|   assert(fd_ == -1 && !directory_.empty()); | ||||
|   assert(mode_ == kWriteMinidumpToFile && !directory_.empty()); | ||||
| 
 | ||||
|   GUID guid; | ||||
|   char guid_str[kGUIDStringLength + 1]; | ||||
|  | ||||
| @ -37,18 +37,25 @@ | ||||
| 
 | ||||
| #include "common/using_std_string.h" | ||||
| 
 | ||||
| // The MinidumpDescriptor describes how to access a minidump: it can contain
 | ||||
| // either a file descriptor or a path.
 | ||||
| // Note that when using files, it is created with the path to a directory.
 | ||||
| // The actual path where the minidump is generated is created by this class.
 | ||||
| // This class describes how a crash dump should be generated, either:
 | ||||
| // - Writing a full minidump to a file in a given directory (the actual path,
 | ||||
| //   inside the directory, is determined by this class).
 | ||||
| // - Writing a full minidump to a given fd.
 | ||||
| // - Writing a reduced microdump to the console (logcat on Android).
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| class MinidumpDescriptor { | ||||
|  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) | ||||
|       : fd_(-1), | ||||
|       : mode_(kWriteMinidumpToFile), | ||||
|         fd_(-1), | ||||
|         directory_(directory), | ||||
|         c_path_(NULL), | ||||
|         size_limit_(-1) { | ||||
| @ -56,16 +63,24 @@ class MinidumpDescriptor { | ||||
|   } | ||||
| 
 | ||||
|   explicit MinidumpDescriptor(int fd) | ||||
|       : fd_(fd), | ||||
|       : mode_(kWriteMinidumpToFd), | ||||
|         fd_(fd), | ||||
|         c_path_(NULL), | ||||
|         size_limit_(-1) { | ||||
|     assert(fd != -1); | ||||
|   } | ||||
| 
 | ||||
|   explicit MinidumpDescriptor(const MicrodumpOnConsole&) | ||||
|       : mode_(kWriteMicrodumpToConsole), | ||||
|         fd_(-1), | ||||
|         size_limit_(-1) {} | ||||
| 
 | ||||
|   explicit MinidumpDescriptor(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_; } | ||||
| 
 | ||||
| @ -73,6 +88,10 @@ class MinidumpDescriptor { | ||||
| 
 | ||||
|   const char* path() const { return c_path_; } | ||||
| 
 | ||||
|   bool IsMicrodumpOnConsole() const { | ||||
|     return mode_ == kWriteMicrodumpToConsole; | ||||
|   } | ||||
| 
 | ||||
|   // Updates the path so it is unique.
 | ||||
|   // Should be called from a normal context: this methods uses the heap.
 | ||||
|   void UpdatePath(); | ||||
| @ -81,6 +100,16 @@ class MinidumpDescriptor { | ||||
|   void set_size_limit(off_t limit) { size_limit_ = limit; } | ||||
| 
 | ||||
|  private: | ||||
|   enum DumpMode { | ||||
|     kUninitialized = 0, | ||||
|     kWriteMinidumpToFile, | ||||
|     kWriteMinidumpToFd, | ||||
|     kWriteMicrodumpToConsole | ||||
|   }; | ||||
| 
 | ||||
|   // Specifies the dump mode (see DumpMode).
 | ||||
|   DumpMode mode_; | ||||
| 
 | ||||
|   // The file descriptor where the minidump is generated.
 | ||||
|   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 "client/linux/minidump_writer/cpu_set.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/tests/auto_testfile.h" | ||||
| #include "common/linux/tests/auto_testfile.h" | ||||
| 
 | ||||
| using namespace google_breakpad; | ||||
| 
 | ||||
|  | ||||
| @ -33,8 +33,7 @@ | ||||
| 
 | ||||
| #include "client/linux/minidump_writer/line_reader.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/tests/auto_testfile.h" | ||||
| #include "common/linux/tests/auto_testfile.h" | ||||
| 
 | ||||
| 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)); | ||||
| #elif defined(__ARM_EABI__) | ||||
|   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 | ||||
| #error "This code hasn't been ported to your platform yet." | ||||
| #endif | ||||
| @ -119,7 +124,7 @@ bool LinuxCoreDumper::ThreadsResume() { | ||||
| } | ||||
| 
 | ||||
| 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"); | ||||
|     return false; | ||||
|   } | ||||
| @ -183,7 +188,19 @@ bool LinuxCoreDumper::EnumerateThreads() { | ||||
|         memset(&info, 0, sizeof(ThreadInfo)); | ||||
|         info.tgid = status->pr_pgrp; | ||||
|         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)); | ||||
| #endif | ||||
|         if (first_thread) { | ||||
|           crash_thread_ = pid; | ||||
|           crash_signal_ = status->pr_info.si_signo; | ||||
|  | ||||
| @ -74,18 +74,23 @@ TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { | ||||
|   const unsigned kCrashThread = 1; | ||||
|   const int kCrashSignal = SIGABRT; | ||||
|   pid_t child_pid; | ||||
|   // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
 | ||||
|   // CrashGenerator is identified and fixed.
 | ||||
|   if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, | ||||
|                                         kCrashSignal, &child_pid)) { | ||||
|     fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " | ||||
|             "is skipped due to no core dump generated\n"); | ||||
|     return; | ||||
|   } | ||||
|   ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, | ||||
|                                                kCrashSignal, &child_pid)); | ||||
| 
 | ||||
|   const string core_file = crash_generator.GetCoreFilePath(); | ||||
|   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()); | ||||
| 
 | ||||
|   EXPECT_TRUE(dumper.Init()); | ||||
| 
 | ||||
|   EXPECT_TRUE(dumper.IsPostMortem()); | ||||
|  | ||||
| @ -38,12 +38,14 @@ | ||||
| #include "client/linux/minidump_writer/linux_dumper.h" | ||||
| 
 | ||||
| #include <assert.h> | ||||
| #include <elf.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
| #include <stddef.h> | ||||
| #include <string.h> | ||||
| 
 | ||||
| #include "client/linux/minidump_writer/line_reader.h" | ||||
| #include "common/linux/elfutils.h" | ||||
| #include "common/linux/file_id.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/linux/memory_mapped_file.h" | ||||
| @ -52,6 +54,7 @@ | ||||
| 
 | ||||
| static const char kMappedFileUnsafePrefix[] = "/dev/"; | ||||
| static const char kDeletedSuffix[] = " (deleted)"; | ||||
| static const char kReservedFlags[] = " ---p"; | ||||
| 
 | ||||
| inline static bool IsMappedFileOpenUnsafe( | ||||
|     const google_breakpad::MappingInfo& mapping) { | ||||
| @ -73,10 +76,13 @@ LinuxDumper::LinuxDumper(pid_t pid) | ||||
|     : pid_(pid), | ||||
|       crash_address_(0), | ||||
|       crash_signal_(0), | ||||
|       crash_thread_(0), | ||||
|       crash_thread_(pid), | ||||
|       threads_(&allocator_, 8), | ||||
|       mappings_(&allocator_), | ||||
|       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() { | ||||
| @ -90,8 +96,7 @@ bool | ||||
| LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, | ||||
|                                          bool member, | ||||
|                                          unsigned int mapping_id, | ||||
|                                          uint8_t identifier[sizeof(MDGUID)]) | ||||
| { | ||||
|                                          uint8_t identifier[sizeof(MDGUID)]) { | ||||
|   assert(!member || mapping_id < mappings_.size()); | ||||
|   my_memset(identifier, 0, sizeof(MDGUID)); | ||||
|   if (IsMappedFileOpenUnsafe(mapping)) | ||||
| @ -113,15 +118,16 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, | ||||
| 
 | ||||
|   char filename[NAME_MAX]; | ||||
|   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; | ||||
|   } | ||||
|   my_memcpy(filename, mapping.name, filename_len); | ||||
|   filename[filename_len] = '\0'; | ||||
|   bool filename_modified = HandleDeletedFileInMapping(filename); | ||||
| 
 | ||||
|   MemoryMappedFile mapped_file(filename); | ||||
|   if (!mapped_file.data())  // Should probably check if size >= ElfW(Ehdr)?
 | ||||
|   MemoryMappedFile mapped_file(filename, mapping.offset); | ||||
|   if (!mapped_file.data() || mapped_file.size() < SELFMAG) | ||||
|     return false; | ||||
| 
 | ||||
|   bool success = | ||||
| @ -134,6 +140,120 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, | ||||
|   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() { | ||||
|   char auxv_path[NAME_MAX]; | ||||
|   if (!BuildProcPath(auxv_path, pid_, "auxv")) { | ||||
| @ -193,6 +313,7 @@ bool LinuxDumper::EnumerateMappings() { | ||||
|     if (*i1 == '-') { | ||||
|       const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); | ||||
|       if (*i2 == ' ') { | ||||
|         bool exec = (*(i2 + 3) == 'x'); | ||||
|         const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); | ||||
|         if (*i3 == ' ') { | ||||
|           const char* name = NULL; | ||||
| @ -216,11 +337,29 @@ bool LinuxDumper::EnumerateMappings() { | ||||
|               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; | ||||
|           my_memset(module, 0, sizeof(MappingInfo)); | ||||
|           module->start_addr = start_addr; | ||||
|           module->size = end_addr - start_addr; | ||||
|           module->offset = offset; | ||||
|           module->exec = exec; | ||||
|           if (name != NULL) { | ||||
|             const unsigned l = my_strlen(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); | ||||
|   if (!mapping) | ||||
|     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 = | ||||
|       static_cast<ptrdiff_t>(mapping->size) - offset; | ||||
|   *stack_len = distance_to_end > kStackToCapture ? | ||||
|  | ||||
| @ -44,19 +44,17 @@ | ||||
| #include <sys/types.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 "google_breakpad/common/minidump_format.h" | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| #if defined(__i386) || defined(__x86_64) | ||||
| typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; | ||||
| #endif | ||||
| 
 | ||||
| // Typedef for our parsing of the auxv variables in /proc/pid/auxv.
 | ||||
| #if defined(__i386) || defined(__ARM_EABI__) | ||||
| #if defined(__i386) || defined(__ARM_EABI__) || defined(__mips__) | ||||
| typedef Elf32_auxv_t elf_aux_entry; | ||||
| #elif defined(__x86_64) | ||||
| #elif defined(__x86_64) || defined(__aarch64__) | ||||
| typedef Elf64_auxv_t elf_aux_entry; | ||||
| #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!
 | ||||
| const char kLinuxGateLibraryName[] = "linux-gate.so"; | ||||
| 
 | ||||
| // We produce one of these structures for each thread in the crashed process.
 | ||||
| struct ThreadInfo { | ||||
|   pid_t tgid;   // thread group id
 | ||||
|   pid_t ppid;   // parent process
 | ||||
| 
 | ||||
|   uintptr_t stack_pointer;  // thread stack pointer
 | ||||
| 
 | ||||
| 
 | ||||
| #if defined(__i386) || defined(__x86_64) | ||||
|   user_regs_struct regs; | ||||
|   user_fpregs_struct fpregs; | ||||
|   static const unsigned kNumDebugRegisters = 8; | ||||
|   debugreg_t dregs[8]; | ||||
| #if defined(__i386) | ||||
|   user_fpxregs_struct fpxregs; | ||||
| #endif  // defined(__i386)
 | ||||
| 
 | ||||
| #elif defined(__ARM_EABI__) | ||||
|   // Mimicking how strace does this(see syscall.c, search for GETREGS)
 | ||||
|   struct user_regs regs; | ||||
|   struct user_fpregs fpregs; | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| // One of these is produced for each mapping in the process (i.e. line in
 | ||||
| // /proc/$x/maps).
 | ||||
| struct MappingInfo { | ||||
|   uintptr_t start_addr; | ||||
|   size_t size; | ||||
|   size_t offset;  // offset into the backed file.
 | ||||
|   char name[NAME_MAX]; | ||||
| }; | ||||
| 
 | ||||
| class LinuxDumper { | ||||
|  public: | ||||
|   explicit LinuxDumper(pid_t pid); | ||||
| @ -146,7 +111,8 @@ class LinuxDumper { | ||||
|   virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0; | ||||
| 
 | ||||
|   // Generate a File ID from the .text section of a mapped entry.
 | ||||
|   // If not a member, mapping_id is ignored.
 | ||||
|   // 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 member, | ||||
|                                    unsigned int mapping_id, | ||||
| @ -163,6 +129,17 @@ class LinuxDumper { | ||||
|   pid_t crash_thread() const { return 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: | ||||
|   bool ReadAuxv(); | ||||
| 
 | ||||
|  | ||||
| @ -43,10 +43,14 @@ | ||||
| 
 | ||||
| #if defined(__ARM_EABI__) | ||||
| #define TID_PTR_REGISTER "r3" | ||||
| #elif defined(__aarch64__) | ||||
| #define TID_PTR_REGISTER "x3" | ||||
| #elif defined(__i386) | ||||
| #define TID_PTR_REGISTER "ecx" | ||||
| #elif defined(__x86_64) | ||||
| #define TID_PTR_REGISTER "rcx" | ||||
| #elif defined(__mips__) | ||||
| #define TID_PTR_REGISTER "$1" | ||||
| #else | ||||
| #error This test has not been ported to this platform. | ||||
| #endif | ||||
|  | ||||
| @ -47,8 +47,13 @@ | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/ptrace.h> | ||||
| #include <sys/uio.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/line_reader.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) | ||||
|     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) { | ||||
|     return false; | ||||
|   } | ||||
| @ -189,11 +208,23 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { | ||||
|   if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { | ||||
|     return false; | ||||
|   } | ||||
| #endif | ||||
| 
 | ||||
| #if defined(__i386) | ||||
|   if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) | ||||
|     return false; | ||||
| #if !defined(bit_FXSAVE)  // e.g. Clang
 | ||||
| #define bit_FXSAVE bit_FXSR | ||||
| #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) | ||||
|   for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { | ||||
| @ -208,6 +239,17 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { | ||||
|   } | ||||
| #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; | ||||
| #if defined(__i386) | ||||
|   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)); | ||||
| #elif defined(__ARM_EABI__) | ||||
|   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 | ||||
| #error "This code hasn't been ported to your platform yet." | ||||
| #endif | ||||
|  | ||||
| @ -28,7 +28,7 @@ | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||
| 
 | ||||
| // 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
 | ||||
| // to LinuxDumper being splitted into two classes.
 | ||||
| @ -41,6 +41,7 @@ | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/prctl.h> | ||||
| #include <sys/poll.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| @ -57,20 +58,72 @@ | ||||
| #include "common/memory.h" | ||||
| #include "common/using_std_string.h" | ||||
| 
 | ||||
| #ifndef PR_SET_PTRACER | ||||
| #define PR_SET_PTRACER 0x59616d61 | ||||
| #endif | ||||
| 
 | ||||
| using namespace google_breakpad; | ||||
| 
 | ||||
| namespace { | ||||
| 
 | ||||
| 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
 | ||||
| 
 | ||||
| TEST(LinuxPtraceDumperTest, Setup) { | ||||
|   LinuxPtraceDumper dumper(getpid()); | ||||
| /* Replace TestBody declarations within TEST*() with RealTestBody
 | ||||
|  * declarations */ | ||||
| #define TestBody RealTestBody | ||||
| 
 | ||||
| TEST_F(LinuxPtraceDumperChildTest, Setup) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
| } | ||||
| 
 | ||||
| TEST(LinuxPtraceDumperTest, FindMappings) { | ||||
|   LinuxPtraceDumper dumper(getpid()); | ||||
| TEST_F(LinuxPtraceDumperChildTest, FindMappings) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
| 
 | ||||
|   ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid))); | ||||
| @ -78,14 +131,14 @@ TEST(LinuxPtraceDumperTest, FindMappings) { | ||||
|   ASSERT_FALSE(dumper.FindMapping(NULL)); | ||||
| } | ||||
| 
 | ||||
| TEST(LinuxPtraceDumperTest, ThreadList) { | ||||
|   LinuxPtraceDumper dumper(getpid()); | ||||
| TEST_F(LinuxPtraceDumperChildTest, ThreadList) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
| 
 | ||||
|   ASSERT_GE(dumper.threads().size(), (size_t)1); | ||||
|   bool found = false; | ||||
|   for (size_t i = 0; i < dumper.threads().size(); ++i) { | ||||
|     if (dumper.threads()[i] == getpid()) { | ||||
|     if (dumper.threads()[i] == getppid()) { | ||||
|       ASSERT_FALSE(found); | ||||
|       found = true; | ||||
|     } | ||||
| @ -97,12 +150,22 @@ TEST(LinuxPtraceDumperTest, ThreadList) { | ||||
| // a mmap'ed mapping.
 | ||||
| class StackHelper { | ||||
|  public: | ||||
|   StackHelper(int fd, char* mapping, size_t size) | ||||
|     : fd_(fd), mapping_(mapping), size_(size) {} | ||||
|   StackHelper() | ||||
|     : fd_(-1), mapping_(NULL), size_(0) {} | ||||
|   ~StackHelper() { | ||||
|     munmap(mapping_, size_); | ||||
|     close(fd_); | ||||
|     if (size_) | ||||
|       munmap(mapping_, size_); | ||||
|     if (fd_ >= 0) | ||||
|       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: | ||||
|   int fd_; | ||||
| @ -110,19 +173,28 @@ class StackHelper { | ||||
|   size_t size_; | ||||
| }; | ||||
| 
 | ||||
| TEST(LinuxPtraceDumperTest, MergedMappings) { | ||||
|   string helper_path(GetHelperBinary()); | ||||
|   if (helper_path.empty()) { | ||||
| class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest { | ||||
|  protected: | ||||
|   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"; | ||||
|     exit(1); | ||||
|   } | ||||
| 
 | ||||
|   // mmap two segments out of the helper binary, one
 | ||||
|   // enclosed in the other, but with different protections.
 | ||||
|   const size_t kPageSize = sysconf(_SC_PAGESIZE); | ||||
|   const size_t kMappingSize = 3 * kPageSize; | ||||
|   int fd = open(helper_path.c_str(), O_RDONLY); | ||||
|   ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path | ||||
|   page_size_ = sysconf(_SC_PAGESIZE); | ||||
|   const size_t kMappingSize = 3 * page_size_; | ||||
|   int fd = open(helper_path_.c_str(), O_RDONLY); | ||||
|   ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_ | ||||
|                     << ", Error: " << strerror(errno); | ||||
|   char* mapping = | ||||
|     reinterpret_cast<char*>(mmap(NULL, | ||||
| @ -133,34 +205,37 @@ TEST(LinuxPtraceDumperTest, MergedMappings) { | ||||
|                                  0)); | ||||
|   ASSERT_TRUE(mapping); | ||||
| 
 | ||||
|   const uintptr_t kMappingAddress = reinterpret_cast<uintptr_t>(mapping); | ||||
| 
 | ||||
|   // 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.
 | ||||
|   char* inside_mapping =  reinterpret_cast<char*>( | ||||
|       mmap(mapping + 2 *kPageSize, | ||||
|            kPageSize, | ||||
|       mmap(mapping + 2 * page_size_, | ||||
|            page_size_, | ||||
|            PROT_NONE, | ||||
|            MAP_SHARED | MAP_FIXED, | ||||
|            fd, | ||||
|            // Map a different offset just to
 | ||||
|            // better test real-world conditions.
 | ||||
|            kPageSize)); | ||||
|            page_size_)); | ||||
|   ASSERT_TRUE(inside_mapping); | ||||
| 
 | ||||
|   LinuxPtraceDumperChildTest::SetUp(); | ||||
| } | ||||
| 
 | ||||
| TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) { | ||||
|   // Now check that LinuxPtraceDumper interpreted the mappings properly.
 | ||||
|   LinuxPtraceDumper dumper(getpid()); | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|   int mapping_count = 0; | ||||
|   for (unsigned i = 0; i < dumper.mappings().size(); ++i) { | ||||
|     const MappingInfo& mapping = *dumper.mappings()[i]; | ||||
|     if (strcmp(mapping.name, helper_path.c_str()) == 0) { | ||||
|     if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) { | ||||
|       // This mapping should encompass the entire original mapped
 | ||||
|       // range.
 | ||||
|       EXPECT_EQ(kMappingAddress, mapping.start_addr); | ||||
|       EXPECT_EQ(kMappingSize, mapping.size); | ||||
|       EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()), | ||||
|                 mapping.start_addr); | ||||
|       EXPECT_EQ(this->helper_.size(), mapping.size); | ||||
|       EXPECT_EQ(0U, mapping.offset); | ||||
|       mapping_count++; | ||||
|     } | ||||
| @ -168,6 +243,124 @@ TEST(LinuxPtraceDumperTest, MergedMappings) { | ||||
|   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) { | ||||
|   static const int kNumberOfThreadsInHelperProgram = 5; | ||||
|   char kNumberOfThreadsArgument[2]; | ||||
| @ -239,11 +432,16 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { | ||||
|     // In the helper program, we stored a pointer to the thread id in a
 | ||||
|     // specific register. Check that we can recover its value.
 | ||||
| #if defined(__ARM_EABI__) | ||||
|     pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]); | ||||
|     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) | ||||
|     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) | ||||
|     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 | ||||
| #error This test has not been ported to this platform. | ||||
| #endif | ||||
| @ -263,178 +461,3 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { | ||||
|   ASSERT_TRUE(WIFSIGNALED(status)); | ||||
|   ASSERT_EQ(SIGKILL, WTERMSIG(status)); | ||||
| } | ||||
| 
 | ||||
| TEST(LinuxPtraceDumperTest, BuildProcPath) { | ||||
|   const pid_t pid = getpid(); | ||||
|   LinuxPtraceDumper dumper(pid); | ||||
| 
 | ||||
|   char maps_path[NAME_MAX] = ""; | ||||
|   char maps_path_expected[NAME_MAX]; | ||||
|   snprintf(maps_path_expected, sizeof(maps_path_expected), | ||||
|            "/proc/%d/maps", pid); | ||||
|   EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); | ||||
|   EXPECT_STREQ(maps_path_expected, maps_path); | ||||
| 
 | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); | ||||
| 
 | ||||
|   char long_node[NAME_MAX]; | ||||
|   size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1; | ||||
|   memset(long_node, 'a', long_node_len); | ||||
|   long_node[long_node_len] = '\0'; | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); | ||||
| } | ||||
| 
 | ||||
| #if !defined(__ARM_EABI__) | ||||
| // Ensure that the linux-gate VDSO is included in the mapping list.
 | ||||
| TEST(LinuxPtraceDumperTest, MappingsIncludeLinuxGate) { | ||||
|   LinuxPtraceDumper dumper(getpid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
| 
 | ||||
|   void* linux_gate_loc = | ||||
|     reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]); | ||||
|   ASSERT_TRUE(linux_gate_loc); | ||||
|   bool found_linux_gate = false; | ||||
| 
 | ||||
|   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||
|   const MappingInfo* mapping; | ||||
|   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||
|     mapping = mappings[i]; | ||||
|     if (!strcmp(mapping->name, kLinuxGateLibraryName)) { | ||||
|       found_linux_gate = true; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   EXPECT_TRUE(found_linux_gate); | ||||
|   EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr)); | ||||
|   EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); | ||||
| } | ||||
| 
 | ||||
| // Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
 | ||||
| TEST(LinuxPtraceDumperTest, LinuxGateMappingID) { | ||||
|   LinuxPtraceDumper dumper(getpid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
| 
 | ||||
|   bool found_linux_gate = false; | ||||
|   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||
|   unsigned index = 0; | ||||
|   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||
|     if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { | ||||
|       found_linux_gate = true; | ||||
|       index = i; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   ASSERT_TRUE(found_linux_gate); | ||||
| 
 | ||||
|   uint8_t identifier[sizeof(MDGUID)]; | ||||
|   ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], | ||||
|                                                  true, | ||||
|                                                  index, | ||||
|                                                  identifier)); | ||||
|   uint8_t empty_identifier[sizeof(MDGUID)]; | ||||
|   memset(empty_identifier, 0, sizeof(empty_identifier)); | ||||
|   EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier))); | ||||
| } | ||||
| 
 | ||||
| // Ensure that the linux-gate VDSO can generate a non-zeroed File ID
 | ||||
| // from a child process.
 | ||||
| TEST(LinuxPtraceDumperTest, LinuxGateMappingIDChild) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
| 
 | ||||
|   // Fork a child so ptrace works.
 | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     // Now wait forever for the parent.
 | ||||
|     char b; | ||||
|     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
| 
 | ||||
|   LinuxPtraceDumper dumper(child); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
| 
 | ||||
|   bool found_linux_gate = false; | ||||
|   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||
|   unsigned index = 0; | ||||
|   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||
|     if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { | ||||
|       found_linux_gate = true; | ||||
|       index = i; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   ASSERT_TRUE(found_linux_gate); | ||||
| 
 | ||||
|   // Need to suspend the child so ptrace actually works.
 | ||||
|   ASSERT_TRUE(dumper.ThreadsSuspend()); | ||||
|   uint8_t identifier[sizeof(MDGUID)]; | ||||
|   ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], | ||||
|                                                  true, | ||||
|                                                  index, | ||||
|                                                  identifier)); | ||||
|   uint8_t empty_identifier[sizeof(MDGUID)]; | ||||
|   memset(empty_identifier, 0, sizeof(empty_identifier)); | ||||
|   EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier))); | ||||
|   EXPECT_TRUE(dumper.ThreadsResume()); | ||||
|   close(fds[1]); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| TEST(LinuxPtraceDumperTest, FileIDsMatch) { | ||||
|   // Calculate the File ID of our binary using both
 | ||||
|   // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
 | ||||
|   // and ensure that we get the same result from both.
 | ||||
|   char exe_name[PATH_MAX]; | ||||
|   ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); | ||||
| 
 | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
| 
 | ||||
|   // Fork a child so ptrace works.
 | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     // Now wait forever for the parent.
 | ||||
|     char b; | ||||
|     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
| 
 | ||||
|   LinuxPtraceDumper dumper(child); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||
|   bool found_exe = false; | ||||
|   unsigned i; | ||||
|   for (i = 0; i < mappings.size(); ++i) { | ||||
|     const MappingInfo* mapping = mappings[i]; | ||||
|     if (!strcmp(mapping->name, exe_name)) { | ||||
|       found_exe = true; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   ASSERT_TRUE(found_exe); | ||||
| 
 | ||||
|   uint8_t identifier1[sizeof(MDGUID)]; | ||||
|   uint8_t identifier2[sizeof(MDGUID)]; | ||||
|   EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i, | ||||
|                                                  identifier1)); | ||||
|   FileID fileid(exe_name); | ||||
|   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); | ||||
|   char identifier_string1[37]; | ||||
|   char identifier_string2[37]; | ||||
|   FileID::ConvertIdentifierToString(identifier1, identifier_string1, | ||||
|                                     37); | ||||
|   FileID::ConvertIdentifierToString(identifier2, identifier_string2, | ||||
|                                     37); | ||||
|   EXPECT_STREQ(identifier_string1, identifier_string2); | ||||
|   close(fds[1]); | ||||
| } | ||||
|  | ||||
| @ -59,10 +59,14 @@ | ||||
| #include <sys/ucontext.h> | ||||
| #include <sys/user.h> | ||||
| #include <sys/utsname.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #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/minidump_writer/cpu_set.h" | ||||
| #include "client/linux/minidump_writer/line_reader.h" | ||||
| @ -88,295 +92,14 @@ using google_breakpad::MappingList; | ||||
| using google_breakpad::MinidumpFileWriter; | ||||
| using google_breakpad::PageAllocator; | ||||
| using google_breakpad::ProcCpuInfoReader; | ||||
| using google_breakpad::RawContextCPU; | ||||
| using google_breakpad::SeccompUnwinder; | ||||
| using google_breakpad::ThreadInfo; | ||||
| using google_breakpad::TypedMDRVA; | ||||
| using google_breakpad::UContextReader; | ||||
| using google_breakpad::UntypedMDRVA; | ||||
| 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 { | ||||
|  public: | ||||
| @ -405,11 +128,8 @@ class MinidumpWriter { | ||||
|       : fd_(minidump_fd), | ||||
|         path_(minidump_path), | ||||
|         ucontext_(context ? &context->context : NULL), | ||||
| #if !defined(__ARM_EABI__) | ||||
| #if !defined(__ARM_EABI__) && !defined(__mips__) | ||||
|         float_state_(context ? &context->float_state : NULL), | ||||
| #else | ||||
|         // TODO: fix this after fixing ExceptionHandler
 | ||||
|         float_state_(NULL), | ||||
| #endif | ||||
|         dumper_(dumper), | ||||
|         minidump_size_limit_(-1), | ||||
| @ -533,123 +253,6 @@ class MinidumpWriter { | ||||
|     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, | ||||
|                        int max_stack_len, uint8_t** stack_copy) { | ||||
|     *stack_copy = NULL; | ||||
| @ -720,12 +323,13 @@ class MinidumpWriter { | ||||
|           ucontext_ && | ||||
|           !dumper_->IsPostMortem()) { | ||||
|         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; | ||||
| 
 | ||||
|         // Copy 256 bytes around crashing instruction pointer to minidump.
 | ||||
|         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
 | ||||
|         // it's contained within. If it's not in mapped memory,
 | ||||
|         // don't bother trying to write it.
 | ||||
| @ -770,9 +374,13 @@ class MinidumpWriter { | ||||
|         if (!cpu.Allocate()) | ||||
|           return false; | ||||
|         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) | ||||
|           PopSeccompStackFrame(cpu.get(), thread, stack_copy); | ||||
|           SeccompUnwinder::PopSeccompStackFrame(cpu.get(), thread, stack_copy); | ||||
|         thread.thread_context = cpu.location(); | ||||
|         crashing_thread_context_ = cpu.location(); | ||||
|       } else { | ||||
| @ -792,9 +400,9 @@ class MinidumpWriter { | ||||
|         if (!cpu.Allocate()) | ||||
|           return false; | ||||
|         my_memset(cpu.get(), 0, sizeof(RawContextCPU)); | ||||
|         CPUFillFromThreadInfo(cpu.get(), info); | ||||
|         info.FillCPUContext(cpu.get()); | ||||
|         if (stack_copy) | ||||
|           PopSeccompStackFrame(cpu.get(), thread, stack_copy); | ||||
|           SeccompUnwinder::PopSeccompStackFrame(cpu.get(), thread, stack_copy); | ||||
|         thread.thread_context = cpu.location(); | ||||
|         if (dumper_->threads()[i] == GetCrashThread()) { | ||||
|           crashing_thread_context_ = cpu.location(); | ||||
| @ -802,7 +410,7 @@ class MinidumpWriter { | ||||
|             // This is the crashing thread of a live process, but
 | ||||
|             // no context was provided, so set the crash address
 | ||||
|             // 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) { | ||||
|     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.
 | ||||
|       return false; | ||||
|     } | ||||
| @ -930,24 +540,9 @@ class MinidumpWriter { | ||||
| 
 | ||||
|     mod.base_of_image = mapping.start_addr; | ||||
|     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_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; | ||||
|     my_memcpy(cv_ptr, &cv_signature, sizeof(cv_signature)); | ||||
| @ -958,20 +553,31 @@ class MinidumpWriter { | ||||
|       // GUID was provided by caller.
 | ||||
|       my_memcpy(signature, identifier, sizeof(MDGUID)); | ||||
|     } else { | ||||
|       // Note: ElfFileIdentifierForMapping() can manipulate the |mapping.name|.
 | ||||
|       dumper_->ElfFileIdentifierForMapping(mapping, member, | ||||
|                                            mapping_id, signature); | ||||
|     } | ||||
|     my_memset(cv_ptr, 0, sizeof(uint32_t));  // Set age to 0 on Linux.
 | ||||
|     cv_ptr += sizeof(uint32_t); | ||||
| 
 | ||||
|     char file_name[NAME_MAX]; | ||||
|     char file_path[NAME_MAX]; | ||||
|     LinuxDumper::GetMappingEffectiveNameAndPath( | ||||
|         mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); | ||||
| 
 | ||||
|     const size_t file_name_len = my_strlen(file_name); | ||||
|     UntypedMDRVA cv(&minidump_writer_); | ||||
|     if (!cv.Allocate(MDCVInfoPDB70_minsize + file_name_len + 1)) | ||||
|       return false; | ||||
| 
 | ||||
|     // Write pdb_file_name
 | ||||
|     my_memcpy(cv_ptr, filename_ptr, filename_len + 1); | ||||
|     cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1); | ||||
|     my_memcpy(cv_ptr, file_name, file_name_len + 1); | ||||
|     cv.Copy(cv_buf, MDCVInfoPDB70_minsize + file_name_len + 1); | ||||
| 
 | ||||
|     mod.cv_record = cv.location(); | ||||
| 
 | ||||
|     MDLocationDescriptor ld; | ||||
|     if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) | ||||
|     if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld)) | ||||
|       return false; | ||||
|     mod.module_name_rva = ld.rva; | ||||
|     return true; | ||||
| @ -1127,8 +733,8 @@ class MinidumpWriter { | ||||
|           return false; | ||||
|         MDRawLinkMap entry; | ||||
|         entry.name = location.rva; | ||||
|         entry.addr = (void*)map.l_addr; | ||||
|         entry.ld = (void*)map.l_ld; | ||||
|         entry.addr = reinterpret_cast<void*>(map.l_addr); | ||||
|         entry.ld = reinterpret_cast<void*>(map.l_ld); | ||||
|         linkmap.CopyIndex(idx++, &entry); | ||||
|       } | ||||
|     } | ||||
| @ -1144,11 +750,14 @@ class MinidumpWriter { | ||||
|     debug.get()->version = debug_entry.r_version; | ||||
|     debug.get()->map = linkmap_rva; | ||||
|     debug.get()->dso_count = dso_count; | ||||
|     debug.get()->brk = (void*)debug_entry.r_brk; | ||||
|     debug.get()->ldbase = (void*)debug_entry.r_ldbase; | ||||
|     debug.get()->brk = reinterpret_cast<void*>(debug_entry.r_brk); | ||||
|     debug.get()->ldbase = reinterpret_cast<void*>(debug_entry.r_ldbase); | ||||
|     debug.get()->dynamic = dynamic; | ||||
| 
 | ||||
|     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, | ||||
|                              dynamic_length); | ||||
|     debug.CopyIndexAfterObject(0, &dso_debug_data[0], dynamic_length); | ||||
| @ -1167,53 +776,13 @@ class MinidumpWriter { | ||||
|     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) { | ||||
|     dirent->stream_type = 0; | ||||
|     dirent->location.data_size = 0; | ||||
|     dirent->location.rva = 0; | ||||
|   } | ||||
| 
 | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
| #if defined(__i386__) || defined(__x86_64__) || defined(__mips__) | ||||
|   bool WriteCPUInformation(MDRawSystemInfo* sys_info) { | ||||
|     char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; | ||||
|     static const char vendor_id_name[] = "vendor_id"; | ||||
| @ -1224,14 +793,18 @@ class MinidumpWriter { | ||||
|       bool found; | ||||
|     } cpu_info_table[] = { | ||||
|       { "processor", -1, false }, | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
|       { "model", 0, false }, | ||||
|       { "stepping",  0, false }, | ||||
|       { "cpu family", 0, false }, | ||||
| #endif | ||||
|     }; | ||||
| 
 | ||||
|     // processor_architecture should always be set, do this first
 | ||||
|     sys_info->processor_architecture = | ||||
| #if defined(__i386__) | ||||
| #if defined(__mips__) | ||||
|         MD_CPU_ARCHITECTURE_MIPS; | ||||
| #elif defined(__i386__) | ||||
|         MD_CPU_ARCHITECTURE_X86; | ||||
| #else | ||||
|         MD_CPU_ARCHITECTURE_AMD64; | ||||
| @ -1294,9 +867,11 @@ class MinidumpWriter { | ||||
|     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_revision   = cpu_info_table[1].value << 8 | | ||||
|                                      cpu_info_table[2].value; | ||||
| #endif | ||||
| 
 | ||||
|     if (vendor_id[0] != '\0') { | ||||
|       my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, | ||||
| @ -1304,7 +879,7 @@ class MinidumpWriter { | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
| #elif defined(__arm__) | ||||
| #elif defined(__arm__) || defined(__aarch64__) | ||||
|   bool WriteCPUInformation(MDRawSystemInfo* sys_info) { | ||||
|     // The CPUID value is broken up in several entries in /proc/cpuinfo.
 | ||||
|     // This table is used to rebuild it from the entries.
 | ||||
| @ -1326,6 +901,7 @@ class MinidumpWriter { | ||||
|       const char* tag; | ||||
|       uint32_t hwcaps; | ||||
|     } cpu_features_entries[] = { | ||||
| #if defined(__arm__) | ||||
|       { "swp",  MD_CPU_ARM_ELF_HWCAP_SWP }, | ||||
|       { "half", MD_CPU_ARM_ELF_HWCAP_HALF }, | ||||
|       { "thumb", MD_CPU_ARM_ELF_HWCAP_THUMB }, | ||||
| @ -1346,10 +922,18 @@ class MinidumpWriter { | ||||
|       { "idiva", MD_CPU_ARM_ELF_HWCAP_IDIVA }, | ||||
|       { "idivt", 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
 | ||||
|     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
 | ||||
|     // (e.g. Android services with the android:isolatedProcess attribute)
 | ||||
| @ -1420,10 +1004,11 @@ class MinidumpWriter { | ||||
|           const char* p = value; | ||||
|           if (value[0] == '0' && value[1] == 'x') { | ||||
|             p = my_read_hex_ptr(&result, value+2); | ||||
|           } else if (entry->format == 'x') | ||||
|           } else if (entry->format == 'x') { | ||||
|             p = my_read_hex_ptr(&result, value); | ||||
|           else | ||||
|           } else { | ||||
|             p = my_read_decimal_ptr(&result, value); | ||||
|           } | ||||
|           if (p == value) | ||||
|             continue; | ||||
| 
 | ||||
| @ -1432,13 +1017,14 @@ class MinidumpWriter { | ||||
|           sys_info->cpu.arm_cpu_info.cpuid |= | ||||
|               static_cast<uint32_t>(result); | ||||
|         } | ||||
| #if defined(__arm__) | ||||
|         // Get the architecture version from the "Processor" field.
 | ||||
|         // Note that it is also available in the "CPU architecture" field,
 | ||||
|         // however, some existing kernels are misconfigured and will report
 | ||||
|         // invalid values here (e.g. 6, while the CPU is ARMv7-A based).
 | ||||
|         // The "Processor" field doesn't have this issue.
 | ||||
|         if (!my_strcmp(field, "Processor")) { | ||||
|           unsigned value_len; | ||||
|           size_t value_len; | ||||
|           const char* value = reader->GetValueAndLen(&value_len); | ||||
|           // Expected format: <text> (v<level><endian>)
 | ||||
|           // 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); | ||||
|           } | ||||
|         } | ||||
| #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.
 | ||||
|         if (!my_strcmp(field, "Features")) { | ||||
|           unsigned value_len; | ||||
|           size_t value_len; | ||||
|           const char* value = reader->GetValueAndLen(&value_len); | ||||
| 
 | ||||
|           // Parse each space-separated tag.
 | ||||
| @ -1601,23 +1201,6 @@ class MinidumpWriter { | ||||
|       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; | ||||
|     if (!minidump_writer_.WriteString(buf, 0, &location)) | ||||
|       return false; | ||||
| @ -1639,7 +1222,9 @@ class MinidumpWriter { | ||||
|   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 _libc_fpstate* const float_state_;  // ditto
 | ||||
| #if !defined(__ARM_EABI__) && !defined(__mips__) | ||||
|   const google_breakpad::fpstate_t* const float_state_;  // ditto
 | ||||
| #endif | ||||
|   LinuxDumper* dumper_; | ||||
|   MinidumpFileWriter minidump_writer_; | ||||
|   off_t minidump_size_limit_; | ||||
|  | ||||
| @ -32,6 +32,7 @@ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/ucontext.h> | ||||
| #include <unistd.h> | ||||
| 
 | ||||
| #include <list> | ||||
| @ -44,13 +45,11 @@ namespace google_breakpad { | ||||
| 
 | ||||
| class ExceptionHandler; | ||||
| 
 | ||||
| struct MappingEntry { | ||||
|   MappingInfo first; | ||||
|   uint8_t second[sizeof(MDGUID)]; | ||||
| }; | ||||
| 
 | ||||
| // A list of <MappingInfo, GUID>
 | ||||
| typedef std::list<MappingEntry> MappingList; | ||||
| #if defined(__aarch64__) | ||||
| typedef struct fpsimd_context fpstate_t; | ||||
| #elif !defined(__ARM_EABI__) && !defined(__mips__) | ||||
| typedef struct _libc_fpstate fpstate_t; | ||||
| #endif | ||||
| 
 | ||||
| // These entries store a list of memory regions that the client wants included
 | ||||
| // in the minidump.
 | ||||
|  | ||||
| @ -84,7 +84,7 @@ TEST(MinidumpWriterTest, SetupWithPath) { | ||||
|   AutoTempDir temp_dir; | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   // 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))); | ||||
|   struct stat st; | ||||
|   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||
| @ -114,7 +114,7 @@ TEST(MinidumpWriterTest, SetupWithFD) { | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU); | ||||
|   // Set a non-zero tid to avoid tripping asserts.
 | ||||
|   context.tid = 1; | ||||
|   context.tid = child; | ||||
|   ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context))); | ||||
|   struct stat st; | ||||
|   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||
| @ -391,7 +391,7 @@ TEST(MinidumpWriterTest, DeletedBinary) { | ||||
| 
 | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   // 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, | ||||
|                             sizeof(context))); | ||||
|   kill(child_pid, SIGKILL); | ||||
| @ -525,21 +525,20 @@ TEST(MinidumpWriterTest, InvalidStackPointer) { | ||||
| 
 | ||||
|   // Fake the child's stack pointer for its crashing thread.  NOTE: This must
 | ||||
|   // be an invalid memory address for the child process (stack or otherwise).
 | ||||
| #if defined(__i386) | ||||
|   // Try 1MB below the current stack.
 | ||||
|   uintptr_t invalid_stack_pointer = | ||||
|       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||
| #if defined(__i386) | ||||
|   context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer; | ||||
| #elif defined(__x86_64) | ||||
|   // Try 1MB below the current stack.
 | ||||
|   uintptr_t invalid_stack_pointer = | ||||
|       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||
|   context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer; | ||||
| #elif defined(__ARM_EABI__) | ||||
|   // Try 1MB below the current stack.
 | ||||
|   uintptr_t invalid_stack_pointer = | ||||
|       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||
|   context.context.uc_mcontext.arm_sp = invalid_stack_pointer; | ||||
| #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 | ||||
| # error "This code has not been ported to your platform yet." | ||||
| #endif | ||||
|  | ||||
| @ -36,8 +36,7 @@ | ||||
| 
 | ||||
| #include "client/linux/minidump_writer/proc_cpuinfo_reader.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/tests/auto_testfile.h" | ||||
| #include "common/linux/tests/auto_testfile.h" | ||||
| 
 | ||||
| using namespace google_breakpad; | ||||
| 
 | ||||
|  | ||||
| @ -100,5 +100,5 @@ int main(int argc, char *argv[]) { | ||||
|                                              FLAGS_crash_server, | ||||
|                                              FLAGS_proxy_host, | ||||
|                                              FLAGS_proxy_userpasswd); | ||||
|   g.Upload(); | ||||
|   g.Upload(NULL, NULL, NULL); | ||||
| } | ||||
|  | ||||
| @ -40,7 +40,7 @@ | ||||
| #include "client/minidump_file_writer-inl.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/string_conversion.h" | ||||
| #if __linux__ | ||||
| #if defined(__linux__) && __linux__ | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
| #endif | ||||
| 
 | ||||
| @ -62,7 +62,7 @@ MinidumpFileWriter::~MinidumpFileWriter() { | ||||
| 
 | ||||
| bool MinidumpFileWriter::Open(const char *path) { | ||||
|   assert(file_ == -1); | ||||
| #if __linux__ | ||||
| #if defined(__linux__) && __linux__ | ||||
|   file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); | ||||
| #else | ||||
|   file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); | ||||
| @ -84,7 +84,7 @@ bool MinidumpFileWriter::Close() { | ||||
|     if (-1 == ftruncate(file_, position_)) { | ||||
|        return false; | ||||
|     } | ||||
| #if __linux__ | ||||
| #if defined(__linux__) && __linux__ | ||||
|     result = (sys_close(file_) == 0); | ||||
| #else | ||||
|     result = (close(file_) == 0); | ||||
| @ -253,7 +253,7 @@ bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) { | ||||
|     return false; | ||||
| 
 | ||||
|   // 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_write(file_, src, size) == size) { | ||||
| #else | ||||
|  | ||||
| @ -74,8 +74,8 @@ public: | ||||
|   MinidumpFileWriter(); | ||||
|   ~MinidumpFileWriter(); | ||||
| 
 | ||||
|   // Open |path| as the destination of the minidump data.  Any existing file
 | ||||
|   // will be overwritten.
 | ||||
|   // Open |path| as the destination of the minidump data. If |path| already
 | ||||
|   // exists, then Open() will fail.
 | ||||
|   // Return true on success, or false on failure.
 | ||||
|   bool Open(const char *path); | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| # Copyright (c) 2010, Google Inc. | ||||
| # All rights reserved. | ||||
| # Copyright 2010 Google Inc. All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are | ||||
| @ -29,9 +28,21 @@ | ||||
| 
 | ||||
| { | ||||
|   'includes': [ | ||||
|     'build/common.gypi', | ||||
|     '../../build/common.gypi', | ||||
|   ], | ||||
|   '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', | ||||
|       '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__ | ||||
| #define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ | ||||
| 
 | ||||
| #include <Windows.h> | ||||
| #include <windows.h> | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
|  | ||||
| @ -30,8 +30,8 @@ | ||||
| #ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ | ||||
| #define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ | ||||
| 
 | ||||
| #include <Windows.h> | ||||
| #include <DbgHelp.h> | ||||
| #include <windows.h> | ||||
| #include <dbghelp.h> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| #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.
 | ||||
|   FILETIME creation_time, exit_time, kernel_time, user_time; | ||||
|   if (GetProcessTimes(process_handle_, &creation_time, &exit_time, | ||||
|                       &kernel_time, &user_time)) | ||||
|     crash_id_ = creation_time.dwLowDateTime; | ||||
|                       &kernel_time, &user_time)) { | ||||
|     start_time_ = creation_time; | ||||
|   } | ||||
|   crash_id_ = start_time_.dwLowDateTime; | ||||
| 
 | ||||
|   dump_requested_handle_ = CreateEvent(NULL,    // Security attributes.
 | ||||
|                                        TRUE,    // Manual reset.
 | ||||
| @ -206,7 +208,7 @@ bool ClientInfo::PopulateCustomInfo() { | ||||
|   } | ||||
| 
 | ||||
|   SetProcessUptime(); | ||||
|   return (bytes_count != read_count); | ||||
|   return (bytes_count == read_count); | ||||
| } | ||||
| 
 | ||||
| CustomClientInfo ClientInfo::GetCustomInfo() const { | ||||
|  | ||||
| @ -30,8 +30,8 @@ | ||||
| #ifndef CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ | ||||
| #define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ | ||||
| 
 | ||||
| #include <Windows.h> | ||||
| #include <DbgHelp.h> | ||||
| #include <windows.h> | ||||
| #include <dbghelp.h> | ||||
| #include "client/windows/common/ipc_protocol.h" | ||||
| #include "common/scoped_ptr.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_generated_handle() const { return dump_generated_handle_; } | ||||
|   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) { | ||||
|     dump_request_wait_handle_ = value; | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| # Copyright (c) 2010, Google Inc. | ||||
| # All rights reserved. | ||||
| # Copyright 2010 Google Inc. All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are | ||||
| @ -29,7 +28,7 @@ | ||||
| 
 | ||||
| { | ||||
|   'includes': [ | ||||
|     '../build/common.gypi', | ||||
|     '../../../build/common.gypi', | ||||
|   ], | ||||
|   'targets': [ | ||||
|     { | ||||
|  | ||||
| @ -85,6 +85,15 @@ static bool IsClientRequestValid(const ProtocolMessage& msg) { | ||||
|           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( | ||||
|     const std::wstring& pipe_name, | ||||
|     SECURITY_ATTRIBUTES* pipe_sec_attrs, | ||||
| @ -112,16 +121,13 @@ CrashGenerationServer::CrashGenerationServer( | ||||
|       upload_request_callback_(upload_request_callback), | ||||
|       upload_context_(upload_context), | ||||
|       generate_dumps_(generate_dumps), | ||||
|       dump_generator_(NULL), | ||||
|       dump_path_(dump_path ? *dump_path : L""), | ||||
|       server_state_(IPC_SERVER_STATE_UNINITIALIZED), | ||||
|       shutting_down_(false), | ||||
|       overlapped_(), | ||||
|       client_info_(NULL) { | ||||
|       client_info_(NULL), | ||||
|       pre_fetch_custom_info_(true) { | ||||
|   InitializeCriticalSection(&sync_); | ||||
| 
 | ||||
|   if (dump_path) { | ||||
|     dump_generator_.reset(new MinidumpGenerator(*dump_path)); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // This should never be called from the OnPipeConnected callback.
 | ||||
| @ -387,18 +393,13 @@ void CrashGenerationServer::HandleReadingState() { | ||||
|                                      &overlapped_, | ||||
|                                      &bytes_count, | ||||
|                                      FALSE) != FALSE; | ||||
|   DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); | ||||
| 
 | ||||
|   if (success && bytes_count == sizeof(ProtocolMessage)) { | ||||
|     EnterStateImmediately(IPC_SERVER_STATE_READ_DONE); | ||||
|   } else { | ||||
|     // 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); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   assert(!CheckForIOIncomplete(success)); | ||||
|   EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); | ||||
| } | ||||
| 
 | ||||
| // When the server thread serving the client is in the READ_DONE state,
 | ||||
| @ -467,18 +468,12 @@ void CrashGenerationServer::HandleWritingState() { | ||||
|                                      &overlapped_, | ||||
|                                      &bytes_count, | ||||
|                                      FALSE) != FALSE; | ||||
|   DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); | ||||
| 
 | ||||
|   if (success) { | ||||
|     EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE); | ||||
|     return; | ||||
|   } | ||||
| 
 | ||||
|   // We should never get an I/O incomplete since we should not execute this
 | ||||
|   // 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); | ||||
| 
 | ||||
|   assert(!CheckForIOIncomplete(success)); | ||||
|   EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); | ||||
| } | ||||
| 
 | ||||
| @ -516,8 +511,6 @@ void CrashGenerationServer::HandleReadingAckState() { | ||||
|                                      &overlapped_, | ||||
|                                      &bytes_count, | ||||
|                                      FALSE) != FALSE; | ||||
|   DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); | ||||
| 
 | ||||
|   if (success) { | ||||
|     // The connection handshake with the client is now complete; perform
 | ||||
|     // the callback.
 | ||||
| @ -550,10 +543,7 @@ void CrashGenerationServer::HandleReadingAckState() { | ||||
|       } | ||||
|     } | ||||
|   } else { | ||||
|     // 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); | ||||
|     assert(!CheckForIOIncomplete(success)); | ||||
|   } | ||||
| 
 | ||||
|   EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); | ||||
| @ -831,10 +821,12 @@ void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) { | ||||
| void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) { | ||||
|   assert(context); | ||||
|   ClientInfo* client_info = reinterpret_cast<ClientInfo*>(context); | ||||
|   client_info->PopulateCustomInfo(); | ||||
| 
 | ||||
|   CrashGenerationServer* crash_server = client_info->crash_server(); | ||||
|   assert(crash_server); | ||||
|   if (crash_server->pre_fetch_custom_info_) { | ||||
|     client_info->PopulateCustomInfo(); | ||||
|   } | ||||
|   crash_server->HandleDumpRequest(*client_info); | ||||
| 
 | ||||
|   ResetEvent(client_info->dump_requested_handle()); | ||||
| @ -921,15 +913,19 @@ bool CrashGenerationServer::GenerateDump(const ClientInfo& client, | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   return dump_generator_->WriteMinidump(client.process_handle(), | ||||
|                                         client.pid(), | ||||
|                                         client_thread_id, | ||||
|                                         GetCurrentThreadId(), | ||||
|                                         client_ex_info, | ||||
|                                         client.assert_info(), | ||||
|                                         client.dump_type(), | ||||
|                                         true, | ||||
|                                         dump_path); | ||||
|   MinidumpGenerator dump_generator(dump_path_, | ||||
|                                    client.process_handle(), | ||||
|                                    client.pid(), | ||||
|                                    client_thread_id, | ||||
|                                    GetCurrentThreadId(), | ||||
|                                    client_ex_info, | ||||
|                                    client.assert_info(), | ||||
|                                    client.dump_type(), | ||||
|                                    true); | ||||
|   if (!dump_generator.GenerateDumpFile(dump_path)) { | ||||
|     return false; | ||||
|   } | ||||
|   return dump_generator.WriteMinidump(); | ||||
| } | ||||
| 
 | ||||
| }  // namespace google_breakpad
 | ||||
|  | ||||
| @ -102,6 +102,10 @@ class CrashGenerationServer { | ||||
|   // Returns true if initialization is successful; false otherwise.
 | ||||
|   bool Start(); | ||||
| 
 | ||||
|   void pre_fetch_custom_info(bool do_pre_fetch) { | ||||
|     pre_fetch_custom_info_ = do_pre_fetch; | ||||
|   } | ||||
| 
 | ||||
|  private: | ||||
|   // Various states the client can be in during the handshake with
 | ||||
|   // the server.
 | ||||
| @ -261,8 +265,11 @@ class CrashGenerationServer { | ||||
|   // Whether to generate dumps.
 | ||||
|   bool generate_dumps_; | ||||
| 
 | ||||
|   // Instance of a mini dump generator.
 | ||||
|   scoped_ptr<MinidumpGenerator> dump_generator_; | ||||
|   // Wether to populate custom information up-front.
 | ||||
|   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.
 | ||||
|   // Note that since we restrict the pipe to one instance, we
 | ||||
|  | ||||
| @ -38,6 +38,7 @@ | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "client/windows/common/auto_critical_section.h" | ||||
| #include "common/scoped_ptr.h" | ||||
| #include "common/windows/guid_string.h" | ||||
| 
 | ||||
| using std::wstring; | ||||
| @ -175,9 +176,14 @@ bool HandleTraceData::CollectHandleData( | ||||
|   stream_data->Reserved = 0; | ||||
|   std::copy(operations_.begin(), | ||||
|             operations_.end(), | ||||
| #ifdef _MSC_VER | ||||
|             stdext::checked_array_iterator<AVRF_HANDLE_OPERATION*>( | ||||
|                 reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1), | ||||
|                 operations_.size())); | ||||
|                 operations_.size()) | ||||
| #else | ||||
|             reinterpret_cast<AVRF_HANDLE_OPERATION*>(stream_data + 1) | ||||
| #endif | ||||
|             ); | ||||
| 
 | ||||
|   return true; | ||||
| } | ||||
| @ -242,10 +248,33 @@ ULONG CALLBACK HandleTraceData::RecordHandleOperations( | ||||
| 
 | ||||
| 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), | ||||
|       rpcrt4_module_(NULL), | ||||
|       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), | ||||
|       create_uuid_(NULL) { | ||||
|   InitializeCriticalSection(&module_load_sync_); | ||||
| @ -253,6 +282,14 @@ MinidumpGenerator::MinidumpGenerator(const wstring& dump_path) | ||||
| } | ||||
| 
 | ||||
| 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_) { | ||||
|     FreeLibrary(dbghelp_module_); | ||||
|   } | ||||
| @ -265,91 +302,28 @@ MinidumpGenerator::~MinidumpGenerator() { | ||||
|   DeleteCriticalSection(&module_load_sync_); | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
|   // 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() { | ||||
|   bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0; | ||||
|   if (dump_file_ == INVALID_HANDLE_VALUE || | ||||
|       (full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
| 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(); | ||||
|   if (!write_dump) { | ||||
|     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_info; | ||||
| 
 | ||||
|   // Setup the exception information object only if it's a dump
 | ||||
|   // due to an exception.
 | ||||
|   if (exception_pointers) { | ||||
|   if (exception_pointers_) { | ||||
|     dump_exception_pointers = &dump_exception_info; | ||||
|     dump_exception_info.ThreadId = thread_id; | ||||
|     dump_exception_info.ExceptionPointers = exception_pointers; | ||||
|     dump_exception_info.ClientPointers = is_client_pointers; | ||||
|     dump_exception_info.ThreadId = thread_id_; | ||||
|     dump_exception_info.ExceptionPointers = exception_pointers_; | ||||
|     dump_exception_info.ClientPointers = is_client_pointers_; | ||||
|   } | ||||
| 
 | ||||
|   // 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.
 | ||||
|   // The native debugger is not harmed by the presence of this information.
 | ||||
|   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
 | ||||
|     // in-process dump generation.
 | ||||
|     breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | | ||||
|                              MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; | ||||
|     breakpad_info.dump_thread_id = thread_id; | ||||
|     breakpad_info.requesting_thread_id = requesting_thread_id; | ||||
|     breakpad_info.dump_thread_id = thread_id_; | ||||
|     breakpad_info.requesting_thread_id = requesting_thread_id_; | ||||
|   } | ||||
| 
 | ||||
|   // Leave room in user_stream_array for possible assertion info and handle
 | ||||
|   // operations streams.
 | ||||
|   MINIDUMP_USER_STREAM user_stream_array[3]; | ||||
|   int additional_streams_count = additional_streams_ ? | ||||
|       additional_streams_->UserStreamCount : 0; | ||||
|   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].BufferSize = sizeof(breakpad_info); | ||||
|   user_stream_array[0].Buffer = &breakpad_info; | ||||
| 
 | ||||
|   MINIDUMP_USER_STREAM_INFORMATION user_streams; | ||||
|   user_streams.UserStreamCount = 1; | ||||
|   user_streams.UserStreamArray = user_stream_array; | ||||
|   user_streams.UserStreamArray = user_stream_array.get(); | ||||
| 
 | ||||
|   MDRawAssertionInfo* actual_assert_info = assert_info; | ||||
|   MDRawAssertionInfo client_assert_info = {0}; | ||||
|   MDRawAssertionInfo* actual_assert_info = assert_info_; | ||||
|   MDRawAssertionInfo client_assert_info = {{0}}; | ||||
| 
 | ||||
|   if (assert_info) { | ||||
|   if (assert_info_) { | ||||
|     // If the assertion info object lives in the client process,
 | ||||
|     // read the memory of the client process.
 | ||||
|     if (is_client_pointers) { | ||||
|     if (is_client_pointers_) { | ||||
|       SIZE_T bytes_read = 0; | ||||
|       if (!ReadProcessMemory(process_handle, | ||||
|                              assert_info, | ||||
|       if (!ReadProcessMemory(process_handle_, | ||||
|                              assert_info_, | ||||
|                              &client_assert_info, | ||||
|                              sizeof(client_assert_info), | ||||
|                              &bytes_read)) { | ||||
|         CloseHandle(dump_file); | ||||
|         if (full_dump_file != INVALID_HANDLE_VALUE) | ||||
|           CloseHandle(full_dump_file); | ||||
|         if (dump_file_is_internal_) | ||||
|           CloseHandle(dump_file_); | ||||
|         if (full_dump_file_is_internal_ && | ||||
|             full_dump_file_ != INVALID_HANDLE_VALUE) | ||||
|           CloseHandle(full_dump_file_); | ||||
|         return false; | ||||
|       } | ||||
| 
 | ||||
|       if (bytes_read != sizeof(client_assert_info)) { | ||||
|         CloseHandle(dump_file); | ||||
|         if (full_dump_file != INVALID_HANDLE_VALUE) | ||||
|           CloseHandle(full_dump_file); | ||||
|         if (dump_file_is_internal_) | ||||
|           CloseHandle(dump_file_); | ||||
|         if (full_dump_file_is_internal_ && | ||||
|             full_dump_file_ != INVALID_HANDLE_VALUE) | ||||
|           CloseHandle(full_dump_file_); | ||||
|         return false; | ||||
|       } | ||||
| 
 | ||||
| @ -414,16 +393,31 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, | ||||
|     ++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
 | ||||
|   // 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.
 | ||||
|   HandleTraceData handle_trace_data; | ||||
|   if (exception_pointers && (dump_type & MiniDumpWithHandleData) == 0) { | ||||
|     if (!handle_trace_data.CollectHandleData(process_handle, | ||||
|                                              exception_pointers)) { | ||||
|       CloseHandle(dump_file); | ||||
|       if (full_dump_file != INVALID_HANDLE_VALUE) | ||||
|         CloseHandle(full_dump_file); | ||||
|   if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) { | ||||
|     if (!handle_trace_data.CollectHandleData(process_handle_, | ||||
|                                              exception_pointers_)) { | ||||
|       if (dump_file_is_internal_) | ||||
|         CloseHandle(dump_file_); | ||||
|       if (full_dump_file_is_internal_ && | ||||
|           full_dump_file_ != INVALID_HANDLE_VALUE) | ||||
|         CloseHandle(full_dump_file_); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| @ -431,12 +425,12 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, | ||||
|   bool result_full_memory = true; | ||||
|   if (full_memory_dump) { | ||||
|     result_full_memory = write_dump( | ||||
|         process_handle, | ||||
|         process_id, | ||||
|         full_dump_file, | ||||
|         static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpNormal)) | ||||
|         process_handle_, | ||||
|         process_id_, | ||||
|         full_dump_file_, | ||||
|         static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpNormal)) | ||||
|                                     | MiniDumpWithHandleData), | ||||
|         exception_pointers ? &dump_exception_info : NULL, | ||||
|         exception_pointers_ ? &dump_exception_info : NULL, | ||||
|         &user_streams, | ||||
|         NULL) != FALSE; | ||||
|   } | ||||
| @ -448,31 +442,79 @@ bool MinidumpGenerator::WriteMinidump(HANDLE process_handle, | ||||
|   } | ||||
| 
 | ||||
|   bool result_minidump = write_dump( | ||||
|       process_handle, | ||||
|       process_id, | ||||
|       dump_file, | ||||
|       static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpWithFullMemory)) | ||||
|       process_handle_, | ||||
|       process_id_, | ||||
|       dump_file_, | ||||
|       static_cast<MINIDUMP_TYPE>((dump_type_ & (~MiniDumpWithFullMemory)) | ||||
|                                   | MiniDumpNormal), | ||||
|       exception_pointers ? &dump_exception_info : NULL, | ||||
|       exception_pointers_ ? &dump_exception_info : NULL, | ||||
|       &user_streams, | ||||
|       NULL) != FALSE; | ||||
|       callback_info_) != FALSE; | ||||
| 
 | ||||
|   bool result = result_minidump && result_full_memory; | ||||
|   return result_minidump && result_full_memory; | ||||
| } | ||||
| 
 | ||||
|   CloseHandle(dump_file); | ||||
|   if (full_dump_file != INVALID_HANDLE_VALUE) | ||||
|     CloseHandle(full_dump_file); | ||||
| 
 | ||||
|   // Store the path of the dump file in the out parameter if dump generation
 | ||||
|   // succeeded.
 | ||||
|   if (result && dump_path) { | ||||
|     *dump_path = dump_file_path; | ||||
|   } | ||||
|   if (result && full_memory_dump && full_dump_path) { | ||||
|     *full_dump_path = full_dump_file_path; | ||||
| bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) { | ||||
|   // The dump file was already set by handle or this function was previously
 | ||||
|   // called.
 | ||||
|   if (dump_file_ != INVALID_HANDLE_VALUE) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   return result; | ||||
|   wstring dump_file_path; | ||||
|   if (!GenerateDumpFilePath(&dump_file_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; | ||||
|   return true; | ||||
| } | ||||
| 
 | ||||
| bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) { | ||||
|   // A full minidump was not requested.
 | ||||
|   if ((dump_type_ & MiniDumpWithFullMemory) == 0) { | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   // 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() { | ||||
|  | ||||
| @ -34,6 +34,7 @@ | ||||
| #include <dbghelp.h> | ||||
| #include <rpc.h> | ||||
| #include <list> | ||||
| #include <string> | ||||
| #include "google_breakpad/common/minidump_format.h" | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| @ -44,37 +45,55 @@ namespace google_breakpad { | ||||
| // the clients to generate minidumps.
 | ||||
| class MinidumpGenerator { | ||||
|  public: | ||||
|   // Creates an instance with the given dump path.
 | ||||
|   explicit MinidumpGenerator(const std::wstring& dump_path); | ||||
|   // Creates an instance with the given parameters.
 | ||||
|   // 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(); | ||||
| 
 | ||||
|   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
 | ||||
|   // dump file path in the dump_path parameter if dump generation
 | ||||
|   // succeeds.
 | ||||
|   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); | ||||
| 
 | ||||
|   // 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); | ||||
|   bool WriteMinidump(); | ||||
| 
 | ||||
|  private: | ||||
|   // Function pointer type for MiniDumpWriteDump, which is looked up
 | ||||
| @ -120,9 +139,53 @@ class MinidumpGenerator { | ||||
|   // Pointer to the UuidCreate function.
 | ||||
|   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.
 | ||||
|   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 module_load_sync_; | ||||
| 
 | ||||
|  | ||||
| @ -27,7 +27,7 @@ | ||||
| // (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 <ObjBase.h> | ||||
| #include <objbase.h> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <cassert> | ||||
| @ -104,19 +104,19 @@ ExceptionHandler::ExceptionHandler( | ||||
|     MinidumpCallback callback, | ||||
|     void* callback_context, | ||||
|     int handler_types, | ||||
|     MINIDUMP_TYPE dump_type, | ||||
|     CrashGenerationClient* crash_generation_client, | ||||
|     const CustomClientInfo* custom_info) { | ||||
|     CrashGenerationClient* crash_generation_client) { | ||||
|   // The dump_type, pipe_name and custom_info that are passed in to Initialize()
 | ||||
|   // are not used.  The ones set in crash_generation_client are used instead.
 | ||||
|   Initialize(dump_path, | ||||
|              filter, | ||||
|              callback, | ||||
|              callback_context, | ||||
|              handler_types, | ||||
|              MiniDumpNormal, | ||||
|              NULL,  // pipe_name
 | ||||
|              NULL,  // pipe_handle
 | ||||
|              MiniDumpNormal,           // dump_type - not used
 | ||||
|              NULL,                     // pipe_name - not used
 | ||||
|              NULL,                     // pipe_handle
 | ||||
|              crash_generation_client, | ||||
|              custom_info); | ||||
|              NULL);                    // custom_info - not used
 | ||||
| } | ||||
| 
 | ||||
| ExceptionHandler::ExceptionHandler(const wstring &dump_path, | ||||
|  | ||||
| @ -1,5 +1,4 @@ | ||||
| # Copyright (c) 2010, Google Inc. | ||||
| # All rights reserved. | ||||
| # Copyright 2010 Google Inc. All rights reserved. | ||||
| # | ||||
| # Redistribution and use in source and binary forms, with or without | ||||
| # modification, are permitted provided that the following conditions are | ||||
| @ -29,7 +28,7 @@ | ||||
| 
 | ||||
| { | ||||
|   'includes': [ | ||||
|     '../build/common.gypi', | ||||
|     '../../../build/common.gypi', | ||||
|   ], | ||||
|   'targets': [ | ||||
|     { | ||||
| @ -41,7 +40,7 @@ | ||||
|       ], | ||||
|       'dependencies': [ | ||||
|         '../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__ | ||||
| 
 | ||||
| #include <stdlib.h> | ||||
| #include <Windows.h> | ||||
| #include <DbgHelp.h> | ||||
| #include <windows.h> | ||||
| #include <dbghelp.h> | ||||
| #include <rpc.h> | ||||
| 
 | ||||
| #pragma warning( push ) | ||||
| #pragma warning(push) | ||||
| // Disable exception handler warnings.
 | ||||
| #pragma warning( disable : 4530 ) | ||||
| #pragma warning(disable:4530) | ||||
| 
 | ||||
| #include <list> | ||||
| #include <string> | ||||
| @ -212,9 +212,7 @@ class ExceptionHandler { | ||||
|                    MinidumpCallback callback, | ||||
|                    void* callback_context, | ||||
|                    int handler_types, | ||||
|                    MINIDUMP_TYPE dump_type, | ||||
|                    CrashGenerationClient* crash_generation_client, | ||||
|                    const CustomClientInfo* custom_info); | ||||
|                    CrashGenerationClient* crash_generation_client); | ||||
| 
 | ||||
|   ~ExceptionHandler(); | ||||
| 
 | ||||
| @ -497,7 +495,7 @@ class ExceptionHandler { | ||||
|   static CRITICAL_SECTION handler_stack_critical_section_; | ||||
| 
 | ||||
|   // The number of instances of this class.
 | ||||
|   volatile static LONG instance_count_; | ||||
|   static volatile LONG instance_count_; | ||||
| 
 | ||||
|   // disallow copy ctor and operator=
 | ||||
|   explicit ExceptionHandler(const ExceptionHandler &); | ||||
| @ -506,6 +504,6 @@ class ExceptionHandler { | ||||
| 
 | ||||
| }  // namespace google_breakpad
 | ||||
| 
 | ||||
| #pragma warning( pop ) | ||||
| #pragma warning(pop) | ||||
| 
 | ||||
| #endif  // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__
 | ||||
|  | ||||
| @ -38,4 +38,21 @@ | ||||
|   void operator=(const TypeName&) | ||||
| #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_
 | ||||
|  | ||||
							
								
								
									
										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_LOW_START   (UTF32)0xDC00 | ||||
| #define UNI_SUR_LOW_END     (UTF32)0xDFFF | ||||
| 
 | ||||
| #ifndef false | ||||
| #define false	   0 | ||||
| #endif | ||||
| #ifndef true | ||||
| #define true	    1 | ||||
| #endif | ||||
| 
 | ||||
| /* --------------------------------------------------------------------- */ | ||||
| 
 | ||||
|  | ||||
| @ -20,6 +20,9 @@ | ||||
|  * remains attached. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef COMMON_CONVERT_UTF_H_ | ||||
| #define COMMON_CONVERT_UTF_H_ | ||||
| 
 | ||||
| /* ---------------------------------------------------------------------
 | ||||
| 
 | ||||
| Conversions between UTF32, UTF-16, and UTF-8.  Header file. | ||||
| @ -141,3 +144,5 @@ Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); | ||||
| #endif | ||||
| 
 | ||||
| /* --------------------------------------------------------------------- */ | ||||
| 
 | ||||
| #endif  // COMMON_CONVERT_UTF_H_
 | ||||
|  | ||||
| @ -1512,16 +1512,19 @@ bool CallFrameInfo::State::DoInstruction() { | ||||
| 
 | ||||
|     // Change the base register used to compute the CFA.
 | ||||
|     case DW_CFA_def_cfa_register: { | ||||
|       if (!ParseOperands("r", &ops)) return false; | ||||
|       Rule *cfa_rule = rules_.CFARule(); | ||||
|       if (!cfa_rule) { | ||||
|         reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); | ||||
|         if (!DoDefCFA(ops.register_number, ops.offset)) { | ||||
|           reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); | ||||
|           return false; | ||||
|         } | ||||
|       } else { | ||||
|         cfa_rule->SetBaseRegister(ops.register_number); | ||||
|         if (!cfa_rule->Handle(handler_, address_, | ||||
|                               Handler::kCFARegister)) | ||||
|         return false; | ||||
|       } | ||||
|       if (!ParseOperands("r", &ops)) return false; | ||||
|       cfa_rule->SetBaseRegister(ops.register_number); | ||||
|       if (!cfa_rule->Handle(handler_, address_, | ||||
|                             Handler::kCFARegister)) | ||||
|         return false; | ||||
|       break; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -105,6 +105,43 @@ vector<string> DwarfCFIToModule::RegisterNames::ARM() { | ||||
|   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, | ||||
|                              uint8 version, const string &augmentation, | ||||
|                              unsigned return_address) { | ||||
|  | ||||
| @ -109,6 +109,12 @@ class DwarfCFIToModule: public CallFrameInfo::Handler { | ||||
|     // ARM.
 | ||||
|     static vector<string> ARM(); | ||||
| 
 | ||||
|     // ARM64, aka AARCH64.
 | ||||
|     static vector<string> ARM64(); | ||||
| 
 | ||||
|     // MIPS.
 | ||||
|     static vector<string> MIPS(); | ||||
| 
 | ||||
|    private: | ||||
|     // Given STRINGS, an array of C strings with SIZE elements, return an
 | ||||
|     // equivalent vector<string>.
 | ||||
|  | ||||
| @ -46,16 +46,15 @@ | ||||
| #include <stdio.h> | ||||
| 
 | ||||
| #include <algorithm> | ||||
| #include <set> | ||||
| #include <utility> | ||||
| 
 | ||||
| #include "common/dwarf_line_to_module.h" | ||||
| #include "common/unordered.h" | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| using std::map; | ||||
| using std::pair; | ||||
| using std::set; | ||||
| using std::sort; | ||||
| using std::vector; | ||||
| 
 | ||||
| @ -118,7 +117,7 @@ struct DwarfCUToModule::FilePrivate { | ||||
|   // so this set will actually hold yet another copy of the string (although
 | ||||
|   // everything will still work). To improve memory consumption portably,
 | ||||
|   // 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
 | ||||
|   // Specifications describing those DIEs. Specification references can
 | ||||
| @ -337,7 +336,7 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( | ||||
| } | ||||
| 
 | ||||
| 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); | ||||
|   return *result.first; | ||||
| } | ||||
| @ -531,7 +530,7 @@ void DwarfCUToModule::FuncHandler::Finish() { | ||||
|   if (low_pc_ < high_pc_) { | ||||
|     // Create a Module::Function based on the data we've gathered, and
 | ||||
|     // 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
 | ||||
|     // have names.
 | ||||
|     if (!name_.empty()) { | ||||
| @ -546,7 +545,7 @@ void DwarfCUToModule::FuncHandler::Finish() { | ||||
|     if (func->address) { | ||||
|        // If the function address is zero this is a sign that this function
 | ||||
|        // 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_) { | ||||
|     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_cu_to_module.h" | ||||
| #include "common/dwarf_line_to_module.h" | ||||
| #include "common/linux/crc32.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/linux/elfutils.h" | ||||
| #include "common/linux/elfutils-inl.h" | ||||
| #include "common/linux/elf_symbols_to_module.h" | ||||
| @ -88,6 +90,11 @@ using google_breakpad::StabsToModule; | ||||
| #endif | ||||
| 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
 | ||||
| //
 | ||||
| @ -141,7 +148,7 @@ class MmapWrapper { | ||||
| 
 | ||||
|  private: | ||||
|   bool is_set_; | ||||
|   void *base_; | ||||
|   void* base_; | ||||
|   size_t size_; | ||||
| }; | ||||
| 
 | ||||
| @ -204,8 +211,8 @@ class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler { | ||||
|   void StartCompilationUnit(const string& compilation_dir) { | ||||
|     compilation_dir_ = compilation_dir; | ||||
|   } | ||||
|   void ReadProgram(const char *program, uint64 length, | ||||
|                    Module *module, std::vector<Module::Line> *lines) { | ||||
|   void ReadProgram(const char* program, uint64 length, | ||||
|                    Module* module, std::vector<Module::Line>* lines) { | ||||
|     DwarfLineToModule handler(module, compilation_dir_, lines); | ||||
|     dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); | ||||
|     parser.Start(); | ||||
| @ -291,6 +298,12 @@ bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, | ||||
|     case EM_ARM: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::ARM(); | ||||
|       return true; | ||||
|     case EM_AARCH64: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::ARM64(); | ||||
|       return true; | ||||
|     case EM_MIPS: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::MIPS(); | ||||
|       return true; | ||||
|     case EM_X86_64: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::X86_64(); | ||||
|       return true; | ||||
| @ -366,7 +379,7 @@ bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper, | ||||
|             obj_file.c_str(), strerror(errno)); | ||||
|     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); | ||||
|   if (obj_base == MAP_FAILED) { | ||||
|     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
 | ||||
| // wrong, return an empty string.
 | ||||
| template<typename ElfClass> | ||||
| string ReadDebugLink(const char* debuglink, | ||||
|                      size_t debuglink_size, | ||||
|                      const size_t debuglink_size, | ||||
|                      const bool big_endian, | ||||
|                      const string& obj_file, | ||||
|                      const std::vector<string>& debug_dirs) { | ||||
|   size_t debuglink_len = strlen(debuglink) + 5;  // '\0' + CRC32.
 | ||||
|   debuglink_len = 4 * ((debuglink_len + 3) / 4);  // Round to nearest 4 bytes.
 | ||||
|   size_t debuglink_len = strlen(debuglink) + 5;  // Include '\0' + CRC32.
 | ||||
|   debuglink_len = 4 * ((debuglink_len + 3) / 4);  // Round up to 4 bytes.
 | ||||
| 
 | ||||
|   // Sanity check.
 | ||||
|   if (debuglink_len != debuglink_size) { | ||||
|     fprintf(stderr, "Mismatched .gnu_debuglink string / section size: " | ||||
|             "%zx %zx\n", debuglink_len, debuglink_size); | ||||
|     return ""; | ||||
|     return string(); | ||||
|   } | ||||
| 
 | ||||
|   bool found = false; | ||||
| @ -425,10 +438,39 @@ string ReadDebugLink(const char* debuglink, | ||||
|     const string& debug_dir = *it; | ||||
|     debuglink_path = debug_dir + "/" + debuglink; | ||||
|     debuglink_fd = open(debuglink_path.c_str(), O_RDONLY); | ||||
|     if (debuglink_fd >= 0) { | ||||
|       found = true; | ||||
|       break; | ||||
|     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; | ||||
|     break; | ||||
|   } | ||||
| 
 | ||||
|   if (!found) { | ||||
| @ -438,13 +480,9 @@ string ReadDebugLink(const char* debuglink, | ||||
|       const string debug_dir = *it; | ||||
|       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; | ||||
| } | ||||
| 
 | ||||
| @ -534,6 +572,7 @@ bool LoadSymbols(const string& obj_file, | ||||
|   typedef typename ElfClass::Addr Addr; | ||||
|   typedef typename ElfClass::Phdr Phdr; | ||||
|   typedef typename ElfClass::Shdr Shdr; | ||||
|   typedef typename ElfClass::Word Word; | ||||
| 
 | ||||
|   Addr loading_addr = GetLoadingAddress<ElfClass>( | ||||
|       GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff), | ||||
| @ -541,6 +580,8 @@ bool LoadSymbols(const string& obj_file, | ||||
|   module->SetLoadAddress(loading_addr); | ||||
|   info->set_loading_addr(loading_addr, obj_file); | ||||
| 
 | ||||
|   Word debug_section_type = | ||||
|       elf_header->e_machine == EM_MIPS ? SHT_MIPS_DWARF : SHT_PROGBITS; | ||||
|   const Shdr* sections = | ||||
|       GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); | ||||
|   const Shdr* section_names = sections + elf_header->e_shstrndx; | ||||
| @ -574,7 +615,7 @@ bool LoadSymbols(const string& obj_file, | ||||
| 
 | ||||
|     // Look for DWARF debugging information, and load it if present.
 | ||||
|     const Shdr* dwarf_section = | ||||
|       FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS, | ||||
|       FindElfSectionByName<ElfClass>(".debug_info", debug_section_type, | ||||
|                                      sections, names, names_end, | ||||
|                                      elf_header->e_shnum); | ||||
|     if (dwarf_section) { | ||||
| @ -593,7 +634,7 @@ bool LoadSymbols(const string& obj_file, | ||||
|     // Dwarf Call Frame Information (CFI) is actually independent from
 | ||||
|     // the other DWARF debugging information, and can be used alone.
 | ||||
|     const Shdr* dwarf_cfi_section = | ||||
|         FindElfSectionByName<ElfClass>(".debug_frame", SHT_PROGBITS, | ||||
|         FindElfSectionByName<ElfClass>(".debug_frame", debug_section_type, | ||||
|                                        sections, names, names_end, | ||||
|                                        elf_header->e_shnum); | ||||
|     if (dwarf_cfi_section) { | ||||
| @ -648,13 +689,17 @@ bool LoadSymbols(const string& obj_file, | ||||
|                                            names_end, elf_header->e_shnum); | ||||
|       if (gnu_debuglink_section) { | ||||
|         if (!info->debug_dirs().empty()) { | ||||
|           found_debug_info_section = true; | ||||
| 
 | ||||
|           const char* debuglink_contents = | ||||
|               GetOffset<ElfClass, char>(elf_header, | ||||
|                                         gnu_debuglink_section->sh_offset); | ||||
|           string debuglink_file | ||||
|               = ReadDebugLink<ElfClass>(debuglink_contents, | ||||
|                                         gnu_debuglink_section->sh_size, | ||||
|                                         obj_file, info->debug_dirs()); | ||||
|           string debuglink_file = | ||||
|               ReadDebugLink(debuglink_contents, | ||||
|                             gnu_debuglink_section->sh_size, | ||||
|                             big_endian, | ||||
|                             obj_file, | ||||
|                             info->debug_dirs()); | ||||
|           info->set_debuglink_file(debuglink_file); | ||||
|         } else { | ||||
|           fprintf(stderr, ".gnu_debuglink section found in '%s', " | ||||
| @ -664,50 +709,45 @@ bool LoadSymbols(const string& obj_file, | ||||
|         fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n", | ||||
|                 obj_file.c_str()); | ||||
|       } | ||||
|     } else { | ||||
|       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 = | ||||
|           FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM, | ||||
|                                          sections, names, names_end, | ||||
|                                          elf_header->e_shnum); | ||||
|         const Shdr* dynstr_section = | ||||
|           FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB, | ||||
|                                          sections, names, names_end, | ||||
|                                          elf_header->e_shnum); | ||||
|         if (dynsym_section && dynstr_section) { | ||||
|           info->LoadedSection(".dynsym"); | ||||
| 
 | ||||
|           const uint8_t* dynsyms = | ||||
|               GetOffset<ElfClass, uint8_t>(elf_header, | ||||
|                                            dynsym_section->sh_offset); | ||||
|           const uint8_t* dynstrs = | ||||
|               GetOffset<ElfClass, uint8_t>(elf_header, | ||||
|                                            dynstr_section->sh_offset); | ||||
|           bool result = | ||||
|               ELFSymbolsToModule(dynsyms, | ||||
|                                  dynsym_section->sh_size, | ||||
|                                  dynstrs, | ||||
|                                  dynstr_section->sh_size, | ||||
|                                  big_endian, | ||||
|                                  ElfClass::kAddrSize, | ||||
|                                  module); | ||||
|           found_usable_info = found_usable_info || result; | ||||
|         } | ||||
|       } | ||||
| 
 | ||||
|       // Return true if some usable information was found, since
 | ||||
|       // the caller doesn't want to use .gnu_debuglink.
 | ||||
|       return found_usable_info; | ||||
|     } | ||||
| 
 | ||||
|     // No debug info was found, let the user try again with .gnu_debuglink
 | ||||
|     // if present.
 | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   return true; | ||||
|   if (options.symbol_data != ONLY_CFI) { | ||||
|     const Shdr* dynsym_section = | ||||
|       FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM, | ||||
|                                      sections, names, names_end, | ||||
|                                      elf_header->e_shnum); | ||||
|     const Shdr* dynstr_section = | ||||
|       FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB, | ||||
|                                      sections, names, names_end, | ||||
|                                      elf_header->e_shnum); | ||||
|     if (dynsym_section && dynstr_section) { | ||||
|       info->LoadedSection(".dynsym"); | ||||
| 
 | ||||
|       const uint8_t* dynsyms = | ||||
|           GetOffset<ElfClass, uint8_t>(elf_header, | ||||
|                                        dynsym_section->sh_offset); | ||||
|       const uint8_t* dynstrs = | ||||
|           GetOffset<ElfClass, uint8_t>(elf_header, | ||||
|                                        dynstr_section->sh_offset); | ||||
|       bool result = | ||||
|           ELFSymbolsToModule(dynsyms, | ||||
|                              dynsym_section->sh_size, | ||||
|                              dynstrs, | ||||
|                              dynstr_section->sh_size, | ||||
|                              big_endian, | ||||
|                              ElfClass::kAddrSize, | ||||
|                              module); | ||||
|       found_usable_info = found_usable_info || result; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   if (read_gnu_debug_link) { | ||||
|     return found_debug_info_section; | ||||
|   } | ||||
| 
 | ||||
|   // Return true if some usable information was found
 | ||||
|   return found_usable_info; | ||||
| } | ||||
| 
 | ||||
| // 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) { | ||||
|     case EM_386:        return "x86"; | ||||
|     case EM_ARM:        return "arm"; | ||||
|     case EM_AARCH64:    return "arm64"; | ||||
|     case EM_MIPS:       return "mips"; | ||||
|     case EM_PPC64:      return "ppc64"; | ||||
|     case EM_PPC:        return "ppc"; | ||||
| @ -753,7 +794,7 @@ string FormatIdentifier(unsigned char identifier[16]) { | ||||
| // last slash, or the whole filename if there are no slashes.
 | ||||
| string BaseFileName(const string &filename) { | ||||
|   // Lots of copies!  basename's behavior is less than ideal.
 | ||||
|   char *c_filename = strdup(filename.c_str()); | ||||
|   char* c_filename = strdup(filename.c_str()); | ||||
|   string base = basename(c_filename); | ||||
|   free(c_filename); | ||||
|   return base; | ||||
|  | ||||
| @ -37,11 +37,22 @@ | ||||
| //
 | ||||
| 
 | ||||
| #define HANDLE_EINTR(x) ({ \ | ||||
|   typeof(x) __eintr_result__; \ | ||||
|   typeof(x) eintr_wrapper_result; \ | ||||
|   do { \ | ||||
|     __eintr_result__ = x; \ | ||||
|   } while (__eintr_result__ == -1 && errno == EINTR); \ | ||||
|   __eintr_result__;\ | ||||
|     eintr_wrapper_result = (x); \ | ||||
|   } while (eintr_wrapper_result == -1 && errno == EINTR); \ | ||||
|   eintr_wrapper_result; \ | ||||
| }) | ||||
| 
 | ||||
| #define IGNORE_EINTR(x) ({ \ | ||||
|   typeof(x) eintr_wrapper_result; \ | ||||
|   do { \ | ||||
|     eintr_wrapper_result = (x); \ | ||||
|     if (eintr_wrapper_result == -1 && errno == EINTR) { \ | ||||
|       eintr_wrapper_result = 0; \ | ||||
|     } \ | ||||
|   } while (0); \ | ||||
|   eintr_wrapper_result; \ | ||||
| }) | ||||
| 
 | ||||
| #endif  // COMMON_LINUX_EINTR_WRAPPER_H_
 | ||||
|  | ||||
| @ -70,7 +70,7 @@ TEST(ElfCoreDumpTest, TestElfHeader) { | ||||
|   ElfCoreDump core; | ||||
| 
 | ||||
|   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()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|   EXPECT_EQ(NULL, core.GetHeader()); | ||||
| @ -80,49 +80,49 @@ TEST(ElfCoreDumpTest, TestElfHeader) { | ||||
|   EXPECT_FALSE(core.GetFirstNote().IsValid()); | ||||
| 
 | ||||
|   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()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
| 
 | ||||
|   header.e_ident[0] = ELFMAG0; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
| 
 | ||||
|   header.e_ident[1] = ELFMAG1; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
| 
 | ||||
|   header.e_ident[2] = ELFMAG2; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
| 
 | ||||
|   header.e_ident[3] = ELFMAG3; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
| 
 | ||||
|   header.e_ident[4] = ElfCoreDump::kClass; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
| 
 | ||||
|   header.e_version = EV_CURRENT; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
| 
 | ||||
|   header.e_type = ET_CORE; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file, 0)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_TRUE(core.IsValid()); | ||||
| } | ||||
| @ -138,22 +138,26 @@ TEST(ElfCoreDumpTest, ValidCoreFile) { | ||||
|   const unsigned kNumOfThreads = 3; | ||||
|   const unsigned kCrashThread = 1; | ||||
|   const int kCrashSignal = SIGABRT; | ||||
|   // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
 | ||||
|   // CrashGenerator is identified and fixed.
 | ||||
|   if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, | ||||
|                                         kCrashSignal, NULL)) { | ||||
|     fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " | ||||
|             "due to no core dump generated"); | ||||
|     return; | ||||
|   } | ||||
|   ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, | ||||
|                                                kCrashSignal, NULL)); | ||||
|   pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread); | ||||
|   set<pid_t> expected_thread_ids; | ||||
|   for (unsigned i = 0; i < kNumOfThreads; ++i) { | ||||
|     expected_thread_ids.insert(crash_generator.GetThreadId(i)); | ||||
|   } | ||||
| 
 | ||||
| #if defined(__ANDROID__) | ||||
|   struct stat st; | ||||
|   if (stat(crash_generator.GetCoreFilePath().c_str(), &st) != 0) { | ||||
|     fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " | ||||
|             "due to no core file being generated"); | ||||
|     return; | ||||
|   } | ||||
| #endif | ||||
| 
 | ||||
|   MemoryMappedFile mapped_core_file; | ||||
|   ASSERT_TRUE(mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str())); | ||||
|   ASSERT_TRUE( | ||||
|       mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str(), 0)); | ||||
| 
 | ||||
|   ElfCoreDump core; | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
| @ -182,6 +186,7 @@ TEST(ElfCoreDumpTest, ValidCoreFile) { | ||||
| 
 | ||||
|   size_t num_nt_prpsinfo = 0; | ||||
|   size_t num_nt_prstatus = 0; | ||||
|   size_t num_pr_fpvalid = 0; | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
|   size_t num_nt_fpregset = 0; | ||||
| #endif | ||||
| @ -213,6 +218,8 @@ TEST(ElfCoreDumpTest, ValidCoreFile) { | ||||
|           EXPECT_EQ(kCrashSignal, status->pr_info.si_signo); | ||||
|         } | ||||
|         ++num_nt_prstatus; | ||||
|         if (status->pr_fpvalid) | ||||
|           ++num_pr_fpvalid; | ||||
|         break; | ||||
|       } | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
| @ -241,9 +248,9 @@ TEST(ElfCoreDumpTest, ValidCoreFile) { | ||||
|   EXPECT_EQ(1U, num_nt_prpsinfo); | ||||
|   EXPECT_EQ(kNumOfThreads, num_nt_prstatus); | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
|   EXPECT_EQ(kNumOfThreads, num_nt_fpregset); | ||||
|   EXPECT_EQ(num_pr_fpvalid, num_nt_fpregset); | ||||
| #endif | ||||
| #if defined(__i386__) | ||||
|   EXPECT_EQ(kNumOfThreads, num_nt_prxfpreg); | ||||
|   EXPECT_EQ(num_pr_fpvalid, num_nt_prxfpreg); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| @ -44,7 +44,7 @@ void FindElfClassSection(const char *elf_base, | ||||
|                          const char *section_name, | ||||
|                          typename ElfClass::Word section_type, | ||||
|                          const void **section_start, | ||||
|                          int *section_size) { | ||||
|                          size_t *section_size) { | ||||
|   typedef typename ElfClass::Ehdr Ehdr; | ||||
|   typedef typename ElfClass::Shdr Shdr; | ||||
| 
 | ||||
| @ -58,10 +58,10 @@ void FindElfClassSection(const char *elf_base, | ||||
|   assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); | ||||
| 
 | ||||
|   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 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 Shdr* section = | ||||
| @ -79,7 +79,7 @@ template<typename ElfClass> | ||||
| void FindElfClassSegment(const char *elf_base, | ||||
|                          typename ElfClass::Word segment_type, | ||||
|                          const void **segment_start, | ||||
|                          int *segment_size) { | ||||
|                          size_t *segment_size) { | ||||
|   typedef typename ElfClass::Ehdr Ehdr; | ||||
|   typedef typename ElfClass::Phdr Phdr; | ||||
| 
 | ||||
| @ -93,7 +93,7 @@ void FindElfClassSegment(const char *elf_base, | ||||
|   assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); | ||||
| 
 | ||||
|   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) { | ||||
|     if (phdrs[i].p_type == segment_type) { | ||||
| @ -122,7 +122,7 @@ bool FindElfSection(const void *elf_mapped_base, | ||||
|                     const char *section_name, | ||||
|                     uint32_t section_type, | ||||
|                     const void **section_start, | ||||
|                     int *section_size, | ||||
|                     size_t *section_size, | ||||
|                     int *elfclass) { | ||||
|   assert(elf_mapped_base); | ||||
|   assert(section_start); | ||||
| @ -158,7 +158,7 @@ bool FindElfSection(const void *elf_mapped_base, | ||||
| bool FindElfSegment(const void *elf_mapped_base, | ||||
|                     uint32_t segment_type, | ||||
|                     const void **segment_start, | ||||
|                     int *segment_size, | ||||
|                     size_t *segment_size, | ||||
|                     int *elfclass) { | ||||
|   assert(elf_mapped_base); | ||||
|   assert(segment_start); | ||||
|  | ||||
| @ -30,8 +30,8 @@ | ||||
| // elfutils.h: Utilities for dealing with ELF files.
 | ||||
| //
 | ||||
| 
 | ||||
| #ifndef COMMON_LINUX_ELFUTILS_H__ | ||||
| #define COMMON_LINUX_ELFUTILS_H__ | ||||
| #ifndef COMMON_LINUX_ELFUTILS_H_ | ||||
| #define COMMON_LINUX_ELFUTILS_H_ | ||||
| 
 | ||||
| #include <elf.h> | ||||
| #include <link.h> | ||||
| @ -79,7 +79,7 @@ bool FindElfSection(const void *elf_mapped_base, | ||||
|                     const char *section_name, | ||||
|                     uint32_t section_type, | ||||
|                     const void **section_start, | ||||
|                     int *section_size, | ||||
|                     size_t *section_size, | ||||
|                     int *elfclass); | ||||
| 
 | ||||
| // Internal helper method, exposed for convenience for callers
 | ||||
| @ -101,7 +101,7 @@ FindElfSectionByName(const char* name, | ||||
| bool FindElfSegment(const void *elf_mapped_base, | ||||
|                     uint32_t segment_type, | ||||
|                     const void **segment_start, | ||||
|                     int *segment_size, | ||||
|                     size_t *segment_size, | ||||
|                     int *elfclass); | ||||
| 
 | ||||
| // 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
 | ||||
| 
 | ||||
| #endif  // COMMON_LINUX_ELFUTILS_H__
 | ||||
| #endif  // COMMON_LINUX_ELFUTILS_H_
 | ||||
|  | ||||
| @ -48,9 +48,7 @@ | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| FileID::FileID(const char* path) { | ||||
|   strncpy(path_, path, sizeof(path_)); | ||||
| } | ||||
| FileID::FileID(const char* path) : path_(path) {} | ||||
| 
 | ||||
| // ELF note name and desc are 32-bits word padded.
 | ||||
| #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.
 | ||||
| 
 | ||||
| template<typename ElfClass> | ||||
| static bool ElfClassBuildIDNoteIdentifier(const void *section, int length, | ||||
| static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length, | ||||
|                                           uint8_t identifier[kMDGUIDSize]) { | ||||
|   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, | ||||
|                                uint8_t identifier[kMDGUIDSize]) { | ||||
|   void* note_section; | ||||
|   int note_size, elfclass; | ||||
|   size_t note_size; | ||||
|   int elfclass; | ||||
|   if ((!FindElfSegment(elf_mapped_base, PT_NOTE, | ||||
|                        (const void**)¬e_section, ¬e_size, &elfclass) || | ||||
|       note_size == 0)  && | ||||
| @ -120,7 +119,7 @@ static bool FindElfBuildIDNote(const void *elf_mapped_base, | ||||
| static bool HashElfTextSection(const void *elf_mapped_base, | ||||
|                                uint8_t identifier[kMDGUIDSize]) { | ||||
|   void* text_section; | ||||
|   int text_size; | ||||
|   size_t text_size; | ||||
|   if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, | ||||
|                       (const void**)&text_section, &text_size, NULL) || | ||||
|       text_size == 0) { | ||||
| @ -129,7 +128,7 @@ static bool HashElfTextSection(const void *elf_mapped_base, | ||||
| 
 | ||||
|   my_memset(identifier, 0, kMDGUIDSize); | ||||
|   const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); | ||||
|   const uint8_t* ptr_end = ptr + std::min(text_size, 4096); | ||||
|   const uint8_t* ptr_end = ptr + std::min(text_size, static_cast<size_t>(4096)); | ||||
|   while (ptr < ptr_end) { | ||||
|     for (unsigned i = 0; i < kMDGUIDSize; i++) | ||||
|       identifier[i] ^= ptr[i]; | ||||
| @ -150,7 +149,7 @@ bool FileID::ElfFileIdentifierFromMappedFile(const void* base, | ||||
| } | ||||
| 
 | ||||
| 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)?
 | ||||
|     return false; | ||||
| 
 | ||||
|  | ||||
| @ -34,6 +34,7 @@ | ||||
| #define COMMON_LINUX_FILE_ID_H__ | ||||
| 
 | ||||
| #include <limits.h> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/linux/guid_creator.h" | ||||
| 
 | ||||
| @ -69,7 +70,7 @@ class FileID { | ||||
| 
 | ||||
|  private: | ||||
|   // Storage for the path specified
 | ||||
|   char path_[PATH_MAX]; | ||||
|   std::string path_; | ||||
| }; | ||||
| 
 | ||||
| }  // namespace google_breakpad
 | ||||
|  | ||||
| @ -66,6 +66,9 @@ void PopulateSection(Section* section, int size, int prime_number) { | ||||
| 
 | ||||
| }  // namespace
 | ||||
| 
 | ||||
| #ifndef __ANDROID__ | ||||
| // This test is disabled on Android: It will always fail, since there is no
 | ||||
| // 'strip' binary installed on test devices.
 | ||||
| TEST(FileIDStripTest, StripSelf) { | ||||
|   // Calculate the File ID of this binary using
 | ||||
|   // FileID::ElfFileIdentifier, then make a copy of this binary,
 | ||||
| @ -98,6 +101,7 @@ TEST(FileIDStripTest, StripSelf) { | ||||
|                                     37); | ||||
|   EXPECT_STREQ(identifier_string1, identifier_string2); | ||||
| } | ||||
| #endif  // !__ANDROID__
 | ||||
| 
 | ||||
| template<typename ElfClass> | ||||
| class FileIDTest : public testing::Test { | ||||
|  | ||||
| @ -29,7 +29,6 @@ | ||||
| 
 | ||||
| 
 | ||||
| #include "common/linux/google_crashdump_uploader.h" | ||||
| #include "common/linux/libcurl_wrapper.h" | ||||
| 
 | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| @ -112,7 +111,7 @@ void GoogleCrashdumpUploader::Init(const string& product, | ||||
|   ctime_ = ctime; | ||||
|   email_ = email; | ||||
|   comments_ = comments; | ||||
|   http_layer_ = http_layer; | ||||
|   http_layer_.reset(http_layer); | ||||
| 
 | ||||
|   crash_server_ = crash_server; | ||||
|   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(); | ||||
|   if (!ok) { | ||||
|     std::cout << "http layer init failed"; | ||||
| @ -194,6 +195,8 @@ bool GoogleCrashdumpUploader::Upload() { | ||||
|   std::cout << "Sending request to " << crash_server_; | ||||
|   return http_layer_->SendRequest(crash_server_, | ||||
|                                   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.
 | ||||
| 
 | ||||
| 
 | ||||
| #ifndef COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_ | ||||
| #define COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_ | ||||
| 
 | ||||
| #include <string> | ||||
| #include <map> | ||||
| 
 | ||||
| #include "common/linux/libcurl_wrapper.h" | ||||
| #include "common/scoped_ptr.h" | ||||
| #include "common/using_std_string.h" | ||||
| 
 | ||||
| namespace google_breakpad { | ||||
| 
 | ||||
| class LibcurlWrapper; | ||||
| 
 | ||||
| class GoogleCrashdumpUploader { | ||||
|  public: | ||||
|   GoogleCrashdumpUploader(const string& product, | ||||
| @ -76,12 +79,14 @@ class GoogleCrashdumpUploader { | ||||
|             const string& proxy_host, | ||||
|             const string& proxy_userpassword, | ||||
|             LibcurlWrapper* http_layer); | ||||
|   bool Upload(); | ||||
|   bool Upload(int* http_status_code, | ||||
|               string* http_response_header, | ||||
|               string* http_response_body); | ||||
| 
 | ||||
|  private: | ||||
|   bool CheckRequiredParametersArePresent(); | ||||
| 
 | ||||
|   LibcurlWrapper* http_layer_; | ||||
|   scoped_ptr<LibcurlWrapper> http_layer_; | ||||
|   string product_; | ||||
|   string version_; | ||||
|   string guid_; | ||||
| @ -98,3 +103,5 @@ class GoogleCrashdumpUploader { | ||||
|   std::map<string, string> parameters_; | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| #endif  // COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_
 | ||||
|  | ||||
| @ -32,7 +32,6 @@ | ||||
| #include <string> | ||||
| 
 | ||||
| #include "common/linux/google_crashdump_uploader.h" | ||||
| #include "common/linux/libcurl_wrapper.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/using_std_string.h" | ||||
| 
 | ||||
| @ -48,10 +47,12 @@ class MockLibcurlWrapper : public LibcurlWrapper { | ||||
|                               const string& proxy_userpwd)); | ||||
|   MOCK_METHOD2(AddFile, bool(const string& upload_file_path, | ||||
|                              const string& basename)); | ||||
|   MOCK_METHOD3(SendRequest, | ||||
|   MOCK_METHOD5(SendRequest, | ||||
|                bool(const string& url, | ||||
|                     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 { | ||||
| @ -72,7 +73,7 @@ TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) { | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   &m); | ||||
|   ASSERT_FALSE(uploader->Upload()); | ||||
|   ASSERT_FALSE(uploader->Upload(NULL, NULL, NULL)); | ||||
| } | ||||
| 
 | ||||
| TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { | ||||
| @ -86,7 +87,7 @@ TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { | ||||
|   EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); | ||||
|   EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true)); | ||||
|   EXPECT_CALL(m, | ||||
|               SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true)); | ||||
|               SendRequest("http://foo.com",_,_,_,_)).Times(1).WillOnce(Return(true)); | ||||
|   GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", | ||||
|                                                                   "1.0", | ||||
|                                                                   "AAA-BBB", | ||||
| @ -99,14 +100,14 @@ TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   &m); | ||||
|   ASSERT_TRUE(uploader->Upload()); | ||||
|   ASSERT_TRUE(uploader->Upload(NULL, NULL, NULL)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { | ||||
|   MockLibcurlWrapper m; | ||||
|   EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); | ||||
|   EXPECT_CALL(m, SendRequest(_,_,_)).Times(0); | ||||
|   EXPECT_CALL(m, SendRequest(_,_,_,_,_)).Times(0); | ||||
|   GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", | ||||
|                                                                   "1.0", | ||||
|                                                                   "AAA-BBB", | ||||
| @ -119,7 +120,7 @@ TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   &m); | ||||
|   ASSERT_FALSE(uploader->Upload()); | ||||
|   ASSERT_FALSE(uploader->Upload(NULL, NULL, NULL)); | ||||
| } | ||||
| 
 | ||||
| TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { | ||||
| @ -135,7 +136,7 @@ TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { | ||||
|                                    "http://foo.com", | ||||
|                                    "", | ||||
|                                    ""); | ||||
|   ASSERT_FALSE(uploader.Upload()); | ||||
|   ASSERT_FALSE(uploader.Upload(NULL, NULL, NULL)); | ||||
| 
 | ||||
|   // Test with empty product version.
 | ||||
|   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.
 | ||||
|   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)) | ||||
|     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 (error_description != NULL) | ||||
|       *error_description = dlerror(); | ||||
|  | ||||
| @ -57,6 +57,8 @@ LibcurlWrapper::LibcurlWrapper() | ||||
|   return; | ||||
| } | ||||
| 
 | ||||
| LibcurlWrapper::~LibcurlWrapper() {} | ||||
| 
 | ||||
| bool LibcurlWrapper::SetProxy(const string& proxy_host, | ||||
|                               const string& proxy_userpwd) { | ||||
|   if (!init_ok_) { | ||||
| @ -108,7 +110,9 @@ static size_t WriteCallback(void *ptr, size_t size, | ||||
| 
 | ||||
| bool LibcurlWrapper::SendRequest(const string& url, | ||||
|                                  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()); | ||||
|   std::map<string, string>::const_iterator iter = parameters.begin(); | ||||
|   for (; iter != parameters.end(); ++iter) | ||||
| @ -118,10 +122,17 @@ bool LibcurlWrapper::SendRequest(const string& url, | ||||
|                 CURLFORM_END); | ||||
| 
 | ||||
|   (*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_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; | ||||
| @ -129,6 +140,10 @@ bool LibcurlWrapper::SendRequest(const string& url, | ||||
|   easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)> | ||||
|                        (dlsym(curl_lib_, "curl_easy_strerror")); | ||||
| 
 | ||||
|   if (http_status_code != NULL) { | ||||
|     (*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code); | ||||
|   } | ||||
| 
 | ||||
| #ifndef NDEBUG | ||||
|   if (err_code != CURLE_OK) | ||||
|     fprintf(stderr, "Failed to send http request to %s, error: %s\n", | ||||
| @ -209,6 +224,10 @@ bool LibcurlWrapper::SetFunctionPointers() { | ||||
|                                  "curl_easy_cleanup", | ||||
|                                  void(*)(CURL*)); | ||||
| 
 | ||||
|   SET_AND_CHECK_FUNCTION_POINTER(easy_getinfo_, | ||||
|                                  "curl_easy_getinfo", | ||||
|                                  CURLcode(*)(CURL *, CURLINFO info, ...)); | ||||
| 
 | ||||
|   SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_, | ||||
|                                  "curl_slist_free_all", | ||||
|                                  void(*)(curl_slist*)); | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user