Added stripped down google-breakpad repo.
This commit is contained in:
		
							parent
							
								
									bad8c99a0b
								
							
						
					
					
						commit
						744ac40f3c
					
				| @ -3,7 +3,7 @@ | |||||||
| #define _AUTO_VERSION_INFORMATION_H_ | #define _AUTO_VERSION_INFORMATION_H_ | ||||||
| 
 | 
 | ||||||
| #define SM_BUILD_TAG	 	"" | #define SM_BUILD_TAG	 	"" | ||||||
| #define SM_BUILD_UNIQUEID	"38:7b77e54e78c1" SM_BUILD_TAG | #define SM_BUILD_UNIQUEID	"0:fcdef8474873" SM_BUILD_TAG | ||||||
| #define SM_VERSION			"1.0.0" | #define SM_VERSION			"1.0.0" | ||||||
| #define SM_FULL_VERSION		SM_VERSION SM_BUILD_TAG | #define SM_FULL_VERSION		SM_VERSION SM_BUILD_TAG | ||||||
| #define SM_FILE_VERSION		1,0,0,0 | #define SM_FILE_VERSION		1,0,0,0 | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								google-breakpad/AUTHORS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								google-breakpad/AUTHORS
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | opensource@google.com | ||||||
							
								
								
									
										28
									
								
								google-breakpad/COPYING
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								google-breakpad/COPYING
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | Copyright (c) 2006, Google Inc. | ||||||
|  | All rights reserved. | ||||||
|  | 
 | ||||||
|  | Redistribution and use in source and binary forms, with or without | ||||||
|  | modification, are permitted provided that the following conditions are | ||||||
|  | met: | ||||||
|  | 
 | ||||||
|  |     * Redistributions of source code must retain the above copyright | ||||||
|  | notice, this list of conditions and the following disclaimer. | ||||||
|  |     * Redistributions in binary form must reproduce the above | ||||||
|  | copyright notice, this list of conditions and the following disclaimer | ||||||
|  | in the documentation and/or other materials provided with the | ||||||
|  | distribution. | ||||||
|  |     * Neither the name of Google Inc. nor the names of its | ||||||
|  | contributors may be used to endorse or promote products derived from | ||||||
|  | this software without specific prior written permission. | ||||||
|  | 
 | ||||||
|  | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||||
|  | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||||
|  | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||||
|  | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||||
|  | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||||
|  | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||||
|  | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
							
								
								
									
										0
									
								
								google-breakpad/ChangeLog
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								google-breakpad/ChangeLog
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										44
									
								
								google-breakpad/DEPS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								google-breakpad/DEPS
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | # Copyright (c) 2010, Google Inc. | ||||||
|  | # All rights reserved. | ||||||
|  | # | ||||||
|  | # Redistribution and use in source and binary forms, with or without | ||||||
|  | # modification, are permitted provided that the following conditions are | ||||||
|  | # met: | ||||||
|  | # | ||||||
|  | #     * Redistributions of source code must retain the above copyright | ||||||
|  | # notice, this list of conditions and the following disclaimer. | ||||||
|  | #     * Redistributions in binary form must reproduce the above | ||||||
|  | # copyright notice, this list of conditions and the following disclaimer | ||||||
|  | # in the documentation and/or other materials provided with the | ||||||
|  | # distribution. | ||||||
|  | #     * Neither the name of Google Inc. nor the names of its | ||||||
|  | # contributors may be used to endorse or promote products derived from | ||||||
|  | # this software without specific prior written permission. | ||||||
|  | # | ||||||
|  | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||||
|  | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||||
|  | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||||
|  | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||||
|  | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||||
|  | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||||
|  | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | 
 | ||||||
|  | # We only use this file to ease the steps of generating projects after | ||||||
|  | # syncing, if we use gclient. All dependencies are svn:externals instead. | ||||||
|  | # If you're not using gclient, you need to run the gyp python script to | ||||||
|  | # generate the projects. | ||||||
|  | # This can be done by the following command (assuming current directory): | ||||||
|  | #   src\tools\gyp\gyp.bat src\client\windows\breakpad_client.gyp | ||||||
|  | hooks = [ | ||||||
|  |   { | ||||||
|  |     # A change to a .gyp, .gypi, or to GYP itself should run the generator. | ||||||
|  |     "pattern": ".", | ||||||
|  |     "action": ["python", | ||||||
|  |                "src/src/tools/gyp/gyp", | ||||||
|  |                "src/src/client/windows/breakpad_client.gyp"], | ||||||
|  |   }, | ||||||
|  | ] | ||||||
							
								
								
									
										234
									
								
								google-breakpad/INSTALL
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								google-breakpad/INSTALL
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,234 @@ | |||||||
|  | Installation Instructions | ||||||
|  | ************************* | ||||||
|  | 
 | ||||||
|  | Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, | ||||||
|  | 2006 Free Software Foundation, Inc. | ||||||
|  | 
 | ||||||
|  | This file is free documentation; the Free Software Foundation gives | ||||||
|  | unlimited permission to copy, distribute and modify it. | ||||||
|  | 
 | ||||||
|  | Basic Installation | ||||||
|  | ================== | ||||||
|  | 
 | ||||||
|  | Briefly, the shell commands `./configure; make; make install' should | ||||||
|  | configure, build, and install this package.  The following | ||||||
|  | more-detailed instructions are generic; see the `README' file for | ||||||
|  | instructions specific to this package. | ||||||
|  | 
 | ||||||
|  |    The `configure' shell script attempts to guess correct values for | ||||||
|  | various system-dependent variables used during compilation.  It uses | ||||||
|  | those values to create a `Makefile' in each directory of the package. | ||||||
|  | It may also create one or more `.h' files containing system-dependent | ||||||
|  | definitions.  Finally, it creates a shell script `config.status' that | ||||||
|  | you can run in the future to recreate the current configuration, and a | ||||||
|  | file `config.log' containing compiler output (useful mainly for | ||||||
|  | debugging `configure'). | ||||||
|  | 
 | ||||||
|  |    It can also use an optional file (typically called `config.cache' | ||||||
|  | and enabled with `--cache-file=config.cache' or simply `-C') that saves | ||||||
|  | the results of its tests to speed up reconfiguring.  Caching is | ||||||
|  | disabled by default to prevent problems with accidental use of stale | ||||||
|  | cache files. | ||||||
|  | 
 | ||||||
|  |    If you need to do unusual things to compile the package, please try | ||||||
|  | to figure out how `configure' could check whether to do them, and mail | ||||||
|  | diffs or instructions to the address given in the `README' so they can | ||||||
|  | be considered for the next release.  If you are using the cache, and at | ||||||
|  | some point `config.cache' contains results you don't want to keep, you | ||||||
|  | may remove or edit it. | ||||||
|  | 
 | ||||||
|  |    The file `configure.ac' (or `configure.in') is used to create | ||||||
|  | `configure' by a program called `autoconf'.  You need `configure.ac' if | ||||||
|  | you want to change it or regenerate `configure' using a newer version | ||||||
|  | of `autoconf'. | ||||||
|  | 
 | ||||||
|  | The simplest way to compile this package is: | ||||||
|  | 
 | ||||||
|  |   1. `cd' to the directory containing the package's source code and type | ||||||
|  |      `./configure' to configure the package for your system. | ||||||
|  | 
 | ||||||
|  |      Running `configure' might take a while.  While running, it prints | ||||||
|  |      some messages telling which features it is checking for. | ||||||
|  | 
 | ||||||
|  |   2. Type `make' to compile the package. | ||||||
|  | 
 | ||||||
|  |   3. Optionally, type `make check' to run any self-tests that come with | ||||||
|  |      the package. | ||||||
|  | 
 | ||||||
|  |   4. Type `make install' to install the programs and any data files and | ||||||
|  |      documentation. | ||||||
|  | 
 | ||||||
|  |   5. You can remove the program binaries and object files from the | ||||||
|  |      source code directory by typing `make clean'.  To also remove the | ||||||
|  |      files that `configure' created (so you can compile the package for | ||||||
|  |      a different kind of computer), type `make distclean'.  There is | ||||||
|  |      also a `make maintainer-clean' target, but that is intended mainly | ||||||
|  |      for the package's developers.  If you use it, you may have to get | ||||||
|  |      all sorts of other programs in order to regenerate files that came | ||||||
|  |      with the distribution. | ||||||
|  | 
 | ||||||
|  | Compilers and Options | ||||||
|  | ===================== | ||||||
|  | 
 | ||||||
|  | Some systems require unusual options for compilation or linking that the | ||||||
|  | `configure' script does not know about.  Run `./configure --help' for | ||||||
|  | details on some of the pertinent environment variables. | ||||||
|  | 
 | ||||||
|  |    You can give `configure' initial values for configuration parameters | ||||||
|  | by setting variables in the command line or in the environment.  Here | ||||||
|  | is an example: | ||||||
|  | 
 | ||||||
|  |      ./configure CC=c99 CFLAGS=-g LIBS=-lposix | ||||||
|  | 
 | ||||||
|  |    *Note Defining Variables::, for more details. | ||||||
|  | 
 | ||||||
|  | Compiling For Multiple Architectures | ||||||
|  | ==================================== | ||||||
|  | 
 | ||||||
|  | You can compile the package for more than one kind of computer at the | ||||||
|  | same time, by placing the object files for each architecture in their | ||||||
|  | own directory.  To do this, you can use GNU `make'.  `cd' to the | ||||||
|  | directory where you want the object files and executables to go and run | ||||||
|  | the `configure' script.  `configure' automatically checks for the | ||||||
|  | source code in the directory that `configure' is in and in `..'. | ||||||
|  | 
 | ||||||
|  |    With a non-GNU `make', it is safer to compile the package for one | ||||||
|  | architecture at a time in the source code directory.  After you have | ||||||
|  | installed the package for one architecture, use `make distclean' before | ||||||
|  | reconfiguring for another architecture. | ||||||
|  | 
 | ||||||
|  | Installation Names | ||||||
|  | ================== | ||||||
|  | 
 | ||||||
|  | By default, `make install' installs the package's commands under | ||||||
|  | `/usr/local/bin', include files under `/usr/local/include', etc.  You | ||||||
|  | can specify an installation prefix other than `/usr/local' by giving | ||||||
|  | `configure' the option `--prefix=PREFIX'. | ||||||
|  | 
 | ||||||
|  |    You can specify separate installation prefixes for | ||||||
|  | architecture-specific files and architecture-independent files.  If you | ||||||
|  | pass the option `--exec-prefix=PREFIX' to `configure', the package uses | ||||||
|  | PREFIX as the prefix for installing programs and libraries. | ||||||
|  | Documentation and other data files still use the regular prefix. | ||||||
|  | 
 | ||||||
|  |    In addition, if you use an unusual directory layout you can give | ||||||
|  | options like `--bindir=DIR' to specify different values for particular | ||||||
|  | kinds of files.  Run `configure --help' for a list of the directories | ||||||
|  | you can set and what kinds of files go in them. | ||||||
|  | 
 | ||||||
|  |    If the package supports it, you can cause programs to be installed | ||||||
|  | with an extra prefix or suffix on their names by giving `configure' the | ||||||
|  | option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. | ||||||
|  | 
 | ||||||
|  | Optional Features | ||||||
|  | ================= | ||||||
|  | 
 | ||||||
|  | Some packages pay attention to `--enable-FEATURE' options to | ||||||
|  | `configure', where FEATURE indicates an optional part of the package. | ||||||
|  | They may also pay attention to `--with-PACKAGE' options, where PACKAGE | ||||||
|  | is something like `gnu-as' or `x' (for the X Window System).  The | ||||||
|  | `README' should mention any `--enable-' and `--with-' options that the | ||||||
|  | package recognizes. | ||||||
|  | 
 | ||||||
|  |    For packages that use the X Window System, `configure' can usually | ||||||
|  | find the X include and library files automatically, but if it doesn't, | ||||||
|  | you can use the `configure' options `--x-includes=DIR' and | ||||||
|  | `--x-libraries=DIR' to specify their locations. | ||||||
|  | 
 | ||||||
|  | Specifying the System Type | ||||||
|  | ========================== | ||||||
|  | 
 | ||||||
|  | There may be some features `configure' cannot figure out automatically, | ||||||
|  | but needs to determine by the type of machine the package will run on. | ||||||
|  | Usually, assuming the package is built to be run on the _same_ | ||||||
|  | architectures, `configure' can figure that out, but if it prints a | ||||||
|  | message saying it cannot guess the machine type, give it the | ||||||
|  | `--build=TYPE' option.  TYPE can either be a short name for the system | ||||||
|  | type, such as `sun4', or a canonical name which has the form: | ||||||
|  | 
 | ||||||
|  |      CPU-COMPANY-SYSTEM | ||||||
|  | 
 | ||||||
|  | where SYSTEM can have one of these forms: | ||||||
|  | 
 | ||||||
|  |      OS KERNEL-OS | ||||||
|  | 
 | ||||||
|  |    See the file `config.sub' for the possible values of each field.  If | ||||||
|  | `config.sub' isn't included in this package, then this package doesn't | ||||||
|  | need to know the machine type. | ||||||
|  | 
 | ||||||
|  |    If you are _building_ compiler tools for cross-compiling, you should | ||||||
|  | use the option `--target=TYPE' to select the type of system they will | ||||||
|  | produce code for. | ||||||
|  | 
 | ||||||
|  |    If you want to _use_ a cross compiler, that generates code for a | ||||||
|  | platform different from the build platform, you should specify the | ||||||
|  | "host" platform (i.e., that on which the generated programs will | ||||||
|  | eventually be run) with `--host=TYPE'. | ||||||
|  | 
 | ||||||
|  | Sharing Defaults | ||||||
|  | ================ | ||||||
|  | 
 | ||||||
|  | If you want to set default values for `configure' scripts to share, you | ||||||
|  | can create a site shell script called `config.site' that gives default | ||||||
|  | values for variables like `CC', `cache_file', and `prefix'. | ||||||
|  | `configure' looks for `PREFIX/share/config.site' if it exists, then | ||||||
|  | `PREFIX/etc/config.site' if it exists.  Or, you can set the | ||||||
|  | `CONFIG_SITE' environment variable to the location of the site script. | ||||||
|  | A warning: not all `configure' scripts look for a site script. | ||||||
|  | 
 | ||||||
|  | Defining Variables | ||||||
|  | ================== | ||||||
|  | 
 | ||||||
|  | Variables not defined in a site shell script can be set in the | ||||||
|  | environment passed to `configure'.  However, some packages may run | ||||||
|  | configure again during the build, and the customized values of these | ||||||
|  | variables may be lost.  In order to avoid this problem, you should set | ||||||
|  | them in the `configure' command line, using `VAR=value'.  For example: | ||||||
|  | 
 | ||||||
|  |      ./configure CC=/usr/local2/bin/gcc | ||||||
|  | 
 | ||||||
|  | causes the specified `gcc' to be used as the C compiler (unless it is | ||||||
|  | overridden in the site shell script). | ||||||
|  | 
 | ||||||
|  | Unfortunately, this technique does not work for `CONFIG_SHELL' due to | ||||||
|  | an Autoconf bug.  Until the bug is fixed you can use this workaround: | ||||||
|  | 
 | ||||||
|  |      CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash | ||||||
|  | 
 | ||||||
|  | `configure' Invocation | ||||||
|  | ====================== | ||||||
|  | 
 | ||||||
|  | `configure' recognizes the following options to control how it operates. | ||||||
|  | 
 | ||||||
|  | `--help' | ||||||
|  | `-h' | ||||||
|  |      Print a summary of the options to `configure', and exit. | ||||||
|  | 
 | ||||||
|  | `--version' | ||||||
|  | `-V' | ||||||
|  |      Print the version of Autoconf used to generate the `configure' | ||||||
|  |      script, and exit. | ||||||
|  | 
 | ||||||
|  | `--cache-file=FILE' | ||||||
|  |      Enable the cache: use and save the results of the tests in FILE, | ||||||
|  |      traditionally `config.cache'.  FILE defaults to `/dev/null' to | ||||||
|  |      disable caching. | ||||||
|  | 
 | ||||||
|  | `--config-cache' | ||||||
|  | `-C' | ||||||
|  |      Alias for `--cache-file=config.cache'. | ||||||
|  | 
 | ||||||
|  | `--quiet' | ||||||
|  | `--silent' | ||||||
|  | `-q' | ||||||
|  |      Do not print messages saying which checks are being made.  To | ||||||
|  |      suppress all normal output, redirect it to `/dev/null' (any error | ||||||
|  |      messages will still be shown). | ||||||
|  | 
 | ||||||
|  | `--srcdir=DIR' | ||||||
|  |      Look for the package's source code in directory DIR.  Usually | ||||||
|  |      `configure' can determine that directory automatically. | ||||||
|  | 
 | ||||||
|  | `configure' also accepts some other, not widely useful, options.  Run | ||||||
|  | `configure --help' for more details. | ||||||
|  | 
 | ||||||
							
								
								
									
										1112
									
								
								google-breakpad/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1112
									
								
								google-breakpad/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										6397
									
								
								google-breakpad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6397
									
								
								google-breakpad/Makefile.in
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										0
									
								
								google-breakpad/NEWS
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								google-breakpad/NEWS
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										43
									
								
								google-breakpad/README
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								google-breakpad/README
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | |||||||
|  | Breakpad is a set of client and server components which implement a | ||||||
|  | crash-reporting system. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ----- | ||||||
|  | Getting started in 32-bit mode (from trunk) | ||||||
|  | Configure: CXXFLAGS=-m32 CFLAGS=-m32 CPPFLAGS=-m32 ./configure | ||||||
|  |     Build: make | ||||||
|  |      Test: make check | ||||||
|  |   Install: make install | ||||||
|  | 
 | ||||||
|  | If you need to reconfigure your build be sure to run "make distclean" first. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ----- | ||||||
|  | To request change review: | ||||||
|  | 0. Get access to a read-write copy of source. | ||||||
|  |    Owners at http://code.google.com/p/google-breakpad/ are able to grant | ||||||
|  |    this access. | ||||||
|  | 
 | ||||||
|  | 1. Check out a read-write copy of source using instructions at | ||||||
|  |    http://code.google.com/p/google-breakpad/source/checkout | ||||||
|  | 
 | ||||||
|  | 2. Make changes. Build and test your changes. | ||||||
|  |    For core code like processor use methods above. | ||||||
|  |    For linux/mac/windows, there are test targets in each project file. | ||||||
|  | 
 | ||||||
|  | 3. Download http://codereview.appspot.com/static/upload.py | ||||||
|  | 
 | ||||||
|  | 4. Run upload.py from the 'src' directory: | ||||||
|  |    upload.py --server=breakpad.appspot.com | ||||||
|  | 
 | ||||||
|  |    You will be prompted for credential and a description. | ||||||
|  | 
 | ||||||
|  | 5. At http://breakpad.appspot.com you'll find your issue listed; click on it, | ||||||
|  |    and select Publish+Mail, and enter in the code reviewer and CC | ||||||
|  |    google-breakpad-dev@googlegroups.com | ||||||
|  | 
 | ||||||
|  | 6. When applying code review feedback, specify the '-i' option when running | ||||||
|  |    upload.py again and pass the issue number so it updates the existing issue, | ||||||
|  |    rather than creating a new one. | ||||||
|  |    Be sure to rerun upload.py from the same directory as you did for previous | ||||||
|  |    uploads to allow for proper diff calculations. | ||||||
							
								
								
									
										1037
									
								
								google-breakpad/aclocal.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1037
									
								
								google-breakpad/aclocal.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1
									
								
								google-breakpad/autotools/compile
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								google-breakpad/autotools/compile
									
									
									
									
									
										Symbolic link
									
								
							| @ -0,0 +1 @@ | |||||||
|  | /usr/share/automake-1.11/compile | ||||||
							
								
								
									
										1534
									
								
								google-breakpad/autotools/config.guess
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										1534
									
								
								google-breakpad/autotools/config.guess
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1782
									
								
								google-breakpad/autotools/config.sub
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										1782
									
								
								google-breakpad/autotools/config.sub
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										584
									
								
								google-breakpad/autotools/depcomp
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										584
									
								
								google-breakpad/autotools/depcomp
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,584 @@ | |||||||
|  | #! /bin/sh | ||||||
|  | # depcomp - compile a program generating dependencies as side-effects | ||||||
|  | 
 | ||||||
|  | scriptversion=2006-10-15.18 | ||||||
|  | 
 | ||||||
|  | # Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 Free Software | ||||||
|  | # Foundation, Inc. | ||||||
|  | 
 | ||||||
|  | # This program is free software; you can redistribute it and/or modify | ||||||
|  | # it under the terms of the GNU General Public License as published by | ||||||
|  | # the Free Software Foundation; either version 2, or (at your option) | ||||||
|  | # any later version. | ||||||
|  | 
 | ||||||
|  | # This program is distributed in the hope that it will be useful, | ||||||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | # GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | # You should have received a copy of the GNU General Public License | ||||||
|  | # along with this program; if not, write to the Free Software | ||||||
|  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||||
|  | # 02110-1301, USA. | ||||||
|  | 
 | ||||||
|  | # As a special exception to the GNU General Public License, if you | ||||||
|  | # distribute this file as part of a program that contains a | ||||||
|  | # configuration script generated by Autoconf, you may include it under | ||||||
|  | # the same distribution terms that you use for the rest of that program. | ||||||
|  | 
 | ||||||
|  | # Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>. | ||||||
|  | 
 | ||||||
|  | case $1 in | ||||||
|  |   '') | ||||||
|  |      echo "$0: No command.  Try \`$0 --help' for more information." 1>&2 | ||||||
|  |      exit 1; | ||||||
|  |      ;; | ||||||
|  |   -h | --h*) | ||||||
|  |     cat <<\EOF | ||||||
|  | Usage: depcomp [--help] [--version] PROGRAM [ARGS] | ||||||
|  | 
 | ||||||
|  | Run PROGRAMS ARGS to compile a file, generating dependencies | ||||||
|  | as side-effects. | ||||||
|  | 
 | ||||||
|  | Environment variables: | ||||||
|  |   depmode     Dependency tracking mode. | ||||||
|  |   source      Source file read by `PROGRAMS ARGS'. | ||||||
|  |   object      Object file output by `PROGRAMS ARGS'. | ||||||
|  |   DEPDIR      directory where to store dependencies. | ||||||
|  |   depfile     Dependency file to output. | ||||||
|  |   tmpdepfile  Temporary file to use when outputing dependencies. | ||||||
|  |   libtool     Whether libtool is used (yes/no). | ||||||
|  | 
 | ||||||
|  | Report bugs to <bug-automake@gnu.org>. | ||||||
|  | EOF | ||||||
|  |     exit $? | ||||||
|  |     ;; | ||||||
|  |   -v | --v*) | ||||||
|  |     echo "depcomp $scriptversion" | ||||||
|  |     exit $? | ||||||
|  |     ;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | if test -z "$depmode" || test -z "$source" || test -z "$object"; then | ||||||
|  |   echo "depcomp: Variables source, object and depmode must be set" 1>&2 | ||||||
|  |   exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. | ||||||
|  | depfile=${depfile-`echo "$object" | | ||||||
|  |   sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} | ||||||
|  | tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} | ||||||
|  | 
 | ||||||
|  | rm -f "$tmpdepfile" | ||||||
|  | 
 | ||||||
|  | # Some modes work just like other modes, but use different flags.  We | ||||||
|  | # parameterize here, but still list the modes in the big case below, | ||||||
|  | # to make depend.m4 easier to write.  Note that we *cannot* use a case | ||||||
|  | # here, because this file can only contain one case statement. | ||||||
|  | if test "$depmode" = hp; then | ||||||
|  |   # HP compiler uses -M and no extra arg. | ||||||
|  |   gccflag=-M | ||||||
|  |   depmode=gcc | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if test "$depmode" = dashXmstdout; then | ||||||
|  |    # This is just like dashmstdout with a different argument. | ||||||
|  |    dashmflag=-xM | ||||||
|  |    depmode=dashmstdout | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | case "$depmode" in | ||||||
|  | gcc3) | ||||||
|  | ## gcc 3 implements dependency tracking that does exactly what | ||||||
|  | ## we want.  Yay!  Note: for some reason libtool 1.4 doesn't like | ||||||
|  | ## it if -MD -MP comes after the -MF stuff.  Hmm. | ||||||
|  | ## Unfortunately, FreeBSD c89 acceptance of flags depends upon | ||||||
|  | ## the command line argument order; so add the flags where they | ||||||
|  | ## appear in depend2.am.  Note that the slowdown incurred here | ||||||
|  | ## affects only configure: in makefiles, %FASTDEP% shortcuts this. | ||||||
|  |   for arg | ||||||
|  |   do | ||||||
|  |     case $arg in | ||||||
|  |     -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; | ||||||
|  |     *)  set fnord "$@" "$arg" ;; | ||||||
|  |     esac | ||||||
|  |     shift # fnord | ||||||
|  |     shift # $arg | ||||||
|  |   done | ||||||
|  |   "$@" | ||||||
|  |   stat=$? | ||||||
|  |   if test $stat -eq 0; then : | ||||||
|  |   else | ||||||
|  |     rm -f "$tmpdepfile" | ||||||
|  |     exit $stat | ||||||
|  |   fi | ||||||
|  |   mv "$tmpdepfile" "$depfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | gcc) | ||||||
|  | ## There are various ways to get dependency output from gcc.  Here's | ||||||
|  | ## why we pick this rather obscure method: | ||||||
|  | ## - Don't want to use -MD because we'd like the dependencies to end | ||||||
|  | ##   up in a subdir.  Having to rename by hand is ugly. | ||||||
|  | ##   (We might end up doing this anyway to support other compilers.) | ||||||
|  | ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like | ||||||
|  | ##   -MM, not -M (despite what the docs say). | ||||||
|  | ## - Using -M directly means running the compiler twice (even worse | ||||||
|  | ##   than renaming). | ||||||
|  |   if test -z "$gccflag"; then | ||||||
|  |     gccflag=-MD, | ||||||
|  |   fi | ||||||
|  |   "$@" -Wp,"$gccflag$tmpdepfile" | ||||||
|  |   stat=$? | ||||||
|  |   if test $stat -eq 0; then : | ||||||
|  |   else | ||||||
|  |     rm -f "$tmpdepfile" | ||||||
|  |     exit $stat | ||||||
|  |   fi | ||||||
|  |   rm -f "$depfile" | ||||||
|  |   echo "$object : \\" > "$depfile" | ||||||
|  |   alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz | ||||||
|  | ## The second -e expression handles DOS-style file names with drive letters. | ||||||
|  |   sed -e 's/^[^:]*: / /' \ | ||||||
|  |       -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" | ||||||
|  | ## This next piece of magic avoids the `deleted header file' problem. | ||||||
|  | ## The problem is that when a header file which appears in a .P file | ||||||
|  | ## is deleted, the dependency causes make to die (because there is | ||||||
|  | ## typically no way to rebuild the header).  We avoid this by adding | ||||||
|  | ## dummy dependencies for each header file.  Too bad gcc doesn't do | ||||||
|  | ## this for us directly. | ||||||
|  |   tr ' ' ' | ||||||
|  | ' < "$tmpdepfile" | | ||||||
|  | ## Some versions of gcc put a space before the `:'.  On the theory | ||||||
|  | ## that the space means something, we add a space to the output as | ||||||
|  | ## well. | ||||||
|  | ## Some versions of the HPUX 10.20 sed can't process this invocation | ||||||
|  | ## correctly.  Breaking it into two sed invocations is a workaround. | ||||||
|  |     sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" | ||||||
|  |   rm -f "$tmpdepfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | hp) | ||||||
|  |   # This case exists only to let depend.m4 do its work.  It works by | ||||||
|  |   # looking at the text of this script.  This case will never be run, | ||||||
|  |   # since it is checked for above. | ||||||
|  |   exit 1 | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | sgi) | ||||||
|  |   if test "$libtool" = yes; then | ||||||
|  |     "$@" "-Wp,-MDupdate,$tmpdepfile" | ||||||
|  |   else | ||||||
|  |     "$@" -MDupdate "$tmpdepfile" | ||||||
|  |   fi | ||||||
|  |   stat=$? | ||||||
|  |   if test $stat -eq 0; then : | ||||||
|  |   else | ||||||
|  |     rm -f "$tmpdepfile" | ||||||
|  |     exit $stat | ||||||
|  |   fi | ||||||
|  |   rm -f "$depfile" | ||||||
|  | 
 | ||||||
|  |   if test -f "$tmpdepfile"; then  # yes, the sourcefile depend on other files | ||||||
|  |     echo "$object : \\" > "$depfile" | ||||||
|  | 
 | ||||||
|  |     # Clip off the initial element (the dependent).  Don't try to be | ||||||
|  |     # clever and replace this with sed code, as IRIX sed won't handle | ||||||
|  |     # lines with more than a fixed number of characters (4096 in | ||||||
|  |     # IRIX 6.2 sed, 8192 in IRIX 6.5).  We also remove comment lines; | ||||||
|  |     # the IRIX cc adds comments like `#:fec' to the end of the | ||||||
|  |     # dependency line. | ||||||
|  |     tr ' ' ' | ||||||
|  | ' < "$tmpdepfile" \ | ||||||
|  |     | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ | ||||||
|  |     tr ' | ||||||
|  | ' ' ' >> $depfile | ||||||
|  |     echo >> $depfile | ||||||
|  | 
 | ||||||
|  |     # The second pass generates a dummy entry for each header file. | ||||||
|  |     tr ' ' ' | ||||||
|  | ' < "$tmpdepfile" \ | ||||||
|  |    | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ | ||||||
|  |    >> $depfile | ||||||
|  |   else | ||||||
|  |     # The sourcefile does not contain any dependencies, so just | ||||||
|  |     # store a dummy comment line, to avoid errors with the Makefile | ||||||
|  |     # "include basename.Plo" scheme. | ||||||
|  |     echo "#dummy" > "$depfile" | ||||||
|  |   fi | ||||||
|  |   rm -f "$tmpdepfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | aix) | ||||||
|  |   # The C for AIX Compiler uses -M and outputs the dependencies | ||||||
|  |   # in a .u file.  In older versions, this file always lives in the | ||||||
|  |   # current directory.  Also, the AIX compiler puts `$object:' at the | ||||||
|  |   # start of each line; $object doesn't have directory information. | ||||||
|  |   # Version 6 uses the directory in both cases. | ||||||
|  |   stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` | ||||||
|  |   tmpdepfile="$stripped.u" | ||||||
|  |   if test "$libtool" = yes; then | ||||||
|  |     "$@" -Wc,-M | ||||||
|  |   else | ||||||
|  |     "$@" -M | ||||||
|  |   fi | ||||||
|  |   stat=$? | ||||||
|  | 
 | ||||||
|  |   if test -f "$tmpdepfile"; then : | ||||||
|  |   else | ||||||
|  |     stripped=`echo "$stripped" | sed 's,^.*/,,'` | ||||||
|  |     tmpdepfile="$stripped.u" | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if test $stat -eq 0; then : | ||||||
|  |   else | ||||||
|  |     rm -f "$tmpdepfile" | ||||||
|  |     exit $stat | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if test -f "$tmpdepfile"; then | ||||||
|  |     outname="$stripped.o" | ||||||
|  |     # Each line is of the form `foo.o: dependent.h'. | ||||||
|  |     # Do two passes, one to just change these to | ||||||
|  |     # `$object: dependent.h' and one to simply `dependent.h:'. | ||||||
|  |     sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" | ||||||
|  |     sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" | ||||||
|  |   else | ||||||
|  |     # The sourcefile does not contain any dependencies, so just | ||||||
|  |     # store a dummy comment line, to avoid errors with the Makefile | ||||||
|  |     # "include basename.Plo" scheme. | ||||||
|  |     echo "#dummy" > "$depfile" | ||||||
|  |   fi | ||||||
|  |   rm -f "$tmpdepfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | icc) | ||||||
|  |   # Intel's C compiler understands `-MD -MF file'.  However on | ||||||
|  |   #    icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c | ||||||
|  |   # ICC 7.0 will fill foo.d with something like | ||||||
|  |   #    foo.o: sub/foo.c | ||||||
|  |   #    foo.o: sub/foo.h | ||||||
|  |   # which is wrong.  We want: | ||||||
|  |   #    sub/foo.o: sub/foo.c | ||||||
|  |   #    sub/foo.o: sub/foo.h | ||||||
|  |   #    sub/foo.c: | ||||||
|  |   #    sub/foo.h: | ||||||
|  |   # ICC 7.1 will output | ||||||
|  |   #    foo.o: sub/foo.c sub/foo.h | ||||||
|  |   # and will wrap long lines using \ : | ||||||
|  |   #    foo.o: sub/foo.c ... \ | ||||||
|  |   #     sub/foo.h ... \ | ||||||
|  |   #     ... | ||||||
|  | 
 | ||||||
|  |   "$@" -MD -MF "$tmpdepfile" | ||||||
|  |   stat=$? | ||||||
|  |   if test $stat -eq 0; then : | ||||||
|  |   else | ||||||
|  |     rm -f "$tmpdepfile" | ||||||
|  |     exit $stat | ||||||
|  |   fi | ||||||
|  |   rm -f "$depfile" | ||||||
|  |   # Each line is of the form `foo.o: dependent.h', | ||||||
|  |   # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. | ||||||
|  |   # Do two passes, one to just change these to | ||||||
|  |   # `$object: dependent.h' and one to simply `dependent.h:'. | ||||||
|  |   sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" | ||||||
|  |   # Some versions of the HPUX 10.20 sed can't process this invocation | ||||||
|  |   # correctly.  Breaking it into two sed invocations is a workaround. | ||||||
|  |   sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | | ||||||
|  |     sed -e 's/$/ :/' >> "$depfile" | ||||||
|  |   rm -f "$tmpdepfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | hp2) | ||||||
|  |   # The "hp" stanza above does not work with aCC (C++) and HP's ia64 | ||||||
|  |   # compilers, which have integrated preprocessors.  The correct option | ||||||
|  |   # to use with these is +Maked; it writes dependencies to a file named | ||||||
|  |   # 'foo.d', which lands next to the object file, wherever that | ||||||
|  |   # happens to be. | ||||||
|  |   # Much of this is similar to the tru64 case; see comments there. | ||||||
|  |   dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` | ||||||
|  |   test "x$dir" = "x$object" && dir= | ||||||
|  |   base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` | ||||||
|  |   if test "$libtool" = yes; then | ||||||
|  |     tmpdepfile1=$dir$base.d | ||||||
|  |     tmpdepfile2=$dir.libs/$base.d | ||||||
|  |     "$@" -Wc,+Maked | ||||||
|  |   else | ||||||
|  |     tmpdepfile1=$dir$base.d | ||||||
|  |     tmpdepfile2=$dir$base.d | ||||||
|  |     "$@" +Maked | ||||||
|  |   fi | ||||||
|  |   stat=$? | ||||||
|  |   if test $stat -eq 0; then : | ||||||
|  |   else | ||||||
|  |      rm -f "$tmpdepfile1" "$tmpdepfile2" | ||||||
|  |      exit $stat | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" | ||||||
|  |   do | ||||||
|  |     test -f "$tmpdepfile" && break | ||||||
|  |   done | ||||||
|  |   if test -f "$tmpdepfile"; then | ||||||
|  |     sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" | ||||||
|  |     # Add `dependent.h:' lines. | ||||||
|  |     sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile" | ||||||
|  |   else | ||||||
|  |     echo "#dummy" > "$depfile" | ||||||
|  |   fi | ||||||
|  |   rm -f "$tmpdepfile" "$tmpdepfile2" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | tru64) | ||||||
|  |    # The Tru64 compiler uses -MD to generate dependencies as a side | ||||||
|  |    # effect.  `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. | ||||||
|  |    # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put | ||||||
|  |    # dependencies in `foo.d' instead, so we check for that too. | ||||||
|  |    # Subdirectories are respected. | ||||||
|  |    dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` | ||||||
|  |    test "x$dir" = "x$object" && dir= | ||||||
|  |    base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` | ||||||
|  | 
 | ||||||
|  |    if test "$libtool" = yes; then | ||||||
|  |       # With Tru64 cc, shared objects can also be used to make a | ||||||
|  |       # static library.  This mechanism is used in libtool 1.4 series to | ||||||
|  |       # handle both shared and static libraries in a single compilation. | ||||||
|  |       # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. | ||||||
|  |       # | ||||||
|  |       # With libtool 1.5 this exception was removed, and libtool now | ||||||
|  |       # generates 2 separate objects for the 2 libraries.  These two | ||||||
|  |       # compilations output dependencies in $dir.libs/$base.o.d and | ||||||
|  |       # in $dir$base.o.d.  We have to check for both files, because | ||||||
|  |       # one of the two compilations can be disabled.  We should prefer | ||||||
|  |       # $dir$base.o.d over $dir.libs/$base.o.d because the latter is | ||||||
|  |       # automatically cleaned when .libs/ is deleted, while ignoring | ||||||
|  |       # the former would cause a distcleancheck panic. | ||||||
|  |       tmpdepfile1=$dir.libs/$base.lo.d   # libtool 1.4 | ||||||
|  |       tmpdepfile2=$dir$base.o.d          # libtool 1.5 | ||||||
|  |       tmpdepfile3=$dir.libs/$base.o.d    # libtool 1.5 | ||||||
|  |       tmpdepfile4=$dir.libs/$base.d      # Compaq CCC V6.2-504 | ||||||
|  |       "$@" -Wc,-MD | ||||||
|  |    else | ||||||
|  |       tmpdepfile1=$dir$base.o.d | ||||||
|  |       tmpdepfile2=$dir$base.d | ||||||
|  |       tmpdepfile3=$dir$base.d | ||||||
|  |       tmpdepfile4=$dir$base.d | ||||||
|  |       "$@" -MD | ||||||
|  |    fi | ||||||
|  | 
 | ||||||
|  |    stat=$? | ||||||
|  |    if test $stat -eq 0; then : | ||||||
|  |    else | ||||||
|  |       rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" | ||||||
|  |       exit $stat | ||||||
|  |    fi | ||||||
|  | 
 | ||||||
|  |    for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" | ||||||
|  |    do | ||||||
|  |      test -f "$tmpdepfile" && break | ||||||
|  |    done | ||||||
|  |    if test -f "$tmpdepfile"; then | ||||||
|  |       sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" | ||||||
|  |       # That's a tab and a space in the []. | ||||||
|  |       sed -e 's,^.*\.[a-z]*:[	 ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" | ||||||
|  |    else | ||||||
|  |       echo "#dummy" > "$depfile" | ||||||
|  |    fi | ||||||
|  |    rm -f "$tmpdepfile" | ||||||
|  |    ;; | ||||||
|  | 
 | ||||||
|  | #nosideeffect) | ||||||
|  |   # This comment above is used by automake to tell side-effect | ||||||
|  |   # dependency tracking mechanisms from slower ones. | ||||||
|  | 
 | ||||||
|  | dashmstdout) | ||||||
|  |   # Important note: in order to support this mode, a compiler *must* | ||||||
|  |   # always write the preprocessed file to stdout, regardless of -o. | ||||||
|  |   "$@" || exit $? | ||||||
|  | 
 | ||||||
|  |   # Remove the call to Libtool. | ||||||
|  |   if test "$libtool" = yes; then | ||||||
|  |     while test $1 != '--mode=compile'; do | ||||||
|  |       shift | ||||||
|  |     done | ||||||
|  |     shift | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   # Remove `-o $object'. | ||||||
|  |   IFS=" " | ||||||
|  |   for arg | ||||||
|  |   do | ||||||
|  |     case $arg in | ||||||
|  |     -o) | ||||||
|  |       shift | ||||||
|  |       ;; | ||||||
|  |     $object) | ||||||
|  |       shift | ||||||
|  |       ;; | ||||||
|  |     *) | ||||||
|  |       set fnord "$@" "$arg" | ||||||
|  |       shift # fnord | ||||||
|  |       shift # $arg | ||||||
|  |       ;; | ||||||
|  |     esac | ||||||
|  |   done | ||||||
|  | 
 | ||||||
|  |   test -z "$dashmflag" && dashmflag=-M | ||||||
|  |   # Require at least two characters before searching for `:' | ||||||
|  |   # in the target name.  This is to cope with DOS-style filenames: | ||||||
|  |   # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. | ||||||
|  |   "$@" $dashmflag | | ||||||
|  |     sed 's:^[  ]*[^: ][^:][^:]*\:[    ]*:'"$object"'\: :' > "$tmpdepfile" | ||||||
|  |   rm -f "$depfile" | ||||||
|  |   cat < "$tmpdepfile" > "$depfile" | ||||||
|  |   tr ' ' ' | ||||||
|  | ' < "$tmpdepfile" | \ | ||||||
|  | ## Some versions of the HPUX 10.20 sed can't process this invocation | ||||||
|  | ## correctly.  Breaking it into two sed invocations is a workaround. | ||||||
|  |     sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" | ||||||
|  |   rm -f "$tmpdepfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | dashXmstdout) | ||||||
|  |   # This case only exists to satisfy depend.m4.  It is never actually | ||||||
|  |   # run, as this mode is specially recognized in the preamble. | ||||||
|  |   exit 1 | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | makedepend) | ||||||
|  |   "$@" || exit $? | ||||||
|  |   # Remove any Libtool call | ||||||
|  |   if test "$libtool" = yes; then | ||||||
|  |     while test $1 != '--mode=compile'; do | ||||||
|  |       shift | ||||||
|  |     done | ||||||
|  |     shift | ||||||
|  |   fi | ||||||
|  |   # X makedepend | ||||||
|  |   shift | ||||||
|  |   cleared=no | ||||||
|  |   for arg in "$@"; do | ||||||
|  |     case $cleared in | ||||||
|  |     no) | ||||||
|  |       set ""; shift | ||||||
|  |       cleared=yes ;; | ||||||
|  |     esac | ||||||
|  |     case "$arg" in | ||||||
|  |     -D*|-I*) | ||||||
|  |       set fnord "$@" "$arg"; shift ;; | ||||||
|  |     # Strip any option that makedepend may not understand.  Remove | ||||||
|  |     # the object too, otherwise makedepend will parse it as a source file. | ||||||
|  |     -*|$object) | ||||||
|  |       ;; | ||||||
|  |     *) | ||||||
|  |       set fnord "$@" "$arg"; shift ;; | ||||||
|  |     esac | ||||||
|  |   done | ||||||
|  |   obj_suffix="`echo $object | sed 's/^.*\././'`" | ||||||
|  |   touch "$tmpdepfile" | ||||||
|  |   ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" | ||||||
|  |   rm -f "$depfile" | ||||||
|  |   cat < "$tmpdepfile" > "$depfile" | ||||||
|  |   sed '1,2d' "$tmpdepfile" | tr ' ' ' | ||||||
|  | ' | \ | ||||||
|  | ## Some versions of the HPUX 10.20 sed can't process this invocation | ||||||
|  | ## correctly.  Breaking it into two sed invocations is a workaround. | ||||||
|  |     sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" | ||||||
|  |   rm -f "$tmpdepfile" "$tmpdepfile".bak | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | cpp) | ||||||
|  |   # Important note: in order to support this mode, a compiler *must* | ||||||
|  |   # always write the preprocessed file to stdout. | ||||||
|  |   "$@" || exit $? | ||||||
|  | 
 | ||||||
|  |   # Remove the call to Libtool. | ||||||
|  |   if test "$libtool" = yes; then | ||||||
|  |     while test $1 != '--mode=compile'; do | ||||||
|  |       shift | ||||||
|  |     done | ||||||
|  |     shift | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   # Remove `-o $object'. | ||||||
|  |   IFS=" " | ||||||
|  |   for arg | ||||||
|  |   do | ||||||
|  |     case $arg in | ||||||
|  |     -o) | ||||||
|  |       shift | ||||||
|  |       ;; | ||||||
|  |     $object) | ||||||
|  |       shift | ||||||
|  |       ;; | ||||||
|  |     *) | ||||||
|  |       set fnord "$@" "$arg" | ||||||
|  |       shift # fnord | ||||||
|  |       shift # $arg | ||||||
|  |       ;; | ||||||
|  |     esac | ||||||
|  |   done | ||||||
|  | 
 | ||||||
|  |   "$@" -E | | ||||||
|  |     sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | ||||||
|  |        -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | | ||||||
|  |     sed '$ s: \\$::' > "$tmpdepfile" | ||||||
|  |   rm -f "$depfile" | ||||||
|  |   echo "$object : \\" > "$depfile" | ||||||
|  |   cat < "$tmpdepfile" >> "$depfile" | ||||||
|  |   sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" | ||||||
|  |   rm -f "$tmpdepfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | msvisualcpp) | ||||||
|  |   # Important note: in order to support this mode, a compiler *must* | ||||||
|  |   # always write the preprocessed file to stdout, regardless of -o, | ||||||
|  |   # because we must use -o when running libtool. | ||||||
|  |   "$@" || exit $? | ||||||
|  |   IFS=" " | ||||||
|  |   for arg | ||||||
|  |   do | ||||||
|  |     case "$arg" in | ||||||
|  |     "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") | ||||||
|  | 	set fnord "$@" | ||||||
|  | 	shift | ||||||
|  | 	shift | ||||||
|  | 	;; | ||||||
|  |     *) | ||||||
|  | 	set fnord "$@" "$arg" | ||||||
|  | 	shift | ||||||
|  | 	shift | ||||||
|  | 	;; | ||||||
|  |     esac | ||||||
|  |   done | ||||||
|  |   "$@" -E | | ||||||
|  |   sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" | ||||||
|  |   rm -f "$depfile" | ||||||
|  |   echo "$object : \\" > "$depfile" | ||||||
|  |   . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::	\1 \\:p' >> "$depfile" | ||||||
|  |   echo "	" >> "$depfile" | ||||||
|  |   . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" | ||||||
|  |   rm -f "$tmpdepfile" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | none) | ||||||
|  |   exec "$@" | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  | *) | ||||||
|  |   echo "Unknown depmode $depmode" 1>&2 | ||||||
|  |   exit 1 | ||||||
|  |   ;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | exit 0 | ||||||
|  | 
 | ||||||
|  | # Local Variables: | ||||||
|  | # mode: shell-script | ||||||
|  | # sh-indentation: 2 | ||||||
|  | # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||||
|  | # time-stamp-start: "scriptversion=" | ||||||
|  | # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||||
|  | # time-stamp-end: "$" | ||||||
|  | # End: | ||||||
							
								
								
									
										507
									
								
								google-breakpad/autotools/install-sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										507
									
								
								google-breakpad/autotools/install-sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,507 @@ | |||||||
|  | #!/bin/sh | ||||||
|  | # install - install a program, script, or datafile | ||||||
|  | 
 | ||||||
|  | scriptversion=2006-10-14.15 | ||||||
|  | 
 | ||||||
|  | # This originates from X11R5 (mit/util/scripts/install.sh), which was | ||||||
|  | # later released in X11R6 (xc/config/util/install.sh) with the | ||||||
|  | # following copyright and license. | ||||||
|  | # | ||||||
|  | # Copyright (C) 1994 X Consortium | ||||||
|  | # | ||||||
|  | # Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  | # of this software and associated documentation files (the "Software"), to | ||||||
|  | # deal in the Software without restriction, including without limitation the | ||||||
|  | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or | ||||||
|  | # sell copies of the Software, and to permit persons to whom the Software is | ||||||
|  | # furnished to do so, subject to the following conditions: | ||||||
|  | # | ||||||
|  | # The above copyright notice and this permission notice shall be included in | ||||||
|  | # all copies or substantial portions of the Software. | ||||||
|  | # | ||||||
|  | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE | ||||||
|  | # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN | ||||||
|  | # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- | ||||||
|  | # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||||||
|  | # | ||||||
|  | # Except as contained in this notice, the name of the X Consortium shall not | ||||||
|  | # be used in advertising or otherwise to promote the sale, use or other deal- | ||||||
|  | # ings in this Software without prior written authorization from the X Consor- | ||||||
|  | # tium. | ||||||
|  | # | ||||||
|  | # | ||||||
|  | # FSF changes to this file are in the public domain. | ||||||
|  | # | ||||||
|  | # Calling this script install-sh is preferred over install.sh, to prevent | ||||||
|  | # `make' implicit rules from creating a file called install from it | ||||||
|  | # when there is no Makefile. | ||||||
|  | # | ||||||
|  | # This script is compatible with the BSD install script, but was written | ||||||
|  | # from scratch. | ||||||
|  | 
 | ||||||
|  | nl=' | ||||||
|  | ' | ||||||
|  | IFS=" ""	$nl" | ||||||
|  | 
 | ||||||
|  | # set DOITPROG to echo to test this script | ||||||
|  | 
 | ||||||
|  | # Don't use :- since 4.3BSD and earlier shells don't like it. | ||||||
|  | doit="${DOITPROG-}" | ||||||
|  | if test -z "$doit"; then | ||||||
|  |   doit_exec=exec | ||||||
|  | else | ||||||
|  |   doit_exec=$doit | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # Put in absolute file names if you don't have them in your path; | ||||||
|  | # or use environment vars. | ||||||
|  | 
 | ||||||
|  | mvprog="${MVPROG-mv}" | ||||||
|  | cpprog="${CPPROG-cp}" | ||||||
|  | chmodprog="${CHMODPROG-chmod}" | ||||||
|  | chownprog="${CHOWNPROG-chown}" | ||||||
|  | chgrpprog="${CHGRPPROG-chgrp}" | ||||||
|  | stripprog="${STRIPPROG-strip}" | ||||||
|  | rmprog="${RMPROG-rm}" | ||||||
|  | mkdirprog="${MKDIRPROG-mkdir}" | ||||||
|  | 
 | ||||||
|  | posix_glob= | ||||||
|  | posix_mkdir= | ||||||
|  | 
 | ||||||
|  | # Desired mode of installed file. | ||||||
|  | mode=0755 | ||||||
|  | 
 | ||||||
|  | chmodcmd=$chmodprog | ||||||
|  | chowncmd= | ||||||
|  | chgrpcmd= | ||||||
|  | stripcmd= | ||||||
|  | rmcmd="$rmprog -f" | ||||||
|  | mvcmd="$mvprog" | ||||||
|  | src= | ||||||
|  | dst= | ||||||
|  | dir_arg= | ||||||
|  | dstarg= | ||||||
|  | no_target_directory= | ||||||
|  | 
 | ||||||
|  | usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE | ||||||
|  |    or: $0 [OPTION]... SRCFILES... DIRECTORY | ||||||
|  |    or: $0 [OPTION]... -t DIRECTORY SRCFILES... | ||||||
|  |    or: $0 [OPTION]... -d DIRECTORIES... | ||||||
|  | 
 | ||||||
|  | In the 1st form, copy SRCFILE to DSTFILE. | ||||||
|  | In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. | ||||||
|  | In the 4th, create DIRECTORIES. | ||||||
|  | 
 | ||||||
|  | Options: | ||||||
|  | -c         (ignored) | ||||||
|  | -d         create directories instead of installing files. | ||||||
|  | -g GROUP   $chgrpprog installed files to GROUP. | ||||||
|  | -m MODE    $chmodprog installed files to MODE. | ||||||
|  | -o USER    $chownprog installed files to USER. | ||||||
|  | -s         $stripprog installed files. | ||||||
|  | -t DIRECTORY  install into DIRECTORY. | ||||||
|  | -T         report an error if DSTFILE is a directory. | ||||||
|  | --help     display this help and exit. | ||||||
|  | --version  display version info and exit. | ||||||
|  | 
 | ||||||
|  | Environment variables override the default commands: | ||||||
|  |   CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG | ||||||
|  | " | ||||||
|  | 
 | ||||||
|  | while test $# -ne 0; do | ||||||
|  |   case $1 in | ||||||
|  |     -c) shift | ||||||
|  |         continue;; | ||||||
|  | 
 | ||||||
|  |     -d) dir_arg=true | ||||||
|  |         shift | ||||||
|  |         continue;; | ||||||
|  | 
 | ||||||
|  |     -g) chgrpcmd="$chgrpprog $2" | ||||||
|  |         shift | ||||||
|  |         shift | ||||||
|  |         continue;; | ||||||
|  | 
 | ||||||
|  |     --help) echo "$usage"; exit $?;; | ||||||
|  | 
 | ||||||
|  |     -m) mode=$2 | ||||||
|  |         shift | ||||||
|  |         shift | ||||||
|  | 	case $mode in | ||||||
|  | 	  *' '* | *'	'* | *' | ||||||
|  | '*	  | *'*'* | *'?'* | *'['*) | ||||||
|  | 	    echo "$0: invalid mode: $mode" >&2 | ||||||
|  | 	    exit 1;; | ||||||
|  | 	esac | ||||||
|  |         continue;; | ||||||
|  | 
 | ||||||
|  |     -o) chowncmd="$chownprog $2" | ||||||
|  |         shift | ||||||
|  |         shift | ||||||
|  |         continue;; | ||||||
|  | 
 | ||||||
|  |     -s) stripcmd=$stripprog | ||||||
|  |         shift | ||||||
|  |         continue;; | ||||||
|  | 
 | ||||||
|  |     -t) dstarg=$2 | ||||||
|  | 	shift | ||||||
|  | 	shift | ||||||
|  | 	continue;; | ||||||
|  | 
 | ||||||
|  |     -T) no_target_directory=true | ||||||
|  | 	shift | ||||||
|  | 	continue;; | ||||||
|  | 
 | ||||||
|  |     --version) echo "$0 $scriptversion"; exit $?;; | ||||||
|  | 
 | ||||||
|  |     --)	shift | ||||||
|  | 	break;; | ||||||
|  | 
 | ||||||
|  |     -*)	echo "$0: invalid option: $1" >&2 | ||||||
|  | 	exit 1;; | ||||||
|  | 
 | ||||||
|  |     *)  break;; | ||||||
|  |   esac | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | if test $# -ne 0 && test -z "$dir_arg$dstarg"; then | ||||||
|  |   # When -d is used, all remaining arguments are directories to create. | ||||||
|  |   # When -t is used, the destination is already specified. | ||||||
|  |   # Otherwise, the last argument is the destination.  Remove it from $@. | ||||||
|  |   for arg | ||||||
|  |   do | ||||||
|  |     if test -n "$dstarg"; then | ||||||
|  |       # $@ is not empty: it contains at least $arg. | ||||||
|  |       set fnord "$@" "$dstarg" | ||||||
|  |       shift # fnord | ||||||
|  |     fi | ||||||
|  |     shift # arg | ||||||
|  |     dstarg=$arg | ||||||
|  |   done | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if test $# -eq 0; then | ||||||
|  |   if test -z "$dir_arg"; then | ||||||
|  |     echo "$0: no input file specified." >&2 | ||||||
|  |     exit 1 | ||||||
|  |   fi | ||||||
|  |   # It's OK to call `install-sh -d' without argument. | ||||||
|  |   # This can happen when creating conditional directories. | ||||||
|  |   exit 0 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | if test -z "$dir_arg"; then | ||||||
|  |   trap '(exit $?); exit' 1 2 13 15 | ||||||
|  | 
 | ||||||
|  |   # Set umask so as not to create temps with too-generous modes. | ||||||
|  |   # However, 'strip' requires both read and write access to temps. | ||||||
|  |   case $mode in | ||||||
|  |     # Optimize common cases. | ||||||
|  |     *644) cp_umask=133;; | ||||||
|  |     *755) cp_umask=22;; | ||||||
|  | 
 | ||||||
|  |     *[0-7]) | ||||||
|  |       if test -z "$stripcmd"; then | ||||||
|  | 	u_plus_rw= | ||||||
|  |       else | ||||||
|  | 	u_plus_rw='% 200' | ||||||
|  |       fi | ||||||
|  |       cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; | ||||||
|  |     *) | ||||||
|  |       if test -z "$stripcmd"; then | ||||||
|  | 	u_plus_rw= | ||||||
|  |       else | ||||||
|  | 	u_plus_rw=,u+rw | ||||||
|  |       fi | ||||||
|  |       cp_umask=$mode$u_plus_rw;; | ||||||
|  |   esac | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | for src | ||||||
|  | do | ||||||
|  |   # Protect names starting with `-'. | ||||||
|  |   case $src in | ||||||
|  |     -*) src=./$src ;; | ||||||
|  |   esac | ||||||
|  | 
 | ||||||
|  |   if test -n "$dir_arg"; then | ||||||
|  |     dst=$src | ||||||
|  |     dstdir=$dst | ||||||
|  |     test -d "$dstdir" | ||||||
|  |     dstdir_status=$? | ||||||
|  |   else | ||||||
|  | 
 | ||||||
|  |     # Waiting for this to be detected by the "$cpprog $src $dsttmp" command | ||||||
|  |     # might cause directories to be created, which would be especially bad | ||||||
|  |     # if $src (and thus $dsttmp) contains '*'. | ||||||
|  |     if test ! -f "$src" && test ! -d "$src"; then | ||||||
|  |       echo "$0: $src does not exist." >&2 | ||||||
|  |       exit 1 | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     if test -z "$dstarg"; then | ||||||
|  |       echo "$0: no destination specified." >&2 | ||||||
|  |       exit 1 | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     dst=$dstarg | ||||||
|  |     # Protect names starting with `-'. | ||||||
|  |     case $dst in | ||||||
|  |       -*) dst=./$dst ;; | ||||||
|  |     esac | ||||||
|  | 
 | ||||||
|  |     # If destination is a directory, append the input filename; won't work | ||||||
|  |     # if double slashes aren't ignored. | ||||||
|  |     if test -d "$dst"; then | ||||||
|  |       if test -n "$no_target_directory"; then | ||||||
|  | 	echo "$0: $dstarg: Is a directory" >&2 | ||||||
|  | 	exit 1 | ||||||
|  |       fi | ||||||
|  |       dstdir=$dst | ||||||
|  |       dst=$dstdir/`basename "$src"` | ||||||
|  |       dstdir_status=0 | ||||||
|  |     else | ||||||
|  |       # Prefer dirname, but fall back on a substitute if dirname fails. | ||||||
|  |       dstdir=` | ||||||
|  | 	(dirname "$dst") 2>/dev/null || | ||||||
|  | 	expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ | ||||||
|  | 	     X"$dst" : 'X\(//\)[^/]' \| \ | ||||||
|  | 	     X"$dst" : 'X\(//\)$' \| \ | ||||||
|  | 	     X"$dst" : 'X\(/\)' \| . 2>/dev/null || | ||||||
|  | 	echo X"$dst" | | ||||||
|  | 	    sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ | ||||||
|  | 		   s//\1/ | ||||||
|  | 		   q | ||||||
|  | 		 } | ||||||
|  | 		 /^X\(\/\/\)[^/].*/{ | ||||||
|  | 		   s//\1/ | ||||||
|  | 		   q | ||||||
|  | 		 } | ||||||
|  | 		 /^X\(\/\/\)$/{ | ||||||
|  | 		   s//\1/ | ||||||
|  | 		   q | ||||||
|  | 		 } | ||||||
|  | 		 /^X\(\/\).*/{ | ||||||
|  | 		   s//\1/ | ||||||
|  | 		   q | ||||||
|  | 		 } | ||||||
|  | 		 s/.*/./; q' | ||||||
|  |       ` | ||||||
|  | 
 | ||||||
|  |       test -d "$dstdir" | ||||||
|  |       dstdir_status=$? | ||||||
|  |     fi | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   obsolete_mkdir_used=false | ||||||
|  | 
 | ||||||
|  |   if test $dstdir_status != 0; then | ||||||
|  |     case $posix_mkdir in | ||||||
|  |       '') | ||||||
|  | 	# Create intermediate dirs using mode 755 as modified by the umask. | ||||||
|  | 	# This is like FreeBSD 'install' as of 1997-10-28. | ||||||
|  | 	umask=`umask` | ||||||
|  | 	case $stripcmd.$umask in | ||||||
|  | 	  # Optimize common cases. | ||||||
|  | 	  *[2367][2367]) mkdir_umask=$umask;; | ||||||
|  | 	  .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; | ||||||
|  | 
 | ||||||
|  | 	  *[0-7]) | ||||||
|  | 	    mkdir_umask=`expr $umask + 22 \ | ||||||
|  | 	      - $umask % 100 % 40 + $umask % 20 \ | ||||||
|  | 	      - $umask % 10 % 4 + $umask % 2 | ||||||
|  | 	    `;; | ||||||
|  | 	  *) mkdir_umask=$umask,go-w;; | ||||||
|  | 	esac | ||||||
|  | 
 | ||||||
|  | 	# With -d, create the new directory with the user-specified mode. | ||||||
|  | 	# Otherwise, rely on $mkdir_umask. | ||||||
|  | 	if test -n "$dir_arg"; then | ||||||
|  | 	  mkdir_mode=-m$mode | ||||||
|  | 	else | ||||||
|  | 	  mkdir_mode= | ||||||
|  | 	fi | ||||||
|  | 
 | ||||||
|  | 	posix_mkdir=false | ||||||
|  | 	case $umask in | ||||||
|  | 	  *[123567][0-7][0-7]) | ||||||
|  | 	    # POSIX mkdir -p sets u+wx bits regardless of umask, which | ||||||
|  | 	    # is incompatible with FreeBSD 'install' when (umask & 300) != 0. | ||||||
|  | 	    ;; | ||||||
|  | 	  *) | ||||||
|  | 	    tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ | ||||||
|  | 	    trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 | ||||||
|  | 
 | ||||||
|  | 	    if (umask $mkdir_umask && | ||||||
|  | 		exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 | ||||||
|  | 	    then | ||||||
|  | 	      if test -z "$dir_arg" || { | ||||||
|  | 		   # Check for POSIX incompatibilities with -m. | ||||||
|  | 		   # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or | ||||||
|  | 		   # other-writeable bit of parent directory when it shouldn't. | ||||||
|  | 		   # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. | ||||||
|  | 		   ls_ld_tmpdir=`ls -ld "$tmpdir"` | ||||||
|  | 		   case $ls_ld_tmpdir in | ||||||
|  | 		     d????-?r-*) different_mode=700;; | ||||||
|  | 		     d????-?--*) different_mode=755;; | ||||||
|  | 		     *) false;; | ||||||
|  | 		   esac && | ||||||
|  | 		   $mkdirprog -m$different_mode -p -- "$tmpdir" && { | ||||||
|  | 		     ls_ld_tmpdir_1=`ls -ld "$tmpdir"` | ||||||
|  | 		     test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" | ||||||
|  | 		   } | ||||||
|  | 		 } | ||||||
|  | 	      then posix_mkdir=: | ||||||
|  | 	      fi | ||||||
|  | 	      rmdir "$tmpdir/d" "$tmpdir" | ||||||
|  | 	    else | ||||||
|  | 	      # Remove any dirs left behind by ancient mkdir implementations. | ||||||
|  | 	      rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null | ||||||
|  | 	    fi | ||||||
|  | 	    trap '' 0;; | ||||||
|  | 	esac;; | ||||||
|  |     esac | ||||||
|  | 
 | ||||||
|  |     if | ||||||
|  |       $posix_mkdir && ( | ||||||
|  | 	umask $mkdir_umask && | ||||||
|  | 	$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" | ||||||
|  |       ) | ||||||
|  |     then : | ||||||
|  |     else | ||||||
|  | 
 | ||||||
|  |       # The umask is ridiculous, or mkdir does not conform to POSIX, | ||||||
|  |       # or it failed possibly due to a race condition.  Create the | ||||||
|  |       # directory the slow way, step by step, checking for races as we go. | ||||||
|  | 
 | ||||||
|  |       case $dstdir in | ||||||
|  | 	/*) prefix=/ ;; | ||||||
|  | 	-*) prefix=./ ;; | ||||||
|  | 	*)  prefix= ;; | ||||||
|  |       esac | ||||||
|  | 
 | ||||||
|  |       case $posix_glob in | ||||||
|  |         '') | ||||||
|  | 	  if (set -f) 2>/dev/null; then | ||||||
|  | 	    posix_glob=true | ||||||
|  | 	  else | ||||||
|  | 	    posix_glob=false | ||||||
|  | 	  fi ;; | ||||||
|  |       esac | ||||||
|  | 
 | ||||||
|  |       oIFS=$IFS | ||||||
|  |       IFS=/ | ||||||
|  |       $posix_glob && set -f | ||||||
|  |       set fnord $dstdir | ||||||
|  |       shift | ||||||
|  |       $posix_glob && set +f | ||||||
|  |       IFS=$oIFS | ||||||
|  | 
 | ||||||
|  |       prefixes= | ||||||
|  | 
 | ||||||
|  |       for d | ||||||
|  |       do | ||||||
|  | 	test -z "$d" && continue | ||||||
|  | 
 | ||||||
|  | 	prefix=$prefix$d | ||||||
|  | 	if test -d "$prefix"; then | ||||||
|  | 	  prefixes= | ||||||
|  | 	else | ||||||
|  | 	  if $posix_mkdir; then | ||||||
|  | 	    (umask=$mkdir_umask && | ||||||
|  | 	     $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break | ||||||
|  | 	    # Don't fail if two instances are running concurrently. | ||||||
|  | 	    test -d "$prefix" || exit 1 | ||||||
|  | 	  else | ||||||
|  | 	    case $prefix in | ||||||
|  | 	      *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; | ||||||
|  | 	      *) qprefix=$prefix;; | ||||||
|  | 	    esac | ||||||
|  | 	    prefixes="$prefixes '$qprefix'" | ||||||
|  | 	  fi | ||||||
|  | 	fi | ||||||
|  | 	prefix=$prefix/ | ||||||
|  |       done | ||||||
|  | 
 | ||||||
|  |       if test -n "$prefixes"; then | ||||||
|  | 	# Don't fail if two instances are running concurrently. | ||||||
|  | 	(umask $mkdir_umask && | ||||||
|  | 	 eval "\$doit_exec \$mkdirprog $prefixes") || | ||||||
|  | 	  test -d "$dstdir" || exit 1 | ||||||
|  | 	obsolete_mkdir_used=true | ||||||
|  |       fi | ||||||
|  |     fi | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   if test -n "$dir_arg"; then | ||||||
|  |     { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && | ||||||
|  |     { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && | ||||||
|  |     { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || | ||||||
|  |       test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 | ||||||
|  |   else | ||||||
|  | 
 | ||||||
|  |     # Make a couple of temp file names in the proper directory. | ||||||
|  |     dsttmp=$dstdir/_inst.$$_ | ||||||
|  |     rmtmp=$dstdir/_rm.$$_ | ||||||
|  | 
 | ||||||
|  |     # Trap to clean up those temp files at exit. | ||||||
|  |     trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 | ||||||
|  | 
 | ||||||
|  |     # Copy the file name to the temp name. | ||||||
|  |     (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && | ||||||
|  | 
 | ||||||
|  |     # and set any options; do chmod last to preserve setuid bits. | ||||||
|  |     # | ||||||
|  |     # If any of these fail, we abort the whole thing.  If we want to | ||||||
|  |     # ignore errors from any of these, just make sure not to ignore | ||||||
|  |     # errors from the above "$doit $cpprog $src $dsttmp" command. | ||||||
|  |     # | ||||||
|  |     { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ | ||||||
|  |       && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ | ||||||
|  |       && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ | ||||||
|  |       && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && | ||||||
|  | 
 | ||||||
|  |     # Now rename the file to the real destination. | ||||||
|  |     { $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \ | ||||||
|  |       || { | ||||||
|  | 	   # The rename failed, perhaps because mv can't rename something else | ||||||
|  | 	   # to itself, or perhaps because mv is so ancient that it does not | ||||||
|  | 	   # support -f. | ||||||
|  | 
 | ||||||
|  | 	   # Now remove or move aside any old file at destination location. | ||||||
|  | 	   # We try this two ways since rm can't unlink itself on some | ||||||
|  | 	   # systems and the destination file might be busy for other | ||||||
|  | 	   # reasons.  In this case, the final cleanup might fail but the new | ||||||
|  | 	   # file should still install successfully. | ||||||
|  | 	   { | ||||||
|  | 	     if test -f "$dst"; then | ||||||
|  | 	       $doit $rmcmd -f "$dst" 2>/dev/null \ | ||||||
|  | 	       || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \ | ||||||
|  | 		     && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\ | ||||||
|  | 	       || { | ||||||
|  | 		 echo "$0: cannot unlink or rename $dst" >&2 | ||||||
|  | 		 (exit 1); exit 1 | ||||||
|  | 	       } | ||||||
|  | 	     else | ||||||
|  | 	       : | ||||||
|  | 	     fi | ||||||
|  | 	   } && | ||||||
|  | 
 | ||||||
|  | 	   # Now rename the file to the real destination. | ||||||
|  | 	   $doit $mvcmd "$dsttmp" "$dst" | ||||||
|  | 	 } | ||||||
|  |     } || exit 1 | ||||||
|  | 
 | ||||||
|  |     trap '' 0 | ||||||
|  |   fi | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | # Local variables: | ||||||
|  | # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||||
|  | # time-stamp-start: "scriptversion=" | ||||||
|  | # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||||
|  | # time-stamp-end: "$" | ||||||
|  | # End: | ||||||
							
								
								
									
										8406
									
								
								google-breakpad/autotools/ltmain.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8406
									
								
								google-breakpad/autotools/ltmain.sh
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										367
									
								
								google-breakpad/autotools/missing
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										367
									
								
								google-breakpad/autotools/missing
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,367 @@ | |||||||
|  | #! /bin/sh | ||||||
|  | # Common stub for a few missing GNU programs while installing. | ||||||
|  | 
 | ||||||
|  | scriptversion=2006-05-10.23 | ||||||
|  | 
 | ||||||
|  | # Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006 | ||||||
|  | #   Free Software Foundation, Inc. | ||||||
|  | # Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. | ||||||
|  | 
 | ||||||
|  | # This program is free software; you can redistribute it and/or modify | ||||||
|  | # it under the terms of the GNU General Public License as published by | ||||||
|  | # the Free Software Foundation; either version 2, or (at your option) | ||||||
|  | # any later version. | ||||||
|  | 
 | ||||||
|  | # This program is distributed in the hope that it will be useful, | ||||||
|  | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | # GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | # You should have received a copy of the GNU General Public License | ||||||
|  | # along with this program; if not, write to the Free Software | ||||||
|  | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||||||
|  | # 02110-1301, USA. | ||||||
|  | 
 | ||||||
|  | # As a special exception to the GNU General Public License, if you | ||||||
|  | # distribute this file as part of a program that contains a | ||||||
|  | # configuration script generated by Autoconf, you may include it under | ||||||
|  | # the same distribution terms that you use for the rest of that program. | ||||||
|  | 
 | ||||||
|  | if test $# -eq 0; then | ||||||
|  |   echo 1>&2 "Try \`$0 --help' for more information" | ||||||
|  |   exit 1 | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | run=: | ||||||
|  | sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' | ||||||
|  | sed_minuso='s/.* -o \([^ ]*\).*/\1/p' | ||||||
|  | 
 | ||||||
|  | # In the cases where this matters, `missing' is being run in the | ||||||
|  | # srcdir already. | ||||||
|  | if test -f configure.ac; then | ||||||
|  |   configure_ac=configure.ac | ||||||
|  | else | ||||||
|  |   configure_ac=configure.in | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | msg="missing on your system" | ||||||
|  | 
 | ||||||
|  | case $1 in | ||||||
|  | --run) | ||||||
|  |   # Try to run requested program, and just exit if it succeeds. | ||||||
|  |   run= | ||||||
|  |   shift | ||||||
|  |   "$@" && exit 0 | ||||||
|  |   # Exit code 63 means version mismatch.  This often happens | ||||||
|  |   # when the user try to use an ancient version of a tool on | ||||||
|  |   # a file that requires a minimum version.  In this case we | ||||||
|  |   # we should proceed has if the program had been absent, or | ||||||
|  |   # if --run hadn't been passed. | ||||||
|  |   if test $? = 63; then | ||||||
|  |     run=: | ||||||
|  |     msg="probably too old" | ||||||
|  |   fi | ||||||
|  |   ;; | ||||||
|  | 
 | ||||||
|  |   -h|--h|--he|--hel|--help) | ||||||
|  |     echo "\ | ||||||
|  | $0 [OPTION]... PROGRAM [ARGUMENT]... | ||||||
|  | 
 | ||||||
|  | Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an | ||||||
|  | error status if there is no known handling for PROGRAM. | ||||||
|  | 
 | ||||||
|  | Options: | ||||||
|  |   -h, --help      display this help and exit | ||||||
|  |   -v, --version   output version information and exit | ||||||
|  |   --run           try to run the given command, and emulate it if it fails | ||||||
|  | 
 | ||||||
|  | Supported PROGRAM values: | ||||||
|  |   aclocal      touch file \`aclocal.m4' | ||||||
|  |   autoconf     touch file \`configure' | ||||||
|  |   autoheader   touch file \`config.h.in' | ||||||
|  |   autom4te     touch the output file, or create a stub one | ||||||
|  |   automake     touch all \`Makefile.in' files | ||||||
|  |   bison        create \`y.tab.[ch]', if possible, from existing .[ch] | ||||||
|  |   flex         create \`lex.yy.c', if possible, from existing .c | ||||||
|  |   help2man     touch the output file | ||||||
|  |   lex          create \`lex.yy.c', if possible, from existing .c | ||||||
|  |   makeinfo     touch the output file | ||||||
|  |   tar          try tar, gnutar, gtar, then tar without non-portable flags | ||||||
|  |   yacc         create \`y.tab.[ch]', if possible, from existing .[ch] | ||||||
|  | 
 | ||||||
|  | Send bug reports to <bug-automake@gnu.org>." | ||||||
|  |     exit $? | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   -v|--v|--ve|--ver|--vers|--versi|--versio|--version) | ||||||
|  |     echo "missing $scriptversion (GNU Automake)" | ||||||
|  |     exit $? | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   -*) | ||||||
|  |     echo 1>&2 "$0: Unknown \`$1' option" | ||||||
|  |     echo 1>&2 "Try \`$0 --help' for more information" | ||||||
|  |     exit 1 | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | # Now exit if we have it, but it failed.  Also exit now if we | ||||||
|  | # don't have it and --version was passed (most likely to detect | ||||||
|  | # the program). | ||||||
|  | case $1 in | ||||||
|  |   lex|yacc) | ||||||
|  |     # Not GNU programs, they don't have --version. | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   tar) | ||||||
|  |     if test -n "$run"; then | ||||||
|  |        echo 1>&2 "ERROR: \`tar' requires --run" | ||||||
|  |        exit 1 | ||||||
|  |     elif test "x$2" = "x--version" || test "x$2" = "x--help"; then | ||||||
|  |        exit 1 | ||||||
|  |     fi | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   *) | ||||||
|  |     if test -z "$run" && ($1 --version) > /dev/null 2>&1; then | ||||||
|  |        # We have it, but it failed. | ||||||
|  |        exit 1 | ||||||
|  |     elif test "x$2" = "x--version" || test "x$2" = "x--help"; then | ||||||
|  |        # Could not run --version or --help.  This is probably someone | ||||||
|  |        # running `$TOOL --version' or `$TOOL --help' to check whether | ||||||
|  |        # $TOOL exists and not knowing $TOOL uses missing. | ||||||
|  |        exit 1 | ||||||
|  |     fi | ||||||
|  |     ;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | # If it does not exist, or fails to run (possibly an outdated version), | ||||||
|  | # try to emulate it. | ||||||
|  | case $1 in | ||||||
|  |   aclocal*) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is $msg.  You should only need it if | ||||||
|  |          you modified \`acinclude.m4' or \`${configure_ac}'.  You might want | ||||||
|  |          to install the \`Automake' and \`Perl' packages.  Grab them from | ||||||
|  |          any GNU archive site." | ||||||
|  |     touch aclocal.m4 | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   autoconf) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is $msg.  You should only need it if | ||||||
|  |          you modified \`${configure_ac}'.  You might want to install the | ||||||
|  |          \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU | ||||||
|  |          archive site." | ||||||
|  |     touch configure | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   autoheader) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is $msg.  You should only need it if | ||||||
|  |          you modified \`acconfig.h' or \`${configure_ac}'.  You might want | ||||||
|  |          to install the \`Autoconf' and \`GNU m4' packages.  Grab them | ||||||
|  |          from any GNU archive site." | ||||||
|  |     files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` | ||||||
|  |     test -z "$files" && files="config.h" | ||||||
|  |     touch_files= | ||||||
|  |     for f in $files; do | ||||||
|  |       case $f in | ||||||
|  |       *:*) touch_files="$touch_files "`echo "$f" | | ||||||
|  | 				       sed -e 's/^[^:]*://' -e 's/:.*//'`;; | ||||||
|  |       *) touch_files="$touch_files $f.in";; | ||||||
|  |       esac | ||||||
|  |     done | ||||||
|  |     touch $touch_files | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   automake*) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is $msg.  You should only need it if | ||||||
|  |          you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. | ||||||
|  |          You might want to install the \`Automake' and \`Perl' packages. | ||||||
|  |          Grab them from any GNU archive site." | ||||||
|  |     find . -type f -name Makefile.am -print | | ||||||
|  | 	   sed 's/\.am$/.in/' | | ||||||
|  | 	   while read f; do touch "$f"; done | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   autom4te) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is needed, but is $msg. | ||||||
|  |          You might have modified some files without having the | ||||||
|  |          proper tools for further handling them. | ||||||
|  |          You can get \`$1' as part of \`Autoconf' from any GNU | ||||||
|  |          archive site." | ||||||
|  | 
 | ||||||
|  |     file=`echo "$*" | sed -n "$sed_output"` | ||||||
|  |     test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` | ||||||
|  |     if test -f "$file"; then | ||||||
|  | 	touch $file | ||||||
|  |     else | ||||||
|  | 	test -z "$file" || exec >$file | ||||||
|  | 	echo "#! /bin/sh" | ||||||
|  | 	echo "# Created by GNU Automake missing as a replacement of" | ||||||
|  | 	echo "#  $ $@" | ||||||
|  | 	echo "exit 0" | ||||||
|  | 	chmod +x $file | ||||||
|  | 	exit 1 | ||||||
|  |     fi | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   bison|yacc) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' $msg.  You should only need it if | ||||||
|  |          you modified a \`.y' file.  You may need the \`Bison' package | ||||||
|  |          in order for those modifications to take effect.  You can get | ||||||
|  |          \`Bison' from any GNU archive site." | ||||||
|  |     rm -f y.tab.c y.tab.h | ||||||
|  |     if test $# -ne 1; then | ||||||
|  |         eval LASTARG="\${$#}" | ||||||
|  | 	case $LASTARG in | ||||||
|  | 	*.y) | ||||||
|  | 	    SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` | ||||||
|  | 	    if test -f "$SRCFILE"; then | ||||||
|  | 	         cp "$SRCFILE" y.tab.c | ||||||
|  | 	    fi | ||||||
|  | 	    SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` | ||||||
|  | 	    if test -f "$SRCFILE"; then | ||||||
|  | 	         cp "$SRCFILE" y.tab.h | ||||||
|  | 	    fi | ||||||
|  | 	  ;; | ||||||
|  | 	esac | ||||||
|  |     fi | ||||||
|  |     if test ! -f y.tab.h; then | ||||||
|  | 	echo >y.tab.h | ||||||
|  |     fi | ||||||
|  |     if test ! -f y.tab.c; then | ||||||
|  | 	echo 'main() { return 0; }' >y.tab.c | ||||||
|  |     fi | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   lex|flex) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is $msg.  You should only need it if | ||||||
|  |          you modified a \`.l' file.  You may need the \`Flex' package | ||||||
|  |          in order for those modifications to take effect.  You can get | ||||||
|  |          \`Flex' from any GNU archive site." | ||||||
|  |     rm -f lex.yy.c | ||||||
|  |     if test $# -ne 1; then | ||||||
|  |         eval LASTARG="\${$#}" | ||||||
|  | 	case $LASTARG in | ||||||
|  | 	*.l) | ||||||
|  | 	    SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` | ||||||
|  | 	    if test -f "$SRCFILE"; then | ||||||
|  | 	         cp "$SRCFILE" lex.yy.c | ||||||
|  | 	    fi | ||||||
|  | 	  ;; | ||||||
|  | 	esac | ||||||
|  |     fi | ||||||
|  |     if test ! -f lex.yy.c; then | ||||||
|  | 	echo 'main() { return 0; }' >lex.yy.c | ||||||
|  |     fi | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   help2man) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is $msg.  You should only need it if | ||||||
|  | 	 you modified a dependency of a manual page.  You may need the | ||||||
|  | 	 \`Help2man' package in order for those modifications to take | ||||||
|  | 	 effect.  You can get \`Help2man' from any GNU archive site." | ||||||
|  | 
 | ||||||
|  |     file=`echo "$*" | sed -n "$sed_output"` | ||||||
|  |     test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` | ||||||
|  |     if test -f "$file"; then | ||||||
|  | 	touch $file | ||||||
|  |     else | ||||||
|  | 	test -z "$file" || exec >$file | ||||||
|  | 	echo ".ab help2man is required to generate this page" | ||||||
|  | 	exit 1 | ||||||
|  |     fi | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   makeinfo) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is $msg.  You should only need it if | ||||||
|  |          you modified a \`.texi' or \`.texinfo' file, or any other file | ||||||
|  |          indirectly affecting the aspect of the manual.  The spurious | ||||||
|  |          call might also be the consequence of using a buggy \`make' (AIX, | ||||||
|  |          DU, IRIX).  You might want to install the \`Texinfo' package or | ||||||
|  |          the \`GNU make' package.  Grab either from any GNU archive site." | ||||||
|  |     # The file to touch is that specified with -o ... | ||||||
|  |     file=`echo "$*" | sed -n "$sed_output"` | ||||||
|  |     test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` | ||||||
|  |     if test -z "$file"; then | ||||||
|  |       # ... or it is the one specified with @setfilename ... | ||||||
|  |       infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` | ||||||
|  |       file=`sed -n ' | ||||||
|  | 	/^@setfilename/{ | ||||||
|  | 	  s/.* \([^ ]*\) *$/\1/ | ||||||
|  | 	  p | ||||||
|  | 	  q | ||||||
|  | 	}' $infile` | ||||||
|  |       # ... or it is derived from the source name (dir/f.texi becomes f.info) | ||||||
|  |       test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info | ||||||
|  |     fi | ||||||
|  |     # If the file does not exist, the user really needs makeinfo; | ||||||
|  |     # let's fail without touching anything. | ||||||
|  |     test -f $file || exit 1 | ||||||
|  |     touch $file | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   tar) | ||||||
|  |     shift | ||||||
|  | 
 | ||||||
|  |     # We have already tried tar in the generic part. | ||||||
|  |     # Look for gnutar/gtar before invocation to avoid ugly error | ||||||
|  |     # messages. | ||||||
|  |     if (gnutar --version > /dev/null 2>&1); then | ||||||
|  |        gnutar "$@" && exit 0 | ||||||
|  |     fi | ||||||
|  |     if (gtar --version > /dev/null 2>&1); then | ||||||
|  |        gtar "$@" && exit 0 | ||||||
|  |     fi | ||||||
|  |     firstarg="$1" | ||||||
|  |     if shift; then | ||||||
|  | 	case $firstarg in | ||||||
|  | 	*o*) | ||||||
|  | 	    firstarg=`echo "$firstarg" | sed s/o//` | ||||||
|  | 	    tar "$firstarg" "$@" && exit 0 | ||||||
|  | 	    ;; | ||||||
|  | 	esac | ||||||
|  | 	case $firstarg in | ||||||
|  | 	*h*) | ||||||
|  | 	    firstarg=`echo "$firstarg" | sed s/h//` | ||||||
|  | 	    tar "$firstarg" "$@" && exit 0 | ||||||
|  | 	    ;; | ||||||
|  | 	esac | ||||||
|  |     fi | ||||||
|  | 
 | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: I can't seem to be able to run \`tar' with the given arguments. | ||||||
|  |          You may want to install GNU tar or Free paxutils, or check the | ||||||
|  |          command line arguments." | ||||||
|  |     exit 1 | ||||||
|  |     ;; | ||||||
|  | 
 | ||||||
|  |   *) | ||||||
|  |     echo 1>&2 "\ | ||||||
|  | WARNING: \`$1' is needed, and is $msg. | ||||||
|  |          You might have modified some files without having the | ||||||
|  |          proper tools for further handling them.  Check the \`README' file, | ||||||
|  |          it often tells you about the needed prerequisites for installing | ||||||
|  |          this package.  You may also peek at any GNU archive site, in case | ||||||
|  |          some other package would contain this missing \`$1' program." | ||||||
|  |     exit 1 | ||||||
|  |     ;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | exit 0 | ||||||
|  | 
 | ||||||
|  | # Local variables: | ||||||
|  | # eval: (add-hook 'write-file-hooks 'time-stamp) | ||||||
|  | # time-stamp-start: "scriptversion=" | ||||||
|  | # time-stamp-format: "%:y-%02m-%02d.%02H" | ||||||
|  | # time-stamp-end: "$" | ||||||
|  | # End: | ||||||
							
								
								
									
										7649
									
								
								google-breakpad/configure
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										7649
									
								
								google-breakpad/configure
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										151
									
								
								google-breakpad/configure.ac
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								google-breakpad/configure.ac
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,151 @@ | |||||||
|  | # Copyright (c) 2006, Google Inc. | ||||||
|  | # All rights reserved. | ||||||
|  | # | ||||||
|  | # Redistribution and use in source and binary forms, with or without | ||||||
|  | # modification, are permitted provided that the following conditions are | ||||||
|  | # met: | ||||||
|  | # | ||||||
|  | #     * Redistributions of source code must retain the above copyright | ||||||
|  | # notice, this list of conditions and the following disclaimer. | ||||||
|  | #     * Redistributions in binary form must reproduce the above | ||||||
|  | # copyright notice, this list of conditions and the following disclaimer | ||||||
|  | # in the documentation and/or other materials provided with the | ||||||
|  | # distribution. | ||||||
|  | #     * Neither the name of Google Inc. nor the names of its | ||||||
|  | # contributors may be used to endorse or promote products derived from | ||||||
|  | # this software without specific prior written permission. | ||||||
|  | # | ||||||
|  | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||||
|  | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||||
|  | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||||
|  | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||||
|  | # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||||
|  | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||||
|  | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||||
|  | # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||||
|  | # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||||
|  | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | AC_PREREQ(2.57) | ||||||
|  | 
 | ||||||
|  | AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com) | ||||||
|  | dnl Sanity check: the argument is just a file that should exist. | ||||||
|  | AC_CONFIG_SRCDIR(README) | ||||||
|  | AC_CONFIG_AUX_DIR(autotools) | ||||||
|  | AC_CONFIG_MACRO_DIR([m4]) | ||||||
|  | AC_CANONICAL_HOST | ||||||
|  | 
 | ||||||
|  | AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1) | ||||||
|  | AM_CONFIG_HEADER(src/config.h) | ||||||
|  | 
 | ||||||
|  | AM_PROG_AS | ||||||
|  | AC_PROG_CC | ||||||
|  | AM_PROG_CC_C_O | ||||||
|  | AC_PROG_CPP | ||||||
|  | AC_PROG_CXX | ||||||
|  | AC_PROG_RANLIB | ||||||
|  | AM_CONDITIONAL(GCC, test "$GCC" = yes)   # let the Makefile know if we're gcc | ||||||
|  | 
 | ||||||
|  | AC_HEADER_STDC | ||||||
|  | m4_include(m4/ax_pthread.m4) | ||||||
|  | AX_PTHREAD | ||||||
|  | AC_CHECK_HEADERS([a.out.h]) | ||||||
|  | 
 | ||||||
|  | # Only build Linux client libs when compiling for Linux | ||||||
|  | case $host in | ||||||
|  |   *-*-linux* | *-android* ) | ||||||
|  |     LINUX_HOST=true | ||||||
|  |     ;; | ||||||
|  | esac | ||||||
|  | AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue) | ||||||
|  | 
 | ||||||
|  | # Only use Android support headers when compiling for Android | ||||||
|  | case $host in | ||||||
|  |   *-android*) | ||||||
|  |     ANDROID_HOST=true | ||||||
|  |     ;; | ||||||
|  | esac | ||||||
|  | AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue) | ||||||
|  | 
 | ||||||
|  | AC_ARG_ENABLE(m32, | ||||||
|  |               AS_HELP_STRING([--enable-m32], | ||||||
|  |                              [Compile/build with -m32] | ||||||
|  |                              [(default is no)]), | ||||||
|  |               [case "${enableval}" in | ||||||
|  |                  yes) | ||||||
|  |                    CFLAGS="${CFLAGS} -m32" | ||||||
|  |                    CXXFLAGS="${CXXFLAGS} -m32" | ||||||
|  |                    usem32=true | ||||||
|  |                    ;; | ||||||
|  |                  no) | ||||||
|  |                    usem32=false | ||||||
|  |                    ;; | ||||||
|  |                  *) | ||||||
|  |                    AC_MSG_ERROR(bad value ${enableval} for --enable-m32) | ||||||
|  |                    ;; | ||||||
|  |                esac], | ||||||
|  |               [usem32=false]) | ||||||
|  | 
 | ||||||
|  | AC_ARG_ENABLE(processor, | ||||||
|  |               AS_HELP_STRING([--disable-processor], | ||||||
|  |                              [Don't build processor library] | ||||||
|  |                              [(default is no)]), | ||||||
|  |               [case "${enableval}" in | ||||||
|  |                  yes) | ||||||
|  |                    disable_processor=false | ||||||
|  |                    ;; | ||||||
|  |                  no) | ||||||
|  |                    disable_processor=true | ||||||
|  |                    ;; | ||||||
|  |                  *) | ||||||
|  |                    AC_MSG_ERROR(bad value ${enableval} for --disable-processor) | ||||||
|  |                    ;; | ||||||
|  |                esac], | ||||||
|  |               [disable_processor=false]) | ||||||
|  | AM_CONDITIONAL(DISABLE_PROCESSOR, test x$disable_processor = xtrue) | ||||||
|  | 
 | ||||||
|  | AC_ARG_ENABLE(tools, | ||||||
|  |               AS_HELP_STRING([--disable-tools], | ||||||
|  |                              [Don't build tool binaries] | ||||||
|  |                              [(default is no)]), | ||||||
|  |               [case "${enableval}" in | ||||||
|  |                  yes) | ||||||
|  |                    disable_tools=false | ||||||
|  |                    ;; | ||||||
|  |                  no) | ||||||
|  |                    disable_tools=true | ||||||
|  |                    ;; | ||||||
|  |                  *) | ||||||
|  |                    AC_MSG_ERROR(bad value ${enableval} for --disable-tools) | ||||||
|  |                    ;; | ||||||
|  |                esac], | ||||||
|  |               [disable_tools=false]) | ||||||
|  | AM_CONDITIONAL(DISABLE_TOOLS, test x$disable_tools = xtrue) | ||||||
|  | 
 | ||||||
|  | if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then | ||||||
|  |    AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!]) | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | AC_ARG_ENABLE(selftest, | ||||||
|  |               AS_HELP_STRING([--enable-selftest], | ||||||
|  |                              [Run extra tests with "make check" ] | ||||||
|  |                              [(may conflict with optimizations) ] | ||||||
|  |                              [(default is no)]), | ||||||
|  |               [case "${enableval}" in | ||||||
|  |                  yes) | ||||||
|  |                    selftest=true | ||||||
|  |                    ;; | ||||||
|  |                  no) | ||||||
|  |                    selftest=false | ||||||
|  |                    ;; | ||||||
|  |                  *) | ||||||
|  |                    AC_MSG_ERROR(bad value ${enableval} for --enable-selftest) | ||||||
|  |                    ;; | ||||||
|  |                esac], | ||||||
|  |               [selftest=false]) | ||||||
|  | AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue) | ||||||
|  | 
 | ||||||
|  | AC_CONFIG_FILES([Makefile]) | ||||||
|  | AC_OUTPUT | ||||||
							
								
								
									
										283
									
								
								google-breakpad/m4/ax_pthread.m4
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										283
									
								
								google-breakpad/m4/ax_pthread.m4
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,283 @@ | |||||||
|  | # =========================================================================== | ||||||
|  | #           http://www.nongnu.org/autoconf-archive/ax_pthread.html | ||||||
|  | # =========================================================================== | ||||||
|  | # | ||||||
|  | # SYNOPSIS | ||||||
|  | # | ||||||
|  | #   AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) | ||||||
|  | # | ||||||
|  | # DESCRIPTION | ||||||
|  | # | ||||||
|  | #   This macro figures out how to build C programs using POSIX threads. It | ||||||
|  | #   sets the PTHREAD_LIBS output variable to the threads library and linker | ||||||
|  | #   flags, and the PTHREAD_CFLAGS output variable to any special C compiler | ||||||
|  | #   flags that are needed. (The user can also force certain compiler | ||||||
|  | #   flags/libs to be tested by setting these environment variables.) | ||||||
|  | # | ||||||
|  | #   Also sets PTHREAD_CC to any special C compiler that is needed for | ||||||
|  | #   multi-threaded programs (defaults to the value of CC otherwise). (This | ||||||
|  | #   is necessary on AIX to use the special cc_r compiler alias.) | ||||||
|  | # | ||||||
|  | #   NOTE: You are assumed to not only compile your program with these flags, | ||||||
|  | #   but also link it with them as well. e.g. you should link with | ||||||
|  | #   $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS | ||||||
|  | # | ||||||
|  | #   If you are only building threads programs, you may wish to use these | ||||||
|  | #   variables in your default LIBS, CFLAGS, and CC: | ||||||
|  | # | ||||||
|  | #     LIBS="$PTHREAD_LIBS $LIBS" | ||||||
|  | #     CFLAGS="$CFLAGS $PTHREAD_CFLAGS" | ||||||
|  | #     CC="$PTHREAD_CC" | ||||||
|  | # | ||||||
|  | #   In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant | ||||||
|  | #   has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name | ||||||
|  | #   (e.g. PTHREAD_CREATE_UNDETACHED on AIX). | ||||||
|  | # | ||||||
|  | #   ACTION-IF-FOUND is a list of shell commands to run if a threads library | ||||||
|  | #   is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it | ||||||
|  | #   is not found. If ACTION-IF-FOUND is not specified, the default action | ||||||
|  | #   will define HAVE_PTHREAD. | ||||||
|  | # | ||||||
|  | #   Please let the authors know if this macro fails on any platform, or if | ||||||
|  | #   you have any other suggestions or comments. This macro was based on work | ||||||
|  | #   by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help | ||||||
|  | #   from M. Frigo), as well as ac_pthread and hb_pthread macros posted by | ||||||
|  | #   Alejandro Forero Cuervo to the autoconf macro repository. We are also | ||||||
|  | #   grateful for the helpful feedback of numerous users. | ||||||
|  | # | ||||||
|  | # LICENSE | ||||||
|  | # | ||||||
|  | #   Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu> | ||||||
|  | # | ||||||
|  | #   This program is free software: you can redistribute it and/or modify it | ||||||
|  | #   under the terms of the GNU General Public License as published by the | ||||||
|  | #   Free Software Foundation, either version 3 of the License, or (at your | ||||||
|  | #   option) any later version. | ||||||
|  | # | ||||||
|  | #   This program is distributed in the hope that it will be useful, but | ||||||
|  | #   WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General | ||||||
|  | #   Public License for more details. | ||||||
|  | # | ||||||
|  | #   You should have received a copy of the GNU General Public License along | ||||||
|  | #   with this program. If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | # | ||||||
|  | #   As a special exception, the respective Autoconf Macro's copyright owner | ||||||
|  | #   gives unlimited permission to copy, distribute and modify the configure | ||||||
|  | #   scripts that are the output of Autoconf when processing the Macro. You | ||||||
|  | #   need not follow the terms of the GNU General Public License when using | ||||||
|  | #   or distributing such scripts, even though portions of the text of the | ||||||
|  | #   Macro appear in them. The GNU General Public License (GPL) does govern | ||||||
|  | #   all other use of the material that constitutes the Autoconf Macro. | ||||||
|  | # | ||||||
|  | #   This special exception to the GPL applies to versions of the Autoconf | ||||||
|  | #   Macro released by the Autoconf Archive. When you make and distribute a | ||||||
|  | #   modified version of the Autoconf Macro, you may extend this special | ||||||
|  | #   exception to the GPL to apply to your modified version as well. | ||||||
|  | 
 | ||||||
|  | #serial 6 | ||||||
|  | 
 | ||||||
|  | AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) | ||||||
|  | AC_DEFUN([AX_PTHREAD], [ | ||||||
|  | AC_REQUIRE([AC_CANONICAL_HOST]) | ||||||
|  | AC_LANG_SAVE | ||||||
|  | AC_LANG_C | ||||||
|  | ax_pthread_ok=no | ||||||
|  | 
 | ||||||
|  | # We used to check for pthread.h first, but this fails if pthread.h | ||||||
|  | # requires special compiler flags (e.g. on True64 or Sequent). | ||||||
|  | # It gets checked for in the link test anyway. | ||||||
|  | 
 | ||||||
|  | # First of all, check if the user has set any of the PTHREAD_LIBS, | ||||||
|  | # etcetera environment variables, and if threads linking works using | ||||||
|  | # them: | ||||||
|  | if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then | ||||||
|  |         save_CFLAGS="$CFLAGS" | ||||||
|  |         CFLAGS="$CFLAGS $PTHREAD_CFLAGS" | ||||||
|  |         save_LIBS="$LIBS" | ||||||
|  |         LIBS="$PTHREAD_LIBS $LIBS" | ||||||
|  |         AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) | ||||||
|  |         AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) | ||||||
|  |         AC_MSG_RESULT($ax_pthread_ok) | ||||||
|  |         if test x"$ax_pthread_ok" = xno; then | ||||||
|  |                 PTHREAD_LIBS="" | ||||||
|  |                 PTHREAD_CFLAGS="" | ||||||
|  |         fi | ||||||
|  |         LIBS="$save_LIBS" | ||||||
|  |         CFLAGS="$save_CFLAGS" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # We must check for the threads library under a number of different | ||||||
|  | # names; the ordering is very important because some systems | ||||||
|  | # (e.g. DEC) have both -lpthread and -lpthreads, where one of the | ||||||
|  | # libraries is broken (non-POSIX). | ||||||
|  | 
 | ||||||
|  | # Create a list of thread flags to try.  Items starting with a "-" are | ||||||
|  | # C compiler flags, and other items are library names, except for "none" | ||||||
|  | # which indicates that we try without any flags at all, and "pthread-config" | ||||||
|  | # which is a program returning the flags for the Pth emulation library. | ||||||
|  | 
 | ||||||
|  | ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" | ||||||
|  | 
 | ||||||
|  | # The ordering *is* (sometimes) important.  Some notes on the | ||||||
|  | # individual items follow: | ||||||
|  | 
 | ||||||
|  | # pthreads: AIX (must check this before -lpthread) | ||||||
|  | # none: in case threads are in libc; should be tried before -Kthread and | ||||||
|  | #       other compiler flags to prevent continual compiler warnings | ||||||
|  | # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) | ||||||
|  | # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) | ||||||
|  | # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) | ||||||
|  | # -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) | ||||||
|  | # -pthreads: Solaris/gcc | ||||||
|  | # -mthreads: Mingw32/gcc, Lynx/gcc | ||||||
|  | # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it | ||||||
|  | #      doesn't hurt to check since this sometimes defines pthreads too; | ||||||
|  | #      also defines -D_REENTRANT) | ||||||
|  | #      ... -mt is also the pthreads flag for HP/aCC | ||||||
|  | # pthread: Linux, etcetera | ||||||
|  | # --thread-safe: KAI C++ | ||||||
|  | # pthread-config: use pthread-config program (for GNU Pth library) | ||||||
|  | 
 | ||||||
|  | case "${host_cpu}-${host_os}" in | ||||||
|  |         *solaris*) | ||||||
|  | 
 | ||||||
|  |         # On Solaris (at least, for some versions), libc contains stubbed | ||||||
|  |         # (non-functional) versions of the pthreads routines, so link-based | ||||||
|  |         # tests will erroneously succeed.  (We need to link with -pthreads/-mt/ | ||||||
|  |         # -lpthread.)  (The stubs are missing pthread_cleanup_push, or rather | ||||||
|  |         # a function called by this macro, so we could check for that, but | ||||||
|  |         # who knows whether they'll stub that too in a future libc.)  So, | ||||||
|  |         # we'll just look for -pthreads and -lpthread first: | ||||||
|  | 
 | ||||||
|  |         ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" | ||||||
|  |         ;; | ||||||
|  | 
 | ||||||
|  | 	*-darwin*) | ||||||
|  | 	acx_pthread_flags="-pthread $acx_pthread_flags" | ||||||
|  | 	;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | if test x"$ax_pthread_ok" = xno; then | ||||||
|  | for flag in $ax_pthread_flags; do | ||||||
|  | 
 | ||||||
|  |         case $flag in | ||||||
|  |                 none) | ||||||
|  |                 AC_MSG_CHECKING([whether pthreads work without any flags]) | ||||||
|  |                 ;; | ||||||
|  | 
 | ||||||
|  |                 -*) | ||||||
|  |                 AC_MSG_CHECKING([whether pthreads work with $flag]) | ||||||
|  |                 PTHREAD_CFLAGS="$flag" | ||||||
|  |                 ;; | ||||||
|  | 
 | ||||||
|  | 		pthread-config) | ||||||
|  | 		AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) | ||||||
|  | 		if test x"$ax_pthread_config" = xno; then continue; fi | ||||||
|  | 		PTHREAD_CFLAGS="`pthread-config --cflags`" | ||||||
|  | 		PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" | ||||||
|  | 		;; | ||||||
|  | 
 | ||||||
|  |                 *) | ||||||
|  |                 AC_MSG_CHECKING([for the pthreads library -l$flag]) | ||||||
|  |                 PTHREAD_LIBS="-l$flag" | ||||||
|  |                 ;; | ||||||
|  |         esac | ||||||
|  | 
 | ||||||
|  |         save_LIBS="$LIBS" | ||||||
|  |         save_CFLAGS="$CFLAGS" | ||||||
|  |         LIBS="$PTHREAD_LIBS $LIBS" | ||||||
|  |         CFLAGS="$CFLAGS $PTHREAD_CFLAGS" | ||||||
|  | 
 | ||||||
|  |         # Check for various functions.  We must include pthread.h, | ||||||
|  |         # since some functions may be macros.  (On the Sequent, we | ||||||
|  |         # need a special flag -Kthread to make this header compile.) | ||||||
|  |         # We check for pthread_join because it is in -lpthread on IRIX | ||||||
|  |         # while pthread_create is in libc.  We check for pthread_attr_init | ||||||
|  |         # due to DEC craziness with -lpthreads.  We check for | ||||||
|  |         # pthread_cleanup_push because it is one of the few pthread | ||||||
|  |         # functions on Solaris that doesn't have a non-functional libc stub. | ||||||
|  |         # We try pthread_create on general principles. | ||||||
|  |         AC_TRY_LINK([#include <pthread.h> | ||||||
|  | 	             static void routine(void* a) {a=0;} | ||||||
|  | 	             static void* start_routine(void* a) {return a;}], | ||||||
|  |                     [pthread_t th; pthread_attr_t attr; | ||||||
|  |                      pthread_join(th, 0); | ||||||
|  |                      pthread_attr_init(&attr); | ||||||
|  |                      pthread_cleanup_push(routine, 0); | ||||||
|  |                      pthread_create(&th,0,start_routine,0); | ||||||
|  |                      pthread_cleanup_pop(0); ], | ||||||
|  |                     [ax_pthread_ok=yes]) | ||||||
|  | 
 | ||||||
|  |         LIBS="$save_LIBS" | ||||||
|  |         CFLAGS="$save_CFLAGS" | ||||||
|  | 
 | ||||||
|  |         AC_MSG_RESULT($ax_pthread_ok) | ||||||
|  |         if test "x$ax_pthread_ok" = xyes; then | ||||||
|  |                 break; | ||||||
|  |         fi | ||||||
|  | 
 | ||||||
|  |         PTHREAD_LIBS="" | ||||||
|  |         PTHREAD_CFLAGS="" | ||||||
|  | done | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | # Various other checks: | ||||||
|  | if test "x$ax_pthread_ok" = xyes; then | ||||||
|  |         save_LIBS="$LIBS" | ||||||
|  |         LIBS="$PTHREAD_LIBS $LIBS" | ||||||
|  |         save_CFLAGS="$CFLAGS" | ||||||
|  |         CFLAGS="$CFLAGS $PTHREAD_CFLAGS" | ||||||
|  | 
 | ||||||
|  |         # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. | ||||||
|  | 	AC_MSG_CHECKING([for joinable pthread attribute]) | ||||||
|  | 	attr_name=unknown | ||||||
|  | 	for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do | ||||||
|  | 	    AC_TRY_LINK([#include <pthread.h>], [int attr=$attr; return attr;], | ||||||
|  |                         [attr_name=$attr; break]) | ||||||
|  | 	done | ||||||
|  |         AC_MSG_RESULT($attr_name) | ||||||
|  |         if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then | ||||||
|  |             AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, | ||||||
|  |                                [Define to necessary symbol if this constant | ||||||
|  |                                 uses a non-standard name on your system.]) | ||||||
|  |         fi | ||||||
|  | 
 | ||||||
|  |         AC_MSG_CHECKING([if more special flags are required for pthreads]) | ||||||
|  |         flag=no | ||||||
|  |         case "${host_cpu}-${host_os}" in | ||||||
|  |             *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; | ||||||
|  |             *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; | ||||||
|  |         esac | ||||||
|  |         AC_MSG_RESULT(${flag}) | ||||||
|  |         if test "x$flag" != xno; then | ||||||
|  |             PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" | ||||||
|  |         fi | ||||||
|  | 
 | ||||||
|  |         LIBS="$save_LIBS" | ||||||
|  |         CFLAGS="$save_CFLAGS" | ||||||
|  | 
 | ||||||
|  |         # More AIX lossage: must compile with xlc_r or cc_r | ||||||
|  | 	if test x"$GCC" != xyes; then | ||||||
|  |           AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) | ||||||
|  |         else | ||||||
|  |           PTHREAD_CC=$CC | ||||||
|  | 	fi | ||||||
|  | else | ||||||
|  |         PTHREAD_CC="$CC" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | AC_SUBST(PTHREAD_LIBS) | ||||||
|  | AC_SUBST(PTHREAD_CFLAGS) | ||||||
|  | AC_SUBST(PTHREAD_CC) | ||||||
|  | 
 | ||||||
|  | # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: | ||||||
|  | if test x"$ax_pthread_ok" = xyes; then | ||||||
|  |         ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) | ||||||
|  |         : | ||||||
|  | else | ||||||
|  |         ax_pthread_ok=no | ||||||
|  |         $2 | ||||||
|  | fi | ||||||
|  | AC_LANG_RESTORE | ||||||
|  | ])dnl AX_PTHREAD | ||||||
							
								
								
									
										7377
									
								
								google-breakpad/m4/libtool.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										7377
									
								
								google-breakpad/m4/libtool.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										368
									
								
								google-breakpad/m4/ltoptions.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										368
									
								
								google-breakpad/m4/ltoptions.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,368 @@ | |||||||
|  | # Helper functions for option handling.                    -*- Autoconf -*- | ||||||
|  | # | ||||||
|  | #   Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. | ||||||
|  | #   Written by Gary V. Vaughan, 2004 | ||||||
|  | # | ||||||
|  | # This file is free software; the Free Software Foundation gives | ||||||
|  | # unlimited permission to copy and/or distribute it, with or without | ||||||
|  | # modifications, as long as this notice is preserved. | ||||||
|  | 
 | ||||||
|  | # serial 6 ltoptions.m4 | ||||||
|  | 
 | ||||||
|  | # This is to help aclocal find these macros, as it can't see m4_define. | ||||||
|  | AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) | ||||||
|  | # ------------------------------------------ | ||||||
|  | m4_define([_LT_MANGLE_OPTION], | ||||||
|  | [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) | ||||||
|  | # --------------------------------------- | ||||||
|  | # Set option OPTION-NAME for macro MACRO-NAME, and if there is a | ||||||
|  | # matching handler defined, dispatch to it.  Other OPTION-NAMEs are | ||||||
|  | # saved as a flag. | ||||||
|  | m4_define([_LT_SET_OPTION], | ||||||
|  | [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl | ||||||
|  | m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), | ||||||
|  |         _LT_MANGLE_DEFUN([$1], [$2]), | ||||||
|  |     [m4_warning([Unknown $1 option `$2'])])[]dnl | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) | ||||||
|  | # ------------------------------------------------------------ | ||||||
|  | # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. | ||||||
|  | m4_define([_LT_IF_OPTION], | ||||||
|  | [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) | ||||||
|  | # ------------------------------------------------------- | ||||||
|  | # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME | ||||||
|  | # are set. | ||||||
|  | m4_define([_LT_UNLESS_OPTIONS], | ||||||
|  | [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), | ||||||
|  | 	    [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), | ||||||
|  | 		      [m4_define([$0_found])])])[]dnl | ||||||
|  | m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 | ||||||
|  | ])[]dnl | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) | ||||||
|  | # ---------------------------------------- | ||||||
|  | # OPTION-LIST is a space-separated list of Libtool options associated | ||||||
|  | # with MACRO-NAME.  If any OPTION has a matching handler declared with | ||||||
|  | # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about | ||||||
|  | # the unknown option and exit. | ||||||
|  | m4_defun([_LT_SET_OPTIONS], | ||||||
|  | [# Set options | ||||||
|  | m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), | ||||||
|  |     [_LT_SET_OPTION([$1], _LT_Option)]) | ||||||
|  | 
 | ||||||
|  | m4_if([$1],[LT_INIT],[ | ||||||
|  |   dnl | ||||||
|  |   dnl Simply set some default values (i.e off) if boolean options were not | ||||||
|  |   dnl specified: | ||||||
|  |   _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no | ||||||
|  |   ]) | ||||||
|  |   _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no | ||||||
|  |   ]) | ||||||
|  |   dnl | ||||||
|  |   dnl If no reference was made to various pairs of opposing options, then | ||||||
|  |   dnl we run the default mode handler for the pair.  For example, if neither | ||||||
|  |   dnl `shared' nor `disable-shared' was passed, we enable building of shared | ||||||
|  |   dnl archives by default: | ||||||
|  |   _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) | ||||||
|  |   _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) | ||||||
|  |   _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) | ||||||
|  |   _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], | ||||||
|  |   		   [_LT_ENABLE_FAST_INSTALL]) | ||||||
|  |   ]) | ||||||
|  | ])# _LT_SET_OPTIONS | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | ## --------------------------------- ## | ||||||
|  | ## Macros to handle LT_INIT options. ## | ||||||
|  | ## --------------------------------- ## | ||||||
|  | 
 | ||||||
|  | # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) | ||||||
|  | # ----------------------------------------- | ||||||
|  | m4_define([_LT_MANGLE_DEFUN], | ||||||
|  | [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) | ||||||
|  | # ----------------------------------------------- | ||||||
|  | m4_define([LT_OPTION_DEFINE], | ||||||
|  | [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl | ||||||
|  | ])# LT_OPTION_DEFINE | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # dlopen | ||||||
|  | # ------ | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | AU_DEFUN([AC_LIBTOOL_DLOPEN], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], [dlopen]) | ||||||
|  | AC_DIAGNOSE([obsolete], | ||||||
|  | [$0: Remove this warning and the call to _LT_SET_OPTION when you | ||||||
|  | put the `dlopen' option into LT_INIT's first parameter.]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | dnl aclocal-1.4 backwards compatibility: | ||||||
|  | dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # win32-dll | ||||||
|  | # --------- | ||||||
|  | # Declare package support for building win32 dll's. | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [win32-dll], | ||||||
|  | [enable_win32_dll=yes | ||||||
|  | 
 | ||||||
|  | case $host in | ||||||
|  | *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*) | ||||||
|  |   AC_CHECK_TOOL(AS, as, false) | ||||||
|  |   AC_CHECK_TOOL(DLLTOOL, dlltool, false) | ||||||
|  |   AC_CHECK_TOOL(OBJDUMP, objdump, false) | ||||||
|  |   ;; | ||||||
|  | esac | ||||||
|  | 
 | ||||||
|  | test -z "$AS" && AS=as | ||||||
|  | _LT_DECL([], [AS],      [0], [Assembler program])dnl | ||||||
|  | 
 | ||||||
|  | test -z "$DLLTOOL" && DLLTOOL=dlltool | ||||||
|  | _LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl | ||||||
|  | 
 | ||||||
|  | test -z "$OBJDUMP" && OBJDUMP=objdump | ||||||
|  | _LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl | ||||||
|  | ])# win32-dll | ||||||
|  | 
 | ||||||
|  | AU_DEFUN([AC_LIBTOOL_WIN32_DLL], | ||||||
|  | [AC_REQUIRE([AC_CANONICAL_HOST])dnl | ||||||
|  | _LT_SET_OPTION([LT_INIT], [win32-dll]) | ||||||
|  | AC_DIAGNOSE([obsolete], | ||||||
|  | [$0: Remove this warning and the call to _LT_SET_OPTION when you | ||||||
|  | put the `win32-dll' option into LT_INIT's first parameter.]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | dnl aclocal-1.4 backwards compatibility: | ||||||
|  | dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_ENABLE_SHARED([DEFAULT]) | ||||||
|  | # ---------------------------- | ||||||
|  | # implement the --enable-shared flag, and supports the `shared' and | ||||||
|  | # `disable-shared' LT_INIT options. | ||||||
|  | # DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'. | ||||||
|  | m4_define([_LT_ENABLE_SHARED], | ||||||
|  | [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl | ||||||
|  | AC_ARG_ENABLE([shared], | ||||||
|  |     [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], | ||||||
|  | 	[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], | ||||||
|  |     [p=${PACKAGE-default} | ||||||
|  |     case $enableval in | ||||||
|  |     yes) enable_shared=yes ;; | ||||||
|  |     no) enable_shared=no ;; | ||||||
|  |     *) | ||||||
|  |       enable_shared=no | ||||||
|  |       # Look at the argument we got.  We use all the common list separators. | ||||||
|  |       lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," | ||||||
|  |       for pkg in $enableval; do | ||||||
|  | 	IFS="$lt_save_ifs" | ||||||
|  | 	if test "X$pkg" = "X$p"; then | ||||||
|  | 	  enable_shared=yes | ||||||
|  | 	fi | ||||||
|  |       done | ||||||
|  |       IFS="$lt_save_ifs" | ||||||
|  |       ;; | ||||||
|  |     esac], | ||||||
|  |     [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) | ||||||
|  | 
 | ||||||
|  |     _LT_DECL([build_libtool_libs], [enable_shared], [0], | ||||||
|  | 	[Whether or not to build shared libraries]) | ||||||
|  | ])# _LT_ENABLE_SHARED | ||||||
|  | 
 | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) | ||||||
|  | 
 | ||||||
|  | # Old names: | ||||||
|  | AC_DEFUN([AC_ENABLE_SHARED], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | AC_DEFUN([AC_DISABLE_SHARED], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], [disable-shared]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) | ||||||
|  | AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) | ||||||
|  | 
 | ||||||
|  | dnl aclocal-1.4 backwards compatibility: | ||||||
|  | dnl AC_DEFUN([AM_ENABLE_SHARED], []) | ||||||
|  | dnl AC_DEFUN([AM_DISABLE_SHARED], []) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_ENABLE_STATIC([DEFAULT]) | ||||||
|  | # ---------------------------- | ||||||
|  | # implement the --enable-static flag, and support the `static' and | ||||||
|  | # `disable-static' LT_INIT options. | ||||||
|  | # DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'. | ||||||
|  | m4_define([_LT_ENABLE_STATIC], | ||||||
|  | [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl | ||||||
|  | AC_ARG_ENABLE([static], | ||||||
|  |     [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], | ||||||
|  | 	[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], | ||||||
|  |     [p=${PACKAGE-default} | ||||||
|  |     case $enableval in | ||||||
|  |     yes) enable_static=yes ;; | ||||||
|  |     no) enable_static=no ;; | ||||||
|  |     *) | ||||||
|  |      enable_static=no | ||||||
|  |       # Look at the argument we got.  We use all the common list separators. | ||||||
|  |       lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," | ||||||
|  |       for pkg in $enableval; do | ||||||
|  | 	IFS="$lt_save_ifs" | ||||||
|  | 	if test "X$pkg" = "X$p"; then | ||||||
|  | 	  enable_static=yes | ||||||
|  | 	fi | ||||||
|  |       done | ||||||
|  |       IFS="$lt_save_ifs" | ||||||
|  |       ;; | ||||||
|  |     esac], | ||||||
|  |     [enable_static=]_LT_ENABLE_STATIC_DEFAULT) | ||||||
|  | 
 | ||||||
|  |     _LT_DECL([build_old_libs], [enable_static], [0], | ||||||
|  | 	[Whether or not to build static libraries]) | ||||||
|  | ])# _LT_ENABLE_STATIC | ||||||
|  | 
 | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) | ||||||
|  | 
 | ||||||
|  | # Old names: | ||||||
|  | AC_DEFUN([AC_ENABLE_STATIC], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | AC_DEFUN([AC_DISABLE_STATIC], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], [disable-static]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) | ||||||
|  | AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) | ||||||
|  | 
 | ||||||
|  | dnl aclocal-1.4 backwards compatibility: | ||||||
|  | dnl AC_DEFUN([AM_ENABLE_STATIC], []) | ||||||
|  | dnl AC_DEFUN([AM_DISABLE_STATIC], []) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_ENABLE_FAST_INSTALL([DEFAULT]) | ||||||
|  | # ---------------------------------- | ||||||
|  | # implement the --enable-fast-install flag, and support the `fast-install' | ||||||
|  | # and `disable-fast-install' LT_INIT options. | ||||||
|  | # DEFAULT is either `yes' or `no'.  If omitted, it defaults to `yes'. | ||||||
|  | m4_define([_LT_ENABLE_FAST_INSTALL], | ||||||
|  | [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl | ||||||
|  | AC_ARG_ENABLE([fast-install], | ||||||
|  |     [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], | ||||||
|  |     [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], | ||||||
|  |     [p=${PACKAGE-default} | ||||||
|  |     case $enableval in | ||||||
|  |     yes) enable_fast_install=yes ;; | ||||||
|  |     no) enable_fast_install=no ;; | ||||||
|  |     *) | ||||||
|  |       enable_fast_install=no | ||||||
|  |       # Look at the argument we got.  We use all the common list separators. | ||||||
|  |       lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," | ||||||
|  |       for pkg in $enableval; do | ||||||
|  | 	IFS="$lt_save_ifs" | ||||||
|  | 	if test "X$pkg" = "X$p"; then | ||||||
|  | 	  enable_fast_install=yes | ||||||
|  | 	fi | ||||||
|  |       done | ||||||
|  |       IFS="$lt_save_ifs" | ||||||
|  |       ;; | ||||||
|  |     esac], | ||||||
|  |     [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) | ||||||
|  | 
 | ||||||
|  | _LT_DECL([fast_install], [enable_fast_install], [0], | ||||||
|  | 	 [Whether or not to optimize for fast installation])dnl | ||||||
|  | ])# _LT_ENABLE_FAST_INSTALL | ||||||
|  | 
 | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) | ||||||
|  | 
 | ||||||
|  | # Old names: | ||||||
|  | AU_DEFUN([AC_ENABLE_FAST_INSTALL], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) | ||||||
|  | AC_DIAGNOSE([obsolete], | ||||||
|  | [$0: Remove this warning and the call to _LT_SET_OPTION when you put | ||||||
|  | the `fast-install' option into LT_INIT's first parameter.]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | AU_DEFUN([AC_DISABLE_FAST_INSTALL], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) | ||||||
|  | AC_DIAGNOSE([obsolete], | ||||||
|  | [$0: Remove this warning and the call to _LT_SET_OPTION when you put | ||||||
|  | the `disable-fast-install' option into LT_INIT's first parameter.]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | dnl aclocal-1.4 backwards compatibility: | ||||||
|  | dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) | ||||||
|  | dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # _LT_WITH_PIC([MODE]) | ||||||
|  | # -------------------- | ||||||
|  | # implement the --with-pic flag, and support the `pic-only' and `no-pic' | ||||||
|  | # LT_INIT options. | ||||||
|  | # MODE is either `yes' or `no'.  If omitted, it defaults to `both'. | ||||||
|  | m4_define([_LT_WITH_PIC], | ||||||
|  | [AC_ARG_WITH([pic], | ||||||
|  |     [AS_HELP_STRING([--with-pic], | ||||||
|  | 	[try to use only PIC/non-PIC objects @<:@default=use both@:>@])], | ||||||
|  |     [pic_mode="$withval"], | ||||||
|  |     [pic_mode=default]) | ||||||
|  | 
 | ||||||
|  | test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) | ||||||
|  | 
 | ||||||
|  | _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl | ||||||
|  | ])# _LT_WITH_PIC | ||||||
|  | 
 | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) | ||||||
|  | LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) | ||||||
|  | 
 | ||||||
|  | # Old name: | ||||||
|  | AU_DEFUN([AC_LIBTOOL_PICMODE], | ||||||
|  | [_LT_SET_OPTION([LT_INIT], [pic-only]) | ||||||
|  | AC_DIAGNOSE([obsolete], | ||||||
|  | [$0: Remove this warning and the call to _LT_SET_OPTION when you | ||||||
|  | put the `pic-only' option into LT_INIT's first parameter.]) | ||||||
|  | ]) | ||||||
|  | 
 | ||||||
|  | dnl aclocal-1.4 backwards compatibility: | ||||||
|  | dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) | ||||||
|  | 
 | ||||||
|  | ## ----------------- ## | ||||||
|  | ## LTDL_INIT Options ## | ||||||
|  | ## ----------------- ## | ||||||
|  | 
 | ||||||
|  | m4_define([_LTDL_MODE], []) | ||||||
|  | LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], | ||||||
|  | 		 [m4_define([_LTDL_MODE], [nonrecursive])]) | ||||||
|  | LT_OPTION_DEFINE([LTDL_INIT], [recursive], | ||||||
|  | 		 [m4_define([_LTDL_MODE], [recursive])]) | ||||||
|  | LT_OPTION_DEFINE([LTDL_INIT], [subproject], | ||||||
|  | 		 [m4_define([_LTDL_MODE], [subproject])]) | ||||||
|  | 
 | ||||||
|  | m4_define([_LTDL_TYPE], []) | ||||||
|  | LT_OPTION_DEFINE([LTDL_INIT], [installable], | ||||||
|  | 		 [m4_define([_LTDL_TYPE], [installable])]) | ||||||
|  | LT_OPTION_DEFINE([LTDL_INIT], [convenience], | ||||||
|  | 		 [m4_define([_LTDL_TYPE], [convenience])]) | ||||||
							
								
								
									
										123
									
								
								google-breakpad/m4/ltsugar.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								google-breakpad/m4/ltsugar.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,123 @@ | |||||||
|  | # ltsugar.m4 -- libtool m4 base layer.                         -*-Autoconf-*- | ||||||
|  | # | ||||||
|  | # Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. | ||||||
|  | # Written by Gary V. Vaughan, 2004 | ||||||
|  | # | ||||||
|  | # This file is free software; the Free Software Foundation gives | ||||||
|  | # unlimited permission to copy and/or distribute it, with or without | ||||||
|  | # modifications, as long as this notice is preserved. | ||||||
|  | 
 | ||||||
|  | # serial 6 ltsugar.m4 | ||||||
|  | 
 | ||||||
|  | # This is to help aclocal find these macros, as it can't see m4_define. | ||||||
|  | AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_join(SEP, ARG1, [ARG2...]) | ||||||
|  | # ----------------------------- | ||||||
|  | # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their | ||||||
|  | # associated separator. | ||||||
|  | # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier | ||||||
|  | # versions in m4sugar had bugs. | ||||||
|  | m4_define([lt_join], | ||||||
|  | [m4_if([$#], [1], [], | ||||||
|  |        [$#], [2], [[$2]], | ||||||
|  |        [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) | ||||||
|  | m4_define([_lt_join], | ||||||
|  | [m4_if([$#$2], [2], [], | ||||||
|  |        [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_car(LIST) | ||||||
|  | # lt_cdr(LIST) | ||||||
|  | # ------------ | ||||||
|  | # Manipulate m4 lists. | ||||||
|  | # These macros are necessary as long as will still need to support | ||||||
|  | # Autoconf-2.59 which quotes differently. | ||||||
|  | m4_define([lt_car], [[$1]]) | ||||||
|  | m4_define([lt_cdr], | ||||||
|  | [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], | ||||||
|  |        [$#], 1, [], | ||||||
|  |        [m4_dquote(m4_shift($@))])]) | ||||||
|  | m4_define([lt_unquote], $1) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_append(MACRO-NAME, STRING, [SEPARATOR]) | ||||||
|  | # ------------------------------------------ | ||||||
|  | # Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. | ||||||
|  | # Note that neither SEPARATOR nor STRING are expanded; they are appended | ||||||
|  | # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). | ||||||
|  | # No SEPARATOR is output if MACRO-NAME was previously undefined (different | ||||||
|  | # than defined and empty). | ||||||
|  | # | ||||||
|  | # This macro is needed until we can rely on Autoconf 2.62, since earlier | ||||||
|  | # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. | ||||||
|  | m4_define([lt_append], | ||||||
|  | [m4_define([$1], | ||||||
|  | 	   m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) | ||||||
|  | # ---------------------------------------------------------- | ||||||
|  | # Produce a SEP delimited list of all paired combinations of elements of | ||||||
|  | # PREFIX-LIST with SUFFIX1 through SUFFIXn.  Each element of the list | ||||||
|  | # has the form PREFIXmINFIXSUFFIXn. | ||||||
|  | # Needed until we can rely on m4_combine added in Autoconf 2.62. | ||||||
|  | m4_define([lt_combine], | ||||||
|  | [m4_if(m4_eval([$# > 3]), [1], | ||||||
|  |        [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl | ||||||
|  | [[m4_foreach([_Lt_prefix], [$2], | ||||||
|  | 	     [m4_foreach([_Lt_suffix], | ||||||
|  | 		]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, | ||||||
|  | 	[_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) | ||||||
|  | # ----------------------------------------------------------------------- | ||||||
|  | # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited | ||||||
|  | # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. | ||||||
|  | m4_define([lt_if_append_uniq], | ||||||
|  | [m4_ifdef([$1], | ||||||
|  | 	  [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], | ||||||
|  | 		 [lt_append([$1], [$2], [$3])$4], | ||||||
|  | 		 [$5])], | ||||||
|  | 	  [lt_append([$1], [$2], [$3])$4])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_dict_add(DICT, KEY, VALUE) | ||||||
|  | # ----------------------------- | ||||||
|  | m4_define([lt_dict_add], | ||||||
|  | [m4_define([$1($2)], [$3])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) | ||||||
|  | # -------------------------------------------- | ||||||
|  | m4_define([lt_dict_add_subkey], | ||||||
|  | [m4_define([$1($2:$3)], [$4])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_dict_fetch(DICT, KEY, [SUBKEY]) | ||||||
|  | # ---------------------------------- | ||||||
|  | m4_define([lt_dict_fetch], | ||||||
|  | [m4_ifval([$3], | ||||||
|  | 	m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), | ||||||
|  |     m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) | ||||||
|  | # ----------------------------------------------------------------- | ||||||
|  | m4_define([lt_if_dict_fetch], | ||||||
|  | [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], | ||||||
|  | 	[$5], | ||||||
|  |     [$6])]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) | ||||||
|  | # -------------------------------------------------------------- | ||||||
|  | m4_define([lt_dict_filter], | ||||||
|  | [m4_if([$5], [], [], | ||||||
|  |   [lt_join(m4_quote(m4_default([$4], [[, ]])), | ||||||
|  |            lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), | ||||||
|  | 		      [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl | ||||||
|  | ]) | ||||||
							
								
								
									
										23
									
								
								google-breakpad/m4/ltversion.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								google-breakpad/m4/ltversion.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | # ltversion.m4 -- version numbers			-*- Autoconf -*- | ||||||
|  | # | ||||||
|  | #   Copyright (C) 2004 Free Software Foundation, Inc. | ||||||
|  | #   Written by Scott James Remnant, 2004 | ||||||
|  | # | ||||||
|  | # This file is free software; the Free Software Foundation gives | ||||||
|  | # unlimited permission to copy and/or distribute it, with or without | ||||||
|  | # modifications, as long as this notice is preserved. | ||||||
|  | 
 | ||||||
|  | # Generated from ltversion.in. | ||||||
|  | 
 | ||||||
|  | # serial 3017 ltversion.m4 | ||||||
|  | # This file is part of GNU Libtool | ||||||
|  | 
 | ||||||
|  | m4_define([LT_PACKAGE_VERSION], [2.2.6b]) | ||||||
|  | m4_define([LT_PACKAGE_REVISION], [1.3017]) | ||||||
|  | 
 | ||||||
|  | AC_DEFUN([LTVERSION_VERSION], | ||||||
|  | [macro_version='2.2.6b' | ||||||
|  | macro_revision='1.3017' | ||||||
|  | _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) | ||||||
|  | _LT_DECL(, macro_revision, 0) | ||||||
|  | ]) | ||||||
							
								
								
									
										92
									
								
								google-breakpad/m4/lt~obsolete.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								google-breakpad/m4/lt~obsolete.m4
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | |||||||
|  | # lt~obsolete.m4 -- aclocal satisfying obsolete definitions.    -*-Autoconf-*- | ||||||
|  | # | ||||||
|  | #   Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. | ||||||
|  | #   Written by Scott James Remnant, 2004. | ||||||
|  | # | ||||||
|  | # This file is free software; the Free Software Foundation gives | ||||||
|  | # unlimited permission to copy and/or distribute it, with or without | ||||||
|  | # modifications, as long as this notice is preserved. | ||||||
|  | 
 | ||||||
|  | # serial 4 lt~obsolete.m4 | ||||||
|  | 
 | ||||||
|  | # These exist entirely to fool aclocal when bootstrapping libtool. | ||||||
|  | # | ||||||
|  | # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) | ||||||
|  | # which have later been changed to m4_define as they aren't part of the | ||||||
|  | # exported API, or moved to Autoconf or Automake where they belong. | ||||||
|  | # | ||||||
|  | # The trouble is, aclocal is a bit thick.  It'll see the old AC_DEFUN | ||||||
|  | # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us | ||||||
|  | # using a macro with the same name in our local m4/libtool.m4 it'll | ||||||
|  | # pull the old libtool.m4 in (it doesn't see our shiny new m4_define | ||||||
|  | # and doesn't know about Autoconf macros at all.) | ||||||
|  | # | ||||||
|  | # So we provide this file, which has a silly filename so it's always | ||||||
|  | # included after everything else.  This provides aclocal with the | ||||||
|  | # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything | ||||||
|  | # because those macros already exist, or will be overwritten later. | ||||||
|  | # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.  | ||||||
|  | # | ||||||
|  | # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. | ||||||
|  | # Yes, that means every name once taken will need to remain here until | ||||||
|  | # we give up compatibility with versions before 1.7, at which point | ||||||
|  | # we need to keep only those names which we still refer to. | ||||||
|  | 
 | ||||||
|  | # This is to help aclocal find these macros, as it can't see m4_define. | ||||||
|  | AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) | ||||||
|  | 
 | ||||||
|  | m4_ifndef([AC_LIBTOOL_LINKER_OPTION],	[AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) | ||||||
|  | m4_ifndef([AC_PROG_EGREP],		[AC_DEFUN([AC_PROG_EGREP])]) | ||||||
|  | m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH],	[AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) | ||||||
|  | m4_ifndef([_LT_AC_SHELL_INIT],		[AC_DEFUN([_LT_AC_SHELL_INIT])]) | ||||||
|  | m4_ifndef([_LT_AC_SYS_LIBPATH_AIX],	[AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) | ||||||
|  | m4_ifndef([_LT_PROG_LTMAIN],		[AC_DEFUN([_LT_PROG_LTMAIN])]) | ||||||
|  | m4_ifndef([_LT_AC_TAGVAR],		[AC_DEFUN([_LT_AC_TAGVAR])]) | ||||||
|  | m4_ifndef([AC_LTDL_ENABLE_INSTALL],	[AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) | ||||||
|  | m4_ifndef([AC_LTDL_PREOPEN],		[AC_DEFUN([AC_LTDL_PREOPEN])]) | ||||||
|  | m4_ifndef([_LT_AC_SYS_COMPILER],	[AC_DEFUN([_LT_AC_SYS_COMPILER])]) | ||||||
|  | m4_ifndef([_LT_AC_LOCK],		[AC_DEFUN([_LT_AC_LOCK])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE],	[AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) | ||||||
|  | m4_ifndef([_LT_AC_TRY_DLOPEN_SELF],	[AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_PROG_CC_C_O],	[AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_OBJDIR],		[AC_DEFUN([AC_LIBTOOL_OBJDIR])]) | ||||||
|  | m4_ifndef([AC_LTDL_OBJDIR],		[AC_DEFUN([AC_LTDL_OBJDIR])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP],	[AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) | ||||||
|  | m4_ifndef([AC_PATH_MAGIC],		[AC_DEFUN([AC_PATH_MAGIC])]) | ||||||
|  | m4_ifndef([AC_PROG_LD_GNU],		[AC_DEFUN([AC_PROG_LD_GNU])]) | ||||||
|  | m4_ifndef([AC_PROG_LD_RELOAD_FLAG],	[AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) | ||||||
|  | m4_ifndef([AC_DEPLIBS_CHECK_METHOD],	[AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS],	[AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP],	[AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) | ||||||
|  | m4_ifndef([LT_AC_PROG_EGREP],		[AC_DEFUN([LT_AC_PROG_EGREP])]) | ||||||
|  | m4_ifndef([LT_AC_PROG_SED],		[AC_DEFUN([LT_AC_PROG_SED])]) | ||||||
|  | m4_ifndef([_LT_CC_BASENAME],		[AC_DEFUN([_LT_CC_BASENAME])]) | ||||||
|  | m4_ifndef([_LT_COMPILER_BOILERPLATE],	[AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) | ||||||
|  | m4_ifndef([_LT_LINKER_BOILERPLATE],	[AC_DEFUN([_LT_LINKER_BOILERPLATE])]) | ||||||
|  | m4_ifndef([_AC_PROG_LIBTOOL],		[AC_DEFUN([_AC_PROG_LIBTOOL])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_SETUP],		[AC_DEFUN([AC_LIBTOOL_SETUP])]) | ||||||
|  | m4_ifndef([_LT_AC_CHECK_DLFCN],		[AC_DEFUN([_LT_AC_CHECK_DLFCN])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER],	[AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) | ||||||
|  | m4_ifndef([_LT_AC_TAGCONFIG],		[AC_DEFUN([_LT_AC_TAGCONFIG])]) | ||||||
|  | m4_ifndef([AC_DISABLE_FAST_INSTALL],	[AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_CXX],		[AC_DEFUN([_LT_AC_LANG_CXX])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_F77],		[AC_DEFUN([_LT_AC_LANG_F77])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_GCJ],		[AC_DEFUN([_LT_AC_LANG_GCJ])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_RC],		[AC_DEFUN([AC_LIBTOOL_RC])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_C_CONFIG],	[AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_CXX_CONFIG],	[AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_F77_CONFIG],	[AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_GCJ_CONFIG],	[AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG],	[AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) | ||||||
|  | m4_ifndef([_LT_AC_LANG_RC_CONFIG],	[AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) | ||||||
|  | m4_ifndef([AC_LIBTOOL_CONFIG],		[AC_DEFUN([AC_LIBTOOL_CONFIG])]) | ||||||
|  | m4_ifndef([_LT_AC_FILE_LTDLL_C],	[AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) | ||||||
							
								
								
									
										36
									
								
								google-breakpad/src/breakpad_googletest_includes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								google-breakpad/src/breakpad_googletest_includes.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,36 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef BREAKPAD_GOOGLETEST_INCLUDES_H__ | ||||||
|  | #define BREAKPAD_GOOGLETEST_INCLUDES_H__ | ||||||
|  | 
 | ||||||
|  | #include "testing/gtest/include/gtest/gtest.h" | ||||||
|  | #include "testing/include/gmock/gmock.h" | ||||||
|  | 
 | ||||||
|  | #endif  // BREAKPAD_GOOGLETEST_INCLUDES_H__
 | ||||||
| @ -0,0 +1,53 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ | ||||||
|  | #define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class CrashGenerationServer; | ||||||
|  | 
 | ||||||
|  | class ClientInfo { | ||||||
|  |  public: | ||||||
|  |   ClientInfo(pid_t pid, CrashGenerationServer* crash_server) | ||||||
|  |     : crash_server_(crash_server), | ||||||
|  |       pid_(pid) {} | ||||||
|  | 
 | ||||||
|  |   CrashGenerationServer* crash_server() const { return crash_server_; } | ||||||
|  |   pid_t pid() const { return pid_; } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   CrashGenerationServer* crash_server_; | ||||||
|  |   pid_t pid_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_
 | ||||||
| @ -0,0 +1,92 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/crash_generation/crash_generation_client.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "common/linux/ignore_ret.h" | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | bool | ||||||
|  | CrashGenerationClient::RequestDump(const void* blob, size_t blob_size) | ||||||
|  | { | ||||||
|  |   int fds[2]; | ||||||
|  |   sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds); | ||||||
|  |   static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); | ||||||
|  | 
 | ||||||
|  |   struct kernel_msghdr msg; | ||||||
|  |   my_memset(&msg, 0, sizeof(struct kernel_msghdr)); | ||||||
|  |   struct kernel_iovec iov[1]; | ||||||
|  |   iov[0].iov_base = const_cast<void*>(blob); | ||||||
|  |   iov[0].iov_len = blob_size; | ||||||
|  | 
 | ||||||
|  |   msg.msg_iov = iov; | ||||||
|  |   msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); | ||||||
|  |   char cmsg[kControlMsgSize]; | ||||||
|  |   my_memset(cmsg, 0, kControlMsgSize); | ||||||
|  |   msg.msg_control = cmsg; | ||||||
|  |   msg.msg_controllen = sizeof(cmsg); | ||||||
|  | 
 | ||||||
|  |   struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); | ||||||
|  |   hdr->cmsg_level = SOL_SOCKET; | ||||||
|  |   hdr->cmsg_type = SCM_RIGHTS; | ||||||
|  |   hdr->cmsg_len = CMSG_LEN(sizeof(int)); | ||||||
|  |   int* p = reinterpret_cast<int*>(CMSG_DATA(hdr)); | ||||||
|  |   *p = fds[1]; | ||||||
|  | 
 | ||||||
|  |   ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); | ||||||
|  |   sys_close(fds[1]); | ||||||
|  |   if (ret <= 0) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // wait for an ACK from the server
 | ||||||
|  |   char b; | ||||||
|  |   IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //static
 | ||||||
|  | CrashGenerationClient* | ||||||
|  | CrashGenerationClient::TryCreate(int server_fd) | ||||||
|  | { | ||||||
|  |   if (0 > server_fd) | ||||||
|  |     return NULL; | ||||||
|  |   return new CrashGenerationClient(server_fd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -0,0 +1,69 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ | ||||||
|  | #define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ | ||||||
|  | 
 | ||||||
|  | #include <stddef.h> | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class CrashGenerationClient { | ||||||
|  | public: | ||||||
|  |   ~CrashGenerationClient() | ||||||
|  |   { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Request the crash server to generate a dump.  |blob| is a hack,
 | ||||||
|  |   // see exception_handler.h and minidump_writer.h
 | ||||||
|  |   //
 | ||||||
|  |   // Return true if the dump was successful; false otherwise.
 | ||||||
|  |   bool RequestDump(const void* blob, size_t blob_size); | ||||||
|  | 
 | ||||||
|  |   // Return a new CrashGenerationClient if |server_fd| is valid and
 | ||||||
|  |   // connects to a CrashGenerationServer.  Otherwise, return NULL.
 | ||||||
|  |   // The returned CrashGenerationClient* is owned by the caller of
 | ||||||
|  |   // this function.
 | ||||||
|  |   static CrashGenerationClient* TryCreate(int server_fd); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |   CrashGenerationClient(int server_fd) : server_fd_(server_fd) | ||||||
|  |   { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int server_fd_; | ||||||
|  | 
 | ||||||
|  |   // prevent copy construction and assignment
 | ||||||
|  |   CrashGenerationClient(const CrashGenerationClient&); | ||||||
|  |   CrashGenerationClient& operator=(const CrashGenerationClient&); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_
 | ||||||
| @ -0,0 +1,464 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <dirent.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <poll.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/socket.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/crash_generation/crash_generation_server.h" | ||||||
|  | #include "client/linux/crash_generation/client_info.h" | ||||||
|  | #include "client/linux/handler/exception_handler.h" | ||||||
|  | #include "client/linux/minidump_writer/minidump_writer.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "common/linux/guid_creator.h" | ||||||
|  | #include "common/linux/safe_readlink.h" | ||||||
|  | 
 | ||||||
|  | static const char kCommandQuit = 'x'; | ||||||
|  | 
 | ||||||
|  | static bool | ||||||
|  | GetInodeForFileDescriptor(ino_t* inode_out, int fd) | ||||||
|  | { | ||||||
|  |   assert(inode_out); | ||||||
|  | 
 | ||||||
|  |   struct stat buf; | ||||||
|  |   if (fstat(fd, &buf) < 0) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   if (!S_ISSOCK(buf.st_mode)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   *inode_out = buf.st_ino; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // expected prefix of the target of the /proc/self/fd/%d link for a socket
 | ||||||
|  | static const char kSocketLinkPrefix[] = "socket:["; | ||||||
|  | 
 | ||||||
|  | // Parse a symlink in /proc/pid/fd/$x and return the inode number of the
 | ||||||
|  | // socket.
 | ||||||
|  | //   inode_out: (output) set to the inode number on success
 | ||||||
|  | //   path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor)
 | ||||||
|  | static bool | ||||||
|  | GetInodeForProcPath(ino_t* inode_out, const char* path) | ||||||
|  | { | ||||||
|  |   assert(inode_out); | ||||||
|  |   assert(path); | ||||||
|  | 
 | ||||||
|  |   char buf[PATH_MAX]; | ||||||
|  |   if (!google_breakpad::SafeReadLink(path, buf)) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   char* endptr; | ||||||
|  |   const uint64_t inode_ul = | ||||||
|  |       strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); | ||||||
|  |   if (*endptr != ']') | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   if (inode_ul == ULLONG_MAX) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   *inode_out = inode_ul; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool | ||||||
|  | FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) | ||||||
|  | { | ||||||
|  |   assert(pid_out); | ||||||
|  |   bool already_found = false; | ||||||
|  | 
 | ||||||
|  |   DIR* proc = opendir("/proc"); | ||||||
|  |   if (!proc) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   std::vector<pid_t> pids; | ||||||
|  | 
 | ||||||
|  |   struct dirent* dent; | ||||||
|  |   while ((dent = readdir(proc))) { | ||||||
|  |     char* endptr; | ||||||
|  |     const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); | ||||||
|  |     if (pid_ul == ULONG_MAX || '\0' != *endptr) | ||||||
|  |       continue; | ||||||
|  |     pids.push_back(pid_ul); | ||||||
|  |   } | ||||||
|  |   closedir(proc); | ||||||
|  | 
 | ||||||
|  |   for (std::vector<pid_t>::const_iterator | ||||||
|  |        i = pids.begin(); i != pids.end(); ++i) { | ||||||
|  |     const pid_t current_pid = *i; | ||||||
|  |     char buf[PATH_MAX]; | ||||||
|  |     snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); | ||||||
|  |     DIR* fd = opendir(buf); | ||||||
|  |     if (!fd) | ||||||
|  |       continue; | ||||||
|  | 
 | ||||||
|  |     while ((dent = readdir(fd))) { | ||||||
|  |       if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, | ||||||
|  |                    dent->d_name) >= static_cast<int>(sizeof(buf))) { | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       ino_t fd_inode; | ||||||
|  |       if (GetInodeForProcPath(&fd_inode, buf) | ||||||
|  |           && fd_inode == socket_inode) { | ||||||
|  |         if (already_found) { | ||||||
|  |           closedir(fd); | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         already_found = true; | ||||||
|  |         *pid_out = current_pid; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     closedir(fd); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return already_found; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | CrashGenerationServer::CrashGenerationServer( | ||||||
|  |   const int listen_fd, | ||||||
|  |   OnClientDumpRequestCallback dump_callback, | ||||||
|  |   void* dump_context, | ||||||
|  |   OnClientExitingCallback exit_callback, | ||||||
|  |   void* exit_context, | ||||||
|  |   bool generate_dumps, | ||||||
|  |   const string* dump_path) : | ||||||
|  |     server_fd_(listen_fd), | ||||||
|  |     dump_callback_(dump_callback), | ||||||
|  |     dump_context_(dump_context), | ||||||
|  |     exit_callback_(exit_callback), | ||||||
|  |     exit_context_(exit_context), | ||||||
|  |     generate_dumps_(generate_dumps), | ||||||
|  |     started_(false) | ||||||
|  | { | ||||||
|  |   if (dump_path) | ||||||
|  |     dump_dir_ = *dump_path; | ||||||
|  |   else | ||||||
|  |     dump_dir_ = "/tmp"; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CrashGenerationServer::~CrashGenerationServer() | ||||||
|  | { | ||||||
|  |   if (started_) | ||||||
|  |     Stop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool | ||||||
|  | CrashGenerationServer::Start() | ||||||
|  | { | ||||||
|  |   if (started_ || 0 > server_fd_) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   int control_pipe[2]; | ||||||
|  |   if (pipe(control_pipe)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) | ||||||
|  |     return false; | ||||||
|  |   if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   control_pipe_in_ = control_pipe[0]; | ||||||
|  |   control_pipe_out_ = control_pipe[1]; | ||||||
|  | 
 | ||||||
|  |   if (pthread_create(&thread_, NULL, | ||||||
|  |                      ThreadMain, reinterpret_cast<void*>(this))) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   started_ = true; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | CrashGenerationServer::Stop() | ||||||
|  | { | ||||||
|  |   assert(pthread_self() != thread_); | ||||||
|  | 
 | ||||||
|  |   if (!started_) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); | ||||||
|  | 
 | ||||||
|  |   void* dummy; | ||||||
|  |   pthread_join(thread_, &dummy); | ||||||
|  | 
 | ||||||
|  |   started_ = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //static
 | ||||||
|  | bool | ||||||
|  | CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) | ||||||
|  | { | ||||||
|  |   int fds[2]; | ||||||
|  | 
 | ||||||
|  |   if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   static const int on = 1; | ||||||
|  |   // Enable passcred on the server end of the socket
 | ||||||
|  |   if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) | ||||||
|  |     return false; | ||||||
|  |   if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   *client_fd = fds[0]; | ||||||
|  |   *server_fd = fds[1]; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // The following methods/functions execute on the server thread
 | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | CrashGenerationServer::Run() | ||||||
|  | { | ||||||
|  |   struct pollfd pollfds[2]; | ||||||
|  |   memset(&pollfds, 0, sizeof(pollfds)); | ||||||
|  | 
 | ||||||
|  |   pollfds[0].fd = server_fd_; | ||||||
|  |   pollfds[0].events = POLLIN; | ||||||
|  | 
 | ||||||
|  |   pollfds[1].fd = control_pipe_in_; | ||||||
|  |   pollfds[1].events = POLLIN; | ||||||
|  | 
 | ||||||
|  |   while (true) { | ||||||
|  |     // infinite timeout
 | ||||||
|  |     int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); | ||||||
|  |     if (-1 == nevents) { | ||||||
|  |       if (EINTR == errno) { | ||||||
|  |         continue; | ||||||
|  |       } else { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |     if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) | ||||||
|  |       return; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool | ||||||
|  | CrashGenerationServer::ClientEvent(short revents) | ||||||
|  | { | ||||||
|  |   if (POLLHUP & revents) | ||||||
|  |     return false; | ||||||
|  |   assert(POLLIN & revents); | ||||||
|  | 
 | ||||||
|  |   // A process has crashed and has signaled us by writing a datagram
 | ||||||
|  |   // to the death signal socket. The datagram contains the crash context needed
 | ||||||
|  |   // for writing the minidump as well as a file descriptor and a credentials
 | ||||||
|  |   // block so that they can't lie about their pid.
 | ||||||
|  | 
 | ||||||
|  |   // The length of the control message:
 | ||||||
|  |   static const unsigned kControlMsgSize = | ||||||
|  |       CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); | ||||||
|  |   // The length of the regular payload:
 | ||||||
|  |   static const unsigned kCrashContextSize = | ||||||
|  |       sizeof(google_breakpad::ExceptionHandler::CrashContext); | ||||||
|  | 
 | ||||||
|  |   struct msghdr msg = {0}; | ||||||
|  |   struct iovec iov[1]; | ||||||
|  |   char crash_context[kCrashContextSize]; | ||||||
|  |   char control[kControlMsgSize]; | ||||||
|  |   const ssize_t expected_msg_size = sizeof(crash_context); | ||||||
|  | 
 | ||||||
|  |   iov[0].iov_base = crash_context; | ||||||
|  |   iov[0].iov_len = sizeof(crash_context); | ||||||
|  |   msg.msg_iov = iov; | ||||||
|  |   msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); | ||||||
|  |   msg.msg_control = control; | ||||||
|  |   msg.msg_controllen = kControlMsgSize; | ||||||
|  | 
 | ||||||
|  |   const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); | ||||||
|  |   if (msg_size != expected_msg_size) | ||||||
|  |     return true; | ||||||
|  | 
 | ||||||
|  |   if (msg.msg_controllen != kControlMsgSize || | ||||||
|  |       msg.msg_flags & ~MSG_TRUNC) | ||||||
|  |     return true; | ||||||
|  | 
 | ||||||
|  |   // Walk the control payload and extract the file descriptor and validated pid.
 | ||||||
|  |   pid_t crashing_pid = -1; | ||||||
|  |   int signal_fd = -1; | ||||||
|  |   for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; | ||||||
|  |        hdr = CMSG_NXTHDR(&msg, hdr)) { | ||||||
|  |     if (hdr->cmsg_level != SOL_SOCKET) | ||||||
|  |       continue; | ||||||
|  |     if (hdr->cmsg_type == SCM_RIGHTS) { | ||||||
|  |       const unsigned len = hdr->cmsg_len - | ||||||
|  |           (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); | ||||||
|  |       assert(len % sizeof(int) == 0u); | ||||||
|  |       const unsigned num_fds = len / sizeof(int); | ||||||
|  |       if (num_fds > 1 || num_fds == 0) { | ||||||
|  |         // A nasty process could try and send us too many descriptors and
 | ||||||
|  |         // force a leak.
 | ||||||
|  |         for (unsigned i = 0; i < num_fds; ++i) | ||||||
|  |           HANDLE_EINTR(close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i])); | ||||||
|  |         return true; | ||||||
|  |       } else { | ||||||
|  |         signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0]; | ||||||
|  |       } | ||||||
|  |     } else if (hdr->cmsg_type == SCM_CREDENTIALS) { | ||||||
|  |       const struct ucred *cred = | ||||||
|  |           reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); | ||||||
|  |       crashing_pid = cred->pid; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (crashing_pid == -1 || signal_fd == -1) { | ||||||
|  |     if (signal_fd) | ||||||
|  |       HANDLE_EINTR(close(signal_fd)); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Kernel bug workaround (broken in 2.6.30 at least):
 | ||||||
|  |   // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID
 | ||||||
|  |   // namespaces. Thus |crashing_pid| might be garbage from our point of view.
 | ||||||
|  |   // In the future we can remove this workaround, but we have to wait a couple
 | ||||||
|  |   // of years to be sure that it's worked its way out into the world.
 | ||||||
|  | 
 | ||||||
|  |   ino_t inode_number; | ||||||
|  |   if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) { | ||||||
|  |     HANDLE_EINTR(close(signal_fd)); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { | ||||||
|  |     HANDLE_EINTR(close(signal_fd)); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   string minidump_filename; | ||||||
|  |   if (!MakeMinidumpFilename(minidump_filename)) | ||||||
|  |     return true; | ||||||
|  | 
 | ||||||
|  |   if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), | ||||||
|  |                                       crashing_pid, crash_context, | ||||||
|  |                                       kCrashContextSize)) { | ||||||
|  |     HANDLE_EINTR(close(signal_fd)); | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (dump_callback_) { | ||||||
|  |     ClientInfo info(crashing_pid, this); | ||||||
|  | 
 | ||||||
|  |     dump_callback_(dump_context_, &info, &minidump_filename); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Send the done signal to the process: it can exit now.
 | ||||||
|  |   memset(&msg, 0, sizeof(msg)); | ||||||
|  |   struct iovec done_iov; | ||||||
|  |   done_iov.iov_base = const_cast<char*>("\x42"); | ||||||
|  |   done_iov.iov_len = 1; | ||||||
|  |   msg.msg_iov = &done_iov; | ||||||
|  |   msg.msg_iovlen = 1; | ||||||
|  | 
 | ||||||
|  |   HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); | ||||||
|  |   HANDLE_EINTR(close(signal_fd)); | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool | ||||||
|  | CrashGenerationServer::ControlEvent(short revents) | ||||||
|  | { | ||||||
|  |   if (POLLHUP & revents) | ||||||
|  |     return false; | ||||||
|  |   assert(POLLIN & revents); | ||||||
|  | 
 | ||||||
|  |   char command; | ||||||
|  |   if (read(control_pipe_in_, &command, 1)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   switch (command) { | ||||||
|  |   case kCommandQuit: | ||||||
|  |     return false; | ||||||
|  |   default: | ||||||
|  |     assert(0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool | ||||||
|  | CrashGenerationServer::MakeMinidumpFilename(string& outFilename) | ||||||
|  | { | ||||||
|  |   GUID guid; | ||||||
|  |   char guidString[kGUIDStringLength+1]; | ||||||
|  | 
 | ||||||
|  |   if (!(CreateGUID(&guid) | ||||||
|  |         && GUIDToString(&guid, guidString, sizeof(guidString)))) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   char path[PATH_MAX]; | ||||||
|  |   snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); | ||||||
|  | 
 | ||||||
|  |   outFilename = path; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // static
 | ||||||
|  | void* | ||||||
|  | CrashGenerationServer::ThreadMain(void *arg) | ||||||
|  | { | ||||||
|  |   reinterpret_cast<CrashGenerationServer*>(arg)->Run(); | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
| @ -0,0 +1,135 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ | ||||||
|  | #define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ | ||||||
|  | 
 | ||||||
|  | #include <pthread.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class ClientInfo; | ||||||
|  | 
 | ||||||
|  | class CrashGenerationServer { | ||||||
|  | public: | ||||||
|  |   // WARNING: callbacks may be invoked on a different thread
 | ||||||
|  |   // than that which creates the CrashGenerationServer.  They must
 | ||||||
|  |   // be thread safe.
 | ||||||
|  |   typedef void (*OnClientDumpRequestCallback)(void* context, | ||||||
|  |                                               const ClientInfo* client_info, | ||||||
|  |                                               const string* file_path); | ||||||
|  | 
 | ||||||
|  |   typedef void (*OnClientExitingCallback)(void* context, | ||||||
|  |                                           const ClientInfo* client_info); | ||||||
|  | 
 | ||||||
|  |   // Create an instance with the given parameters.
 | ||||||
|  |   //
 | ||||||
|  |   // Parameter listen_fd: The server fd created by CreateReportChannel().
 | ||||||
|  |   // Parameter dump_callback: Callback for a client crash dump request.
 | ||||||
|  |   // Parameter dump_context: Context for client crash dump request callback.
 | ||||||
|  |   // Parameter exit_callback: Callback for client process exit.
 | ||||||
|  |   // Parameter exit_context: Context for client exit callback.
 | ||||||
|  |   // Parameter generate_dumps: Whether to automatically generate dumps.
 | ||||||
|  |   //     Client code of this class might want to generate dumps explicitly
 | ||||||
|  |   //     in the crash dump request callback. In that case, false can be
 | ||||||
|  |   //     passed for this parameter.
 | ||||||
|  |   // Parameter dump_path: Path for generating dumps; required only if true is
 | ||||||
|  |   //     passed for generateDumps parameter; NULL can be passed otherwise.
 | ||||||
|  |   CrashGenerationServer(const int listen_fd, | ||||||
|  |                         OnClientDumpRequestCallback dump_callback, | ||||||
|  |                         void* dump_context, | ||||||
|  |                         OnClientExitingCallback exit_callback, | ||||||
|  |                         void* exit_context, | ||||||
|  |                         bool generate_dumps, | ||||||
|  |                         const string* dump_path); | ||||||
|  | 
 | ||||||
|  |   ~CrashGenerationServer(); | ||||||
|  | 
 | ||||||
|  |   // Perform initialization steps needed to start listening to clients.
 | ||||||
|  |   //
 | ||||||
|  |   // Return true if initialization is successful; false otherwise.
 | ||||||
|  |   bool Start(); | ||||||
|  | 
 | ||||||
|  |   // Stop the server.
 | ||||||
|  |   void Stop(); | ||||||
|  | 
 | ||||||
|  |   // Create a "channel" that can be used by clients to report crashes
 | ||||||
|  |   // to a CrashGenerationServer.  |*server_fd| should be passed to
 | ||||||
|  |   // this class's constructor, and |*client_fd| should be passed to
 | ||||||
|  |   // the ExceptionHandler constructor in the client process.
 | ||||||
|  |   static bool CreateReportChannel(int* server_fd, int* client_fd); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |   // Run the server's event loop
 | ||||||
|  |   void Run(); | ||||||
|  | 
 | ||||||
|  |   // Invoked when an child process (client) event occurs
 | ||||||
|  |   // Returning true => "keep running", false => "exit loop"
 | ||||||
|  |   bool ClientEvent(short revents); | ||||||
|  | 
 | ||||||
|  |   // Invoked when the controlling thread (main) event occurs
 | ||||||
|  |   // Returning true => "keep running", false => "exit loop"
 | ||||||
|  |   bool ControlEvent(short revents); | ||||||
|  | 
 | ||||||
|  |   // Return a unique filename at which a minidump can be written
 | ||||||
|  |   bool MakeMinidumpFilename(string& outFilename); | ||||||
|  | 
 | ||||||
|  |   // Trampoline to |Run()|
 | ||||||
|  |   static void* ThreadMain(void* arg); | ||||||
|  | 
 | ||||||
|  |   int server_fd_; | ||||||
|  | 
 | ||||||
|  |   OnClientDumpRequestCallback dump_callback_; | ||||||
|  |   void* dump_context_; | ||||||
|  | 
 | ||||||
|  |   OnClientExitingCallback exit_callback_; | ||||||
|  |   void* exit_context_; | ||||||
|  | 
 | ||||||
|  |   bool generate_dumps_; | ||||||
|  | 
 | ||||||
|  |   string dump_dir_; | ||||||
|  | 
 | ||||||
|  |   bool started_; | ||||||
|  | 
 | ||||||
|  |   pthread_t thread_; | ||||||
|  |   int control_pipe_in_; | ||||||
|  |   int control_pipe_out_; | ||||||
|  | 
 | ||||||
|  |   // disable these
 | ||||||
|  |   CrashGenerationServer(const CrashGenerationServer&); | ||||||
|  |   CrashGenerationServer& operator=(const CrashGenerationServer&); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_
 | ||||||
							
								
								
									
										3
									
								
								google-breakpad/src/client/linux/data/linux-gate-amd.sym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								google-breakpad/src/client/linux/data/linux-gate-amd.sym
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so | ||||||
|  | PUBLIC 400 0 __kernel_vsyscall | ||||||
|  | STACK WIN 4 400 100 1 1 0 0 0 0 0 1 | ||||||
| @ -0,0 +1,3 @@ | |||||||
|  | MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so | ||||||
|  | PUBLIC 400 0 __kernel_vsyscall | ||||||
|  | STACK WIN 4 400 200 3 3 0 0 0 0 0 1 | ||||||
							
								
								
									
										688
									
								
								google-breakpad/src/client/linux/handler/exception_handler.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										688
									
								
								google-breakpad/src/client/linux/handler/exception_handler.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,688 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // The ExceptionHandler object installs signal handlers for a number of
 | ||||||
|  | // signals. We rely on the signal handler running on the thread which crashed
 | ||||||
|  | // in order to identify it. This is true of the synchronous signals (SEGV etc),
 | ||||||
|  | // but not true of ABRT. Thus, if you send ABRT to yourself in a program which
 | ||||||
|  | // uses ExceptionHandler, you need to use tgkill to direct it to the current
 | ||||||
|  | // thread.
 | ||||||
|  | //
 | ||||||
|  | // The signal flow looks like this:
 | ||||||
|  | //
 | ||||||
|  | //   SignalHandler (uses a global stack of ExceptionHandler objects to find
 | ||||||
|  | //        |         one to handle the signal. If the first rejects it, try
 | ||||||
|  | //        |         the second etc...)
 | ||||||
|  | //        V
 | ||||||
|  | //   HandleSignal ----------------------------| (clones a new process which
 | ||||||
|  | //        |                                   |  shares an address space with
 | ||||||
|  | //   (wait for cloned                         |  the crashed process. This
 | ||||||
|  | //     process)                               |  allows us to ptrace the crashed
 | ||||||
|  | //        |                                   |  process)
 | ||||||
|  | //        V                                   V
 | ||||||
|  | //   (set signal handler to             ThreadEntry (static function to bounce
 | ||||||
|  | //    SIG_DFL and rethrow,                    |      back into the object)
 | ||||||
|  | //    killing the crashed                     |
 | ||||||
|  | //    process)                                V
 | ||||||
|  | //                                          DoDump  (writes minidump)
 | ||||||
|  | //                                            |
 | ||||||
|  | //                                            V
 | ||||||
|  | //                                         sys_exit
 | ||||||
|  | //
 | ||||||
|  | 
 | ||||||
|  | // This code is a little fragmented. Different functions of the ExceptionHandler
 | ||||||
|  | // class run in a number of different contexts. Some of them run in a normal
 | ||||||
|  | // context and are easy to code, others run in a compromised context and the
 | ||||||
|  | // restrictions at the top of minidump_writer.cc apply: no libc and use the
 | ||||||
|  | // alternative malloc. Each function should have comment above it detailing the
 | ||||||
|  | // context which it runs in.
 | ||||||
|  | 
 | ||||||
|  | #include "client/linux/handler/exception_handler.h" | ||||||
|  | 
 | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <linux/limits.h> | ||||||
|  | #include <sched.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <sys/prctl.h> | ||||||
|  | #include <sys/syscall.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <sys/signal.h> | ||||||
|  | #include <sys/ucontext.h> | ||||||
|  | #include <sys/user.h> | ||||||
|  | #include <ucontext.h> | ||||||
|  | 
 | ||||||
|  | #include <algorithm> | ||||||
|  | #include <utility> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "common/memory.h" | ||||||
|  | #include "client/linux/log/log.h" | ||||||
|  | #include "client/linux/minidump_writer/linux_dumper.h" | ||||||
|  | #include "client/linux/minidump_writer/minidump_writer.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | #if defined(__ANDROID__) | ||||||
|  | #include "linux/sched.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #ifndef PR_SET_PTRACER | ||||||
|  | #define PR_SET_PTRACER 0x59616d61 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // A wrapper for the tgkill syscall: send a signal to a specific thread.
 | ||||||
|  | static int tgkill(pid_t tgid, pid_t tid, int sig) { | ||||||
|  |   return syscall(__NR_tgkill, tgid, tid, sig); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | // The list of signals which we consider to be crashes. The default action for
 | ||||||
|  | // all these signals must be Core (see man 7 signal) because we rethrow the
 | ||||||
|  | // signal after handling it and expect that it'll be fatal.
 | ||||||
|  | const int kExceptionSignals[] = { | ||||||
|  |   SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS | ||||||
|  | }; | ||||||
|  | const int kNumHandledSignals = | ||||||
|  |     sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); | ||||||
|  | struct sigaction old_handlers[kNumHandledSignals]; | ||||||
|  | bool handlers_installed = false; | ||||||
|  | 
 | ||||||
|  | // InstallAlternateStackLocked will store the newly installed stack in new_stack
 | ||||||
|  | // and (if it exists) the previously installed stack in old_stack.
 | ||||||
|  | stack_t old_stack; | ||||||
|  | stack_t new_stack; | ||||||
|  | bool stack_installed = false; | ||||||
|  | 
 | ||||||
|  | // Create an alternative stack to run the signal handlers on. This is done since
 | ||||||
|  | // the signal might have been caused by a stack overflow.
 | ||||||
|  | // Runs before crashing: normal context.
 | ||||||
|  | void InstallAlternateStackLocked() { | ||||||
|  |   if (stack_installed) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   memset(&old_stack, 0, sizeof(old_stack)); | ||||||
|  |   memset(&new_stack, 0, sizeof(new_stack)); | ||||||
|  | 
 | ||||||
|  |   // SIGSTKSZ may be too small to prevent the signal handlers from overrunning
 | ||||||
|  |   // the alternative stack. Ensure that the size of the alternative stack is
 | ||||||
|  |   // large enough.
 | ||||||
|  |   static const unsigned kSigStackSize = std::max(8192, SIGSTKSZ); | ||||||
|  | 
 | ||||||
|  |   // Only set an alternative stack if there isn't already one, or if the current
 | ||||||
|  |   // one is too small.
 | ||||||
|  |   if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || | ||||||
|  |       old_stack.ss_size < kSigStackSize) { | ||||||
|  |     new_stack.ss_sp = malloc(kSigStackSize); | ||||||
|  |     new_stack.ss_size = kSigStackSize; | ||||||
|  | 
 | ||||||
|  |     if (sys_sigaltstack(&new_stack, NULL) == -1) { | ||||||
|  |       free(new_stack.ss_sp); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     stack_installed = true; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Runs before crashing: normal context.
 | ||||||
|  | void RestoreAlternateStackLocked() { | ||||||
|  |   if (!stack_installed) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   stack_t current_stack; | ||||||
|  |   if (sys_sigaltstack(NULL, ¤t_stack) == -1) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   // Only restore the old_stack if the current alternative stack is the one
 | ||||||
|  |   // installed by the call to InstallAlternateStackLocked.
 | ||||||
|  |   if (current_stack.ss_sp == new_stack.ss_sp) { | ||||||
|  |     if (old_stack.ss_sp) { | ||||||
|  |       if (sys_sigaltstack(&old_stack, NULL) == -1) | ||||||
|  |         return; | ||||||
|  |     } else { | ||||||
|  |       stack_t disable_stack; | ||||||
|  |       disable_stack.ss_flags = SS_DISABLE; | ||||||
|  |       if (sys_sigaltstack(&disable_stack, NULL) == -1) | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   free(new_stack.ss_sp); | ||||||
|  |   stack_installed = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | 
 | ||||||
|  | // We can stack multiple exception handlers. In that case, this is the global
 | ||||||
|  | // which holds the stack.
 | ||||||
|  | std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL; | ||||||
|  | pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = | ||||||
|  |     PTHREAD_MUTEX_INITIALIZER; | ||||||
|  | 
 | ||||||
|  | // Runs before crashing: normal context.
 | ||||||
|  | ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, | ||||||
|  |                                    FilterCallback filter, | ||||||
|  |                                    MinidumpCallback callback, | ||||||
|  |                                    void* callback_context, | ||||||
|  |                                    bool install_handler, | ||||||
|  |                                    const int server_fd) | ||||||
|  |     : filter_(filter), | ||||||
|  |       callback_(callback), | ||||||
|  |       callback_context_(callback_context), | ||||||
|  |       minidump_descriptor_(descriptor), | ||||||
|  |       crash_handler_(NULL) { | ||||||
|  |   if (server_fd >= 0) | ||||||
|  |     crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); | ||||||
|  | 
 | ||||||
|  |   if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) | ||||||
|  |     minidump_descriptor_.UpdatePath(); | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_lock(&handler_stack_mutex_); | ||||||
|  |   if (!handler_stack_) | ||||||
|  |     handler_stack_ = new std::vector<ExceptionHandler*>; | ||||||
|  |   if (install_handler) { | ||||||
|  |     InstallAlternateStackLocked(); | ||||||
|  |     InstallHandlersLocked(); | ||||||
|  |   } | ||||||
|  |   handler_stack_->push_back(this); | ||||||
|  |   pthread_mutex_unlock(&handler_stack_mutex_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Runs before crashing: normal context.
 | ||||||
|  | ExceptionHandler::~ExceptionHandler() { | ||||||
|  |   pthread_mutex_lock(&handler_stack_mutex_); | ||||||
|  |   std::vector<ExceptionHandler*>::iterator handler = | ||||||
|  |       std::find(handler_stack_->begin(), handler_stack_->end(), this); | ||||||
|  |   handler_stack_->erase(handler); | ||||||
|  |   if (handler_stack_->empty()) { | ||||||
|  |     RestoreAlternateStackLocked(); | ||||||
|  |     RestoreHandlersLocked(); | ||||||
|  |   } | ||||||
|  |   pthread_mutex_unlock(&handler_stack_mutex_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Runs before crashing: normal context.
 | ||||||
|  | // static
 | ||||||
|  | bool ExceptionHandler::InstallHandlersLocked() { | ||||||
|  |   if (handlers_installed) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // Fail if unable to store all the old handlers.
 | ||||||
|  |   for (int i = 0; i < kNumHandledSignals; ++i) { | ||||||
|  |     if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1) | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   struct sigaction sa; | ||||||
|  |   memset(&sa, 0, sizeof(sa)); | ||||||
|  |   sigemptyset(&sa.sa_mask); | ||||||
|  | 
 | ||||||
|  |   // Mask all exception signals when we're handling one of them.
 | ||||||
|  |   for (int i = 0; i < kNumHandledSignals; ++i) | ||||||
|  |     sigaddset(&sa.sa_mask, kExceptionSignals[i]); | ||||||
|  | 
 | ||||||
|  |   sa.sa_sigaction = SignalHandler; | ||||||
|  |   sa.sa_flags = SA_ONSTACK | SA_SIGINFO; | ||||||
|  | 
 | ||||||
|  |   for (int i = 0; i < kNumHandledSignals; ++i) { | ||||||
|  |     if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) { | ||||||
|  |       // At this point it is impractical to back out changes, and so failure to
 | ||||||
|  |       // install a signal is intentionally ignored.
 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   handlers_installed = true; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This function runs in a compromised context: see the top of the file.
 | ||||||
|  | // Runs on the crashing thread.
 | ||||||
|  | // static
 | ||||||
|  | void ExceptionHandler::RestoreHandlersLocked() { | ||||||
|  |   if (!handlers_installed) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   for (int i = 0; i < kNumHandledSignals; ++i) { | ||||||
|  |     if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) { | ||||||
|  |       signal(kExceptionSignals[i], SIG_DFL); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   handlers_installed = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // void ExceptionHandler::set_crash_handler(HandlerCallback callback) {
 | ||||||
|  | //   crash_handler_ = callback;
 | ||||||
|  | // }
 | ||||||
|  | 
 | ||||||
|  | // This function runs in a compromised context: see the top of the file.
 | ||||||
|  | // Runs on the crashing thread.
 | ||||||
|  | // static
 | ||||||
|  | void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { | ||||||
|  |   // All the exception signals are blocked at this point.
 | ||||||
|  |   pthread_mutex_lock(&handler_stack_mutex_); | ||||||
|  | 
 | ||||||
|  |   // Sometimes, Breakpad runs inside a process where some other buggy code
 | ||||||
|  |   // saves and restores signal handlers temporarily with 'signal'
 | ||||||
|  |   // instead of 'sigaction'. This loses the SA_SIGINFO flag associated
 | ||||||
|  |   // with this function. As a consequence, the values of 'info' and 'uc'
 | ||||||
|  |   // become totally bogus, generally inducing a crash.
 | ||||||
|  |   //
 | ||||||
|  |   // The following code tries to detect this case. When it does, it
 | ||||||
|  |   // resets the signal handlers with sigaction + SA_SIGINFO and returns.
 | ||||||
|  |   // This forces the signal to be thrown again, but this time the kernel
 | ||||||
|  |   // will call the function with the right arguments.
 | ||||||
|  |   struct sigaction cur_handler; | ||||||
|  |   if (sigaction(sig, NULL, &cur_handler) == 0 && | ||||||
|  |       (cur_handler.sa_flags & SA_SIGINFO) == 0) { | ||||||
|  |     // Reset signal handler with the right flags.
 | ||||||
|  |     sigemptyset(&cur_handler.sa_mask); | ||||||
|  |     sigaddset(&cur_handler.sa_mask, sig); | ||||||
|  | 
 | ||||||
|  |     cur_handler.sa_sigaction = SignalHandler; | ||||||
|  |     cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO; | ||||||
|  | 
 | ||||||
|  |     if (sigaction(sig, &cur_handler, NULL) == -1) { | ||||||
|  |       // When resetting the handler fails, try to reset the
 | ||||||
|  |       // default one to avoid an infinite loop here.
 | ||||||
|  |       signal(sig, SIG_DFL); | ||||||
|  |     } | ||||||
|  |     pthread_mutex_unlock(&handler_stack_mutex_); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool handled = false; | ||||||
|  |   for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) { | ||||||
|  |     handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Upon returning from this signal handler, sig will become unmasked and then
 | ||||||
|  |   // it will be retriggered. If one of the ExceptionHandlers handled it
 | ||||||
|  |   // successfully, restore the default handler. Otherwise, restore the
 | ||||||
|  |   // previously installed handler. Then, when the signal is retriggered, it will
 | ||||||
|  |   // be delivered to the appropriate handler.
 | ||||||
|  |   if (handled) { | ||||||
|  |     signal(sig, SIG_DFL); | ||||||
|  |   } else { | ||||||
|  |     RestoreHandlersLocked(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   pthread_mutex_unlock(&handler_stack_mutex_); | ||||||
|  | 
 | ||||||
|  |   if (info->si_pid) { | ||||||
|  |     // This signal was triggered by somebody sending us the signal with kill().
 | ||||||
|  |     // In order to retrigger it, we have to queue a new signal by calling
 | ||||||
|  |     // kill() ourselves.
 | ||||||
|  |     if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { | ||||||
|  |       // If we failed to kill ourselves (e.g. because a sandbox disallows us
 | ||||||
|  |       // to do so), we instead resort to terminating our process. This will
 | ||||||
|  |       // result in an incorrect exit code.
 | ||||||
|  |       _exit(1); | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV).
 | ||||||
|  |     // No need to reissue the signal. It will automatically trigger again,
 | ||||||
|  |     // when we return from the signal handler.
 | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct ThreadArgument { | ||||||
|  |   pid_t pid;  // the crashing process
 | ||||||
|  |   const MinidumpDescriptor* minidump_descriptor; | ||||||
|  |   ExceptionHandler* handler; | ||||||
|  |   const void* context;  // a CrashContext structure
 | ||||||
|  |   size_t context_size; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // This is the entry function for the cloned process. We are in a compromised
 | ||||||
|  | // context here: see the top of the file.
 | ||||||
|  | // static
 | ||||||
|  | int ExceptionHandler::ThreadEntry(void *arg) { | ||||||
|  |   const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg); | ||||||
|  | 
 | ||||||
|  |   // Block here until the crashing process unblocks us when
 | ||||||
|  |   // we're allowed to use ptrace
 | ||||||
|  |   thread_arg->handler->WaitForContinueSignal(); | ||||||
|  | 
 | ||||||
|  |   return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, | ||||||
|  |                                      thread_arg->context_size) == false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This function runs in a compromised context: see the top of the file.
 | ||||||
|  | // Runs on the crashing thread.
 | ||||||
|  | bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { | ||||||
|  |   if (filter_ && !filter_(callback_context_)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // Allow ourselves to be dumped if the signal is trusted.
 | ||||||
|  |   bool signal_trusted = info->si_code > 0; | ||||||
|  |   bool signal_pid_trusted = info->si_code == SI_USER || | ||||||
|  |       info->si_code == SI_TKILL; | ||||||
|  |   if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { | ||||||
|  |     sys_prctl(PR_SET_DUMPABLE, 1); | ||||||
|  |   } | ||||||
|  |   CrashContext context; | ||||||
|  |   memcpy(&context.siginfo, info, sizeof(siginfo_t)); | ||||||
|  |   memcpy(&context.context, uc, sizeof(struct ucontext)); | ||||||
|  | #if !defined(__ARM_EABI__) | ||||||
|  |   // FP state is not part of user ABI on ARM Linux.
 | ||||||
|  |   struct ucontext *uc_ptr = (struct ucontext*)uc; | ||||||
|  |   if (uc_ptr->uc_mcontext.fpregs) { | ||||||
|  |     memcpy(&context.float_state, | ||||||
|  |            uc_ptr->uc_mcontext.fpregs, | ||||||
|  |            sizeof(context.float_state)); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   context.tid = syscall(__NR_gettid); | ||||||
|  |   if (crash_handler_ != NULL) { | ||||||
|  |     if (crash_handler_(&context, sizeof(context), callback_context_)) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return GenerateDump(&context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This is a public interface to HandleSignal that allows the client to
 | ||||||
|  | // generate a crash dump. This function may run in a compromised context.
 | ||||||
|  | bool ExceptionHandler::SimulateSignalDelivery(int sig) { | ||||||
|  |   siginfo_t siginfo = {}; | ||||||
|  |   // Mimic a trusted signal to allow tracing the process (see
 | ||||||
|  |   // ExceptionHandler::HandleSignal().
 | ||||||
|  |   siginfo.si_code = SI_USER; | ||||||
|  |   siginfo.si_pid = getpid(); | ||||||
|  |   struct ucontext context; | ||||||
|  |   getcontext(&context); | ||||||
|  |   return HandleSignal(sig, &siginfo, &context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This function may run in a compromised context: see the top of the file.
 | ||||||
|  | bool ExceptionHandler::GenerateDump(CrashContext *context) { | ||||||
|  |   if (IsOutOfProcess()) | ||||||
|  |     return crash_generation_client_->RequestDump(context, sizeof(*context)); | ||||||
|  | 
 | ||||||
|  |   static const unsigned kChildStackSize = 8000; | ||||||
|  |   PageAllocator allocator; | ||||||
|  |   uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); | ||||||
|  |   if (!stack) | ||||||
|  |     return false; | ||||||
|  |   // clone() needs the top-most address. (scrub just to be safe)
 | ||||||
|  |   stack += kChildStackSize; | ||||||
|  |   my_memset(stack - 16, 0, 16); | ||||||
|  | 
 | ||||||
|  |   ThreadArgument thread_arg; | ||||||
|  |   thread_arg.handler = this; | ||||||
|  |   thread_arg.minidump_descriptor = &minidump_descriptor_; | ||||||
|  |   thread_arg.pid = getpid(); | ||||||
|  |   thread_arg.context = context; | ||||||
|  |   thread_arg.context_size = sizeof(*context); | ||||||
|  | 
 | ||||||
|  |   // We need to explicitly enable ptrace of parent processes on some
 | ||||||
|  |   // kernels, but we need to know the PID of the cloned process before we
 | ||||||
|  |   // can do this. Create a pipe here which we can use to block the
 | ||||||
|  |   // cloned process after creating it, until we have explicitly enabled ptrace
 | ||||||
|  |   if(sys_pipe(fdes) == -1) { | ||||||
|  |     // Creating the pipe failed. We'll log an error but carry on anyway,
 | ||||||
|  |     // as we'll probably still get a useful crash report. All that will happen
 | ||||||
|  |     // is the write() and read() calls will fail with EBADF
 | ||||||
|  |     static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \
 | ||||||
|  |                                        sys_pipe failed:"; | ||||||
|  |     logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); | ||||||
|  |     logger::write(strerror(errno), strlen(strerror(errno))); | ||||||
|  |     logger::write("\n", 1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const pid_t child = sys_clone( | ||||||
|  |       ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, | ||||||
|  |       &thread_arg, NULL, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |   int r, status; | ||||||
|  |   // Allow the child to ptrace us
 | ||||||
|  |   sys_prctl(PR_SET_PTRACER, child); | ||||||
|  |   SendContinueSignalToChild(); | ||||||
|  |   do { | ||||||
|  |     r = sys_waitpid(child, &status, __WALL); | ||||||
|  |   } while (r == -1 && errno == EINTR); | ||||||
|  | 
 | ||||||
|  |   sys_close(fdes[0]); | ||||||
|  |   sys_close(fdes[1]); | ||||||
|  | 
 | ||||||
|  |   if (r == -1) { | ||||||
|  |     static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; | ||||||
|  |     logger::write(msg, sizeof(msg) - 1); | ||||||
|  |     logger::write(strerror(errno), strlen(strerror(errno))); | ||||||
|  |     logger::write("\n", 1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; | ||||||
|  |   if (callback_) | ||||||
|  |     success = callback_(minidump_descriptor_, callback_context_, success); | ||||||
|  |   return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This function runs in a compromised context: see the top of the file.
 | ||||||
|  | void ExceptionHandler::SendContinueSignalToChild() { | ||||||
|  |   static const char okToContinueMessage = 'a'; | ||||||
|  |   int r; | ||||||
|  |   r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); | ||||||
|  |   if(r == -1) { | ||||||
|  |     static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \
 | ||||||
|  |                                sys_write failed:"; | ||||||
|  |     logger::write(msg, sizeof(msg) - 1); | ||||||
|  |     logger::write(strerror(errno), strlen(strerror(errno))); | ||||||
|  |     logger::write("\n", 1); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This function runs in a compromised context: see the top of the file.
 | ||||||
|  | // Runs on the cloned process.
 | ||||||
|  | void ExceptionHandler::WaitForContinueSignal() { | ||||||
|  |   int r; | ||||||
|  |   char receivedMessage; | ||||||
|  |   r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); | ||||||
|  |   if(r == -1) { | ||||||
|  |     static const char msg[] = "ExceptionHandler::WaitForContinueSignal \
 | ||||||
|  |                                sys_read failed:"; | ||||||
|  |     logger::write(msg, sizeof(msg) - 1); | ||||||
|  |     logger::write(strerror(errno), strlen(strerror(errno))); | ||||||
|  |     logger::write("\n", 1); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // This function runs in a compromised context: see the top of the file.
 | ||||||
|  | // Runs on the cloned process.
 | ||||||
|  | bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, | ||||||
|  |                               size_t context_size) { | ||||||
|  |   if (minidump_descriptor_.IsFD()) { | ||||||
|  |     return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), | ||||||
|  |                                           minidump_descriptor_.size_limit(), | ||||||
|  |                                           crashing_process, | ||||||
|  |                                           context, | ||||||
|  |                                           context_size, | ||||||
|  |                                           mapping_list_, | ||||||
|  |                                           app_memory_list_); | ||||||
|  |   } | ||||||
|  |   return google_breakpad::WriteMinidump(minidump_descriptor_.path(), | ||||||
|  |                                         minidump_descriptor_.size_limit(), | ||||||
|  |                                         crashing_process, | ||||||
|  |                                         context, | ||||||
|  |                                         context_size, | ||||||
|  |                                         mapping_list_, | ||||||
|  |                                         app_memory_list_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // static
 | ||||||
|  | bool ExceptionHandler::WriteMinidump(const string& dump_path, | ||||||
|  |                                      MinidumpCallback callback, | ||||||
|  |                                      void* callback_context) { | ||||||
|  |   MinidumpDescriptor descriptor(dump_path); | ||||||
|  |   ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); | ||||||
|  |   return eh.WriteMinidump(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // In order to making using EBP to calculate the desired value for ESP
 | ||||||
|  | // a valid operation, ensure that this function is compiled with a
 | ||||||
|  | // frame pointer using the following attribute. This attribute
 | ||||||
|  | // is supported on GCC but not on clang.
 | ||||||
|  | #if defined(__i386__) && defined(__GNUC__) && !defined(__clang__) | ||||||
|  | __attribute__((optimize("no-omit-frame-pointer"))) | ||||||
|  | #endif | ||||||
|  | bool ExceptionHandler::WriteMinidump() { | ||||||
|  |   if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) { | ||||||
|  |     // Update the path of the minidump so that this can be called multiple times
 | ||||||
|  |     // and new files are created for each minidump.  This is done before the
 | ||||||
|  |     // generation happens, as clients may want to access the MinidumpDescriptor
 | ||||||
|  |     // after this call to find the exact path to the minidump file.
 | ||||||
|  |     minidump_descriptor_.UpdatePath(); | ||||||
|  |   } else if (minidump_descriptor_.IsFD()) { | ||||||
|  |     // Reposition the FD to its beginning and resize it to get rid of the
 | ||||||
|  |     // previous minidump info.
 | ||||||
|  |     lseek(minidump_descriptor_.fd(), 0, SEEK_SET); | ||||||
|  |     static_cast<void>(ftruncate(minidump_descriptor_.fd(), 0)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Allow this process to be dumped.
 | ||||||
|  |   sys_prctl(PR_SET_DUMPABLE, 1); | ||||||
|  | 
 | ||||||
|  |   CrashContext context; | ||||||
|  |   int getcontext_result = getcontext(&context.context); | ||||||
|  |   if (getcontext_result) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  | #if defined(__i386__) | ||||||
|  |   // In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved
 | ||||||
|  |   // from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer
 | ||||||
|  |   // and it only makes sense when running in kernel mode with a different stack
 | ||||||
|  |   // pointer. When WriteMiniDump is called during normal processing REG_UESP is
 | ||||||
|  |   // zero which leads to bad minidump files.
 | ||||||
|  |   if (!context.context.uc_mcontext.gregs[REG_UESP]) { | ||||||
|  |     // If REG_UESP is set to REG_ESP then that includes the stack space for the
 | ||||||
|  |     // CrashContext object in this function, which is about 128 KB. Since the
 | ||||||
|  |     // Linux dumper only records 32 KB of stack this would mean that nothing
 | ||||||
|  |     // useful would be recorded. A better option is to set REG_UESP to REG_EBP,
 | ||||||
|  |     // perhaps with a small negative offset in case there is any code that
 | ||||||
|  |     // objects to them being equal.
 | ||||||
|  |     context.context.uc_mcontext.gregs[REG_UESP] = | ||||||
|  |       context.context.uc_mcontext.gregs[REG_EBP] - 16; | ||||||
|  |     // The stack saving is based off of REG_ESP so it must be set to match the
 | ||||||
|  |     // new REG_UESP.
 | ||||||
|  |     context.context.uc_mcontext.gregs[REG_ESP] = | ||||||
|  |       context.context.uc_mcontext.gregs[REG_UESP]; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if !defined(__ARM_EABI__) | ||||||
|  |   // FPU state is not part of ARM EABI ucontext_t.
 | ||||||
|  |   memcpy(&context.float_state, context.context.uc_mcontext.fpregs, | ||||||
|  |          sizeof(context.float_state)); | ||||||
|  | #endif | ||||||
|  |   context.tid = sys_gettid(); | ||||||
|  | 
 | ||||||
|  |   // Add an exception stream to the minidump for better reporting.
 | ||||||
|  |   memset(&context.siginfo, 0, sizeof(context.siginfo)); | ||||||
|  |   context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; | ||||||
|  | #if defined(__i386__) | ||||||
|  |   context.siginfo.si_addr = | ||||||
|  |       reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]); | ||||||
|  | #elif defined(__x86_64__) | ||||||
|  |   context.siginfo.si_addr = | ||||||
|  |       reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]); | ||||||
|  | #elif defined(__arm__) | ||||||
|  |   context.siginfo.si_addr = | ||||||
|  |       reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc); | ||||||
|  | #else | ||||||
|  | #error "This code has not been ported to your platform yet." | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   return GenerateDump(&context); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ExceptionHandler::AddMappingInfo(const string& name, | ||||||
|  |                                       const uint8_t identifier[sizeof(MDGUID)], | ||||||
|  |                                       uintptr_t start_address, | ||||||
|  |                                       size_t mapping_size, | ||||||
|  |                                       size_t file_offset) { | ||||||
|  |   MappingInfo info; | ||||||
|  |   info.start_addr = start_address; | ||||||
|  |   info.size = mapping_size; | ||||||
|  |   info.offset = file_offset; | ||||||
|  |   strncpy(info.name, name.c_str(), sizeof(info.name) - 1); | ||||||
|  |   info.name[sizeof(info.name) - 1] = '\0'; | ||||||
|  | 
 | ||||||
|  |   MappingEntry mapping; | ||||||
|  |   mapping.first = info; | ||||||
|  |   memcpy(mapping.second, identifier, sizeof(MDGUID)); | ||||||
|  |   mapping_list_.push_back(mapping); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { | ||||||
|  |   AppMemoryList::iterator iter = | ||||||
|  |     std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); | ||||||
|  |   if (iter != app_memory_list_.end()) { | ||||||
|  |     // Don't allow registering the same pointer twice.
 | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   AppMemory app_memory; | ||||||
|  |   app_memory.ptr = ptr; | ||||||
|  |   app_memory.length = length; | ||||||
|  |   app_memory_list_.push_back(app_memory); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ExceptionHandler::UnregisterAppMemory(void* ptr) { | ||||||
|  |   AppMemoryList::iterator iter = | ||||||
|  |     std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); | ||||||
|  |   if (iter != app_memory_list_.end()) { | ||||||
|  |     app_memory_list_.erase(iter); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // static
 | ||||||
|  | bool ExceptionHandler::WriteMinidumpForChild(pid_t child, | ||||||
|  |                                              pid_t child_blamed_thread, | ||||||
|  |                                              const string& dump_path, | ||||||
|  |                                              MinidumpCallback callback, | ||||||
|  |                                              void* callback_context) { | ||||||
|  |   // This function is not run in a compromised context.
 | ||||||
|  |   MinidumpDescriptor descriptor(dump_path); | ||||||
|  |   descriptor.UpdatePath(); | ||||||
|  |   if (!google_breakpad::WriteMinidump(descriptor.path(), | ||||||
|  |                                       child, | ||||||
|  |                                       child_blamed_thread)) | ||||||
|  |       return false; | ||||||
|  | 
 | ||||||
|  |   return callback ? callback(descriptor, callback_context, true) : true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
							
								
								
									
										273
									
								
								google-breakpad/src/client/linux/handler/exception_handler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								google-breakpad/src/client/linux/handler/exception_handler.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,273 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ | ||||||
|  | #define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <sys/ucontext.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/crash_generation/crash_generation_client.h" | ||||||
|  | #include "client/linux/handler/minidump_descriptor.h" | ||||||
|  | #include "client/linux/minidump_writer/minidump_writer.h" | ||||||
|  | #include "common/scoped_ptr.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | #include "google_breakpad/common/minidump_format.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // ExceptionHandler
 | ||||||
|  | //
 | ||||||
|  | // ExceptionHandler can write a minidump file when an exception occurs,
 | ||||||
|  | // or when WriteMinidump() is called explicitly by your program.
 | ||||||
|  | //
 | ||||||
|  | // To have the exception handler write minidumps when an uncaught exception
 | ||||||
|  | // (crash) occurs, you should create an instance early in the execution
 | ||||||
|  | // of your program, and keep it around for the entire time you want to
 | ||||||
|  | // have crash handling active (typically, until shutdown).
 | ||||||
|  | // (NOTE): There should be only be one this kind of exception handler
 | ||||||
|  | // object per process.
 | ||||||
|  | //
 | ||||||
|  | // If you want to write minidumps without installing the exception handler,
 | ||||||
|  | // you can create an ExceptionHandler with install_handler set to false,
 | ||||||
|  | // then call WriteMinidump.  You can also use this technique if you want to
 | ||||||
|  | // use different minidump callbacks for different call sites.
 | ||||||
|  | //
 | ||||||
|  | // In either case, a callback function is called when a minidump is written,
 | ||||||
|  | // which receives the full path or file descriptor of the minidump.  The
 | ||||||
|  | // caller can collect and write additional application state to that minidump,
 | ||||||
|  | // and launch an external crash-reporting application.
 | ||||||
|  | //
 | ||||||
|  | // Caller should try to make the callbacks as crash-friendly as possible,
 | ||||||
|  | // it should avoid use heap memory allocation as much as possible.
 | ||||||
|  | 
 | ||||||
|  | class ExceptionHandler { | ||||||
|  |  public: | ||||||
|  |   // A callback function to run before Breakpad performs any substantial
 | ||||||
|  |   // processing of an exception.  A FilterCallback is called before writing
 | ||||||
|  |   // a minidump.  |context| is the parameter supplied by the user as
 | ||||||
|  |   // callback_context when the handler was created.
 | ||||||
|  |   //
 | ||||||
|  |   // If a FilterCallback returns true, Breakpad will continue processing,
 | ||||||
|  |   // attempting to write a minidump.  If a FilterCallback returns false,
 | ||||||
|  |   // Breakpad  will immediately report the exception as unhandled without
 | ||||||
|  |   // writing a minidump, allowing another handler the opportunity to handle it.
 | ||||||
|  |   typedef bool (*FilterCallback)(void *context); | ||||||
|  | 
 | ||||||
|  |   // A callback function to run after the minidump has been written.
 | ||||||
|  |   // |descriptor| contains the file descriptor or file path containing the
 | ||||||
|  |   // minidump. |context| is the parameter supplied by the user as
 | ||||||
|  |   // callback_context when the handler was created.  |succeeded| indicates
 | ||||||
|  |   // whether a minidump file was successfully written.
 | ||||||
|  |   //
 | ||||||
|  |   // If an exception occurred and the callback returns true, Breakpad will
 | ||||||
|  |   // treat the exception as fully-handled, suppressing any other handlers from
 | ||||||
|  |   // being notified of the exception.  If the callback returns false, Breakpad
 | ||||||
|  |   // will treat the exception as unhandled, and allow another handler to handle
 | ||||||
|  |   // it. If there are no other handlers, Breakpad will report the exception to
 | ||||||
|  |   // the system as unhandled, allowing a debugger or native crash dialog the
 | ||||||
|  |   // opportunity to handle the exception.  Most callback implementations
 | ||||||
|  |   // should normally return the value of |succeeded|, or when they wish to
 | ||||||
|  |   // not report an exception of handled, false.  Callbacks will rarely want to
 | ||||||
|  |   // return true directly (unless |succeeded| is true).
 | ||||||
|  |   typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor, | ||||||
|  |                                    void* context, | ||||||
|  |                                    bool succeeded); | ||||||
|  | 
 | ||||||
|  |   // In certain cases, a user may wish to handle the generation of the minidump
 | ||||||
|  |   // themselves. In this case, they can install a handler callback which is
 | ||||||
|  |   // called when a crash has occurred. If this function returns true, no other
 | ||||||
|  |   // processing of occurs and the process will shortly be crashed. If this
 | ||||||
|  |   // returns false, the normal processing continues.
 | ||||||
|  |   typedef bool (*HandlerCallback)(const void* crash_context, | ||||||
|  |                                   size_t crash_context_size, | ||||||
|  |                                   void* context); | ||||||
|  | 
 | ||||||
|  |   // Creates a new ExceptionHandler instance to handle writing minidumps.
 | ||||||
|  |   // Before writing a minidump, the optional |filter| callback will be called.
 | ||||||
|  |   // Its return value determines whether or not Breakpad should write a
 | ||||||
|  |   // minidump.  The minidump content will be written to the file path or file
 | ||||||
|  |   // descriptor from |descriptor|, and the optional |callback| is called after
 | ||||||
|  |   // writing the dump file, as described above.
 | ||||||
|  |   // If install_handler is true, then a minidump will be written whenever
 | ||||||
|  |   // an unhandled exception occurs.  If it is false, minidumps will only
 | ||||||
|  |   // be written when WriteMinidump is called.
 | ||||||
|  |   // If |server_fd| is valid, the minidump is generated out-of-process.  If it
 | ||||||
|  |   // is -1, in-process generation will always be used.
 | ||||||
|  |   ExceptionHandler(const MinidumpDescriptor& descriptor, | ||||||
|  |                    FilterCallback filter, | ||||||
|  |                    MinidumpCallback callback, | ||||||
|  |                    void *callback_context, | ||||||
|  |                    bool install_handler, | ||||||
|  |                    const int server_fd); | ||||||
|  |   ~ExceptionHandler(); | ||||||
|  | 
 | ||||||
|  |   const MinidumpDescriptor& minidump_descriptor() const { | ||||||
|  |     return minidump_descriptor_; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void set_minidump_descriptor(const MinidumpDescriptor& descriptor) { | ||||||
|  |     minidump_descriptor_ = descriptor; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void set_crash_handler(HandlerCallback callback) { | ||||||
|  |     crash_handler_ = callback; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Writes a minidump immediately.  This can be used to capture the execution
 | ||||||
|  |   // state independently of a crash.
 | ||||||
|  |   // Returns true on success.
 | ||||||
|  |   // If the ExceptionHandler has been created with a path, a new file is
 | ||||||
|  |   // generated for each minidump.  The file path can be retrieved in the
 | ||||||
|  |   // MinidumpDescriptor passed to the MinidumpCallback or by accessing the
 | ||||||
|  |   // MinidumpDescriptor directly from the ExceptionHandler (with
 | ||||||
|  |   // minidump_descriptor()).
 | ||||||
|  |   // If the ExceptionHandler has been created with a file descriptor, the file
 | ||||||
|  |   // descriptor is repositioned to its beginning and the previous generated
 | ||||||
|  |   // minidump is overwritten.
 | ||||||
|  |   // Note that this method is not supposed to be called from a compromised
 | ||||||
|  |   // context as it uses the heap.
 | ||||||
|  |   bool WriteMinidump(); | ||||||
|  | 
 | ||||||
|  |   // Convenience form of WriteMinidump which does not require an
 | ||||||
|  |   // ExceptionHandler instance.
 | ||||||
|  |   static bool WriteMinidump(const string& dump_path, | ||||||
|  |                             MinidumpCallback callback, | ||||||
|  |                             void* callback_context); | ||||||
|  | 
 | ||||||
|  |   // Write a minidump of |child| immediately.  This can be used to
 | ||||||
|  |   // capture the execution state of |child| independently of a crash.
 | ||||||
|  |   // Pass a meaningful |child_blamed_thread| to make that thread in
 | ||||||
|  |   // the child process the one from which a crash signature is
 | ||||||
|  |   // extracted.
 | ||||||
|  |   //
 | ||||||
|  |   // WARNING: the return of this function *must* happen before
 | ||||||
|  |   // the code that will eventually reap |child| executes.
 | ||||||
|  |   // Otherwise there's a pernicious race condition in which |child|
 | ||||||
|  |   // exits, is reaped, another process created with its pid, then that
 | ||||||
|  |   // new process dumped.
 | ||||||
|  |   static bool WriteMinidumpForChild(pid_t child, | ||||||
|  |                                     pid_t child_blamed_thread, | ||||||
|  |                                     const string& dump_path, | ||||||
|  |                                     MinidumpCallback callback, | ||||||
|  |                                     void* callback_context); | ||||||
|  | 
 | ||||||
|  |   // This structure is passed to minidump_writer.h:WriteMinidump via an opaque
 | ||||||
|  |   // blob. It shouldn't be needed in any user code.
 | ||||||
|  |   struct CrashContext { | ||||||
|  |     siginfo_t siginfo; | ||||||
|  |     pid_t tid;  // the crashing thread.
 | ||||||
|  |     struct ucontext context; | ||||||
|  | #if !defined(__ARM_EABI__) | ||||||
|  |     // #ifdef this out because FP state is not part of user ABI for Linux ARM.
 | ||||||
|  |     struct _libc_fpstate float_state; | ||||||
|  | #endif | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Returns whether out-of-process dump generation is used or not.
 | ||||||
|  |   bool IsOutOfProcess() const { | ||||||
|  |       return crash_generation_client_.get() != NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Add information about a memory mapping. This can be used if
 | ||||||
|  |   // a custom library loader is used that maps things in a way
 | ||||||
|  |   // that the linux dumper can't handle by reading the maps file.
 | ||||||
|  |   void AddMappingInfo(const string& name, | ||||||
|  |                       const uint8_t identifier[sizeof(MDGUID)], | ||||||
|  |                       uintptr_t start_address, | ||||||
|  |                       size_t mapping_size, | ||||||
|  |                       size_t file_offset); | ||||||
|  | 
 | ||||||
|  |   // Register a block of memory of length bytes starting at address ptr
 | ||||||
|  |   // to be copied to the minidump when a crash happens.
 | ||||||
|  |   void RegisterAppMemory(void* ptr, size_t length); | ||||||
|  | 
 | ||||||
|  |   // Unregister a block of memory that was registered with RegisterAppMemory.
 | ||||||
|  |   void UnregisterAppMemory(void* ptr); | ||||||
|  | 
 | ||||||
|  |   // Force signal handling for the specified signal.
 | ||||||
|  |   bool SimulateSignalDelivery(int sig); | ||||||
|  |  private: | ||||||
|  |   // Save the old signal handlers and install new ones.
 | ||||||
|  |   static bool InstallHandlersLocked(); | ||||||
|  |   // Restore the old signal handlers.
 | ||||||
|  |   static void RestoreHandlersLocked(); | ||||||
|  | 
 | ||||||
|  |   void PreresolveSymbols(); | ||||||
|  |   bool GenerateDump(CrashContext *context); | ||||||
|  |   void SendContinueSignalToChild(); | ||||||
|  |   void WaitForContinueSignal(); | ||||||
|  | 
 | ||||||
|  |   static void SignalHandler(int sig, siginfo_t* info, void* uc); | ||||||
|  |   bool HandleSignal(int sig, siginfo_t* info, void* uc); | ||||||
|  |   static int ThreadEntry(void* arg); | ||||||
|  |   bool DoDump(pid_t crashing_process, const void* context, | ||||||
|  |               size_t context_size); | ||||||
|  | 
 | ||||||
|  |   const FilterCallback filter_; | ||||||
|  |   const MinidumpCallback callback_; | ||||||
|  |   void* const callback_context_; | ||||||
|  | 
 | ||||||
|  |   scoped_ptr<CrashGenerationClient> crash_generation_client_; | ||||||
|  | 
 | ||||||
|  |   MinidumpDescriptor minidump_descriptor_; | ||||||
|  | 
 | ||||||
|  |   HandlerCallback crash_handler_; | ||||||
|  | 
 | ||||||
|  |   // The global exception handler stack. This is need becuase there may exist
 | ||||||
|  |   // multiple ExceptionHandler instances in a process. Each will have itself
 | ||||||
|  |   // registered in this stack.
 | ||||||
|  |   static std::vector<ExceptionHandler*> *handler_stack_; | ||||||
|  |   static pthread_mutex_t handler_stack_mutex_; | ||||||
|  | 
 | ||||||
|  |   // We need to explicitly enable ptrace of parent processes on some
 | ||||||
|  |   // kernels, but we need to know the PID of the cloned process before we
 | ||||||
|  |   // can do this. We create a pipe which we can use to block the
 | ||||||
|  |   // cloned process after creating it, until we have explicitly enabled 
 | ||||||
|  |   // ptrace. This is used to store the file descriptors for the pipe
 | ||||||
|  |   int fdes[2]; | ||||||
|  | 
 | ||||||
|  |   // Callers can add extra info about mappings for cases where the
 | ||||||
|  |   // dumper code cannot extract enough information from /proc/<pid>/maps.
 | ||||||
|  |   MappingList mapping_list_; | ||||||
|  | 
 | ||||||
|  |   // Callers can request additional memory regions to be included in
 | ||||||
|  |   // the dump.
 | ||||||
|  |   AppMemoryList app_memory_list_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
 | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | // Copyright (c) 2012 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/handler/minidump_descriptor.h" | ||||||
|  | 
 | ||||||
|  | #include "common/linux/guid_creator.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor) | ||||||
|  |     : fd_(descriptor.fd_), | ||||||
|  |       directory_(descriptor.directory_), | ||||||
|  |       c_path_(NULL), | ||||||
|  |       size_limit_(descriptor.size_limit_) { | ||||||
|  |   // The copy constructor is not allowed to be called on a MinidumpDescriptor
 | ||||||
|  |   // with a valid path_, as getting its c_path_ would require the heap which
 | ||||||
|  |   // can cause problems in compromised environments.
 | ||||||
|  |   assert(descriptor.path_.empty()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MinidumpDescriptor& MinidumpDescriptor::operator=( | ||||||
|  |     const MinidumpDescriptor& descriptor) { | ||||||
|  |   assert(descriptor.path_.empty()); | ||||||
|  | 
 | ||||||
|  |   fd_ = descriptor.fd_; | ||||||
|  |   directory_ = descriptor.directory_; | ||||||
|  |   path_.clear(); | ||||||
|  |   if (c_path_) { | ||||||
|  |     // This descriptor already had a path set, so generate a new one.
 | ||||||
|  |     c_path_ = NULL; | ||||||
|  |     UpdatePath(); | ||||||
|  |   } | ||||||
|  |   size_limit_ = descriptor.size_limit_; | ||||||
|  |   return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MinidumpDescriptor::UpdatePath() { | ||||||
|  |   assert(fd_ == -1 && !directory_.empty()); | ||||||
|  | 
 | ||||||
|  |   GUID guid; | ||||||
|  |   char guid_str[kGUIDStringLength + 1]; | ||||||
|  |   if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) { | ||||||
|  |     assert(false); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   path_.clear(); | ||||||
|  |   path_ = directory_ + "/" + guid_str + ".dmp";   | ||||||
|  |   c_path_ = path_.c_str(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
							
								
								
									
										100
									
								
								google-breakpad/src/client/linux/handler/minidump_descriptor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								google-breakpad/src/client/linux/handler/minidump_descriptor.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,100 @@ | |||||||
|  | // Copyright (c) 2012 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ | ||||||
|  | #define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | // The MinidumpDescriptor describes how to access a minidump: it can contain
 | ||||||
|  | // either a file descriptor or a path.
 | ||||||
|  | // Note that when using files, it is created with the path to a directory.
 | ||||||
|  | // The actual path where the minidump is generated is created by this class.
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class MinidumpDescriptor { | ||||||
|  |  public: | ||||||
|  |   MinidumpDescriptor() : fd_(-1), size_limit_(-1) {} | ||||||
|  | 
 | ||||||
|  |   explicit MinidumpDescriptor(const string& directory) | ||||||
|  |       : fd_(-1), | ||||||
|  |         directory_(directory), | ||||||
|  |         c_path_(NULL), | ||||||
|  |         size_limit_(-1) { | ||||||
|  |     assert(!directory.empty()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   explicit MinidumpDescriptor(int fd) | ||||||
|  |       : fd_(fd), | ||||||
|  |         c_path_(NULL), | ||||||
|  |         size_limit_(-1) { | ||||||
|  |     assert(fd != -1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor); | ||||||
|  |   MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor); | ||||||
|  | 
 | ||||||
|  |   bool IsFD() const { return fd_ != -1; } | ||||||
|  | 
 | ||||||
|  |   int fd() const { return fd_; } | ||||||
|  | 
 | ||||||
|  |   string directory() const { return directory_; } | ||||||
|  | 
 | ||||||
|  |   const char* path() const { return c_path_; } | ||||||
|  | 
 | ||||||
|  |   // Updates the path so it is unique.
 | ||||||
|  |   // Should be called from a normal context: this methods uses the heap.
 | ||||||
|  |   void UpdatePath(); | ||||||
|  | 
 | ||||||
|  |   off_t size_limit() const { return size_limit_; } | ||||||
|  |   void set_size_limit(off_t limit) { size_limit_ = limit; } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // The file descriptor where the minidump is generated.
 | ||||||
|  |   int fd_; | ||||||
|  | 
 | ||||||
|  |   // The directory where the minidump should be generated.
 | ||||||
|  |   string directory_; | ||||||
|  |   // The full path to the generated minidump.
 | ||||||
|  |   string path_; | ||||||
|  |   // The C string of |path_|. Precomputed so it can be access from a compromised
 | ||||||
|  |   // context.
 | ||||||
|  |   const char* c_path_; | ||||||
|  | 
 | ||||||
|  |   off_t size_limit_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_
 | ||||||
							
								
								
									
										48
									
								
								google-breakpad/src/client/linux/log/log.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								google-breakpad/src/client/linux/log/log.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | |||||||
|  | // Copyright (c) 2012 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include "client/linux/log/log.h" | ||||||
|  | 
 | ||||||
|  | #if defined(__ANDROID__) | ||||||
|  | #include <android/log.h> | ||||||
|  | #else | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace logger { | ||||||
|  | 
 | ||||||
|  | int write(const char* buf, size_t nbytes) { | ||||||
|  | #if defined(__ANDROID__) | ||||||
|  |   return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf); | ||||||
|  | #else | ||||||
|  |   return sys_write(2, buf, nbytes); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace logger
 | ||||||
							
								
								
									
										41
									
								
								google-breakpad/src/client/linux/log/log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								google-breakpad/src/client/linux/log/log.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_LOG_LOG_H_ | ||||||
|  | #define CLIENT_LINUX_LOG_LOG_H_ | ||||||
|  | 
 | ||||||
|  | #include <stddef.h> | ||||||
|  | 
 | ||||||
|  | namespace logger { | ||||||
|  | 
 | ||||||
|  | int write(const char* buf, size_t nbytes); | ||||||
|  | 
 | ||||||
|  | }  // namespace logger
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_LOG_LOG_H_
 | ||||||
							
								
								
									
										144
									
								
								google-breakpad/src/client/linux/minidump_writer/cpu_set.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								google-breakpad/src/client/linux/minidump_writer/cpu_set.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,144 @@ | |||||||
|  | // Copyright (c) 2013, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // Helper class used to model a set of CPUs, as read from sysfs
 | ||||||
|  | // files like /sys/devices/system/cpu/present
 | ||||||
|  | // See See http://www.kernel.org/doc/Documentation/cputopology.txt
 | ||||||
|  | class CpuSet { | ||||||
|  | public: | ||||||
|  |   // The maximum number of supported CPUs.
 | ||||||
|  |   static const size_t kMaxCpus = 1024; | ||||||
|  | 
 | ||||||
|  |   CpuSet() { | ||||||
|  |     my_memset(mask_, 0, sizeof(mask_)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Parse a sysfs file to extract the corresponding CPU set.
 | ||||||
|  |   bool ParseSysFile(int fd) { | ||||||
|  |     char buffer[512]; | ||||||
|  |     int ret = sys_read(fd, buffer, sizeof(buffer)-1); | ||||||
|  |     if (ret < 0) | ||||||
|  |       return false; | ||||||
|  | 
 | ||||||
|  |     buffer[ret] = '\0'; | ||||||
|  | 
 | ||||||
|  |     // Expected format: comma-separated list of items, where each
 | ||||||
|  |     // item can be a decimal integer, or two decimal integers separated
 | ||||||
|  |     // by a dash.
 | ||||||
|  |     // E.g.:
 | ||||||
|  |     //       0
 | ||||||
|  |     //       0,1,2,3
 | ||||||
|  |     //       0-3
 | ||||||
|  |     //       1,10-23
 | ||||||
|  |     const char* p = buffer; | ||||||
|  |     const char* p_end = p + ret; | ||||||
|  |     while (p < p_end) { | ||||||
|  |       // Skip leading space, if any
 | ||||||
|  |       while (p < p_end && my_isspace(*p)) | ||||||
|  |         p++; | ||||||
|  | 
 | ||||||
|  |       // Find start and size of current item.
 | ||||||
|  |       const char* item = p; | ||||||
|  |       size_t item_len = static_cast<size_t>(p_end - p); | ||||||
|  |       const char* item_next = | ||||||
|  |           static_cast<const char*>(my_memchr(p, ',', item_len)); | ||||||
|  |       if (item_next != NULL) { | ||||||
|  |         p = item_next + 1; | ||||||
|  |         item_len = static_cast<size_t>(item_next - item); | ||||||
|  |       } else { | ||||||
|  |         p = p_end; | ||||||
|  |         item_next = p_end; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Ignore trailing spaces.
 | ||||||
|  |       while (item_next > item && my_isspace(item_next[-1])) | ||||||
|  |         item_next--; | ||||||
|  | 
 | ||||||
|  |       // skip empty items.
 | ||||||
|  |       if (item_next == item) | ||||||
|  |         continue; | ||||||
|  | 
 | ||||||
|  |       // read first decimal value.
 | ||||||
|  |       uintptr_t start = 0; | ||||||
|  |       const char* next = my_read_decimal_ptr(&start, item); | ||||||
|  |       uintptr_t end = start; | ||||||
|  |       if (*next == '-') | ||||||
|  |         my_read_decimal_ptr(&end, next+1); | ||||||
|  | 
 | ||||||
|  |       while (start <= end) | ||||||
|  |         SetBit(start++); | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Intersect this CPU set with another one.
 | ||||||
|  |   void IntersectWith(const CpuSet& other) { | ||||||
|  |     for (size_t nn = 0; nn < kMaskWordCount; ++nn) | ||||||
|  |       mask_[nn] &= other.mask_[nn]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Return the number of CPUs in this set.
 | ||||||
|  |   int GetCount() { | ||||||
|  |     int result = 0; | ||||||
|  |     for (size_t nn = 0; nn < kMaskWordCount; ++nn) { | ||||||
|  |       result += __builtin_popcount(mask_[nn]); | ||||||
|  |     } | ||||||
|  |     return result; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |   void SetBit(uintptr_t index) { | ||||||
|  |     size_t nn = static_cast<size_t>(index); | ||||||
|  |     if (nn < kMaxCpus) | ||||||
|  |       mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   typedef uint32_t MaskWordType; | ||||||
|  |   static const size_t kMaskWordBits = 8*sizeof(MaskWordType); | ||||||
|  |   static const size_t kMaskWordCount = | ||||||
|  |       (kMaxCpus + kMaskWordBits - 1) / kMaskWordBits; | ||||||
|  | 
 | ||||||
|  |   MaskWordType mask_[kMaskWordCount]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_
 | ||||||
| @ -0,0 +1,165 @@ | |||||||
|  | // Copyright (c) 2013, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "client/linux/minidump_writer/cpu_set.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "common/tests/auto_testfile.h" | ||||||
|  | 
 | ||||||
|  | using namespace google_breakpad; | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | typedef testing::Test CpuSetTest; | ||||||
|  | 
 | ||||||
|  | // Helper class to write test text file to a temporary file and return
 | ||||||
|  | // its file descriptor.
 | ||||||
|  | class ScopedTestFile : public AutoTestFile { | ||||||
|  | public: | ||||||
|  |   explicit ScopedTestFile(const char* text) | ||||||
|  |     : AutoTestFile("cpu_set", text) { | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, EmptyCount) { | ||||||
|  |   CpuSet set; | ||||||
|  |   ASSERT_EQ(0, set.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, OneCpu) { | ||||||
|  |   ScopedTestFile file("10"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  | 
 | ||||||
|  |   CpuSet set; | ||||||
|  |   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||||
|  |   ASSERT_EQ(1, set.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, OneCpuTerminated) { | ||||||
|  |   ScopedTestFile file("10\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  | 
 | ||||||
|  |   CpuSet set; | ||||||
|  |   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||||
|  |   ASSERT_EQ(1, set.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, TwoCpusWithComma) { | ||||||
|  |   ScopedTestFile file("1,10"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  | 
 | ||||||
|  |   CpuSet set; | ||||||
|  |   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||||
|  |   ASSERT_EQ(2, set.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, TwoCpusWithRange) { | ||||||
|  |   ScopedTestFile file("1-2"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  | 
 | ||||||
|  |   CpuSet set; | ||||||
|  |   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||||
|  |   ASSERT_EQ(2, set.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, TenCpusWithRange) { | ||||||
|  |   ScopedTestFile file("9-18"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  | 
 | ||||||
|  |   CpuSet set; | ||||||
|  |   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||||
|  |   ASSERT_EQ(10, set.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, MultiItems) { | ||||||
|  |   ScopedTestFile file("0, 2-4, 128"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  | 
 | ||||||
|  |   CpuSet set; | ||||||
|  |   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||||
|  |   ASSERT_EQ(5, set.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, IntersectWith) { | ||||||
|  |   ScopedTestFile file1("9-19"); | ||||||
|  |   ASSERT_TRUE(file1.IsOk()); | ||||||
|  |   CpuSet set1; | ||||||
|  |   ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); | ||||||
|  |   ASSERT_EQ(11, set1.GetCount()); | ||||||
|  | 
 | ||||||
|  |   ScopedTestFile file2("16-24"); | ||||||
|  |   ASSERT_TRUE(file2.IsOk()); | ||||||
|  |   CpuSet set2; | ||||||
|  |   ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); | ||||||
|  |   ASSERT_EQ(9, set2.GetCount()); | ||||||
|  | 
 | ||||||
|  |   set1.IntersectWith(set2); | ||||||
|  |   ASSERT_EQ(4, set1.GetCount()); | ||||||
|  |   ASSERT_EQ(9, set2.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, SelfIntersection) { | ||||||
|  |   ScopedTestFile file1("9-19"); | ||||||
|  |   ASSERT_TRUE(file1.IsOk()); | ||||||
|  |   CpuSet set1; | ||||||
|  |   ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); | ||||||
|  |   ASSERT_EQ(11, set1.GetCount()); | ||||||
|  | 
 | ||||||
|  |   set1.IntersectWith(set1); | ||||||
|  |   ASSERT_EQ(11, set1.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(CpuSetTest, EmptyIntersection) { | ||||||
|  |   ScopedTestFile file1("0-19"); | ||||||
|  |   ASSERT_TRUE(file1.IsOk()); | ||||||
|  |   CpuSet set1; | ||||||
|  |   ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); | ||||||
|  |   ASSERT_EQ(20, set1.GetCount()); | ||||||
|  | 
 | ||||||
|  |   ScopedTestFile file2("20-39"); | ||||||
|  |   ASSERT_TRUE(file2.IsOk()); | ||||||
|  |   CpuSet set2; | ||||||
|  |   ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); | ||||||
|  |   ASSERT_EQ(20, set2.GetCount()); | ||||||
|  | 
 | ||||||
|  |   set1.IntersectWith(set2); | ||||||
|  |   ASSERT_EQ(0, set1.GetCount()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_EQ(20, set2.GetCount()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @ -0,0 +1,106 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // A class for enumerating a directory without using diropen/readdir or other
 | ||||||
|  | // functions which may allocate memory.
 | ||||||
|  | class DirectoryReader { | ||||||
|  |  public: | ||||||
|  |   DirectoryReader(int fd) | ||||||
|  |       : fd_(fd), | ||||||
|  |         buf_used_(0) { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Return the next entry from the directory
 | ||||||
|  |   //   name: (output) the NUL terminated entry name
 | ||||||
|  |   //
 | ||||||
|  |   // Returns true iff successful (false on EOF).
 | ||||||
|  |   //
 | ||||||
|  |   // After calling this, one must call |PopEntry| otherwise you'll get the same
 | ||||||
|  |   // entry over and over.
 | ||||||
|  |   bool GetNextEntry(const char** name) { | ||||||
|  |     struct kernel_dirent* const dent = | ||||||
|  |       reinterpret_cast<kernel_dirent*>(buf_); | ||||||
|  | 
 | ||||||
|  |     if (buf_used_ == 0) { | ||||||
|  |       // need to read more entries.
 | ||||||
|  |       const int n = sys_getdents(fd_, dent, sizeof(buf_)); | ||||||
|  |       if (n < 0) { | ||||||
|  |         return false; | ||||||
|  |       } else if (n == 0) { | ||||||
|  |         hit_eof_ = true; | ||||||
|  |       } else { | ||||||
|  |         buf_used_ += n; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (buf_used_ == 0 && hit_eof_) | ||||||
|  |       return false; | ||||||
|  | 
 | ||||||
|  |     assert(buf_used_ > 0); | ||||||
|  | 
 | ||||||
|  |     *name = dent->d_name; | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void PopEntry() { | ||||||
|  |     if (!buf_used_) | ||||||
|  |       return; | ||||||
|  | 
 | ||||||
|  |     const struct kernel_dirent* const dent = | ||||||
|  |       reinterpret_cast<kernel_dirent*>(buf_); | ||||||
|  | 
 | ||||||
|  |     buf_used_ -= dent->d_reclen; | ||||||
|  |     my_memmove(buf_, buf_ + dent->d_reclen, buf_used_); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   const int fd_; | ||||||
|  |   bool hit_eof_; | ||||||
|  |   unsigned buf_used_; | ||||||
|  |   uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_
 | ||||||
| @ -0,0 +1,78 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include <dirent.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/directory_reader.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | 
 | ||||||
|  | using namespace google_breakpad; | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | typedef testing::Test DirectoryReaderTest; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(DirectoryReaderTest, CompareResults) { | ||||||
|  |   std::set<string> dent_set; | ||||||
|  | 
 | ||||||
|  |   DIR *const dir = opendir("/proc/self"); | ||||||
|  |   ASSERT_TRUE(dir != NULL); | ||||||
|  | 
 | ||||||
|  |   struct dirent* dent; | ||||||
|  |   while ((dent = readdir(dir))) | ||||||
|  |     dent_set.insert(dent->d_name); | ||||||
|  | 
 | ||||||
|  |   closedir(dir); | ||||||
|  | 
 | ||||||
|  |   const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); | ||||||
|  |   ASSERT_GE(fd, 0); | ||||||
|  | 
 | ||||||
|  |   DirectoryReader dir_reader(fd); | ||||||
|  |   unsigned seen = 0; | ||||||
|  | 
 | ||||||
|  |   const char* name; | ||||||
|  |   while (dir_reader.GetNextEntry(&name)) { | ||||||
|  |     ASSERT_TRUE(dent_set.find(name) != dent_set.end()); | ||||||
|  |     seen++; | ||||||
|  |     dir_reader.PopEntry(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ASSERT_TRUE(dent_set.find("status") != dent_set.end()); | ||||||
|  |   ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); | ||||||
|  |   ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_EQ(dent_set.size(), seen); | ||||||
|  |   close(fd); | ||||||
|  | } | ||||||
							
								
								
									
										131
									
								
								google-breakpad/src/client/linux/minidump_writer/line_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								google-breakpad/src/client/linux/minidump_writer/line_reader.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,131 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // A class for reading a file, line by line, without using fopen/fgets or other
 | ||||||
|  | // functions which may allocate memory.
 | ||||||
|  | class LineReader { | ||||||
|  |  public: | ||||||
|  |   LineReader(int fd) | ||||||
|  |       : fd_(fd), | ||||||
|  |         hit_eof_(false), | ||||||
|  |         buf_used_(0) { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // The maximum length of a line.
 | ||||||
|  |   static const size_t kMaxLineLen = 512; | ||||||
|  | 
 | ||||||
|  |   // Return the next line from the file.
 | ||||||
|  |   //   line: (output) a pointer to the start of the line. The line is NUL
 | ||||||
|  |   //     terminated.
 | ||||||
|  |   //   len: (output) the length of the line (not inc the NUL byte)
 | ||||||
|  |   //
 | ||||||
|  |   // Returns true iff successful (false on EOF).
 | ||||||
|  |   //
 | ||||||
|  |   // One must call |PopLine| after this function, otherwise you'll continue to
 | ||||||
|  |   // get the same line over and over.
 | ||||||
|  |   bool GetNextLine(const char **line, unsigned *len) { | ||||||
|  |     for (;;) { | ||||||
|  |       if (buf_used_ == 0 && hit_eof_) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |       for (unsigned i = 0; i < buf_used_; ++i) { | ||||||
|  |         if (buf_[i] == '\n' || buf_[i] == 0) { | ||||||
|  |           buf_[i] = 0; | ||||||
|  |           *len = i; | ||||||
|  |           *line = buf_; | ||||||
|  |           return true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (buf_used_ == sizeof(buf_)) { | ||||||
|  |         // we scanned the whole buffer and didn't find an end-of-line marker.
 | ||||||
|  |         // This line is too long to process.
 | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // We didn't find any end-of-line terminators in the buffer. However, if
 | ||||||
|  |       // this is the last line in the file it might not have one:
 | ||||||
|  |       if (hit_eof_) { | ||||||
|  |         assert(buf_used_); | ||||||
|  |         // There's room for the NUL because of the buf_used_ == sizeof(buf_)
 | ||||||
|  |         // check above.
 | ||||||
|  |         buf_[buf_used_] = 0; | ||||||
|  |         *len = buf_used_; | ||||||
|  |         buf_used_ += 1;  // since we appended the NUL.
 | ||||||
|  |         *line = buf_; | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Otherwise, we should pull in more data from the file
 | ||||||
|  |       const ssize_t n = sys_read(fd_, buf_ + buf_used_, | ||||||
|  |                                  sizeof(buf_) - buf_used_); | ||||||
|  |       if (n < 0) { | ||||||
|  |         return false; | ||||||
|  |       } else if (n == 0) { | ||||||
|  |         hit_eof_ = true; | ||||||
|  |       } else { | ||||||
|  |         buf_used_ += n; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // At this point, we have either set the hit_eof_ flag, or we have more
 | ||||||
|  |       // data to process...
 | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void PopLine(unsigned len) { | ||||||
|  |     // len doesn't include the NUL byte at the end.
 | ||||||
|  | 
 | ||||||
|  |     assert(buf_used_ >= len + 1); | ||||||
|  |     buf_used_ -= len + 1; | ||||||
|  |     my_memmove(buf_, buf_ + len + 1, buf_used_); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   const int fd_; | ||||||
|  | 
 | ||||||
|  |   bool hit_eof_; | ||||||
|  |   unsigned buf_used_; | ||||||
|  |   char buf_[kMaxLineLen]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_
 | ||||||
| @ -0,0 +1,170 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/line_reader.h" | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "common/tests/auto_testfile.h" | ||||||
|  | 
 | ||||||
|  | using namespace google_breakpad; | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | typedef testing::Test LineReaderTest; | ||||||
|  | 
 | ||||||
|  | class ScopedTestFile : public AutoTestFile { | ||||||
|  | public: | ||||||
|  |   explicit ScopedTestFile(const char* text) | ||||||
|  |     : AutoTestFile("line_reader", text) { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ScopedTestFile(const char* text, size_t text_len) | ||||||
|  |     : AutoTestFile("line_reader", text, text_len) { | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LineReaderTest, EmptyFile) { | ||||||
|  |   ScopedTestFile file(""); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   LineReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *line; | ||||||
|  |   unsigned len; | ||||||
|  |   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LineReaderTest, OneLineTerminated) { | ||||||
|  |   ScopedTestFile file("a\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   LineReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *line; | ||||||
|  |   unsigned int len; | ||||||
|  |   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||||
|  |   ASSERT_EQ((unsigned int)1, len); | ||||||
|  |   ASSERT_EQ('a', line[0]); | ||||||
|  |   ASSERT_EQ('\0', line[1]); | ||||||
|  |   reader.PopLine(len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LineReaderTest, OneLine) { | ||||||
|  |   ScopedTestFile file("a"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   LineReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *line; | ||||||
|  |   unsigned len; | ||||||
|  |   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||||
|  |   ASSERT_EQ((unsigned)1, len); | ||||||
|  |   ASSERT_EQ('a', line[0]); | ||||||
|  |   ASSERT_EQ('\0', line[1]); | ||||||
|  |   reader.PopLine(len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LineReaderTest, TwoLinesTerminated) { | ||||||
|  |   ScopedTestFile file("a\nb\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   LineReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *line; | ||||||
|  |   unsigned len; | ||||||
|  |   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||||
|  |   ASSERT_EQ((unsigned)1, len); | ||||||
|  |   ASSERT_EQ('a', line[0]); | ||||||
|  |   ASSERT_EQ('\0', line[1]); | ||||||
|  |   reader.PopLine(len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||||
|  |   ASSERT_EQ((unsigned)1, len); | ||||||
|  |   ASSERT_EQ('b', line[0]); | ||||||
|  |   ASSERT_EQ('\0', line[1]); | ||||||
|  |   reader.PopLine(len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LineReaderTest, TwoLines) { | ||||||
|  |   ScopedTestFile file("a\nb"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   LineReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *line; | ||||||
|  |   unsigned len; | ||||||
|  |   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||||
|  |   ASSERT_EQ((unsigned)1, len); | ||||||
|  |   ASSERT_EQ('a', line[0]); | ||||||
|  |   ASSERT_EQ('\0', line[1]); | ||||||
|  |   reader.PopLine(len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||||
|  |   ASSERT_EQ((unsigned)1, len); | ||||||
|  |   ASSERT_EQ('b', line[0]); | ||||||
|  |   ASSERT_EQ('\0', line[1]); | ||||||
|  |   reader.PopLine(len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LineReaderTest, MaxLength) { | ||||||
|  |   char l[LineReader::kMaxLineLen-1]; | ||||||
|  |   memset(l, 'a', sizeof(l)); | ||||||
|  |   ScopedTestFile file(l, sizeof(l)); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   LineReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *line; | ||||||
|  |   unsigned len; | ||||||
|  |   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||||
|  |   ASSERT_EQ(sizeof(l), len); | ||||||
|  |   ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); | ||||||
|  |   ASSERT_EQ('\0', line[len]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LineReaderTest, TooLong) { | ||||||
|  |   // Note: this writes kMaxLineLen 'a' chars in the test file.
 | ||||||
|  |   char l[LineReader::kMaxLineLen]; | ||||||
|  |   memset(l, 'a', sizeof(l)); | ||||||
|  |   ScopedTestFile file(l, sizeof(l)); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   LineReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *line; | ||||||
|  |   unsigned len; | ||||||
|  |   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||||
|  | } | ||||||
| @ -0,0 +1,233 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper.
 | ||||||
|  | // See linux_core_dumper.h for details.
 | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/linux_core_dumper.h" | ||||||
|  | 
 | ||||||
|  | #include <asm/ptrace.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <elf.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/procfs.h> | ||||||
|  | 
 | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | LinuxCoreDumper::LinuxCoreDumper(pid_t pid, | ||||||
|  |                                  const char* core_path, | ||||||
|  |                                  const char* procfs_path) | ||||||
|  |     : LinuxDumper(pid), | ||||||
|  |       core_path_(core_path), | ||||||
|  |       procfs_path_(procfs_path), | ||||||
|  |       thread_infos_(&allocator_, 8) { | ||||||
|  |   assert(core_path_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid, | ||||||
|  |                                     const char* node) const { | ||||||
|  |   if (!path || !node) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   size_t node_len = my_strlen(node); | ||||||
|  |   if (node_len == 0) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   size_t procfs_path_len = my_strlen(procfs_path_); | ||||||
|  |   size_t total_length = procfs_path_len + 1 + node_len; | ||||||
|  |   if (total_length >= NAME_MAX) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   memcpy(path, procfs_path_, procfs_path_len); | ||||||
|  |   path[procfs_path_len] = '/'; | ||||||
|  |   memcpy(path + procfs_path_len + 1, node, node_len); | ||||||
|  |   path[total_length] = '\0'; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, | ||||||
|  |                                       const void* src, size_t length) { | ||||||
|  |   ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src); | ||||||
|  |   // TODO(benchan): Investigate whether the data to be copied could span
 | ||||||
|  |   // across multiple segments in the core dump file. ElfCoreDump::CopyData
 | ||||||
|  |   // and this method do not handle that case yet.
 | ||||||
|  |   if (!core_.CopyData(dest, virtual_address, length)) { | ||||||
|  |     // If the data segment is not found in the core dump, fill the result
 | ||||||
|  |     // with marker characters.
 | ||||||
|  |     memset(dest, 0xab, length); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { | ||||||
|  |   if (index >= thread_infos_.size()) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   *info = thread_infos_[index]; | ||||||
|  |   const uint8_t* stack_pointer; | ||||||
|  | #if defined(__i386) | ||||||
|  |   memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); | ||||||
|  | #elif defined(__x86_64) | ||||||
|  |   memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); | ||||||
|  | #elif defined(__ARM_EABI__) | ||||||
|  |   memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); | ||||||
|  | #else | ||||||
|  | #error "This code hasn't been ported to your platform yet." | ||||||
|  | #endif | ||||||
|  |   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxCoreDumper::IsPostMortem() const { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxCoreDumper::ThreadsSuspend() { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxCoreDumper::ThreadsResume() { | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxCoreDumper::EnumerateThreads() { | ||||||
|  |   if (!mapped_core_file_.Map(core_path_)) { | ||||||
|  |     fprintf(stderr, "Could not map core dump file into memory\n"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   core_.SetContent(mapped_core_file_.content()); | ||||||
|  |   if (!core_.IsValid()) { | ||||||
|  |     fprintf(stderr, "Invalid core dump file\n"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ElfCoreDump::Note note = core_.GetFirstNote(); | ||||||
|  |   if (!note.IsValid()) { | ||||||
|  |     fprintf(stderr, "PT_NOTE section not found\n"); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool first_thread = true; | ||||||
|  |   do { | ||||||
|  |     ElfCoreDump::Word type = note.GetType(); | ||||||
|  |     MemoryRange name = note.GetName(); | ||||||
|  |     MemoryRange description = note.GetDescription(); | ||||||
|  | 
 | ||||||
|  |     if (type == 0 || name.IsEmpty() || description.IsEmpty()) { | ||||||
|  |       fprintf(stderr, "Could not found a valid PT_NOTE.\n"); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are
 | ||||||
|  |     // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific):
 | ||||||
|  |     //   Thread           Name          Type
 | ||||||
|  |     //   -------------------------------------------------------------------
 | ||||||
|  |     //   1st thread       CORE          NT_PRSTATUS
 | ||||||
|  |     //   process-wide     CORE          NT_PRPSINFO
 | ||||||
|  |     //   process-wide     CORE          NT_AUXV
 | ||||||
|  |     //   1st thread       CORE          NT_FPREGSET
 | ||||||
|  |     //   1st thread       LINUX         NT_PRXFPREG
 | ||||||
|  |     //   1st thread       LINUX         NT_386_TLS
 | ||||||
|  |     //
 | ||||||
|  |     //   2nd thread       CORE          NT_PRSTATUS
 | ||||||
|  |     //   2nd thread       CORE          NT_FPREGSET
 | ||||||
|  |     //   2nd thread       LINUX         NT_PRXFPREG
 | ||||||
|  |     //   2nd thread       LINUX         NT_386_TLS
 | ||||||
|  |     //
 | ||||||
|  |     //   3rd thread       CORE          NT_PRSTATUS
 | ||||||
|  |     //   3rd thread       CORE          NT_FPREGSET
 | ||||||
|  |     //   3rd thread       LINUX         NT_PRXFPREG
 | ||||||
|  |     //   3rd thread       LINUX         NT_386_TLS
 | ||||||
|  |     //
 | ||||||
|  |     // The following code only works if notes are ordered as expected.
 | ||||||
|  |     switch (type) { | ||||||
|  |       case NT_PRSTATUS: { | ||||||
|  |         if (description.length() != sizeof(elf_prstatus)) { | ||||||
|  |           fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n"); | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         const elf_prstatus* status = | ||||||
|  |             reinterpret_cast<const elf_prstatus*>(description.data()); | ||||||
|  |         pid_t pid = status->pr_pid; | ||||||
|  |         ThreadInfo info; | ||||||
|  |         memset(&info, 0, sizeof(ThreadInfo)); | ||||||
|  |         info.tgid = status->pr_pgrp; | ||||||
|  |         info.ppid = status->pr_ppid; | ||||||
|  |         memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); | ||||||
|  |         if (first_thread) { | ||||||
|  |           crash_thread_ = pid; | ||||||
|  |           crash_signal_ = status->pr_info.si_signo; | ||||||
|  |         } | ||||||
|  |         first_thread = false; | ||||||
|  |         threads_.push_back(pid); | ||||||
|  |         thread_infos_.push_back(info); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | #if defined(__i386) || defined(__x86_64) | ||||||
|  |       case NT_FPREGSET: { | ||||||
|  |         if (thread_infos_.empty()) | ||||||
|  |           return false; | ||||||
|  | 
 | ||||||
|  |         ThreadInfo* info = &thread_infos_.back(); | ||||||
|  |         if (description.length() != sizeof(info->fpregs)) { | ||||||
|  |           fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n"); | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  | #if defined(__i386) | ||||||
|  |       case NT_PRXFPREG: { | ||||||
|  |         if (thread_infos_.empty()) | ||||||
|  |           return false; | ||||||
|  | 
 | ||||||
|  |         ThreadInfo* info = &thread_infos_.back(); | ||||||
|  |         if (description.length() != sizeof(info->fpxregs)) { | ||||||
|  |           fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n"); | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  | #endif | ||||||
|  |     } | ||||||
|  |     note = note.GetNextNote(); | ||||||
|  |   } while (note.IsValid()); | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
| @ -0,0 +1,122 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper
 | ||||||
|  | // class, which is derived from google_breakpad::LinuxDumper to extract
 | ||||||
|  | // information from a crashed process via its core dump and proc files.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/linux_dumper.h" | ||||||
|  | #include "common/linux/elf_core_dump.h" | ||||||
|  | #include "common/linux/memory_mapped_file.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class LinuxCoreDumper : public LinuxDumper { | ||||||
|  |  public: | ||||||
|  |   // Constructs a dumper for extracting information of a given process
 | ||||||
|  |   // with a process ID of |pid| via its core dump file at |core_path| and
 | ||||||
|  |   // its proc files at |procfs_path|. If |procfs_path| is a copy of
 | ||||||
|  |   // /proc/<pid>, it should contain the following files:
 | ||||||
|  |   //     auxv, cmdline, environ, exe, maps, status
 | ||||||
|  |   LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::BuildProcPath().
 | ||||||
|  |   // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
 | ||||||
|  |   // |path| is a character array of at least NAME_MAX bytes to return the
 | ||||||
|  |   // result.|node| is the final node without any slashes. Return true on
 | ||||||
|  |   // success.
 | ||||||
|  |   //
 | ||||||
|  |   // As this dumper performs a post-mortem dump and makes use of a copy
 | ||||||
|  |   // of the proc files of the crashed process, this derived method does
 | ||||||
|  |   // not actually make use of |pid| and always returns a subpath of
 | ||||||
|  |   // |procfs_path_| regardless of whether |pid| corresponds to the main
 | ||||||
|  |   // process or a thread of the process, i.e. assuming both the main process
 | ||||||
|  |   // and its threads have the following proc files with the same content:
 | ||||||
|  |   //     auxv, cmdline, environ, exe, maps, status
 | ||||||
|  |   virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::CopyFromProcess().
 | ||||||
|  |   // Copies content of |length| bytes from a given process |child|,
 | ||||||
|  |   // starting from |src|, into |dest|. This method extracts the content
 | ||||||
|  |   // the core dump and fills |dest| with a sequence of marker bytes
 | ||||||
|  |   // if the expected data is not found in the core dump.
 | ||||||
|  |   virtual void CopyFromProcess(void* dest, pid_t child, const void* src, | ||||||
|  |                                size_t length); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::GetThreadInfoByIndex().
 | ||||||
|  |   // Reads information about the |index|-th thread of |threads_|.
 | ||||||
|  |   // Returns true on success. One must have called |ThreadsSuspend| first.
 | ||||||
|  |   virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::IsPostMortem().
 | ||||||
|  |   // Always returns true to indicate that this dumper performs a
 | ||||||
|  |   // post-mortem dump of a crashed process via a core dump file.
 | ||||||
|  |   virtual bool IsPostMortem() const; | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::ThreadsSuspend().
 | ||||||
|  |   // As the dumper performs a post-mortem dump via a core dump file,
 | ||||||
|  |   // there is no threads to suspend. This method does nothing and
 | ||||||
|  |   // always returns true.
 | ||||||
|  |   virtual bool ThreadsSuspend(); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::ThreadsResume().
 | ||||||
|  |   // As the dumper performs a post-mortem dump via a core dump file,
 | ||||||
|  |   // there is no threads to resume. This method does nothing and
 | ||||||
|  |   // always returns true.
 | ||||||
|  |   virtual bool ThreadsResume(); | ||||||
|  | 
 | ||||||
|  |  protected: | ||||||
|  |   // Implements LinuxDumper::EnumerateThreads().
 | ||||||
|  |   // Enumerates all threads of the given process into |threads_|.
 | ||||||
|  |   virtual bool EnumerateThreads(); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // Path of the core dump file.
 | ||||||
|  |   const char* core_path_; | ||||||
|  | 
 | ||||||
|  |   // Path of the directory containing the proc files of the given process,
 | ||||||
|  |   // which is usually a copy of /proc/<pid>.
 | ||||||
|  |   const char* procfs_path_; | ||||||
|  | 
 | ||||||
|  |   // Memory-mapped core dump file at |core_path_|.
 | ||||||
|  |   MemoryMappedFile mapped_core_file_; | ||||||
|  | 
 | ||||||
|  |   // Content of the core dump file.
 | ||||||
|  |   ElfCoreDump core_; | ||||||
|  | 
 | ||||||
|  |   // Thread info found in the core dump file.
 | ||||||
|  |   wasteful_vector<ThreadInfo> thread_infos_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_
 | ||||||
| @ -0,0 +1,113 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_core_dumper_unittest.cc:
 | ||||||
|  | // Unit tests for google_breakpad::LinuxCoreDumoer.
 | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "client/linux/minidump_writer/linux_core_dumper.h" | ||||||
|  | #include "common/linux/tests/crash_generator.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | using namespace google_breakpad; | ||||||
|  | 
 | ||||||
|  | TEST(LinuxCoreDumperTest, BuildProcPath) { | ||||||
|  |   const pid_t pid = getpid(); | ||||||
|  |   const char procfs_path[] = "/procfs_copy"; | ||||||
|  |   LinuxCoreDumper dumper(getpid(), "core_file", procfs_path); | ||||||
|  | 
 | ||||||
|  |   char maps_path[NAME_MAX] = ""; | ||||||
|  |   char maps_path_expected[NAME_MAX]; | ||||||
|  |   snprintf(maps_path_expected, sizeof(maps_path_expected), | ||||||
|  |            "%s/maps", procfs_path); | ||||||
|  |   EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); | ||||||
|  |   EXPECT_STREQ(maps_path_expected, maps_path); | ||||||
|  | 
 | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); | ||||||
|  | 
 | ||||||
|  |   char long_node[NAME_MAX]; | ||||||
|  |   size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1; | ||||||
|  |   memset(long_node, 'a', long_node_len); | ||||||
|  |   long_node[long_node_len] = '\0'; | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { | ||||||
|  |   CrashGenerator crash_generator; | ||||||
|  |   if (!crash_generator.HasDefaultCorePattern()) { | ||||||
|  |     fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " | ||||||
|  |             "is skipped due to non-default core pattern\n"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const unsigned kNumOfThreads = 3; | ||||||
|  |   const unsigned kCrashThread = 1; | ||||||
|  |   const int kCrashSignal = SIGABRT; | ||||||
|  |   pid_t child_pid; | ||||||
|  |   // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in
 | ||||||
|  |   // CrashGenerator is identified and fixed.
 | ||||||
|  |   if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, | ||||||
|  |                                         kCrashSignal, &child_pid)) { | ||||||
|  |     fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " | ||||||
|  |             "is skipped due to no core dump generated\n"); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const string core_file = crash_generator.GetCoreFilePath(); | ||||||
|  |   const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); | ||||||
|  |   LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); | ||||||
|  |   EXPECT_TRUE(dumper.Init()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(dumper.IsPostMortem()); | ||||||
|  | 
 | ||||||
|  |   // These are no-ops and should always return true.
 | ||||||
|  |   EXPECT_TRUE(dumper.ThreadsSuspend()); | ||||||
|  |   EXPECT_TRUE(dumper.ThreadsResume()); | ||||||
|  | 
 | ||||||
|  |   // LinuxCoreDumper cannot determine the crash address and thus it always
 | ||||||
|  |   // sets the crash address to 0.
 | ||||||
|  |   EXPECT_EQ(0U, dumper.crash_address()); | ||||||
|  |   EXPECT_EQ(kCrashSignal, dumper.crash_signal()); | ||||||
|  |   EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), | ||||||
|  |             dumper.crash_thread()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(kNumOfThreads, dumper.threads().size()); | ||||||
|  |   for (unsigned i = 0; i < kNumOfThreads; ++i) { | ||||||
|  |     ThreadInfo info; | ||||||
|  |     EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info)); | ||||||
|  |     const void* stack; | ||||||
|  |     size_t stack_len; | ||||||
|  |     EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer)); | ||||||
|  |     EXPECT_EQ(getpid(), info.ppid); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										335
									
								
								google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										335
									
								
								google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,335 @@ | |||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_dumper.cc: Implement google_breakpad::LinuxDumper.
 | ||||||
|  | // See linux_dumper.h for details.
 | ||||||
|  | 
 | ||||||
|  | // This code deals with the mechanics of getting information about a crashed
 | ||||||
|  | // process. Since this code may run in a compromised address space, the same
 | ||||||
|  | // rules apply as detailed at the top of minidump_writer.h: no libc calls and
 | ||||||
|  | // use the alternative allocator.
 | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/linux_dumper.h" | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/line_reader.h" | ||||||
|  | #include "common/linux/file_id.h" | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "common/linux/memory_mapped_file.h" | ||||||
|  | #include "common/linux/safe_readlink.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | static const char kMappedFileUnsafePrefix[] = "/dev/"; | ||||||
|  | static const char kDeletedSuffix[] = " (deleted)"; | ||||||
|  | 
 | ||||||
|  | inline static bool IsMappedFileOpenUnsafe( | ||||||
|  |     const google_breakpad::MappingInfo& mapping) { | ||||||
|  |   // It is unsafe to attempt to open a mapped file that lives under /dev,
 | ||||||
|  |   // because the semantics of the open may be driver-specific so we'd risk
 | ||||||
|  |   // hanging the crash dumper. And a file in /dev/ almost certainly has no
 | ||||||
|  |   // ELF file identifier anyways.
 | ||||||
|  |   return my_strncmp(mapping.name, | ||||||
|  |                     kMappedFileUnsafePrefix, | ||||||
|  |                     sizeof(kMappedFileUnsafePrefix) - 1) == 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // All interesting auvx entry types are below AT_SYSINFO_EHDR
 | ||||||
|  | #define AT_MAX AT_SYSINFO_EHDR | ||||||
|  | 
 | ||||||
|  | LinuxDumper::LinuxDumper(pid_t pid) | ||||||
|  |     : pid_(pid), | ||||||
|  |       crash_address_(0), | ||||||
|  |       crash_signal_(0), | ||||||
|  |       crash_thread_(0), | ||||||
|  |       threads_(&allocator_, 8), | ||||||
|  |       mappings_(&allocator_), | ||||||
|  |       auxv_(&allocator_, AT_MAX + 1) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | LinuxDumper::~LinuxDumper() { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxDumper::Init() { | ||||||
|  |   return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool | ||||||
|  | LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, | ||||||
|  |                                          bool member, | ||||||
|  |                                          unsigned int mapping_id, | ||||||
|  |                                          uint8_t identifier[sizeof(MDGUID)]) | ||||||
|  | { | ||||||
|  |   assert(!member || mapping_id < mappings_.size()); | ||||||
|  |   my_memset(identifier, 0, sizeof(MDGUID)); | ||||||
|  |   if (IsMappedFileOpenUnsafe(mapping)) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // Special-case linux-gate because it's not a real file.
 | ||||||
|  |   if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) { | ||||||
|  |     void* linux_gate = NULL; | ||||||
|  |     if (pid_ == sys_getpid()) { | ||||||
|  |       linux_gate = reinterpret_cast<void*>(mapping.start_addr); | ||||||
|  |     } else { | ||||||
|  |       linux_gate = allocator_.Alloc(mapping.size); | ||||||
|  |       CopyFromProcess(linux_gate, pid_, | ||||||
|  |                       reinterpret_cast<const void*>(mapping.start_addr), | ||||||
|  |                       mapping.size); | ||||||
|  |     } | ||||||
|  |     return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   char filename[NAME_MAX]; | ||||||
|  |   size_t filename_len = my_strlen(mapping.name); | ||||||
|  |   assert(filename_len < NAME_MAX); | ||||||
|  |   if (filename_len >= NAME_MAX) | ||||||
|  |     return false; | ||||||
|  |   my_memcpy(filename, mapping.name, filename_len); | ||||||
|  |   filename[filename_len] = '\0'; | ||||||
|  |   bool filename_modified = HandleDeletedFileInMapping(filename); | ||||||
|  | 
 | ||||||
|  |   MemoryMappedFile mapped_file(filename); | ||||||
|  |   if (!mapped_file.data())  // Should probably check if size >= ElfW(Ehdr)?
 | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   bool success = | ||||||
|  |       FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); | ||||||
|  |   if (success && member && filename_modified) { | ||||||
|  |     mappings_[mapping_id]->name[filename_len - | ||||||
|  |                                 sizeof(kDeletedSuffix) + 1] = '\0'; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return success; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxDumper::ReadAuxv() { | ||||||
|  |   char auxv_path[NAME_MAX]; | ||||||
|  |   if (!BuildProcPath(auxv_path, pid_, "auxv")) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int fd = sys_open(auxv_path, O_RDONLY, 0); | ||||||
|  |   if (fd < 0) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   elf_aux_entry one_aux_entry; | ||||||
|  |   bool res = false; | ||||||
|  |   while (sys_read(fd, | ||||||
|  |                   &one_aux_entry, | ||||||
|  |                   sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && | ||||||
|  |          one_aux_entry.a_type != AT_NULL) { | ||||||
|  |     if (one_aux_entry.a_type <= AT_MAX) { | ||||||
|  |       auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val; | ||||||
|  |       res = true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   sys_close(fd); | ||||||
|  |   return res; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxDumper::EnumerateMappings() { | ||||||
|  |   char maps_path[NAME_MAX]; | ||||||
|  |   if (!BuildProcPath(maps_path, pid_, "maps")) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // linux_gate_loc is the beginning of the kernel's mapping of
 | ||||||
|  |   // linux-gate.so in the process.  It doesn't actually show up in the
 | ||||||
|  |   // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR
 | ||||||
|  |   // aux vector entry, which gives the information necessary to special
 | ||||||
|  |   // case its entry when creating the list of mappings.
 | ||||||
|  |   // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more
 | ||||||
|  |   // information.
 | ||||||
|  |   const void* linux_gate_loc = | ||||||
|  |       reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]); | ||||||
|  |   // Although the initial executable is usually the first mapping, it's not
 | ||||||
|  |   // guaranteed (see http://crosbug.com/25355); therefore, try to use the
 | ||||||
|  |   // actual entry point to find the mapping.
 | ||||||
|  |   const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]); | ||||||
|  | 
 | ||||||
|  |   const int fd = sys_open(maps_path, O_RDONLY, 0); | ||||||
|  |   if (fd < 0) | ||||||
|  |     return false; | ||||||
|  |   LineReader* const line_reader = new(allocator_) LineReader(fd); | ||||||
|  | 
 | ||||||
|  |   const char* line; | ||||||
|  |   unsigned line_len; | ||||||
|  |   while (line_reader->GetNextLine(&line, &line_len)) { | ||||||
|  |     uintptr_t start_addr, end_addr, offset; | ||||||
|  | 
 | ||||||
|  |     const char* i1 = my_read_hex_ptr(&start_addr, line); | ||||||
|  |     if (*i1 == '-') { | ||||||
|  |       const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); | ||||||
|  |       if (*i2 == ' ') { | ||||||
|  |         const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); | ||||||
|  |         if (*i3 == ' ') { | ||||||
|  |           const char* name = NULL; | ||||||
|  |           // Only copy name if the name is a valid path name, or if
 | ||||||
|  |           // it's the VDSO image.
 | ||||||
|  |           if (((name = my_strchr(line, '/')) == NULL) && | ||||||
|  |               linux_gate_loc && | ||||||
|  |               reinterpret_cast<void*>(start_addr) == linux_gate_loc) { | ||||||
|  |             name = kLinuxGateLibraryName; | ||||||
|  |             offset = 0; | ||||||
|  |           } | ||||||
|  |           // Merge adjacent mappings with the same name into one module,
 | ||||||
|  |           // assuming they're a single library mapped by the dynamic linker
 | ||||||
|  |           if (name && !mappings_.empty()) { | ||||||
|  |             MappingInfo* module = mappings_.back(); | ||||||
|  |             if ((start_addr == module->start_addr + module->size) && | ||||||
|  |                 (my_strlen(name) == my_strlen(module->name)) && | ||||||
|  |                 (my_strncmp(name, module->name, my_strlen(name)) == 0)) { | ||||||
|  |               module->size = end_addr - module->start_addr; | ||||||
|  |               line_reader->PopLine(line_len); | ||||||
|  |               continue; | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |           MappingInfo* const module = new(allocator_) MappingInfo; | ||||||
|  |           my_memset(module, 0, sizeof(MappingInfo)); | ||||||
|  |           module->start_addr = start_addr; | ||||||
|  |           module->size = end_addr - start_addr; | ||||||
|  |           module->offset = offset; | ||||||
|  |           if (name != NULL) { | ||||||
|  |             const unsigned l = my_strlen(name); | ||||||
|  |             if (l < sizeof(module->name)) | ||||||
|  |               my_memcpy(module->name, name, l); | ||||||
|  |           } | ||||||
|  |           // If this is the entry-point mapping, and it's not already the
 | ||||||
|  |           // first one, then we need to make it be first.  This is because
 | ||||||
|  |           // the minidump format assumes the first module is the one that
 | ||||||
|  |           // corresponds to the main executable (as codified in
 | ||||||
|  |           // processor/minidump.cc:MinidumpModuleList::GetMainModule()).
 | ||||||
|  |           if (entry_point_loc && | ||||||
|  |               (entry_point_loc >= | ||||||
|  |                   reinterpret_cast<void*>(module->start_addr)) && | ||||||
|  |               (entry_point_loc < | ||||||
|  |                   reinterpret_cast<void*>(module->start_addr+module->size)) && | ||||||
|  |               !mappings_.empty()) { | ||||||
|  |             // push the module onto the front of the list.
 | ||||||
|  |             mappings_.resize(mappings_.size() + 1); | ||||||
|  |             for (size_t idx = mappings_.size() - 1; idx > 0; idx--) | ||||||
|  |               mappings_[idx] = mappings_[idx - 1]; | ||||||
|  |             mappings_[0] = module; | ||||||
|  |           } else { | ||||||
|  |             mappings_.push_back(module); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     line_reader->PopLine(line_len); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   sys_close(fd); | ||||||
|  | 
 | ||||||
|  |   return !mappings_.empty(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Get information about the stack, given the stack pointer. We don't try to
 | ||||||
|  | // walk the stack since we might not have all the information needed to do
 | ||||||
|  | // unwind. So we just grab, up to, 32k of stack.
 | ||||||
|  | bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, | ||||||
|  |                                uintptr_t int_stack_pointer) { | ||||||
|  |   // Move the stack pointer to the bottom of the page that it's in.
 | ||||||
|  |   const uintptr_t page_size = getpagesize(); | ||||||
|  | 
 | ||||||
|  |   uint8_t* const stack_pointer = | ||||||
|  |       reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); | ||||||
|  | 
 | ||||||
|  |   // The number of bytes of stack which we try to capture.
 | ||||||
|  |   static const ptrdiff_t kStackToCapture = 32 * 1024; | ||||||
|  | 
 | ||||||
|  |   const MappingInfo* mapping = FindMapping(stack_pointer); | ||||||
|  |   if (!mapping) | ||||||
|  |     return false; | ||||||
|  |   const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; | ||||||
|  |   const ptrdiff_t distance_to_end = | ||||||
|  |       static_cast<ptrdiff_t>(mapping->size) - offset; | ||||||
|  |   *stack_len = distance_to_end > kStackToCapture ? | ||||||
|  |       kStackToCapture : distance_to_end; | ||||||
|  |   *stack = stack_pointer; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Find the mapping which the given memory address falls in.
 | ||||||
|  | const MappingInfo* LinuxDumper::FindMapping(const void* address) const { | ||||||
|  |   const uintptr_t addr = (uintptr_t) address; | ||||||
|  | 
 | ||||||
|  |   for (size_t i = 0; i < mappings_.size(); ++i) { | ||||||
|  |     const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr); | ||||||
|  |     if (addr >= start && addr - start < mappings_[i]->size) | ||||||
|  |       return mappings_[i]; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { | ||||||
|  |   static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1; | ||||||
|  | 
 | ||||||
|  |   // Check for ' (deleted)' in |path|.
 | ||||||
|  |   // |path| has to be at least as long as "/x (deleted)".
 | ||||||
|  |   const size_t path_len = my_strlen(path); | ||||||
|  |   if (path_len < kDeletedSuffixLen + 2) | ||||||
|  |     return false; | ||||||
|  |   if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, | ||||||
|  |                  kDeletedSuffixLen) != 0) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Check |path| against the /proc/pid/exe 'symlink'.
 | ||||||
|  |   char exe_link[NAME_MAX]; | ||||||
|  |   char new_path[NAME_MAX]; | ||||||
|  |   if (!BuildProcPath(exe_link, pid_, "exe")) | ||||||
|  |     return false; | ||||||
|  |   if (!SafeReadLink(exe_link, new_path)) | ||||||
|  |     return false; | ||||||
|  |   if (my_strcmp(path, new_path) != 0) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // Check to see if someone actually named their executable 'foo (deleted)'.
 | ||||||
|  |   struct kernel_stat exe_stat; | ||||||
|  |   struct kernel_stat new_path_stat; | ||||||
|  |   if (sys_stat(exe_link, &exe_stat) == 0 && | ||||||
|  |       sys_stat(new_path, &new_path_stat) == 0 && | ||||||
|  |       exe_stat.st_dev == new_path_stat.st_dev && | ||||||
|  |       exe_stat.st_ino == new_path_stat.st_ino) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   my_memcpy(path, exe_link, NAME_MAX); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
							
								
								
									
										210
									
								
								google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								google-breakpad/src/client/linux/minidump_writer/linux_dumper.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,210 @@ | |||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_dumper.h: Define the google_breakpad::LinuxDumper class, which
 | ||||||
|  | // is a base class for extracting information of a crashed process. It
 | ||||||
|  | // was originally a complete implementation using the ptrace API, but
 | ||||||
|  | // has been refactored to allow derived implementations supporting both
 | ||||||
|  | // ptrace and core dump. A portion of the original implementation is now
 | ||||||
|  | // in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for
 | ||||||
|  | // details).
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ | ||||||
|  | 
 | ||||||
|  | #include <elf.h> | ||||||
|  | #include <linux/limits.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/user.h> | ||||||
|  | 
 | ||||||
|  | #include "common/memory.h" | ||||||
|  | #include "google_breakpad/common/minidump_format.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | #if defined(__i386) || defined(__x86_64) | ||||||
|  | typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // Typedef for our parsing of the auxv variables in /proc/pid/auxv.
 | ||||||
|  | #if defined(__i386) || defined(__ARM_EABI__) | ||||||
|  | typedef Elf32_auxv_t elf_aux_entry; | ||||||
|  | #elif defined(__x86_64) | ||||||
|  | typedef Elf64_auxv_t elf_aux_entry; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | typedef typeof(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t; | ||||||
|  | 
 | ||||||
|  | // When we find the VDSO mapping in the process's address space, this
 | ||||||
|  | // is the name we use for it when writing it to the minidump.
 | ||||||
|  | // This should always be less than NAME_MAX!
 | ||||||
|  | const char kLinuxGateLibraryName[] = "linux-gate.so"; | ||||||
|  | 
 | ||||||
|  | // We produce one of these structures for each thread in the crashed process.
 | ||||||
|  | struct ThreadInfo { | ||||||
|  |   pid_t tgid;   // thread group id
 | ||||||
|  |   pid_t ppid;   // parent process
 | ||||||
|  | 
 | ||||||
|  |   uintptr_t stack_pointer;  // thread stack pointer
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #if defined(__i386) || defined(__x86_64) | ||||||
|  |   user_regs_struct regs; | ||||||
|  |   user_fpregs_struct fpregs; | ||||||
|  |   static const unsigned kNumDebugRegisters = 8; | ||||||
|  |   debugreg_t dregs[8]; | ||||||
|  | #if defined(__i386) | ||||||
|  |   user_fpxregs_struct fpxregs; | ||||||
|  | #endif  // defined(__i386)
 | ||||||
|  | 
 | ||||||
|  | #elif defined(__ARM_EABI__) | ||||||
|  |   // Mimicking how strace does this(see syscall.c, search for GETREGS)
 | ||||||
|  |   struct user_regs regs; | ||||||
|  |   struct user_fpregs fpregs; | ||||||
|  | #endif | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // One of these is produced for each mapping in the process (i.e. line in
 | ||||||
|  | // /proc/$x/maps).
 | ||||||
|  | struct MappingInfo { | ||||||
|  |   uintptr_t start_addr; | ||||||
|  |   size_t size; | ||||||
|  |   size_t offset;  // offset into the backed file.
 | ||||||
|  |   char name[NAME_MAX]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class LinuxDumper { | ||||||
|  |  public: | ||||||
|  |   explicit LinuxDumper(pid_t pid); | ||||||
|  | 
 | ||||||
|  |   virtual ~LinuxDumper(); | ||||||
|  | 
 | ||||||
|  |   // Parse the data for |threads| and |mappings|.
 | ||||||
|  |   virtual bool Init(); | ||||||
|  | 
 | ||||||
|  |   // Return true if the dumper performs a post-mortem dump.
 | ||||||
|  |   virtual bool IsPostMortem() const = 0; | ||||||
|  | 
 | ||||||
|  |   // Suspend/resume all threads in the given process.
 | ||||||
|  |   virtual bool ThreadsSuspend() = 0; | ||||||
|  |   virtual bool ThreadsResume() = 0; | ||||||
|  | 
 | ||||||
|  |   // Read information about the |index|-th thread of |threads_|.
 | ||||||
|  |   // Returns true on success. One must have called |ThreadsSuspend| first.
 | ||||||
|  |   virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0; | ||||||
|  | 
 | ||||||
|  |   // These are only valid after a call to |Init|.
 | ||||||
|  |   const wasteful_vector<pid_t> &threads() { return threads_; } | ||||||
|  |   const wasteful_vector<MappingInfo*> &mappings() { return mappings_; } | ||||||
|  |   const MappingInfo* FindMapping(const void* address) const; | ||||||
|  |   const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; } | ||||||
|  | 
 | ||||||
|  |   // Find a block of memory to take as the stack given the top of stack pointer.
 | ||||||
|  |   //   stack: (output) the lowest address in the memory area
 | ||||||
|  |   //   stack_len: (output) the length of the memory area
 | ||||||
|  |   //   stack_top: the current top of the stack
 | ||||||
|  |   bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); | ||||||
|  | 
 | ||||||
|  |   PageAllocator* allocator() { return &allocator_; } | ||||||
|  | 
 | ||||||
|  |   // Copy content of |length| bytes from a given process |child|,
 | ||||||
|  |   // starting from |src|, into |dest|.
 | ||||||
|  |   virtual void CopyFromProcess(void* dest, pid_t child, const void* src, | ||||||
|  |                                size_t length) = 0; | ||||||
|  | 
 | ||||||
|  |   // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
 | ||||||
|  |   // |path| is a character array of at least NAME_MAX bytes to return the
 | ||||||
|  |   // result.|node| is the final node without any slashes. Returns true on
 | ||||||
|  |   // success.
 | ||||||
|  |   virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0; | ||||||
|  | 
 | ||||||
|  |   // Generate a File ID from the .text section of a mapped entry.
 | ||||||
|  |   // If not a member, mapping_id is ignored.
 | ||||||
|  |   bool ElfFileIdentifierForMapping(const MappingInfo& mapping, | ||||||
|  |                                    bool member, | ||||||
|  |                                    unsigned int mapping_id, | ||||||
|  |                                    uint8_t identifier[sizeof(MDGUID)]); | ||||||
|  | 
 | ||||||
|  |   uintptr_t crash_address() const { return crash_address_; } | ||||||
|  |   void set_crash_address(uintptr_t crash_address) { | ||||||
|  |     crash_address_ = crash_address; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int crash_signal() const { return crash_signal_; } | ||||||
|  |   void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; } | ||||||
|  | 
 | ||||||
|  |   pid_t crash_thread() const { return crash_thread_; } | ||||||
|  |   void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } | ||||||
|  | 
 | ||||||
|  |  protected: | ||||||
|  |   bool ReadAuxv(); | ||||||
|  | 
 | ||||||
|  |   virtual bool EnumerateMappings(); | ||||||
|  | 
 | ||||||
|  |   virtual bool EnumerateThreads() = 0; | ||||||
|  | 
 | ||||||
|  |   // For the case where a running program has been deleted, it'll show up in
 | ||||||
|  |   // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then
 | ||||||
|  |   // see if '/path/to/program (deleted)' matches /proc/pid/exe and return
 | ||||||
|  |   // /proc/pid/exe in |path| so ELF identifier generation works correctly. This
 | ||||||
|  |   // also checks to see if '/path/to/program (deleted)' exists, so it does not
 | ||||||
|  |   // get fooled by a poorly named binary.
 | ||||||
|  |   // For programs that don't end with ' (deleted)', this is a no-op.
 | ||||||
|  |   // This assumes |path| is a buffer with length NAME_MAX.
 | ||||||
|  |   // Returns true if |path| is modified.
 | ||||||
|  |   bool HandleDeletedFileInMapping(char* path) const; | ||||||
|  | 
 | ||||||
|  |    // ID of the crashed process.
 | ||||||
|  |   const pid_t pid_; | ||||||
|  | 
 | ||||||
|  |   // Virtual address at which the process crashed.
 | ||||||
|  |   uintptr_t crash_address_; | ||||||
|  | 
 | ||||||
|  |   // Signal that terminated the crashed process.
 | ||||||
|  |   int crash_signal_; | ||||||
|  | 
 | ||||||
|  |   // ID of the crashed thread.
 | ||||||
|  |   pid_t crash_thread_; | ||||||
|  | 
 | ||||||
|  |   mutable PageAllocator allocator_; | ||||||
|  | 
 | ||||||
|  |   // IDs of all the threads.
 | ||||||
|  |   wasteful_vector<pid_t> threads_; | ||||||
|  | 
 | ||||||
|  |   // Info from /proc/<pid>/maps.
 | ||||||
|  |   wasteful_vector<MappingInfo*> mappings_; | ||||||
|  | 
 | ||||||
|  |   // Info from /proc/<pid>/auxv
 | ||||||
|  |   wasteful_vector<elf_aux_val_t> auxv_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
 | ||||||
| @ -0,0 +1,90 @@ | |||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | //
 | ||||||
|  | // Helper program for the linux_dumper class, which creates a bunch of
 | ||||||
|  | // threads. The first word of each thread's stack is set to the thread
 | ||||||
|  | // id.
 | ||||||
|  | 
 | ||||||
|  | #include <pthread.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <sys/syscall.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include "common/scoped_ptr.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | #if defined(__ARM_EABI__) | ||||||
|  | #define TID_PTR_REGISTER "r3" | ||||||
|  | #elif defined(__i386) | ||||||
|  | #define TID_PTR_REGISTER "ecx" | ||||||
|  | #elif defined(__x86_64) | ||||||
|  | #define TID_PTR_REGISTER "rcx" | ||||||
|  | #else | ||||||
|  | #error This test has not been ported to this platform. | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | void *thread_function(void *data) { | ||||||
|  |   int pipefd = *static_cast<int *>(data); | ||||||
|  |   volatile pid_t thread_id = syscall(__NR_gettid); | ||||||
|  |   // Signal parent that a thread has started.
 | ||||||
|  |   uint8_t byte = 1; | ||||||
|  |   if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) { | ||||||
|  |     perror("ERROR: parent notification failed"); | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |   register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id; | ||||||
|  |   while (true) | ||||||
|  |     asm volatile ("" : : "r" (thread_id_ptr)); | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char *argv[]) { | ||||||
|  |   if (argc < 3) { | ||||||
|  |     fprintf(stderr, | ||||||
|  |             "usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n"); | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   int pipefd = atoi(argv[1]); | ||||||
|  |   int num_threads = atoi(argv[2]); | ||||||
|  |   if (num_threads < 1) { | ||||||
|  |     fprintf(stderr, "ERROR: number of threads is 0"); | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]); | ||||||
|  |   pthread_attr_t thread_attributes; | ||||||
|  |   pthread_attr_init(&thread_attributes); | ||||||
|  |   pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED); | ||||||
|  |   for (int i = 1; i < num_threads; i++) { | ||||||
|  |     pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd); | ||||||
|  |   } | ||||||
|  |   thread_function(&pipefd); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
| @ -0,0 +1,291 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper.
 | ||||||
|  | // See linux_ptrace_dumper.h for detals.
 | ||||||
|  | // This class was originally splitted from google_breakpad::LinuxDumper.
 | ||||||
|  | 
 | ||||||
|  | // This code deals with the mechanics of getting information about a crashed
 | ||||||
|  | // process. Since this code may run in a compromised address space, the same
 | ||||||
|  | // rules apply as detailed at the top of minidump_writer.h: no libc calls and
 | ||||||
|  | // use the alternative allocator.
 | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/linux_ptrace_dumper.h" | ||||||
|  | 
 | ||||||
|  | #include <asm/ptrace.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/ptrace.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/directory_reader.h" | ||||||
|  | #include "client/linux/minidump_writer/line_reader.h" | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | // Suspends a thread by attaching to it.
 | ||||||
|  | static bool SuspendThread(pid_t pid) { | ||||||
|  |   // This may fail if the thread has just died or debugged.
 | ||||||
|  |   errno = 0; | ||||||
|  |   if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && | ||||||
|  |       errno != 0) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   while (sys_waitpid(pid, NULL, __WALL) < 0) { | ||||||
|  |     if (errno != EINTR) { | ||||||
|  |       sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | #if defined(__i386) || defined(__x86_64) | ||||||
|  |   // On x86, the stack pointer is NULL or -1, when executing trusted code in
 | ||||||
|  |   // the seccomp sandbox. Not only does this cause difficulties down the line
 | ||||||
|  |   // when trying to dump the thread's stack, it also results in the minidumps
 | ||||||
|  |   // containing information about the trusted threads. This information is
 | ||||||
|  |   // generally completely meaningless and just pollutes the minidumps.
 | ||||||
|  |   // We thus test the stack pointer and exclude any threads that are part of
 | ||||||
|  |   // the seccomp sandbox's trusted code.
 | ||||||
|  |   user_regs_struct regs; | ||||||
|  |   if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || | ||||||
|  | #if defined(__i386) | ||||||
|  |       !regs.esp | ||||||
|  | #elif defined(__x86_64) | ||||||
|  |       !regs.rsp | ||||||
|  | #endif | ||||||
|  |       ) { | ||||||
|  |     sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Resumes a thread by detaching from it.
 | ||||||
|  | static bool ResumeThread(pid_t pid) { | ||||||
|  |   return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) | ||||||
|  |     : LinuxDumper(pid), | ||||||
|  |       threads_suspended_(false) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid, | ||||||
|  |                                       const char* node) const { | ||||||
|  |   if (!path || !node || pid <= 0) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   size_t node_len = my_strlen(node); | ||||||
|  |   if (node_len == 0) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   const unsigned pid_len = my_uint_len(pid); | ||||||
|  |   const size_t total_length = 6 + pid_len + 1 + node_len; | ||||||
|  |   if (total_length >= NAME_MAX) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   my_memcpy(path, "/proc/", 6); | ||||||
|  |   my_uitos(path + 6, pid, pid_len); | ||||||
|  |   path[6 + pid_len] = '/'; | ||||||
|  |   my_memcpy(path + 6 + pid_len + 1, node, node_len); | ||||||
|  |   path[total_length] = '\0'; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, | ||||||
|  |                                         const void* src, size_t length) { | ||||||
|  |   unsigned long tmp = 55; | ||||||
|  |   size_t done = 0; | ||||||
|  |   static const size_t word_size = sizeof(tmp); | ||||||
|  |   uint8_t* const local = (uint8_t*) dest; | ||||||
|  |   uint8_t* const remote = (uint8_t*) src; | ||||||
|  | 
 | ||||||
|  |   while (done < length) { | ||||||
|  |     const size_t l = (length - done > word_size) ? word_size : (length - done); | ||||||
|  |     if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { | ||||||
|  |       tmp = 0; | ||||||
|  |     } | ||||||
|  |     my_memcpy(local + done, &tmp, l); | ||||||
|  |     done += l; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read thread info from /proc/$pid/status.
 | ||||||
|  | // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable,
 | ||||||
|  | // these members are set to -1. Returns true iff all three members are
 | ||||||
|  | // available.
 | ||||||
|  | bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { | ||||||
|  |   if (index >= threads_.size()) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   pid_t tid = threads_[index]; | ||||||
|  | 
 | ||||||
|  |   assert(info != NULL); | ||||||
|  |   char status_path[NAME_MAX]; | ||||||
|  |   if (!BuildProcPath(status_path, tid, "status")) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   const int fd = sys_open(status_path, O_RDONLY, 0); | ||||||
|  |   if (fd < 0) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   LineReader* const line_reader = new(allocator_) LineReader(fd); | ||||||
|  |   const char* line; | ||||||
|  |   unsigned line_len; | ||||||
|  | 
 | ||||||
|  |   info->ppid = info->tgid = -1; | ||||||
|  | 
 | ||||||
|  |   while (line_reader->GetNextLine(&line, &line_len)) { | ||||||
|  |     if (my_strncmp("Tgid:\t", line, 6) == 0) { | ||||||
|  |       my_strtoui(&info->tgid, line + 6); | ||||||
|  |     } else if (my_strncmp("PPid:\t", line, 6) == 0) { | ||||||
|  |       my_strtoui(&info->ppid, line + 6); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     line_reader->PopLine(line_len); | ||||||
|  |   } | ||||||
|  |   sys_close(fd); | ||||||
|  | 
 | ||||||
|  |   if (info->ppid == -1 || info->tgid == -1) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | #if defined(__i386) | ||||||
|  |   if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) | ||||||
|  |     return false; | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #if defined(__i386) || defined(__x86_64) | ||||||
|  |   for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { | ||||||
|  |     if (sys_ptrace( | ||||||
|  |         PTRACE_PEEKUSER, tid, | ||||||
|  |         reinterpret_cast<void*> (offsetof(struct user, | ||||||
|  |                                           u_debugreg[0]) + i * | ||||||
|  |                                  sizeof(debugreg_t)), | ||||||
|  |         &info->dregs[i]) == -1) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   const uint8_t* stack_pointer; | ||||||
|  | #if defined(__i386) | ||||||
|  |   my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); | ||||||
|  | #elif defined(__x86_64) | ||||||
|  |   my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); | ||||||
|  | #elif defined(__ARM_EABI__) | ||||||
|  |   my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); | ||||||
|  | #else | ||||||
|  | #error "This code hasn't been ported to your platform yet." | ||||||
|  | #endif | ||||||
|  |   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxPtraceDumper::IsPostMortem() const { | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxPtraceDumper::ThreadsSuspend() { | ||||||
|  |   if (threads_suspended_) | ||||||
|  |     return true; | ||||||
|  |   for (size_t i = 0; i < threads_.size(); ++i) { | ||||||
|  |     if (!SuspendThread(threads_[i])) { | ||||||
|  |       // If the thread either disappeared before we could attach to it, or if
 | ||||||
|  |       // it was part of the seccomp sandbox's trusted code, it is OK to
 | ||||||
|  |       // silently drop it from the minidump.
 | ||||||
|  |       my_memmove(&threads_[i], &threads_[i+1], | ||||||
|  |                  (threads_.size() - i - 1) * sizeof(threads_[i])); | ||||||
|  |       threads_.resize(threads_.size() - 1); | ||||||
|  |       --i; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   threads_suspended_ = true; | ||||||
|  |   return threads_.size() > 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool LinuxPtraceDumper::ThreadsResume() { | ||||||
|  |   if (!threads_suspended_) | ||||||
|  |     return false; | ||||||
|  |   bool good = true; | ||||||
|  |   for (size_t i = 0; i < threads_.size(); ++i) | ||||||
|  |     good &= ResumeThread(threads_[i]); | ||||||
|  |   threads_suspended_ = false; | ||||||
|  |   return good; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Parse /proc/$pid/task to list all the threads of the process identified by
 | ||||||
|  | // pid.
 | ||||||
|  | bool LinuxPtraceDumper::EnumerateThreads() { | ||||||
|  |   char task_path[NAME_MAX]; | ||||||
|  |   if (!BuildProcPath(task_path, pid_, "task")) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); | ||||||
|  |   if (fd < 0) | ||||||
|  |     return false; | ||||||
|  |   DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); | ||||||
|  | 
 | ||||||
|  |   // The directory may contain duplicate entries which we filter by assuming
 | ||||||
|  |   // that they are consecutive.
 | ||||||
|  |   int last_tid = -1; | ||||||
|  |   const char* dent_name; | ||||||
|  |   while (dir_reader->GetNextEntry(&dent_name)) { | ||||||
|  |     if (my_strcmp(dent_name, ".") && | ||||||
|  |         my_strcmp(dent_name, "..")) { | ||||||
|  |       int tid = 0; | ||||||
|  |       if (my_strtoui(&tid, dent_name) && | ||||||
|  |           last_tid != tid) { | ||||||
|  |         last_tid = tid; | ||||||
|  |         threads_.push_back(tid); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     dir_reader->PopEntry(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   sys_close(fd); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
| @ -0,0 +1,92 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper
 | ||||||
|  | // class, which is derived from google_breakpad::LinuxDumper to extract
 | ||||||
|  | // information from a crashed process via ptrace.
 | ||||||
|  | // This class was originally splitted from google_breakpad::LinuxDumper.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/linux_dumper.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class LinuxPtraceDumper : public LinuxDumper { | ||||||
|  |  public: | ||||||
|  |   // Constructs a dumper for extracting information of a given process
 | ||||||
|  |   // with a process ID of |pid|.
 | ||||||
|  |   explicit LinuxPtraceDumper(pid_t pid); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::BuildProcPath().
 | ||||||
|  |   // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).
 | ||||||
|  |   // |path| is a character array of at least NAME_MAX bytes to return the
 | ||||||
|  |   // result. |node| is the final node without any slashes. Returns true on
 | ||||||
|  |   // success.
 | ||||||
|  |   virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::CopyFromProcess().
 | ||||||
|  |   // Copies content of |length| bytes from a given process |child|,
 | ||||||
|  |   // starting from |src|, into |dest|. This method uses ptrace to extract
 | ||||||
|  |   // the content from the target process.
 | ||||||
|  |   virtual void CopyFromProcess(void* dest, pid_t child, const void* src, | ||||||
|  |                                size_t length); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::GetThreadInfoByIndex().
 | ||||||
|  |   // Reads information about the |index|-th thread of |threads_|.
 | ||||||
|  |   // Returns true on success. One must have called |ThreadsSuspend| first.
 | ||||||
|  |   virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::IsPostMortem().
 | ||||||
|  |   // Always returns false to indicate this dumper performs a dump of
 | ||||||
|  |   // a crashed process via ptrace.
 | ||||||
|  |   virtual bool IsPostMortem() const; | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::ThreadsSuspend().
 | ||||||
|  |   // Suspends all threads in the given process. Returns true on success.
 | ||||||
|  |   virtual bool ThreadsSuspend(); | ||||||
|  | 
 | ||||||
|  |   // Implements LinuxDumper::ThreadsResume().
 | ||||||
|  |   // Resumes all threads in the given process. Returns true on success.
 | ||||||
|  |   virtual bool ThreadsResume(); | ||||||
|  | 
 | ||||||
|  |  protected: | ||||||
|  |   // Implements LinuxDumper::EnumerateThreads().
 | ||||||
|  |   // Enumerates all threads of the given process into |threads_|.
 | ||||||
|  |   virtual bool EnumerateThreads(); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // Set to true if all threads of the crashed process are suspended.
 | ||||||
|  |   bool threads_suspended_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_
 | ||||||
| @ -0,0 +1,440 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // linux_ptrace_dumper_unittest.cc:
 | ||||||
|  | // Unit tests for google_breakpad::LinuxPtraceDumoer.
 | ||||||
|  | //
 | ||||||
|  | // This file was renamed from linux_dumper_unittest.cc and modified due
 | ||||||
|  | // to LinuxDumper being splitted into two classes.
 | ||||||
|  | 
 | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <signal.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <sys/poll.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "client/linux/minidump_writer/linux_ptrace_dumper.h" | ||||||
|  | #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "common/linux/file_id.h" | ||||||
|  | #include "common/linux/ignore_ret.h" | ||||||
|  | #include "common/linux/safe_readlink.h" | ||||||
|  | #include "common/memory.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | using namespace google_breakpad; | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | typedef testing::Test LinuxPtraceDumperTest; | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
|  | 
 | ||||||
|  | TEST(LinuxPtraceDumperTest, Setup) { | ||||||
|  |   LinuxPtraceDumper dumper(getpid()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LinuxPtraceDumperTest, FindMappings) { | ||||||
|  |   LinuxPtraceDumper dumper(getpid()); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid))); | ||||||
|  |   ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf))); | ||||||
|  |   ASSERT_FALSE(dumper.FindMapping(NULL)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LinuxPtraceDumperTest, ThreadList) { | ||||||
|  |   LinuxPtraceDumper dumper(getpid()); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_GE(dumper.threads().size(), (size_t)1); | ||||||
|  |   bool found = false; | ||||||
|  |   for (size_t i = 0; i < dumper.threads().size(); ++i) { | ||||||
|  |     if (dumper.threads()[i] == getpid()) { | ||||||
|  |       ASSERT_FALSE(found); | ||||||
|  |       found = true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   ASSERT_TRUE(found); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Helper stack class to close a file descriptor and unmap
 | ||||||
|  | // a mmap'ed mapping.
 | ||||||
|  | class StackHelper { | ||||||
|  |  public: | ||||||
|  |   StackHelper(int fd, char* mapping, size_t size) | ||||||
|  |     : fd_(fd), mapping_(mapping), size_(size) {} | ||||||
|  |   ~StackHelper() { | ||||||
|  |     munmap(mapping_, size_); | ||||||
|  |     close(fd_); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   int fd_; | ||||||
|  |   char* mapping_; | ||||||
|  |   size_t size_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | TEST(LinuxPtraceDumperTest, MergedMappings) { | ||||||
|  |   string helper_path(GetHelperBinary()); | ||||||
|  |   if (helper_path.empty()) { | ||||||
|  |     FAIL() << "Couldn't find helper binary"; | ||||||
|  |     exit(1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // mmap two segments out of the helper binary, one
 | ||||||
|  |   // enclosed in the other, but with different protections.
 | ||||||
|  |   const size_t kPageSize = sysconf(_SC_PAGESIZE); | ||||||
|  |   const size_t kMappingSize = 3 * kPageSize; | ||||||
|  |   int fd = open(helper_path.c_str(), O_RDONLY); | ||||||
|  |   ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path | ||||||
|  |                     << ", Error: " << strerror(errno); | ||||||
|  |   char* mapping = | ||||||
|  |     reinterpret_cast<char*>(mmap(NULL, | ||||||
|  |                                  kMappingSize, | ||||||
|  |                                  PROT_READ, | ||||||
|  |                                  MAP_SHARED, | ||||||
|  |                                  fd, | ||||||
|  |                                  0)); | ||||||
|  |   ASSERT_TRUE(mapping); | ||||||
|  | 
 | ||||||
|  |   const uintptr_t kMappingAddress = reinterpret_cast<uintptr_t>(mapping); | ||||||
|  | 
 | ||||||
|  |   // Ensure that things get cleaned up.
 | ||||||
|  |   StackHelper helper(fd, mapping, kMappingSize); | ||||||
|  | 
 | ||||||
|  |   // Carve a page out of the first mapping with different permissions.
 | ||||||
|  |   char* inside_mapping =  reinterpret_cast<char*>( | ||||||
|  |       mmap(mapping + 2 *kPageSize, | ||||||
|  |            kPageSize, | ||||||
|  |            PROT_NONE, | ||||||
|  |            MAP_SHARED | MAP_FIXED, | ||||||
|  |            fd, | ||||||
|  |            // Map a different offset just to
 | ||||||
|  |            // better test real-world conditions.
 | ||||||
|  |            kPageSize)); | ||||||
|  |   ASSERT_TRUE(inside_mapping); | ||||||
|  | 
 | ||||||
|  |   // Now check that LinuxPtraceDumper interpreted the mappings properly.
 | ||||||
|  |   LinuxPtraceDumper dumper(getpid()); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  |   int mapping_count = 0; | ||||||
|  |   for (unsigned i = 0; i < dumper.mappings().size(); ++i) { | ||||||
|  |     const MappingInfo& mapping = *dumper.mappings()[i]; | ||||||
|  |     if (strcmp(mapping.name, helper_path.c_str()) == 0) { | ||||||
|  |       // This mapping should encompass the entire original mapped
 | ||||||
|  |       // range.
 | ||||||
|  |       EXPECT_EQ(kMappingAddress, mapping.start_addr); | ||||||
|  |       EXPECT_EQ(kMappingSize, mapping.size); | ||||||
|  |       EXPECT_EQ(0U, mapping.offset); | ||||||
|  |       mapping_count++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   EXPECT_EQ(1, mapping_count); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { | ||||||
|  |   static const int kNumberOfThreadsInHelperProgram = 5; | ||||||
|  |   char kNumberOfThreadsArgument[2]; | ||||||
|  |   sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); | ||||||
|  | 
 | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   pid_t child_pid = fork(); | ||||||
|  |   if (child_pid == 0) { | ||||||
|  |     // In child process.
 | ||||||
|  |     close(fds[0]); | ||||||
|  | 
 | ||||||
|  |     string helper_path(GetHelperBinary()); | ||||||
|  |     if (helper_path.empty()) { | ||||||
|  |       FAIL() << "Couldn't find helper binary"; | ||||||
|  |       exit(1); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Pass the pipe fd and the number of threads as arguments.
 | ||||||
|  |     char pipe_fd_string[8]; | ||||||
|  |     sprintf(pipe_fd_string, "%d", fds[1]); | ||||||
|  |     execl(helper_path.c_str(), | ||||||
|  |           "linux_dumper_unittest_helper", | ||||||
|  |           pipe_fd_string, | ||||||
|  |           kNumberOfThreadsArgument, | ||||||
|  |           NULL); | ||||||
|  |     // Kill if we get here.
 | ||||||
|  |     printf("Errno from exec: %d", errno); | ||||||
|  |     FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno); | ||||||
|  |     exit(0); | ||||||
|  |   } | ||||||
|  |   close(fds[1]); | ||||||
|  | 
 | ||||||
|  |   // Wait for all child threads to indicate that they have started
 | ||||||
|  |   for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { | ||||||
|  |     struct pollfd pfd; | ||||||
|  |     memset(&pfd, 0, sizeof(pfd)); | ||||||
|  |     pfd.fd = fds[0]; | ||||||
|  |     pfd.events = POLLIN | POLLERR; | ||||||
|  | 
 | ||||||
|  |     const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); | ||||||
|  |     ASSERT_EQ(1, r); | ||||||
|  |     ASSERT_TRUE(pfd.revents & POLLIN); | ||||||
|  |     uint8_t junk; | ||||||
|  |     ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),  | ||||||
|  |               static_cast<ssize_t>(sizeof(junk))); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   // There is a race here because we may stop a child thread before
 | ||||||
|  |   // it is actually running the busy loop. Empirically this sleep
 | ||||||
|  |   // is sufficient to avoid the race.
 | ||||||
|  |   usleep(100000); | ||||||
|  | 
 | ||||||
|  |   // Children are ready now.
 | ||||||
|  |   LinuxPtraceDumper dumper(child_pid); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  |   EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size()); | ||||||
|  |   EXPECT_TRUE(dumper.ThreadsSuspend()); | ||||||
|  | 
 | ||||||
|  |   ThreadInfo one_thread; | ||||||
|  |   for (size_t i = 0; i < dumper.threads().size(); ++i) { | ||||||
|  |     EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread)); | ||||||
|  |     const void* stack; | ||||||
|  |     size_t stack_len; | ||||||
|  |     EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, | ||||||
|  |         one_thread.stack_pointer)); | ||||||
|  |     // In the helper program, we stored a pointer to the thread id in a
 | ||||||
|  |     // specific register. Check that we can recover its value.
 | ||||||
|  | #if defined(__ARM_EABI__) | ||||||
|  |     pid_t *process_tid_location = (pid_t *)(one_thread.regs.uregs[3]); | ||||||
|  | #elif defined(__i386) | ||||||
|  |     pid_t *process_tid_location = (pid_t *)(one_thread.regs.ecx); | ||||||
|  | #elif defined(__x86_64) | ||||||
|  |     pid_t *process_tid_location = (pid_t *)(one_thread.regs.rcx); | ||||||
|  | #else | ||||||
|  | #error This test has not been ported to this platform. | ||||||
|  | #endif | ||||||
|  |     pid_t one_thread_id; | ||||||
|  |     dumper.CopyFromProcess(&one_thread_id, | ||||||
|  |                            dumper.threads()[i], | ||||||
|  |                            process_tid_location, | ||||||
|  |                            4); | ||||||
|  |     EXPECT_EQ(dumper.threads()[i], one_thread_id); | ||||||
|  |   } | ||||||
|  |   EXPECT_TRUE(dumper.ThreadsResume()); | ||||||
|  |   kill(child_pid, SIGKILL); | ||||||
|  | 
 | ||||||
|  |   // Reap child
 | ||||||
|  |   int status; | ||||||
|  |   ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0))); | ||||||
|  |   ASSERT_TRUE(WIFSIGNALED(status)); | ||||||
|  |   ASSERT_EQ(SIGKILL, WTERMSIG(status)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LinuxPtraceDumperTest, BuildProcPath) { | ||||||
|  |   const pid_t pid = getpid(); | ||||||
|  |   LinuxPtraceDumper dumper(pid); | ||||||
|  | 
 | ||||||
|  |   char maps_path[NAME_MAX] = ""; | ||||||
|  |   char maps_path_expected[NAME_MAX]; | ||||||
|  |   snprintf(maps_path_expected, sizeof(maps_path_expected), | ||||||
|  |            "/proc/%d/maps", pid); | ||||||
|  |   EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); | ||||||
|  |   EXPECT_STREQ(maps_path_expected, maps_path); | ||||||
|  | 
 | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps")); | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); | ||||||
|  | 
 | ||||||
|  |   char long_node[NAME_MAX]; | ||||||
|  |   size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1; | ||||||
|  |   memset(long_node, 'a', long_node_len); | ||||||
|  |   long_node[long_node_len] = '\0'; | ||||||
|  |   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #if !defined(__ARM_EABI__) | ||||||
|  | // Ensure that the linux-gate VDSO is included in the mapping list.
 | ||||||
|  | TEST(LinuxPtraceDumperTest, MappingsIncludeLinuxGate) { | ||||||
|  |   LinuxPtraceDumper dumper(getpid()); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  | 
 | ||||||
|  |   void* linux_gate_loc = | ||||||
|  |     reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]); | ||||||
|  |   ASSERT_TRUE(linux_gate_loc); | ||||||
|  |   bool found_linux_gate = false; | ||||||
|  | 
 | ||||||
|  |   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||||
|  |   const MappingInfo* mapping; | ||||||
|  |   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||||
|  |     mapping = mappings[i]; | ||||||
|  |     if (!strcmp(mapping->name, kLinuxGateLibraryName)) { | ||||||
|  |       found_linux_gate = true; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   EXPECT_TRUE(found_linux_gate); | ||||||
|  |   EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr)); | ||||||
|  |   EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Ensure that the linux-gate VDSO can generate a non-zeroed File ID.
 | ||||||
|  | TEST(LinuxPtraceDumperTest, LinuxGateMappingID) { | ||||||
|  |   LinuxPtraceDumper dumper(getpid()); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  | 
 | ||||||
|  |   bool found_linux_gate = false; | ||||||
|  |   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||||
|  |   unsigned index = 0; | ||||||
|  |   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||||
|  |     if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { | ||||||
|  |       found_linux_gate = true; | ||||||
|  |       index = i; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   ASSERT_TRUE(found_linux_gate); | ||||||
|  | 
 | ||||||
|  |   uint8_t identifier[sizeof(MDGUID)]; | ||||||
|  |   ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], | ||||||
|  |                                                  true, | ||||||
|  |                                                  index, | ||||||
|  |                                                  identifier)); | ||||||
|  |   uint8_t empty_identifier[sizeof(MDGUID)]; | ||||||
|  |   memset(empty_identifier, 0, sizeof(empty_identifier)); | ||||||
|  |   EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Ensure that the linux-gate VDSO can generate a non-zeroed File ID
 | ||||||
|  | // from a child process.
 | ||||||
|  | TEST(LinuxPtraceDumperTest, LinuxGateMappingIDChild) { | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   // Fork a child so ptrace works.
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     // Now wait forever for the parent.
 | ||||||
|  |     char b; | ||||||
|  |     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   LinuxPtraceDumper dumper(child); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  | 
 | ||||||
|  |   bool found_linux_gate = false; | ||||||
|  |   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||||
|  |   unsigned index = 0; | ||||||
|  |   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||||
|  |     if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { | ||||||
|  |       found_linux_gate = true; | ||||||
|  |       index = i; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   ASSERT_TRUE(found_linux_gate); | ||||||
|  | 
 | ||||||
|  |   // Need to suspend the child so ptrace actually works.
 | ||||||
|  |   ASSERT_TRUE(dumper.ThreadsSuspend()); | ||||||
|  |   uint8_t identifier[sizeof(MDGUID)]; | ||||||
|  |   ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], | ||||||
|  |                                                  true, | ||||||
|  |                                                  index, | ||||||
|  |                                                  identifier)); | ||||||
|  |   uint8_t empty_identifier[sizeof(MDGUID)]; | ||||||
|  |   memset(empty_identifier, 0, sizeof(empty_identifier)); | ||||||
|  |   EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier))); | ||||||
|  |   EXPECT_TRUE(dumper.ThreadsResume()); | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | TEST(LinuxPtraceDumperTest, FileIDsMatch) { | ||||||
|  |   // Calculate the File ID of our binary using both
 | ||||||
|  |   // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping
 | ||||||
|  |   // and ensure that we get the same result from both.
 | ||||||
|  |   char exe_name[PATH_MAX]; | ||||||
|  |   ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); | ||||||
|  | 
 | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   // Fork a child so ptrace works.
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     // Now wait forever for the parent.
 | ||||||
|  |     char b; | ||||||
|  |     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   LinuxPtraceDumper dumper(child); | ||||||
|  |   ASSERT_TRUE(dumper.Init()); | ||||||
|  |   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||||
|  |   bool found_exe = false; | ||||||
|  |   unsigned i; | ||||||
|  |   for (i = 0; i < mappings.size(); ++i) { | ||||||
|  |     const MappingInfo* mapping = mappings[i]; | ||||||
|  |     if (!strcmp(mapping->name, exe_name)) { | ||||||
|  |       found_exe = true; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   ASSERT_TRUE(found_exe); | ||||||
|  | 
 | ||||||
|  |   uint8_t identifier1[sizeof(MDGUID)]; | ||||||
|  |   uint8_t identifier2[sizeof(MDGUID)]; | ||||||
|  |   EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i, | ||||||
|  |                                                  identifier1)); | ||||||
|  |   FileID fileid(exe_name); | ||||||
|  |   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); | ||||||
|  |   char identifier_string1[37]; | ||||||
|  |   char identifier_string2[37]; | ||||||
|  |   FileID::ConvertIdentifierToString(identifier1, identifier_string1, | ||||||
|  |                                     37); | ||||||
|  |   FileID::ConvertIdentifierToString(identifier2, identifier_string2, | ||||||
|  |                                     37); | ||||||
|  |   EXPECT_STREQ(identifier_string1, identifier_string2); | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
							
								
								
									
										1765
									
								
								google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1765
									
								
								google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -0,0 +1,125 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <list> | ||||||
|  | #include <utility> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/linux_dumper.h" | ||||||
|  | #include "google_breakpad/common/minidump_format.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class ExceptionHandler; | ||||||
|  | 
 | ||||||
|  | struct MappingEntry { | ||||||
|  |   MappingInfo first; | ||||||
|  |   uint8_t second[sizeof(MDGUID)]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // A list of <MappingInfo, GUID>
 | ||||||
|  | typedef std::list<MappingEntry> MappingList; | ||||||
|  | 
 | ||||||
|  | // These entries store a list of memory regions that the client wants included
 | ||||||
|  | // in the minidump.
 | ||||||
|  | struct AppMemory { | ||||||
|  |   void* ptr; | ||||||
|  |   size_t length; | ||||||
|  | 
 | ||||||
|  |   bool operator==(const struct AppMemory& other) const { | ||||||
|  |     return ptr == other.ptr; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   bool operator==(const void* other) const { | ||||||
|  |     return ptr == other; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | typedef std::list<AppMemory> AppMemoryList; | ||||||
|  | 
 | ||||||
|  | // Writes a minidump to the filesystem. These functions do not malloc nor use
 | ||||||
|  | // libc functions which may. Thus, it can be used in contexts where the state
 | ||||||
|  | // of the heap may be corrupt.
 | ||||||
|  | //   minidump_path: the path to the file to write to. This is opened O_EXCL and
 | ||||||
|  | //     fails open fails.
 | ||||||
|  | //   crashing_process: the pid of the crashing process. This must be trusted.
 | ||||||
|  | //   blob: a blob of data from the crashing process. See exception_handler.h
 | ||||||
|  | //   blob_size: the length of |blob|, in bytes
 | ||||||
|  | //
 | ||||||
|  | // Returns true iff successful.
 | ||||||
|  | bool WriteMinidump(const char* minidump_path, pid_t crashing_process, | ||||||
|  |                    const void* blob, size_t blob_size); | ||||||
|  | // Same as above but takes an open file descriptor instead of a path.
 | ||||||
|  | bool WriteMinidump(int minidump_fd, pid_t crashing_process, | ||||||
|  |                    const void* blob, size_t blob_size); | ||||||
|  | 
 | ||||||
|  | // Alternate form of WriteMinidump() that works with processes that
 | ||||||
|  | // are not expected to have crashed.  If |process_blamed_thread| is
 | ||||||
|  | // meaningful, it will be the one from which a crash signature is
 | ||||||
|  | // extracted.  It is not expected that this function will be called
 | ||||||
|  | // from a compromised context, but it is safe to do so.
 | ||||||
|  | bool WriteMinidump(const char* minidump_path, pid_t process, | ||||||
|  |                    pid_t process_blamed_thread); | ||||||
|  | 
 | ||||||
|  | // These overloads also allow passing a list of known mappings and
 | ||||||
|  | // a list of additional memory regions to be included in the minidump.
 | ||||||
|  | bool WriteMinidump(const char* minidump_path, pid_t crashing_process, | ||||||
|  |                    const void* blob, size_t blob_size, | ||||||
|  |                    const MappingList& mappings, | ||||||
|  |                    const AppMemoryList& appdata); | ||||||
|  | bool WriteMinidump(int minidump_fd, pid_t crashing_process, | ||||||
|  |                    const void* blob, size_t blob_size, | ||||||
|  |                    const MappingList& mappings, | ||||||
|  |                    const AppMemoryList& appdata); | ||||||
|  | 
 | ||||||
|  | // These overloads also allow passing a file size limit for the minidump.
 | ||||||
|  | bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit, | ||||||
|  |                    pid_t crashing_process, | ||||||
|  |                    const void* blob, size_t blob_size, | ||||||
|  |                    const MappingList& mappings, | ||||||
|  |                    const AppMemoryList& appdata); | ||||||
|  | bool WriteMinidump(int minidump_fd, off_t minidump_size_limit, | ||||||
|  |                    pid_t crashing_process, | ||||||
|  |                    const void* blob, size_t blob_size, | ||||||
|  |                    const MappingList& mappings, | ||||||
|  |                    const AppMemoryList& appdata); | ||||||
|  | 
 | ||||||
|  | bool WriteMinidump(const char* filename, | ||||||
|  |                    const MappingList& mappings, | ||||||
|  |                    const AppMemoryList& appdata, | ||||||
|  |                    LinuxDumper* dumper); | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
 | ||||||
| @ -0,0 +1,757 @@ | |||||||
|  | // Copyright (c) 2011 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <sys/poll.h> | ||||||
|  | #include <sys/stat.h> | ||||||
|  | #include <sys/syscall.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <ucontext.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "client/linux/handler/exception_handler.h" | ||||||
|  | #include "client/linux/minidump_writer/linux_dumper.h" | ||||||
|  | #include "client/linux/minidump_writer/minidump_writer.h" | ||||||
|  | #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "common/linux/file_id.h" | ||||||
|  | #include "common/linux/ignore_ret.h" | ||||||
|  | #include "common/linux/safe_readlink.h" | ||||||
|  | #include "common/scoped_ptr.h" | ||||||
|  | #include "common/tests/auto_tempdir.h" | ||||||
|  | #include "common/tests/file_utils.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | #include "google_breakpad/processor/minidump.h" | ||||||
|  | 
 | ||||||
|  | using namespace google_breakpad; | ||||||
|  | 
 | ||||||
|  | // Length of a formatted GUID string =
 | ||||||
|  | // sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
 | ||||||
|  | const int kGUIDStringSize = 37; | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | typedef testing::Test MinidumpWriterTest; | ||||||
|  | 
 | ||||||
|  | const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest"; | ||||||
|  | 
 | ||||||
|  | TEST(MinidumpWriterTest, SetupWithPath) { | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     char b; | ||||||
|  |     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   ExceptionHandler::CrashContext context; | ||||||
|  |   memset(&context, 0, sizeof(context)); | ||||||
|  | 
 | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  |   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||||
|  |   // Set a non-zero tid to avoid tripping asserts.
 | ||||||
|  |   context.tid = 1; | ||||||
|  |   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); | ||||||
|  |   struct stat st; | ||||||
|  |   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||||
|  |   ASSERT_GT(st.st_size, 0); | ||||||
|  | 
 | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(MinidumpWriterTest, SetupWithFD) { | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     char b; | ||||||
|  |     HANDLE_EINTR(read(fds[0], &b, sizeof(b))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   ExceptionHandler::CrashContext context; | ||||||
|  |   memset(&context, 0, sizeof(context)); | ||||||
|  | 
 | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  |   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||||
|  |   int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU); | ||||||
|  |   // Set a non-zero tid to avoid tripping asserts.
 | ||||||
|  |   context.tid = 1; | ||||||
|  |   ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context))); | ||||||
|  |   struct stat st; | ||||||
|  |   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||||
|  |   ASSERT_GT(st.st_size, 0); | ||||||
|  | 
 | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Test that mapping info can be specified when writing a minidump,
 | ||||||
|  | // and that it ends up in the module list of the minidump.
 | ||||||
|  | TEST(MinidumpWriterTest, MappingInfo) { | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   // These are defined here so the parent can use them to check the
 | ||||||
|  |   // data from the minidump afterwards.
 | ||||||
|  |   const uint32_t memory_size = sysconf(_SC_PAGESIZE); | ||||||
|  |   const char* kMemoryName = "a fake module"; | ||||||
|  |   const uint8_t kModuleGUID[sizeof(MDGUID)] = { | ||||||
|  |     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, | ||||||
|  |     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF | ||||||
|  |   }; | ||||||
|  |   char module_identifier_buffer[kGUIDStringSize]; | ||||||
|  |   FileID::ConvertIdentifierToString(kModuleGUID, | ||||||
|  |                                     module_identifier_buffer, | ||||||
|  |                                     sizeof(module_identifier_buffer)); | ||||||
|  |   string module_identifier(module_identifier_buffer); | ||||||
|  |   // Strip out dashes
 | ||||||
|  |   size_t pos; | ||||||
|  |   while ((pos = module_identifier.find('-')) != string::npos) { | ||||||
|  |     module_identifier.erase(pos, 1); | ||||||
|  |   } | ||||||
|  |   // And append a zero, because module IDs include an "age" field
 | ||||||
|  |   // which is always zero on Linux.
 | ||||||
|  |   module_identifier += "0"; | ||||||
|  | 
 | ||||||
|  |   // Get some memory.
 | ||||||
|  |   char* memory = | ||||||
|  |     reinterpret_cast<char*>(mmap(NULL, | ||||||
|  |                                  memory_size, | ||||||
|  |                                  PROT_READ | PROT_WRITE, | ||||||
|  |                                  MAP_PRIVATE | MAP_ANON, | ||||||
|  |                                  -1, | ||||||
|  |                                  0)); | ||||||
|  |   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); | ||||||
|  |   ASSERT_TRUE(memory); | ||||||
|  | 
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     char b; | ||||||
|  |     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   ExceptionHandler::CrashContext context; | ||||||
|  |   memset(&context, 0, sizeof(context)); | ||||||
|  |   ASSERT_EQ(0, getcontext(&context.context)); | ||||||
|  |   context.tid = child; | ||||||
|  | 
 | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  |   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||||
|  | 
 | ||||||
|  |   // Add information about the mapped memory.
 | ||||||
|  |   MappingInfo info; | ||||||
|  |   info.start_addr = kMemoryAddress; | ||||||
|  |   info.size = memory_size; | ||||||
|  |   info.offset = 0; | ||||||
|  |   strcpy(info.name, kMemoryName); | ||||||
|  | 
 | ||||||
|  |   MappingList mappings; | ||||||
|  |   AppMemoryList memory_list; | ||||||
|  |   MappingEntry mapping; | ||||||
|  |   mapping.first = info; | ||||||
|  |   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); | ||||||
|  |   mappings.push_back(mapping); | ||||||
|  |   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), | ||||||
|  |                             mappings, memory_list)); | ||||||
|  | 
 | ||||||
|  |   // Read the minidump. Load the module list, and ensure that
 | ||||||
|  |   // the mmap'ed |memory| is listed with the given module name
 | ||||||
|  |   // and debug ID.
 | ||||||
|  |   Minidump minidump(templ); | ||||||
|  |   ASSERT_TRUE(minidump.Read()); | ||||||
|  | 
 | ||||||
|  |   MinidumpModuleList* module_list = minidump.GetModuleList(); | ||||||
|  |   ASSERT_TRUE(module_list); | ||||||
|  |   const MinidumpModule* module = | ||||||
|  |     module_list->GetModuleForAddress(kMemoryAddress); | ||||||
|  |   ASSERT_TRUE(module); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(kMemoryAddress, module->base_address()); | ||||||
|  |   EXPECT_EQ(memory_size, module->size()); | ||||||
|  |   EXPECT_EQ(kMemoryName, module->code_file()); | ||||||
|  |   EXPECT_EQ(module_identifier, module->debug_identifier()); | ||||||
|  | 
 | ||||||
|  |   uint32_t len; | ||||||
|  |   // These streams are expected to be there
 | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len)); | ||||||
|  |   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len)); | ||||||
|  | 
 | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Test that mapping info can be specified, and that it overrides
 | ||||||
|  | // existing mappings that are wholly contained within the specified
 | ||||||
|  | // range.
 | ||||||
|  | TEST(MinidumpWriterTest, MappingInfoContained) { | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   // These are defined here so the parent can use them to check the
 | ||||||
|  |   // data from the minidump afterwards.
 | ||||||
|  |   const int32_t memory_size = sysconf(_SC_PAGESIZE); | ||||||
|  |   const char* kMemoryName = "a fake module"; | ||||||
|  |   const uint8_t kModuleGUID[sizeof(MDGUID)] = { | ||||||
|  |     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, | ||||||
|  |     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF | ||||||
|  |   }; | ||||||
|  |   char module_identifier_buffer[kGUIDStringSize]; | ||||||
|  |   FileID::ConvertIdentifierToString(kModuleGUID, | ||||||
|  |                                     module_identifier_buffer, | ||||||
|  |                                     sizeof(module_identifier_buffer)); | ||||||
|  |   string module_identifier(module_identifier_buffer); | ||||||
|  |   // Strip out dashes
 | ||||||
|  |   size_t pos; | ||||||
|  |   while ((pos = module_identifier.find('-')) != string::npos) { | ||||||
|  |     module_identifier.erase(pos, 1); | ||||||
|  |   } | ||||||
|  |   // And append a zero, because module IDs include an "age" field
 | ||||||
|  |   // which is always zero on Linux.
 | ||||||
|  |   module_identifier += "0"; | ||||||
|  | 
 | ||||||
|  |   // mmap a file
 | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  |   string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp"; | ||||||
|  |   int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0); | ||||||
|  |   ASSERT_NE(-1, fd); | ||||||
|  |   unlink(tempfile.c_str()); | ||||||
|  |   // fill with zeros
 | ||||||
|  |   google_breakpad::scoped_array<char> buffer(new char[memory_size]); | ||||||
|  |   memset(buffer.get(), 0, memory_size); | ||||||
|  |   ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size)); | ||||||
|  |   lseek(fd, 0, SEEK_SET); | ||||||
|  | 
 | ||||||
|  |   char* memory = | ||||||
|  |     reinterpret_cast<char*>(mmap(NULL, | ||||||
|  |                                  memory_size, | ||||||
|  |                                  PROT_READ | PROT_WRITE, | ||||||
|  |                                  MAP_PRIVATE, | ||||||
|  |                                  fd, | ||||||
|  |                                  0)); | ||||||
|  |   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); | ||||||
|  |   ASSERT_TRUE(memory); | ||||||
|  |   close(fd); | ||||||
|  | 
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     char b; | ||||||
|  |     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   ExceptionHandler::CrashContext context; | ||||||
|  |   memset(&context, 0, sizeof(context)); | ||||||
|  |   context.tid = 1; | ||||||
|  | 
 | ||||||
|  |   string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName; | ||||||
|  | 
 | ||||||
|  |   // Add information about the mapped memory. Report it as being larger than
 | ||||||
|  |   // it actually is.
 | ||||||
|  |   MappingInfo info; | ||||||
|  |   info.start_addr = kMemoryAddress - memory_size; | ||||||
|  |   info.size = memory_size * 3; | ||||||
|  |   info.offset = 0; | ||||||
|  |   strcpy(info.name, kMemoryName); | ||||||
|  | 
 | ||||||
|  |   MappingList mappings; | ||||||
|  |   AppMemoryList memory_list; | ||||||
|  |   MappingEntry mapping; | ||||||
|  |   mapping.first = info; | ||||||
|  |   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); | ||||||
|  |   mappings.push_back(mapping); | ||||||
|  |   ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context), | ||||||
|  |                             mappings, memory_list)); | ||||||
|  | 
 | ||||||
|  |   // Read the minidump. Load the module list, and ensure that
 | ||||||
|  |   // the mmap'ed |memory| is listed with the given module name
 | ||||||
|  |   // and debug ID.
 | ||||||
|  |   Minidump minidump(dumpfile); | ||||||
|  |   ASSERT_TRUE(minidump.Read()); | ||||||
|  | 
 | ||||||
|  |   MinidumpModuleList* module_list = minidump.GetModuleList(); | ||||||
|  |   ASSERT_TRUE(module_list); | ||||||
|  |   const MinidumpModule* module = | ||||||
|  |     module_list->GetModuleForAddress(kMemoryAddress); | ||||||
|  |   ASSERT_TRUE(module); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(info.start_addr, module->base_address()); | ||||||
|  |   EXPECT_EQ(info.size, module->size()); | ||||||
|  |   EXPECT_EQ(kMemoryName, module->code_file()); | ||||||
|  |   EXPECT_EQ(module_identifier, module->debug_identifier()); | ||||||
|  | 
 | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(MinidumpWriterTest, DeletedBinary) { | ||||||
|  |   const string kNumberOfThreadsArgument = "1"; | ||||||
|  |   const string helper_path(GetHelperBinary()); | ||||||
|  |   if (helper_path.empty()) { | ||||||
|  |     FAIL() << "Couldn't find helper binary"; | ||||||
|  |     exit(1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Copy binary to a temp file.
 | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  |   string binpath = temp_dir.path() + "/linux-dumper-unittest-helper"; | ||||||
|  |   ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str())) | ||||||
|  |       << "Failed to copy " << helper_path << " to " << binpath; | ||||||
|  |   ASSERT_EQ(0, chmod(binpath.c_str(), 0755)); | ||||||
|  | 
 | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   pid_t child_pid = fork(); | ||||||
|  |   if (child_pid == 0) { | ||||||
|  |     // In child process.
 | ||||||
|  |     close(fds[0]); | ||||||
|  | 
 | ||||||
|  |     // Pass the pipe fd and the number of threads as arguments.
 | ||||||
|  |     char pipe_fd_string[8]; | ||||||
|  |     sprintf(pipe_fd_string, "%d", fds[1]); | ||||||
|  |     execl(binpath.c_str(), | ||||||
|  |           binpath.c_str(), | ||||||
|  |           pipe_fd_string, | ||||||
|  |           kNumberOfThreadsArgument.c_str(), | ||||||
|  |           NULL); | ||||||
|  |   } | ||||||
|  |   close(fds[1]); | ||||||
|  |   // Wait for the child process to signal that it's ready.
 | ||||||
|  |   struct pollfd pfd; | ||||||
|  |   memset(&pfd, 0, sizeof(pfd)); | ||||||
|  |   pfd.fd = fds[0]; | ||||||
|  |   pfd.events = POLLIN | POLLERR; | ||||||
|  | 
 | ||||||
|  |   const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); | ||||||
|  |   ASSERT_EQ(1, r); | ||||||
|  |   ASSERT_TRUE(pfd.revents & POLLIN); | ||||||
|  |   uint8_t junk; | ||||||
|  |   const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk))); | ||||||
|  |   ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr); | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   // Child is ready now.
 | ||||||
|  |   // Unlink the test binary.
 | ||||||
|  |   unlink(binpath.c_str()); | ||||||
|  | 
 | ||||||
|  |   ExceptionHandler::CrashContext context; | ||||||
|  |   memset(&context, 0, sizeof(context)); | ||||||
|  | 
 | ||||||
|  |   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||||
|  |   // Set a non-zero tid to avoid tripping asserts.
 | ||||||
|  |   context.tid = 1; | ||||||
|  |   ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context, | ||||||
|  |                             sizeof(context))); | ||||||
|  |   kill(child_pid, SIGKILL); | ||||||
|  | 
 | ||||||
|  |   struct stat st; | ||||||
|  |   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||||
|  |   ASSERT_GT(st.st_size, 0); | ||||||
|  | 
 | ||||||
|  |   Minidump minidump(templ); | ||||||
|  |   ASSERT_TRUE(minidump.Read()); | ||||||
|  | 
 | ||||||
|  |   // Check that the main module filename is correct.
 | ||||||
|  |   MinidumpModuleList* module_list = minidump.GetModuleList(); | ||||||
|  |   ASSERT_TRUE(module_list); | ||||||
|  |   const MinidumpModule* module = module_list->GetMainModule(); | ||||||
|  |   EXPECT_STREQ(binpath.c_str(), module->code_file().c_str()); | ||||||
|  |   // Check that the file ID is correct.
 | ||||||
|  |   FileID fileid(helper_path.c_str()); | ||||||
|  |   uint8_t identifier[sizeof(MDGUID)]; | ||||||
|  |   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier)); | ||||||
|  |   char identifier_string[kGUIDStringSize]; | ||||||
|  |   FileID::ConvertIdentifierToString(identifier, | ||||||
|  |                                     identifier_string, | ||||||
|  |                                     kGUIDStringSize); | ||||||
|  |   string module_identifier(identifier_string); | ||||||
|  |   // Strip out dashes
 | ||||||
|  |   size_t pos; | ||||||
|  |   while ((pos = module_identifier.find('-')) != string::npos) { | ||||||
|  |     module_identifier.erase(pos, 1); | ||||||
|  |   } | ||||||
|  |   // And append a zero, because module IDs include an "age" field
 | ||||||
|  |   // which is always zero on Linux.
 | ||||||
|  |   module_identifier += "0"; | ||||||
|  |   EXPECT_EQ(module_identifier, module->debug_identifier()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Test that an additional memory region can be added to the minidump.
 | ||||||
|  | TEST(MinidumpWriterTest, AdditionalMemory) { | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   // These are defined here so the parent can use them to check the
 | ||||||
|  |   // data from the minidump afterwards.
 | ||||||
|  |   const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); | ||||||
|  | 
 | ||||||
|  |   // Get some heap memory.
 | ||||||
|  |   uint8_t* memory = new uint8_t[kMemorySize]; | ||||||
|  |   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); | ||||||
|  |   ASSERT_TRUE(memory); | ||||||
|  | 
 | ||||||
|  |   // Stick some data into the memory so the contents can be verified.
 | ||||||
|  |   for (uint32_t i = 0; i < kMemorySize; ++i) { | ||||||
|  |     memory[i] = i % 255; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     char b; | ||||||
|  |     HANDLE_EINTR(read(fds[0], &b, sizeof(b))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   ExceptionHandler::CrashContext context; | ||||||
|  | 
 | ||||||
|  |   // This needs a valid context for minidump writing to work, but getting
 | ||||||
|  |   // a useful one from the child is too much work, so just use one from
 | ||||||
|  |   // the parent since the child is just a forked copy anyway.
 | ||||||
|  |   ASSERT_EQ(0, getcontext(&context.context)); | ||||||
|  |   context.tid = child; | ||||||
|  | 
 | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  |   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||||
|  |   unlink(templ.c_str()); | ||||||
|  | 
 | ||||||
|  |   MappingList mappings; | ||||||
|  |   AppMemoryList memory_list; | ||||||
|  | 
 | ||||||
|  |   // Add the memory region to the list of memory to be included.
 | ||||||
|  |   AppMemory app_memory; | ||||||
|  |   app_memory.ptr = memory; | ||||||
|  |   app_memory.length = kMemorySize; | ||||||
|  |   memory_list.push_back(app_memory); | ||||||
|  |   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), | ||||||
|  |                             mappings, memory_list)); | ||||||
|  | 
 | ||||||
|  |   // Read the minidump. Ensure that the memory region is present
 | ||||||
|  |   Minidump minidump(templ); | ||||||
|  |   ASSERT_TRUE(minidump.Read()); | ||||||
|  | 
 | ||||||
|  |   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); | ||||||
|  |   ASSERT_TRUE(dump_memory_list); | ||||||
|  |   const MinidumpMemoryRegion* region = | ||||||
|  |     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); | ||||||
|  |   ASSERT_TRUE(region); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(kMemoryAddress, region->GetBase()); | ||||||
|  |   EXPECT_EQ(kMemorySize, region->GetSize()); | ||||||
|  | 
 | ||||||
|  |   // Verify memory contents.
 | ||||||
|  |   EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); | ||||||
|  | 
 | ||||||
|  |   delete[] memory; | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Test that an invalid thread stack pointer still results in a minidump.
 | ||||||
|  | TEST(MinidumpWriterTest, InvalidStackPointer) { | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   const pid_t child = fork(); | ||||||
|  |   if (child == 0) { | ||||||
|  |     close(fds[1]); | ||||||
|  |     char b; | ||||||
|  |     HANDLE_EINTR(read(fds[0], &b, sizeof(b))); | ||||||
|  |     close(fds[0]); | ||||||
|  |     syscall(__NR_exit); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   ExceptionHandler::CrashContext context; | ||||||
|  | 
 | ||||||
|  |   // This needs a valid context for minidump writing to work, but getting
 | ||||||
|  |   // a useful one from the child is too much work, so just use one from
 | ||||||
|  |   // the parent since the child is just a forked copy anyway.
 | ||||||
|  |   ASSERT_EQ(0, getcontext(&context.context)); | ||||||
|  |   context.tid = child; | ||||||
|  | 
 | ||||||
|  |   // Fake the child's stack pointer for its crashing thread.  NOTE: This must
 | ||||||
|  |   // be an invalid memory address for the child process (stack or otherwise).
 | ||||||
|  | #if defined(__i386) | ||||||
|  |   // Try 1MB below the current stack.
 | ||||||
|  |   uintptr_t invalid_stack_pointer = | ||||||
|  |       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||||
|  |   context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer; | ||||||
|  | #elif defined(__x86_64) | ||||||
|  |   // Try 1MB below the current stack.
 | ||||||
|  |   uintptr_t invalid_stack_pointer = | ||||||
|  |       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||||
|  |   context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer; | ||||||
|  | #elif defined(__ARM_EABI__) | ||||||
|  |   // Try 1MB below the current stack.
 | ||||||
|  |   uintptr_t invalid_stack_pointer = | ||||||
|  |       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||||
|  |   context.context.uc_mcontext.arm_sp = invalid_stack_pointer; | ||||||
|  | #else | ||||||
|  | # error "This code has not been ported to your platform yet." | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  |   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||||
|  |   // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if
 | ||||||
|  |   // presented with an invalid stack pointer.
 | ||||||
|  |   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); | ||||||
|  | 
 | ||||||
|  |   // Read the minidump. Ensure that the memory region is present
 | ||||||
|  |   Minidump minidump(templ); | ||||||
|  |   ASSERT_TRUE(minidump.Read()); | ||||||
|  | 
 | ||||||
|  |   // TODO(ted.mielczarek,mkrebs): Enable this part of the test once
 | ||||||
|  |   // https://breakpad.appspot.com/413002/ is committed.
 | ||||||
|  | #if 0 | ||||||
|  |   // Make sure there's a thread without a stack.  NOTE: It's okay if
 | ||||||
|  |   // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory
 | ||||||
|  |   // region problem".
 | ||||||
|  |   MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); | ||||||
|  |   ASSERT_TRUE(dump_thread_list); | ||||||
|  |   bool found_empty_stack = false; | ||||||
|  |   for (int i = 0; i < dump_thread_list->thread_count(); i++) { | ||||||
|  |     MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); | ||||||
|  |     ASSERT_TRUE(thread->thread() != NULL); | ||||||
|  |     // When the stack size is zero bytes, GetMemory() returns NULL.
 | ||||||
|  |     if (thread->GetMemory() == NULL) { | ||||||
|  |       found_empty_stack = true; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   // NOTE: If you fail this, first make sure that "invalid_stack_pointer"
 | ||||||
|  |   // above is indeed set to an invalid address.
 | ||||||
|  |   ASSERT_TRUE(found_empty_stack); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   close(fds[1]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Test that limiting the size of the minidump works.
 | ||||||
|  | TEST(MinidumpWriterTest, MinidumpSizeLimit) { | ||||||
|  |   static const int kNumberOfThreadsInHelperProgram = 40; | ||||||
|  | 
 | ||||||
|  |   char number_of_threads_arg[3]; | ||||||
|  |   sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram); | ||||||
|  | 
 | ||||||
|  |   string helper_path(GetHelperBinary()); | ||||||
|  |   if (helper_path.empty()) { | ||||||
|  |     FAIL() << "Couldn't find helper binary"; | ||||||
|  |     exit(1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   int fds[2]; | ||||||
|  |   ASSERT_NE(-1, pipe(fds)); | ||||||
|  | 
 | ||||||
|  |   pid_t child_pid = fork(); | ||||||
|  |   if (child_pid == 0) { | ||||||
|  |     // In child process.
 | ||||||
|  |     close(fds[0]); | ||||||
|  | 
 | ||||||
|  |     // Pass the pipe fd and the number of threads as arguments.
 | ||||||
|  |     char pipe_fd_string[8]; | ||||||
|  |     sprintf(pipe_fd_string, "%d", fds[1]); | ||||||
|  |     execl(helper_path.c_str(), | ||||||
|  |           helper_path.c_str(), | ||||||
|  |           pipe_fd_string, | ||||||
|  |           number_of_threads_arg, | ||||||
|  |           NULL); | ||||||
|  |   } | ||||||
|  |   close(fds[1]); | ||||||
|  | 
 | ||||||
|  |   // Wait for all child threads to indicate that they have started
 | ||||||
|  |   for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { | ||||||
|  |     struct pollfd pfd; | ||||||
|  |     memset(&pfd, 0, sizeof(pfd)); | ||||||
|  |     pfd.fd = fds[0]; | ||||||
|  |     pfd.events = POLLIN | POLLERR; | ||||||
|  | 
 | ||||||
|  |     const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); | ||||||
|  |     ASSERT_EQ(1, r); | ||||||
|  |     ASSERT_TRUE(pfd.revents & POLLIN); | ||||||
|  |     uint8_t junk; | ||||||
|  |     ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),  | ||||||
|  |               static_cast<ssize_t>(sizeof(junk))); | ||||||
|  |   } | ||||||
|  |   close(fds[0]); | ||||||
|  | 
 | ||||||
|  |   // There is a race here because we may stop a child thread before
 | ||||||
|  |   // it is actually running the busy loop. Empirically this sleep
 | ||||||
|  |   // is sufficient to avoid the race.
 | ||||||
|  |   usleep(100000); | ||||||
|  | 
 | ||||||
|  |   // Child and its threads are ready now.
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   off_t normal_file_size; | ||||||
|  |   int total_normal_stack_size = 0; | ||||||
|  |   AutoTempDir temp_dir; | ||||||
|  | 
 | ||||||
|  |   // First, write a minidump with no size limit.
 | ||||||
|  |   { | ||||||
|  |     string normal_dump = temp_dir.path() + | ||||||
|  |         "/minidump-writer-unittest.dmp"; | ||||||
|  |     ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1, | ||||||
|  |                               child_pid, NULL, 0, | ||||||
|  |                               MappingList(), AppMemoryList())); | ||||||
|  |     struct stat st; | ||||||
|  |     ASSERT_EQ(0, stat(normal_dump.c_str(), &st)); | ||||||
|  |     ASSERT_GT(st.st_size, 0); | ||||||
|  |     normal_file_size = st.st_size; | ||||||
|  | 
 | ||||||
|  |     Minidump minidump(normal_dump); | ||||||
|  |     ASSERT_TRUE(minidump.Read()); | ||||||
|  |     MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); | ||||||
|  |     ASSERT_TRUE(dump_thread_list); | ||||||
|  |     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { | ||||||
|  |       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); | ||||||
|  |       ASSERT_TRUE(thread->thread() != NULL); | ||||||
|  |       // When the stack size is zero bytes, GetMemory() returns NULL.
 | ||||||
|  |       MinidumpMemoryRegion* memory = thread->GetMemory(); | ||||||
|  |       ASSERT_TRUE(memory != NULL); | ||||||
|  |       total_normal_stack_size += memory->GetSize(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Second, write a minidump with a size limit big enough to not trigger
 | ||||||
|  |   // anything.
 | ||||||
|  |   { | ||||||
|  |     // Set size limit arbitrarily 1MB larger than the normal file size -- such
 | ||||||
|  |     // that the limiting code will not kick in.
 | ||||||
|  |     const off_t minidump_size_limit = normal_file_size + 1024*1024; | ||||||
|  | 
 | ||||||
|  |     string same_dump = temp_dir.path() + | ||||||
|  |         "/minidump-writer-unittest-same.dmp"; | ||||||
|  |     ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit, | ||||||
|  |                               child_pid, NULL, 0, | ||||||
|  |                               MappingList(), AppMemoryList())); | ||||||
|  |     struct stat st; | ||||||
|  |     ASSERT_EQ(0, stat(same_dump.c_str(), &st)); | ||||||
|  |     // Make sure limiting wasn't actually triggered.  NOTE: If you fail this,
 | ||||||
|  |     // first make sure that "minidump_size_limit" above is indeed set to a
 | ||||||
|  |     // large enough value -- the limit-checking code in minidump_writer.cc
 | ||||||
|  |     // does just a rough estimate.
 | ||||||
|  |     ASSERT_EQ(normal_file_size, st.st_size); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Third, write a minidump with a size limit small enough to be triggered.
 | ||||||
|  |   { | ||||||
|  |     // Set size limit to some arbitrary amount, such that the limiting code
 | ||||||
|  |     // will kick in.  The equation used to set this value was determined by
 | ||||||
|  |     // simply reversing the size-limit logic a little bit in order to pick a
 | ||||||
|  |     // size we know will trigger it.  The definition of
 | ||||||
|  |     // kLimitAverageThreadStackLength here was copied from class
 | ||||||
|  |     // MinidumpWriter in minidump_writer.cc.
 | ||||||
|  |     static const unsigned kLimitAverageThreadStackLength = 8 * 1024; | ||||||
|  |     off_t minidump_size_limit = kNumberOfThreadsInHelperProgram * | ||||||
|  |         kLimitAverageThreadStackLength; | ||||||
|  |     // If, in reality, each of the threads' stack is *smaller* than
 | ||||||
|  |     // kLimitAverageThreadStackLength, the normal file size could very well be
 | ||||||
|  |     // smaller than the arbitrary limit that was just set.  In that case,
 | ||||||
|  |     // either of these numbers should trigger the size-limiting code, but we
 | ||||||
|  |     // might as well pick the smallest.
 | ||||||
|  |     if (normal_file_size < minidump_size_limit) | ||||||
|  |       minidump_size_limit = normal_file_size; | ||||||
|  | 
 | ||||||
|  |     string limit_dump = temp_dir.path() + | ||||||
|  |         "/minidump-writer-unittest-limit.dmp"; | ||||||
|  |     ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit, | ||||||
|  |                               child_pid, NULL, 0, | ||||||
|  |                               MappingList(), AppMemoryList())); | ||||||
|  |     struct stat st; | ||||||
|  |     ASSERT_EQ(0, stat(limit_dump.c_str(), &st)); | ||||||
|  |     ASSERT_GT(st.st_size, 0); | ||||||
|  |     // Make sure the file size is at least smaller than the original.  If this
 | ||||||
|  |     // fails because it's the same size, then the size-limit logic didn't kick
 | ||||||
|  |     // in like it was supposed to.
 | ||||||
|  |     EXPECT_LT(st.st_size, normal_file_size); | ||||||
|  | 
 | ||||||
|  |     Minidump minidump(limit_dump); | ||||||
|  |     ASSERT_TRUE(minidump.Read()); | ||||||
|  |     MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); | ||||||
|  |     ASSERT_TRUE(dump_thread_list); | ||||||
|  |     int total_limit_stack_size = 0; | ||||||
|  |     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { | ||||||
|  |       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); | ||||||
|  |       ASSERT_TRUE(thread->thread() != NULL); | ||||||
|  |       // When the stack size is zero bytes, GetMemory() returns NULL.
 | ||||||
|  |       MinidumpMemoryRegion* memory = thread->GetMemory(); | ||||||
|  |       ASSERT_TRUE(memory != NULL); | ||||||
|  |       total_limit_stack_size += memory->GetSize(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Make sure stack size shrunk by at least 1KB per extra thread.  The
 | ||||||
|  |     // definition of kLimitBaseThreadCount here was copied from class
 | ||||||
|  |     // MinidumpWriter in minidump_writer.cc.
 | ||||||
|  |     // Note: The 1KB is arbitrary, and assumes that the thread stacks are big
 | ||||||
|  |     // enough to shrink by that much.  For example, if each thread stack was
 | ||||||
|  |     // originally only 2KB, the current size-limit logic wouldn't actually
 | ||||||
|  |     // shrink them because that's the size to which it tries to shrink.  If
 | ||||||
|  |     // you fail this part of the test due to something like that, the test
 | ||||||
|  |     // logic should probably be improved to account for your situation.
 | ||||||
|  |     const unsigned kLimitBaseThreadCount = 20; | ||||||
|  |     const unsigned kMinPerExtraThreadStackReduction = 1024; | ||||||
|  |     const int min_expected_reduction = (kNumberOfThreadsInHelperProgram - | ||||||
|  |         kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction; | ||||||
|  |     EXPECT_LT(total_limit_stack_size, | ||||||
|  |               total_normal_stack_size - min_expected_reduction); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Kill the helper program.
 | ||||||
|  |   kill(child_pid, SIGKILL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace
 | ||||||
| @ -0,0 +1,66 @@ | |||||||
|  | // Copyright (c) 2011 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // minidump_writer_unittest_utils.cc:
 | ||||||
|  | // Shared routines used by unittests under client/linux/minidump_writer.
 | ||||||
|  | 
 | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" | ||||||
|  | #include "common/linux/safe_readlink.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | string GetHelperBinary() { | ||||||
|  |   string helper_path; | ||||||
|  |   char *bindir = getenv("bindir"); | ||||||
|  |   if (bindir) { | ||||||
|  |     helper_path = string(bindir) + "/"; | ||||||
|  |   } else { | ||||||
|  |     // Locate helper binary next to the current binary.
 | ||||||
|  |     char self_path[PATH_MAX]; | ||||||
|  |     if (!SafeReadLink("/proc/self/exe", self_path)) { | ||||||
|  |       return ""; | ||||||
|  |     } | ||||||
|  |     helper_path = string(self_path); | ||||||
|  |     size_t pos = helper_path.rfind('/'); | ||||||
|  |     if (pos == string::npos) { | ||||||
|  |       return ""; | ||||||
|  |     } | ||||||
|  |     helper_path.erase(pos + 1); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   helper_path += "linux_dumper_unittest_helper"; | ||||||
|  | 
 | ||||||
|  |   return helper_path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
| @ -0,0 +1,49 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // minidump_writer_unittest_utils.h:
 | ||||||
|  | // Shared routines used by unittests under client/linux/minidump_writer.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // Returns the full path to linux_dumper_unittest_helper.  The full path is
 | ||||||
|  | // discovered either by using the environment variable "bindir" or by using
 | ||||||
|  | // the location of the main module of the currently running process.
 | ||||||
|  | string GetHelperBinary(); | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_
 | ||||||
| @ -0,0 +1,130 @@ | |||||||
|  | // Copyright (c) 2013, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ | ||||||
|  | #define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <assert.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/line_reader.h" | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // A class for reading /proc/cpuinfo without using fopen/fgets or other
 | ||||||
|  | // functions which may allocate memory.
 | ||||||
|  | class ProcCpuInfoReader { | ||||||
|  | public: | ||||||
|  |   ProcCpuInfoReader(int fd) | ||||||
|  |     : line_reader_(fd), pop_count_(-1) { | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Return the next field name, or NULL in case of EOF.
 | ||||||
|  |   // field: (output) Pointer to zero-terminated field name.
 | ||||||
|  |   // Returns true on success, or false on EOF or error (line too long).
 | ||||||
|  |   bool GetNextField(const char** field) { | ||||||
|  |     for (;;) { | ||||||
|  |       const char* line; | ||||||
|  |       unsigned line_len; | ||||||
|  | 
 | ||||||
|  |       // Try to read next line.
 | ||||||
|  |       if (pop_count_ >= 0) { | ||||||
|  |         line_reader_.PopLine(pop_count_); | ||||||
|  |         pop_count_ = -1; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       if (!line_reader_.GetNextLine(&line, &line_len)) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |       pop_count_ = static_cast<int>(line_len); | ||||||
|  | 
 | ||||||
|  |       const char* line_end = line + line_len; | ||||||
|  | 
 | ||||||
|  |       // Expected format: <field-name> <space>+ ':' <space> <value>
 | ||||||
|  |       // Note that:
 | ||||||
|  |       //   - empty lines happen.
 | ||||||
|  |       //   - <field-name> can contain spaces.
 | ||||||
|  |       //   - some fields have an empty <value>
 | ||||||
|  |       char* sep = static_cast<char*>(my_memchr(line, ':', line_len)); | ||||||
|  |       if (sep == NULL) | ||||||
|  |         continue; | ||||||
|  | 
 | ||||||
|  |       // Record the value. Skip leading space after the column to get
 | ||||||
|  |       // its start.
 | ||||||
|  |       const char* val = sep+1; | ||||||
|  |       while (val < line_end && my_isspace(*val)) | ||||||
|  |         val++; | ||||||
|  | 
 | ||||||
|  |       value_ = val; | ||||||
|  |       value_len_ = static_cast<size_t>(line_end - val); | ||||||
|  | 
 | ||||||
|  |       // Remove trailing spaces before the column to properly 0-terminate
 | ||||||
|  |       // the field name.
 | ||||||
|  |       while (sep > line && my_isspace(sep[-1])) | ||||||
|  |         sep--; | ||||||
|  | 
 | ||||||
|  |       if (sep == line) | ||||||
|  |         continue; | ||||||
|  | 
 | ||||||
|  |       // zero-terminate field name.
 | ||||||
|  |       *sep = '\0'; | ||||||
|  | 
 | ||||||
|  |       *field = line; | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Return the field value. This must be called after a succesful
 | ||||||
|  |   // call to GetNextField().
 | ||||||
|  |   const char* GetValue() { | ||||||
|  |     assert(value_); | ||||||
|  |     return value_; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Same as GetValue(), but also returns the length in characters of
 | ||||||
|  |   // the value.
 | ||||||
|  |   const char* GetValueAndLen(size_t* length) { | ||||||
|  |     assert(value_); | ||||||
|  |     *length = value_len_; | ||||||
|  |     return value_; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |   LineReader line_reader_; | ||||||
|  |   int pop_count_; | ||||||
|  |   const char* value_; | ||||||
|  |   size_t value_len_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_
 | ||||||
| @ -0,0 +1,200 @@ | |||||||
|  | // Copyright (c) 2013, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <errno.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "client/linux/minidump_writer/proc_cpuinfo_reader.h" | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "common/linux/eintr_wrapper.h" | ||||||
|  | #include "common/tests/auto_testfile.h" | ||||||
|  | 
 | ||||||
|  | using namespace google_breakpad; | ||||||
|  | 
 | ||||||
|  | #if !defined(__ANDROID__) | ||||||
|  | #define TEMPDIR "/tmp" | ||||||
|  | #else | ||||||
|  | #define TEMPDIR "/data/local/tmp" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace { | ||||||
|  | 
 | ||||||
|  | typedef testing::Test ProcCpuInfoReaderTest; | ||||||
|  | 
 | ||||||
|  | class ScopedTestFile : public AutoTestFile { | ||||||
|  | public: | ||||||
|  |   explicit ScopedTestFile(const char* text) | ||||||
|  |     : AutoTestFile("proc_cpuinfo_reader", text) { | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, EmptyFile) { | ||||||
|  |   ScopedTestFile file(""); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *field; | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, OneLineTerminated) { | ||||||
|  |   ScopedTestFile file("foo : bar\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   ASSERT_STREQ("bar", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, OneLine) { | ||||||
|  |   ScopedTestFile file("foo : bar"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char *field; | ||||||
|  |   size_t value_len; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len)); | ||||||
|  |   ASSERT_EQ(3U, value_len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) { | ||||||
|  |   ScopedTestFile file("foo : bar\nzoo : tut\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char* field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   ASSERT_STREQ("bar", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("zoo", field); | ||||||
|  |   ASSERT_STREQ("tut", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, SkipMalformedLine) { | ||||||
|  |   ScopedTestFile file("this line should have a column\nfoo : bar\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char* field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   ASSERT_STREQ("bar", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) { | ||||||
|  |   ScopedTestFile file("\n\nfoo : bar\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char* field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   ASSERT_STREQ("bar", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, SkipEmptyField) { | ||||||
|  |   ScopedTestFile file(" : bar\nzoo : tut\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char* field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("zoo", field); | ||||||
|  |   ASSERT_STREQ("tut", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) { | ||||||
|  |   ScopedTestFile file("foo : bar\n\n\nfoo : bar\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char* field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   ASSERT_STREQ("bar", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   ASSERT_STREQ("bar", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, FieldWithSpaces) { | ||||||
|  |   ScopedTestFile file("foo bar    : zoo\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char* field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo bar", field); | ||||||
|  |   ASSERT_STREQ("zoo", reader.GetValue()); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(ProcCpuInfoReaderTest, EmptyValue) { | ||||||
|  |   ScopedTestFile file("foo :\n"); | ||||||
|  |   ASSERT_TRUE(file.IsOk()); | ||||||
|  |   ProcCpuInfoReader reader(file.GetFd()); | ||||||
|  | 
 | ||||||
|  |   const char* field; | ||||||
|  |   ASSERT_TRUE(reader.GetNextField(&field)); | ||||||
|  |   ASSERT_STREQ("foo", field); | ||||||
|  |   size_t value_len; | ||||||
|  |   ASSERT_STREQ("", reader.GetValueAndLen(&value_len)); | ||||||
|  |   ASSERT_EQ(0U, value_len); | ||||||
|  | 
 | ||||||
|  |   ASSERT_FALSE(reader.GetNextField(&field)); | ||||||
|  | } | ||||||
| @ -0,0 +1,104 @@ | |||||||
|  | // Copyright (c) 2009, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include "common/linux/google_crashdump_uploader.h" | ||||||
|  | #include "third_party/linux/include/gflags/gflags.h" | ||||||
|  | #include <string> | ||||||
|  | #include <iostream> | ||||||
|  | 
 | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | DEFINE_string(crash_server, "https://clients2.google.com/cr", | ||||||
|  |               "The crash server to upload minidumps to."); | ||||||
|  | DEFINE_string(product_name, "", | ||||||
|  |               "The product name that the minidump corresponds to."); | ||||||
|  | DEFINE_string(product_version, "", | ||||||
|  |               "The version of the product that produced the minidump."); | ||||||
|  | DEFINE_string(client_id, "", | ||||||
|  |               "The client GUID"); | ||||||
|  | DEFINE_string(minidump_path, "", | ||||||
|  |               "The path of the minidump file."); | ||||||
|  | DEFINE_string(ptime, "", | ||||||
|  |               "The process uptime in milliseconds."); | ||||||
|  | DEFINE_string(ctime, "", | ||||||
|  |               "The cumulative process uptime in milliseconds."); | ||||||
|  | DEFINE_string(email, "", | ||||||
|  |               "The user's email address."); | ||||||
|  | DEFINE_string(comments, "", | ||||||
|  |               "Extra user comments"); | ||||||
|  | DEFINE_string(proxy_host, "", | ||||||
|  |               "Proxy host"); | ||||||
|  | DEFINE_string(proxy_userpasswd, "", | ||||||
|  |               "Proxy username/password in user:pass format."); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | bool CheckForRequiredFlagsOrDie() { | ||||||
|  |   string error_text = ""; | ||||||
|  |   if (FLAGS_product_name.empty()) { | ||||||
|  |     error_text.append("\nProduct name must be specified."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (FLAGS_product_version.empty()) { | ||||||
|  |     error_text.append("\nProduct version must be specified."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (FLAGS_client_id.empty()) { | ||||||
|  |     error_text.append("\nClient ID must be specified."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (FLAGS_minidump_path.empty()) { | ||||||
|  |     error_text.append("\nMinidump pathname must be specified."); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   if (!error_text.empty()) { | ||||||
|  |     std::cout << error_text; | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char *argv[]) { | ||||||
|  |   google::InitGoogleLogging(argv[0]); | ||||||
|  |   google::ParseCommandLineFlags(&argc, &argv, true); | ||||||
|  |   if (!CheckForRequiredFlagsOrDie()) { | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, | ||||||
|  |                                              FLAGS_product_version, | ||||||
|  |                                              FLAGS_client_id, | ||||||
|  |                                              FLAGS_ptime, | ||||||
|  |                                              FLAGS_ctime, | ||||||
|  |                                              FLAGS_email, | ||||||
|  |                                              FLAGS_comments, | ||||||
|  |                                              FLAGS_minidump_path, | ||||||
|  |                                              FLAGS_crash_server, | ||||||
|  |                                              FLAGS_proxy_host, | ||||||
|  |                                              FLAGS_proxy_userpasswd); | ||||||
|  |   g.Upload(); | ||||||
|  | } | ||||||
							
								
								
									
										97
									
								
								google-breakpad/src/client/minidump_file_writer-inl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								google-breakpad/src/client/minidump_file_writer-inl.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | |||||||
|  | // Copyright (c) 2006, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // minidump_file_writer-inl.h: Minidump file writer implementation.
 | ||||||
|  | //
 | ||||||
|  | // See minidump_file_writer.h for documentation.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__ | ||||||
|  | #define CLIENT_MINIDUMP_FILE_WRITER_INL_H__ | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | #include "client/minidump_file_writer.h" | ||||||
|  | #include "google_breakpad/common/minidump_size.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | template<typename MDType> | ||||||
|  | inline bool TypedMDRVA<MDType>::Allocate() { | ||||||
|  |   allocation_state_ = SINGLE_OBJECT; | ||||||
|  |   return UntypedMDRVA::Allocate(minidump_size<MDType>::size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename MDType> | ||||||
|  | inline bool TypedMDRVA<MDType>::Allocate(size_t additional) { | ||||||
|  |   allocation_state_ = SINGLE_OBJECT; | ||||||
|  |   return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + additional); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename MDType> | ||||||
|  | inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) { | ||||||
|  |   assert(count); | ||||||
|  |   allocation_state_ = ARRAY; | ||||||
|  |   return UntypedMDRVA::Allocate(minidump_size<MDType>::size() * count); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename MDType> | ||||||
|  | inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(size_t count, | ||||||
|  |                                                        size_t length) { | ||||||
|  |   assert(count && length); | ||||||
|  |   allocation_state_ = SINGLE_OBJECT_WITH_ARRAY; | ||||||
|  |   return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * length); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename MDType> | ||||||
|  | inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) { | ||||||
|  |   assert(allocation_state_ == ARRAY); | ||||||
|  |   return writer_->Copy( | ||||||
|  |       static_cast<MDRVA>(position_ + index * minidump_size<MDType>::size()),  | ||||||
|  |       item, minidump_size<MDType>::size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename MDType> | ||||||
|  | inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index, | ||||||
|  |                                                      const void *src,  | ||||||
|  |                                                      size_t length) { | ||||||
|  |   assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY); | ||||||
|  |   return writer_->Copy( | ||||||
|  |       static_cast<MDRVA>(position_ + minidump_size<MDType>::size()  | ||||||
|  |                          + index * length), | ||||||
|  |       src, length); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename MDType> | ||||||
|  | inline bool TypedMDRVA<MDType>::Flush() { | ||||||
|  |   return writer_->Copy(position_, &data_, minidump_size<MDType>::size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_MINIDUMP_FILE_WRITER_INL_H__
 | ||||||
							
								
								
									
										284
									
								
								google-breakpad/src/client/minidump_file_writer.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								google-breakpad/src/client/minidump_file_writer.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,284 @@ | |||||||
|  | // Copyright (c) 2006, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // minidump_file_writer.cc: Minidump file writer implementation.
 | ||||||
|  | //
 | ||||||
|  | // See minidump_file_writer.h for documentation.
 | ||||||
|  | 
 | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include "client/minidump_file_writer-inl.h" | ||||||
|  | #include "common/linux/linux_libc_support.h" | ||||||
|  | #include "common/string_conversion.h" | ||||||
|  | #if __linux__ | ||||||
|  | #include "third_party/lss/linux_syscall_support.h" | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1); | ||||||
|  | 
 | ||||||
|  | MinidumpFileWriter::MinidumpFileWriter() | ||||||
|  |     : file_(-1), | ||||||
|  |       close_file_when_destroyed_(true), | ||||||
|  |       position_(0), | ||||||
|  |       size_(0) { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MinidumpFileWriter::~MinidumpFileWriter() { | ||||||
|  |   if (close_file_when_destroyed_) | ||||||
|  |     Close(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::Open(const char *path) { | ||||||
|  |   assert(file_ == -1); | ||||||
|  | #if __linux__ | ||||||
|  |   file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); | ||||||
|  | #else | ||||||
|  |   file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  |   return file_ != -1; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void MinidumpFileWriter::SetFile(const int file) { | ||||||
|  |   assert(file_ == -1); | ||||||
|  |   file_ = file; | ||||||
|  |   close_file_when_destroyed_ = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::Close() { | ||||||
|  |   bool result = true; | ||||||
|  | 
 | ||||||
|  |   if (file_ != -1) { | ||||||
|  |     if (-1 == ftruncate(file_, position_)) { | ||||||
|  |        return false; | ||||||
|  |     } | ||||||
|  | #if __linux__ | ||||||
|  |     result = (sys_close(file_) == 0); | ||||||
|  | #else | ||||||
|  |     result = (close(file_) == 0); | ||||||
|  | #endif | ||||||
|  |     file_ = -1; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str, | ||||||
|  |                                               unsigned int length, | ||||||
|  |                                               TypedMDRVA<MDString> *mdstring) { | ||||||
|  |   bool result = true; | ||||||
|  |   if (sizeof(wchar_t) == sizeof(uint16_t)) { | ||||||
|  |     // Shortcut if wchar_t is the same size as MDString's buffer
 | ||||||
|  |     result = mdstring->Copy(str, mdstring->get()->length); | ||||||
|  |   } else { | ||||||
|  |     uint16_t out[2]; | ||||||
|  |     int out_idx = 0; | ||||||
|  | 
 | ||||||
|  |     // Copy the string character by character
 | ||||||
|  |     while (length && result) { | ||||||
|  |       UTF32ToUTF16Char(*str, out); | ||||||
|  |       if (!out[0]) | ||||||
|  |         return false; | ||||||
|  | 
 | ||||||
|  |       // Process one character at a time
 | ||||||
|  |       --length; | ||||||
|  |       ++str; | ||||||
|  | 
 | ||||||
|  |       // Append the one or two UTF-16 characters.  The first one will be non-
 | ||||||
|  |       // zero, but the second one may be zero, depending on the conversion from
 | ||||||
|  |       // UTF-32.
 | ||||||
|  |       int out_count = out[1] ? 2 : 1; | ||||||
|  |       size_t out_size = sizeof(uint16_t) * out_count; | ||||||
|  |       result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); | ||||||
|  |       out_idx += out_count; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::CopyStringToMDString(const char *str, | ||||||
|  |                                               unsigned int length, | ||||||
|  |                                               TypedMDRVA<MDString> *mdstring) { | ||||||
|  |   bool result = true; | ||||||
|  |   uint16_t out[2]; | ||||||
|  |   int out_idx = 0; | ||||||
|  | 
 | ||||||
|  |   // Copy the string character by character
 | ||||||
|  |   while (length && result) { | ||||||
|  |     int conversion_count = UTF8ToUTF16Char(str, length, out); | ||||||
|  |     if (!conversion_count) | ||||||
|  |       return false; | ||||||
|  | 
 | ||||||
|  |     // Move the pointer along based on the nubmer of converted characters
 | ||||||
|  |     length -= conversion_count; | ||||||
|  |     str += conversion_count; | ||||||
|  | 
 | ||||||
|  |     // Append the one or two UTF-16 characters
 | ||||||
|  |     int out_count = out[1] ? 2 : 1; | ||||||
|  |     size_t out_size = sizeof(uint16_t) * out_count; | ||||||
|  |     result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); | ||||||
|  |     out_idx += out_count; | ||||||
|  |   } | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template <typename CharType> | ||||||
|  | bool MinidumpFileWriter::WriteStringCore(const CharType *str, | ||||||
|  |                                          unsigned int length, | ||||||
|  |                                          MDLocationDescriptor *location) { | ||||||
|  |   assert(str); | ||||||
|  |   assert(location); | ||||||
|  |   // Calculate the mdstring length by either limiting to |length| as passed in
 | ||||||
|  |   // or by finding the location of the NULL character.
 | ||||||
|  |   unsigned int mdstring_length = 0; | ||||||
|  |   if (!length) | ||||||
|  |     length = INT_MAX; | ||||||
|  |   for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) | ||||||
|  |     ; | ||||||
|  | 
 | ||||||
|  |   // Allocate the string buffer
 | ||||||
|  |   TypedMDRVA<MDString> mdstring(this); | ||||||
|  |   if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t))) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // Set length excluding the NULL and copy the string
 | ||||||
|  |   mdstring.get()->length = | ||||||
|  |       static_cast<uint32_t>(mdstring_length * sizeof(uint16_t)); | ||||||
|  |   bool result = CopyStringToMDString(str, mdstring_length, &mdstring); | ||||||
|  | 
 | ||||||
|  |   // NULL terminate
 | ||||||
|  |   if (result) { | ||||||
|  |     uint16_t ch = 0; | ||||||
|  |     result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch)); | ||||||
|  | 
 | ||||||
|  |     if (result) | ||||||
|  |       *location = mdstring.location(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length, | ||||||
|  |                  MDLocationDescriptor *location) { | ||||||
|  |   return WriteStringCore(str, length, location); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::WriteString(const char *str, unsigned int length, | ||||||
|  |                  MDLocationDescriptor *location) { | ||||||
|  |   return WriteStringCore(str, length, location); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::WriteMemory(const void *src, size_t size, | ||||||
|  |                                      MDMemoryDescriptor *output) { | ||||||
|  |   assert(src); | ||||||
|  |   assert(output); | ||||||
|  |   UntypedMDRVA mem(this); | ||||||
|  | 
 | ||||||
|  |   if (!mem.Allocate(size)) | ||||||
|  |     return false; | ||||||
|  |   if (!mem.Copy(src, mem.size())) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   output->start_of_memory_range = reinterpret_cast<uint64_t>(src); | ||||||
|  |   output->memory = mem.location(); | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | MDRVA MinidumpFileWriter::Allocate(size_t size) { | ||||||
|  |   assert(size); | ||||||
|  |   assert(file_ != -1); | ||||||
|  |   size_t aligned_size = (size + 7) & ~7;  // 64-bit alignment
 | ||||||
|  | 
 | ||||||
|  |   if (position_ + aligned_size > size_) { | ||||||
|  |     size_t growth = aligned_size; | ||||||
|  |     size_t minimal_growth = getpagesize(); | ||||||
|  | 
 | ||||||
|  |     // Ensure that the file grows by at least the size of a memory page
 | ||||||
|  |     if (growth < minimal_growth) | ||||||
|  |       growth = minimal_growth; | ||||||
|  | 
 | ||||||
|  |     size_t new_size = size_ + growth; | ||||||
|  |     if (ftruncate(file_, new_size) != 0) | ||||||
|  |       return kInvalidMDRVA; | ||||||
|  | 
 | ||||||
|  |     size_ = new_size; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   MDRVA current_position = position_; | ||||||
|  |   position_ += static_cast<MDRVA>(aligned_size); | ||||||
|  | 
 | ||||||
|  |   return current_position; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) { | ||||||
|  |   assert(src); | ||||||
|  |   assert(size); | ||||||
|  |   assert(file_ != -1); | ||||||
|  | 
 | ||||||
|  |   // Ensure that the data will fit in the allocated space
 | ||||||
|  |   if (static_cast<size_t>(size + position) > size_) | ||||||
|  |     return false; | ||||||
|  | 
 | ||||||
|  |   // Seek and write the data
 | ||||||
|  | #if __linux__ | ||||||
|  |   if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) { | ||||||
|  |     if (sys_write(file_, src, size) == size) { | ||||||
|  | #else | ||||||
|  |   if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) { | ||||||
|  |     if (write(file_, src, size) == size) { | ||||||
|  | #endif | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool UntypedMDRVA::Allocate(size_t size) { | ||||||
|  |   assert(size_ == 0); | ||||||
|  |   size_ = size; | ||||||
|  |   position_ = writer_->Allocate(size_); | ||||||
|  |   return position_ != MinidumpFileWriter::kInvalidMDRVA; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool UntypedMDRVA::Copy(MDRVA pos, const void *src, size_t size) { | ||||||
|  |   assert(src); | ||||||
|  |   assert(size); | ||||||
|  |   assert(pos + size <= position_ + size_); | ||||||
|  |   return writer_->Copy(pos, src, size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
							
								
								
									
										272
									
								
								google-breakpad/src/client/minidump_file_writer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								google-breakpad/src/client/minidump_file_writer.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,272 @@ | |||||||
|  | // Copyright (c) 2006, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // minidump_file_writer.h:  Implements file-based minidump generation.  It's
 | ||||||
|  | // intended to be used with the Google Breakpad open source crash handling
 | ||||||
|  | // project.
 | ||||||
|  | 
 | ||||||
|  | #ifndef CLIENT_MINIDUMP_FILE_WRITER_H__ | ||||||
|  | #define CLIENT_MINIDUMP_FILE_WRITER_H__ | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "google_breakpad/common/minidump_format.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | class UntypedMDRVA; | ||||||
|  | template<typename MDType> class TypedMDRVA; | ||||||
|  | 
 | ||||||
|  | // The user of this class can Open() a file and add minidump streams, data, and
 | ||||||
|  | // strings using the definitions in minidump_format.h.  Since this class is
 | ||||||
|  | // expected to be used in a situation where the current process may be
 | ||||||
|  | // damaged, it will not allocate heap memory.
 | ||||||
|  | // Sample usage:
 | ||||||
|  | // MinidumpFileWriter writer;
 | ||||||
|  | // writer.Open("/tmp/minidump.dmp");
 | ||||||
|  | // TypedMDRVA<MDRawHeader> header(&writer_);
 | ||||||
|  | // header.Allocate();
 | ||||||
|  | // header->get()->signature = MD_HEADER_SIGNATURE;
 | ||||||
|  | //  :
 | ||||||
|  | // writer.Close();
 | ||||||
|  | //
 | ||||||
|  | // An alternative is to use SetFile and provide a file descriptor:
 | ||||||
|  | // MinidumpFileWriter writer;
 | ||||||
|  | // writer.SetFile(minidump_fd);
 | ||||||
|  | // TypedMDRVA<MDRawHeader> header(&writer_);
 | ||||||
|  | // header.Allocate();
 | ||||||
|  | // header->get()->signature = MD_HEADER_SIGNATURE;
 | ||||||
|  | //  :
 | ||||||
|  | // writer.Close();
 | ||||||
|  | 
 | ||||||
|  | class MinidumpFileWriter { | ||||||
|  | public: | ||||||
|  |   // Invalid MDRVA (Minidump Relative Virtual Address)
 | ||||||
|  |   // returned on failed allocation
 | ||||||
|  |   static const MDRVA kInvalidMDRVA; | ||||||
|  | 
 | ||||||
|  |   MinidumpFileWriter(); | ||||||
|  |   ~MinidumpFileWriter(); | ||||||
|  | 
 | ||||||
|  |   // Open |path| as the destination of the minidump data.  Any existing file
 | ||||||
|  |   // will be overwritten.
 | ||||||
|  |   // Return true on success, or false on failure.
 | ||||||
|  |   bool Open(const char *path); | ||||||
|  | 
 | ||||||
|  |   // Sets the file descriptor |file| as the destination of the minidump data.
 | ||||||
|  |   // Can be used as an alternative to Open() when a file descriptor is
 | ||||||
|  |   // available.
 | ||||||
|  |   // Note that |fd| is not closed when the instance of MinidumpFileWriter is
 | ||||||
|  |   // destroyed.
 | ||||||
|  |   void SetFile(const int file); | ||||||
|  | 
 | ||||||
|  |   // Close the current file (that was either created when Open was called, or
 | ||||||
|  |   // specified with SetFile).
 | ||||||
|  |   // Return true on success, or false on failure.
 | ||||||
|  |   bool Close(); | ||||||
|  | 
 | ||||||
|  |   // Copy the contents of |str| to a MDString and write it to the file.
 | ||||||
|  |   // |str| is expected to be either UTF-16 or UTF-32 depending on the size
 | ||||||
|  |   // of wchar_t.
 | ||||||
|  |   // Maximum |length| of characters to copy from |str|, or specify 0 to use the
 | ||||||
|  |   // entire NULL terminated string.  Copying will stop at the first NULL.
 | ||||||
|  |   // |location| the allocated location
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool WriteString(const wchar_t *str, unsigned int length, | ||||||
|  |                    MDLocationDescriptor *location); | ||||||
|  | 
 | ||||||
|  |   // Same as above, except with |str| as a UTF-8 string
 | ||||||
|  |   bool WriteString(const char *str, unsigned int length, | ||||||
|  |                    MDLocationDescriptor *location); | ||||||
|  | 
 | ||||||
|  |   // Write |size| bytes starting at |src| into the current position.
 | ||||||
|  |   // Return true on success and set |output| to position, or false on failure
 | ||||||
|  |   bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output); | ||||||
|  | 
 | ||||||
|  |   // Copies |size| bytes from |src| to |position|
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool Copy(MDRVA position, const void *src, ssize_t size); | ||||||
|  | 
 | ||||||
|  |   // Return the current position for writing to the minidump
 | ||||||
|  |   inline MDRVA position() const { return position_; } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   friend class UntypedMDRVA; | ||||||
|  | 
 | ||||||
|  |   // Allocates an area of |size| bytes.
 | ||||||
|  |   // Returns the position of the allocation, or kInvalidMDRVA if it was
 | ||||||
|  |   // unable to allocate the bytes.
 | ||||||
|  |   MDRVA Allocate(size_t size); | ||||||
|  | 
 | ||||||
|  |   // The file descriptor for the output file.
 | ||||||
|  |   int file_; | ||||||
|  | 
 | ||||||
|  |   // Whether |file_| should be closed when the instance is destroyed.
 | ||||||
|  |   bool close_file_when_destroyed_; | ||||||
|  | 
 | ||||||
|  |   // Current position in buffer
 | ||||||
|  |   MDRVA position_; | ||||||
|  | 
 | ||||||
|  |   // Current allocated size
 | ||||||
|  |   size_t size_; | ||||||
|  | 
 | ||||||
|  |   // Copy |length| characters from |str| to |mdstring|.  These are distinct
 | ||||||
|  |   // because the underlying MDString is a UTF-16 based string.  The wchar_t
 | ||||||
|  |   // variant may need to create a MDString that has more characters than the
 | ||||||
|  |   // source |str|, whereas the UTF-8 variant may coalesce characters to form
 | ||||||
|  |   // a single UTF-16 character.
 | ||||||
|  |   bool CopyStringToMDString(const wchar_t *str, unsigned int length, | ||||||
|  |                             TypedMDRVA<MDString> *mdstring); | ||||||
|  |   bool CopyStringToMDString(const char *str, unsigned int length, | ||||||
|  |                             TypedMDRVA<MDString> *mdstring); | ||||||
|  | 
 | ||||||
|  |   // The common templated code for writing a string
 | ||||||
|  |   template <typename CharType> | ||||||
|  |   bool WriteStringCore(const CharType *str, unsigned int length, | ||||||
|  |                        MDLocationDescriptor *location); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Represents an untyped allocated chunk
 | ||||||
|  | class UntypedMDRVA { | ||||||
|  |  public: | ||||||
|  |   explicit UntypedMDRVA(MinidumpFileWriter *writer) | ||||||
|  |       : writer_(writer), | ||||||
|  |         position_(writer->position()), | ||||||
|  |         size_(0) {} | ||||||
|  | 
 | ||||||
|  |   // Allocates |size| bytes.  Must not call more than once.
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool Allocate(size_t size); | ||||||
|  | 
 | ||||||
|  |   // Returns the current position or kInvalidMDRVA if allocation failed
 | ||||||
|  |   inline MDRVA position() const { return position_; } | ||||||
|  | 
 | ||||||
|  |   // Number of bytes allocated
 | ||||||
|  |   inline size_t size() const { return size_; } | ||||||
|  | 
 | ||||||
|  |   // Return size and position
 | ||||||
|  |   inline MDLocationDescriptor location() const { | ||||||
|  |     MDLocationDescriptor location = { static_cast<uint32_t>(size_), | ||||||
|  |                                       position_ }; | ||||||
|  |     return location; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Copy |size| bytes starting at |src| into the minidump at |position|
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool Copy(MDRVA position, const void *src, size_t size); | ||||||
|  | 
 | ||||||
|  |   // Copy |size| bytes from |src| to the current position
 | ||||||
|  |   inline bool Copy(const void *src, size_t size) { | ||||||
|  |     return Copy(position_, src, size); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |  protected: | ||||||
|  |   // Writer we associate with
 | ||||||
|  |   MinidumpFileWriter *writer_; | ||||||
|  | 
 | ||||||
|  |   // Position of the start of the data
 | ||||||
|  |   MDRVA position_; | ||||||
|  | 
 | ||||||
|  |   // Allocated size
 | ||||||
|  |   size_t size_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Represents a Minidump object chunk.  Additional memory can be allocated at
 | ||||||
|  | // the end of the object as a:
 | ||||||
|  | // - single allocation
 | ||||||
|  | // - Array of MDType objects
 | ||||||
|  | // - A MDType object followed by an array
 | ||||||
|  | template<typename MDType> | ||||||
|  | class TypedMDRVA : public UntypedMDRVA { | ||||||
|  |  public: | ||||||
|  |   // Constructs an unallocated MDRVA
 | ||||||
|  |   explicit TypedMDRVA(MinidumpFileWriter *writer) | ||||||
|  |       : UntypedMDRVA(writer), | ||||||
|  |         data_(), | ||||||
|  |         allocation_state_(UNALLOCATED) {} | ||||||
|  | 
 | ||||||
|  |   inline ~TypedMDRVA() { | ||||||
|  |     // Ensure that the data_ object is written out
 | ||||||
|  |     if (allocation_state_ != ARRAY) | ||||||
|  |       Flush(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Address of object data_ of MDType.  This is not declared const as the
 | ||||||
|  |   // typical usage will be to access the underlying |data_| object as to
 | ||||||
|  |   // alter its contents.
 | ||||||
|  |   MDType *get() { return &data_; } | ||||||
|  | 
 | ||||||
|  |   // Allocates minidump_size<MDType>::size() bytes.
 | ||||||
|  |   // Must not call more than once.
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool Allocate(); | ||||||
|  | 
 | ||||||
|  |   // Allocates minidump_size<MDType>::size() + |additional| bytes.
 | ||||||
|  |   // Must not call more than once.
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool Allocate(size_t additional); | ||||||
|  | 
 | ||||||
|  |   // Allocate an array of |count| elements of MDType.
 | ||||||
|  |   // Must not call more than once.
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool AllocateArray(size_t count); | ||||||
|  | 
 | ||||||
|  |   // Allocate an array of |count| elements of |size| after object of MDType
 | ||||||
|  |   // Must not call more than once.
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool AllocateObjectAndArray(size_t count, size_t size); | ||||||
|  | 
 | ||||||
|  |   // Copy |item| to |index|
 | ||||||
|  |   // Must have been allocated using AllocateArray().
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool CopyIndex(unsigned int index, MDType *item); | ||||||
|  | 
 | ||||||
|  |   // Copy |size| bytes starting at |str| to |index|
 | ||||||
|  |   // Must have been allocated using AllocateObjectAndArray().
 | ||||||
|  |   // Return true on success, or false on failure
 | ||||||
|  |   bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size); | ||||||
|  | 
 | ||||||
|  |   // Write data_
 | ||||||
|  |   bool Flush(); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   enum AllocationState { | ||||||
|  |     UNALLOCATED = 0, | ||||||
|  |     SINGLE_OBJECT, | ||||||
|  |     ARRAY, | ||||||
|  |     SINGLE_OBJECT_WITH_ARRAY | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   MDType data_; | ||||||
|  |   AllocationState allocation_state_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // CLIENT_MINIDUMP_FILE_WRITER_H__
 | ||||||
							
								
								
									
										179
									
								
								google-breakpad/src/client/minidump_file_writer_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								google-breakpad/src/client/minidump_file_writer_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,179 @@ | |||||||
|  | // Copyright (c) 2006, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Author: waylonis@google.com (Dan Waylonis)
 | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  g++ -I../ ../common/convert_UTF.c \ | ||||||
|  |  ../common/string_conversion.cc \ | ||||||
|  |  minidump_file_writer.cc \ | ||||||
|  |  minidump_file_writer_unittest.cc \ | ||||||
|  |  -o minidump_file_writer_unittest | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include "minidump_file_writer-inl.h" | ||||||
|  | 
 | ||||||
|  | using google_breakpad::MinidumpFileWriter; | ||||||
|  | 
 | ||||||
|  | #define ASSERT_TRUE(cond) \ | ||||||
|  | if (!(cond)) { \ | ||||||
|  |   fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ | ||||||
|  |     return false; \ | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) | ||||||
|  | #define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2)) | ||||||
|  | 
 | ||||||
|  | struct StringStructure { | ||||||
|  |   unsigned long integer_value; | ||||||
|  |   MDLocationDescriptor first_string; | ||||||
|  |   MDLocationDescriptor second_string; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct ArrayStructure { | ||||||
|  |   unsigned char char_value; | ||||||
|  |   unsigned short short_value; | ||||||
|  |   unsigned long long_value; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |   unsigned long count; | ||||||
|  |   ArrayStructure array[0]; | ||||||
|  | } ObjectAndArrayStructure; | ||||||
|  | 
 | ||||||
|  | static bool WriteFile(const char *path) { | ||||||
|  |   MinidumpFileWriter writer; | ||||||
|  |   if (writer.Open(path)) { | ||||||
|  |     // Test a single structure
 | ||||||
|  |     google_breakpad::TypedMDRVA<StringStructure> strings(&writer); | ||||||
|  |     ASSERT_TRUE(strings.Allocate()); | ||||||
|  |     strings.get()->integer_value = 0xBEEF; | ||||||
|  |     const char *first = "First String"; | ||||||
|  |     ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string)); | ||||||
|  |     const wchar_t *second = L"Second String"; | ||||||
|  |     ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string)); | ||||||
|  | 
 | ||||||
|  |     // Test an array structure
 | ||||||
|  |     google_breakpad::TypedMDRVA<ArrayStructure> array(&writer); | ||||||
|  |     unsigned int count = 10; | ||||||
|  |     ASSERT_TRUE(array.AllocateArray(count)); | ||||||
|  |     for (unsigned char i = 0; i < count; ++i) { | ||||||
|  |       ArrayStructure local; | ||||||
|  |       local.char_value = i; | ||||||
|  |       local.short_value = i + 1; | ||||||
|  |       local.long_value = i + 2; | ||||||
|  |       ASSERT_TRUE(array.CopyIndex(i, &local)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Test an object followed by an array
 | ||||||
|  |     google_breakpad::TypedMDRVA<ObjectAndArrayStructure> obj_array(&writer); | ||||||
|  |     ASSERT_TRUE(obj_array.AllocateObjectAndArray(count, | ||||||
|  |                                                  sizeof(ArrayStructure))); | ||||||
|  |     obj_array.get()->count = count; | ||||||
|  |     for (unsigned char i = 0; i < count; ++i) { | ||||||
|  |       ArrayStructure local; | ||||||
|  |       local.char_value = i; | ||||||
|  |       local.short_value = i + 1; | ||||||
|  |       local.long_value = i + 2; | ||||||
|  |       ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local))); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return writer.Close(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool CompareFile(const char *path) { | ||||||
|  |   unsigned long expected[] = { | ||||||
|  | #if defined(__BIG_ENDIAN__) | ||||||
|  |     0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,  | ||||||
|  |     0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069, | ||||||
|  |     0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064, | ||||||
|  |     0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002, | ||||||
|  |     0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005, | ||||||
|  |     0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008, | ||||||
|  |     0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b, | ||||||
|  |     0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003, | ||||||
|  |     0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006, | ||||||
|  |     0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009, | ||||||
|  |     0x0000000a, 0x0900000a, 0x0000000b, 0x00000000 | ||||||
|  | #else | ||||||
|  |     0x0000beef, 0x0000001e, 0x00000018, 0x00000020, | ||||||
|  |     0x00000038, 0x00000000, 0x00000018, 0x00690046, | ||||||
|  |     0x00730072, 0x00200074, 0x00740053, 0x00690072, | ||||||
|  |     0x0067006e, 0x00000000, 0x0000001a, 0x00650053, | ||||||
|  |     0x006f0063, 0x0064006e, 0x00530020, 0x00720074, | ||||||
|  |     0x006e0069, 0x00000067, 0x00011e00, 0x00000002, | ||||||
|  |     0x00021e01, 0x00000003, 0x00031e02, 0x00000004, | ||||||
|  |     0x00041e03, 0x00000005, 0x00051e04, 0x00000006, | ||||||
|  |     0x00061e05, 0x00000007, 0x00071e06, 0x00000008, | ||||||
|  |     0x00081e07, 0x00000009, 0x00091e08, 0x0000000a, | ||||||
|  |     0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00, | ||||||
|  |     0x00000002, 0x00021c01, 0x00000003, 0x00031c02, | ||||||
|  |     0x00000004, 0x00041c03, 0x00000005, 0x00051c04, | ||||||
|  |     0x00000006, 0x00061c05, 0x00000007, 0x00071c06, | ||||||
|  |     0x00000008, 0x00081c07, 0x00000009, 0x00091c08, | ||||||
|  |     0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000, | ||||||
|  | #endif | ||||||
|  |   }; | ||||||
|  |   size_t expected_byte_count = sizeof(expected); | ||||||
|  |   int fd = open(path, O_RDONLY, 0600); | ||||||
|  |   void *buffer = malloc(expected_byte_count); | ||||||
|  |   ASSERT_NE(fd, -1); | ||||||
|  |   ASSERT_TRUE(buffer); | ||||||
|  |   ASSERT_EQ(read(fd, buffer, expected_byte_count),  | ||||||
|  |             static_cast<ssize_t>(expected_byte_count)); | ||||||
|  | 
 | ||||||
|  |   char *b1, *b2; | ||||||
|  |   b1 = reinterpret_cast<char*>(buffer); | ||||||
|  |   b2 = reinterpret_cast<char*>(expected); | ||||||
|  |   while (*b1 == *b2) { | ||||||
|  |     b1++; | ||||||
|  |     b2++; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   printf("%p\n", reinterpret_cast<void*>(b1 - (char*)buffer)); | ||||||
|  | 
 | ||||||
|  |   ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool RunTests() { | ||||||
|  |   const char *path = "/tmp/minidump_file_writer_unittest.dmp"; | ||||||
|  |   ASSERT_TRUE(WriteFile(path)); | ||||||
|  |   ASSERT_TRUE(CompareFile(path)); | ||||||
|  |   unlink(path); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | extern "C" int main(int argc, const char *argv[]) { | ||||||
|  |   return RunTests() ? 0 : 1; | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								google-breakpad/src/common/basictypes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								google-breakpad/src/common/basictypes.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,41 @@ | |||||||
|  | // Copyright (c) 2011 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_BASICTYPES_H_ | ||||||
|  | #define COMMON_BASICTYPES_H_ | ||||||
|  | 
 | ||||||
|  | // A macro to disallow the copy constructor and operator= functions
 | ||||||
|  | // This should be used in the private: declarations for a class
 | ||||||
|  | #ifndef DISALLOW_COPY_AND_ASSIGN | ||||||
|  | #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ | ||||||
|  |   TypeName(const TypeName&);               \ | ||||||
|  |   void operator=(const TypeName&) | ||||||
|  | #endif  // DISALLOW_COPY_AND_ASSIGN
 | ||||||
|  | 
 | ||||||
|  | #endif  // COMMON_BASICTYPES_H_
 | ||||||
							
								
								
									
										265
									
								
								google-breakpad/src/common/byte_cursor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								google-breakpad/src/common/byte_cursor.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,265 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // byte_cursor.h: Classes for parsing values from a buffer of bytes.
 | ||||||
|  | // The ByteCursor class provides a convenient interface for reading
 | ||||||
|  | // fixed-size integers of arbitrary endianness, being thorough about
 | ||||||
|  | // checking for buffer overruns.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_BYTE_CURSOR_H_ | ||||||
|  | #define COMMON_BYTE_CURSOR_H_ | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <stdint.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // A buffer holding a series of bytes.
 | ||||||
|  | struct ByteBuffer { | ||||||
|  |   ByteBuffer() : start(0), end(0) { } | ||||||
|  |   ByteBuffer(const uint8_t *set_start, size_t set_size) | ||||||
|  |       : start(set_start), end(set_start + set_size) { } | ||||||
|  |   ~ByteBuffer() { }; | ||||||
|  | 
 | ||||||
|  |   // Equality operators. Useful in unit tests, and when we're using
 | ||||||
|  |   // ByteBuffers to refer to regions of a larger buffer.
 | ||||||
|  |   bool operator==(const ByteBuffer &that) const { | ||||||
|  |     return start == that.start && end == that.end; | ||||||
|  |   } | ||||||
|  |   bool operator!=(const ByteBuffer &that) const { | ||||||
|  |     return start != that.start || end != that.end; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Not C++ style guide compliant, but this definitely belongs here.
 | ||||||
|  |   size_t Size() const { | ||||||
|  |     assert(start <= end); | ||||||
|  |     return end - start; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const uint8_t *start, *end; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // A cursor pointing into a ByteBuffer that can parse numbers of various
 | ||||||
|  | // widths and representations, strings, and data blocks, advancing through
 | ||||||
|  | // the buffer as it goes. All ByteCursor operations check that accesses
 | ||||||
|  | // haven't gone beyond the end of the enclosing ByteBuffer.
 | ||||||
|  | class ByteCursor { | ||||||
|  |  public: | ||||||
|  |   // Create a cursor reading bytes from the start of BUFFER. By default, the
 | ||||||
|  |   // cursor reads multi-byte values in little-endian form.
 | ||||||
|  |   ByteCursor(const ByteBuffer *buffer, bool big_endian = false) | ||||||
|  |       : buffer_(buffer), here_(buffer->start),  | ||||||
|  |         big_endian_(big_endian), complete_(true) { } | ||||||
|  | 
 | ||||||
|  |   // Accessor and setter for this cursor's endianness flag.
 | ||||||
|  |   bool big_endian() const { return big_endian_; } | ||||||
|  |   void set_big_endian(bool big_endian) { big_endian_ = big_endian; } | ||||||
|  | 
 | ||||||
|  |   // Accessor and setter for this cursor's current position. The setter
 | ||||||
|  |   // returns a reference to this cursor.
 | ||||||
|  |   const uint8_t *here() const { return here_; } | ||||||
|  |   ByteCursor &set_here(const uint8_t *here) { | ||||||
|  |     assert(buffer_->start <= here && here <= buffer_->end); | ||||||
|  |     here_ = here; | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Return the number of bytes available to read at the cursor.
 | ||||||
|  |   size_t Available() const { return size_t(buffer_->end - here_); } | ||||||
|  | 
 | ||||||
|  |   // Return true if this cursor is at the end of its buffer.
 | ||||||
|  |   bool AtEnd() const { return Available() == 0; } | ||||||
|  | 
 | ||||||
|  |   // When used as a boolean value this cursor converts to true if all
 | ||||||
|  |   // prior reads have been completed, or false if we ran off the end
 | ||||||
|  |   // of the buffer.
 | ||||||
|  |   operator bool() const { return complete_; } | ||||||
|  | 
 | ||||||
|  |   // Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true,
 | ||||||
|  |   // unsigned otherwise, using the cursor's established endianness, and set
 | ||||||
|  |   // *RESULT to the number. If we read off the end of our buffer, clear
 | ||||||
|  |   // this cursor's complete_ flag, and store a dummy value in *RESULT.
 | ||||||
|  |   // Return a reference to this cursor.
 | ||||||
|  |   template<typename T> | ||||||
|  |   ByteCursor &Read(size_t size, bool is_signed, T *result) { | ||||||
|  |     if (CheckAvailable(size)) { | ||||||
|  |       T v = 0; | ||||||
|  |       if (big_endian_) { | ||||||
|  |         for (size_t i = 0; i < size; i++) | ||||||
|  |           v = (v << 8) + here_[i]; | ||||||
|  |       } else { | ||||||
|  |         // This loop condition looks weird, but size_t is unsigned, so
 | ||||||
|  |         // decrementing i after it is zero yields the largest size_t value.
 | ||||||
|  |         for (size_t i = size - 1; i < size; i--) | ||||||
|  |           v = (v << 8) + here_[i]; | ||||||
|  |       } | ||||||
|  |       if (is_signed && size < sizeof(T)) { | ||||||
|  |         size_t sign_bit = (T)1 << (size * 8 - 1); | ||||||
|  |         v = (v ^ sign_bit) - sign_bit; | ||||||
|  |       } | ||||||
|  |       here_ += size; | ||||||
|  |       *result = v; | ||||||
|  |     } else { | ||||||
|  |       *result = (T) 0xdeadbeef; | ||||||
|  |     } | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Read an integer, using the cursor's established endianness and
 | ||||||
|  |   // *RESULT's size and signedness, and set *RESULT to the number. If we
 | ||||||
|  |   // read off the end of our buffer, clear this cursor's complete_ flag.
 | ||||||
|  |   // Return a reference to this cursor.
 | ||||||
|  |   template<typename T> | ||||||
|  |   ByteCursor &operator>>(T &result) { | ||||||
|  |     bool T_is_signed = (T)-1 < 0; | ||||||
|  |     return Read(sizeof(T), T_is_signed, &result);  | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Copy the SIZE bytes at the cursor to BUFFER, and advance this
 | ||||||
|  |   // cursor to the end of them. If we read off the end of our buffer,
 | ||||||
|  |   // clear this cursor's complete_ flag, and set *POINTER to NULL.
 | ||||||
|  |   // Return a reference to this cursor.
 | ||||||
|  |   ByteCursor &Read(uint8_t *buffer, size_t size) { | ||||||
|  |     if (CheckAvailable(size)) { | ||||||
|  |       memcpy(buffer, here_, size); | ||||||
|  |       here_ += size; | ||||||
|  |     } | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Set STR to a copy of the '\0'-terminated string at the cursor. If the
 | ||||||
|  |   // byte buffer does not contain a terminating zero, clear this cursor's
 | ||||||
|  |   // complete_ flag, and set STR to the empty string. Return a reference to
 | ||||||
|  |   // this cursor.
 | ||||||
|  |   ByteCursor &CString(string *str) { | ||||||
|  |     const uint8_t *end | ||||||
|  |       = static_cast<const uint8_t *>(memchr(here_, '\0', Available())); | ||||||
|  |     if (end) { | ||||||
|  |       str->assign(reinterpret_cast<const char *>(here_), end - here_); | ||||||
|  |       here_ = end + 1; | ||||||
|  |     } else { | ||||||
|  |       str->clear(); | ||||||
|  |       here_ = buffer_->end; | ||||||
|  |       complete_ = false; | ||||||
|  |     } | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Like CString(STR), but extract the string from a fixed-width buffer
 | ||||||
|  |   // LIMIT bytes long, which may or may not contain a terminating '\0'
 | ||||||
|  |   // byte. Specifically:
 | ||||||
|  |   //
 | ||||||
|  |   // - If there are not LIMIT bytes available at the cursor, clear the
 | ||||||
|  |   //   cursor's complete_ flag and set STR to the empty string.
 | ||||||
|  |   //
 | ||||||
|  |   // - Otherwise, if the LIMIT bytes at the cursor contain any '\0'
 | ||||||
|  |   //   characters, set *STR to a copy of the bytes before the first '\0',
 | ||||||
|  |   //   and advance the cursor by LIMIT bytes.
 | ||||||
|  |   //   
 | ||||||
|  |   // - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the
 | ||||||
|  |   //   cursor by LIMIT bytes.
 | ||||||
|  |   ByteCursor &CString(string *str, size_t limit) { | ||||||
|  |     if (CheckAvailable(limit)) { | ||||||
|  |       const uint8_t *end | ||||||
|  |         = static_cast<const uint8_t *>(memchr(here_, '\0', limit)); | ||||||
|  |       if (end) | ||||||
|  |         str->assign(reinterpret_cast<const char *>(here_), end - here_); | ||||||
|  |       else | ||||||
|  |         str->assign(reinterpret_cast<const char *>(here_), limit); | ||||||
|  |       here_ += limit; | ||||||
|  |     } else { | ||||||
|  |       str->clear(); | ||||||
|  |     } | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Set *POINTER to point to the SIZE bytes at the cursor, and advance
 | ||||||
|  |   // this cursor to the end of them. If SIZE is omitted, don't move the
 | ||||||
|  |   // cursor. If we read off the end of our buffer, clear this cursor's
 | ||||||
|  |   // complete_ flag, and set *POINTER to NULL. Return a reference to this
 | ||||||
|  |   // cursor.
 | ||||||
|  |   ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) { | ||||||
|  |     if (CheckAvailable(size)) { | ||||||
|  |       *pointer = here_; | ||||||
|  |       here_ += size; | ||||||
|  |     } else { | ||||||
|  |       *pointer = NULL; | ||||||
|  |     } | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Skip SIZE bytes at the cursor. If doing so would advance us off
 | ||||||
|  |   // the end of our buffer, clear this cursor's complete_ flag, and
 | ||||||
|  |   // set *POINTER to NULL. Return a reference to this cursor.
 | ||||||
|  |   ByteCursor &Skip(size_t size) { | ||||||
|  |     if (CheckAvailable(size)) | ||||||
|  |       here_ += size; | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // If there are at least SIZE bytes available to read from the buffer,
 | ||||||
|  |   // return true. Otherwise, set here_ to the end of the buffer, set
 | ||||||
|  |   // complete_ to false, and return false.
 | ||||||
|  |   bool CheckAvailable(size_t size) { | ||||||
|  |     if (Available() >= size) { | ||||||
|  |       return true; | ||||||
|  |     } else { | ||||||
|  |       here_ = buffer_->end; | ||||||
|  |       complete_ = false; | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // The buffer we're reading bytes from.
 | ||||||
|  |   const ByteBuffer *buffer_; | ||||||
|  | 
 | ||||||
|  |   // The next byte within buffer_ that we'll read.
 | ||||||
|  |   const uint8_t *here_; | ||||||
|  | 
 | ||||||
|  |   // True if we should read numbers in big-endian form; false if we
 | ||||||
|  |   // should read in little-endian form.
 | ||||||
|  |   bool big_endian_; | ||||||
|  | 
 | ||||||
|  |   // True if we've been able to read all we've been asked to.
 | ||||||
|  |   bool complete_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // COMMON_BYTE_CURSOR_H_
 | ||||||
							
								
								
									
										776
									
								
								google-breakpad/src/common/byte_cursor_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										776
									
								
								google-breakpad/src/common/byte_cursor_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,776 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer
 | ||||||
|  | // and google_breakpad::ByteCursor.
 | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "common/byte_cursor.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | using google_breakpad::ByteBuffer; | ||||||
|  | using google_breakpad::ByteCursor; | ||||||
|  | 
 | ||||||
|  | TEST(Buffer, SizeOfNothing) { | ||||||
|  |   uint8_t data[1]; | ||||||
|  |   ByteBuffer buffer(data, 0); | ||||||
|  |   EXPECT_EQ(0U, buffer.Size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Buffer, SizeOfSomething) { | ||||||
|  |   uint8_t data[10]; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   EXPECT_EQ(10U, buffer.Size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, AvailableEmpty) { | ||||||
|  |   uint8_t data[1]; | ||||||
|  |   ByteBuffer buffer(data, 0); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   EXPECT_EQ(0U, cursor.Available()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, AtEndEmpty) { | ||||||
|  |   uint8_t data[1]; | ||||||
|  |   ByteBuffer buffer(data, 0); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, AsBoolEmpty) { | ||||||
|  |   uint8_t data[1]; | ||||||
|  |   ByteBuffer buffer(data, 0); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   EXPECT_TRUE(cursor); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, AvailableSome) { | ||||||
|  |   uint8_t data[10]; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   EXPECT_EQ(10U, cursor.Available()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, AtEndSome) { | ||||||
|  |   uint8_t data[10]; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   EXPECT_FALSE(cursor.AtEnd()); | ||||||
|  |   EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, AsBoolSome) { | ||||||
|  |   uint8_t data[10]; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   EXPECT_TRUE(cursor); | ||||||
|  |   EXPECT_TRUE(cursor.Skip(sizeof(data))); | ||||||
|  |   EXPECT_FALSE(cursor.Skip(1)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, Cursor) { | ||||||
|  |   uint8_t data[] = { 0xf7, | ||||||
|  |                      0x9f, 0xbe, | ||||||
|  |                      0x67, 0xfb, 0xd3, 0x58, | ||||||
|  |                      0x6f, 0x36, 0xde, 0xd1, | ||||||
|  |                      0x2a, 0x2a, 0x2a }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   uint8_t a; | ||||||
|  |   uint16_t b; | ||||||
|  |   uint32_t c; | ||||||
|  |   uint32_t d; | ||||||
|  |   uint8_t stars[3]; | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ(data + 0U, cursor.here()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(data + 1U, cursor.here()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor >> b); | ||||||
|  |   EXPECT_EQ(data + 3U, cursor.here()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor >> c); | ||||||
|  |   EXPECT_EQ(data + 7U, cursor.here()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.Skip(4)); | ||||||
|  |   EXPECT_EQ(data + 11U, cursor.here()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.Read(stars, 3)); | ||||||
|  |   EXPECT_EQ(data + 14U, cursor.here()); | ||||||
|  | 
 | ||||||
|  |   EXPECT_FALSE(cursor >> d); | ||||||
|  |   EXPECT_EQ(data + 14U, cursor.here()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extent, SetOffset) { | ||||||
|  |   uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   uint8_t a, b, c, d, e; | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(0x5cU, a); | ||||||
|  |   EXPECT_EQ(data + 1U, cursor.here()); | ||||||
|  |   EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1) | ||||||
|  |               >> d >> e); | ||||||
|  |   EXPECT_EQ(0x79U, b); | ||||||
|  |   EXPECT_EQ(0xd5U, c); | ||||||
|  |   EXPECT_EQ(0x79U, d); | ||||||
|  |   EXPECT_EQ(0x8cU, e); | ||||||
|  |   EXPECT_EQ(data + 3U, cursor.here()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Signed1) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   cursor.set_big_endian(true); | ||||||
|  |   int a, b, c, d, e; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(1, true, &a) | ||||||
|  |               .Read(1, true, &b) | ||||||
|  |               .Read(1, true, &c) | ||||||
|  |               .Read(1, true, &d)); | ||||||
|  |   EXPECT_EQ(0,     a); | ||||||
|  |   EXPECT_EQ(0x7f,  b); | ||||||
|  |   EXPECT_EQ(-0x80, c); | ||||||
|  |   EXPECT_EQ(-1,    d); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(1, true, &e)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Signed2) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00,   0x00, 0x80,   0x7f, 0xff, | ||||||
|  |                      0x80, 0x00,   0x80, 0x80,   0xff, 0xff, | ||||||
|  |                      0x39, 0xf1,   0x8a, 0xbc,   0x5a, 0xec }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer, true); | ||||||
|  |   int a, b, c, d, e, f, g, h, i, j; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(2, true, &a) | ||||||
|  |               .Read(2, true, &b) | ||||||
|  |               .Read(2, true, &c) | ||||||
|  |               .Read(2, true, &d) | ||||||
|  |               .Read(2, true, &e) | ||||||
|  |               .Read(2, true, &f) | ||||||
|  |               .Read(2, true, &g) | ||||||
|  |               .Read(2, true, &h) | ||||||
|  |               .Read(2, true, &i)); | ||||||
|  |   EXPECT_EQ(0,       a); | ||||||
|  |   EXPECT_EQ(0x80,    b); | ||||||
|  |   EXPECT_EQ(0x7fff,  c); | ||||||
|  |   EXPECT_EQ(-0x8000, d); | ||||||
|  |   EXPECT_EQ(-0x7f80, e); | ||||||
|  |   EXPECT_EQ(-1,      f); | ||||||
|  |   EXPECT_EQ(0x39f1,  g); | ||||||
|  |   EXPECT_EQ(-0x7544, h); | ||||||
|  |   EXPECT_EQ(0x5aec,  i); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(2, true, &j)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Signed4) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||||
|  |                      0x7f, 0xff, 0xff, 0xff, | ||||||
|  |                      0x80, 0x00, 0x00, 0x00, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0xb6, 0xb1, 0xff, 0xef, | ||||||
|  |                      0x19, 0x6a, 0xca, 0x46 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   cursor.set_big_endian(true); | ||||||
|  |   int64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(4, true, &a) | ||||||
|  |               .Read(4, true, &b) | ||||||
|  |               .Read(4, true, &c) | ||||||
|  |               .Read(4, true, &d) | ||||||
|  |               .Read(4, true, &e) | ||||||
|  |               .Read(4, true, &f)); | ||||||
|  |   EXPECT_EQ(0,                    a); | ||||||
|  |   EXPECT_EQ(0x7fffffff,           b); | ||||||
|  |   EXPECT_EQ(-0x80000000LL,        c); | ||||||
|  |   EXPECT_EQ(-1,                   d); | ||||||
|  |   EXPECT_EQ((int32_t) 0xb6b1ffef, e); | ||||||
|  |   EXPECT_EQ(0x196aca46,           f); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(4, true, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Signed8) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||||
|  |                      0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, | ||||||
|  |                      0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer, true); | ||||||
|  |   int64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(8, true, &a) | ||||||
|  |               .Read(8, true, &b) | ||||||
|  |               .Read(8, true, &c) | ||||||
|  |               .Read(8, true, &d) | ||||||
|  |               .Read(8, true, &e) | ||||||
|  |               .Read(8, true, &f)); | ||||||
|  |   EXPECT_EQ(0,                               a); | ||||||
|  |   EXPECT_EQ(0x7fffffffffffffffLL,            b); | ||||||
|  |   EXPECT_EQ(-0x7fffffffffffffffLL - 1,       c); | ||||||
|  |   EXPECT_EQ(-1,                              d); | ||||||
|  |   EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); | ||||||
|  |   EXPECT_EQ(0x4e4249d27f8414a4LL,            f);   | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(8, true, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Unsigned1) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   cursor.set_big_endian(true); | ||||||
|  |   int32_t a, b, c, d, e; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(1, false, &a) | ||||||
|  |               .Read(1, false, &b) | ||||||
|  |               .Read(1, false, &c) | ||||||
|  |               .Read(1, false, &d)); | ||||||
|  |   EXPECT_EQ(0,    a); | ||||||
|  |   EXPECT_EQ(0x7f, b); | ||||||
|  |   EXPECT_EQ(0x80, c); | ||||||
|  |   EXPECT_EQ(0xff, d); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(1, false, &e)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Unsigned2) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00,   0x00, 0x80,   0x7f, 0xff, | ||||||
|  |                      0x80, 0x00,   0x80, 0x80,   0xff, 0xff, | ||||||
|  |                      0x39, 0xf1,   0x8a, 0xbc,   0x5a, 0xec }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer, true); | ||||||
|  |   int64_t a, b, c, d, e, f, g, h, i, j; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(2, false, &a) | ||||||
|  |               .Read(2, false, &b) | ||||||
|  |               .Read(2, false, &c) | ||||||
|  |               .Read(2, false, &d) | ||||||
|  |               .Read(2, false, &e) | ||||||
|  |               .Read(2, false, &f) | ||||||
|  |               .Read(2, false, &g) | ||||||
|  |               .Read(2, false, &h) | ||||||
|  |               .Read(2, false, &i)); | ||||||
|  |   EXPECT_EQ(0,      a); | ||||||
|  |   EXPECT_EQ(0x80,   b); | ||||||
|  |   EXPECT_EQ(0x7fff, c); | ||||||
|  |   EXPECT_EQ(0x8000, d); | ||||||
|  |   EXPECT_EQ(0x8080, e); | ||||||
|  |   EXPECT_EQ(0xffff, f); | ||||||
|  |   EXPECT_EQ(0x39f1, g); | ||||||
|  |   EXPECT_EQ(0x8abc, h); | ||||||
|  |   EXPECT_EQ(0x5aec, i); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(2, false, &j)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Unsigned4) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||||
|  |                      0x7f, 0xff, 0xff, 0xff, | ||||||
|  |                      0x80, 0x00, 0x00, 0x00, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0xb6, 0xb1, 0xff, 0xef, | ||||||
|  |                      0x19, 0x6a, 0xca, 0x46 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   cursor.set_big_endian(true); | ||||||
|  |   int64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(4, false, &a) | ||||||
|  |               .Read(4, false, &b) | ||||||
|  |               .Read(4, false, &c) | ||||||
|  |               .Read(4, false, &d) | ||||||
|  |               .Read(4, false, &e) | ||||||
|  |               .Read(4, false, &f)); | ||||||
|  |   EXPECT_EQ(0,          a); | ||||||
|  |   EXPECT_EQ(0x7fffffff, b); | ||||||
|  |   EXPECT_EQ(0x80000000, c); | ||||||
|  |   EXPECT_EQ(0xffffffff, d); | ||||||
|  |   EXPECT_EQ(0xb6b1ffef, e); | ||||||
|  |   EXPECT_EQ(0x196aca46, f); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(4, false, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(BigEndian, Unsigned8) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||||
|  |                      0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, | ||||||
|  |                      0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer, true); | ||||||
|  |   uint64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(8, false, &a) | ||||||
|  |               .Read(8, false, &b) | ||||||
|  |               .Read(8, false, &c) | ||||||
|  |               .Read(8, false, &d) | ||||||
|  |               .Read(8, false, &e) | ||||||
|  |               .Read(8, false, &f)); | ||||||
|  |   EXPECT_EQ(0U,                    a); | ||||||
|  |   EXPECT_EQ(0x7fffffffffffffffULL, b); | ||||||
|  |   EXPECT_EQ(0x8000000000000000ULL, c); | ||||||
|  |   EXPECT_EQ(0xffffffffffffffffULL, d); | ||||||
|  |   EXPECT_EQ(0x9320d5e9d2d5879cULL, e); | ||||||
|  |   EXPECT_EQ(0x4e4249d27f8414a4ULL, f);   | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(8, false, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Signed1) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int32_t a, b, c, d, e; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(1, true, &a) | ||||||
|  |               .Read(1, true, &b) | ||||||
|  |               .Read(1, true, &c) | ||||||
|  |               .Read(1, true, &d)); | ||||||
|  |   EXPECT_EQ(0,     a); | ||||||
|  |   EXPECT_EQ(0x7f,  b); | ||||||
|  |   EXPECT_EQ(-0x80, c); | ||||||
|  |   EXPECT_EQ(-1,    d); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(1, true, &e)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Signed2) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00,   0x80, 0x00,   0xff, 0x7f, | ||||||
|  |                      0x00, 0x80,   0x80, 0x80,   0xff, 0xff, | ||||||
|  |                      0xf1, 0x39,   0xbc, 0x8a,   0xec, 0x5a }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer, false); | ||||||
|  |   int32_t a, b, c, d, e, f, g, h, i, j; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(2, true, &a) | ||||||
|  |               .Read(2, true, &b) | ||||||
|  |               .Read(2, true, &c) | ||||||
|  |               .Read(2, true, &d) | ||||||
|  |               .Read(2, true, &e) | ||||||
|  |               .Read(2, true, &f) | ||||||
|  |               .Read(2, true, &g) | ||||||
|  |               .Read(2, true, &h) | ||||||
|  |               .Read(2, true, &i)); | ||||||
|  |   EXPECT_EQ(0,       a); | ||||||
|  |   EXPECT_EQ(0x80,    b); | ||||||
|  |   EXPECT_EQ(0x7fff,  c); | ||||||
|  |   EXPECT_EQ(-0x8000, d); | ||||||
|  |   EXPECT_EQ(-0x7f80, e); | ||||||
|  |   EXPECT_EQ(-1,      f); | ||||||
|  |   EXPECT_EQ(0x39f1,  g); | ||||||
|  |   EXPECT_EQ(-0x7544, h); | ||||||
|  |   EXPECT_EQ(0x5aec,  i); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(2, true, &j)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Signed4) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||||
|  |                      0xff, 0xff, 0xff, 0x7f, | ||||||
|  |                      0x00, 0x00, 0x00, 0x80, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0xef, 0xff, 0xb1, 0xb6,  | ||||||
|  |                      0x46, 0xca, 0x6a, 0x19 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(4, true, &a) | ||||||
|  |               .Read(4, true, &b) | ||||||
|  |               .Read(4, true, &c) | ||||||
|  |               .Read(4, true, &d) | ||||||
|  |               .Read(4, true, &e) | ||||||
|  |               .Read(4, true, &f)); | ||||||
|  |   EXPECT_EQ(0,                    a); | ||||||
|  |   EXPECT_EQ(0x7fffffff,           b); | ||||||
|  |   EXPECT_EQ(-0x80000000LL,        c); | ||||||
|  |   EXPECT_EQ(-1,                   d); | ||||||
|  |   EXPECT_EQ((int32_t) 0xb6b1ffef, e); | ||||||
|  |   EXPECT_EQ(0x196aca46,           f); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(4, true, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Signed8) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, | ||||||
|  |                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, | ||||||
|  |                      0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer, false); | ||||||
|  |   int64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(8, true, &a) | ||||||
|  |               .Read(8, true, &b) | ||||||
|  |               .Read(8, true, &c) | ||||||
|  |               .Read(8, true, &d) | ||||||
|  |               .Read(8, true, &e) | ||||||
|  |               .Read(8, true, &f)); | ||||||
|  |   EXPECT_EQ(0,                               a); | ||||||
|  |   EXPECT_EQ(0x7fffffffffffffffLL,            b); | ||||||
|  |   EXPECT_EQ(-0x7fffffffffffffffLL - 1,       c); | ||||||
|  |   EXPECT_EQ(-1,                              d); | ||||||
|  |   EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); | ||||||
|  |   EXPECT_EQ(0x4e4249d27f8414a4LL,            f);   | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(8, true, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Unsigned1) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int32_t a, b, c, d, e; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(1, false, &a) | ||||||
|  |               .Read(1, false, &b) | ||||||
|  |               .Read(1, false, &c) | ||||||
|  |               .Read(1, false, &d)); | ||||||
|  |   EXPECT_EQ(0,    a); | ||||||
|  |   EXPECT_EQ(0x7f, b); | ||||||
|  |   EXPECT_EQ(0x80, c); | ||||||
|  |   EXPECT_EQ(0xff, d); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(1, false, &e)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Unsigned2) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00,   0x80, 0x00,   0xff, 0x7f, | ||||||
|  |                      0x00, 0x80,   0x80, 0x80,   0xff, 0xff, | ||||||
|  |                      0xf1, 0x39,   0xbc, 0x8a,   0xec, 0x5a }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int32_t a, b, c, d, e, f, g, h, i, j; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(2, false, &a) | ||||||
|  |               .Read(2, false, &b) | ||||||
|  |               .Read(2, false, &c) | ||||||
|  |               .Read(2, false, &d) | ||||||
|  |               .Read(2, false, &e) | ||||||
|  |               .Read(2, false, &f) | ||||||
|  |               .Read(2, false, &g) | ||||||
|  |               .Read(2, false, &h) | ||||||
|  |               .Read(2, false, &i)); | ||||||
|  |   EXPECT_EQ(0,      a); | ||||||
|  |   EXPECT_EQ(0x80,   b); | ||||||
|  |   EXPECT_EQ(0x7fff, c); | ||||||
|  |   EXPECT_EQ(0x8000, d); | ||||||
|  |   EXPECT_EQ(0x8080, e); | ||||||
|  |   EXPECT_EQ(0xffff, f); | ||||||
|  |   EXPECT_EQ(0x39f1, g); | ||||||
|  |   EXPECT_EQ(0x8abc, h); | ||||||
|  |   EXPECT_EQ(0x5aec, i); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(2, false, &j)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Unsigned4) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||||
|  |                      0xff, 0xff, 0xff, 0x7f, | ||||||
|  |                      0x00, 0x00, 0x00, 0x80, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0xef, 0xff, 0xb1, 0xb6, | ||||||
|  |                      0x46, 0xca, 0x6a, 0x19 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(4, false, &a) | ||||||
|  |               .Read(4, false, &b) | ||||||
|  |               .Read(4, false, &c) | ||||||
|  |               .Read(4, false, &d) | ||||||
|  |               .Read(4, false, &e) | ||||||
|  |               .Read(4, false, &f)); | ||||||
|  |   EXPECT_EQ(0,          a); | ||||||
|  |   EXPECT_EQ(0x7fffffff, b); | ||||||
|  |   EXPECT_EQ(0x80000000, c); | ||||||
|  |   EXPECT_EQ(0xffffffff, d); | ||||||
|  |   EXPECT_EQ(0xb6b1ffef, e); | ||||||
|  |   EXPECT_EQ(0x196aca46, f); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(4, false, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(LittleEndian, Unsigned8) { | ||||||
|  |   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, | ||||||
|  |                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, | ||||||
|  |                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||||
|  |                      0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, | ||||||
|  |                      0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   uint64_t a, b, c, d, e, f, g; | ||||||
|  |   ASSERT_TRUE(cursor | ||||||
|  |               .Read(8, false, &a) | ||||||
|  |               .Read(8, false, &b) | ||||||
|  |               .Read(8, false, &c) | ||||||
|  |               .Read(8, false, &d) | ||||||
|  |               .Read(8, false, &e) | ||||||
|  |               .Read(8, false, &f)); | ||||||
|  |   EXPECT_EQ(0U,                    a); | ||||||
|  |   EXPECT_EQ(0x7fffffffffffffffULL, b); | ||||||
|  |   EXPECT_EQ(0x8000000000000000ULL, c); | ||||||
|  |   EXPECT_EQ(0xffffffffffffffffULL, d); | ||||||
|  |   EXPECT_EQ(0x9320d5e9d2d5879cULL, e); | ||||||
|  |   EXPECT_EQ(0x4e4249d27f8414a4ULL, f);   | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor.Read(8, false, &g)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extractor, Signed1) { | ||||||
|  |   uint8_t data[] = { 0xfd }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int8_t a; | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(-3, a); | ||||||
|  |   EXPECT_FALSE(cursor >> a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extractor, Signed2) { | ||||||
|  |   uint8_t data[] = { 0x13, 0xcd }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int16_t a; | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(-13037, a); | ||||||
|  |   EXPECT_FALSE(cursor >> a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extractor, Signed4) { | ||||||
|  |   uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   int32_t a; | ||||||
|  |   // For some reason, G++ 4.4.1 complains:
 | ||||||
|  |   //   warning: array subscript is above array bounds
 | ||||||
|  |   // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
 | ||||||
|  |   // I'm not able to see how such a reference would occur.
 | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(-380377902, a); | ||||||
|  |   EXPECT_FALSE(cursor >> a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extractor, Unsigned1) { | ||||||
|  |   uint8_t data[] = { 0xfd }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   uint8_t a; | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(0xfd, a); | ||||||
|  |   EXPECT_FALSE(cursor >> a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extractor, Unsigned2) { | ||||||
|  |   uint8_t data[] = { 0x13, 0xcd }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   uint16_t a; | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(0xcd13, a); | ||||||
|  |   EXPECT_FALSE(cursor >> a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extractor, Unsigned4) { | ||||||
|  |   uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   uint32_t a; | ||||||
|  |   // For some reason, G++ 4.4.1 complains:
 | ||||||
|  |   //   warning: array subscript is above array bounds
 | ||||||
|  |   // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but
 | ||||||
|  |   // I'm not able to see how such a reference would occur.
 | ||||||
|  |   EXPECT_TRUE(cursor >> a); | ||||||
|  |   EXPECT_EQ(0xe953e4d2, a); | ||||||
|  |   EXPECT_FALSE(cursor >> a); | ||||||
|  |   EXPECT_FALSE(cursor >> a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Extractor, Mixed) { | ||||||
|  |   uint8_t data[] = { 0x42, | ||||||
|  |                      0x25, 0x0b, | ||||||
|  |                      0x3d, 0x25, 0xed, 0x2a, | ||||||
|  |                      0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf, | ||||||
|  |                      0xd8, | ||||||
|  |                      0x22, 0xa5, | ||||||
|  |                      0x3a, 0x02, 0x6a, 0xd7, | ||||||
|  |                      0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  |   cursor.set_big_endian(true); | ||||||
|  | 
 | ||||||
|  |   uint8_t a; | ||||||
|  |   uint16_t b; | ||||||
|  |   uint32_t c; | ||||||
|  |   uint64_t d; | ||||||
|  |   int8_t e; | ||||||
|  |   int16_t f; | ||||||
|  |   int32_t g; | ||||||
|  |   int64_t h; | ||||||
|  |   int z; | ||||||
|  |   EXPECT_FALSE(cursor.AtEnd()); | ||||||
|  |   EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h); | ||||||
|  |   EXPECT_EQ(0x42U, a); | ||||||
|  |   EXPECT_EQ(0x250bU, b); | ||||||
|  |   EXPECT_EQ(0x3d25ed2aU, c); | ||||||
|  |   EXPECT_EQ(0xec169e14615b2ccfULL, d); | ||||||
|  |   EXPECT_EQ(-40, e); | ||||||
|  |   EXPECT_EQ(0x22a5, f); | ||||||
|  |   EXPECT_EQ(0x3a026ad7, g); | ||||||
|  |   EXPECT_EQ(-7842405714468937530LL, h); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  |   EXPECT_FALSE(cursor >> z); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Strings, Zero) { | ||||||
|  |   uint8_t data[] = { 0xa6 }; | ||||||
|  |   ByteBuffer buffer(data, 0); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   uint8_t received[1]; | ||||||
|  |   received[0] = 0xc2; | ||||||
|  |   EXPECT_TRUE(cursor.Read(received, 0)); | ||||||
|  |   EXPECT_EQ(0xc2U, received[0]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Strings, Some) { | ||||||
|  |   uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed }; | ||||||
|  |   EXPECT_TRUE(cursor.Skip(2).Read(received, 5)); | ||||||
|  |   uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed }; | ||||||
|  |   EXPECT_TRUE(memcmp(received, expected, 7) == 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Strings, TooMuch) { | ||||||
|  |   uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   uint8_t received1[3]; | ||||||
|  |   uint8_t received2[3]; | ||||||
|  |   uint8_t received3[3]; | ||||||
|  |   EXPECT_FALSE(cursor | ||||||
|  |                .Read(received1, 3) | ||||||
|  |                .Read(received2, 3) | ||||||
|  |                .Read(received3, 3)); | ||||||
|  |   uint8_t expected1[3] = { 0x5d, 0x31, 0x09 }; | ||||||
|  |   uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c }; | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(memcmp(received1, expected1, 3) == 0); | ||||||
|  |   EXPECT_TRUE(memcmp(received2, expected2, 3) == 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Strings, PointTo) { | ||||||
|  |   uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 }; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data)); | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   const uint8_t *received1; | ||||||
|  |   const uint8_t *received2; | ||||||
|  |   const uint8_t *received3; | ||||||
|  |   const uint8_t *received4; | ||||||
|  |   EXPECT_FALSE(cursor | ||||||
|  |                .PointTo(&received1, 3) | ||||||
|  |                .PointTo(&received2, 3) | ||||||
|  |                .PointTo(&received3) | ||||||
|  |                .PointTo(&received4, 3)); | ||||||
|  |   EXPECT_EQ(data + 0, received1); | ||||||
|  |   EXPECT_EQ(data + 3, received2); | ||||||
|  |   EXPECT_EQ(data + 6, received3); | ||||||
|  |   EXPECT_EQ(NULL, received4); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Strings, CString) { | ||||||
|  |   uint8_t data[] = "abc\0\0foo"; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data) - 1);  // don't include terminating '\0'
 | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   string a, b, c; | ||||||
|  |   EXPECT_TRUE(cursor.CString(&a).CString(&b)); | ||||||
|  |   EXPECT_EQ("abc", a); | ||||||
|  |   EXPECT_EQ("", b); | ||||||
|  |   EXPECT_FALSE(cursor.CString(&c)); | ||||||
|  |   EXPECT_EQ("", c); | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Strings, CStringLimit) { | ||||||
|  |   uint8_t data[] = "abcdef\0\0foobar"; | ||||||
|  |   ByteBuffer buffer(data, sizeof(data) - 1);  // don't include terminating '\0'
 | ||||||
|  |   ByteCursor cursor(&buffer); | ||||||
|  | 
 | ||||||
|  |   string a, b, c, d, e; | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.CString(&a, 3)); | ||||||
|  |   EXPECT_EQ("abc", a); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.CString(&b, 0)); | ||||||
|  |   EXPECT_EQ("", b); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.CString(&c, 6)); | ||||||
|  |   EXPECT_EQ("def", c); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.CString(&d, 4)); | ||||||
|  |   EXPECT_EQ("ooba", d); | ||||||
|  | 
 | ||||||
|  |   EXPECT_FALSE(cursor.CString(&e, 4)); | ||||||
|  |   EXPECT_EQ("", e); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(cursor.AtEnd()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //  uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 };
 | ||||||
|  | //  ByteBuffer buffer(data, sizeof(data));
 | ||||||
							
								
								
									
										533
									
								
								google-breakpad/src/common/convert_UTF.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										533
									
								
								google-breakpad/src/common/convert_UTF.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,533 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright 2001-2004 Unicode, Inc. | ||||||
|  |  * | ||||||
|  |  * Disclaimer | ||||||
|  |  * | ||||||
|  |  * This source code is provided as is by Unicode, Inc. No claims are | ||||||
|  |  * made as to fitness for any particular purpose. No warranties of any | ||||||
|  |  * kind are expressed or implied. The recipient agrees to determine | ||||||
|  |  * applicability of information provided. If this file has been | ||||||
|  |  * purchased on magnetic or optical media from Unicode, Inc., the | ||||||
|  |  * sole remedy for any claim will be exchange of defective media | ||||||
|  |  * within 90 days of receipt. | ||||||
|  |  * | ||||||
|  |  * Limitations on Rights to Redistribute This Code | ||||||
|  |  * | ||||||
|  |  * Unicode, Inc. hereby grants the right to freely use the information | ||||||
|  |  * supplied in this file in the creation of products supporting the | ||||||
|  |  * Unicode Standard, and to make copies of this file in any form | ||||||
|  |  * for internal or external distribution as long as this notice | ||||||
|  |  * remains attached. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* ---------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | Conversions between UTF32, UTF-16, and UTF-8. Source code file. | ||||||
|  | Author: Mark E. Davis, 1994. | ||||||
|  | Rev History: Rick McGowan, fixes & updates May 2001. | ||||||
|  | Sept 2001: fixed const & error conditions per | ||||||
|  | mods suggested by S. Parent & A. Lillich. | ||||||
|  | June 2002: Tim Dodd added detection and handling of incomplete | ||||||
|  | source sequences, enhanced error detection, added casts | ||||||
|  | to eliminate compiler warnings. | ||||||
|  | July 2003: slight mods to back out aggressive FFFE detection. | ||||||
|  | Jan 2004: updated switches in from-UTF8 conversions. | ||||||
|  | Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. | ||||||
|  | 
 | ||||||
|  | See the header file "ConvertUTF.h" for complete documentation. | ||||||
|  | 
 | ||||||
|  | ------------------------------------------------------------------------ */ | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #include "convert_UTF.h" | ||||||
|  | #ifdef CVTUTF_DEBUG | ||||||
|  | #include <stdio.h> | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static const int halfShift  = 10; /* used for shifting by 10 bits */ | ||||||
|  | 
 | ||||||
|  | static const UTF32 halfBase = 0x0010000UL; | ||||||
|  | static const UTF32 halfMask = 0x3FFUL; | ||||||
|  | 
 | ||||||
|  | #define UNI_SUR_HIGH_START  (UTF32)0xD800 | ||||||
|  | #define UNI_SUR_HIGH_END    (UTF32)0xDBFF | ||||||
|  | #define UNI_SUR_LOW_START   (UTF32)0xDC00 | ||||||
|  | #define UNI_SUR_LOW_END     (UTF32)0xDFFF | ||||||
|  | #define false	   0 | ||||||
|  | #define true	    1 | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||||
|  |                                       UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { | ||||||
|  |   ConversionResult result = conversionOK; | ||||||
|  |   const UTF32* source = *sourceStart; | ||||||
|  |   UTF16* target = *targetStart; | ||||||
|  |   while (source < sourceEnd) { | ||||||
|  |     UTF32 ch; | ||||||
|  |     if (target >= targetEnd) { | ||||||
|  | 	    result = targetExhausted; break; | ||||||
|  |     } | ||||||
|  |     ch = *source++; | ||||||
|  |     if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ | ||||||
|  | 	    /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ | ||||||
|  | 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||||
|  |         if (flags == strictConversion) { | ||||||
|  |           --source; /* return to the illegal value itself */ | ||||||
|  |           result = sourceIllegal; | ||||||
|  |           break; | ||||||
|  |         } else { | ||||||
|  |           *target++ = UNI_REPLACEMENT_CHAR; | ||||||
|  |         } | ||||||
|  | 	    } else { | ||||||
|  |         *target++ = (UTF16)ch; /* normal case */ | ||||||
|  | 	    } | ||||||
|  |     } else if (ch > UNI_MAX_LEGAL_UTF32) { | ||||||
|  | 	    if (flags == strictConversion) { | ||||||
|  |         result = sourceIllegal; | ||||||
|  | 	    } else { | ||||||
|  |         *target++ = UNI_REPLACEMENT_CHAR; | ||||||
|  | 	    } | ||||||
|  |     } else { | ||||||
|  | 	    /* target is a character in range 0xFFFF - 0x10FFFF. */ | ||||||
|  | 	    if (target + 1 >= targetEnd) { | ||||||
|  |         --source; /* Back up source pointer! */ | ||||||
|  |         result = targetExhausted; break; | ||||||
|  | 	    } | ||||||
|  | 	    ch -= halfBase; | ||||||
|  | 	    *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); | ||||||
|  | 	    *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | *sourceStart = source; | ||||||
|  | *targetStart = target; | ||||||
|  | return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||||
|  |                                       UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { | ||||||
|  |   ConversionResult result = conversionOK; | ||||||
|  |   const UTF16* source = *sourceStart; | ||||||
|  |   UTF32* target = *targetStart; | ||||||
|  |   UTF32 ch, ch2; | ||||||
|  |   while (source < sourceEnd) { | ||||||
|  |     const UTF16* oldSource = source; /*  In case we have to back up because of target overflow. */ | ||||||
|  |     ch = *source++; | ||||||
|  |     /* If we have a surrogate pair, convert to UTF32 first. */ | ||||||
|  |     if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { | ||||||
|  | 	    /* If the 16 bits following the high surrogate are in the source buffer... */ | ||||||
|  | 	    if (source < sourceEnd) { | ||||||
|  |         ch2 = *source; | ||||||
|  |         /* If it's a low surrogate, convert to UTF32. */ | ||||||
|  |         if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { | ||||||
|  |           ch = ((ch - UNI_SUR_HIGH_START) << halfShift) | ||||||
|  |           + (ch2 - UNI_SUR_LOW_START) + halfBase; | ||||||
|  |           ++source; | ||||||
|  |         } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ | ||||||
|  |           --source; /* return to the illegal value itself */ | ||||||
|  |           result = sourceIllegal; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  | 	    } else { /* We don't have the 16 bits following the high surrogate. */ | ||||||
|  |         --source; /* return to the high surrogate */ | ||||||
|  |         result = sourceExhausted; | ||||||
|  |         break; | ||||||
|  | 	    } | ||||||
|  |     } else if (flags == strictConversion) { | ||||||
|  | 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||||
|  | 	    if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { | ||||||
|  |         --source; /* return to the illegal value itself */ | ||||||
|  |         result = sourceIllegal; | ||||||
|  |         break; | ||||||
|  | 	    } | ||||||
|  |     } | ||||||
|  |     if (target >= targetEnd) { | ||||||
|  | 	    source = oldSource; /* Back up source pointer! */ | ||||||
|  | 	    result = targetExhausted; break; | ||||||
|  |     } | ||||||
|  |     *target++ = ch; | ||||||
|  |   } | ||||||
|  |   *sourceStart = source; | ||||||
|  |   *targetStart = target; | ||||||
|  | #ifdef CVTUTF_DEBUG | ||||||
|  |   if (result == sourceIllegal) { | ||||||
|  |     fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); | ||||||
|  |     fflush(stderr); | ||||||
|  |   } | ||||||
|  | #endif | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Index into the table below with the first byte of a UTF-8 sequence to | ||||||
|  |  * get the number of trailing bytes that are supposed to follow it. | ||||||
|  |  * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is | ||||||
|  |  * left as-is for anyone who may want to do such conversion, which was | ||||||
|  |  * allowed in earlier algorithms. | ||||||
|  |  */ | ||||||
|  | static const char trailingBytesForUTF8[256] = { | ||||||
|  |   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||||
|  |   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||||
|  |   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||||
|  |   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||||
|  |   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||||
|  |   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||||
|  |   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, | ||||||
|  |   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Magic values subtracted from a buffer value during UTF8 conversion. | ||||||
|  |  * This table contains as many values as there might be trailing bytes | ||||||
|  |  * in a UTF-8 sequence. | ||||||
|  |  */ | ||||||
|  | static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, | ||||||
|  |   0x03C82080UL, 0xFA082080UL, 0x82082080UL }; | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed | ||||||
|  |  * into the first byte, depending on how many bytes follow.  There are | ||||||
|  |  * as many entries in this table as there are UTF-8 sequence types. | ||||||
|  |  * (I.e., one byte sequence, two byte... etc.). Remember that sequencs | ||||||
|  |  * for *legal* UTF-8 will be 4 or fewer bytes total. | ||||||
|  |  */ | ||||||
|  | static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | /* The interface converts a whole buffer to avoid function-call overhead.
 | ||||||
|  | * Constants have been gathered. Loops & conditionals have been removed as | ||||||
|  | * much as possible for efficiency, in favor of drop-through switches. | ||||||
|  | * (See "Note A" at the bottom of the file for equivalent code.) | ||||||
|  | * If your compiler supports it, the "isLegalUTF8" call can be turned | ||||||
|  | * into an inline function. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||||
|  |                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { | ||||||
|  |   ConversionResult result = conversionOK; | ||||||
|  |   const UTF16* source = *sourceStart; | ||||||
|  |   UTF8* target = *targetStart; | ||||||
|  |   while (source < sourceEnd) { | ||||||
|  |     UTF32 ch; | ||||||
|  |     unsigned short bytesToWrite = 0; | ||||||
|  |     const UTF32 byteMask = 0xBF; | ||||||
|  |     const UTF32 byteMark = 0x80; | ||||||
|  |     const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ | ||||||
|  |     ch = *source++; | ||||||
|  |     /* If we have a surrogate pair, convert to UTF32 first. */ | ||||||
|  |     if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { | ||||||
|  | 	    /* If the 16 bits following the high surrogate are in the source buffer... */ | ||||||
|  | 	    if (source < sourceEnd) { | ||||||
|  |         UTF32 ch2 = *source; | ||||||
|  |         /* If it's a low surrogate, convert to UTF32. */ | ||||||
|  |         if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { | ||||||
|  |           ch = ((ch - UNI_SUR_HIGH_START) << halfShift) | ||||||
|  |           + (ch2 - UNI_SUR_LOW_START) + halfBase; | ||||||
|  |           ++source; | ||||||
|  |         } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ | ||||||
|  |           --source; /* return to the illegal value itself */ | ||||||
|  |           result = sourceIllegal; | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  | 	    } else { /* We don't have the 16 bits following the high surrogate. */ | ||||||
|  |         --source; /* return to the high surrogate */ | ||||||
|  |         result = sourceExhausted; | ||||||
|  |         break; | ||||||
|  | 	    } | ||||||
|  |     } else if (flags == strictConversion) { | ||||||
|  | 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||||
|  | 	    if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { | ||||||
|  |         --source; /* return to the illegal value itself */ | ||||||
|  |         result = sourceIllegal; | ||||||
|  |         break; | ||||||
|  | 	    } | ||||||
|  |     } | ||||||
|  |     /* Figure out how many bytes the result will require */ | ||||||
|  |     if (ch < (UTF32)0x80) {	     bytesToWrite = 1; | ||||||
|  |     } else if (ch < (UTF32)0x800) {     bytesToWrite = 2; | ||||||
|  |     } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3; | ||||||
|  |     } else if (ch < (UTF32)0x110000) {  bytesToWrite = 4; | ||||||
|  |     } else {			    bytesToWrite = 3; | ||||||
|  |       ch = UNI_REPLACEMENT_CHAR; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     target += bytesToWrite; | ||||||
|  |     if (target > targetEnd) { | ||||||
|  | 	    source = oldSource; /* Back up source pointer! */ | ||||||
|  | 	    target -= bytesToWrite; result = targetExhausted; break; | ||||||
|  |     } | ||||||
|  |     switch (bytesToWrite) { /* note: everything falls through. */ | ||||||
|  | 	    case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||||
|  | 	    case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||||
|  | 	    case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||||
|  | 	    case 1: *--target =  (UTF8)(ch | firstByteMark[bytesToWrite]); | ||||||
|  |     } | ||||||
|  |     target += bytesToWrite; | ||||||
|  |   } | ||||||
|  | *sourceStart = source; | ||||||
|  | *targetStart = target; | ||||||
|  | return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Utility routine to tell whether a sequence of bytes is legal UTF-8. | ||||||
|  |  * This must be called with the length pre-determined by the first byte. | ||||||
|  |  * If not calling this from ConvertUTF8to*, then the length can be set by: | ||||||
|  |  *  length = trailingBytesForUTF8[*source]+1; | ||||||
|  |  * and the sequence is illegal right away if there aren't that many bytes | ||||||
|  |  * available. | ||||||
|  |  * If presented with a length > 4, this returns false.  The Unicode | ||||||
|  |  * definition of UTF-8 goes up to 4-byte sequences. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | static Boolean isLegalUTF8(const UTF8 *source, int length) { | ||||||
|  |   UTF8 a; | ||||||
|  |   const UTF8 *srcptr = source+length; | ||||||
|  |   switch (length) { | ||||||
|  |     default: return false; | ||||||
|  |       /* Everything else falls through when "true"... */ | ||||||
|  |     case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; | ||||||
|  |     case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; | ||||||
|  |     case 2: if ((a = (*--srcptr)) > 0xBF) return false; | ||||||
|  | 
 | ||||||
|  |       switch (*source) { | ||||||
|  |         /* no fall-through in this inner switch */ | ||||||
|  |         case 0xE0: if (a < 0xA0) return false; break; | ||||||
|  |         case 0xED: if (a > 0x9F) return false; break; | ||||||
|  |         case 0xF0: if (a < 0x90) return false; break; | ||||||
|  |         case 0xF4: if (a > 0x8F) return false; break; | ||||||
|  |         default:   if (a < 0x80) return false; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       case 1: if (*source >= 0x80 && *source < 0xC2) return false; | ||||||
|  |   } | ||||||
|  |   if (*source > 0xF4) return false; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  * Exported function to return whether a UTF-8 sequence is legal or not. | ||||||
|  |  * This is not used here; it's just exported. | ||||||
|  |  */ | ||||||
|  | Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { | ||||||
|  |   int length = trailingBytesForUTF8[*source]+1; | ||||||
|  |   if (source+length > sourceEnd) { | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |   return isLegalUTF8(source, length); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||||
|  |                                      UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { | ||||||
|  |   ConversionResult result = conversionOK; | ||||||
|  |   const UTF8* source = *sourceStart; | ||||||
|  |   UTF16* target = *targetStart; | ||||||
|  |   while (source < sourceEnd) { | ||||||
|  |     UTF32 ch = 0; | ||||||
|  |     unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; | ||||||
|  |     if (source + extraBytesToRead >= sourceEnd) { | ||||||
|  | 	    result = sourceExhausted; break; | ||||||
|  |     } | ||||||
|  |     /* Do this check whether lenient or strict */ | ||||||
|  |     if (! isLegalUTF8(source, extraBytesToRead+1)) { | ||||||
|  | 	    result = sourceIllegal; | ||||||
|  | 	    break; | ||||||
|  |     } | ||||||
|  |     /*
 | ||||||
|  |      * The cases all fall through. See "Note A" below. | ||||||
|  |      */ | ||||||
|  |     switch (extraBytesToRead) { | ||||||
|  | 	    case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ | ||||||
|  | 	    case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ | ||||||
|  | 	    case 3: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 2: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 1: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 0: ch += *source++; | ||||||
|  |     } | ||||||
|  |     ch -= offsetsFromUTF8[extraBytesToRead]; | ||||||
|  | 
 | ||||||
|  |     if (target >= targetEnd) { | ||||||
|  | 	    source -= (extraBytesToRead+1); /* Back up source pointer! */ | ||||||
|  | 	    result = targetExhausted; break; | ||||||
|  |     } | ||||||
|  |     if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ | ||||||
|  | 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||||
|  | 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||||
|  |         if (flags == strictConversion) { | ||||||
|  |           source -= (extraBytesToRead+1); /* return to the illegal value itself */ | ||||||
|  |           result = sourceIllegal; | ||||||
|  |           break; | ||||||
|  |         } else { | ||||||
|  |           *target++ = UNI_REPLACEMENT_CHAR; | ||||||
|  |         } | ||||||
|  | 	    } else { | ||||||
|  |         *target++ = (UTF16)ch; /* normal case */ | ||||||
|  | 	    } | ||||||
|  |     } else if (ch > UNI_MAX_UTF16) { | ||||||
|  | 	    if (flags == strictConversion) { | ||||||
|  |         result = sourceIllegal; | ||||||
|  |         source -= (extraBytesToRead+1); /* return to the start */ | ||||||
|  |         break; /* Bail out; shouldn't continue */ | ||||||
|  | 	    } else { | ||||||
|  |         *target++ = UNI_REPLACEMENT_CHAR; | ||||||
|  | 	    } | ||||||
|  |     } else { | ||||||
|  | 	    /* target is a character in range 0xFFFF - 0x10FFFF. */ | ||||||
|  | 	    if (target + 1 >= targetEnd) { | ||||||
|  |         source -= (extraBytesToRead+1); /* Back up source pointer! */ | ||||||
|  |         result = targetExhausted; break; | ||||||
|  | 	    } | ||||||
|  | 	    ch -= halfBase; | ||||||
|  | 	    *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); | ||||||
|  | 	    *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | *sourceStart = source; | ||||||
|  | *targetStart = target; | ||||||
|  | return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||||
|  |                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { | ||||||
|  |   ConversionResult result = conversionOK; | ||||||
|  |   const UTF32* source = *sourceStart; | ||||||
|  |   UTF8* target = *targetStart; | ||||||
|  |   while (source < sourceEnd) { | ||||||
|  |     UTF32 ch; | ||||||
|  |     unsigned short bytesToWrite = 0; | ||||||
|  |     const UTF32 byteMask = 0xBF; | ||||||
|  |     const UTF32 byteMark = 0x80; | ||||||
|  |     ch = *source++; | ||||||
|  |     if (flags == strictConversion ) { | ||||||
|  | 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||||
|  | 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||||
|  |         --source; /* return to the illegal value itself */ | ||||||
|  |         result = sourceIllegal; | ||||||
|  |         break; | ||||||
|  | 	    } | ||||||
|  |     } | ||||||
|  |     /*
 | ||||||
|  |      * Figure out how many bytes the result will require. Turn any | ||||||
|  |      * illegally large UTF32 things (> Plane 17) into replacement chars. | ||||||
|  |      */ | ||||||
|  |     if (ch < (UTF32)0x80) {	     bytesToWrite = 1; | ||||||
|  |     } else if (ch < (UTF32)0x800) {     bytesToWrite = 2; | ||||||
|  |     } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3; | ||||||
|  |     } else if (ch <= UNI_MAX_LEGAL_UTF32) {  bytesToWrite = 4; | ||||||
|  |     } else {			    bytesToWrite = 3; | ||||||
|  |       ch = UNI_REPLACEMENT_CHAR; | ||||||
|  |       result = sourceIllegal; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     target += bytesToWrite; | ||||||
|  |     if (target > targetEnd) { | ||||||
|  | 	    --source; /* Back up source pointer! */ | ||||||
|  | 	    target -= bytesToWrite; result = targetExhausted; break; | ||||||
|  |     } | ||||||
|  |     switch (bytesToWrite) { /* note: everything falls through. */ | ||||||
|  | 	    case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||||
|  | 	    case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||||
|  | 	    case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||||
|  | 	    case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); | ||||||
|  |     } | ||||||
|  |     target += bytesToWrite; | ||||||
|  |   } | ||||||
|  | *sourceStart = source; | ||||||
|  | *targetStart = target; | ||||||
|  | return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||||
|  |                                      UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { | ||||||
|  |   ConversionResult result = conversionOK; | ||||||
|  |   const UTF8* source = *sourceStart; | ||||||
|  |   UTF32* target = *targetStart; | ||||||
|  |   while (source < sourceEnd) { | ||||||
|  |     UTF32 ch = 0; | ||||||
|  |     unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; | ||||||
|  |     if (source + extraBytesToRead >= sourceEnd) { | ||||||
|  | 	    result = sourceExhausted; break; | ||||||
|  |     } | ||||||
|  |     /* Do this check whether lenient or strict */ | ||||||
|  |     if (! isLegalUTF8(source, extraBytesToRead+1)) { | ||||||
|  | 	    result = sourceIllegal; | ||||||
|  | 	    break; | ||||||
|  |     } | ||||||
|  |     /*
 | ||||||
|  |      * The cases all fall through. See "Note A" below. | ||||||
|  |      */ | ||||||
|  |     switch (extraBytesToRead) { | ||||||
|  | 	    case 5: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 4: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 3: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 2: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 1: ch += *source++; ch <<= 6; | ||||||
|  | 	    case 0: ch += *source++; | ||||||
|  |     } | ||||||
|  |     ch -= offsetsFromUTF8[extraBytesToRead]; | ||||||
|  | 
 | ||||||
|  |     if (target >= targetEnd) { | ||||||
|  | 	    source -= (extraBytesToRead+1); /* Back up the source pointer! */ | ||||||
|  | 	    result = targetExhausted; break; | ||||||
|  |     } | ||||||
|  |     if (ch <= UNI_MAX_LEGAL_UTF32) { | ||||||
|  | 	    /*
 | ||||||
|  | 	     * UTF-16 surrogate values are illegal in UTF-32, and anything | ||||||
|  | 	     * over Plane 17 (> 0x10FFFF) is illegal. | ||||||
|  | 	     */ | ||||||
|  | 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||||
|  |         if (flags == strictConversion) { | ||||||
|  |           source -= (extraBytesToRead+1); /* return to the illegal value itself */ | ||||||
|  |           result = sourceIllegal; | ||||||
|  |           break; | ||||||
|  |         } else { | ||||||
|  |           *target++ = UNI_REPLACEMENT_CHAR; | ||||||
|  |         } | ||||||
|  | 	    } else { | ||||||
|  |         *target++ = ch; | ||||||
|  | 	    } | ||||||
|  |     } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ | ||||||
|  | 	    result = sourceIllegal; | ||||||
|  | 	    *target++ = UNI_REPLACEMENT_CHAR; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   *sourceStart = source; | ||||||
|  |   *targetStart = target; | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* ---------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | Note A. | ||||||
|  | The fall-through switches in UTF-8 reading code save a | ||||||
|  | temp variable, some decrements & conditionals.  The switches | ||||||
|  | are equivalent to the following loop: | ||||||
|  | { | ||||||
|  |   int tmpBytesToRead = extraBytesToRead+1; | ||||||
|  |   do { | ||||||
|  | 		ch += *source++; | ||||||
|  | 		--tmpBytesToRead; | ||||||
|  | 		if (tmpBytesToRead) ch <<= 6; | ||||||
|  |   } while (tmpBytesToRead > 0); | ||||||
|  | } | ||||||
|  | In UTF-8 writing code, the switches on "bytesToWrite" are | ||||||
|  | similarly unrolled loops. | ||||||
|  | 
 | ||||||
|  | --------------------------------------------------------------------- */ | ||||||
							
								
								
									
										143
									
								
								google-breakpad/src/common/convert_UTF.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								google-breakpad/src/common/convert_UTF.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright 2001-2004 Unicode, Inc. | ||||||
|  |  * | ||||||
|  |  * Disclaimer | ||||||
|  |  * | ||||||
|  |  * This source code is provided as is by Unicode, Inc. No claims are | ||||||
|  |  * made as to fitness for any particular purpose. No warranties of any | ||||||
|  |  * kind are expressed or implied. The recipient agrees to determine | ||||||
|  |  * applicability of information provided. If this file has been | ||||||
|  |  * purchased on magnetic or optical media from Unicode, Inc., the | ||||||
|  |  * sole remedy for any claim will be exchange of defective media | ||||||
|  |  * within 90 days of receipt. | ||||||
|  |  * | ||||||
|  |  * Limitations on Rights to Redistribute This Code | ||||||
|  |  * | ||||||
|  |  * Unicode, Inc. hereby grants the right to freely use the information | ||||||
|  |  * supplied in this file in the creation of products supporting the | ||||||
|  |  * Unicode Standard, and to make copies of this file in any form | ||||||
|  |  * for internal or external distribution as long as this notice | ||||||
|  |  * remains attached. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* ---------------------------------------------------------------------
 | ||||||
|  | 
 | ||||||
|  | Conversions between UTF32, UTF-16, and UTF-8.  Header file. | ||||||
|  | 
 | ||||||
|  | Several funtions are included here, forming a complete set of | ||||||
|  | conversions between the three formats.  UTF-7 is not included | ||||||
|  | here, but is handled in a separate source file. | ||||||
|  | 
 | ||||||
|  | Each of these routines takes pointers to input buffers and output | ||||||
|  | buffers.  The input buffers are const. | ||||||
|  | 
 | ||||||
|  | Each routine converts the text between *sourceStart and sourceEnd, | ||||||
|  | putting the result into the buffer between *targetStart and | ||||||
|  | targetEnd. Note: the end pointers are *after* the last item: e.g. | ||||||
|  | *(sourceEnd - 1) is the last item. | ||||||
|  | 
 | ||||||
|  | The return result indicates whether the conversion was successful, | ||||||
|  | and if not, whether the problem was in the source or target buffers. | ||||||
|  | (Only the first encountered problem is indicated.) | ||||||
|  | 
 | ||||||
|  | After the conversion, *sourceStart and *targetStart are both | ||||||
|  | updated to point to the end of last text successfully converted in | ||||||
|  | the respective buffers. | ||||||
|  | 
 | ||||||
|  | Input parameters: | ||||||
|  | sourceStart - pointer to a pointer to the source buffer. | ||||||
|  | The contents of this are modified on return so that | ||||||
|  | it points at the next thing to be converted. | ||||||
|  | targetStart - similarly, pointer to pointer to the target buffer. | ||||||
|  | sourceEnd, targetEnd - respectively pointers to the ends of the | ||||||
|  | two buffers, for overflow checking only. | ||||||
|  | 
 | ||||||
|  | These conversion functions take a ConversionFlags argument. When this | ||||||
|  | flag is set to strict, both irregular sequences and isolated surrogates | ||||||
|  | will cause an error.  When the flag is set to lenient, both irregular | ||||||
|  | sequences and isolated surrogates are converted. | ||||||
|  | 
 | ||||||
|  | Whether the flag is strict or lenient, all illegal sequences will cause | ||||||
|  | an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, | ||||||
|  | or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code | ||||||
|  | must check for illegal sequences. | ||||||
|  | 
 | ||||||
|  | When the flag is set to lenient, characters over 0x10FFFF are converted | ||||||
|  | to the replacement character; otherwise (when the flag is set to strict) | ||||||
|  | they constitute an error. | ||||||
|  | 
 | ||||||
|  | Output parameters: | ||||||
|  | The value "sourceIllegal" is returned from some routines if the input | ||||||
|  | sequence is malformed.  When "sourceIllegal" is returned, the source | ||||||
|  | value will point to the illegal value that caused the problem. E.g., | ||||||
|  | in UTF-8 when a sequence is malformed, it points to the start of the | ||||||
|  | malformed sequence. | ||||||
|  | 
 | ||||||
|  | Author: Mark E. Davis, 1994. | ||||||
|  | Rev History: Rick McGowan, fixes & updates May 2001. | ||||||
|  | Fixes & updates, Sept 2001. | ||||||
|  | 
 | ||||||
|  | ------------------------------------------------------------------------ */ | ||||||
|  | 
 | ||||||
|  | /* ---------------------------------------------------------------------
 | ||||||
|  | The following 4 definitions are compiler-specific. | ||||||
|  | The C standard does not guarantee that wchar_t has at least | ||||||
|  | 16 bits, so wchar_t is no less portable than unsigned short! | ||||||
|  | All should be unsigned values to avoid sign extension during | ||||||
|  | bit mask & shift operations. | ||||||
|  | ------------------------------------------------------------------------ */ | ||||||
|  | 
 | ||||||
|  | typedef unsigned long	UTF32;	/* at least 32 bits */ | ||||||
|  | typedef unsigned short	UTF16;	/* at least 16 bits */ | ||||||
|  | typedef unsigned char	UTF8;	/* typically 8 bits */ | ||||||
|  | typedef unsigned char	Boolean; /* 0 or 1 */ | ||||||
|  | 
 | ||||||
|  | /* Some fundamental constants */ | ||||||
|  | #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD | ||||||
|  | #define UNI_MAX_BMP (UTF32)0x0000FFFF | ||||||
|  | #define UNI_MAX_UTF16 (UTF32)0x0010FFFF | ||||||
|  | #define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF | ||||||
|  | #define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  | 	conversionOK, 		/* conversion successful */ | ||||||
|  | 	sourceExhausted,	/* partial character in source, but hit end */ | ||||||
|  | 	targetExhausted,	/* insuff. room in target for conversion */ | ||||||
|  | 	sourceIllegal		/* source sequence is illegal/malformed */ | ||||||
|  | } ConversionResult; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  | 	strictConversion = 0, | ||||||
|  | 	lenientConversion | ||||||
|  | } ConversionFlags; | ||||||
|  | 
 | ||||||
|  | /* This is for C++ and does no harm in C */ | ||||||
|  | #ifdef __cplusplus | ||||||
|  | extern "C" { | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||||
|  |                                      UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||||
|  |                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||||
|  |                                      UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||||
|  |                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||||
|  |                                       UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); | ||||||
|  | 
 | ||||||
|  | ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||||
|  |                                       UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); | ||||||
|  | 
 | ||||||
|  | Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); | ||||||
|  | 
 | ||||||
|  | #ifdef __cplusplus | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /* --------------------------------------------------------------------- */ | ||||||
							
								
								
									
										175
									
								
								google-breakpad/src/common/dwarf/bytereader-inl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								google-breakpad/src/common/dwarf/bytereader-inl.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,175 @@ | |||||||
|  | // Copyright 2006 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__ | ||||||
|  | #define UTIL_DEBUGINFO_BYTEREADER_INL_H__ | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/bytereader.h" | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | inline uint8 ByteReader::ReadOneByte(const char* buffer) const { | ||||||
|  |   return buffer[0]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const { | ||||||
|  |   const unsigned char *buffer | ||||||
|  |     = reinterpret_cast<const unsigned char *>(signed_buffer); | ||||||
|  |   const uint16 buffer0 = buffer[0]; | ||||||
|  |   const uint16 buffer1 = buffer[1]; | ||||||
|  |   if (endian_ == ENDIANNESS_LITTLE) { | ||||||
|  |     return buffer0 | buffer1 << 8; | ||||||
|  |   } else { | ||||||
|  |     return buffer1 | buffer0 << 8; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const { | ||||||
|  |   const unsigned char *buffer | ||||||
|  |     = reinterpret_cast<const unsigned char *>(signed_buffer); | ||||||
|  |   const uint32 buffer0 = buffer[0]; | ||||||
|  |   const uint32 buffer1 = buffer[1]; | ||||||
|  |   const uint32 buffer2 = buffer[2]; | ||||||
|  |   const uint32 buffer3 = buffer[3]; | ||||||
|  |   if (endian_ == ENDIANNESS_LITTLE) { | ||||||
|  |     return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; | ||||||
|  |   } else { | ||||||
|  |     return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const { | ||||||
|  |   const unsigned char *buffer | ||||||
|  |     = reinterpret_cast<const unsigned char *>(signed_buffer); | ||||||
|  |   const uint64 buffer0 = buffer[0]; | ||||||
|  |   const uint64 buffer1 = buffer[1]; | ||||||
|  |   const uint64 buffer2 = buffer[2]; | ||||||
|  |   const uint64 buffer3 = buffer[3]; | ||||||
|  |   const uint64 buffer4 = buffer[4]; | ||||||
|  |   const uint64 buffer5 = buffer[5]; | ||||||
|  |   const uint64 buffer6 = buffer[6]; | ||||||
|  |   const uint64 buffer7 = buffer[7]; | ||||||
|  |   if (endian_ == ENDIANNESS_LITTLE) { | ||||||
|  |     return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | | ||||||
|  |       buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; | ||||||
|  |   } else { | ||||||
|  |     return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 | | ||||||
|  |       buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read an unsigned LEB128 number.  Each byte contains 7 bits of
 | ||||||
|  | // information, plus one bit saying whether the number continues or
 | ||||||
|  | // not.
 | ||||||
|  | 
 | ||||||
|  | inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer, | ||||||
|  |                                              size_t* len) const { | ||||||
|  |   uint64 result = 0; | ||||||
|  |   size_t num_read = 0; | ||||||
|  |   unsigned int shift = 0; | ||||||
|  |   unsigned char byte; | ||||||
|  | 
 | ||||||
|  |   do { | ||||||
|  |     byte = *buffer++; | ||||||
|  |     num_read++; | ||||||
|  | 
 | ||||||
|  |     result |= (static_cast<uint64>(byte & 0x7f)) << shift; | ||||||
|  | 
 | ||||||
|  |     shift += 7; | ||||||
|  | 
 | ||||||
|  |   } while (byte & 0x80); | ||||||
|  | 
 | ||||||
|  |   *len = num_read; | ||||||
|  | 
 | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Read a signed LEB128 number.  These are like regular LEB128
 | ||||||
|  | // numbers, except the last byte may have a sign bit set.
 | ||||||
|  | 
 | ||||||
|  | inline int64 ByteReader::ReadSignedLEB128(const char* buffer, | ||||||
|  |                                           size_t* len) const { | ||||||
|  |   int64 result = 0; | ||||||
|  |   unsigned int shift = 0; | ||||||
|  |   size_t num_read = 0; | ||||||
|  |   unsigned char byte; | ||||||
|  | 
 | ||||||
|  |   do { | ||||||
|  |       byte = *buffer++; | ||||||
|  |       num_read++; | ||||||
|  |       result |= (static_cast<uint64>(byte & 0x7f) << shift); | ||||||
|  |       shift += 7; | ||||||
|  |   } while (byte & 0x80); | ||||||
|  | 
 | ||||||
|  |   if ((shift < 8 * sizeof (result)) && (byte & 0x40)) | ||||||
|  |     result |= -((static_cast<int64>(1)) << shift); | ||||||
|  |   *len = num_read; | ||||||
|  |   return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline uint64 ByteReader::ReadOffset(const char* buffer) const { | ||||||
|  |   assert(this->offset_reader_); | ||||||
|  |   return (this->*offset_reader_)(buffer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline uint64 ByteReader::ReadAddress(const char* buffer) const { | ||||||
|  |   assert(this->address_reader_); | ||||||
|  |   return (this->*address_reader_)(buffer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void ByteReader::SetCFIDataBase(uint64 section_base, | ||||||
|  |                                        const char *buffer_base) { | ||||||
|  |   section_base_ = section_base; | ||||||
|  |   buffer_base_ = buffer_base; | ||||||
|  |   have_section_base_ = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void ByteReader::SetTextBase(uint64 text_base) { | ||||||
|  |   text_base_ = text_base; | ||||||
|  |   have_text_base_ = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void ByteReader::SetDataBase(uint64 data_base) { | ||||||
|  |   data_base_ = data_base; | ||||||
|  |   have_data_base_ = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void ByteReader::SetFunctionBase(uint64 function_base) { | ||||||
|  |   function_base_ = function_base; | ||||||
|  |   have_function_base_ = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline void ByteReader::ClearFunctionBase() { | ||||||
|  |   have_function_base_ = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace dwarf2reader
 | ||||||
|  | 
 | ||||||
|  | #endif  // UTIL_DEBUGINFO_BYTEREADER_INL_H__
 | ||||||
							
								
								
									
										245
									
								
								google-breakpad/src/common/dwarf/bytereader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								google-breakpad/src/common/dwarf/bytereader.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,245 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/bytereader-inl.h" | ||||||
|  | #include "common/dwarf/bytereader.h" | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | ByteReader::ByteReader(enum Endianness endian) | ||||||
|  |     :offset_reader_(NULL), address_reader_(NULL), endian_(endian), | ||||||
|  |      address_size_(0), offset_size_(0), | ||||||
|  |      have_section_base_(), have_text_base_(), have_data_base_(), | ||||||
|  |      have_function_base_() { } | ||||||
|  | 
 | ||||||
|  | ByteReader::~ByteReader() { } | ||||||
|  | 
 | ||||||
|  | void ByteReader::SetOffsetSize(uint8 size) { | ||||||
|  |   offset_size_ = size; | ||||||
|  |   assert(size == 4 || size == 8); | ||||||
|  |   if (size == 4) { | ||||||
|  |     this->offset_reader_ = &ByteReader::ReadFourBytes; | ||||||
|  |   } else { | ||||||
|  |     this->offset_reader_ = &ByteReader::ReadEightBytes; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ByteReader::SetAddressSize(uint8 size) { | ||||||
|  |   address_size_ = size; | ||||||
|  |   assert(size == 4 || size == 8); | ||||||
|  |   if (size == 4) { | ||||||
|  |     this->address_reader_ = &ByteReader::ReadFourBytes; | ||||||
|  |   } else { | ||||||
|  |     this->address_reader_ = &ByteReader::ReadEightBytes; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) { | ||||||
|  |   const uint64 initial_length = ReadFourBytes(start); | ||||||
|  |   start += 4; | ||||||
|  | 
 | ||||||
|  |   // In DWARF2/3, if the initial length is all 1 bits, then the offset
 | ||||||
|  |   // size is 8 and we need to read the next 8 bytes for the real length.
 | ||||||
|  |   if (initial_length == 0xffffffff) { | ||||||
|  |     SetOffsetSize(8); | ||||||
|  |     *len = 12; | ||||||
|  |     return ReadOffset(start); | ||||||
|  |   } else { | ||||||
|  |     SetOffsetSize(4); | ||||||
|  |     *len = 4; | ||||||
|  |   } | ||||||
|  |   return initial_length; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const { | ||||||
|  |   if (encoding == DW_EH_PE_omit) return true; | ||||||
|  |   if (encoding == DW_EH_PE_aligned) return true; | ||||||
|  |   if ((encoding & 0x7) > DW_EH_PE_udata8) | ||||||
|  |     return false; | ||||||
|  |   if ((encoding & 0x70) > DW_EH_PE_funcrel) | ||||||
|  |     return false; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const { | ||||||
|  |   switch (encoding & 0x70) { | ||||||
|  |     case DW_EH_PE_absptr:  return true; | ||||||
|  |     case DW_EH_PE_pcrel:   return have_section_base_; | ||||||
|  |     case DW_EH_PE_textrel: return have_text_base_; | ||||||
|  |     case DW_EH_PE_datarel: return have_data_base_; | ||||||
|  |     case DW_EH_PE_funcrel: return have_function_base_; | ||||||
|  |     default:               return false; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | uint64 ByteReader::ReadEncodedPointer(const char *buffer, | ||||||
|  |                                       DwarfPointerEncoding encoding, | ||||||
|  |                                       size_t *len) const { | ||||||
|  |   // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
 | ||||||
|  |   // see it here.
 | ||||||
|  |   assert(encoding != DW_EH_PE_omit); | ||||||
|  | 
 | ||||||
|  |   // The Linux Standards Base 4.0 does not make this clear, but the
 | ||||||
|  |   // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
 | ||||||
|  |   // agree that aligned pointers are always absolute, machine-sized,
 | ||||||
|  |   // machine-signed pointers.
 | ||||||
|  |   if (encoding == DW_EH_PE_aligned) { | ||||||
|  |     assert(have_section_base_); | ||||||
|  | 
 | ||||||
|  |     // We don't need to align BUFFER in *our* address space. Rather, we
 | ||||||
|  |     // need to find the next position in our buffer that would be aligned
 | ||||||
|  |     // when the .eh_frame section the buffer contains is loaded into the
 | ||||||
|  |     // program's memory. So align assuming that buffer_base_ gets loaded at
 | ||||||
|  |     // address section_base_, where section_base_ itself may or may not be
 | ||||||
|  |     // aligned.
 | ||||||
|  | 
 | ||||||
|  |     // First, find the offset to START from the closest prior aligned
 | ||||||
|  |     // address.
 | ||||||
|  |     uint64 skew = section_base_ & (AddressSize() - 1); | ||||||
|  |     // Now find the offset from that aligned address to buffer.
 | ||||||
|  |     uint64 offset = skew + (buffer - buffer_base_); | ||||||
|  |     // Round up to the next boundary.
 | ||||||
|  |     uint64 aligned = (offset + AddressSize() - 1) & -AddressSize(); | ||||||
|  |     // Convert back to a pointer.
 | ||||||
|  |     const char *aligned_buffer = buffer_base_ + (aligned - skew); | ||||||
|  |     // Finally, store the length and actually fetch the pointer.
 | ||||||
|  |     *len = aligned_buffer - buffer + AddressSize(); | ||||||
|  |     return ReadAddress(aligned_buffer); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Extract the value first, ignoring whether it's a pointer or an
 | ||||||
|  |   // offset relative to some base.
 | ||||||
|  |   uint64 offset; | ||||||
|  |   switch (encoding & 0x0f) { | ||||||
|  |     case DW_EH_PE_absptr: | ||||||
|  |       // DW_EH_PE_absptr is weird, as it is used as a meaningful value for
 | ||||||
|  |       // both the high and low nybble of encoding bytes. When it appears in
 | ||||||
|  |       // the high nybble, it means that the pointer is absolute, not an
 | ||||||
|  |       // offset from some base address. When it appears in the low nybble,
 | ||||||
|  |       // as here, it means that the pointer is stored as a normal
 | ||||||
|  |       // machine-sized and machine-signed address. A low nybble of
 | ||||||
|  |       // DW_EH_PE_absptr does not imply that the pointer is absolute; it is
 | ||||||
|  |       // correct for us to treat the value as an offset from a base address
 | ||||||
|  |       // if the upper nybble is not DW_EH_PE_absptr.
 | ||||||
|  |       offset = ReadAddress(buffer); | ||||||
|  |       *len = AddressSize(); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_uleb128: | ||||||
|  |       offset = ReadUnsignedLEB128(buffer, len); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_udata2: | ||||||
|  |       offset = ReadTwoBytes(buffer); | ||||||
|  |       *len = 2; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_udata4: | ||||||
|  |       offset = ReadFourBytes(buffer); | ||||||
|  |       *len = 4; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_udata8: | ||||||
|  |       offset = ReadEightBytes(buffer); | ||||||
|  |       *len = 8; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_sleb128: | ||||||
|  |       offset = ReadSignedLEB128(buffer, len); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_sdata2: | ||||||
|  |       offset = ReadTwoBytes(buffer); | ||||||
|  |       // Sign-extend from 16 bits.
 | ||||||
|  |       offset = (offset ^ 0x8000) - 0x8000; | ||||||
|  |       *len = 2; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_sdata4: | ||||||
|  |       offset = ReadFourBytes(buffer); | ||||||
|  |       // Sign-extend from 32 bits.
 | ||||||
|  |       offset = (offset ^ 0x80000000ULL) - 0x80000000ULL; | ||||||
|  |       *len = 4; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_sdata8: | ||||||
|  |       // No need to sign-extend; this is the full width of our type.
 | ||||||
|  |       offset = ReadEightBytes(buffer); | ||||||
|  |       *len = 8; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |       abort(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Find the appropriate base address.
 | ||||||
|  |   uint64 base; | ||||||
|  |   switch (encoding & 0x70) { | ||||||
|  |     case DW_EH_PE_absptr: | ||||||
|  |       base = 0; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_pcrel: | ||||||
|  |       assert(have_section_base_); | ||||||
|  |       base = section_base_ + (buffer - buffer_base_); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_textrel: | ||||||
|  |       assert(have_text_base_); | ||||||
|  |       base = text_base_; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_datarel: | ||||||
|  |       assert(have_data_base_); | ||||||
|  |       base = data_base_; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case DW_EH_PE_funcrel: | ||||||
|  |       assert(have_function_base_); | ||||||
|  |       base = function_base_; | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |       abort(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   uint64 pointer = base + offset; | ||||||
|  | 
 | ||||||
|  |   // Remove inappropriate upper bits.
 | ||||||
|  |   if (AddressSize() == 4) | ||||||
|  |     pointer = pointer & 0xffffffff; | ||||||
|  |   else | ||||||
|  |     assert(AddressSize() == sizeof(uint64)); | ||||||
|  | 
 | ||||||
|  |   return pointer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace dwarf2reader
 | ||||||
							
								
								
									
										310
									
								
								google-breakpad/src/common/dwarf/bytereader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								google-breakpad/src/common/dwarf/bytereader.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,310 @@ | |||||||
|  | // -*- mode: C++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_DWARF_BYTEREADER_H__ | ||||||
|  | #define COMMON_DWARF_BYTEREADER_H__ | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include "common/dwarf/types.h" | ||||||
|  | #include "common/dwarf/dwarf2enums.h" | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | // We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN
 | ||||||
|  | // because it conflicts with a macro
 | ||||||
|  | enum Endianness { | ||||||
|  |   ENDIANNESS_BIG, | ||||||
|  |   ENDIANNESS_LITTLE | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // A ByteReader knows how to read single- and multi-byte values of
 | ||||||
|  | // various endiannesses, sizes, and encodings, as used in DWARF
 | ||||||
|  | // debugging information and Linux C++ exception handling data.
 | ||||||
|  | class ByteReader { | ||||||
|  |  public: | ||||||
|  |   // Construct a ByteReader capable of reading one-, two-, four-, and
 | ||||||
|  |   // eight-byte values according to ENDIANNESS, absolute machine-sized
 | ||||||
|  |   // addresses, DWARF-style "initial length" values, signed and
 | ||||||
|  |   // unsigned LEB128 numbers, and Linux C++ exception handling data's
 | ||||||
|  |   // encoded pointers.
 | ||||||
|  |   explicit ByteReader(enum Endianness endianness); | ||||||
|  |   virtual ~ByteReader(); | ||||||
|  | 
 | ||||||
|  |   // Read a single byte from BUFFER and return it as an unsigned 8 bit
 | ||||||
|  |   // number.
 | ||||||
|  |   uint8 ReadOneByte(const char* buffer) const; | ||||||
|  | 
 | ||||||
|  |   // Read two bytes from BUFFER and return them as an unsigned 16 bit
 | ||||||
|  |   // number, using this ByteReader's endianness.
 | ||||||
|  |   uint16 ReadTwoBytes(const char* buffer) const; | ||||||
|  | 
 | ||||||
|  |   // Read four bytes from BUFFER and return them as an unsigned 32 bit
 | ||||||
|  |   // number, using this ByteReader's endianness. This function returns
 | ||||||
|  |   // a uint64 so that it is compatible with ReadAddress and
 | ||||||
|  |   // ReadOffset. The number it returns will never be outside the range
 | ||||||
|  |   // of an unsigned 32 bit integer.
 | ||||||
|  |   uint64 ReadFourBytes(const char* buffer) const; | ||||||
|  | 
 | ||||||
|  |   // Read eight bytes from BUFFER and return them as an unsigned 64
 | ||||||
|  |   // bit number, using this ByteReader's endianness.
 | ||||||
|  |   uint64 ReadEightBytes(const char* buffer) const; | ||||||
|  | 
 | ||||||
|  |   // Read an unsigned LEB128 (Little Endian Base 128) number from
 | ||||||
|  |   // BUFFER and return it as an unsigned 64 bit integer. Set LEN to
 | ||||||
|  |   // the number of bytes read.
 | ||||||
|  |   //
 | ||||||
|  |   // The unsigned LEB128 representation of an integer N is a variable
 | ||||||
|  |   // number of bytes:
 | ||||||
|  |   //
 | ||||||
|  |   // - If N is between 0 and 0x7f, then its unsigned LEB128
 | ||||||
|  |   //   representation is a single byte whose value is N.
 | ||||||
|  |   //
 | ||||||
|  |   // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) |
 | ||||||
|  |   //   0x80, followed by the unsigned LEB128 representation of N /
 | ||||||
|  |   //   128, rounded towards negative infinity.
 | ||||||
|  |   //
 | ||||||
|  |   // In other words, we break VALUE into groups of seven bits, put
 | ||||||
|  |   // them in little-endian order, and then write them as eight-bit
 | ||||||
|  |   // bytes with the high bit on all but the last.
 | ||||||
|  |   uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const; | ||||||
|  | 
 | ||||||
|  |   // Read a signed LEB128 number from BUFFER and return it as an
 | ||||||
|  |   // signed 64 bit integer. Set LEN to the number of bytes read.
 | ||||||
|  |   //
 | ||||||
|  |   // The signed LEB128 representation of an integer N is a variable
 | ||||||
|  |   // number of bytes:
 | ||||||
|  |   //
 | ||||||
|  |   // - If N is between -0x40 and 0x3f, then its signed LEB128
 | ||||||
|  |   //   representation is a single byte whose value is N in two's
 | ||||||
|  |   //   complement.
 | ||||||
|  |   //
 | ||||||
|  |   // - Otherwise, its signed LEB128 representation is (N & 0x7f) |
 | ||||||
|  |   //   0x80, followed by the signed LEB128 representation of N / 128,
 | ||||||
|  |   //   rounded towards negative infinity.
 | ||||||
|  |   //
 | ||||||
|  |   // In other words, we break VALUE into groups of seven bits, put
 | ||||||
|  |   // them in little-endian order, and then write them as eight-bit
 | ||||||
|  |   // bytes with the high bit on all but the last.
 | ||||||
|  |   int64 ReadSignedLEB128(const char* buffer, size_t* len) const; | ||||||
|  | 
 | ||||||
|  |   // Indicate that addresses on this architecture are SIZE bytes long. SIZE
 | ||||||
|  |   // must be either 4 or 8. (DWARF allows addresses to be any number of
 | ||||||
|  |   // bytes in length from 1 to 255, but we only support 32- and 64-bit
 | ||||||
|  |   // addresses at the moment.) You must call this before using the
 | ||||||
|  |   // ReadAddress member function.
 | ||||||
|  |   //
 | ||||||
|  |   // For data in a .debug_info section, or something that .debug_info
 | ||||||
|  |   // refers to like line number or macro data, the compilation unit
 | ||||||
|  |   // header's address_size field indicates the address size to use. Call
 | ||||||
|  |   // frame information doesn't indicate its address size (a shortcoming of
 | ||||||
|  |   // the spec); you must supply the appropriate size based on the
 | ||||||
|  |   // architecture of the target machine.
 | ||||||
|  |   void SetAddressSize(uint8 size); | ||||||
|  | 
 | ||||||
|  |   // Return the current address size, in bytes. This is either 4,
 | ||||||
|  |   // indicating 32-bit addresses, or 8, indicating 64-bit addresses.
 | ||||||
|  |   uint8 AddressSize() const { return address_size_; } | ||||||
|  | 
 | ||||||
|  |   // Read an address from BUFFER and return it as an unsigned 64 bit
 | ||||||
|  |   // integer, respecting this ByteReader's endianness and address size. You
 | ||||||
|  |   // must call SetAddressSize before calling this function.
 | ||||||
|  |   uint64 ReadAddress(const char* buffer) const; | ||||||
|  | 
 | ||||||
|  |   // DWARF actually defines two slightly different formats: 32-bit DWARF
 | ||||||
|  |   // and 64-bit DWARF. This is *not* related to the size of registers or
 | ||||||
|  |   // addresses on the target machine; it refers only to the size of section
 | ||||||
|  |   // offsets and data lengths appearing in the DWARF data. One only needs
 | ||||||
|  |   // 64-bit DWARF when the debugging data itself is larger than 4GiB.
 | ||||||
|  |   // 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the
 | ||||||
|  |   // debugging data itself is very large.
 | ||||||
|  |   //
 | ||||||
|  |   // DWARF information identifies itself as 32-bit or 64-bit DWARF: each
 | ||||||
|  |   // compilation unit and call frame information entry begins with an
 | ||||||
|  |   // "initial length" field, which, in addition to giving the length of the
 | ||||||
|  |   // data, also indicates the size of section offsets and lengths appearing
 | ||||||
|  |   // in that data. The ReadInitialLength member function, below, reads an
 | ||||||
|  |   // initial length and sets the ByteReader's offset size as a side effect.
 | ||||||
|  |   // Thus, in the normal process of reading DWARF data, the appropriate
 | ||||||
|  |   // offset size is set automatically. So, you should only need to call
 | ||||||
|  |   // SetOffsetSize if you are using the same ByteReader to jump from the
 | ||||||
|  |   // midst of one block of DWARF data into another.
 | ||||||
|  | 
 | ||||||
|  |   // Read a DWARF "initial length" field from START, and return it as
 | ||||||
|  |   // an unsigned 64 bit integer, respecting this ByteReader's
 | ||||||
|  |   // endianness. Set *LEN to the length of the initial length in
 | ||||||
|  |   // bytes, either four or twelve. As a side effect, set this
 | ||||||
|  |   // ByteReader's offset size to either 4 (if we see a 32-bit DWARF
 | ||||||
|  |   // initial length) or 8 (if we see a 64-bit DWARF initial length).
 | ||||||
|  |   //
 | ||||||
|  |   // A DWARF initial length is either:
 | ||||||
|  |   //
 | ||||||
|  |   // - a byte count stored as an unsigned 32-bit value less than
 | ||||||
|  |   //   0xffffff00, indicating that the data whose length is being
 | ||||||
|  |   //   measured uses the 32-bit DWARF format, or
 | ||||||
|  |   //
 | ||||||
|  |   // - The 32-bit value 0xffffffff, followed by a 64-bit byte count,
 | ||||||
|  |   //   indicating that the data whose length is being measured uses
 | ||||||
|  |   //   the 64-bit DWARF format.
 | ||||||
|  |   uint64 ReadInitialLength(const char* start, size_t* len); | ||||||
|  | 
 | ||||||
|  |   // Read an offset from BUFFER and return it as an unsigned 64 bit
 | ||||||
|  |   // integer, respecting the ByteReader's endianness. In 32-bit DWARF, the
 | ||||||
|  |   // offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes
 | ||||||
|  |   // long. You must call ReadInitialLength or SetOffsetSize before calling
 | ||||||
|  |   // this function; see the comments above for details.
 | ||||||
|  |   uint64 ReadOffset(const char* buffer) const; | ||||||
|  | 
 | ||||||
|  |   // Return the current offset size, in bytes.
 | ||||||
|  |   // A return value of 4 indicates that we are reading 32-bit DWARF.
 | ||||||
|  |   // A return value of 8 indicates that we are reading 64-bit DWARF.
 | ||||||
|  |   uint8 OffsetSize() const { return offset_size_; } | ||||||
|  | 
 | ||||||
|  |   // Indicate that section offsets and lengths are SIZE bytes long. SIZE
 | ||||||
|  |   // must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF).
 | ||||||
|  |   // Usually, you should not call this function yourself; instead, let a
 | ||||||
|  |   // call to ReadInitialLength establish the data's offset size
 | ||||||
|  |   // automatically.
 | ||||||
|  |   void SetOffsetSize(uint8 size); | ||||||
|  | 
 | ||||||
|  |   // The Linux C++ ABI uses a variant of DWARF call frame information
 | ||||||
|  |   // for exception handling. This data is included in the program's
 | ||||||
|  |   // address space as the ".eh_frame" section, and intepreted at
 | ||||||
|  |   // runtime to walk the stack, find exception handlers, and run
 | ||||||
|  |   // cleanup code. The format is mostly the same as DWARF CFI, with
 | ||||||
|  |   // some adjustments made to provide the additional
 | ||||||
|  |   // exception-handling data, and to make the data easier to work with
 | ||||||
|  |   // in memory --- for example, to allow it to be placed in read-only
 | ||||||
|  |   // memory even when describing position-independent code.
 | ||||||
|  |   //
 | ||||||
|  |   // In particular, exception handling data can select a number of
 | ||||||
|  |   // different encodings for pointers that appear in the data, as
 | ||||||
|  |   // described by the DwarfPointerEncoding enum. There are actually
 | ||||||
|  |   // four axes(!) to the encoding:
 | ||||||
|  |   //
 | ||||||
|  |   // - The pointer size: pointers can be 2, 4, or 8 bytes long, or use
 | ||||||
|  |   //   the DWARF LEB128 encoding.
 | ||||||
|  |   //
 | ||||||
|  |   // - The pointer's signedness: pointers can be signed or unsigned.
 | ||||||
|  |   //
 | ||||||
|  |   // - The pointer's base address: the data stored in the exception
 | ||||||
|  |   //   handling data can be the actual address (that is, an absolute
 | ||||||
|  |   //   pointer), or relative to one of a number of different base
 | ||||||
|  |   //   addreses --- including that of the encoded pointer itself, for
 | ||||||
|  |   //   a form of "pc-relative" addressing.
 | ||||||
|  |   //
 | ||||||
|  |   // - The pointer may be indirect: it may be the address where the
 | ||||||
|  |   //   true pointer is stored. (This is used to refer to things via
 | ||||||
|  |   //   global offset table entries, program linkage table entries, or
 | ||||||
|  |   //   other tricks used in position-independent code.)
 | ||||||
|  |   //
 | ||||||
|  |   // There are also two options that fall outside that matrix
 | ||||||
|  |   // altogether: the pointer may be omitted, or it may have padding to
 | ||||||
|  |   // align it on an appropriate address boundary. (That last option
 | ||||||
|  |   // may seem like it should be just another axis, but it is not.)
 | ||||||
|  | 
 | ||||||
|  |   // Indicate that the exception handling data is loaded starting at
 | ||||||
|  |   // SECTION_BASE, and that the start of its buffer in our own memory
 | ||||||
|  |   // is BUFFER_BASE. This allows us to find the address that a given
 | ||||||
|  |   // byte in our buffer would have when loaded into the program the
 | ||||||
|  |   // data describes. We need this to resolve DW_EH_PE_pcrel pointers.
 | ||||||
|  |   void SetCFIDataBase(uint64 section_base, const char *buffer_base); | ||||||
|  | 
 | ||||||
|  |   // Indicate that the base address of the program's ".text" section
 | ||||||
|  |   // is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers.
 | ||||||
|  |   void SetTextBase(uint64 text_base); | ||||||
|  | 
 | ||||||
|  |   // Indicate that the base address for DW_EH_PE_datarel pointers is
 | ||||||
|  |   // DATA_BASE. The proper value depends on the ABI; it is usually the
 | ||||||
|  |   // address of the global offset table, held in a designated register in
 | ||||||
|  |   // position-independent code. You will need to look at the startup code
 | ||||||
|  |   // for the target system to be sure. I tried; my eyes bled.
 | ||||||
|  |   void SetDataBase(uint64 data_base); | ||||||
|  | 
 | ||||||
|  |   // Indicate that the base address for the FDE we are processing is
 | ||||||
|  |   // FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel
 | ||||||
|  |   // pointers. (This encoding does not seem to be used by the GNU
 | ||||||
|  |   // toolchain.)
 | ||||||
|  |   void SetFunctionBase(uint64 function_base); | ||||||
|  | 
 | ||||||
|  |   // Indicate that we are no longer processing any FDE, so any use of
 | ||||||
|  |   // a DW_EH_PE_funcrel encoding is an error.
 | ||||||
|  |   void ClearFunctionBase(); | ||||||
|  | 
 | ||||||
|  |   // Return true if ENCODING is a valid pointer encoding.
 | ||||||
|  |   bool ValidEncoding(DwarfPointerEncoding encoding) const; | ||||||
|  | 
 | ||||||
|  |   // Return true if we have all the information we need to read a
 | ||||||
|  |   // pointer that uses ENCODING. This checks that the appropriate
 | ||||||
|  |   // SetFooBase function for ENCODING has been called.
 | ||||||
|  |   bool UsableEncoding(DwarfPointerEncoding encoding) const; | ||||||
|  | 
 | ||||||
|  |   // Read an encoded pointer from BUFFER using ENCODING; return the
 | ||||||
|  |   // absolute address it represents, and set *LEN to the pointer's
 | ||||||
|  |   // length in bytes, including any padding for aligned pointers.
 | ||||||
|  |   //
 | ||||||
|  |   // This function calls 'abort' if ENCODING is invalid or refers to a
 | ||||||
|  |   // base address this reader hasn't been given, so you should check
 | ||||||
|  |   // with ValidEncoding and UsableEncoding first if you would rather
 | ||||||
|  |   // die in a more helpful way.
 | ||||||
|  |   uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding, | ||||||
|  |                             size_t *len) const; | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  | 
 | ||||||
|  |   // Function pointer type for our address and offset readers.
 | ||||||
|  |   typedef uint64 (ByteReader::*AddressReader)(const char*) const; | ||||||
|  | 
 | ||||||
|  |   // Read an offset from BUFFER and return it as an unsigned 64 bit
 | ||||||
|  |   // integer.  DWARF2/3 define offsets as either 4 or 8 bytes,
 | ||||||
|  |   // generally depending on the amount of DWARF2/3 info present.
 | ||||||
|  |   // This function pointer gets set by SetOffsetSize.
 | ||||||
|  |   AddressReader offset_reader_; | ||||||
|  | 
 | ||||||
|  |   // Read an address from BUFFER and return it as an unsigned 64 bit
 | ||||||
|  |   // integer.  DWARF2/3 allow addresses to be any size from 0-255
 | ||||||
|  |   // bytes currently.  Internally we support 4 and 8 byte addresses,
 | ||||||
|  |   // and will CHECK on anything else.
 | ||||||
|  |   // This function pointer gets set by SetAddressSize.
 | ||||||
|  |   AddressReader address_reader_; | ||||||
|  | 
 | ||||||
|  |   Endianness endian_; | ||||||
|  |   uint8 address_size_; | ||||||
|  |   uint8 offset_size_; | ||||||
|  | 
 | ||||||
|  |   // Base addresses for Linux C++ exception handling data's encoded pointers.
 | ||||||
|  |   bool have_section_base_, have_text_base_, have_data_base_; | ||||||
|  |   bool have_function_base_; | ||||||
|  |   uint64 section_base_, text_base_, data_base_, function_base_; | ||||||
|  |   const char *buffer_base_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace dwarf2reader
 | ||||||
|  | 
 | ||||||
|  | #endif  // COMMON_DWARF_BYTEREADER_H__
 | ||||||
							
								
								
									
										697
									
								
								google-breakpad/src/common/dwarf/bytereader_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										697
									
								
								google-breakpad/src/common/dwarf/bytereader_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,697 @@ | |||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader
 | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "common/dwarf/bytereader.h" | ||||||
|  | #include "common/dwarf/bytereader-inl.h" | ||||||
|  | #include "common/dwarf/cfi_assembler.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | using dwarf2reader::ByteReader; | ||||||
|  | using dwarf2reader::DwarfPointerEncoding; | ||||||
|  | using dwarf2reader::ENDIANNESS_BIG; | ||||||
|  | using dwarf2reader::ENDIANNESS_LITTLE; | ||||||
|  | using google_breakpad::CFISection; | ||||||
|  | using google_breakpad::test_assembler::Label; | ||||||
|  | using google_breakpad::test_assembler::kBigEndian; | ||||||
|  | using google_breakpad::test_assembler::kLittleEndian; | ||||||
|  | using google_breakpad::test_assembler::Section; | ||||||
|  | using testing::Test; | ||||||
|  | 
 | ||||||
|  | struct ReaderFixture { | ||||||
|  |   string contents; | ||||||
|  |   size_t pointer_size; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Reader: public ReaderFixture, public Test { }; | ||||||
|  | class ReaderDeathTest: public ReaderFixture, public Test { }; | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, SimpleConstructor) { | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   CFISection section(kBigEndian, 4); | ||||||
|  |   section | ||||||
|  |     .D8(0xc0) | ||||||
|  |     .D16(0xcf0d) | ||||||
|  |     .D32(0x96fdd219) | ||||||
|  |     .D64(0xbbf55fef0825f117ULL) | ||||||
|  |     .ULEB128(0xa0927048ba8121afULL) | ||||||
|  |     .LEB128(-0x4f337badf4483f83LL) | ||||||
|  |     .D32(0xfec319c9); | ||||||
|  |   ASSERT_TRUE(section.GetContents(&contents)); | ||||||
|  |   const char *data = contents.data(); | ||||||
|  |   EXPECT_EQ(0xc0U, reader.ReadOneByte(data)); | ||||||
|  |   EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1)); | ||||||
|  |   EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3)); | ||||||
|  |   EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7)); | ||||||
|  |   size_t leb128_size; | ||||||
|  |   EXPECT_EQ(0xa0927048ba8121afULL, | ||||||
|  |             reader.ReadUnsignedLEB128(data + 15, &leb128_size)); | ||||||
|  |   EXPECT_EQ(10U, leb128_size); | ||||||
|  |   EXPECT_EQ(-0x4f337badf4483f83LL, | ||||||
|  |             reader.ReadSignedLEB128(data + 25, &leb128_size)); | ||||||
|  |   EXPECT_EQ(10U, leb128_size); | ||||||
|  |   EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, ValidEncodings) { | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_pcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_textrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_datarel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_absptr | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_uleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_udata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sleb128 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata2 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata4 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  |   EXPECT_TRUE(reader.ValidEncoding( | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||||
|  |                            dwarf2reader::DW_EH_PE_sdata8 | | ||||||
|  |                            dwarf2reader::DW_EH_PE_funcrel))); | ||||||
|  | 
 | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0))); | ||||||
|  |   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(ReaderDeathTest, DW_EH_PE_omit) { | ||||||
|  |   static const char data[1] = { 42 }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit, | ||||||
|  |                                          &pointer_size), | ||||||
|  |                "encoding != DW_EH_PE_omit"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_absptr4) { | ||||||
|  |   static const char data[] = { 0x27, 0x57, 0xea, 0x40 }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   EXPECT_EQ(0x40ea5727U, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(4U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_absptr8) { | ||||||
|  |   static const char data[] = { | ||||||
|  |     0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50 | ||||||
|  |   }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(8); | ||||||
|  |   EXPECT_EQ(0x010598c240ea5727ULL, | ||||||
|  |             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(8U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_uleb128) { | ||||||
|  |   static const char data[] = { 0x81, 0x84, 0x4c }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   EXPECT_EQ(0x130201U, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(3U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_udata2) { | ||||||
|  |   static const char data[] = { 0xf4, 0x8d }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   EXPECT_EQ(0xf48dU, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(2U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_udata4) { | ||||||
|  |   static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(8); | ||||||
|  |   EXPECT_EQ(0xa5628f8b, | ||||||
|  |             reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(4U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_udata8Addr8) { | ||||||
|  |   static const char data[] = { | ||||||
|  |     0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe | ||||||
|  |   }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(8); | ||||||
|  |   EXPECT_EQ(0x8fed199f69047304ULL, | ||||||
|  |             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, | ||||||
|  |                                         &pointer_size)); | ||||||
|  |   EXPECT_EQ(8U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_udata8Addr4) { | ||||||
|  |   static const char data[] = { | ||||||
|  |     0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe | ||||||
|  |   }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   EXPECT_EQ(0x69047304ULL, | ||||||
|  |             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, | ||||||
|  |                                         &pointer_size)); | ||||||
|  |   EXPECT_EQ(8U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_sleb128) { | ||||||
|  |   static const char data[] = { 0x42, 0xff, 0xfb, 0x73 }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   EXPECT_EQ(-0x030201U & 0xffffffff, | ||||||
|  |             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128, | ||||||
|  |                                         &pointer_size)); | ||||||
|  |   EXPECT_EQ(3U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_sdata2) { | ||||||
|  |   static const char data[] = { 0xb9, 0xbf }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(8); | ||||||
|  |   EXPECT_EQ(0xffffffffffffbfb9ULL, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2, | ||||||
|  |                                         &pointer_size)); | ||||||
|  |   EXPECT_EQ(2U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_sdata4) { | ||||||
|  |   static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(8); | ||||||
|  |   EXPECT_EQ(0xffffffffadc2b8f2ULL, | ||||||
|  |             reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4, | ||||||
|  |                                         &pointer_size)); | ||||||
|  |   EXPECT_EQ(4U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_sdata8) { | ||||||
|  |   static const char data[] = { | ||||||
|  |     0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87 | ||||||
|  |   }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(8); | ||||||
|  |   EXPECT_EQ(0x87269b0ce0795766ULL, | ||||||
|  |             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8, | ||||||
|  |                                         &pointer_size)); | ||||||
|  |   EXPECT_EQ(8U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_pcrel) { | ||||||
|  |   static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   DwarfPointerEncoding encoding = | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel | ||||||
|  |                            | dwarf2reader::DW_EH_PE_absptr); | ||||||
|  |   reader.SetCFIDataBase(0x89951377, data); | ||||||
|  |   EXPECT_EQ(0x89951377 + 3 + 0x14c8c402, | ||||||
|  |             reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); | ||||||
|  |   EXPECT_EQ(4U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_textrel) { | ||||||
|  |   static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e }; | ||||||
|  |   ByteReader reader(ENDIANNESS_LITTLE); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   reader.SetTextBase(0xb91beaf0); | ||||||
|  |   DwarfPointerEncoding encoding = | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel | ||||||
|  |                            | dwarf2reader::DW_EH_PE_sdata2); | ||||||
|  |   EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff, | ||||||
|  |             reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); | ||||||
|  |   EXPECT_EQ(2U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_datarel) { | ||||||
|  |   static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(8); | ||||||
|  |   reader.SetDataBase(0xbef308bd25ce74f0ULL); | ||||||
|  |   DwarfPointerEncoding encoding = | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel | ||||||
|  |                            | dwarf2reader::DW_EH_PE_sleb128); | ||||||
|  |   EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL, | ||||||
|  |             reader.ReadEncodedPointer(data + 2, encoding, &pointer_size)); | ||||||
|  |   EXPECT_EQ(3U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Reader, DW_EH_PE_funcrel) { | ||||||
|  |   static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetAddressSize(4); | ||||||
|  |   reader.SetFunctionBase(0x823c3520); | ||||||
|  |   DwarfPointerEncoding encoding = | ||||||
|  |       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel | ||||||
|  |                            | dwarf2reader::DW_EH_PE_udata2); | ||||||
|  |   EXPECT_EQ(0x823c3520 + 0xd148, | ||||||
|  |             reader.ReadEncodedPointer(data + 5, encoding, &pointer_size)); | ||||||
|  |   EXPECT_EQ(2U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UsableBase, CFI) { | ||||||
|  |   static const char data[1] = { 0x42 }; | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetCFIDataBase(0xb31cbd20, data); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UsableBase, Text) { | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetTextBase(0xa899ccb9); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UsableBase, Data) { | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetDataBase(0xf7b10bcd); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UsableBase, Function) { | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetFunctionBase(0xc2c0ed81); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(UsableBase, ClearFunction) { | ||||||
|  |   ByteReader reader(ENDIANNESS_BIG); | ||||||
|  |   reader.SetFunctionBase(0xc2c0ed81); | ||||||
|  |   reader.ClearFunctionBase(); | ||||||
|  |   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||||
|  |   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct AlignedFixture { | ||||||
|  |   AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); } | ||||||
|  |   static const char data[10]; | ||||||
|  |   ByteReader reader; | ||||||
|  |   size_t pointer_size; | ||||||
|  | }; | ||||||
|  |    | ||||||
|  | const char AlignedFixture::data[10] = { | ||||||
|  |   0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Aligned: public AlignedFixture, public Test { }; | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned0) { | ||||||
|  |   reader.SetCFIDataBase(0xb440305c, data); | ||||||
|  |   EXPECT_EQ(0xfe6e93d8U, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(4U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned1) { | ||||||
|  |   reader.SetCFIDataBase(0xb440305d, data); | ||||||
|  |   EXPECT_EQ(0xd834d51cU, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(7U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned2) { | ||||||
|  |   reader.SetCFIDataBase(0xb440305e, data); | ||||||
|  |   EXPECT_EQ(0x93d834d5U, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(6U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned3) { | ||||||
|  |   reader.SetCFIDataBase(0xb440305f, data); | ||||||
|  |   EXPECT_EQ(0x6e93d834U, | ||||||
|  |             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(5U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned11) { | ||||||
|  |   reader.SetCFIDataBase(0xb4403061, data); | ||||||
|  |   EXPECT_EQ(0xd834d51cU, | ||||||
|  |             reader.ReadEncodedPointer(data + 1, | ||||||
|  |                                       dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(6U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned30) { | ||||||
|  |   reader.SetCFIDataBase(0xb4403063, data); | ||||||
|  |   EXPECT_EQ(0x6e93d834U, | ||||||
|  |             reader.ReadEncodedPointer(data + 1, | ||||||
|  |                                       dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(4U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned23) { | ||||||
|  |   reader.SetCFIDataBase(0xb4403062, data); | ||||||
|  |   EXPECT_EQ(0x1cd3ac2bU, | ||||||
|  |             reader.ReadEncodedPointer(data + 3, | ||||||
|  |                                       dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(7U, pointer_size); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Aligned, DW_EH_PE_aligned03) { | ||||||
|  |   reader.SetCFIDataBase(0xb4403064, data); | ||||||
|  |   EXPECT_EQ(0x34d51cd3U, | ||||||
|  |             reader.ReadEncodedPointer(data + 3, | ||||||
|  |                                       dwarf2reader::DW_EH_PE_aligned, | ||||||
|  |                                       &pointer_size)); | ||||||
|  |   EXPECT_EQ(5U, pointer_size); | ||||||
|  | }   | ||||||
							
								
								
									
										198
									
								
								google-breakpad/src/common/dwarf/cfi_assembler.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								google-breakpad/src/common/dwarf/cfi_assembler.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,198 @@ | |||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // cfi_assembler.cc: Implementation of google_breakpad::CFISection class.
 | ||||||
|  | // See cfi_assembler.h for details.
 | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/cfi_assembler.h" | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | using dwarf2reader::DwarfPointerEncoding; | ||||||
|  |    | ||||||
|  | CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor, | ||||||
|  |                                   int data_alignment_factor, | ||||||
|  |                                   unsigned return_address_register, | ||||||
|  |                                   uint8_t version, | ||||||
|  |                                   const string &augmentation, | ||||||
|  |                                   bool dwarf64) { | ||||||
|  |   assert(!entry_length_); | ||||||
|  |   entry_length_ = new PendingLength(); | ||||||
|  |   in_fde_ = false; | ||||||
|  | 
 | ||||||
|  |   if (dwarf64) { | ||||||
|  |     D32(kDwarf64InitialLengthMarker); | ||||||
|  |     D64(entry_length_->length); | ||||||
|  |     entry_length_->start = Here(); | ||||||
|  |     D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier); | ||||||
|  |   } else { | ||||||
|  |     D32(entry_length_->length); | ||||||
|  |     entry_length_->start = Here(); | ||||||
|  |     D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier); | ||||||
|  |   } | ||||||
|  |   D8(version); | ||||||
|  |   AppendCString(augmentation); | ||||||
|  |   ULEB128(code_alignment_factor); | ||||||
|  |   LEB128(data_alignment_factor); | ||||||
|  |   if (version == 1) | ||||||
|  |     D8(return_address_register); | ||||||
|  |   else | ||||||
|  |     ULEB128(return_address_register); | ||||||
|  |   return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CFISection &CFISection::FDEHeader(Label cie_pointer, | ||||||
|  |                                   uint64_t initial_location, | ||||||
|  |                                   uint64_t address_range, | ||||||
|  |                                   bool dwarf64) { | ||||||
|  |   assert(!entry_length_); | ||||||
|  |   entry_length_ = new PendingLength(); | ||||||
|  |   in_fde_ = true; | ||||||
|  |   fde_start_address_ = initial_location; | ||||||
|  | 
 | ||||||
|  |   if (dwarf64) { | ||||||
|  |     D32(0xffffffff); | ||||||
|  |     D64(entry_length_->length); | ||||||
|  |     entry_length_->start = Here(); | ||||||
|  |     if (eh_frame_) | ||||||
|  |       D64(Here() - cie_pointer); | ||||||
|  |     else | ||||||
|  |       D64(cie_pointer); | ||||||
|  |   } else { | ||||||
|  |     D32(entry_length_->length); | ||||||
|  |     entry_length_->start = Here(); | ||||||
|  |     if (eh_frame_) | ||||||
|  |       D32(Here() - cie_pointer); | ||||||
|  |     else | ||||||
|  |       D32(cie_pointer); | ||||||
|  |   } | ||||||
|  |   EncodedPointer(initial_location); | ||||||
|  |   // The FDE length in an .eh_frame section uses the same encoding as the
 | ||||||
|  |   // initial location, but ignores the base address (selected by the upper
 | ||||||
|  |   // nybble of the encoding), as it's a length, not an address that can be
 | ||||||
|  |   // made relative.
 | ||||||
|  |   EncodedPointer(address_range, | ||||||
|  |                  DwarfPointerEncoding(pointer_encoding_ & 0x0f)); | ||||||
|  |   return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CFISection &CFISection::FinishEntry() { | ||||||
|  |   assert(entry_length_); | ||||||
|  |   Align(address_size_, dwarf2reader::DW_CFA_nop); | ||||||
|  |   entry_length_->length = Here() - entry_length_->start; | ||||||
|  |   delete entry_length_; | ||||||
|  |   entry_length_ = NULL; | ||||||
|  |   in_fde_ = false; | ||||||
|  |   return *this; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | CFISection &CFISection::EncodedPointer(uint64_t address, | ||||||
|  |                                        DwarfPointerEncoding encoding, | ||||||
|  |                                        const EncodedPointerBases &bases) { | ||||||
|  |   // Omitted data is extremely easy to emit.
 | ||||||
|  |   if (encoding == dwarf2reader::DW_EH_PE_omit) | ||||||
|  |     return *this; | ||||||
|  | 
 | ||||||
|  |   // If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume
 | ||||||
|  |   // that ADDRESS is the address at which the pointer is stored --- in
 | ||||||
|  |   // other words, that bit has no effect on how we write the pointer.
 | ||||||
|  |   encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect); | ||||||
|  | 
 | ||||||
|  |   // Find the base address to which this pointer is relative. The upper
 | ||||||
|  |   // nybble of the encoding specifies this.
 | ||||||
|  |   uint64_t base; | ||||||
|  |   switch (encoding & 0xf0) { | ||||||
|  |     case dwarf2reader::DW_EH_PE_absptr:  base = 0;                  break; | ||||||
|  |     case dwarf2reader::DW_EH_PE_pcrel:   base = bases.cfi + Size(); break; | ||||||
|  |     case dwarf2reader::DW_EH_PE_textrel: base = bases.text;         break; | ||||||
|  |     case dwarf2reader::DW_EH_PE_datarel: base = bases.data;         break; | ||||||
|  |     case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break; | ||||||
|  |     case dwarf2reader::DW_EH_PE_aligned: base = 0;                  break; | ||||||
|  |     default: abort(); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Make ADDRESS relative. Yes, this is appropriate even for "absptr"
 | ||||||
|  |   // values; see gcc/unwind-pe.h.
 | ||||||
|  |   address -= base; | ||||||
|  | 
 | ||||||
|  |   // Align the pointer, if required.
 | ||||||
|  |   if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned) | ||||||
|  |     Align(AddressSize()); | ||||||
|  | 
 | ||||||
|  |   // Append ADDRESS to this section in the appropriate form. For the
 | ||||||
|  |   // fixed-width forms, we don't need to differentiate between signed and
 | ||||||
|  |   // unsigned encodings, because ADDRESS has already been extended to 64
 | ||||||
|  |   // bits before it was passed to us.
 | ||||||
|  |   switch (encoding & 0x0f) { | ||||||
|  |     case dwarf2reader::DW_EH_PE_absptr: | ||||||
|  |       Address(address); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case dwarf2reader::DW_EH_PE_uleb128: | ||||||
|  |       ULEB128(address); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case dwarf2reader::DW_EH_PE_sleb128: | ||||||
|  |       LEB128(address); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case dwarf2reader::DW_EH_PE_udata2: | ||||||
|  |     case dwarf2reader::DW_EH_PE_sdata2: | ||||||
|  |       D16(address); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case dwarf2reader::DW_EH_PE_udata4: | ||||||
|  |     case dwarf2reader::DW_EH_PE_sdata4: | ||||||
|  |       D32(address); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     case dwarf2reader::DW_EH_PE_udata8: | ||||||
|  |     case dwarf2reader::DW_EH_PE_sdata8: | ||||||
|  |       D64(address); | ||||||
|  |       break; | ||||||
|  | 
 | ||||||
|  |     default: | ||||||
|  |       abort(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return *this; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const uint32_t CFISection::kDwarf64InitialLengthMarker; | ||||||
|  | const uint32_t CFISection::kDwarf32CIEIdentifier; | ||||||
|  | const uint64_t CFISection::kDwarf64CIEIdentifier; | ||||||
|  | const uint32_t CFISection::kEHFrame32CIEIdentifier; | ||||||
|  | const uint64_t CFISection::kEHFrame64CIEIdentifier; | ||||||
|  | 
 | ||||||
|  | } // namespace google_breakpad
 | ||||||
							
								
								
									
										269
									
								
								google-breakpad/src/common/dwarf/cfi_assembler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								google-breakpad/src/common/dwarf/cfi_assembler.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,269 @@ | |||||||
|  | // -*- mode: C++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // cfi_assembler.h: Define CFISection, a class for creating properly
 | ||||||
|  | // (and improperly) formatted DWARF CFI data for unit tests.
 | ||||||
|  | 
 | ||||||
|  | #ifndef PROCESSOR_CFI_ASSEMBLER_H_ | ||||||
|  | #define PROCESSOR_CFI_ASSEMBLER_H_ | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/dwarf2enums.h" | ||||||
|  | #include "common/test_assembler.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | #include "google_breakpad/common/breakpad_types.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | using dwarf2reader::DwarfPointerEncoding; | ||||||
|  | using google_breakpad::test_assembler::Endianness; | ||||||
|  | using google_breakpad::test_assembler::Label; | ||||||
|  | using google_breakpad::test_assembler::Section; | ||||||
|  | 
 | ||||||
|  | class CFISection: public Section { | ||||||
|  |  public: | ||||||
|  | 
 | ||||||
|  |   // CFI augmentation strings beginning with 'z', defined by the
 | ||||||
|  |   // Linux/IA-64 C++ ABI, can specify interesting encodings for
 | ||||||
|  |   // addresses appearing in FDE headers and call frame instructions (and
 | ||||||
|  |   // for additional fields whose presence the augmentation string
 | ||||||
|  |   // specifies). In particular, pointers can be specified to be relative
 | ||||||
|  |   // to various base address: the start of the .text section, the
 | ||||||
|  |   // location holding the address itself, and so on. These allow the
 | ||||||
|  |   // frame data to be position-independent even when they live in
 | ||||||
|  |   // write-protected pages. These variants are specified at the
 | ||||||
|  |   // following two URLs:
 | ||||||
|  |   //
 | ||||||
|  |   // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html
 | ||||||
|  |   // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
 | ||||||
|  |   //
 | ||||||
|  |   // CFISection leaves the production of well-formed 'z'-augmented CIEs and
 | ||||||
|  |   // FDEs to the user, but does provide EncodedPointer, to emit
 | ||||||
|  |   // properly-encoded addresses for a given pointer encoding.
 | ||||||
|  |   // EncodedPointer uses an instance of this structure to find the base
 | ||||||
|  |   // addresses it should use; you can establish a default for all encoded
 | ||||||
|  |   // pointers appended to this section with SetEncodedPointerBases.
 | ||||||
|  |   struct EncodedPointerBases { | ||||||
|  |     EncodedPointerBases() : cfi(), text(), data() { } | ||||||
|  | 
 | ||||||
|  |     // The starting address of this CFI section in memory, for
 | ||||||
|  |     // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data
 | ||||||
|  |     // that has is loaded into the program's address space.
 | ||||||
|  |     uint64_t cfi; | ||||||
|  | 
 | ||||||
|  |     // The starting address of this file's .text section, for DW_EH_PE_textrel.
 | ||||||
|  |     uint64_t text; | ||||||
|  | 
 | ||||||
|  |     // The starting address of this file's .got or .eh_frame_hdr section,
 | ||||||
|  |     // for DW_EH_PE_datarel.
 | ||||||
|  |     uint64_t data; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Create a CFISection whose endianness is ENDIANNESS, and where
 | ||||||
|  |   // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is
 | ||||||
|  |   // true, use the .eh_frame format, as described by the Linux
 | ||||||
|  |   // Standards Base Core Specification, instead of the DWARF CFI
 | ||||||
|  |   // format.
 | ||||||
|  |   CFISection(Endianness endianness, size_t address_size, | ||||||
|  |              bool eh_frame = false) | ||||||
|  |       : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), | ||||||
|  |         pointer_encoding_(dwarf2reader::DW_EH_PE_absptr), | ||||||
|  |         encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) { | ||||||
|  |     // The 'start', 'Here', and 'Mark' members of a CFISection all refer
 | ||||||
|  |     // to section offsets.
 | ||||||
|  |     start() = 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Return this CFISection's address size.
 | ||||||
|  |   size_t AddressSize() const { return address_size_; } | ||||||
|  | 
 | ||||||
|  |   // Return true if this CFISection uses the .eh_frame format, or
 | ||||||
|  |   // false if it contains ordinary DWARF CFI data.
 | ||||||
|  |   bool ContainsEHFrame() const { return eh_frame_; } | ||||||
|  | 
 | ||||||
|  |   // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer.
 | ||||||
|  |   void SetPointerEncoding(DwarfPointerEncoding encoding) { | ||||||
|  |     pointer_encoding_ = encoding; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Use the addresses in BASES as the base addresses for encoded
 | ||||||
|  |   // pointers in subsequent calls to FDEHeader or EncodedPointer.
 | ||||||
|  |   // This function makes a copy of BASES.
 | ||||||
|  |   void SetEncodedPointerBases(const EncodedPointerBases &bases) { | ||||||
|  |     encoded_pointer_bases_ = bases; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Append a Common Information Entry header to this section with the
 | ||||||
|  |   // given values. If dwarf64 is true, use the 64-bit DWARF initial
 | ||||||
|  |   // length format for the CIE's initial length. Return a reference to
 | ||||||
|  |   // this section. You should call FinishEntry after writing the last
 | ||||||
|  |   // instruction for the CIE.
 | ||||||
|  |   //
 | ||||||
|  |   // Before calling this function, you will typically want to use Mark
 | ||||||
|  |   // or Here to make a label to pass to FDEHeader that refers to this
 | ||||||
|  |   // CIE's position in the section.
 | ||||||
|  |   CFISection &CIEHeader(uint64_t code_alignment_factor, | ||||||
|  |                         int data_alignment_factor, | ||||||
|  |                         unsigned return_address_register, | ||||||
|  |                         uint8_t version = 3, | ||||||
|  |                         const string &augmentation = "", | ||||||
|  |                         bool dwarf64 = false); | ||||||
|  | 
 | ||||||
|  |   // Append a Frame Description Entry header to this section with the
 | ||||||
|  |   // given values. If dwarf64 is true, use the 64-bit DWARF initial
 | ||||||
|  |   // length format for the CIE's initial length. Return a reference to
 | ||||||
|  |   // this section. You should call FinishEntry after writing the last
 | ||||||
|  |   // instruction for the CIE.
 | ||||||
|  |   //
 | ||||||
|  |   // This function doesn't support entries that are longer than
 | ||||||
|  |   // 0xffffff00 bytes. (The "initial length" is always a 32-bit
 | ||||||
|  |   // value.) Nor does it support .debug_frame sections longer than
 | ||||||
|  |   // 0xffffff00 bytes.
 | ||||||
|  |   CFISection &FDEHeader(Label cie_pointer, | ||||||
|  |                         uint64_t initial_location, | ||||||
|  |                         uint64_t address_range, | ||||||
|  |                         bool dwarf64 = false); | ||||||
|  | 
 | ||||||
|  |   // Note the current position as the end of the last CIE or FDE we
 | ||||||
|  |   // started, after padding with DW_CFA_nops for alignment. This
 | ||||||
|  |   // defines the label representing the entry's length, cited in the
 | ||||||
|  |   // entry's header. Return a reference to this section.
 | ||||||
|  |   CFISection &FinishEntry(); | ||||||
|  | 
 | ||||||
|  |   // Append the contents of BLOCK as a DW_FORM_block value: an
 | ||||||
|  |   // unsigned LEB128 length, followed by that many bytes of data.
 | ||||||
|  |   CFISection &Block(const string &block) { | ||||||
|  |     ULEB128(block.size()); | ||||||
|  |     Append(block); | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Append ADDRESS to this section, in the appropriate size and
 | ||||||
|  |   // endianness. Return a reference to this section.
 | ||||||
|  |   CFISection &Address(uint64_t address) { | ||||||
|  |     Section::Append(endianness(), address_size_, address); | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  |   CFISection &Address(Label address) { | ||||||
|  |     Section::Append(endianness(), address_size_, address); | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Append ADDRESS to this section, using ENCODING and BASES. ENCODING
 | ||||||
|  |   // defaults to this section's default encoding, established by
 | ||||||
|  |   // SetPointerEncoding. BASES defaults to this section's bases, set by
 | ||||||
|  |   // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the
 | ||||||
|  |   // encoding, assume that ADDRESS is where the true address is stored.
 | ||||||
|  |   // Return a reference to this section.
 | ||||||
|  |   // 
 | ||||||
|  |   // (C++ doesn't let me use default arguments here, because I want to
 | ||||||
|  |   // refer to members of *this in the default argument expression.)
 | ||||||
|  |   CFISection &EncodedPointer(uint64_t address) { | ||||||
|  |     return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_); | ||||||
|  |   } | ||||||
|  |   CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) { | ||||||
|  |     return EncodedPointer(address, encoding, encoded_pointer_bases_); | ||||||
|  |   } | ||||||
|  |   CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding, | ||||||
|  |                              const EncodedPointerBases &bases); | ||||||
|  | 
 | ||||||
|  |   // Restate some member functions, to keep chaining working nicely.
 | ||||||
|  |   CFISection &Mark(Label *label)   { Section::Mark(label); return *this; } | ||||||
|  |   CFISection &D8(uint8_t v)       { Section::D8(v);       return *this; } | ||||||
|  |   CFISection &D16(uint16_t v)     { Section::D16(v);      return *this; } | ||||||
|  |   CFISection &D16(Label v)         { Section::D16(v);      return *this; } | ||||||
|  |   CFISection &D32(uint32_t v)     { Section::D32(v);      return *this; } | ||||||
|  |   CFISection &D32(const Label &v)  { Section::D32(v);      return *this; } | ||||||
|  |   CFISection &D64(uint64_t v)     { Section::D64(v);      return *this; } | ||||||
|  |   CFISection &D64(const Label &v)  { Section::D64(v);      return *this; } | ||||||
|  |   CFISection &LEB128(long long v)  { Section::LEB128(v);   return *this; } | ||||||
|  |   CFISection &ULEB128(uint64_t v) { Section::ULEB128(v);  return *this; } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // A length value that we've appended to the section, but is not yet
 | ||||||
|  |   // known. LENGTH is the appended value; START is a label referring
 | ||||||
|  |   // to the start of the data whose length was cited.
 | ||||||
|  |   struct PendingLength { | ||||||
|  |     Label length; | ||||||
|  |     Label start; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Constants used in CFI/.eh_frame data:
 | ||||||
|  | 
 | ||||||
|  |   // If the first four bytes of an "initial length" are this constant, then
 | ||||||
|  |   // the data uses the 64-bit DWARF format, and the length itself is the
 | ||||||
|  |   // subsequent eight bytes.
 | ||||||
|  |   static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU; | ||||||
|  | 
 | ||||||
|  |   // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data.
 | ||||||
|  |   static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0; | ||||||
|  |   static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0; | ||||||
|  |   static const uint32_t kEHFrame32CIEIdentifier = 0; | ||||||
|  |   static const uint64_t kEHFrame64CIEIdentifier = 0; | ||||||
|  | 
 | ||||||
|  |   // The size of a machine address for the data in this section.
 | ||||||
|  |   size_t address_size_; | ||||||
|  | 
 | ||||||
|  |   // If true, we are generating a Linux .eh_frame section, instead of
 | ||||||
|  |   // a standard DWARF .debug_frame section.
 | ||||||
|  |   bool eh_frame_; | ||||||
|  | 
 | ||||||
|  |   // The encoding to use for FDE pointers.
 | ||||||
|  |   DwarfPointerEncoding pointer_encoding_; | ||||||
|  | 
 | ||||||
|  |   // The base addresses to use when emitting encoded pointers.
 | ||||||
|  |   EncodedPointerBases encoded_pointer_bases_; | ||||||
|  | 
 | ||||||
|  |   // The length value for the current entry.
 | ||||||
|  |   //
 | ||||||
|  |   // Oddly, this must be dynamically allocated. Labels never get new
 | ||||||
|  |   // values; they only acquire constraints on the value they already
 | ||||||
|  |   // have, or assert if you assign them something incompatible. So
 | ||||||
|  |   // each header needs truly fresh Label objects to cite in their
 | ||||||
|  |   // headers and track their positions. The alternative is explicit
 | ||||||
|  |   // destructor invocation and a placement new. Ick.
 | ||||||
|  |   PendingLength *entry_length_; | ||||||
|  | 
 | ||||||
|  |   // True if we are currently emitting an FDE --- that is, we have
 | ||||||
|  |   // called FDEHeader but have not yet called FinishEntry.
 | ||||||
|  |   bool in_fde_; | ||||||
|  | 
 | ||||||
|  |   // If in_fde_ is true, this is its starting address. We use this for
 | ||||||
|  |   // emitting DW_EH_PE_funcrel pointers.
 | ||||||
|  |   uint64_t fde_start_address_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // PROCESSOR_CFI_ASSEMBLER_H_
 | ||||||
							
								
								
									
										198
									
								
								google-breakpad/src/common/dwarf/dwarf2diehandler.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								google-breakpad/src/common/dwarf/dwarf2diehandler.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,198 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class.
 | ||||||
|  | // See dwarf2diehandler.h for details.
 | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/dwarf2diehandler.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | DIEDispatcher::~DIEDispatcher() { | ||||||
|  |   while (!die_handlers_.empty()) { | ||||||
|  |     HandlerStack &entry = die_handlers_.top(); | ||||||
|  |     if (entry.handler_ != root_handler_) | ||||||
|  |       delete entry.handler_; | ||||||
|  |     die_handlers_.pop(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size, | ||||||
|  |                                          uint8 offset_size, uint64 cu_length, | ||||||
|  |                                          uint8 dwarf_version) { | ||||||
|  |   return root_handler_->StartCompilationUnit(offset, address_size, | ||||||
|  |                                              offset_size, cu_length, | ||||||
|  |                                              dwarf_version); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag) { | ||||||
|  |   // The stack entry for the parent of this DIE, if there is one.
 | ||||||
|  |   HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top(); | ||||||
|  | 
 | ||||||
|  |   // Does this call indicate that we're done receiving the parent's
 | ||||||
|  |   // attributes' values?  If so, call its EndAttributes member function.
 | ||||||
|  |   if (parent && parent->handler_ && !parent->reported_attributes_end_) { | ||||||
|  |     parent->reported_attributes_end_ = true; | ||||||
|  |     if (!parent->handler_->EndAttributes()) { | ||||||
|  |       // Finish off this handler now. and edit *PARENT to indicate that
 | ||||||
|  |       // we don't want to visit any of the children.
 | ||||||
|  |       parent->handler_->Finish(); | ||||||
|  |       if (parent->handler_ != root_handler_) | ||||||
|  |         delete parent->handler_; | ||||||
|  |       parent->handler_ = NULL; | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Find a handler for this DIE.
 | ||||||
|  |   DIEHandler *handler; | ||||||
|  |   if (parent) { | ||||||
|  |     if (parent->handler_) | ||||||
|  |       // Ask the parent to find a handler.
 | ||||||
|  |       handler = parent->handler_->FindChildHandler(offset, tag); | ||||||
|  |     else | ||||||
|  |       // No parent handler means we're not interested in any of our
 | ||||||
|  |       // children.
 | ||||||
|  |       handler = NULL; | ||||||
|  |   } else { | ||||||
|  |     // This is the root DIE.  For a non-root DIE, the parent's handler
 | ||||||
|  |     // decides whether to visit it, but the root DIE has no parent
 | ||||||
|  |     // handler, so we have a special method on the root DIE handler
 | ||||||
|  |     // itself to decide.
 | ||||||
|  |     if (root_handler_->StartRootDIE(offset, tag)) | ||||||
|  |       handler = root_handler_; | ||||||
|  |     else | ||||||
|  |       handler = NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Push a handler stack entry for this new handler. As an
 | ||||||
|  |   // optimization, we don't push NULL-handler entries on top of other
 | ||||||
|  |   // NULL-handler entries; we just let the oldest such entry stand for
 | ||||||
|  |   // the whole subtree.
 | ||||||
|  |   if (handler || !parent || parent->handler_) { | ||||||
|  |     HandlerStack entry; | ||||||
|  |     entry.offset_ = offset; | ||||||
|  |     entry.handler_ = handler; | ||||||
|  |     entry.reported_attributes_end_ = false; | ||||||
|  |     die_handlers_.push(entry); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return handler != NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DIEDispatcher::EndDIE(uint64 offset) { | ||||||
|  |   assert(!die_handlers_.empty()); | ||||||
|  |   HandlerStack *entry = &die_handlers_.top(); | ||||||
|  |   if (entry->handler_) { | ||||||
|  |     // This entry had better be the handler for this DIE.
 | ||||||
|  |     assert(entry->offset_ == offset); | ||||||
|  |     // If a DIE has no children, this EndDIE call indicates that we're
 | ||||||
|  |     // done receiving its attributes' values.
 | ||||||
|  |     if (!entry->reported_attributes_end_) | ||||||
|  |       entry->handler_->EndAttributes(); // Ignore return value: no children.
 | ||||||
|  |     entry->handler_->Finish(); | ||||||
|  |     if (entry->handler_ != root_handler_) | ||||||
|  |       delete entry->handler_; | ||||||
|  |   } else { | ||||||
|  |     // If this DIE is within a tree we're ignoring, then don't pop the
 | ||||||
|  |     // handler stack: that entry stands for the whole tree.
 | ||||||
|  |     if (entry->offset_ != offset) | ||||||
|  |       return; | ||||||
|  |   } | ||||||
|  |   die_handlers_.pop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset, | ||||||
|  |                                              enum DwarfAttribute attr, | ||||||
|  |                                              enum DwarfForm form, | ||||||
|  |                                              uint64 data) { | ||||||
|  |   HandlerStack ¤t = die_handlers_.top(); | ||||||
|  |   // This had better be an attribute of the DIE we were meant to handle.
 | ||||||
|  |   assert(offset == current.offset_); | ||||||
|  |   current.handler_->ProcessAttributeUnsigned(attr, form, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DIEDispatcher::ProcessAttributeSigned(uint64 offset, | ||||||
|  |                                            enum DwarfAttribute attr, | ||||||
|  |                                            enum DwarfForm form, | ||||||
|  |                                            int64 data) { | ||||||
|  |   HandlerStack ¤t = die_handlers_.top(); | ||||||
|  |   // This had better be an attribute of the DIE we were meant to handle.
 | ||||||
|  |   assert(offset == current.offset_); | ||||||
|  |   current.handler_->ProcessAttributeSigned(attr, form, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DIEDispatcher::ProcessAttributeReference(uint64 offset, | ||||||
|  |                                               enum DwarfAttribute attr, | ||||||
|  |                                               enum DwarfForm form, | ||||||
|  |                                               uint64 data) { | ||||||
|  |   HandlerStack ¤t = die_handlers_.top(); | ||||||
|  |   // This had better be an attribute of the DIE we were meant to handle.
 | ||||||
|  |   assert(offset == current.offset_); | ||||||
|  |   current.handler_->ProcessAttributeReference(attr, form, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DIEDispatcher::ProcessAttributeBuffer(uint64 offset, | ||||||
|  |                                            enum DwarfAttribute attr, | ||||||
|  |                                            enum DwarfForm form, | ||||||
|  |                                            const char* data, | ||||||
|  |                                            uint64 len) { | ||||||
|  |   HandlerStack ¤t = die_handlers_.top(); | ||||||
|  |   // This had better be an attribute of the DIE we were meant to handle.
 | ||||||
|  |   assert(offset == current.offset_); | ||||||
|  |   current.handler_->ProcessAttributeBuffer(attr, form, data, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DIEDispatcher::ProcessAttributeString(uint64 offset, | ||||||
|  |                                            enum DwarfAttribute attr, | ||||||
|  |                                            enum DwarfForm form, | ||||||
|  |                                            const string& data) { | ||||||
|  |   HandlerStack ¤t = die_handlers_.top(); | ||||||
|  |   // This had better be an attribute of the DIE we were meant to handle.
 | ||||||
|  |   assert(offset == current.offset_); | ||||||
|  |   current.handler_->ProcessAttributeString(attr, form, data); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DIEDispatcher::ProcessAttributeSignature(uint64 offset, | ||||||
|  |                                               enum DwarfAttribute attr, | ||||||
|  |                                               enum DwarfForm form, | ||||||
|  |                                               uint64 signature) { | ||||||
|  |   HandlerStack ¤t = die_handlers_.top(); | ||||||
|  |   // This had better be an attribute of the DIE we were meant to handle.
 | ||||||
|  |   assert(offset == current.offset_); | ||||||
|  |   current.handler_->ProcessAttributeSignature(attr, form, signature); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace dwarf2reader
 | ||||||
							
								
								
									
										363
									
								
								google-breakpad/src/common/dwarf/dwarf2diehandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								google-breakpad/src/common/dwarf/dwarf2diehandler.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,363 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf2reader::CompilationUnit is a simple and direct parser for
 | ||||||
|  | // DWARF data, but its handler interface is not convenient to use.  In
 | ||||||
|  | // particular:
 | ||||||
|  | //
 | ||||||
|  | // - CompilationUnit calls Dwarf2Handler's member functions to report
 | ||||||
|  | //   every attribute's value, regardless of what sort of DIE it is.
 | ||||||
|  | //   As a result, the ProcessAttributeX functions end up looking like
 | ||||||
|  | //   this:
 | ||||||
|  | //
 | ||||||
|  | //     switch (parent_die_tag) {
 | ||||||
|  | //       case DW_TAG_x:
 | ||||||
|  | //         switch (attribute_name) {
 | ||||||
|  | //           case DW_AT_y:
 | ||||||
|  | //             handle attribute y of DIE type x
 | ||||||
|  | //           ...
 | ||||||
|  | //         } break;
 | ||||||
|  | //       ...
 | ||||||
|  | //     } 
 | ||||||
|  | //
 | ||||||
|  | //   In C++ it's much nicer to use virtual function dispatch to find
 | ||||||
|  | //   the right code for a given case than to switch on the DIE tag
 | ||||||
|  | //   like this.
 | ||||||
|  | //
 | ||||||
|  | // - Processing different kinds of DIEs requires different sets of
 | ||||||
|  | //   data: lexical block DIEs have start and end addresses, but struct
 | ||||||
|  | //   type DIEs don't.  It would be nice to be able to have separate
 | ||||||
|  | //   handler classes for separate kinds of DIEs, each with the members
 | ||||||
|  | //   appropriate to its role, instead of having one handler class that
 | ||||||
|  | //   needs to hold data for every DIE type.
 | ||||||
|  | //
 | ||||||
|  | // - There should be a separate instance of the appropriate handler
 | ||||||
|  | //   class for each DIE, instead of a single object with tables
 | ||||||
|  | //   tracking all the dies in the compilation unit.
 | ||||||
|  | //
 | ||||||
|  | // - It's not convenient to take some action after all a DIE's
 | ||||||
|  | //   attributes have been seen, but before visiting any of its
 | ||||||
|  | //   children.  The only indication you have that a DIE's attribute
 | ||||||
|  | //   list is complete is that you get either a StartDIE or an EndDIE
 | ||||||
|  | //   call.
 | ||||||
|  | //
 | ||||||
|  | // - It's not convenient to make use of the tree structure of the
 | ||||||
|  | //   DIEs.  Skipping all the children of a given die requires
 | ||||||
|  | //   maintaining state and returning false from StartDIE until we get
 | ||||||
|  | //   an EndDIE call with the appropriate offset.
 | ||||||
|  | //
 | ||||||
|  | // This interface tries to take care of all that.  (You're shocked, I'm sure.)
 | ||||||
|  | //
 | ||||||
|  | // Using the classes here, you provide an initial handler for the root
 | ||||||
|  | // DIE of the compilation unit.  Each handler receives its DIE's
 | ||||||
|  | // attributes, and provides fresh handler objects for children of
 | ||||||
|  | // interest, if any.  The three classes are:
 | ||||||
|  | //
 | ||||||
|  | // - DIEHandler: the base class for your DIE-type-specific handler
 | ||||||
|  | //   classes.
 | ||||||
|  | //
 | ||||||
|  | // - RootDIEHandler: derived from DIEHandler, the base class for your
 | ||||||
|  | //   root DIE handler class.
 | ||||||
|  | //
 | ||||||
|  | // - DIEDispatcher: derived from Dwarf2Handler, an instance of this
 | ||||||
|  | //   invokes your DIE-type-specific handler objects.
 | ||||||
|  | //
 | ||||||
|  | // In detail:
 | ||||||
|  | //
 | ||||||
|  | // - Define handler classes specialized for the DIE types you're
 | ||||||
|  | //   interested in.  These handler classes must inherit from
 | ||||||
|  | //   DIEHandler.  Thus:
 | ||||||
|  | //
 | ||||||
|  | //     class My_DW_TAG_X_Handler: public DIEHandler { ... };
 | ||||||
|  | //     class My_DW_TAG_Y_Handler: public DIEHandler { ... };
 | ||||||
|  | //
 | ||||||
|  | //   DIEHandler subclasses needn't correspond exactly to single DIE
 | ||||||
|  | //   types, as shown here; the point is that you can have several
 | ||||||
|  | //   different classes appropriate to different kinds of DIEs.
 | ||||||
|  | //
 | ||||||
|  | // - In particular, define a handler class for the compilation
 | ||||||
|  | //   unit's root DIE, that inherits from RootDIEHandler:
 | ||||||
|  | //
 | ||||||
|  | //     class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... };
 | ||||||
|  | //
 | ||||||
|  | //   RootDIEHandler inherits from DIEHandler, adding a few additional
 | ||||||
|  | //   member functions for examining the compilation unit as a whole,
 | ||||||
|  | //   and other quirks of rootness.
 | ||||||
|  | //
 | ||||||
|  | // - Then, create a DIEDispatcher instance, passing it an instance of
 | ||||||
|  | //   your root DIE handler class, and use that DIEDispatcher as the
 | ||||||
|  | //   dwarf2reader::CompilationUnit's handler:
 | ||||||
|  | //
 | ||||||
|  | //     My_DW_TAG_compile_unit_Handler root_die_handler(...);
 | ||||||
|  | //     DIEDispatcher die_dispatcher(&root_die_handler);
 | ||||||
|  | //     CompilationUnit reader(sections, offset, bytereader, &die_dispatcher);
 | ||||||
|  | //
 | ||||||
|  | //   Here, 'die_dispatcher' acts as a shim between 'reader' and the
 | ||||||
|  | //   various DIE-specific handlers you have defined.
 | ||||||
|  | //
 | ||||||
|  | // - When you call reader.Start(), die_dispatcher behaves as follows,
 | ||||||
|  | //   starting with your root die handler and the compilation unit's
 | ||||||
|  | //   root DIE:
 | ||||||
|  | //
 | ||||||
|  | //   - It calls the handler's ProcessAttributeX member functions for
 | ||||||
|  | //     each of the DIE's attributes.
 | ||||||
|  | //
 | ||||||
|  | //   - It calls the handler's EndAttributes member function.  This
 | ||||||
|  | //     should return true if any of the DIE's children should be
 | ||||||
|  | //     visited, in which case:
 | ||||||
|  | //
 | ||||||
|  | //     - For each of the DIE's children, die_dispatcher calls the
 | ||||||
|  | //       DIE's handler's FindChildHandler member function.  If that
 | ||||||
|  | //       returns a pointer to a DIEHandler instance, then
 | ||||||
|  | //       die_dispatcher uses that handler to process the child, using
 | ||||||
|  | //       this procedure recursively.  Alternatively, if
 | ||||||
|  | //       FindChildHandler returns NULL, die_dispatcher ignores that
 | ||||||
|  | //       child and its descendants.
 | ||||||
|  | // 
 | ||||||
|  | //   - When die_dispatcher has finished processing all the DIE's
 | ||||||
|  | //     children, it invokes the handler's Finish() member function,
 | ||||||
|  | //     and destroys the handler.  (As a special case, it doesn't
 | ||||||
|  | //     destroy the root DIE handler.)
 | ||||||
|  | // 
 | ||||||
|  | // This allows the code for handling a particular kind of DIE to be
 | ||||||
|  | // gathered together in a single class, makes it easy to skip all the
 | ||||||
|  | // children or individual children of a particular DIE, and provides
 | ||||||
|  | // appropriate parental context for each die.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__ | ||||||
|  | #define COMMON_DWARF_DWARF2DIEHANDLER_H__ | ||||||
|  | 
 | ||||||
|  | #include <stack> | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/types.h" | ||||||
|  | #include "common/dwarf/dwarf2enums.h" | ||||||
|  | #include "common/dwarf/dwarf2reader.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | // A base class for handlers for specific DIE types.  The series of
 | ||||||
|  | // calls made on a DIE handler is as follows:
 | ||||||
|  | //
 | ||||||
|  | // - for each attribute of the DIE:
 | ||||||
|  | //   - ProcessAttributeX()
 | ||||||
|  | // - EndAttributes()
 | ||||||
|  | // - if that returned true, then for each child:
 | ||||||
|  | //   - FindChildHandler()
 | ||||||
|  | //   - if that returns a non-NULL pointer to a new handler:
 | ||||||
|  | //     - recurse, with the new handler and the child die
 | ||||||
|  | // - Finish()
 | ||||||
|  | // - destruction
 | ||||||
|  | class DIEHandler { | ||||||
|  |  public: | ||||||
|  |   DIEHandler() { } | ||||||
|  |   virtual ~DIEHandler() { } | ||||||
|  | 
 | ||||||
|  |   // When we visit a DIE, we first use these member functions to
 | ||||||
|  |   // report the DIE's attributes and their values.  These have the
 | ||||||
|  |   // same restrictions as the corresponding member functions of
 | ||||||
|  |   // dwarf2reader::Dwarf2Handler.
 | ||||||
|  |   //
 | ||||||
|  |   // Since DWARF does not specify in what order attributes must
 | ||||||
|  |   // appear, avoid making decisions in these functions that would be
 | ||||||
|  |   // affected by the presence of other attributes. The EndAttributes
 | ||||||
|  |   // function is a more appropriate place for such work, as all the
 | ||||||
|  |   // DIE's attributes have been seen at that point.
 | ||||||
|  |   //
 | ||||||
|  |   // The default definitions ignore the values they are passed.
 | ||||||
|  |   virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr, | ||||||
|  |                                         enum DwarfForm form, | ||||||
|  |                                         uint64 data) { } | ||||||
|  |   virtual void ProcessAttributeSigned(enum DwarfAttribute attr, | ||||||
|  |                                       enum DwarfForm form, | ||||||
|  |                                       int64 data) { } | ||||||
|  |   virtual void ProcessAttributeReference(enum DwarfAttribute attr, | ||||||
|  |                                          enum DwarfForm form, | ||||||
|  |                                          uint64 data) { } | ||||||
|  |   virtual void ProcessAttributeBuffer(enum DwarfAttribute attr, | ||||||
|  |                                       enum DwarfForm form, | ||||||
|  |                                       const char* data, | ||||||
|  |                                       uint64 len) { } | ||||||
|  |   virtual void ProcessAttributeString(enum DwarfAttribute attr, | ||||||
|  |                                       enum DwarfForm form, | ||||||
|  |                                       const string& data) { } | ||||||
|  |   virtual void ProcessAttributeSignature(enum DwarfAttribute attr, | ||||||
|  |                                          enum DwarfForm form, | ||||||
|  |                                          uint64 signture) { } | ||||||
|  | 
 | ||||||
|  |   // Once we have reported all the DIE's attributes' values, we call
 | ||||||
|  |   // this member function.  If it returns false, we skip all the DIE's
 | ||||||
|  |   // children.  If it returns true, we call FindChildHandler on each
 | ||||||
|  |   // child.  If that returns a handler object, we use that to visit
 | ||||||
|  |   // the child; otherwise, we skip the child.
 | ||||||
|  |   //
 | ||||||
|  |   // This is a good place to make decisions that depend on more than
 | ||||||
|  |   // one attribute. DWARF does not specify in what order attributes
 | ||||||
|  |   // must appear, so only when the EndAttributes function is called
 | ||||||
|  |   // does the handler have a complete picture of the DIE's attributes.
 | ||||||
|  |   //
 | ||||||
|  |   // The default definition elects to ignore the DIE's children.
 | ||||||
|  |   // You'll need to override this if you override FindChildHandler,
 | ||||||
|  |   // but at least the default behavior isn't to pass the children to
 | ||||||
|  |   // FindChildHandler, which then ignores them all.
 | ||||||
|  |   virtual bool EndAttributes() { return false; } | ||||||
|  | 
 | ||||||
|  |   // If EndAttributes returns true to indicate that some of the DIE's
 | ||||||
|  |   // children might be of interest, then we apply this function to
 | ||||||
|  |   // each of the DIE's children.  If it returns a handler object, then
 | ||||||
|  |   // we use that to visit the child DIE.  If it returns NULL, we skip
 | ||||||
|  |   // that child DIE (and all its descendants).
 | ||||||
|  |   //
 | ||||||
|  |   // OFFSET is the offset of the child; TAG indicates what kind of DIE
 | ||||||
|  |   // it is.
 | ||||||
|  |   //
 | ||||||
|  |   // The default definition skips all children.
 | ||||||
|  |   virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) { | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // When we are done processing a DIE, we call this member function.
 | ||||||
|  |   // This happens after the EndAttributes call, all FindChildHandler
 | ||||||
|  |   // calls (if any), and all operations on the children themselves (if
 | ||||||
|  |   // any). We call Finish on every handler --- even if EndAttributes
 | ||||||
|  |   // returns false.
 | ||||||
|  |   virtual void Finish() { }; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // A subclass of DIEHandler, with additional kludges for handling the
 | ||||||
|  | // compilation unit's root die.
 | ||||||
|  | class RootDIEHandler: public DIEHandler { | ||||||
|  |  public: | ||||||
|  |   RootDIEHandler() { } | ||||||
|  |   virtual ~RootDIEHandler() { } | ||||||
|  | 
 | ||||||
|  |   // We pass the values reported via Dwarf2Handler::StartCompilationUnit
 | ||||||
|  |   // to this member function, and skip the entire compilation unit if it
 | ||||||
|  |   // returns false.  So the root DIE handler is actually also
 | ||||||
|  |   // responsible for handling the compilation unit metadata.
 | ||||||
|  |   // The default definition always visits the compilation unit.
 | ||||||
|  |   virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||||
|  |                                     uint8 offset_size, uint64 cu_length, | ||||||
|  |                                     uint8 dwarf_version) { return true; } | ||||||
|  | 
 | ||||||
|  |   // For the root DIE handler only, we pass the offset, tag and
 | ||||||
|  |   // attributes of the compilation unit's root DIE.  This is the only
 | ||||||
|  |   // way the root DIE handler can find the root DIE's tag.  If this
 | ||||||
|  |   // function returns true, we will visit the root DIE using the usual
 | ||||||
|  |   // DIEHandler methods; otherwise, we skip the entire compilation
 | ||||||
|  |   // unit.
 | ||||||
|  |   //
 | ||||||
|  |   // The default definition elects to visit the root DIE.
 | ||||||
|  |   virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class DIEDispatcher: public Dwarf2Handler { | ||||||
|  |  public: | ||||||
|  |   // Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for
 | ||||||
|  |   // the compilation unit's root die, as described for the DIEHandler
 | ||||||
|  |   // class.
 | ||||||
|  |   DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { } | ||||||
|  |   // Destroying a DIEDispatcher destroys all active handler objects
 | ||||||
|  |   // except the root handler.
 | ||||||
|  |   ~DIEDispatcher(); | ||||||
|  |   bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||||
|  |                             uint8 offset_size, uint64 cu_length, | ||||||
|  |                             uint8 dwarf_version); | ||||||
|  |   bool StartDIE(uint64 offset, enum DwarfTag tag); | ||||||
|  |   void ProcessAttributeUnsigned(uint64 offset, | ||||||
|  |                                 enum DwarfAttribute attr, | ||||||
|  |                                 enum DwarfForm form, | ||||||
|  |                                 uint64 data); | ||||||
|  |   void ProcessAttributeSigned(uint64 offset, | ||||||
|  |                               enum DwarfAttribute attr, | ||||||
|  |                               enum DwarfForm form, | ||||||
|  |                               int64 data); | ||||||
|  |   void ProcessAttributeReference(uint64 offset, | ||||||
|  |                                  enum DwarfAttribute attr, | ||||||
|  |                                  enum DwarfForm form, | ||||||
|  |                                  uint64 data); | ||||||
|  |   void ProcessAttributeBuffer(uint64 offset, | ||||||
|  |                               enum DwarfAttribute attr, | ||||||
|  |                               enum DwarfForm form, | ||||||
|  |                               const char* data, | ||||||
|  |                               uint64 len); | ||||||
|  |   void ProcessAttributeString(uint64 offset, | ||||||
|  |                               enum DwarfAttribute attr, | ||||||
|  |                               enum DwarfForm form, | ||||||
|  |                               const string &data); | ||||||
|  |   void ProcessAttributeSignature(uint64 offset, | ||||||
|  |                                  enum DwarfAttribute attr, | ||||||
|  |                                  enum DwarfForm form, | ||||||
|  |                                  uint64 signature); | ||||||
|  |   void EndDIE(uint64 offset); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  | 
 | ||||||
|  |   // The type of a handler stack entry.  This includes some fields
 | ||||||
|  |   // which don't really need to be on the stack --- they could just be
 | ||||||
|  |   // single data members of DIEDispatcher --- but putting them here
 | ||||||
|  |   // makes it easier to see that the code is correct.
 | ||||||
|  |   struct HandlerStack { | ||||||
|  |     // The offset of the DIE for this handler stack entry.
 | ||||||
|  |     uint64 offset_; | ||||||
|  | 
 | ||||||
|  |     // The handler object interested in this DIE's attributes and
 | ||||||
|  |     // children.  If NULL, we're not interested in either.
 | ||||||
|  |     DIEHandler *handler_; | ||||||
|  | 
 | ||||||
|  |     // Have we reported the end of this DIE's attributes to the handler?
 | ||||||
|  |     bool reported_attributes_end_; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Stack of DIE attribute handlers.  At StartDIE(D), the top of the
 | ||||||
|  |   // stack is the handler of D's parent, whom we may ask for a handler
 | ||||||
|  |   // for D itself.  At EndDIE(D), the top of the stack is D's handler.
 | ||||||
|  |   // Special cases:
 | ||||||
|  |   //
 | ||||||
|  |   // - Before we've seen the compilation unit's root DIE, the stack is
 | ||||||
|  |   //   empty; we'll call root_handler_'s special member functions, and
 | ||||||
|  |   //   perhaps push root_handler_ on the stack to look at the root's
 | ||||||
|  |   //   immediate children.
 | ||||||
|  |   //
 | ||||||
|  |   // - When we decide to ignore a subtree, we only push an entry on
 | ||||||
|  |   //   the stack for the root of the tree being ignored, rather than
 | ||||||
|  |   //   pushing lots of stack entries with handler_ set to NULL.
 | ||||||
|  |   std::stack<HandlerStack> die_handlers_; | ||||||
|  | 
 | ||||||
|  |   // The root handler.  We don't push it on die_handlers_ until we
 | ||||||
|  |   // actually get the StartDIE call for the root.
 | ||||||
|  |   RootDIEHandler *root_handler_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace dwarf2reader
 | ||||||
|  | #endif  // COMMON_DWARF_DWARF2DIEHANDLER_H__
 | ||||||
							
								
								
									
										524
									
								
								google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										524
									
								
								google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,524 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher.
 | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <utility> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/dwarf2diehandler.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | using std::make_pair; | ||||||
|  | 
 | ||||||
|  | using ::testing::_; | ||||||
|  | using ::testing::ContainerEq; | ||||||
|  | using ::testing::ElementsAreArray; | ||||||
|  | using ::testing::Eq; | ||||||
|  | using ::testing::InSequence; | ||||||
|  | using ::testing::Return; | ||||||
|  | using ::testing::Sequence; | ||||||
|  | using ::testing::StrEq; | ||||||
|  | 
 | ||||||
|  | using dwarf2reader::DIEDispatcher; | ||||||
|  | using dwarf2reader::DIEHandler; | ||||||
|  | using dwarf2reader::DwarfAttribute; | ||||||
|  | using dwarf2reader::DwarfForm; | ||||||
|  | using dwarf2reader::DwarfTag; | ||||||
|  | using dwarf2reader::RootDIEHandler; | ||||||
|  | 
 | ||||||
|  | class MockDIEHandler: public DIEHandler { | ||||||
|  |  public: | ||||||
|  |   MOCK_METHOD3(ProcessAttributeUnsigned, | ||||||
|  |                void(DwarfAttribute, DwarfForm, uint64)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeSigned, | ||||||
|  |                void(DwarfAttribute, DwarfForm, int64)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeReference, | ||||||
|  |                void(DwarfAttribute, DwarfForm, uint64)); | ||||||
|  |   MOCK_METHOD4(ProcessAttributeBuffer, | ||||||
|  |                void(DwarfAttribute, DwarfForm, const char *, uint64)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeString, | ||||||
|  |                void(DwarfAttribute, DwarfForm, const string &)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeSignature, | ||||||
|  |                void(DwarfAttribute, DwarfForm, uint64)); | ||||||
|  |   MOCK_METHOD0(EndAttributes, bool()); | ||||||
|  |   MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); | ||||||
|  |   MOCK_METHOD0(Finish, void()); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class MockRootDIEHandler: public RootDIEHandler { | ||||||
|  |  public: | ||||||
|  |   MOCK_METHOD3(ProcessAttributeUnsigned, | ||||||
|  |                void(DwarfAttribute, DwarfForm, uint64)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeSigned, | ||||||
|  |                void(DwarfAttribute, DwarfForm, int64)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeReference, | ||||||
|  |                void(DwarfAttribute, DwarfForm, uint64)); | ||||||
|  |   MOCK_METHOD4(ProcessAttributeBuffer, | ||||||
|  |                void(DwarfAttribute, DwarfForm, const char *, uint64)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeString, | ||||||
|  |                void(DwarfAttribute, DwarfForm, const string &)); | ||||||
|  |   MOCK_METHOD3(ProcessAttributeSignature, | ||||||
|  |                void(DwarfAttribute, DwarfForm, uint64)); | ||||||
|  |   MOCK_METHOD0(EndAttributes, bool()); | ||||||
|  |   MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); | ||||||
|  |   MOCK_METHOD0(Finish, void()); | ||||||
|  |   MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8)); | ||||||
|  |   MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag)); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // If the handler elects to skip the compilation unit, the dispatcher
 | ||||||
|  | // should tell the reader so.
 | ||||||
|  | TEST(Dwarf2DIEHandler, SkipCompilationUnit) { | ||||||
|  |   Sequence s; | ||||||
|  |   MockRootDIEHandler mock_root_handler; | ||||||
|  |   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||||
|  | 
 | ||||||
|  |   EXPECT_CALL(mock_root_handler, | ||||||
|  |               StartCompilationUnit(0x8d42aed77cfccf3eLL, | ||||||
|  |                                    0x89, 0xdc, | ||||||
|  |                                    0x2ecb4dc778a80f21LL, | ||||||
|  |                                    0x66)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return(false)); | ||||||
|  | 
 | ||||||
|  |   EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, | ||||||
|  |                                                    0x89, 0xdc, | ||||||
|  |                                                    0x2ecb4dc778a80f21LL, | ||||||
|  |                                                    0x66)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // If the handler elects to skip the root DIE, the dispatcher should
 | ||||||
|  | // tell the reader so.
 | ||||||
|  | TEST(Dwarf2DIEHandler, SkipRootDIE) { | ||||||
|  |   Sequence s; | ||||||
|  |   MockRootDIEHandler mock_root_handler; | ||||||
|  |   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||||
|  | 
 | ||||||
|  |   EXPECT_CALL(mock_root_handler, | ||||||
|  |               StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02, | ||||||
|  |                                    0xb00febffa76e2b2bLL, 0x5c)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  |   EXPECT_CALL(mock_root_handler, | ||||||
|  |               StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return(false)); | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,  | ||||||
|  |                                                   0xf4, 0x02, | ||||||
|  |                                                   0xb00febffa76e2b2bLL, 0x5c)); | ||||||
|  |   EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, | ||||||
|  |                                        (DwarfTag) 0xb4f98da6)); | ||||||
|  |   die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // If the handler elects to skip the root DIE's children, the
 | ||||||
|  | // dispatcher should tell the reader so --- and avoid deleting the
 | ||||||
|  | // root handler.
 | ||||||
|  | TEST(Dwarf2DIEHandler, SkipRootDIEChildren) { | ||||||
|  |   MockRootDIEHandler mock_root_handler; | ||||||
|  |   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||||
|  | 
 | ||||||
|  |   { | ||||||
|  |     InSequence s; | ||||||
|  | 
 | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0, | ||||||
|  |                                      0x09f8bf0767f91675LL, 0xdb)) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  |     // Please don't tell me about my children.
 | ||||||
|  |     EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||||
|  |       .WillOnce(Return(false)); | ||||||
|  |     EXPECT_CALL(mock_root_handler, Finish()) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL, | ||||||
|  |                                                   0x26, 0xa0, | ||||||
|  |                                                   0x09f8bf0767f91675LL, 0xdb)); | ||||||
|  |   EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, | ||||||
|  |                                       (DwarfTag) 0xb4f98da6)); | ||||||
|  |   EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL, | ||||||
|  |                                        (DwarfTag) 0xc3a17bba)); | ||||||
|  |   die_dispatcher.EndDIE(0x435150ceedccda18LL); | ||||||
|  |   die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // The dispatcher should pass attribute values through to the die
 | ||||||
|  | // handler accurately.
 | ||||||
|  | TEST(Dwarf2DIEHandler, PassAttributeValues) { | ||||||
|  |   MockRootDIEHandler mock_root_handler; | ||||||
|  |   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||||
|  | 
 | ||||||
|  |   const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca, | ||||||
|  |                             0xcf, 0xa8, 0x84, 0xa7, 0x18 }; | ||||||
|  |   string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d"; | ||||||
|  | 
 | ||||||
|  |   // Set expectations.
 | ||||||
|  |   { | ||||||
|  |     InSequence s; | ||||||
|  | 
 | ||||||
|  |     // We'll like the compilation unit header.
 | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc, | ||||||
|  |                                      0x2ecb4dc778a80f21LL, 0x66)) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  | 
 | ||||||
|  |     // We'll like the root DIE.
 | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c)) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  | 
 | ||||||
|  |     // Expect some attribute values.
 | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed, | ||||||
|  |                                          (DwarfForm) 0x424f1468, | ||||||
|  |                                          0xa592571997facda1ULL)) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 ProcessAttributeSigned((DwarfAttribute) 0x43694dc9, | ||||||
|  |                                        (DwarfForm) 0xf6f78901L, | ||||||
|  |                                        0x92602a4e3bf1f446LL)) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 ProcessAttributeReference((DwarfAttribute) 0x4033e8cL, | ||||||
|  |                                           (DwarfForm) 0xf66fbe0bL, | ||||||
|  |                                           0x50fddef44734fdecULL)) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af, | ||||||
|  |                                        (DwarfForm) 0xe99a539a, | ||||||
|  |                                        buffer, sizeof(buffer))) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 ProcessAttributeString((DwarfAttribute) 0x310ed065, | ||||||
|  |                                        (DwarfForm) 0x15762fec, | ||||||
|  |                                        StrEq(str))) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 ProcessAttributeSignature((DwarfAttribute) 0x58790d72, | ||||||
|  |                                           (DwarfForm) 0x4159f138, | ||||||
|  |                                           0x94682463613e6a5fULL)) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |     EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  |     EXPECT_CALL(mock_root_handler, FindChildHandler(_, _)) | ||||||
|  |       .Times(0); | ||||||
|  |     EXPECT_CALL(mock_root_handler, Finish()) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Drive the dispatcher.
 | ||||||
|  | 
 | ||||||
|  |   // Report the CU header.
 | ||||||
|  |   EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, | ||||||
|  |                                                   0x89, 0xdc, | ||||||
|  |                                                   0x2ecb4dc778a80f21LL, | ||||||
|  |                                                   0x66)); | ||||||
|  |   // Report the root DIE.
 | ||||||
|  |   EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL, | ||||||
|  |                                       (DwarfTag) 0x9829445c)); | ||||||
|  | 
 | ||||||
|  |   // Report some attribute values.
 | ||||||
|  |   die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL, | ||||||
|  |                                           (DwarfAttribute) 0x1cc0bfed, | ||||||
|  |                                           (DwarfForm) 0x424f1468, | ||||||
|  |                                           0xa592571997facda1ULL); | ||||||
|  |   die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL, | ||||||
|  |                                         (DwarfAttribute) 0x43694dc9, | ||||||
|  |                                         (DwarfForm) 0xf6f78901, | ||||||
|  |                                         0x92602a4e3bf1f446LL); | ||||||
|  |   die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL, | ||||||
|  |                                            (DwarfAttribute) 0x4033e8c, | ||||||
|  |                                            (DwarfForm) 0xf66fbe0b, | ||||||
|  |                                            0x50fddef44734fdecULL); | ||||||
|  |   die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL, | ||||||
|  |                                         (DwarfAttribute) 0x25d7e0af, | ||||||
|  |                                         (DwarfForm) 0xe99a539a, | ||||||
|  |                                         buffer, sizeof(buffer)); | ||||||
|  |   die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL, | ||||||
|  |                                         (DwarfAttribute) 0x310ed065, | ||||||
|  |                                         (DwarfForm) 0x15762fec, | ||||||
|  |                                         str); | ||||||
|  |   die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL, | ||||||
|  |                                            (DwarfAttribute) 0x58790d72, | ||||||
|  |                                            (DwarfForm) 0x4159f138, | ||||||
|  |                                            0x94682463613e6a5fULL); | ||||||
|  | 
 | ||||||
|  |   // Finish the root DIE (and thus the CU).
 | ||||||
|  |   die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(Dwarf2DIEHandler, FindAndSkipChildren) { | ||||||
|  |   MockRootDIEHandler mock_root_handler; | ||||||
|  |   MockDIEHandler *mock_child1_handler = new(MockDIEHandler); | ||||||
|  |   MockDIEHandler *mock_child3_handler = new(MockDIEHandler); | ||||||
|  |   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||||
|  | 
 | ||||||
|  |   { | ||||||
|  |     InSequence s; | ||||||
|  | 
 | ||||||
|  |     // We'll like the compilation unit header.
 | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, | ||||||
|  |                                      0x47dd3c764275a216LL, 0xa5)) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  | 
 | ||||||
|  |     // Root DIE.
 | ||||||
|  |     { | ||||||
|  |       EXPECT_CALL(mock_root_handler, | ||||||
|  |                   StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59)) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  |       EXPECT_CALL(mock_root_handler, | ||||||
|  |                   ProcessAttributeSigned((DwarfAttribute) 0xf779a642, | ||||||
|  |                                          (DwarfForm) 0x2cb63027, | ||||||
|  |                                          0x18e744661769d08fLL)) | ||||||
|  |         .WillOnce(Return()); | ||||||
|  |       EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  | 
 | ||||||
|  |       // First child DIE.
 | ||||||
|  |       EXPECT_CALL(mock_root_handler, | ||||||
|  |                   FindChildHandler(0x149f644f8116fe8cLL, | ||||||
|  |                                    (DwarfTag) 0xac2cbd8c)) | ||||||
|  |         .WillOnce(Return(mock_child1_handler)); | ||||||
|  |       { | ||||||
|  |         EXPECT_CALL(*mock_child1_handler, | ||||||
|  |                     ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65, | ||||||
|  |                                            (DwarfForm) 0xe4f64c41, | ||||||
|  |                                            0x1b04e5444a55fe67LL)) | ||||||
|  |           .WillOnce(Return()); | ||||||
|  |         EXPECT_CALL(*mock_child1_handler, EndAttributes()) | ||||||
|  |           .WillOnce(Return(false)); | ||||||
|  |         // Skip first grandchild DIE and first great-grandchild DIE.
 | ||||||
|  |         EXPECT_CALL(*mock_child1_handler, Finish()) | ||||||
|  |           .WillOnce(Return()); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       // Second child DIE.  Root handler will decline to return a handler
 | ||||||
|  |       // for this child.
 | ||||||
|  |       EXPECT_CALL(mock_root_handler, | ||||||
|  |                   FindChildHandler(0x97412be24875de9dLL, | ||||||
|  |                                    (DwarfTag) 0x505a068b)) | ||||||
|  |         .WillOnce(Return((DIEHandler *) NULL)); | ||||||
|  | 
 | ||||||
|  |       // Third child DIE.
 | ||||||
|  |       EXPECT_CALL(mock_root_handler, | ||||||
|  |                   FindChildHandler(0x753c964c8ab538aeLL, | ||||||
|  |                                    (DwarfTag) 0x8c22970e)) | ||||||
|  |         .WillOnce(Return(mock_child3_handler)); | ||||||
|  |       { | ||||||
|  |         EXPECT_CALL(*mock_child3_handler, | ||||||
|  |                     ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, | ||||||
|  |                                            (DwarfForm) 0x610b7ae1, | ||||||
|  |                                            0x3ea5c609d7d7560fLL)) | ||||||
|  |           .WillOnce(Return()); | ||||||
|  |         EXPECT_CALL(*mock_child3_handler, EndAttributes()) | ||||||
|  |           .WillOnce(Return(true)); | ||||||
|  |         EXPECT_CALL(*mock_child3_handler, Finish()) | ||||||
|  |           .WillOnce(Return()); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       EXPECT_CALL(mock_root_handler, Finish()) | ||||||
|  |         .WillOnce(Return()); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |      | ||||||
|  |   // Drive the dispatcher.
 | ||||||
|  | 
 | ||||||
|  |   // Report the CU header.
 | ||||||
|  |   EXPECT_TRUE(die_dispatcher | ||||||
|  |               .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, | ||||||
|  |                                     0x47dd3c764275a216LL, 0xa5)); | ||||||
|  |   // Report the root DIE.
 | ||||||
|  |   { | ||||||
|  |     EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL, | ||||||
|  |                                         (DwarfTag) 0xf5d60c59)); | ||||||
|  |     die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL, | ||||||
|  |                                           (DwarfAttribute) 0xf779a642, | ||||||
|  |                                           (DwarfForm) 0x2cb63027, | ||||||
|  |                                           0x18e744661769d08fLL); | ||||||
|  | 
 | ||||||
|  |     // First child DIE.
 | ||||||
|  |     { | ||||||
|  |       EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL, | ||||||
|  |                                           (DwarfTag) 0xac2cbd8c)); | ||||||
|  |       die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL, | ||||||
|  |                                             (DwarfAttribute) 0xa6fd6f65, | ||||||
|  |                                             (DwarfForm) 0xe4f64c41, | ||||||
|  |                                             0x1b04e5444a55fe67LL); | ||||||
|  | 
 | ||||||
|  |       // First grandchild DIE.  Will be skipped.
 | ||||||
|  |       { | ||||||
|  |         EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL, | ||||||
|  |                                             (DwarfTag) 0x22f05a15)); | ||||||
|  |         // First great-grandchild DIE.  Will be skipped without being
 | ||||||
|  |         // mentioned to any handler.
 | ||||||
|  |         { | ||||||
|  |           EXPECT_FALSE(die_dispatcher | ||||||
|  |                        .StartDIE(0xb3076285d25cac25LL, | ||||||
|  |                                  (DwarfTag) 0xcff4061b)); | ||||||
|  |           die_dispatcher.EndDIE(0xb3076285d25cac25LL);           | ||||||
|  |         } | ||||||
|  |         die_dispatcher.EndDIE(0xd68de1ee0bd29419LL); | ||||||
|  |       } | ||||||
|  |       die_dispatcher.EndDIE(0x149f644f8116fe8cLL); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Second child DIE.  Root handler will decline to find a handler for it.
 | ||||||
|  |     { | ||||||
|  |       EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL, | ||||||
|  |                                            (DwarfTag) 0x505a068b)); | ||||||
|  |       die_dispatcher.EndDIE(0x97412be24875de9dLL); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Third child DIE.
 | ||||||
|  |     { | ||||||
|  |       EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL, | ||||||
|  |                                           (DwarfTag) 0x8c22970e)); | ||||||
|  |       die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL, | ||||||
|  |                                             (DwarfAttribute) 0x4e2b7cfb, | ||||||
|  |                                             (DwarfForm) 0x610b7ae1, | ||||||
|  |                                             0x3ea5c609d7d7560fLL); | ||||||
|  |       die_dispatcher.EndDIE(0x753c964c8ab538aeLL); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     // Finish the root DIE (and thus the CU).
 | ||||||
|  |     die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // The DIEDispatcher destructor is supposed to delete all handlers on
 | ||||||
|  | // the stack, except for the root.
 | ||||||
|  | TEST(Dwarf2DIEHandler, FreeHandlersOnStack) { | ||||||
|  |   MockRootDIEHandler mock_root_handler; | ||||||
|  |   MockDIEHandler *mock_child_handler = new(MockDIEHandler); | ||||||
|  |   MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler); | ||||||
|  | 
 | ||||||
|  |   { | ||||||
|  |     InSequence s; | ||||||
|  | 
 | ||||||
|  |     // We'll like the compilation unit header.
 | ||||||
|  |     EXPECT_CALL(mock_root_handler, | ||||||
|  |                 StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, | ||||||
|  |                                      0x76d392ff393ddda2LL, 0xbf)) | ||||||
|  |       .WillOnce(Return(true)); | ||||||
|  | 
 | ||||||
|  |     // Root DIE.
 | ||||||
|  |     { | ||||||
|  |       EXPECT_CALL(mock_root_handler, | ||||||
|  |                   StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361)) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  |       EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  |        | ||||||
|  |       // Child DIE.
 | ||||||
|  |       EXPECT_CALL(mock_root_handler, | ||||||
|  |                   FindChildHandler(0x058f09240c5fc8c9LL, | ||||||
|  |                                    (DwarfTag) 0x898bf0d0)) | ||||||
|  |         .WillOnce(Return(mock_child_handler)); | ||||||
|  |       { | ||||||
|  |         EXPECT_CALL(*mock_child_handler, EndAttributes()) | ||||||
|  |           .WillOnce(Return(true)); | ||||||
|  | 
 | ||||||
|  |         // Grandchild DIE.
 | ||||||
|  |         EXPECT_CALL(*mock_child_handler, | ||||||
|  |                     FindChildHandler(0x32dc00c9945dc0c8LL, | ||||||
|  |                                      (DwarfTag) 0x2802d007)) | ||||||
|  |           .WillOnce(Return(mock_grandchild_handler)); | ||||||
|  |         { | ||||||
|  |           EXPECT_CALL(*mock_grandchild_handler, | ||||||
|  |                       ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, | ||||||
|  |                                              (DwarfForm) 0x610b7ae1, | ||||||
|  |                                              0x3ea5c609d7d7560fLL)) | ||||||
|  |             .WillOnce(Return()); | ||||||
|  | 
 | ||||||
|  |           // At this point, we abandon the traversal, so none of the
 | ||||||
|  |           // usual stuff should get called.
 | ||||||
|  |           EXPECT_CALL(*mock_grandchild_handler, EndAttributes()) | ||||||
|  |             .Times(0); | ||||||
|  |           EXPECT_CALL(*mock_grandchild_handler, Finish()) | ||||||
|  |             .Times(0); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         EXPECT_CALL(*mock_child_handler, Finish()) | ||||||
|  |           .Times(0); | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       EXPECT_CALL(mock_root_handler, Finish()) | ||||||
|  |         .Times(0); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // The dispatcher.
 | ||||||
|  |   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||||
|  |    | ||||||
|  |   // Report the CU header.
 | ||||||
|  |   EXPECT_TRUE(die_dispatcher | ||||||
|  |               .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, | ||||||
|  |                                     0x76d392ff393ddda2LL, 0xbf)); | ||||||
|  |   // Report the root DIE.
 | ||||||
|  |   { | ||||||
|  |     EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL, | ||||||
|  |                                         (DwarfTag) 0x98980361)); | ||||||
|  | 
 | ||||||
|  |     // Child DIE.
 | ||||||
|  |     { | ||||||
|  |       EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL, | ||||||
|  |                                           (DwarfTag) 0x898bf0d0)); | ||||||
|  | 
 | ||||||
|  |       // Grandchild DIE.
 | ||||||
|  |       { | ||||||
|  |         EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL, | ||||||
|  |                                             (DwarfTag) 0x2802d007)); | ||||||
|  |         die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL, | ||||||
|  |                                               (DwarfAttribute) 0x4e2b7cfb, | ||||||
|  |                                               (DwarfForm) 0x610b7ae1, | ||||||
|  |                                               0x3ea5c609d7d7560fLL); | ||||||
|  | 
 | ||||||
|  |         // Stop the traversal abruptly, so that there will still be
 | ||||||
|  |         // handlers on the stack when the dispatcher is destructed.
 | ||||||
|  | 
 | ||||||
|  |         // No EndDIE call...
 | ||||||
|  |       } | ||||||
|  |       // No EndDIE call...
 | ||||||
|  |     } | ||||||
|  |     // No EndDIE call...
 | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										650
									
								
								google-breakpad/src/common/dwarf/dwarf2enums.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										650
									
								
								google-breakpad/src/common/dwarf/dwarf2enums.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,650 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_DWARF_DWARF2ENUMS_H__ | ||||||
|  | #define COMMON_DWARF_DWARF2ENUMS_H__ | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | // These enums do not follow the google3 style only because they are
 | ||||||
|  | // known universally (specs, other implementations) by the names in
 | ||||||
|  | // exactly this capitalization.
 | ||||||
|  | // Tag names and codes.
 | ||||||
|  | enum DwarfTag { | ||||||
|  |   DW_TAG_padding = 0x00, | ||||||
|  |   DW_TAG_array_type = 0x01, | ||||||
|  |   DW_TAG_class_type = 0x02, | ||||||
|  |   DW_TAG_entry_point = 0x03, | ||||||
|  |   DW_TAG_enumeration_type = 0x04, | ||||||
|  |   DW_TAG_formal_parameter = 0x05, | ||||||
|  |   DW_TAG_imported_declaration = 0x08, | ||||||
|  |   DW_TAG_label = 0x0a, | ||||||
|  |   DW_TAG_lexical_block = 0x0b, | ||||||
|  |   DW_TAG_member = 0x0d, | ||||||
|  |   DW_TAG_pointer_type = 0x0f, | ||||||
|  |   DW_TAG_reference_type = 0x10, | ||||||
|  |   DW_TAG_compile_unit = 0x11, | ||||||
|  |   DW_TAG_string_type = 0x12, | ||||||
|  |   DW_TAG_structure_type = 0x13, | ||||||
|  |   DW_TAG_subroutine_type = 0x15, | ||||||
|  |   DW_TAG_typedef = 0x16, | ||||||
|  |   DW_TAG_union_type = 0x17, | ||||||
|  |   DW_TAG_unspecified_parameters = 0x18, | ||||||
|  |   DW_TAG_variant = 0x19, | ||||||
|  |   DW_TAG_common_block = 0x1a, | ||||||
|  |   DW_TAG_common_inclusion = 0x1b, | ||||||
|  |   DW_TAG_inheritance = 0x1c, | ||||||
|  |   DW_TAG_inlined_subroutine = 0x1d, | ||||||
|  |   DW_TAG_module = 0x1e, | ||||||
|  |   DW_TAG_ptr_to_member_type = 0x1f, | ||||||
|  |   DW_TAG_set_type = 0x20, | ||||||
|  |   DW_TAG_subrange_type = 0x21, | ||||||
|  |   DW_TAG_with_stmt = 0x22, | ||||||
|  |   DW_TAG_access_declaration = 0x23, | ||||||
|  |   DW_TAG_base_type = 0x24, | ||||||
|  |   DW_TAG_catch_block = 0x25, | ||||||
|  |   DW_TAG_const_type = 0x26, | ||||||
|  |   DW_TAG_constant = 0x27, | ||||||
|  |   DW_TAG_enumerator = 0x28, | ||||||
|  |   DW_TAG_file_type = 0x29, | ||||||
|  |   DW_TAG_friend = 0x2a, | ||||||
|  |   DW_TAG_namelist = 0x2b, | ||||||
|  |   DW_TAG_namelist_item = 0x2c, | ||||||
|  |   DW_TAG_packed_type = 0x2d, | ||||||
|  |   DW_TAG_subprogram = 0x2e, | ||||||
|  |   DW_TAG_template_type_param = 0x2f, | ||||||
|  |   DW_TAG_template_value_param = 0x30, | ||||||
|  |   DW_TAG_thrown_type = 0x31, | ||||||
|  |   DW_TAG_try_block = 0x32, | ||||||
|  |   DW_TAG_variant_part = 0x33, | ||||||
|  |   DW_TAG_variable = 0x34, | ||||||
|  |   DW_TAG_volatile_type = 0x35, | ||||||
|  |   // DWARF 3.
 | ||||||
|  |   DW_TAG_dwarf_procedure = 0x36, | ||||||
|  |   DW_TAG_restrict_type = 0x37, | ||||||
|  |   DW_TAG_interface_type = 0x38, | ||||||
|  |   DW_TAG_namespace = 0x39, | ||||||
|  |   DW_TAG_imported_module = 0x3a, | ||||||
|  |   DW_TAG_unspecified_type = 0x3b, | ||||||
|  |   DW_TAG_partial_unit = 0x3c, | ||||||
|  |   DW_TAG_imported_unit = 0x3d, | ||||||
|  |   // SGI/MIPS Extensions.
 | ||||||
|  |   DW_TAG_MIPS_loop = 0x4081, | ||||||
|  |   // HP extensions.  See:
 | ||||||
|  |   // ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz
 | ||||||
|  |   DW_TAG_HP_array_descriptor = 0x4090, | ||||||
|  |   // GNU extensions.
 | ||||||
|  |   DW_TAG_format_label = 0x4101,  // For FORTRAN 77 and Fortran 90.
 | ||||||
|  |   DW_TAG_function_template = 0x4102,  // For C++.
 | ||||||
|  |   DW_TAG_class_template = 0x4103,  // For C++.
 | ||||||
|  |   DW_TAG_GNU_BINCL = 0x4104, | ||||||
|  |   DW_TAG_GNU_EINCL = 0x4105, | ||||||
|  |   // Extensions for UPC.  See: http://upc.gwu.edu/~upc.
 | ||||||
|  |   DW_TAG_upc_shared_type = 0x8765, | ||||||
|  |   DW_TAG_upc_strict_type = 0x8766, | ||||||
|  |   DW_TAG_upc_relaxed_type = 0x8767, | ||||||
|  |   // PGI (STMicroelectronics) extensions.  No documentation available.
 | ||||||
|  |   DW_TAG_PGI_kanji_type      = 0xA000, | ||||||
|  |   DW_TAG_PGI_interface_block = 0xA020 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | enum DwarfHasChild { | ||||||
|  |   DW_children_no = 0, | ||||||
|  |   DW_children_yes = 1 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Form names and codes.
 | ||||||
|  | enum DwarfForm { | ||||||
|  |   DW_FORM_addr = 0x01, | ||||||
|  |   DW_FORM_block2 = 0x03, | ||||||
|  |   DW_FORM_block4 = 0x04, | ||||||
|  |   DW_FORM_data2 = 0x05, | ||||||
|  |   DW_FORM_data4 = 0x06, | ||||||
|  |   DW_FORM_data8 = 0x07, | ||||||
|  |   DW_FORM_string = 0x08, | ||||||
|  |   DW_FORM_block = 0x09, | ||||||
|  |   DW_FORM_block1 = 0x0a, | ||||||
|  |   DW_FORM_data1 = 0x0b, | ||||||
|  |   DW_FORM_flag = 0x0c, | ||||||
|  |   DW_FORM_sdata = 0x0d, | ||||||
|  |   DW_FORM_strp = 0x0e, | ||||||
|  |   DW_FORM_udata = 0x0f, | ||||||
|  |   DW_FORM_ref_addr = 0x10, | ||||||
|  |   DW_FORM_ref1 = 0x11, | ||||||
|  |   DW_FORM_ref2 = 0x12, | ||||||
|  |   DW_FORM_ref4 = 0x13, | ||||||
|  |   DW_FORM_ref8 = 0x14, | ||||||
|  |   DW_FORM_ref_udata = 0x15, | ||||||
|  |   DW_FORM_indirect = 0x16, | ||||||
|  | 
 | ||||||
|  |   // Added in DWARF 4:
 | ||||||
|  |   DW_FORM_sec_offset = 0x17, | ||||||
|  |   DW_FORM_exprloc = 0x18, | ||||||
|  |   DW_FORM_flag_present = 0x19, | ||||||
|  |   DW_FORM_ref_sig8 = 0x20 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Attribute names and codes
 | ||||||
|  | enum DwarfAttribute { | ||||||
|  |   DW_AT_sibling = 0x01, | ||||||
|  |   DW_AT_location = 0x02, | ||||||
|  |   DW_AT_name = 0x03, | ||||||
|  |   DW_AT_ordering = 0x09, | ||||||
|  |   DW_AT_subscr_data = 0x0a, | ||||||
|  |   DW_AT_byte_size = 0x0b, | ||||||
|  |   DW_AT_bit_offset = 0x0c, | ||||||
|  |   DW_AT_bit_size = 0x0d, | ||||||
|  |   DW_AT_element_list = 0x0f, | ||||||
|  |   DW_AT_stmt_list = 0x10, | ||||||
|  |   DW_AT_low_pc = 0x11, | ||||||
|  |   DW_AT_high_pc = 0x12, | ||||||
|  |   DW_AT_language = 0x13, | ||||||
|  |   DW_AT_member = 0x14, | ||||||
|  |   DW_AT_discr = 0x15, | ||||||
|  |   DW_AT_discr_value = 0x16, | ||||||
|  |   DW_AT_visibility = 0x17, | ||||||
|  |   DW_AT_import = 0x18, | ||||||
|  |   DW_AT_string_length = 0x19, | ||||||
|  |   DW_AT_common_reference = 0x1a, | ||||||
|  |   DW_AT_comp_dir = 0x1b, | ||||||
|  |   DW_AT_const_value = 0x1c, | ||||||
|  |   DW_AT_containing_type = 0x1d, | ||||||
|  |   DW_AT_default_value = 0x1e, | ||||||
|  |   DW_AT_inline = 0x20, | ||||||
|  |   DW_AT_is_optional = 0x21, | ||||||
|  |   DW_AT_lower_bound = 0x22, | ||||||
|  |   DW_AT_producer = 0x25, | ||||||
|  |   DW_AT_prototyped = 0x27, | ||||||
|  |   DW_AT_return_addr = 0x2a, | ||||||
|  |   DW_AT_start_scope = 0x2c, | ||||||
|  |   DW_AT_stride_size = 0x2e, | ||||||
|  |   DW_AT_upper_bound = 0x2f, | ||||||
|  |   DW_AT_abstract_origin = 0x31, | ||||||
|  |   DW_AT_accessibility = 0x32, | ||||||
|  |   DW_AT_address_class = 0x33, | ||||||
|  |   DW_AT_artificial = 0x34, | ||||||
|  |   DW_AT_base_types = 0x35, | ||||||
|  |   DW_AT_calling_convention = 0x36, | ||||||
|  |   DW_AT_count = 0x37, | ||||||
|  |   DW_AT_data_member_location = 0x38, | ||||||
|  |   DW_AT_decl_column = 0x39, | ||||||
|  |   DW_AT_decl_file = 0x3a, | ||||||
|  |   DW_AT_decl_line = 0x3b, | ||||||
|  |   DW_AT_declaration = 0x3c, | ||||||
|  |   DW_AT_discr_list = 0x3d, | ||||||
|  |   DW_AT_encoding = 0x3e, | ||||||
|  |   DW_AT_external = 0x3f, | ||||||
|  |   DW_AT_frame_base = 0x40, | ||||||
|  |   DW_AT_friend = 0x41, | ||||||
|  |   DW_AT_identifier_case = 0x42, | ||||||
|  |   DW_AT_macro_info = 0x43, | ||||||
|  |   DW_AT_namelist_items = 0x44, | ||||||
|  |   DW_AT_priority = 0x45, | ||||||
|  |   DW_AT_segment = 0x46, | ||||||
|  |   DW_AT_specification = 0x47, | ||||||
|  |   DW_AT_static_link = 0x48, | ||||||
|  |   DW_AT_type = 0x49, | ||||||
|  |   DW_AT_use_location = 0x4a, | ||||||
|  |   DW_AT_variable_parameter = 0x4b, | ||||||
|  |   DW_AT_virtuality = 0x4c, | ||||||
|  |   DW_AT_vtable_elem_location = 0x4d, | ||||||
|  |   // DWARF 3 values.
 | ||||||
|  |   DW_AT_allocated     = 0x4e, | ||||||
|  |   DW_AT_associated    = 0x4f, | ||||||
|  |   DW_AT_data_location = 0x50, | ||||||
|  |   DW_AT_stride        = 0x51, | ||||||
|  |   DW_AT_entry_pc      = 0x52, | ||||||
|  |   DW_AT_use_UTF8      = 0x53, | ||||||
|  |   DW_AT_extension     = 0x54, | ||||||
|  |   DW_AT_ranges        = 0x55, | ||||||
|  |   DW_AT_trampoline    = 0x56, | ||||||
|  |   DW_AT_call_column   = 0x57, | ||||||
|  |   DW_AT_call_file     = 0x58, | ||||||
|  |   DW_AT_call_line     = 0x59, | ||||||
|  |   // SGI/MIPS extensions.
 | ||||||
|  |   DW_AT_MIPS_fde = 0x2001, | ||||||
|  |   DW_AT_MIPS_loop_begin = 0x2002, | ||||||
|  |   DW_AT_MIPS_tail_loop_begin = 0x2003, | ||||||
|  |   DW_AT_MIPS_epilog_begin = 0x2004, | ||||||
|  |   DW_AT_MIPS_loop_unroll_factor = 0x2005, | ||||||
|  |   DW_AT_MIPS_software_pipeline_depth = 0x2006, | ||||||
|  |   DW_AT_MIPS_linkage_name = 0x2007, | ||||||
|  |   DW_AT_MIPS_stride = 0x2008, | ||||||
|  |   DW_AT_MIPS_abstract_name = 0x2009, | ||||||
|  |   DW_AT_MIPS_clone_origin = 0x200a, | ||||||
|  |   DW_AT_MIPS_has_inlines = 0x200b, | ||||||
|  |   // HP extensions.
 | ||||||
|  |   DW_AT_HP_block_index         = 0x2000, | ||||||
|  |   DW_AT_HP_unmodifiable        = 0x2001,  // Same as DW_AT_MIPS_fde.
 | ||||||
|  |   DW_AT_HP_actuals_stmt_list   = 0x2010, | ||||||
|  |   DW_AT_HP_proc_per_section    = 0x2011, | ||||||
|  |   DW_AT_HP_raw_data_ptr        = 0x2012, | ||||||
|  |   DW_AT_HP_pass_by_reference   = 0x2013, | ||||||
|  |   DW_AT_HP_opt_level           = 0x2014, | ||||||
|  |   DW_AT_HP_prof_version_id     = 0x2015, | ||||||
|  |   DW_AT_HP_opt_flags           = 0x2016, | ||||||
|  |   DW_AT_HP_cold_region_low_pc  = 0x2017, | ||||||
|  |   DW_AT_HP_cold_region_high_pc = 0x2018, | ||||||
|  |   DW_AT_HP_all_variables_modifiable = 0x2019, | ||||||
|  |   DW_AT_HP_linkage_name        = 0x201a, | ||||||
|  |   DW_AT_HP_prof_flags          = 0x201b,  // In comp unit of procs_info for -g.
 | ||||||
|  |   // GNU extensions.
 | ||||||
|  |   DW_AT_sf_names   = 0x2101, | ||||||
|  |   DW_AT_src_info   = 0x2102, | ||||||
|  |   DW_AT_mac_info   = 0x2103, | ||||||
|  |   DW_AT_src_coords = 0x2104, | ||||||
|  |   DW_AT_body_begin = 0x2105, | ||||||
|  |   DW_AT_body_end   = 0x2106, | ||||||
|  |   DW_AT_GNU_vector = 0x2107, | ||||||
|  |   // VMS extensions.
 | ||||||
|  |   DW_AT_VMS_rtnbeg_pd_address = 0x2201, | ||||||
|  |   // UPC extension.
 | ||||||
|  |   DW_AT_upc_threads_scaled = 0x3210, | ||||||
|  |   // PGI (STMicroelectronics) extensions.
 | ||||||
|  |   DW_AT_PGI_lbase    = 0x3a00, | ||||||
|  |   DW_AT_PGI_soffset  = 0x3a01, | ||||||
|  |   DW_AT_PGI_lstride  = 0x3a02 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // Line number opcodes.
 | ||||||
|  | enum DwarfLineNumberOps { | ||||||
|  |   DW_LNS_extended_op = 0, | ||||||
|  |   DW_LNS_copy = 1, | ||||||
|  |   DW_LNS_advance_pc = 2, | ||||||
|  |   DW_LNS_advance_line = 3, | ||||||
|  |   DW_LNS_set_file = 4, | ||||||
|  |   DW_LNS_set_column = 5, | ||||||
|  |   DW_LNS_negate_stmt = 6, | ||||||
|  |   DW_LNS_set_basic_block = 7, | ||||||
|  |   DW_LNS_const_add_pc = 8, | ||||||
|  |   DW_LNS_fixed_advance_pc = 9, | ||||||
|  |   // DWARF 3.
 | ||||||
|  |   DW_LNS_set_prologue_end = 10, | ||||||
|  |   DW_LNS_set_epilogue_begin = 11, | ||||||
|  |   DW_LNS_set_isa = 12 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Line number extended opcodes.
 | ||||||
|  | enum DwarfLineNumberExtendedOps { | ||||||
|  |   DW_LNE_end_sequence = 1, | ||||||
|  |   DW_LNE_set_address = 2, | ||||||
|  |   DW_LNE_define_file = 3, | ||||||
|  |   // HP extensions.
 | ||||||
|  |   DW_LNE_HP_negate_is_UV_update      = 0x11, | ||||||
|  |   DW_LNE_HP_push_context             = 0x12, | ||||||
|  |   DW_LNE_HP_pop_context              = 0x13, | ||||||
|  |   DW_LNE_HP_set_file_line_column     = 0x14, | ||||||
|  |   DW_LNE_HP_set_routine_name         = 0x15, | ||||||
|  |   DW_LNE_HP_set_sequence             = 0x16, | ||||||
|  |   DW_LNE_HP_negate_post_semantics    = 0x17, | ||||||
|  |   DW_LNE_HP_negate_function_exit     = 0x18, | ||||||
|  |   DW_LNE_HP_negate_front_end_logical = 0x19, | ||||||
|  |   DW_LNE_HP_define_proc              = 0x20 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Type encoding names and codes
 | ||||||
|  | enum DwarfEncoding { | ||||||
|  |   DW_ATE_address                     =0x1, | ||||||
|  |   DW_ATE_boolean                     =0x2, | ||||||
|  |   DW_ATE_complex_float               =0x3, | ||||||
|  |   DW_ATE_float                       =0x4, | ||||||
|  |   DW_ATE_signed                      =0x5, | ||||||
|  |   DW_ATE_signed_char                 =0x6, | ||||||
|  |   DW_ATE_unsigned                    =0x7, | ||||||
|  |   DW_ATE_unsigned_char               =0x8, | ||||||
|  |   // DWARF3/DWARF3f
 | ||||||
|  |   DW_ATE_imaginary_float             =0x9, | ||||||
|  |   DW_ATE_packed_decimal              =0xa, | ||||||
|  |   DW_ATE_numeric_string              =0xb, | ||||||
|  |   DW_ATE_edited                      =0xc, | ||||||
|  |   DW_ATE_signed_fixed                =0xd, | ||||||
|  |   DW_ATE_unsigned_fixed              =0xe, | ||||||
|  |   DW_ATE_decimal_float               =0xf, | ||||||
|  |   DW_ATE_lo_user                     =0x80, | ||||||
|  |   DW_ATE_hi_user                     =0xff | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Location virtual machine opcodes
 | ||||||
|  | enum DwarfOpcode { | ||||||
|  |   DW_OP_addr                         =0x03, | ||||||
|  |   DW_OP_deref                        =0x06, | ||||||
|  |   DW_OP_const1u                      =0x08, | ||||||
|  |   DW_OP_const1s                      =0x09, | ||||||
|  |   DW_OP_const2u                      =0x0a, | ||||||
|  |   DW_OP_const2s                      =0x0b, | ||||||
|  |   DW_OP_const4u                      =0x0c, | ||||||
|  |   DW_OP_const4s                      =0x0d, | ||||||
|  |   DW_OP_const8u                      =0x0e, | ||||||
|  |   DW_OP_const8s                      =0x0f, | ||||||
|  |   DW_OP_constu                       =0x10, | ||||||
|  |   DW_OP_consts                       =0x11, | ||||||
|  |   DW_OP_dup                          =0x12, | ||||||
|  |   DW_OP_drop                         =0x13, | ||||||
|  |   DW_OP_over                         =0x14, | ||||||
|  |   DW_OP_pick                         =0x15, | ||||||
|  |   DW_OP_swap                         =0x16, | ||||||
|  |   DW_OP_rot                          =0x17, | ||||||
|  |   DW_OP_xderef                       =0x18, | ||||||
|  |   DW_OP_abs                          =0x19, | ||||||
|  |   DW_OP_and                          =0x1a, | ||||||
|  |   DW_OP_div                          =0x1b, | ||||||
|  |   DW_OP_minus                        =0x1c, | ||||||
|  |   DW_OP_mod                          =0x1d, | ||||||
|  |   DW_OP_mul                          =0x1e, | ||||||
|  |   DW_OP_neg                          =0x1f, | ||||||
|  |   DW_OP_not                          =0x20, | ||||||
|  |   DW_OP_or                           =0x21, | ||||||
|  |   DW_OP_plus                         =0x22, | ||||||
|  |   DW_OP_plus_uconst                  =0x23, | ||||||
|  |   DW_OP_shl                          =0x24, | ||||||
|  |   DW_OP_shr                          =0x25, | ||||||
|  |   DW_OP_shra                         =0x26, | ||||||
|  |   DW_OP_xor                          =0x27, | ||||||
|  |   DW_OP_bra                          =0x28, | ||||||
|  |   DW_OP_eq                           =0x29, | ||||||
|  |   DW_OP_ge                           =0x2a, | ||||||
|  |   DW_OP_gt                           =0x2b, | ||||||
|  |   DW_OP_le                           =0x2c, | ||||||
|  |   DW_OP_lt                           =0x2d, | ||||||
|  |   DW_OP_ne                           =0x2e, | ||||||
|  |   DW_OP_skip                         =0x2f, | ||||||
|  |   DW_OP_lit0                         =0x30, | ||||||
|  |   DW_OP_lit1                         =0x31, | ||||||
|  |   DW_OP_lit2                         =0x32, | ||||||
|  |   DW_OP_lit3                         =0x33, | ||||||
|  |   DW_OP_lit4                         =0x34, | ||||||
|  |   DW_OP_lit5                         =0x35, | ||||||
|  |   DW_OP_lit6                         =0x36, | ||||||
|  |   DW_OP_lit7                         =0x37, | ||||||
|  |   DW_OP_lit8                         =0x38, | ||||||
|  |   DW_OP_lit9                         =0x39, | ||||||
|  |   DW_OP_lit10                        =0x3a, | ||||||
|  |   DW_OP_lit11                        =0x3b, | ||||||
|  |   DW_OP_lit12                        =0x3c, | ||||||
|  |   DW_OP_lit13                        =0x3d, | ||||||
|  |   DW_OP_lit14                        =0x3e, | ||||||
|  |   DW_OP_lit15                        =0x3f, | ||||||
|  |   DW_OP_lit16                        =0x40, | ||||||
|  |   DW_OP_lit17                        =0x41, | ||||||
|  |   DW_OP_lit18                        =0x42, | ||||||
|  |   DW_OP_lit19                        =0x43, | ||||||
|  |   DW_OP_lit20                        =0x44, | ||||||
|  |   DW_OP_lit21                        =0x45, | ||||||
|  |   DW_OP_lit22                        =0x46, | ||||||
|  |   DW_OP_lit23                        =0x47, | ||||||
|  |   DW_OP_lit24                        =0x48, | ||||||
|  |   DW_OP_lit25                        =0x49, | ||||||
|  |   DW_OP_lit26                        =0x4a, | ||||||
|  |   DW_OP_lit27                        =0x4b, | ||||||
|  |   DW_OP_lit28                        =0x4c, | ||||||
|  |   DW_OP_lit29                        =0x4d, | ||||||
|  |   DW_OP_lit30                        =0x4e, | ||||||
|  |   DW_OP_lit31                        =0x4f, | ||||||
|  |   DW_OP_reg0                         =0x50, | ||||||
|  |   DW_OP_reg1                         =0x51, | ||||||
|  |   DW_OP_reg2                         =0x52, | ||||||
|  |   DW_OP_reg3                         =0x53, | ||||||
|  |   DW_OP_reg4                         =0x54, | ||||||
|  |   DW_OP_reg5                         =0x55, | ||||||
|  |   DW_OP_reg6                         =0x56, | ||||||
|  |   DW_OP_reg7                         =0x57, | ||||||
|  |   DW_OP_reg8                         =0x58, | ||||||
|  |   DW_OP_reg9                         =0x59, | ||||||
|  |   DW_OP_reg10                        =0x5a, | ||||||
|  |   DW_OP_reg11                        =0x5b, | ||||||
|  |   DW_OP_reg12                        =0x5c, | ||||||
|  |   DW_OP_reg13                        =0x5d, | ||||||
|  |   DW_OP_reg14                        =0x5e, | ||||||
|  |   DW_OP_reg15                        =0x5f, | ||||||
|  |   DW_OP_reg16                        =0x60, | ||||||
|  |   DW_OP_reg17                        =0x61, | ||||||
|  |   DW_OP_reg18                        =0x62, | ||||||
|  |   DW_OP_reg19                        =0x63, | ||||||
|  |   DW_OP_reg20                        =0x64, | ||||||
|  |   DW_OP_reg21                        =0x65, | ||||||
|  |   DW_OP_reg22                        =0x66, | ||||||
|  |   DW_OP_reg23                        =0x67, | ||||||
|  |   DW_OP_reg24                        =0x68, | ||||||
|  |   DW_OP_reg25                        =0x69, | ||||||
|  |   DW_OP_reg26                        =0x6a, | ||||||
|  |   DW_OP_reg27                        =0x6b, | ||||||
|  |   DW_OP_reg28                        =0x6c, | ||||||
|  |   DW_OP_reg29                        =0x6d, | ||||||
|  |   DW_OP_reg30                        =0x6e, | ||||||
|  |   DW_OP_reg31                        =0x6f, | ||||||
|  |   DW_OP_breg0                        =0x70, | ||||||
|  |   DW_OP_breg1                        =0x71, | ||||||
|  |   DW_OP_breg2                        =0x72, | ||||||
|  |   DW_OP_breg3                        =0x73, | ||||||
|  |   DW_OP_breg4                        =0x74, | ||||||
|  |   DW_OP_breg5                        =0x75, | ||||||
|  |   DW_OP_breg6                        =0x76, | ||||||
|  |   DW_OP_breg7                        =0x77, | ||||||
|  |   DW_OP_breg8                        =0x78, | ||||||
|  |   DW_OP_breg9                        =0x79, | ||||||
|  |   DW_OP_breg10                       =0x7a, | ||||||
|  |   DW_OP_breg11                       =0x7b, | ||||||
|  |   DW_OP_breg12                       =0x7c, | ||||||
|  |   DW_OP_breg13                       =0x7d, | ||||||
|  |   DW_OP_breg14                       =0x7e, | ||||||
|  |   DW_OP_breg15                       =0x7f, | ||||||
|  |   DW_OP_breg16                       =0x80, | ||||||
|  |   DW_OP_breg17                       =0x81, | ||||||
|  |   DW_OP_breg18                       =0x82, | ||||||
|  |   DW_OP_breg19                       =0x83, | ||||||
|  |   DW_OP_breg20                       =0x84, | ||||||
|  |   DW_OP_breg21                       =0x85, | ||||||
|  |   DW_OP_breg22                       =0x86, | ||||||
|  |   DW_OP_breg23                       =0x87, | ||||||
|  |   DW_OP_breg24                       =0x88, | ||||||
|  |   DW_OP_breg25                       =0x89, | ||||||
|  |   DW_OP_breg26                       =0x8a, | ||||||
|  |   DW_OP_breg27                       =0x8b, | ||||||
|  |   DW_OP_breg28                       =0x8c, | ||||||
|  |   DW_OP_breg29                       =0x8d, | ||||||
|  |   DW_OP_breg30                       =0x8e, | ||||||
|  |   DW_OP_breg31                       =0x8f, | ||||||
|  |   DW_OP_regX                         =0x90, | ||||||
|  |   DW_OP_fbreg                        =0x91, | ||||||
|  |   DW_OP_bregX                        =0x92, | ||||||
|  |   DW_OP_piece                        =0x93, | ||||||
|  |   DW_OP_deref_size                   =0x94, | ||||||
|  |   DW_OP_xderef_size                  =0x95, | ||||||
|  |   DW_OP_nop                          =0x96, | ||||||
|  |   // DWARF3/DWARF3f
 | ||||||
|  |   DW_OP_push_object_address          =0x97, | ||||||
|  |   DW_OP_call2                        =0x98, | ||||||
|  |   DW_OP_call4                        =0x99, | ||||||
|  |   DW_OP_call_ref                     =0x9a, | ||||||
|  |   DW_OP_form_tls_address             =0x9b, | ||||||
|  |   DW_OP_call_frame_cfa               =0x9c, | ||||||
|  |   DW_OP_bit_piece                    =0x9d, | ||||||
|  |   DW_OP_lo_user                      =0xe0, | ||||||
|  |   DW_OP_hi_user                      =0xff,   | ||||||
|  |   // GNU extensions
 | ||||||
|  |   DW_OP_GNU_push_tls_address         =0xe0 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Source languages.  These are values for DW_AT_language.
 | ||||||
|  | enum DwarfLanguage | ||||||
|  |   { | ||||||
|  |     DW_LANG_none                     =0x0000, | ||||||
|  |     DW_LANG_C89                      =0x0001, | ||||||
|  |     DW_LANG_C                        =0x0002, | ||||||
|  |     DW_LANG_Ada83                    =0x0003, | ||||||
|  |     DW_LANG_C_plus_plus              =0x0004, | ||||||
|  |     DW_LANG_Cobol74                  =0x0005, | ||||||
|  |     DW_LANG_Cobol85                  =0x0006, | ||||||
|  |     DW_LANG_Fortran77                =0x0007, | ||||||
|  |     DW_LANG_Fortran90                =0x0008, | ||||||
|  |     DW_LANG_Pascal83                 =0x0009, | ||||||
|  |     DW_LANG_Modula2                  =0x000a, | ||||||
|  |     DW_LANG_Java                     =0x000b, | ||||||
|  |     DW_LANG_C99                      =0x000c, | ||||||
|  |     DW_LANG_Ada95                    =0x000d, | ||||||
|  |     DW_LANG_Fortran95                =0x000e, | ||||||
|  |     DW_LANG_PLI                      =0x000f, | ||||||
|  |     DW_LANG_ObjC                     =0x0010, | ||||||
|  |     DW_LANG_ObjC_plus_plus           =0x0011, | ||||||
|  |     DW_LANG_UPC                      =0x0012, | ||||||
|  |     DW_LANG_D                        =0x0013, | ||||||
|  |     // Implementation-defined language code range.
 | ||||||
|  |     DW_LANG_lo_user = 0x8000, | ||||||
|  |     DW_LANG_hi_user = 0xffff, | ||||||
|  | 
 | ||||||
|  |     // Extensions.
 | ||||||
|  | 
 | ||||||
|  |     // MIPS assembly language.  The GNU toolchain uses this for all
 | ||||||
|  |     // assembly languages, since there's no generic DW_LANG_ value for that.
 | ||||||
|  |     // See include/dwarf2.h in the binutils, gdb, or gcc source trees.
 | ||||||
|  |     DW_LANG_Mips_Assembler           =0x8001, | ||||||
|  |     DW_LANG_Upc                      =0x8765 // Unified Parallel C
 | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  | // Inline codes.  These are values for DW_AT_inline.
 | ||||||
|  | enum DwarfInline { | ||||||
|  |   DW_INL_not_inlined                 =0x0, | ||||||
|  |   DW_INL_inlined                     =0x1, | ||||||
|  |   DW_INL_declared_not_inlined        =0x2, | ||||||
|  |   DW_INL_declared_inlined            =0x3 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Call Frame Info instructions.
 | ||||||
|  | enum DwarfCFI | ||||||
|  |   { | ||||||
|  |     DW_CFA_advance_loc        = 0x40, | ||||||
|  |     DW_CFA_offset             = 0x80, | ||||||
|  |     DW_CFA_restore            = 0xc0, | ||||||
|  |     DW_CFA_nop                = 0x00, | ||||||
|  |     DW_CFA_set_loc            = 0x01, | ||||||
|  |     DW_CFA_advance_loc1       = 0x02, | ||||||
|  |     DW_CFA_advance_loc2       = 0x03, | ||||||
|  |     DW_CFA_advance_loc4       = 0x04, | ||||||
|  |     DW_CFA_offset_extended    = 0x05, | ||||||
|  |     DW_CFA_restore_extended   = 0x06, | ||||||
|  |     DW_CFA_undefined          = 0x07, | ||||||
|  |     DW_CFA_same_value         = 0x08, | ||||||
|  |     DW_CFA_register           = 0x09, | ||||||
|  |     DW_CFA_remember_state     = 0x0a, | ||||||
|  |     DW_CFA_restore_state      = 0x0b, | ||||||
|  |     DW_CFA_def_cfa            = 0x0c, | ||||||
|  |     DW_CFA_def_cfa_register   = 0x0d, | ||||||
|  |     DW_CFA_def_cfa_offset     = 0x0e, | ||||||
|  |     DW_CFA_def_cfa_expression = 0x0f, | ||||||
|  |     DW_CFA_expression         = 0x10, | ||||||
|  |     DW_CFA_offset_extended_sf = 0x11, | ||||||
|  |     DW_CFA_def_cfa_sf         = 0x12, | ||||||
|  |     DW_CFA_def_cfa_offset_sf  = 0x13, | ||||||
|  |     DW_CFA_val_offset         = 0x14, | ||||||
|  |     DW_CFA_val_offset_sf      = 0x15, | ||||||
|  |     DW_CFA_val_expression     = 0x16, | ||||||
|  | 
 | ||||||
|  |     // Opcodes in this range are reserved for user extensions.
 | ||||||
|  |     DW_CFA_lo_user = 0x1c, | ||||||
|  |     DW_CFA_hi_user = 0x3f, | ||||||
|  | 
 | ||||||
|  |     // SGI/MIPS specific.
 | ||||||
|  |     DW_CFA_MIPS_advance_loc8 = 0x1d, | ||||||
|  | 
 | ||||||
|  |     // GNU extensions.
 | ||||||
|  |     DW_CFA_GNU_window_save = 0x2d, | ||||||
|  |     DW_CFA_GNU_args_size = 0x2e, | ||||||
|  |     DW_CFA_GNU_negative_offset_extended = 0x2f | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  | // Exception handling 'z' augmentation letters.
 | ||||||
|  | enum DwarfZAugmentationCodes { | ||||||
|  |   // If the CFI augmentation string begins with 'z', then the CIE and FDE
 | ||||||
|  |   // have an augmentation data area just before the instructions, whose
 | ||||||
|  |   // contents are determined by the subsequent augmentation letters.
 | ||||||
|  |   DW_Z_augmentation_start = 'z', | ||||||
|  | 
 | ||||||
|  |   // If this letter is present in a 'z' augmentation string, the CIE
 | ||||||
|  |   // augmentation data includes a pointer encoding, and the FDE
 | ||||||
|  |   // augmentation data includes a language-specific data area pointer,
 | ||||||
|  |   // represented using that encoding.
 | ||||||
|  |   DW_Z_has_LSDA = 'L', | ||||||
|  | 
 | ||||||
|  |   // If this letter is present in a 'z' augmentation string, the CIE
 | ||||||
|  |   // augmentation data includes a pointer encoding, followed by a pointer
 | ||||||
|  |   // to a personality routine, represented using that encoding.
 | ||||||
|  |   DW_Z_has_personality_routine = 'P', | ||||||
|  | 
 | ||||||
|  |   // If this letter is present in a 'z' augmentation string, the CIE
 | ||||||
|  |   // augmentation data includes a pointer encoding describing how the FDE's
 | ||||||
|  |   // initial location, address range, and DW_CFA_set_loc operands are
 | ||||||
|  |   // encoded.
 | ||||||
|  |   DW_Z_has_FDE_address_encoding = 'R', | ||||||
|  | 
 | ||||||
|  |   // If this letter is present in a 'z' augmentation string, then code
 | ||||||
|  |   // addresses covered by FDEs that cite this CIE are signal delivery
 | ||||||
|  |   // trampolines. Return addresses of frames in trampolines should not be
 | ||||||
|  |   // adjusted as described in section 6.4.4 of the DWARF 3 spec.
 | ||||||
|  |   DW_Z_is_signal_trampoline = 'S' | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Exception handling frame description pointer formats, as described
 | ||||||
|  | // by the Linux Standard Base Core Specification 4.0, section 11.5,
 | ||||||
|  | // DWARF Extensions.
 | ||||||
|  | enum DwarfPointerEncoding | ||||||
|  |   { | ||||||
|  |     DW_EH_PE_absptr	= 0x00, | ||||||
|  |     DW_EH_PE_omit	= 0xff, | ||||||
|  |     DW_EH_PE_uleb128    = 0x01, | ||||||
|  |     DW_EH_PE_udata2	= 0x02, | ||||||
|  |     DW_EH_PE_udata4	= 0x03, | ||||||
|  |     DW_EH_PE_udata8	= 0x04, | ||||||
|  |     DW_EH_PE_sleb128    = 0x09, | ||||||
|  |     DW_EH_PE_sdata2	= 0x0A, | ||||||
|  |     DW_EH_PE_sdata4	= 0x0B, | ||||||
|  |     DW_EH_PE_sdata8	= 0x0C, | ||||||
|  |     DW_EH_PE_pcrel	= 0x10, | ||||||
|  |     DW_EH_PE_textrel	= 0x20, | ||||||
|  |     DW_EH_PE_datarel	= 0x30, | ||||||
|  |     DW_EH_PE_funcrel	= 0x40, | ||||||
|  |     DW_EH_PE_aligned	= 0x50, | ||||||
|  | 
 | ||||||
|  |     // The GNU toolchain sources define this enum value as well,
 | ||||||
|  |     // simply to help classify the lower nybble values into signed and
 | ||||||
|  |     // unsigned groups.
 | ||||||
|  |     DW_EH_PE_signed	= 0x08, | ||||||
|  | 
 | ||||||
|  |     // This is not documented in LSB 4.0, but it is used in both the
 | ||||||
|  |     // Linux and OS X toolchains. It can be added to any other
 | ||||||
|  |     // encoding (except DW_EH_PE_aligned), and indicates that the
 | ||||||
|  |     // encoded value represents the address at which the true address
 | ||||||
|  |     // is stored, not the true address itself.
 | ||||||
|  |     DW_EH_PE_indirect	= 0x80   | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  | }  // namespace dwarf2reader
 | ||||||
|  | #endif  // COMMON_DWARF_DWARF2ENUMS_H__
 | ||||||
							
								
								
									
										2340
									
								
								google-breakpad/src/common/dwarf/dwarf2reader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2340
									
								
								google-breakpad/src/common/dwarf/dwarf2reader.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1050
									
								
								google-breakpad/src/common/dwarf/dwarf2reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1050
									
								
								google-breakpad/src/common/dwarf/dwarf2reader.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2452
									
								
								google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2452
									
								
								google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										484
									
								
								google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,484 @@ | |||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit
 | ||||||
|  | 
 | ||||||
|  | #include <stdlib.h> | ||||||
|  | 
 | ||||||
|  | #include <iostream> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "common/dwarf/bytereader-inl.h" | ||||||
|  | #include "common/dwarf/dwarf2reader_test_common.h" | ||||||
|  | #include "common/dwarf/dwarf2reader.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | #include "google_breakpad/common/breakpad_types.h" | ||||||
|  | 
 | ||||||
|  | using google_breakpad::test_assembler::Endianness; | ||||||
|  | using google_breakpad::test_assembler::Label; | ||||||
|  | using google_breakpad::test_assembler::Section; | ||||||
|  | using google_breakpad::test_assembler::kBigEndian; | ||||||
|  | using google_breakpad::test_assembler::kLittleEndian; | ||||||
|  | 
 | ||||||
|  | using dwarf2reader::ByteReader; | ||||||
|  | using dwarf2reader::CompilationUnit; | ||||||
|  | using dwarf2reader::Dwarf2Handler; | ||||||
|  | using dwarf2reader::DwarfAttribute; | ||||||
|  | using dwarf2reader::DwarfForm; | ||||||
|  | using dwarf2reader::DwarfHasChild; | ||||||
|  | using dwarf2reader::DwarfTag; | ||||||
|  | using dwarf2reader::ENDIANNESS_BIG; | ||||||
|  | using dwarf2reader::ENDIANNESS_LITTLE; | ||||||
|  | using dwarf2reader::SectionMap; | ||||||
|  | 
 | ||||||
|  | using std::vector; | ||||||
|  | using testing::InSequence; | ||||||
|  | using testing::Pointee; | ||||||
|  | using testing::Return; | ||||||
|  | using testing::Sequence; | ||||||
|  | using testing::Test; | ||||||
|  | using testing::TestWithParam; | ||||||
|  | using testing::_; | ||||||
|  | 
 | ||||||
|  | class MockDwarf2Handler: public Dwarf2Handler { | ||||||
|  |  public: | ||||||
|  |   MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size, | ||||||
|  |                                           uint8 offset_size, uint64 cu_length, | ||||||
|  |                                           uint8 dwarf_version)); | ||||||
|  |   MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag)); | ||||||
|  |   MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset, | ||||||
|  |                                               DwarfAttribute attr, | ||||||
|  |                                               enum DwarfForm form, | ||||||
|  |                                               uint64 data)); | ||||||
|  |   MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset, | ||||||
|  |                                             enum DwarfAttribute attr, | ||||||
|  |                                             enum DwarfForm form, | ||||||
|  |                                             int64 data)); | ||||||
|  |   MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset, | ||||||
|  |                                                enum DwarfAttribute attr, | ||||||
|  |                                                enum DwarfForm form, | ||||||
|  |                                                uint64 data)); | ||||||
|  |   MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset, | ||||||
|  |                                             enum DwarfAttribute attr, | ||||||
|  |                                             enum DwarfForm form, | ||||||
|  |                                             const char* data, | ||||||
|  |                                             uint64 len)); | ||||||
|  |   MOCK_METHOD4(ProcessAttributeString, void(uint64 offset, | ||||||
|  |                                             enum DwarfAttribute attr, | ||||||
|  |                                             enum DwarfForm form, | ||||||
|  |                                             const string& data)); | ||||||
|  |   MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset, | ||||||
|  |                                                DwarfAttribute attr, | ||||||
|  |                                                enum DwarfForm form, | ||||||
|  |                                                uint64 signature)); | ||||||
|  |   MOCK_METHOD1(EndDIE, void(uint64 offset)); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct DIEFixture { | ||||||
|  | 
 | ||||||
|  |   DIEFixture() { | ||||||
|  |     // Fix the initial offset of the .debug_info and .debug_abbrev sections.
 | ||||||
|  |     info.start() = 0; | ||||||
|  |     abbrevs.start() = 0; | ||||||
|  | 
 | ||||||
|  |     // Default expectations for the data handler.
 | ||||||
|  |     EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0); | ||||||
|  |     EXPECT_CALL(handler, StartDIE(_, _)).Times(0); | ||||||
|  |     EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0); | ||||||
|  |     EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0); | ||||||
|  |     EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0); | ||||||
|  |     EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0); | ||||||
|  |     EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0); | ||||||
|  |     EXPECT_CALL(handler, EndDIE(_)).Times(0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Return a reference to a section map whose .debug_info section refers
 | ||||||
|  |   // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This
 | ||||||
|  |   // function returns a reference to the same SectionMap each time; new
 | ||||||
|  |   // calls wipe out maps established by earlier calls.
 | ||||||
|  |   const SectionMap &MakeSectionMap() { | ||||||
|  |     // Copy the sections' contents into strings that will live as long as
 | ||||||
|  |     // the map itself.
 | ||||||
|  |     assert(info.GetContents(&info_contents)); | ||||||
|  |     assert(abbrevs.GetContents(&abbrevs_contents)); | ||||||
|  |     section_map.clear(); | ||||||
|  |     section_map[".debug_info"].first  = info_contents.data(); | ||||||
|  |     section_map[".debug_info"].second = info_contents.size(); | ||||||
|  |     section_map[".debug_abbrev"].first  = abbrevs_contents.data(); | ||||||
|  |     section_map[".debug_abbrev"].second = abbrevs_contents.size(); | ||||||
|  |     return section_map; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   TestCompilationUnit info; | ||||||
|  |   TestAbbrevTable abbrevs; | ||||||
|  |   MockDwarf2Handler handler; | ||||||
|  |   string abbrevs_contents, info_contents; | ||||||
|  |   SectionMap section_map; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct DwarfHeaderParams { | ||||||
|  |   DwarfHeaderParams(Endianness endianness, size_t format_size, | ||||||
|  |                    int version, size_t address_size) | ||||||
|  |       : endianness(endianness), format_size(format_size), | ||||||
|  |         version(version), address_size(address_size) { } | ||||||
|  |   Endianness endianness; | ||||||
|  |   size_t format_size;                   // 4-byte or 8-byte DWARF offsets
 | ||||||
|  |   int version; | ||||||
|  |   size_t address_size; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class DwarfHeader: public DIEFixture, | ||||||
|  |                    public TestWithParam<DwarfHeaderParams> { }; | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfHeader, Header) { | ||||||
|  |   Label abbrev_table = abbrevs.Here(); | ||||||
|  |   abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit, | ||||||
|  |                  dwarf2reader::DW_children_yes) | ||||||
|  |       .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string) | ||||||
|  |       .EndAbbrev() | ||||||
|  |       .EndTable(); | ||||||
|  | 
 | ||||||
|  |   info.set_format_size(GetParam().format_size); | ||||||
|  |   info.set_endianness(GetParam().endianness); | ||||||
|  | 
 | ||||||
|  |   info.Header(GetParam().version, abbrev_table, GetParam().address_size) | ||||||
|  |       .ULEB128(1)                     // DW_TAG_compile_unit, with children
 | ||||||
|  |       .AppendCString("sam")           // DW_AT_name, DW_FORM_string
 | ||||||
|  |       .D8(0);                         // end of children
 | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   { | ||||||
|  |     InSequence s; | ||||||
|  |     EXPECT_CALL(handler, | ||||||
|  |                 StartCompilationUnit(0, GetParam().address_size, | ||||||
|  |                                      GetParam().format_size, _, | ||||||
|  |                                      GetParam().version)) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  |     EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit)) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  |     EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,  | ||||||
|  |                                                 dwarf2reader::DW_FORM_string, | ||||||
|  |                                                 "sam")) | ||||||
|  |         .WillOnce(Return()); | ||||||
|  |     EXPECT_CALL(handler, EndDIE(_)) | ||||||
|  |         .WillOnce(Return()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   ByteReader byte_reader(GetParam().endianness == kLittleEndian ? | ||||||
|  |                          ENDIANNESS_LITTLE : ENDIANNESS_BIG); | ||||||
|  |   CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); | ||||||
|  |   EXPECT_EQ(parser.Start(), info_contents.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | INSTANTIATE_TEST_CASE_P( | ||||||
|  |     HeaderVariants, DwarfHeader, | ||||||
|  |     ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 4, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 4, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 4, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 4, 8))); | ||||||
|  | 
 | ||||||
|  | struct DwarfFormsFixture: public DIEFixture { | ||||||
|  |   // Start a compilation unit, as directed by |params|, containing one
 | ||||||
|  |   // childless DIE of the given tag, with one attribute of the given name
 | ||||||
|  |   // and form. The 'info' fixture member is left just after the abbrev
 | ||||||
|  |   // code, waiting for the attribute value to be appended.
 | ||||||
|  |   void StartSingleAttributeDIE(const DwarfHeaderParams ¶ms, | ||||||
|  |                                DwarfTag tag, DwarfAttribute name, | ||||||
|  |                                DwarfForm form) { | ||||||
|  |     // Create the abbreviation table.
 | ||||||
|  |     Label abbrev_table = abbrevs.Here(); | ||||||
|  |     abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no) | ||||||
|  |         .Attribute(name, form) | ||||||
|  |         .EndAbbrev() | ||||||
|  |         .EndTable(); | ||||||
|  | 
 | ||||||
|  |     // Create the compilation unit, up to the attribute value.
 | ||||||
|  |     info.set_format_size(params.format_size); | ||||||
|  |     info.set_endianness(params.endianness); | ||||||
|  |     info.Header(params.version, abbrev_table, params.address_size) | ||||||
|  |         .ULEB128(1);                    // abbrev code
 | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Set up handler to expect a compilation unit matching |params|,
 | ||||||
|  |   // containing one childless DIE of the given tag, in the sequence s. Stop
 | ||||||
|  |   // just before the expectations.
 | ||||||
|  |   void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, | ||||||
|  |                                   DwarfTag tag, uint64 offset=0) { | ||||||
|  |     EXPECT_CALL(handler, | ||||||
|  |                 StartCompilationUnit(offset, params.address_size, | ||||||
|  |                                      params.format_size, _, | ||||||
|  |                                      params.version)) | ||||||
|  |         .InSequence(s) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  |     EXPECT_CALL(handler, StartDIE(_, tag)) | ||||||
|  |         .InSequence(s) | ||||||
|  |         .WillOnce(Return(true)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void ExpectEndCompilationUnit() { | ||||||
|  |     EXPECT_CALL(handler, EndDIE(_)) | ||||||
|  |         .InSequence(s) | ||||||
|  |         .WillOnce(Return()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) { | ||||||
|  |     ByteReader byte_reader(params.endianness == kLittleEndian ? | ||||||
|  |                            ENDIANNESS_LITTLE : ENDIANNESS_BIG); | ||||||
|  |     CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler); | ||||||
|  |     EXPECT_EQ(offset + parser.Start(), info_contents.size()); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // The sequence to which the fixture's methods append expectations.
 | ||||||
|  |   Sequence s; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct DwarfForms: public DwarfFormsFixture, | ||||||
|  |                    public TestWithParam<DwarfHeaderParams> { }; | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfForms, addr) { | ||||||
|  |   StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit, | ||||||
|  |                           dwarf2reader::DW_AT_low_pc, | ||||||
|  |                           dwarf2reader::DW_FORM_addr); | ||||||
|  |   uint64_t value; | ||||||
|  |   if (GetParam().address_size == 4) { | ||||||
|  |     value = 0xc8e9ffcc; | ||||||
|  |     info.D32(value); | ||||||
|  |   } else { | ||||||
|  |     value = 0xe942517fc2768564ULL; | ||||||
|  |     info.D64(value); | ||||||
|  |   } | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit); | ||||||
|  |   EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc,  | ||||||
|  |                                                 dwarf2reader::DW_FORM_addr, | ||||||
|  |                                                 value)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfForms, block2_empty) { | ||||||
|  |   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, | ||||||
|  |                           (DwarfAttribute) 0xe52c4463, | ||||||
|  |                           dwarf2reader::DW_FORM_block2); | ||||||
|  |   info.D16(0); | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); | ||||||
|  |   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, | ||||||
|  |                                               dwarf2reader::DW_FORM_block2, | ||||||
|  |                                               _, 0)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfForms, block2) { | ||||||
|  |   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, | ||||||
|  |                           (DwarfAttribute) 0xe52c4463, | ||||||
|  |                           dwarf2reader::DW_FORM_block2); | ||||||
|  |   unsigned char data[258]; | ||||||
|  |   memset(data, '*', sizeof(data)); | ||||||
|  |   info.D16(sizeof(data)) | ||||||
|  |       .Append(data, sizeof(data)); | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); | ||||||
|  |   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, | ||||||
|  |                                               dwarf2reader::DW_FORM_block2, | ||||||
|  |                                               Pointee('*'), 258)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfForms, flag_present) { | ||||||
|  |   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2, | ||||||
|  |                           (DwarfAttribute) 0x359d1972, | ||||||
|  |                           dwarf2reader::DW_FORM_flag_present); | ||||||
|  |   // DW_FORM_flag_present occupies no space in the DIE.
 | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2); | ||||||
|  |   EXPECT_CALL(handler, | ||||||
|  |               ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972, | ||||||
|  |                                        dwarf2reader::DW_FORM_flag_present, | ||||||
|  |                                        1)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfForms, sec_offset) { | ||||||
|  |   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689, | ||||||
|  |                           (DwarfAttribute) 0xa060bfd1, | ||||||
|  |                           dwarf2reader::DW_FORM_sec_offset); | ||||||
|  |   uint64_t value; | ||||||
|  |   if (GetParam().format_size == 4) { | ||||||
|  |     value = 0xacc9c388; | ||||||
|  |     info.D32(value); | ||||||
|  |   } else { | ||||||
|  |     value = 0xcffe5696ffe3ed0aULL; | ||||||
|  |     info.D64(value); | ||||||
|  |   } | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689); | ||||||
|  |   EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1, | ||||||
|  |                                                 dwarf2reader::DW_FORM_sec_offset, | ||||||
|  |                                                 value)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfForms, exprloc) { | ||||||
|  |   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb, | ||||||
|  |                           (DwarfAttribute) 0xba3ae5cb, | ||||||
|  |                           dwarf2reader::DW_FORM_exprloc); | ||||||
|  |   info.ULEB128(29) | ||||||
|  |       .Append(29, 173); | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb); | ||||||
|  |   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb, | ||||||
|  |                                               dwarf2reader::DW_FORM_exprloc, | ||||||
|  |                                               Pointee(173), 29)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_P(DwarfForms, ref_sig8) { | ||||||
|  |   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, | ||||||
|  |                           (DwarfAttribute) 0xd708d908, | ||||||
|  |                           dwarf2reader::DW_FORM_ref_sig8); | ||||||
|  |   info.D64(0xf72fa0cb6ddcf9d6ULL); | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); | ||||||
|  |   EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, | ||||||
|  |                                                  dwarf2reader::DW_FORM_ref_sig8, | ||||||
|  |                                                  0xf72fa0cb6ddcf9d6ULL)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // A value passed to ProcessAttributeSignature is just an absolute number,
 | ||||||
|  | // not an offset within the compilation unit as most of the other
 | ||||||
|  | // DW_FORM_ref forms are. Check that the reader doesn't try to apply any
 | ||||||
|  | // offset to the signature, by reading it from a compilation unit that does
 | ||||||
|  | // not start at the beginning of the section.
 | ||||||
|  | TEST_P(DwarfForms, ref_sig8_not_first) { | ||||||
|  |   info.Append(98, '*'); | ||||||
|  |   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, | ||||||
|  |                           (DwarfAttribute) 0xd708d908, | ||||||
|  |                           dwarf2reader::DW_FORM_ref_sig8); | ||||||
|  |   info.D64(0xf72fa0cb6ddcf9d6ULL); | ||||||
|  |   info.Finish(); | ||||||
|  | 
 | ||||||
|  |   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98); | ||||||
|  |   EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, | ||||||
|  |                                                  dwarf2reader::DW_FORM_ref_sig8, | ||||||
|  |                                                  0xf72fa0cb6ddcf9d6ULL)) | ||||||
|  |       .InSequence(s) | ||||||
|  |       .WillOnce(Return()); | ||||||
|  |   ExpectEndCompilationUnit(); | ||||||
|  | 
 | ||||||
|  |   ParseCompilationUnit(GetParam(), 98); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Tests for the other attribute forms could go here.
 | ||||||
|  | 
 | ||||||
|  | INSTANTIATE_TEST_CASE_P( | ||||||
|  |     HeaderVariants, DwarfForms, | ||||||
|  |     ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 4, 4, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kLittleEndian, 8, 4, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    4, 4, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 2, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 2, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 3, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 3, 8), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 4, 4), | ||||||
|  |                       DwarfHeaderParams(kBigEndian,    8, 4, 8))); | ||||||
							
								
								
									
										149
									
								
								google-breakpad/src/common/dwarf/dwarf2reader_test_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								google-breakpad/src/common/dwarf/dwarf2reader_test_common.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,149 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2012, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf2reader_test_common.h: Define TestCompilationUnit and
 | ||||||
|  | // TestAbbrevTable, classes for creating properly (and improperly)
 | ||||||
|  | // formatted DWARF compilation unit data for unit tests.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ | ||||||
|  | #define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ | ||||||
|  | 
 | ||||||
|  | #include "common/test_assembler.h" | ||||||
|  | #include "common/dwarf/dwarf2enums.h" | ||||||
|  | 
 | ||||||
|  | // A subclass of test_assembler::Section, specialized for constructing
 | ||||||
|  | // DWARF compilation units.
 | ||||||
|  | class TestCompilationUnit: public google_breakpad::test_assembler::Section { | ||||||
|  |  public: | ||||||
|  |   typedef dwarf2reader::DwarfTag DwarfTag; | ||||||
|  |   typedef dwarf2reader::DwarfAttribute DwarfAttribute; | ||||||
|  |   typedef dwarf2reader::DwarfForm DwarfForm; | ||||||
|  |   typedef google_breakpad::test_assembler::Label Label; | ||||||
|  | 
 | ||||||
|  |   // Set the section's DWARF format size (the 32-bit DWARF format or the
 | ||||||
|  |   // 64-bit DWARF format, for lengths and section offsets --- not the
 | ||||||
|  |   // address size) to format_size.
 | ||||||
|  |   void set_format_size(size_t format_size) { | ||||||
|  |     assert(format_size == 4 || format_size == 8); | ||||||
|  |     format_size_ = format_size; | ||||||
|  |   } | ||||||
|  |      | ||||||
|  |   // Append a DWARF section offset value, of the appropriate size for this
 | ||||||
|  |   // compilation unit.
 | ||||||
|  |   template<typename T> | ||||||
|  |   void SectionOffset(T offset) { | ||||||
|  |     if (format_size_ == 4) | ||||||
|  |       D32(offset); | ||||||
|  |     else | ||||||
|  |       D64(offset); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Append a DWARF compilation unit header to the section, with the given
 | ||||||
|  |   // DWARF version, abbrev table offset, and address size.
 | ||||||
|  |   TestCompilationUnit &Header(int version, const Label &abbrev_offset, | ||||||
|  |                               size_t address_size) { | ||||||
|  |     if (format_size_ == 4) { | ||||||
|  |       D32(length_); | ||||||
|  |     } else { | ||||||
|  |       D32(0xffffffff); | ||||||
|  |       D64(length_); | ||||||
|  |     } | ||||||
|  |     post_length_offset_ = Size(); | ||||||
|  |     D16(version); | ||||||
|  |     SectionOffset(abbrev_offset); | ||||||
|  |     D8(address_size); | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Mark the end of this header's DIEs.
 | ||||||
|  |   TestCompilationUnit &Finish() { | ||||||
|  |     length_ = Size() - post_length_offset_; | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // The DWARF format size for this compilation unit.
 | ||||||
|  |   size_t format_size_; | ||||||
|  | 
 | ||||||
|  |   // The offset of the point in the compilation unit header immediately
 | ||||||
|  |   // after the initial length field.
 | ||||||
|  |   uint64_t post_length_offset_; | ||||||
|  | 
 | ||||||
|  |   // The length of the compilation unit, not including the initial length field.
 | ||||||
|  |   Label length_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // A subclass of test_assembler::Section specialized for constructing DWARF
 | ||||||
|  | // abbreviation tables.
 | ||||||
|  | class TestAbbrevTable: public google_breakpad::test_assembler::Section { | ||||||
|  |  public: | ||||||
|  |   typedef dwarf2reader::DwarfTag DwarfTag; | ||||||
|  |   typedef dwarf2reader::DwarfAttribute DwarfAttribute; | ||||||
|  |   typedef dwarf2reader::DwarfForm DwarfForm; | ||||||
|  |   typedef dwarf2reader::DwarfHasChild DwarfHasChild; | ||||||
|  |   typedef google_breakpad::test_assembler::Label Label; | ||||||
|  | 
 | ||||||
|  |   // Start a new abbreviation table entry for abbreviation code |code|,
 | ||||||
|  |   // encoding a DIE whose tag is |tag|, and which has children if and only
 | ||||||
|  |   // if |has_children| is true.
 | ||||||
|  |   TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) { | ||||||
|  |     assert(code != 0); | ||||||
|  |     ULEB128(code); | ||||||
|  |     ULEB128(static_cast<unsigned>(tag)); | ||||||
|  |     D8(static_cast<unsigned>(has_children)); | ||||||
|  |     return *this; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Add an attribute to the current abbreviation code whose name is |name|
 | ||||||
|  |   // and whose form is |form|.
 | ||||||
|  |   TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) { | ||||||
|  |     ULEB128(static_cast<unsigned>(name)); | ||||||
|  |     ULEB128(static_cast<unsigned>(form)); | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Finish the current abbreviation code.
 | ||||||
|  |   TestAbbrevTable &EndAbbrev() { | ||||||
|  |     ULEB128(0); | ||||||
|  |     ULEB128(0); | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Finish the current abbreviation table.
 | ||||||
|  |   TestAbbrevTable &EndTable() { | ||||||
|  |     ULEB128(0); | ||||||
|  |     return *this; | ||||||
|  |   } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | #endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__
 | ||||||
							
								
								
									
										231
									
								
								google-breakpad/src/common/dwarf/functioninfo.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								google-breakpad/src/common/dwarf/functioninfo.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,231 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // This is a client for the dwarf2reader to extract function and line
 | ||||||
|  | // information from the debug info.
 | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | #include <queue> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/functioninfo.h" | ||||||
|  | #include "common/dwarf/bytereader.h" | ||||||
|  | #include "common/scoped_ptr.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | using google_breakpad::scoped_ptr; | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files, | ||||||
|  |                                      std::vector<string>* dirs, | ||||||
|  |                                      LineMap* linemap):linemap_(linemap), | ||||||
|  |                                                        files_(files), | ||||||
|  |                                                        dirs_(dirs) { | ||||||
|  |   // The dirs and files are 1 indexed, so just make sure we put
 | ||||||
|  |   // nothing in the 0 vector.
 | ||||||
|  |   assert(dirs->size() == 0); | ||||||
|  |   assert(files->size() == 0); | ||||||
|  |   dirs->push_back(""); | ||||||
|  |   SourceFileInfo s; | ||||||
|  |   s.name = ""; | ||||||
|  |   s.lowpc = ULLONG_MAX; | ||||||
|  |   files->push_back(s); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) { | ||||||
|  |   // These should never come out of order, actually
 | ||||||
|  |   assert(dir_num == dirs_->size()); | ||||||
|  |   dirs_->push_back(name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CULineInfoHandler::DefineFile(const string& name, | ||||||
|  |                                    int32 file_num, uint32 dir_num, | ||||||
|  |                                    uint64 mod_time, uint64 length) { | ||||||
|  |   assert(dir_num >= 0); | ||||||
|  |   assert(dir_num < dirs_->size()); | ||||||
|  | 
 | ||||||
|  |   // These should never come out of order, actually.
 | ||||||
|  |   if (file_num == (int32)files_->size() || file_num == -1) { | ||||||
|  |     string dir = dirs_->at(dir_num); | ||||||
|  | 
 | ||||||
|  |     SourceFileInfo s; | ||||||
|  |     s.lowpc = ULLONG_MAX; | ||||||
|  | 
 | ||||||
|  |     if (dir == "") { | ||||||
|  |       s.name = name; | ||||||
|  |     } else { | ||||||
|  |       s.name = dir + "/" + name; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     files_->push_back(s); | ||||||
|  |   } else { | ||||||
|  |     fprintf(stderr, "error in DefineFile"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num, | ||||||
|  |                                 uint32 line_num, uint32 column_num) { | ||||||
|  |   if (file_num < files_->size()) { | ||||||
|  |     linemap_->insert( | ||||||
|  |         std::make_pair(address, | ||||||
|  |                        std::make_pair(files_->at(file_num).name.c_str(), | ||||||
|  |                                       line_num))); | ||||||
|  | 
 | ||||||
|  |     if (address < files_->at(file_num).lowpc) { | ||||||
|  |       files_->at(file_num).lowpc = address; | ||||||
|  |     } | ||||||
|  |   } else { | ||||||
|  |     fprintf(stderr, "error in AddLine"); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset, | ||||||
|  |                                                  uint8 address_size, | ||||||
|  |                                                  uint8 offset_size, | ||||||
|  |                                                  uint64 cu_length, | ||||||
|  |                                                  uint8 dwarf_version) { | ||||||
|  |   current_compilation_unit_offset_ = offset; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // For function info, we only care about subprograms and inlined
 | ||||||
|  | // subroutines. For line info, the DW_AT_stmt_list lives in the
 | ||||||
|  | // compile unit tag.
 | ||||||
|  | 
 | ||||||
|  | bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) { | ||||||
|  |   switch (tag) { | ||||||
|  |     case DW_TAG_subprogram: | ||||||
|  |     case DW_TAG_inlined_subroutine: { | ||||||
|  |       current_function_info_ = new FunctionInfo; | ||||||
|  |       current_function_info_->lowpc = current_function_info_->highpc = 0; | ||||||
|  |       current_function_info_->name = ""; | ||||||
|  |       current_function_info_->line = 0; | ||||||
|  |       current_function_info_->file = ""; | ||||||
|  |       offset_to_funcinfo_->insert(std::make_pair(offset, | ||||||
|  |                                                  current_function_info_)); | ||||||
|  |     }; | ||||||
|  |       // FALLTHROUGH
 | ||||||
|  |     case DW_TAG_compile_unit: | ||||||
|  |       return true; | ||||||
|  |     default: | ||||||
|  |       return false; | ||||||
|  |   } | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Only care about the name attribute for functions
 | ||||||
|  | 
 | ||||||
|  | void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, | ||||||
|  |                                                    enum DwarfAttribute attr, | ||||||
|  |                                                    enum DwarfForm form, | ||||||
|  |                                                    const string &data) { | ||||||
|  |   if (current_function_info_) { | ||||||
|  |     if (attr == DW_AT_name) | ||||||
|  |       current_function_info_->name = data; | ||||||
|  |     else if (attr == DW_AT_MIPS_linkage_name) | ||||||
|  |       current_function_info_->mangled_name = data; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, | ||||||
|  |                                                      enum DwarfAttribute attr, | ||||||
|  |                                                      enum DwarfForm form, | ||||||
|  |                                                      uint64 data) { | ||||||
|  |   if (attr == DW_AT_stmt_list) { | ||||||
|  |     SectionMap::const_iterator iter = sections_.find("__debug_line"); | ||||||
|  |     assert(iter != sections_.end()); | ||||||
|  | 
 | ||||||
|  |     scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data, | ||||||
|  |                                                iter->second.second  - data, | ||||||
|  |                                                reader_, linehandler_)); | ||||||
|  |     lireader->Start(); | ||||||
|  |   } else if (current_function_info_) { | ||||||
|  |     switch (attr) { | ||||||
|  |       case DW_AT_low_pc: | ||||||
|  |         current_function_info_->lowpc = data; | ||||||
|  |         break; | ||||||
|  |       case DW_AT_high_pc: | ||||||
|  |         current_function_info_->highpc = data; | ||||||
|  |         break; | ||||||
|  |       case DW_AT_decl_line: | ||||||
|  |         current_function_info_->line = data; | ||||||
|  |         break; | ||||||
|  |       case DW_AT_decl_file: | ||||||
|  |         current_function_info_->file = files_->at(data).name; | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset, | ||||||
|  |                                                       enum DwarfAttribute attr, | ||||||
|  |                                                       enum DwarfForm form, | ||||||
|  |                                                       uint64 data) { | ||||||
|  |   if (current_function_info_) { | ||||||
|  |     switch (attr) { | ||||||
|  |       case DW_AT_specification: { | ||||||
|  |         // Some functions have a "specification" attribute
 | ||||||
|  |         // which means they were defined elsewhere. The name
 | ||||||
|  |         // attribute is not repeated, and must be taken from
 | ||||||
|  |         // the specification DIE. Here we'll assume that
 | ||||||
|  |         // any DIE referenced in this manner will already have
 | ||||||
|  |         // been seen, but that's not really required by the spec.
 | ||||||
|  |         FunctionMap::iterator iter = offset_to_funcinfo_->find(data); | ||||||
|  |         if (iter != offset_to_funcinfo_->end()) { | ||||||
|  |           current_function_info_->name = iter->second->name; | ||||||
|  |           current_function_info_->mangled_name = iter->second->mangled_name; | ||||||
|  |         } else { | ||||||
|  |           // If you hit this, this code probably needs to be rewritten.
 | ||||||
|  |           fprintf(stderr, | ||||||
|  |                   "Error: DW_AT_specification was seen before the referenced " | ||||||
|  |                   "DIE! (Looking for DIE at offset %08llx, in DIE at " | ||||||
|  |                   "offset %08llx)\n", data, offset); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void CUFunctionInfoHandler::EndDIE(uint64 offset) { | ||||||
|  |   if (current_function_info_ && current_function_info_->lowpc) | ||||||
|  |     address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc, | ||||||
|  |                                                 current_function_info_)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | }  // namespace dwarf2reader
 | ||||||
							
								
								
									
										188
									
								
								google-breakpad/src/common/dwarf/functioninfo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								google-breakpad/src/common/dwarf/functioninfo.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // This file contains the definitions for a DWARF2/3 information
 | ||||||
|  | // collector that uses the DWARF2/3 reader interface to build a mapping
 | ||||||
|  | // of addresses to files, lines, and functions.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_DWARF_FUNCTIONINFO_H__ | ||||||
|  | #define COMMON_DWARF_FUNCTIONINFO_H__ | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | #include <string> | ||||||
|  | #include <utility> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf/dwarf2reader.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | struct FunctionInfo { | ||||||
|  |   // Name of the function
 | ||||||
|  |   string name; | ||||||
|  |   // Mangled name of the function
 | ||||||
|  |   string mangled_name; | ||||||
|  |   // File containing this function
 | ||||||
|  |   string file; | ||||||
|  |   // Line number for start of function.
 | ||||||
|  |   uint32 line; | ||||||
|  |   // Beginning address for this function
 | ||||||
|  |   uint64 lowpc; | ||||||
|  |   // End address for this function.
 | ||||||
|  |   uint64 highpc; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct SourceFileInfo { | ||||||
|  |   // Name of the source file name
 | ||||||
|  |   string name; | ||||||
|  |   // Low address of source file name
 | ||||||
|  |   uint64 lowpc; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef std::map<uint64, FunctionInfo*> FunctionMap; | ||||||
|  | typedef std::map<uint64, std::pair<string, uint32> > LineMap; | ||||||
|  | 
 | ||||||
|  | // This class is a basic line info handler that fills in the dirs,
 | ||||||
|  | // file, and linemap passed into it with the data produced from the
 | ||||||
|  | // LineInfoHandler.
 | ||||||
|  | class CULineInfoHandler: public LineInfoHandler { | ||||||
|  |  public: | ||||||
|  | 
 | ||||||
|  |   //
 | ||||||
|  |   CULineInfoHandler(std::vector<SourceFileInfo>* files, | ||||||
|  |                     std::vector<string>* dirs, | ||||||
|  |                     LineMap* linemap); | ||||||
|  |   virtual ~CULineInfoHandler() { } | ||||||
|  | 
 | ||||||
|  |   // Called when we define a directory.  We just place NAME into dirs_
 | ||||||
|  |   // at position DIR_NUM.
 | ||||||
|  |   virtual void DefineDir(const string& name, uint32 dir_num); | ||||||
|  | 
 | ||||||
|  |   // Called when we define a filename.  We just place
 | ||||||
|  |   // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM.
 | ||||||
|  |   virtual void DefineFile(const string& name, int32 file_num, | ||||||
|  |                           uint32 dir_num, uint64 mod_time, uint64 length); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |   // Called when the line info reader has a new line, address pair
 | ||||||
|  |   // ready for us. ADDRESS is the address of the code, LENGTH is the
 | ||||||
|  |   // length of its machine code in bytes, FILE_NUM is the file number
 | ||||||
|  |   // containing the code, LINE_NUM is the line number in that file for
 | ||||||
|  |   // the code, and COLUMN_NUM is the column number the code starts at,
 | ||||||
|  |   // if we know it (0 otherwise).
 | ||||||
|  |   virtual void AddLine(uint64 address, uint64 length, | ||||||
|  |                        uint32 file_num, uint32 line_num, uint32 column_num); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   LineMap* linemap_; | ||||||
|  |   std::vector<SourceFileInfo>* files_; | ||||||
|  |   std::vector<string>* dirs_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class CUFunctionInfoHandler: public Dwarf2Handler { | ||||||
|  |  public: | ||||||
|  |   CUFunctionInfoHandler(std::vector<SourceFileInfo>* files, | ||||||
|  |                         std::vector<string>* dirs, | ||||||
|  |                         LineMap* linemap, | ||||||
|  |                         FunctionMap* offset_to_funcinfo, | ||||||
|  |                         FunctionMap* address_to_funcinfo, | ||||||
|  |                         CULineInfoHandler* linehandler, | ||||||
|  |                         const SectionMap& sections, | ||||||
|  |                         ByteReader* reader) | ||||||
|  |       : files_(files), dirs_(dirs), linemap_(linemap), | ||||||
|  |         offset_to_funcinfo_(offset_to_funcinfo), | ||||||
|  |         address_to_funcinfo_(address_to_funcinfo), | ||||||
|  |         linehandler_(linehandler), sections_(sections), | ||||||
|  |         reader_(reader), current_function_info_(NULL) { } | ||||||
|  | 
 | ||||||
|  |   virtual ~CUFunctionInfoHandler() { } | ||||||
|  | 
 | ||||||
|  |   // Start to process a compilation unit at OFFSET from the beginning of the
 | ||||||
|  |   // .debug_info section.  We want to see all compilation units, so we
 | ||||||
|  |   // always return true.
 | ||||||
|  | 
 | ||||||
|  |   virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||||
|  |                                     uint8 offset_size, uint64 cu_length, | ||||||
|  |                                     uint8 dwarf_version); | ||||||
|  | 
 | ||||||
|  |   // Start to process a DIE at OFFSET from the beginning of the
 | ||||||
|  |   // .debug_info section.  We only care about function related DIE's.
 | ||||||
|  |   virtual bool StartDIE(uint64 offset, enum DwarfTag tag); | ||||||
|  | 
 | ||||||
|  |   // Called when we have an attribute with unsigned data to give to
 | ||||||
|  |   // our handler.  The attribute is for the DIE at OFFSET from the
 | ||||||
|  |   // beginning of the .debug_info section, has a name of ATTR, a form of
 | ||||||
|  |   // FORM, and the actual data of the attribute is in DATA.
 | ||||||
|  |   virtual void ProcessAttributeUnsigned(uint64 offset, | ||||||
|  |                                         enum DwarfAttribute attr, | ||||||
|  |                                         enum DwarfForm form, | ||||||
|  |                                         uint64 data); | ||||||
|  | 
 | ||||||
|  |   // Called when we have an attribute with a DIE reference to give to
 | ||||||
|  |   // our handler.  The attribute is for the DIE at OFFSET from the
 | ||||||
|  |   // beginning of the .debug_info section, has a name of ATTR, a form of
 | ||||||
|  |   // FORM, and the offset of the referenced DIE from the start of the
 | ||||||
|  |   // .debug_info section is in DATA.
 | ||||||
|  |   virtual void ProcessAttributeReference(uint64 offset, | ||||||
|  |                                          enum DwarfAttribute attr, | ||||||
|  |                                          enum DwarfForm form, | ||||||
|  |                                          uint64 data); | ||||||
|  | 
 | ||||||
|  |   // Called when we have an attribute with string data to give to
 | ||||||
|  |   // our handler.  The attribute is for the DIE at OFFSET from the
 | ||||||
|  |   // beginning of the .debug_info section, has a name of ATTR, a form of
 | ||||||
|  |   // FORM, and the actual data of the attribute is in DATA.
 | ||||||
|  |   virtual void ProcessAttributeString(uint64 offset, | ||||||
|  |                                       enum DwarfAttribute attr, | ||||||
|  |                                       enum DwarfForm form, | ||||||
|  |                                       const string& data); | ||||||
|  | 
 | ||||||
|  |   // Called when finished processing the DIE at OFFSET.
 | ||||||
|  |   // Because DWARF2/3 specifies a tree of DIEs, you may get starts
 | ||||||
|  |   // before ends of the previous DIE, as we process children before
 | ||||||
|  |   // ending the parent.
 | ||||||
|  |   virtual void EndDIE(uint64 offset); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   std::vector<SourceFileInfo>* files_; | ||||||
|  |   std::vector<string>* dirs_; | ||||||
|  |   LineMap* linemap_; | ||||||
|  |   FunctionMap* offset_to_funcinfo_; | ||||||
|  |   FunctionMap* address_to_funcinfo_; | ||||||
|  |   CULineInfoHandler* linehandler_; | ||||||
|  |   const SectionMap& sections_; | ||||||
|  |   ByteReader* reader_; | ||||||
|  |   FunctionInfo* current_function_info_; | ||||||
|  |   uint64 current_compilation_unit_offset_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace dwarf2reader
 | ||||||
|  | #endif  // COMMON_DWARF_FUNCTIONINFO_H__
 | ||||||
							
								
								
									
										61
									
								
								google-breakpad/src/common/dwarf/line_state_machine.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								google-breakpad/src/common/dwarf/line_state_machine.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | |||||||
|  | // Copyright 2008 Google Inc. All Rights Reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_DWARF_LINE_STATE_MACHINE_H__ | ||||||
|  | #define COMMON_DWARF_LINE_STATE_MACHINE_H__ | ||||||
|  | 
 | ||||||
|  | namespace dwarf2reader { | ||||||
|  | 
 | ||||||
|  | // This is the format of a DWARF2/3 line state machine that we process
 | ||||||
|  | // opcodes using.  There is no need for anything outside the lineinfo
 | ||||||
|  | // processor to know how this works.
 | ||||||
|  | struct LineStateMachine { | ||||||
|  |   void Reset(bool default_is_stmt) { | ||||||
|  |     file_num = 1; | ||||||
|  |     address = 0; | ||||||
|  |     line_num = 1; | ||||||
|  |     column_num = 0; | ||||||
|  |     is_stmt = default_is_stmt; | ||||||
|  |     basic_block = false; | ||||||
|  |     end_sequence = false; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   uint32 file_num; | ||||||
|  |   uint64 address; | ||||||
|  |   uint32 line_num; | ||||||
|  |   uint32 column_num; | ||||||
|  |   bool is_stmt;  // stmt means statement.
 | ||||||
|  |   bool basic_block; | ||||||
|  |   bool end_sequence; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace dwarf2reader
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #endif  // COMMON_DWARF_LINE_STATE_MACHINE_H__
 | ||||||
							
								
								
									
										55
									
								
								google-breakpad/src/common/dwarf/types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								google-breakpad/src/common/dwarf/types.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | // Copyright 2008 Google, Inc.  All Rights reserved
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | // This file contains some typedefs for basic types
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #ifndef _COMMON_DWARF_TYPES_H__ | ||||||
|  | #define _COMMON_DWARF_TYPES_H__ | ||||||
|  | 
 | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | typedef signed char         int8; | ||||||
|  | typedef short               int16; | ||||||
|  | typedef int                 int32; | ||||||
|  | typedef long long           int64; | ||||||
|  | 
 | ||||||
|  | typedef unsigned char      uint8; | ||||||
|  | typedef unsigned short     uint16; | ||||||
|  | typedef unsigned int       uint32; | ||||||
|  | typedef unsigned long long uint64; | ||||||
|  | 
 | ||||||
|  | #ifdef __PTRDIFF_TYPE__ | ||||||
|  | typedef          __PTRDIFF_TYPE__ intptr; | ||||||
|  | typedef unsigned __PTRDIFF_TYPE__ uintptr; | ||||||
|  | #else | ||||||
|  | #error "Can't find pointer-sized integral types." | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | #endif // _COMMON_DWARF_TYPES_H__
 | ||||||
							
								
								
									
										258
									
								
								google-breakpad/src/common/dwarf_cfi_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								google-breakpad/src/common/dwarf_cfi_to_module.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,258 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // Implementation of google_breakpad::DwarfCFIToModule.
 | ||||||
|  | // See dwarf_cfi_to_module.h for details.
 | ||||||
|  | 
 | ||||||
|  | #include <sstream> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf_cfi_to_module.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | using std::ostringstream; | ||||||
|  | 
 | ||||||
|  | vector<string> DwarfCFIToModule::RegisterNames::MakeVector( | ||||||
|  |     const char * const *strings, | ||||||
|  |     size_t size) { | ||||||
|  |   vector<string> names(strings, strings + size); | ||||||
|  |   return names; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vector<string> DwarfCFIToModule::RegisterNames::I386() { | ||||||
|  |   static const char *const names[] = { | ||||||
|  |     "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", | ||||||
|  |     "$eip", "$eflags", "$unused1", | ||||||
|  |     "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", | ||||||
|  |     "$unused2", "$unused3", | ||||||
|  |     "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", | ||||||
|  |     "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", | ||||||
|  |     "$fcw", "$fsw", "$mxcsr", | ||||||
|  |     "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", | ||||||
|  |     "$tr", "$ldtr" | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return MakeVector(names, sizeof(names) / sizeof(names[0])); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vector<string> DwarfCFIToModule::RegisterNames::X86_64() { | ||||||
|  |   static const char *const names[] = { | ||||||
|  |     "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", | ||||||
|  |     "$r8",  "$r9",  "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", | ||||||
|  |     "$rip", | ||||||
|  |     "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", | ||||||
|  |     "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", | ||||||
|  |     "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", | ||||||
|  |     "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", | ||||||
|  |     "$rflags", | ||||||
|  |     "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", | ||||||
|  |     "$fs.base", "$gs.base", "$unused3", "$unused4", | ||||||
|  |     "$tr", "$ldtr", | ||||||
|  |     "$mxcsr", "$fcw", "$fsw" | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return MakeVector(names, sizeof(names) / sizeof(names[0])); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Per ARM IHI 0040A, section 3.1
 | ||||||
|  | vector<string> DwarfCFIToModule::RegisterNames::ARM() { | ||||||
|  |   static const char *const names[] = { | ||||||
|  |     "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7", | ||||||
|  |     "r8",  "r9",  "r10", "r11", "r12", "sp",  "lr",  "pc", | ||||||
|  |     "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7", | ||||||
|  |     "fps", "cpsr", "",   "",    "",    "",    "",    "", | ||||||
|  |     "",    "",    "",    "",    "",    "",    "",    "", | ||||||
|  |     "",    "",    "",    "",    "",    "",    "",    "", | ||||||
|  |     "",    "",    "",    "",    "",    "",    "",    "", | ||||||
|  |     "",    "",    "",    "",    "",    "",    "",    "", | ||||||
|  |     "s0",  "s1",  "s2",  "s3",  "s4",  "s5",  "s6",  "s7", | ||||||
|  |     "s8",  "s9",  "s10", "s11", "s12", "s13", "s14", "s15", | ||||||
|  |     "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", | ||||||
|  |     "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", | ||||||
|  |     "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7" | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   return MakeVector(names, sizeof(names) / sizeof(names[0])); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length, | ||||||
|  |                              uint8 version, const string &augmentation, | ||||||
|  |                              unsigned return_address) { | ||||||
|  |   assert(!entry_); | ||||||
|  | 
 | ||||||
|  |   // If dwarf2reader::CallFrameInfo can handle this version and
 | ||||||
|  |   // augmentation, then we should be okay with that, so there's no
 | ||||||
|  |   // need to check them here.
 | ||||||
|  | 
 | ||||||
|  |   // Get ready to collect entries.
 | ||||||
|  |   entry_ = new Module::StackFrameEntry; | ||||||
|  |   entry_->address = address; | ||||||
|  |   entry_->size = length; | ||||||
|  |   entry_offset_ = offset; | ||||||
|  |   return_address_ = return_address; | ||||||
|  | 
 | ||||||
|  |   // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI
 | ||||||
|  |   // may not establish any rule for .ra if the return address column
 | ||||||
|  |   // is an ordinary register, and that register holds the return
 | ||||||
|  |   // address on entry to the function. So establish an initial .ra
 | ||||||
|  |   // rule citing the return address register.
 | ||||||
|  |   if (return_address_ < register_names_.size()) | ||||||
|  |     entry_->initial_rules[ra_name_] = register_names_[return_address_]; | ||||||
|  | 
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string DwarfCFIToModule::RegisterName(int i) { | ||||||
|  |   assert(entry_); | ||||||
|  |   if (i < 0) { | ||||||
|  |     assert(i == kCFARegister); | ||||||
|  |     return cfa_name_; | ||||||
|  |   } | ||||||
|  |   unsigned reg = i; | ||||||
|  |   if (reg == return_address_) | ||||||
|  |     return ra_name_; | ||||||
|  | 
 | ||||||
|  |   // Ensure that a non-empty name exists for this register value.
 | ||||||
|  |   if (reg < register_names_.size() && !register_names_[reg].empty()) | ||||||
|  |     return register_names_[reg]; | ||||||
|  | 
 | ||||||
|  |   reporter_->UnnamedRegister(entry_offset_, reg); | ||||||
|  |   char buf[30]; | ||||||
|  |   sprintf(buf, "unnamed_register%u", reg); | ||||||
|  |   return buf; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DwarfCFIToModule::Record(Module::Address address, int reg, | ||||||
|  |                               const string &rule) { | ||||||
|  |   assert(entry_); | ||||||
|  | 
 | ||||||
|  |   // Place the name in our global set of strings, and then use the string
 | ||||||
|  |   // from the set. Even though the assignment looks like a copy, all the
 | ||||||
|  |   // major std::string implementations use reference counting internally,
 | ||||||
|  |   // so the effect is to have all our data structures share copies of rules
 | ||||||
|  |   // whenever possible. Since register names are drawn from a
 | ||||||
|  |   // vector<string>, register names are already shared.
 | ||||||
|  |   string shared_rule = *common_strings_.insert(rule).first; | ||||||
|  | 
 | ||||||
|  |   // Is this one of this entry's initial rules?
 | ||||||
|  |   if (address == entry_->address) | ||||||
|  |     entry_->initial_rules[RegisterName(reg)] = shared_rule; | ||||||
|  |   // File it under the appropriate address.
 | ||||||
|  |   else | ||||||
|  |     entry_->rule_changes[address][RegisterName(reg)] = shared_rule; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) { | ||||||
|  |   reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); | ||||||
|  |   // Treat this as a non-fatal error.
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) { | ||||||
|  |   ostringstream s; | ||||||
|  |   s << RegisterName(reg); | ||||||
|  |   Record(address, reg, s.str()); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::OffsetRule(uint64 address, int reg, | ||||||
|  |                                   int base_register, long offset) { | ||||||
|  |   ostringstream s; | ||||||
|  |   s << RegisterName(base_register) << " " << offset << " + ^"; | ||||||
|  |   Record(address, reg, s.str()); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg, | ||||||
|  |                                      int base_register, long offset) { | ||||||
|  |   ostringstream s; | ||||||
|  |   s << RegisterName(base_register) << " " << offset << " +"; | ||||||
|  |   Record(address, reg, s.str()); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::RegisterRule(uint64 address, int reg, | ||||||
|  |                                     int base_register) { | ||||||
|  |   ostringstream s; | ||||||
|  |   s << RegisterName(base_register); | ||||||
|  |   Record(address, reg, s.str()); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg, | ||||||
|  |                                       const string &expression) { | ||||||
|  |   reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); | ||||||
|  |   // Treat this as a non-fatal error.
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg, | ||||||
|  |                                          const string &expression) { | ||||||
|  |   reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); | ||||||
|  |   // Treat this as a non-fatal error.
 | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool DwarfCFIToModule::End() { | ||||||
|  |   module_->AddStackFrameEntry(entry_); | ||||||
|  |   entry_ = NULL; | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) { | ||||||
|  |   fprintf(stderr, "%s, section '%s': " | ||||||
|  |           "the call frame entry at offset 0x%zx refers to register %d," | ||||||
|  |           " whose name we don't know\n", | ||||||
|  |           file_.c_str(), section_.c_str(), offset, reg); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset, | ||||||
|  |                                                        const string ®) { | ||||||
|  |   fprintf(stderr, "%s, section '%s': " | ||||||
|  |           "the call frame entry at offset 0x%zx sets the rule for " | ||||||
|  |           "register '%s' to 'undefined', but the Breakpad symbol file format" | ||||||
|  |           " cannot express this\n", | ||||||
|  |           file_.c_str(), section_.c_str(), offset, reg.c_str()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset, | ||||||
|  |                                                          const string ®) { | ||||||
|  |   fprintf(stderr, "%s, section '%s': " | ||||||
|  |           "the call frame entry at offset 0x%zx uses a DWARF expression to" | ||||||
|  |           " describe how to recover register '%s', " | ||||||
|  |           " but this translator cannot yet translate DWARF expressions to" | ||||||
|  |           " Breakpad postfix expressions\n", | ||||||
|  |           file_.c_str(), section_.c_str(), offset, reg.c_str()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace google_breakpad
 | ||||||
							
								
								
									
										196
									
								
								google-breakpad/src/common/dwarf_cfi_to_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								google-breakpad/src/common/dwarf_cfi_to_module.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,196 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which
 | ||||||
|  | // accepts parsed DWARF call frame info and adds it to a
 | ||||||
|  | // google_breakpad::Module object, which can write that information to
 | ||||||
|  | // a Breakpad symbol file.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H | ||||||
|  | #define COMMON_LINUX_DWARF_CFI_TO_MODULE_H | ||||||
|  | 
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include <set> | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "common/module.h" | ||||||
|  | #include "common/dwarf/dwarf2reader.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | using dwarf2reader::CallFrameInfo; | ||||||
|  | using google_breakpad::Module; | ||||||
|  | using std::set; | ||||||
|  | using std::vector; | ||||||
|  | 
 | ||||||
|  | // A class that accepts parsed call frame information from the DWARF
 | ||||||
|  | // CFI parser and populates a google_breakpad::Module object with the
 | ||||||
|  | // contents.
 | ||||||
|  | class DwarfCFIToModule: public CallFrameInfo::Handler { | ||||||
|  |  public: | ||||||
|  | 
 | ||||||
|  |   // DwarfCFIToModule uses an instance of this class to report errors
 | ||||||
|  |   // detected while converting DWARF CFI to Breakpad STACK CFI records.
 | ||||||
|  |   class Reporter { | ||||||
|  |    public: | ||||||
|  |     // Create a reporter that writes messages to the standard error
 | ||||||
|  |     // stream. FILE is the name of the file we're processing, and
 | ||||||
|  |     // SECTION is the name of the section within that file that we're
 | ||||||
|  |     // looking at (.debug_frame, .eh_frame, etc.).
 | ||||||
|  |     Reporter(const string &file, const string §ion) | ||||||
|  |       : file_(file), section_(section) { } | ||||||
|  |     virtual ~Reporter() { } | ||||||
|  | 
 | ||||||
|  |     // The DWARF CFI entry at OFFSET cites register REG, but REG is not
 | ||||||
|  |     // covered by the vector of register names passed to the
 | ||||||
|  |     // DwarfCFIToModule constructor, nor does it match the return
 | ||||||
|  |     // address column number for this entry.
 | ||||||
|  |     virtual void UnnamedRegister(size_t offset, int reg); | ||||||
|  | 
 | ||||||
|  |     // The DWARF CFI entry at OFFSET says that REG is undefined, but the
 | ||||||
|  |     // Breakpad symbol file format cannot express this.
 | ||||||
|  |     virtual void UndefinedNotSupported(size_t offset, const string ®); | ||||||
|  | 
 | ||||||
|  |     // The DWARF CFI entry at OFFSET says that REG uses a DWARF
 | ||||||
|  |     // expression to find its value, but DwarfCFIToModule is not
 | ||||||
|  |     // capable of translating DWARF expressions to Breakpad postfix
 | ||||||
|  |     // expressions.
 | ||||||
|  |     virtual void ExpressionsNotSupported(size_t offset, const string ®); | ||||||
|  | 
 | ||||||
|  |   protected: | ||||||
|  |     string file_, section_; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Register name tables. If TABLE is a vector returned by one of these
 | ||||||
|  |   // functions, then TABLE[R] is the name of the register numbered R in
 | ||||||
|  |   // DWARF call frame information.
 | ||||||
|  |   class RegisterNames { | ||||||
|  |    public: | ||||||
|  |     // Intel's "x86" or IA-32.
 | ||||||
|  |     static vector<string> I386(); | ||||||
|  | 
 | ||||||
|  |     // AMD x86_64, AMD64, Intel EM64T, or Intel 64
 | ||||||
|  |     static vector<string> X86_64(); | ||||||
|  | 
 | ||||||
|  |     // ARM.
 | ||||||
|  |     static vector<string> ARM(); | ||||||
|  | 
 | ||||||
|  |    private: | ||||||
|  |     // Given STRINGS, an array of C strings with SIZE elements, return an
 | ||||||
|  |     // equivalent vector<string>.
 | ||||||
|  |     static vector<string> MakeVector(const char * const *strings, size_t size); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Create a handler for the dwarf2reader::CallFrameInfo parser that
 | ||||||
|  |   // records the stack unwinding information it receives in MODULE.
 | ||||||
|  |   //
 | ||||||
|  |   // Use REGISTER_NAMES[I] as the name of register number I; *this
 | ||||||
|  |   // keeps a reference to the vector, so the vector should remain
 | ||||||
|  |   // alive for as long as the DwarfCFIToModule does.
 | ||||||
|  |   //
 | ||||||
|  |   // Use REPORTER for reporting problems encountered in the conversion
 | ||||||
|  |   // process.
 | ||||||
|  |   DwarfCFIToModule(Module *module, const vector<string> ®ister_names, | ||||||
|  |                    Reporter *reporter) | ||||||
|  |       : module_(module), register_names_(register_names), reporter_(reporter), | ||||||
|  |         entry_(NULL), return_address_(-1), cfa_name_(".cfa"), ra_name_(".ra") { | ||||||
|  |   } | ||||||
|  |   virtual ~DwarfCFIToModule() { delete entry_; } | ||||||
|  | 
 | ||||||
|  |   virtual bool Entry(size_t offset, uint64 address, uint64 length, | ||||||
|  |                      uint8 version, const string &augmentation, | ||||||
|  |                      unsigned return_address); | ||||||
|  |   virtual bool UndefinedRule(uint64 address, int reg); | ||||||
|  |   virtual bool SameValueRule(uint64 address, int reg); | ||||||
|  |   virtual bool OffsetRule(uint64 address, int reg, | ||||||
|  |                           int base_register, long offset); | ||||||
|  |   virtual bool ValOffsetRule(uint64 address, int reg, | ||||||
|  |                              int base_register, long offset); | ||||||
|  |   virtual bool RegisterRule(uint64 address, int reg, int base_register); | ||||||
|  |   virtual bool ExpressionRule(uint64 address, int reg, | ||||||
|  |                               const string &expression); | ||||||
|  |   virtual bool ValExpressionRule(uint64 address, int reg, | ||||||
|  |                                  const string &expression); | ||||||
|  |   virtual bool End(); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // Return the name to use for register REG.
 | ||||||
|  |   string RegisterName(int i); | ||||||
|  | 
 | ||||||
|  |   // Record RULE for register REG at ADDRESS.
 | ||||||
|  |   void Record(Module::Address address, int reg, const string &rule); | ||||||
|  | 
 | ||||||
|  |   // The module to which we should add entries.
 | ||||||
|  |   Module *module_; | ||||||
|  | 
 | ||||||
|  |   // Map from register numbers to register names.
 | ||||||
|  |   const vector<string> ®ister_names_; | ||||||
|  | 
 | ||||||
|  |   // The reporter to use to report problems.
 | ||||||
|  |   Reporter *reporter_; | ||||||
|  | 
 | ||||||
|  |   // The current entry we're constructing.
 | ||||||
|  |   Module::StackFrameEntry *entry_; | ||||||
|  | 
 | ||||||
|  |   // The section offset of the current frame description entry, for
 | ||||||
|  |   // use in error messages.
 | ||||||
|  |   size_t entry_offset_; | ||||||
|  | 
 | ||||||
|  |   // The return address column for that entry.
 | ||||||
|  |   unsigned return_address_; | ||||||
|  | 
 | ||||||
|  |   // The names of the return address and canonical frame address. Putting
 | ||||||
|  |   // these here instead of using string literals allows us to share their
 | ||||||
|  |   // texts in reference-counted std::string implementations (all the
 | ||||||
|  |   // popular ones). Many, many rules cite these strings.
 | ||||||
|  |   string cfa_name_, ra_name_; | ||||||
|  | 
 | ||||||
|  |   // A set of strings used by this CFI. Before storing a string in one of
 | ||||||
|  |   // our data structures, insert it into this set, and then use the string
 | ||||||
|  |   // from the set.
 | ||||||
|  |   // 
 | ||||||
|  |   // Because std::string uses reference counting internally, simply using
 | ||||||
|  |   // strings from this set, even if passed by value, assigned, or held
 | ||||||
|  |   // directly in structures and containers (map<string, ...>, for example),
 | ||||||
|  |   // causes those strings to share a single instance of each distinct piece
 | ||||||
|  |   // of text.
 | ||||||
|  |   set<string> common_strings_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H
 | ||||||
							
								
								
									
										306
									
								
								google-breakpad/src/common/dwarf_cfi_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								google-breakpad/src/common/dwarf_cfi_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,306 @@ | |||||||
|  | // Copyright (c) 2010, Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule.
 | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
|  | #include "breakpad_googletest_includes.h" | ||||||
|  | #include "common/dwarf_cfi_to_module.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | using std::vector; | ||||||
|  | 
 | ||||||
|  | using google_breakpad::Module; | ||||||
|  | using google_breakpad::DwarfCFIToModule; | ||||||
|  | using testing::ContainerEq; | ||||||
|  | using testing::Test; | ||||||
|  | using testing::_; | ||||||
|  | 
 | ||||||
|  | struct MockCFIReporter: public DwarfCFIToModule::Reporter { | ||||||
|  |   MockCFIReporter(const string &file, const string §ion) | ||||||
|  |       : Reporter(file, section) { } | ||||||
|  |   MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg)); | ||||||
|  |   MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string ®)); | ||||||
|  |   MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string ®)); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct DwarfCFIToModuleFixture { | ||||||
|  |   DwarfCFIToModuleFixture() | ||||||
|  |       : module("module name", "module os", "module arch", "module id"), | ||||||
|  |         reporter("reporter file", "reporter section"), | ||||||
|  |         handler(&module, register_names, &reporter) { | ||||||
|  |     register_names.push_back("reg0"); | ||||||
|  |     register_names.push_back("reg1"); | ||||||
|  |     register_names.push_back("reg2"); | ||||||
|  |     register_names.push_back("reg3"); | ||||||
|  |     register_names.push_back("reg4"); | ||||||
|  |     register_names.push_back("reg5"); | ||||||
|  |     register_names.push_back("reg6"); | ||||||
|  |     register_names.push_back("reg7"); | ||||||
|  |     register_names.push_back("sp"); | ||||||
|  |     register_names.push_back("pc"); | ||||||
|  |     register_names.push_back(""); | ||||||
|  | 
 | ||||||
|  |     EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0); | ||||||
|  |     EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0); | ||||||
|  |     EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   Module module; | ||||||
|  |   vector<string> register_names; | ||||||
|  |   MockCFIReporter reporter; | ||||||
|  |   DwarfCFIToModule handler; | ||||||
|  |   vector<Module::StackFrameEntry *> entries; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Entry: public DwarfCFIToModuleFixture, public Test { }; | ||||||
|  | 
 | ||||||
|  | TEST_F(Entry, Accept) { | ||||||
|  |   ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL, | ||||||
|  |                             0xb440ce248169c8d6ULL, 3, "", 0xea93c106)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   module.GetStackFrameEntries(&entries); | ||||||
|  |   EXPECT_EQ(1U, entries.size()); | ||||||
|  |   EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address); | ||||||
|  |   EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Entry, AcceptOldVersion) { | ||||||
|  |   ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL, | ||||||
|  |                             0xc771f44958d40bbcULL, 1, "", 0x093c945e)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   module.GetStackFrameEntries(&entries); | ||||||
|  |   EXPECT_EQ(1U, entries.size()); | ||||||
|  |   EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address); | ||||||
|  |   EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct RuleFixture: public DwarfCFIToModuleFixture { | ||||||
|  |   RuleFixture() : DwarfCFIToModuleFixture() { | ||||||
|  |     entry_address = 0x89327ebf86b47492ULL; | ||||||
|  |     entry_size    = 0x2f8cd573072fe02aULL; | ||||||
|  |     return_reg    = 0x7886a346; | ||||||
|  |   } | ||||||
|  |   void StartEntry() { | ||||||
|  |     ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size, | ||||||
|  |                               3, "", return_reg)); | ||||||
|  |   } | ||||||
|  |   void CheckEntry() { | ||||||
|  |     module.GetStackFrameEntries(&entries); | ||||||
|  |     EXPECT_EQ(1U, entries.size()); | ||||||
|  |     EXPECT_EQ(entry_address, entries[0]->address); | ||||||
|  |     EXPECT_EQ(entry_size, entries[0]->size); | ||||||
|  |   } | ||||||
|  |   uint64 entry_address, entry_size; | ||||||
|  |   unsigned return_reg; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | class Rule: public RuleFixture, public Test { }; | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, UndefinedRule) { | ||||||
|  |   EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7")); | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.UndefinedRule(entry_address, 7)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, RegisterWithEmptyName) { | ||||||
|  |   EXPECT_CALL(reporter, UnnamedRegister(_, 10)); | ||||||
|  |   EXPECT_CALL(reporter, UndefinedNotSupported(_, "unnamed_register10")); | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.UndefinedRule(entry_address, 10)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, SameValueRule) { | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.SameValueRule(entry_address, 6)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   Module::RuleMap expected_initial; | ||||||
|  |   expected_initial["reg6"] = "reg6"; | ||||||
|  |   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, OffsetRule) { | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg, | ||||||
|  |                                  DwarfCFIToModule::kCFARegister, | ||||||
|  |                                  16927065)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   Module::RuleChangeMap expected_changes; | ||||||
|  |   expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^"; | ||||||
|  |   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, OffsetRuleNegative) { | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.OffsetRule(entry_address + 1, | ||||||
|  |                                  DwarfCFIToModule::kCFARegister, 4, -34530721)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   Module::RuleChangeMap expected_changes; | ||||||
|  |   expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^"; | ||||||
|  |   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, ValOffsetRule) { | ||||||
|  |   // Use an unnamed register number, to exercise that branch of RegisterName.
 | ||||||
|  |   EXPECT_CALL(reporter, UnnamedRegister(_, 11)); | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7, | ||||||
|  |                                     DwarfCFIToModule::kCFARegister, | ||||||
|  |                                     11, 61812979)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   Module::RuleChangeMap expected_changes; | ||||||
|  |   expected_changes[entry_address + 0x5ab7][".cfa"] = | ||||||
|  |       "unnamed_register11 61812979 +"; | ||||||
|  |   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, RegisterRule) { | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   Module::RuleMap expected_initial; | ||||||
|  |   expected_initial[".ra"] = "reg3"; | ||||||
|  |   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, ExpressionRule) { | ||||||
|  |   EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2")); | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2, | ||||||
|  |                                      "it takes two to tango")); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, ValExpressionRule) { | ||||||
|  |   EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0")); | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0, | ||||||
|  |                                         "bit off more than he could chew")); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, DefaultReturnAddressRule) { | ||||||
|  |   return_reg = 2; | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   Module::RuleMap expected_initial; | ||||||
|  |   expected_initial[".ra"] = "reg2"; | ||||||
|  |   expected_initial["reg0"] = "reg1"; | ||||||
|  |   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, DefaultReturnAddressRuleOverride) { | ||||||
|  |   return_reg = 2; | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   Module::RuleMap expected_initial; | ||||||
|  |   expected_initial[".ra"] = "reg1"; | ||||||
|  |   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||||
|  |   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST_F(Rule, DefaultReturnAddressRuleLater) { | ||||||
|  |   return_reg = 2; | ||||||
|  |   StartEntry(); | ||||||
|  |   ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1)); | ||||||
|  |   ASSERT_TRUE(handler.End()); | ||||||
|  |   CheckEntry(); | ||||||
|  |   Module::RuleMap expected_initial; | ||||||
|  |   expected_initial[".ra"] = "reg2"; | ||||||
|  |   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||||
|  |   Module::RuleChangeMap expected_changes; | ||||||
|  |   expected_changes[entry_address + 1][".ra"] = "reg1"; | ||||||
|  |   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(RegisterNames, I386) { | ||||||
|  |   vector<string> names = DwarfCFIToModule::RegisterNames::I386(); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ("$eax", names[0]); | ||||||
|  |   EXPECT_EQ("$ecx", names[1]); | ||||||
|  |   EXPECT_EQ("$esp", names[4]); | ||||||
|  |   EXPECT_EQ("$eip", names[8]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(RegisterNames, ARM) { | ||||||
|  |   vector<string> names = DwarfCFIToModule::RegisterNames::ARM(); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ("r0", names[0]); | ||||||
|  |   EXPECT_EQ("r10", names[10]); | ||||||
|  |   EXPECT_EQ("sp", names[13]); | ||||||
|  |   EXPECT_EQ("lr", names[14]); | ||||||
|  |   EXPECT_EQ("pc", names[15]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | TEST(RegisterNames, X86_64) { | ||||||
|  |   vector<string> names = DwarfCFIToModule::RegisterNames::X86_64(); | ||||||
|  | 
 | ||||||
|  |   EXPECT_EQ("$rax", names[0]); | ||||||
|  |   EXPECT_EQ("$rdx", names[1]); | ||||||
|  |   EXPECT_EQ("$rbp", names[6]); | ||||||
|  |   EXPECT_EQ("$rsp", names[7]); | ||||||
|  |   EXPECT_EQ("$rip", names[16]); | ||||||
|  | } | ||||||
							
								
								
									
										1054
									
								
								google-breakpad/src/common/dwarf_cu_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1054
									
								
								google-breakpad/src/common/dwarf_cu_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										315
									
								
								google-breakpad/src/common/dwarf_cu_to_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								google-breakpad/src/common/dwarf_cu_to_module.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,315 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // Add DWARF debugging information to a Breakpad symbol file. This
 | ||||||
|  | // file defines the DwarfCUToModule class, which accepts parsed DWARF
 | ||||||
|  | // data and populates a google_breakpad::Module with the results; the
 | ||||||
|  | // Module can then write its contents as a Breakpad symbol file.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__ | ||||||
|  | #define COMMON_LINUX_DWARF_CU_TO_MODULE_H__ | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/language.h" | ||||||
|  | #include "common/module.h" | ||||||
|  | #include "common/dwarf/bytereader.h" | ||||||
|  | #include "common/dwarf/dwarf2diehandler.h" | ||||||
|  | #include "common/dwarf/dwarf2reader.h" | ||||||
|  | #include "common/scoped_ptr.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | using dwarf2reader::DwarfAttribute; | ||||||
|  | using dwarf2reader::DwarfForm; | ||||||
|  | using dwarf2reader::DwarfLanguage; | ||||||
|  | using dwarf2reader::DwarfTag; | ||||||
|  | 
 | ||||||
|  | // Populate a google_breakpad::Module with DWARF debugging information.
 | ||||||
|  | //
 | ||||||
|  | // An instance of this class can be provided as a handler to a
 | ||||||
|  | // dwarf2reader::DIEDispatcher, which can in turn be a handler for a
 | ||||||
|  | // dwarf2reader::CompilationUnit DWARF parser. The handler uses the results
 | ||||||
|  | // of parsing to populate a google_breakpad::Module with source file,
 | ||||||
|  | // function, and source line information.
 | ||||||
|  | class DwarfCUToModule: public dwarf2reader::RootDIEHandler { | ||||||
|  |   struct FilePrivate; | ||||||
|  |  public: | ||||||
|  |   // Information global to the DWARF-bearing file we are processing,
 | ||||||
|  |   // for use by DwarfCUToModule. Each DwarfCUToModule instance deals
 | ||||||
|  |   // with a single compilation unit within the file, but information
 | ||||||
|  |   // global to the whole file is held here. The client is responsible
 | ||||||
|  |   // for filling it in appropriately (except for the 'file_private'
 | ||||||
|  |   // field, which the constructor and destructor take care of), and
 | ||||||
|  |   // then providing it to the DwarfCUToModule instance for each
 | ||||||
|  |   // compilation unit we process in that file. Set HANDLE_INTER_CU_REFS
 | ||||||
|  |   // to true to handle debugging symbols with DW_FORM_ref_addr entries.
 | ||||||
|  |   class FileContext { | ||||||
|  |    public: | ||||||
|  |     FileContext(const string &filename, | ||||||
|  |                 Module *module, | ||||||
|  |                 bool handle_inter_cu_refs); | ||||||
|  |     ~FileContext(); | ||||||
|  | 
 | ||||||
|  |     // Add CONTENTS of size LENGTH to the section map as NAME.
 | ||||||
|  |     void AddSectionToSectionMap(const string& name, | ||||||
|  |                                 const char* contents, | ||||||
|  |                                 uint64 length); | ||||||
|  | 
 | ||||||
|  |     // Clear the section map for testing.
 | ||||||
|  |     void ClearSectionMapForTest(); | ||||||
|  | 
 | ||||||
|  |     const dwarf2reader::SectionMap& section_map() const; | ||||||
|  | 
 | ||||||
|  |    private: | ||||||
|  |     friend class DwarfCUToModule; | ||||||
|  | 
 | ||||||
|  |     // Clears all the Specifications if HANDLE_INTER_CU_REFS_ is false.
 | ||||||
|  |     void ClearSpecifications(); | ||||||
|  | 
 | ||||||
|  |     // Given an OFFSET and a CU that starts at COMPILATION_UNIT_START, returns
 | ||||||
|  |     // true if this is an inter-compilation unit reference that is not being
 | ||||||
|  |     // handled.
 | ||||||
|  |     bool IsUnhandledInterCUReference(uint64 offset, | ||||||
|  |                                      uint64 compilation_unit_start) const; | ||||||
|  | 
 | ||||||
|  |     // The name of this file, for use in error messages.
 | ||||||
|  |     const string filename_; | ||||||
|  | 
 | ||||||
|  |     // A map of this file's sections, used for finding other DWARF
 | ||||||
|  |     // sections that the .debug_info section may refer to.
 | ||||||
|  |     dwarf2reader::SectionMap section_map_; | ||||||
|  | 
 | ||||||
|  |     // The Module to which we're contributing definitions.
 | ||||||
|  |     Module *module_; | ||||||
|  | 
 | ||||||
|  |     // True if we are handling references between compilation units.
 | ||||||
|  |     const bool handle_inter_cu_refs_; | ||||||
|  | 
 | ||||||
|  |     // Inter-compilation unit data used internally by the handlers.
 | ||||||
|  |     scoped_ptr<FilePrivate> file_private_; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // An abstract base class for handlers that handle DWARF line data
 | ||||||
|  |   // for DwarfCUToModule. DwarfCUToModule could certainly just use
 | ||||||
|  |   // dwarf2reader::LineInfo itself directly, but decoupling things
 | ||||||
|  |   // this way makes unit testing a little easier.
 | ||||||
|  |   class LineToModuleHandler { | ||||||
|  |    public: | ||||||
|  |     LineToModuleHandler() { } | ||||||
|  |     virtual ~LineToModuleHandler() { } | ||||||
|  | 
 | ||||||
|  |     // Called at the beginning of a new compilation unit, prior to calling
 | ||||||
|  |     // ReadProgram(). compilation_dir will indicate the path that the
 | ||||||
|  |     // current compilation unit was compiled in, consistent with the
 | ||||||
|  |     // DW_AT_comp_dir DIE.
 | ||||||
|  |     virtual void StartCompilationUnit(const string& compilation_dir) = 0; | ||||||
|  | 
 | ||||||
|  |     // Populate MODULE and LINES with source file names and code/line
 | ||||||
|  |     // mappings, given a pointer to some DWARF line number data
 | ||||||
|  |     // PROGRAM, and an overestimate of its size. Add no zero-length
 | ||||||
|  |     // lines to LINES.
 | ||||||
|  |     virtual void ReadProgram(const char *program, uint64 length, | ||||||
|  |                              Module *module, vector<Module::Line> *lines) = 0; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // The interface DwarfCUToModule uses to report warnings. The member
 | ||||||
|  |   // function definitions for this class write messages to stderr, but
 | ||||||
|  |   // you can override them if you'd like to detect or report these
 | ||||||
|  |   // conditions yourself.
 | ||||||
|  |   class WarningReporter { | ||||||
|  |    public: | ||||||
|  |     // Warn about problems in the DWARF file FILENAME, in the
 | ||||||
|  |     // compilation unit at OFFSET.
 | ||||||
|  |     WarningReporter(const string &filename, uint64 cu_offset) | ||||||
|  |         : filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false), | ||||||
|  |           printed_unpaired_header_(false), | ||||||
|  |           uncovered_warnings_enabled_(false) { } | ||||||
|  |     virtual ~WarningReporter() { } | ||||||
|  | 
 | ||||||
|  |     // Set the name of the compilation unit we're processing to NAME.
 | ||||||
|  |     virtual void SetCUName(const string &name) { cu_name_ = name; } | ||||||
|  | 
 | ||||||
|  |     // Accessor and setter for uncovered_warnings_enabled_.
 | ||||||
|  |     // UncoveredFunction and UncoveredLine only report a problem if that is
 | ||||||
|  |     // true. By default, these warnings are disabled, because those
 | ||||||
|  |     // conditions occur occasionally in healthy code.
 | ||||||
|  |     virtual bool uncovered_warnings_enabled() const { | ||||||
|  |       return uncovered_warnings_enabled_; | ||||||
|  |     } | ||||||
|  |     virtual void set_uncovered_warnings_enabled(bool value) { | ||||||
|  |       uncovered_warnings_enabled_ = value; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // A DW_AT_specification in the DIE at OFFSET refers to a DIE we
 | ||||||
|  |     // haven't processed yet, or that wasn't marked as a declaration,
 | ||||||
|  |     // at TARGET.
 | ||||||
|  |     virtual void UnknownSpecification(uint64 offset, uint64 target); | ||||||
|  | 
 | ||||||
|  |     // A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we
 | ||||||
|  |     // haven't processed yet, or that wasn't marked as inline, at TARGET.
 | ||||||
|  |     virtual void UnknownAbstractOrigin(uint64 offset, uint64 target); | ||||||
|  | 
 | ||||||
|  |     // We were unable to find the DWARF section named SECTION_NAME.
 | ||||||
|  |     virtual void MissingSection(const string §ion_name); | ||||||
|  | 
 | ||||||
|  |     // The CU's DW_AT_stmt_list offset OFFSET is bogus.
 | ||||||
|  |     virtual void BadLineInfoOffset(uint64 offset); | ||||||
|  | 
 | ||||||
|  |     // FUNCTION includes code covered by no line number data.
 | ||||||
|  |     virtual void UncoveredFunction(const Module::Function &function); | ||||||
|  | 
 | ||||||
|  |     // Line number NUMBER in LINE_FILE, of length LENGTH, includes code
 | ||||||
|  |     // covered by no function.
 | ||||||
|  |     virtual void UncoveredLine(const Module::Line &line); | ||||||
|  | 
 | ||||||
|  |     // The DW_TAG_subprogram DIE at OFFSET has no name specified directly
 | ||||||
|  |     // in the DIE, nor via a DW_AT_specification or DW_AT_abstract_origin
 | ||||||
|  |     // link.
 | ||||||
|  |     virtual void UnnamedFunction(uint64 offset); | ||||||
|  | 
 | ||||||
|  |     // The DW_FORM_ref_addr at OFFSET to TARGET was not handled because
 | ||||||
|  |     // FilePrivate did not retain the inter-CU specification data.
 | ||||||
|  |     virtual void UnhandledInterCUReference(uint64 offset, uint64 target); | ||||||
|  | 
 | ||||||
|  |     uint64 cu_offset() const { | ||||||
|  |       return cu_offset_; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |    protected: | ||||||
|  |     const string filename_; | ||||||
|  |     const uint64 cu_offset_; | ||||||
|  |     string cu_name_; | ||||||
|  |     bool printed_cu_header_; | ||||||
|  |     bool printed_unpaired_header_; | ||||||
|  |     bool uncovered_warnings_enabled_; | ||||||
|  | 
 | ||||||
|  |    private: | ||||||
|  |     // Print a per-CU heading, once.
 | ||||||
|  |     void CUHeading(); | ||||||
|  |     // Print an unpaired function/line heading, once.
 | ||||||
|  |     void UncoveredHeading(); | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // Create a DWARF debugging info handler for a compilation unit
 | ||||||
|  |   // within FILE_CONTEXT. This uses information received from the
 | ||||||
|  |   // dwarf2reader::CompilationUnit DWARF parser to populate
 | ||||||
|  |   // FILE_CONTEXT->module. Use LINE_READER to handle the compilation
 | ||||||
|  |   // unit's line number data. Use REPORTER to report problems with the
 | ||||||
|  |   // data we find.
 | ||||||
|  |   DwarfCUToModule(FileContext *file_context, | ||||||
|  |                   LineToModuleHandler *line_reader, | ||||||
|  |                   WarningReporter *reporter); | ||||||
|  |   ~DwarfCUToModule(); | ||||||
|  | 
 | ||||||
|  |   void ProcessAttributeSigned(enum DwarfAttribute attr, | ||||||
|  |                               enum DwarfForm form, | ||||||
|  |                               int64 data); | ||||||
|  |   void ProcessAttributeUnsigned(enum DwarfAttribute attr, | ||||||
|  |                                 enum DwarfForm form, | ||||||
|  |                                 uint64 data); | ||||||
|  |   void ProcessAttributeString(enum DwarfAttribute attr, | ||||||
|  |                               enum DwarfForm form, | ||||||
|  |                               const string &data); | ||||||
|  |   bool EndAttributes(); | ||||||
|  |   DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag); | ||||||
|  | 
 | ||||||
|  |   // Assign all our source Lines to the Functions that cover their
 | ||||||
|  |   // addresses, and then add them to module_.
 | ||||||
|  |   void Finish(); | ||||||
|  | 
 | ||||||
|  |   bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||||
|  |                             uint8 offset_size, uint64 cu_length, | ||||||
|  |                             uint8 dwarf_version); | ||||||
|  |   bool StartRootDIE(uint64 offset, enum DwarfTag tag); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  |   // Used internally by the handler. Full definitions are in
 | ||||||
|  |   // dwarf_cu_to_module.cc.
 | ||||||
|  |   struct CUContext; | ||||||
|  |   struct DIEContext; | ||||||
|  |   struct Specification; | ||||||
|  |   class GenericDIEHandler; | ||||||
|  |   class FuncHandler; | ||||||
|  |   class NamedScopeHandler; | ||||||
|  | 
 | ||||||
|  |   // A map from section offsets to specifications.
 | ||||||
|  |   typedef map<uint64, Specification> SpecificationByOffset; | ||||||
|  | 
 | ||||||
|  |   // Set this compilation unit's source language to LANGUAGE.
 | ||||||
|  |   void SetLanguage(DwarfLanguage language); | ||||||
|  | 
 | ||||||
|  |   // Read source line information at OFFSET in the .debug_line
 | ||||||
|  |   // section.  Record source files in module_, but record source lines
 | ||||||
|  |   // in lines_; we apportion them to functions in
 | ||||||
|  |   // AssignLinesToFunctions.
 | ||||||
|  |   void ReadSourceLines(uint64 offset); | ||||||
|  | 
 | ||||||
|  |   // Assign the lines in lines_ to the individual line lists of the
 | ||||||
|  |   // functions in functions_.  (DWARF line information maps an entire
 | ||||||
|  |   // compilation unit at a time, and gives no indication of which
 | ||||||
|  |   // lines belong to which functions, beyond their addresses.)
 | ||||||
|  |   void AssignLinesToFunctions(); | ||||||
|  | 
 | ||||||
|  |   // The only reason cu_context_ and child_context_ are pointers is
 | ||||||
|  |   // that we want to keep their definitions private to
 | ||||||
|  |   // dwarf_cu_to_module.cc, instead of listing them all here. They are
 | ||||||
|  |   // owned by this DwarfCUToModule: the constructor sets them, and the
 | ||||||
|  |   // destructor deletes them.
 | ||||||
|  | 
 | ||||||
|  |   // The handler to use to handle line number data.
 | ||||||
|  |   LineToModuleHandler *line_reader_; | ||||||
|  | 
 | ||||||
|  |   // This compilation unit's context.
 | ||||||
|  |   scoped_ptr<CUContext> cu_context_; | ||||||
|  | 
 | ||||||
|  |   // A context for our children.
 | ||||||
|  |   scoped_ptr<DIEContext> child_context_; | ||||||
|  | 
 | ||||||
|  |   // True if this compilation unit has source line information.
 | ||||||
|  |   bool has_source_line_info_; | ||||||
|  | 
 | ||||||
|  |   // The offset of this compilation unit's line number information in
 | ||||||
|  |   // the .debug_line section.
 | ||||||
|  |   uint64 source_line_offset_; | ||||||
|  | 
 | ||||||
|  |   // The line numbers we have seen thus far.  We accumulate these here
 | ||||||
|  |   // during parsing.  Then, in Finish, we call AssignLinesToFunctions
 | ||||||
|  |   // to dole them out to the appropriate functions.
 | ||||||
|  |   vector<Module::Line> lines_; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | }  // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif  // COMMON_LINUX_DWARF_CU_TO_MODULE_H__
 | ||||||
							
								
								
									
										1780
									
								
								google-breakpad/src/common/dwarf_cu_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1780
									
								
								google-breakpad/src/common/dwarf_cu_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										143
									
								
								google-breakpad/src/common/dwarf_line_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								google-breakpad/src/common/dwarf_line_to_module.cc
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // dwarf_line_to_module.cc: Implementation of DwarfLineToModule class.
 | ||||||
|  | // See dwarf_line_to_module.h for details. 
 | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/dwarf_line_to_module.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | // Trying to support Windows paths in a reasonable way adds a lot of
 | ||||||
|  | // variations to test; it would be better to just put off dealing with
 | ||||||
|  | // it until we actually have to deal with DWARF on Windows.
 | ||||||
|  | 
 | ||||||
|  | // Return true if PATH is an absolute path, false if it is relative.
 | ||||||
|  | static bool PathIsAbsolute(const string &path) { | ||||||
|  |   return (path.size() >= 1 && path[0] == '/'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool HasTrailingSlash(const string &path) { | ||||||
|  |   return (path.size() >= 1 && path[path.size() - 1] == '/'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // If PATH is an absolute path, return PATH.  If PATH is a relative path,
 | ||||||
|  | // treat it as relative to BASE and return the combined path.
 | ||||||
|  | static string ExpandPath(const string &path, | ||||||
|  |                          const string &base) { | ||||||
|  |   if (PathIsAbsolute(path) || base.empty()) | ||||||
|  |     return path; | ||||||
|  |   return base + (HasTrailingSlash(base) ? "" : "/") + path; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) { | ||||||
|  |   // Directory number zero is reserved to mean the compilation
 | ||||||
|  |   // directory. Silently ignore attempts to redefine it.
 | ||||||
|  |   if (dir_num != 0) | ||||||
|  |     directories_[dir_num] = ExpandPath(name, compilation_dir_); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DwarfLineToModule::DefineFile(const string &name, int32 file_num, | ||||||
|  |                                    uint32 dir_num, uint64 mod_time, | ||||||
|  |                                    uint64 length) { | ||||||
|  |   if (file_num == -1) | ||||||
|  |     file_num = ++highest_file_number_; | ||||||
|  |   else if (file_num > highest_file_number_) | ||||||
|  |     highest_file_number_ = file_num; | ||||||
|  | 
 | ||||||
|  |   string dir_name; | ||||||
|  |   if (dir_num == 0) { | ||||||
|  |     // Directory number zero is the compilation directory, and is stored as
 | ||||||
|  |     // an attribute on the compilation unit, rather than in the program table.
 | ||||||
|  |     dir_name = compilation_dir_; | ||||||
|  |   } else { | ||||||
|  |     DirectoryTable::const_iterator directory_it = directories_.find(dir_num); | ||||||
|  |     if (directory_it != directories_.end()) { | ||||||
|  |       dir_name = directory_it->second; | ||||||
|  |     } else { | ||||||
|  |       if (!warned_bad_directory_number_) { | ||||||
|  |         fprintf(stderr, "warning: DWARF line number data refers to undefined" | ||||||
|  |                 " directory numbers\n"); | ||||||
|  |         warned_bad_directory_number_ = true; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   string full_name = ExpandPath(name, dir_name); | ||||||
|  | 
 | ||||||
|  |   // Find a Module::File object of the given name, and add it to the
 | ||||||
|  |   // file table.
 | ||||||
|  |   files_[file_num] = module_->FindFile(full_name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void DwarfLineToModule::AddLine(uint64 address, uint64 length, | ||||||
|  |                                 uint32 file_num, uint32 line_num, | ||||||
|  |                                 uint32 column_num) { | ||||||
|  |   if (length == 0) | ||||||
|  |     return; | ||||||
|  | 
 | ||||||
|  |   // Clip lines not to extend beyond the end of the address space.
 | ||||||
|  |   if (address + length < address) | ||||||
|  |     length = -address; | ||||||
|  | 
 | ||||||
|  |   // Should we omit this line? (See the comments for omitted_line_end_.)
 | ||||||
|  |   if (address == 0 || address == omitted_line_end_) { | ||||||
|  |     omitted_line_end_ = address + length; | ||||||
|  |     return; | ||||||
|  |   } else { | ||||||
|  |     omitted_line_end_ = 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // Find the source file being referred to.
 | ||||||
|  |   Module::File *file = files_[file_num]; | ||||||
|  |   if (!file) { | ||||||
|  |     if (!warned_bad_file_number_) { | ||||||
|  |       fprintf(stderr, "warning: DWARF line number data refers to " | ||||||
|  |               "undefined file numbers\n"); | ||||||
|  |       warned_bad_file_number_ = true; | ||||||
|  |     } | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   Module::Line line; | ||||||
|  |   line.address = address; | ||||||
|  |   // We set the size when we get the next line or the EndSequence call.
 | ||||||
|  |   line.size = length; | ||||||
|  |   line.file = file; | ||||||
|  |   line.number = line_num; | ||||||
|  |   lines_->push_back(line); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } // namespace google_breakpad
 | ||||||
							
								
								
									
										188
									
								
								google-breakpad/src/common/dwarf_line_to_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								google-breakpad/src/common/dwarf_line_to_module.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | |||||||
|  | // -*- mode: c++ -*-
 | ||||||
|  | 
 | ||||||
|  | // Copyright (c) 2010 Google Inc.
 | ||||||
|  | // All rights reserved.
 | ||||||
|  | //
 | ||||||
|  | // Redistribution and use in source and binary forms, with or without
 | ||||||
|  | // modification, are permitted provided that the following conditions are
 | ||||||
|  | // met:
 | ||||||
|  | //
 | ||||||
|  | //     * Redistributions of source code must retain the above copyright
 | ||||||
|  | // notice, this list of conditions and the following disclaimer.
 | ||||||
|  | //     * Redistributions in binary form must reproduce the above
 | ||||||
|  | // copyright notice, this list of conditions and the following disclaimer
 | ||||||
|  | // in the documentation and/or other materials provided with the
 | ||||||
|  | // distribution.
 | ||||||
|  | //     * Neither the name of Google Inc. nor the names of its
 | ||||||
|  | // contributors may be used to endorse or promote products derived from
 | ||||||
|  | // this software without specific prior written permission.
 | ||||||
|  | //
 | ||||||
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | ||||||
|  | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | ||||||
|  | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | ||||||
|  | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | ||||||
|  | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | ||||||
|  | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | ||||||
|  | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | ||||||
|  | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | ||||||
|  | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | ||||||
|  | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | ||||||
|  | 
 | ||||||
|  | // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
 | ||||||
|  | 
 | ||||||
|  | // The DwarfLineToModule class accepts line number information from a
 | ||||||
|  | // DWARF parser and adds it to a google_breakpad::Module. The Module
 | ||||||
|  | // can write that data out as a Breakpad symbol file.
 | ||||||
|  | 
 | ||||||
|  | #ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H | ||||||
|  | #define COMMON_LINUX_DWARF_LINE_TO_MODULE_H | ||||||
|  | 
 | ||||||
|  | #include <string> | ||||||
|  | 
 | ||||||
|  | #include "common/module.h" | ||||||
|  | #include "common/dwarf/dwarf2reader.h" | ||||||
|  | #include "common/using_std_string.h" | ||||||
|  | 
 | ||||||
|  | namespace google_breakpad { | ||||||
|  | 
 | ||||||
|  | // A class for producing a vector of google_breakpad::Module::Line
 | ||||||
|  | // instances from parsed DWARF line number data.  
 | ||||||
|  | //
 | ||||||
|  | // An instance of this class can be provided as a handler to a
 | ||||||
|  | // dwarf2reader::LineInfo DWARF line number information parser. The
 | ||||||
|  | // handler accepts source location information from the parser and
 | ||||||
|  | // uses it to produce a vector of google_breakpad::Module::Line
 | ||||||
|  | // objects, referring to google_breakpad::Module::File objects added
 | ||||||
|  | // to a particular google_breakpad::Module.
 | ||||||
|  | //
 | ||||||
|  | // GNU toolchain omitted sections support:
 | ||||||
|  | // ======================================
 | ||||||
|  | //
 | ||||||
|  | // Given the right options, the GNU toolchain will omit unreferenced
 | ||||||
|  | // functions from the final executable. Unfortunately, when it does so, it
 | ||||||
|  | // does not remove the associated portions of the DWARF line number
 | ||||||
|  | // program; instead, it gives the DW_LNE_set_address instructions referring
 | ||||||
|  | // to the now-deleted code addresses of zero. Given this input, the DWARF
 | ||||||
|  | // line parser will call AddLine with a series of lines starting at address
 | ||||||
|  | // zero. For example, here is the output from 'readelf -wl' for a program
 | ||||||
|  | // with four functions, the first three of which have been omitted:
 | ||||||
|  | //
 | ||||||
|  | //   Line Number Statements:
 | ||||||
|  | //    Extended opcode 2: set Address to 0x0
 | ||||||
|  | //    Advance Line by 14 to 15
 | ||||||
|  | //    Copy
 | ||||||
|  | //    Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
 | ||||||
|  | //    Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
 | ||||||
|  | //    Advance PC by 2 to 0xd
 | ||||||
|  | //    Extended opcode 1: End of Sequence
 | ||||||
|  | // 
 | ||||||
|  | //    Extended opcode 2: set Address to 0x0
 | ||||||
|  | //    Advance Line by 14 to 15
 | ||||||
|  | //    Copy
 | ||||||
|  | //    Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16
 | ||||||
|  | //    Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18
 | ||||||
|  | //    Advance PC by 2 to 0xd
 | ||||||
|  | //    Extended opcode 1: End of Sequence
 | ||||||
|  | // 
 | ||||||
|  | //    Extended opcode 2: set Address to 0x0
 | ||||||
|  | //    Advance Line by 19 to 20
 | ||||||
|  | //    Copy
 | ||||||
|  | //    Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21
 | ||||||
|  | //    Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22
 | ||||||
|  | //    Advance PC by 2 to 0xa
 | ||||||
|  | //    Extended opcode 1: End of Sequence
 | ||||||
|  | // 
 | ||||||
|  | //    Extended opcode 2: set Address to 0x80483a4
 | ||||||
|  | //    Advance Line by 23 to 24
 | ||||||
|  | //    Copy
 | ||||||
|  | //    Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25
 | ||||||
|  | //    Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26
 | ||||||
|  | //    Advance PC by 6 to 0x80483bd
 | ||||||
|  | //    Extended opcode 1: End of Sequence
 | ||||||
|  | //
 | ||||||
|  | // Instead of collecting runs of lines describing code that is not there,
 | ||||||
|  | // we try to recognize and drop them. Since the linker doesn't explicitly
 | ||||||
|  | // distinguish references to dropped sections from genuine references to
 | ||||||
|  | // code at address zero, we must use a heuristic. We have chosen:
 | ||||||
|  | //
 | ||||||
|  | // - If a line starts at address zero, omit it. (On the platforms
 | ||||||
|  | //   breakpad targets, it is extremely unlikely that there will be code
 | ||||||
|  | //   at address zero.)
 | ||||||
|  | //
 | ||||||
|  | // - If a line starts immediately after an omitted line, omit it too.
 | ||||||
|  | class DwarfLineToModule: public dwarf2reader::LineInfoHandler { | ||||||
|  |  public: | ||||||
|  |   // As the DWARF line info parser passes us line records, add source
 | ||||||
|  |   // files to MODULE, and add all lines to the end of LINES. LINES
 | ||||||
|  |   // need not be empty. If the parser hands us a zero-length line, we
 | ||||||
|  |   // omit it. If the parser hands us a line that extends beyond the
 | ||||||
|  |   // end of the address space, we clip it. It's up to our client to
 | ||||||
|  |   // sort out which lines belong to which functions; we don't add them
 | ||||||
|  |   // to any particular function in MODULE ourselves.
 | ||||||
|  |   DwarfLineToModule(Module *module, const string& compilation_dir, | ||||||
|  |                     vector<Module::Line> *lines) | ||||||
|  |       : module_(module), | ||||||
|  |         compilation_dir_(compilation_dir), | ||||||
|  |         lines_(lines), | ||||||
|  |         highest_file_number_(-1), | ||||||
|  |         omitted_line_end_(0), | ||||||
|  |         warned_bad_file_number_(false), | ||||||
|  |         warned_bad_directory_number_(false) { } | ||||||
|  |    | ||||||
|  |   ~DwarfLineToModule() { } | ||||||
|  | 
 | ||||||
|  |   void DefineDir(const string &name, uint32 dir_num); | ||||||
|  |   void DefineFile(const string &name, int32 file_num, | ||||||
|  |                   uint32 dir_num, uint64 mod_time, | ||||||
|  |                   uint64 length); | ||||||
|  |   void AddLine(uint64 address, uint64 length, | ||||||
|  |                uint32 file_num, uint32 line_num, uint32 column_num); | ||||||
|  | 
 | ||||||
|  |  private: | ||||||
|  | 
 | ||||||
|  |   typedef std::map<uint32, string> DirectoryTable; | ||||||
|  |   typedef std::map<uint32, Module::File *> FileTable; | ||||||
|  | 
 | ||||||
|  |   // The module we're contributing debugging info to. Owned by our
 | ||||||
|  |   // client.
 | ||||||
|  |   Module *module_; | ||||||
|  | 
 | ||||||
|  |   // The compilation directory for the current compilation unit whose
 | ||||||
|  |   // lines are being accumulated.
 | ||||||
|  |   string compilation_dir_; | ||||||
|  | 
 | ||||||
|  |   // The vector of lines we're accumulating. Owned by our client.
 | ||||||
|  |   //
 | ||||||
|  |   // In a Module, as in a breakpad symbol file, lines belong to
 | ||||||
|  |   // specific functions, but DWARF simply assigns lines to addresses;
 | ||||||
|  |   // one must infer the line/function relationship using the
 | ||||||
|  |   // functions' beginning and ending addresses. So we can't add these
 | ||||||
|  |   // to the appropriate function from module_ until we've read the
 | ||||||
|  |   // function info as well. Instead, we accumulate lines here, and let
 | ||||||
|  |   // whoever constructed this sort it all out.
 | ||||||
|  |   vector<Module::Line> *lines_; | ||||||
|  | 
 | ||||||
|  |   // A table mapping directory numbers to paths.
 | ||||||
|  |   DirectoryTable directories_; | ||||||
|  | 
 | ||||||
|  |   // A table mapping file numbers to Module::File pointers.
 | ||||||
|  |   FileTable files_; | ||||||
|  | 
 | ||||||
|  |   // The highest file number we've seen so far, or -1 if we've seen
 | ||||||
|  |   // none.  Used for dynamically defined file numbers.
 | ||||||
|  |   int32 highest_file_number_; | ||||||
|  |    | ||||||
|  |   // This is the ending address of the last line we omitted, or zero if we
 | ||||||
|  |   // didn't omit the previous line. It is zero before we have received any
 | ||||||
|  |   // AddLine calls.
 | ||||||
|  |   uint64 omitted_line_end_; | ||||||
|  | 
 | ||||||
|  |   // True if we've warned about:
 | ||||||
|  |   bool warned_bad_file_number_; // bad file numbers
 | ||||||
|  |   bool warned_bad_directory_number_; // bad directory numbers
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // namespace google_breakpad
 | ||||||
|  | 
 | ||||||
|  | #endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H
 | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
		Reference in New Issue
	
	Block a user