зеркало из https://github.com/mozilla/gecko-dev.git
bug 548113 - Sync to Breakpad revision 554
--HG-- extra : rebase_source : d2fec8ccdc5e042a806f257b2c80631373549b3b
This commit is contained in:
Родитель
90aa8f9de7
Коммит
ce696988e7
|
@ -1,6 +1,6 @@
|
|||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
# Copyright (c) 2006, Google Inc.
|
||||
# Copyright (c) 2010, Google Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
|
@ -33,6 +33,8 @@
|
|||
# This allows #includes to be relative to src/
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
|
||||
# Specify include paths for ac macros
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
## Documentation
|
||||
docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION)
|
||||
|
@ -47,7 +49,19 @@ dist_doc_DATA = \
|
|||
|
||||
|
||||
## Libraries
|
||||
lib_LTLIBRARIES = src/libbreakpad.la
|
||||
lib_LTLIBRARIES = src/libbreakpad.la src/client/linux/libbreakpad_client.la
|
||||
|
||||
src_client_linux_libbreakpad_client_la_SOURCES = \
|
||||
src/client/linux/crash_generation/crash_generation_client.cc \
|
||||
src/client/linux/handler/exception_handler.cc \
|
||||
src/client/linux/minidump_writer/linux_dumper.cc \
|
||||
src/client/linux/minidump_writer/minidump_writer.cc \
|
||||
src/client/minidump_file_writer.cc \
|
||||
src/common/convert_UTF.c \
|
||||
src/common/md5.c \
|
||||
src/common/string_conversion.cc \
|
||||
src/common/linux/file_id.cc \
|
||||
src/common/linux/guid_creator.cc
|
||||
|
||||
src_libbreakpad_la_SOURCES = \
|
||||
src/google_breakpad/common/breakpad_types.h \
|
||||
|
@ -74,6 +88,8 @@ src_libbreakpad_la_SOURCES = \
|
|||
src/processor/basic_code_modules.h \
|
||||
src/processor/basic_source_line_resolver.cc \
|
||||
src/processor/call_stack.cc \
|
||||
src/processor/cfi_frame_info.cc \
|
||||
src/processor/cfi_frame_info.h \
|
||||
src/processor/contained_range_map-inl.h \
|
||||
src/processor/contained_range_map.h \
|
||||
src/processor/linked_ptr.h \
|
||||
|
@ -91,7 +107,7 @@ src_libbreakpad_la_SOURCES = \
|
|||
src/processor/scoped_ptr.h \
|
||||
src/processor/simple_symbol_supplier.cc \
|
||||
src/processor/simple_symbol_supplier.h \
|
||||
src/processor/stack_frame_info.h \
|
||||
src/processor/windows_frame_info.h \
|
||||
src/processor/stackwalker.cc \
|
||||
src/processor/stackwalker_amd64.cc \
|
||||
src/processor/stackwalker_amd64.h \
|
||||
|
@ -107,20 +123,27 @@ src_libbreakpad_la_SOURCES = \
|
|||
|
||||
## Programs
|
||||
bin_PROGRAMS = \
|
||||
src/client/linux/linux_dumper_unittest_helper \
|
||||
src/processor/minidump_dump \
|
||||
src/processor/minidump_stackwalk
|
||||
|
||||
|
||||
## Tests
|
||||
check_PROGRAMS = \
|
||||
src/client/linux/linux_client_unittest \
|
||||
src/processor/address_map_unittest \
|
||||
src/processor/basic_source_line_resolver_unittest \
|
||||
src/processor/cfi_frame_info_unittest \
|
||||
src/processor/contained_range_map_unittest \
|
||||
src/processor/minidump_processor_unittest \
|
||||
src/processor/minidump_unittest \
|
||||
src/processor/pathname_stripper_unittest \
|
||||
src/processor/postfix_evaluator_unittest \
|
||||
src/processor/range_map_unittest
|
||||
src/processor/range_map_unittest \
|
||||
src/processor/stackwalker_amd64_unittest \
|
||||
src/processor/stackwalker_arm_unittest \
|
||||
src/processor/stackwalker_x86_unittest \
|
||||
src/processor/synth_minidump_unittest \
|
||||
src/processor/test_assembler_unittest
|
||||
|
||||
if SELFTEST
|
||||
check_PROGRAMS += \
|
||||
|
@ -135,6 +158,40 @@ check_SCRIPTS = \
|
|||
TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
||||
TESTS_ENVIRONMENT =
|
||||
|
||||
src_client_linux_linux_dumper_unittest_helper_SOURCES = \
|
||||
src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
|
||||
src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS)
|
||||
src_client_linux_linux_dumper_unittest_helper_LDFLAGS=$(PTHREAD_CFLAGS)
|
||||
src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC)
|
||||
|
||||
src_client_linux_linux_client_unittest_SOURCES = \
|
||||
src/client/linux/handler/exception_handler_unittest.cc \
|
||||
src/client/linux/minidump_writer/directory_reader_unittest.cc \
|
||||
src/client/linux/minidump_writer/line_reader_unittest.cc \
|
||||
src/client/linux/minidump_writer/linux_dumper_unittest.cc \
|
||||
src/client/linux/minidump_writer/minidump_writer_unittest.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
|
||||
src_client_linux_linux_client_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
src_client_linux_linux_client_unittest_LDADD = \
|
||||
src/client/linux/handler/exception_handler.lo \
|
||||
src/client/linux/crash_generation/crash_generation_client.lo \
|
||||
src/client/linux/minidump_writer/linux_dumper.lo \
|
||||
src/client/linux/minidump_writer/minidump_writer.lo \
|
||||
src/client/minidump_file_writer.lo \
|
||||
src/common/convert_UTF.lo \
|
||||
src/common/md5.lo \
|
||||
src/common/linux/file_id.lo \
|
||||
src/common/linux/guid_creator.lo \
|
||||
src/common/string_conversion.lo
|
||||
src_client_linux_linux_client_unittest_DEPENDENCIES = src/client/linux/linux_dumper_unittest_helper src/client/linux/libbreakpad_client.la
|
||||
|
||||
src_processor_address_map_unittest_SOURCES = \
|
||||
src/processor/address_map_unittest.cc
|
||||
src_processor_address_map_unittest_LDADD = \
|
||||
|
@ -145,9 +202,25 @@ src_processor_basic_source_line_resolver_unittest_SOURCES = \
|
|||
src/processor/basic_source_line_resolver_unittest.cc
|
||||
src_processor_basic_source_line_resolver_unittest_LDADD = \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/cfi_frame_info.lo \
|
||||
src/processor/pathname_stripper.lo \
|
||||
src/processor/logging.lo
|
||||
|
||||
src_processor_cfi_frame_info_unittest_SOURCES = \
|
||||
src/processor/cfi_frame_info_unittest.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_processor_cfi_frame_info_unittest_LDADD = \
|
||||
src/processor/cfi_frame_info.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/pathname_stripper.lo
|
||||
src_processor_cfi_frame_info_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
src_processor_contained_range_map_unittest_SOURCES = \
|
||||
src/processor/contained_range_map_unittest.cc
|
||||
src_processor_contained_range_map_unittest_LDADD = \
|
||||
|
@ -167,6 +240,7 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/cfi_frame_info.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
src/processor/minidump.lo \
|
||||
|
@ -181,7 +255,10 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
|
||||
src_processor_minidump_unittest_SOURCES = \
|
||||
src/processor/minidump_unittest.cc \
|
||||
src/processor/synth_minidump.cc \
|
||||
src/processor/test_assembler.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_processor_minidump_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
|
@ -227,6 +304,76 @@ src_processor_stackwalker_selftest_LDADD = \
|
|||
src/processor/stackwalker_sparc.lo \
|
||||
src/processor/stackwalker_x86.lo
|
||||
|
||||
src_processor_stackwalker_amd64_unittest_SOURCES = \
|
||||
src/processor/stackwalker_amd64_unittest.cc \
|
||||
src/processor/test_assembler.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_processor_stackwalker_amd64_unittest_LDADD = \
|
||||
src/libbreakpad.la
|
||||
src_processor_stackwalker_amd64_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
src_processor_stackwalker_arm_unittest_SOURCES = \
|
||||
src/processor/stackwalker_arm_unittest.cc \
|
||||
src/processor/test_assembler.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_processor_stackwalker_arm_unittest_LDADD = \
|
||||
src/libbreakpad.la
|
||||
src_processor_stackwalker_arm_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
src_processor_stackwalker_x86_unittest_SOURCES = \
|
||||
src/processor/stackwalker_x86_unittest.cc \
|
||||
src/processor/test_assembler.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc
|
||||
src_processor_stackwalker_x86_unittest_LDADD = \
|
||||
src/libbreakpad.la
|
||||
src_processor_stackwalker_x86_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
src_processor_synth_minidump_unittest_SOURCES = \
|
||||
src/processor/synth_minidump_unittest.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc \
|
||||
src/processor/synth_minidump.cc \
|
||||
src/processor/synth_minidump.h \
|
||||
src/processor/test_assembler.cc \
|
||||
src/processor/test_assembler.h
|
||||
src_processor_synth_minidump_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
src_processor_test_assembler_unittest_SOURCES = \
|
||||
src/processor/test_assembler_unittest.cc \
|
||||
src/testing/gtest/src/gtest-all.cc \
|
||||
src/testing/gtest/src/gtest_main.cc \
|
||||
src/testing/src/gmock-all.cc \
|
||||
src/processor/test_assembler.cc \
|
||||
src/processor/test_assembler.h
|
||||
src_processor_test_assembler_unittest_CPPFLAGS = \
|
||||
-I$(top_srcdir)/src/testing/include \
|
||||
-I$(top_srcdir)/src/testing/gtest/include \
|
||||
-I$(top_srcdir)/src/testing/gtest \
|
||||
-I$(top_srcdir)/src/testing
|
||||
|
||||
## Non-installables
|
||||
noinst_PROGRAMS =
|
||||
noinst_SCRIPTS = $(check_SCRIPTS)
|
||||
|
@ -245,6 +392,7 @@ src_processor_minidump_stackwalk_LDADD = \
|
|||
src/processor/basic_code_modules.lo \
|
||||
src/processor/basic_source_line_resolver.lo \
|
||||
src/processor/call_stack.lo \
|
||||
src/processor/cfi_frame_info.lo \
|
||||
src/processor/logging.lo \
|
||||
src/processor/minidump.lo \
|
||||
src/processor/minidump_processor.lo \
|
||||
|
@ -399,3 +547,4 @@ EXTRA_DIST = \
|
|||
## Additional rules
|
||||
libtool: $(LIBTOOL_DEPS)
|
||||
$(SHELL) ./config.status --recheck
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -35,7 +35,7 @@ dnl Sanity check: the argument is just a file that should exist.
|
|||
AC_CONFIG_SRCDIR(README)
|
||||
AC_CONFIG_AUX_DIR(autotools)
|
||||
|
||||
AM_INIT_AUTOMAKE(subdir-objects tar-ustar)
|
||||
AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1)
|
||||
AM_CONFIG_HEADER(src/config.h)
|
||||
|
||||
AC_PROG_CC
|
||||
|
@ -46,6 +46,26 @@ AC_PROG_LIBTOOL
|
|||
AC_SUBST(LIBTOOL_DEPS)
|
||||
|
||||
AC_HEADER_STDC
|
||||
m4_include(m4/ax_pthread.m4)
|
||||
AX_PTHREAD
|
||||
|
||||
AC_ARG_ENABLE(m32,
|
||||
AS_HELP_STRING([--enable-m32],
|
||||
[Compile/build with -m32]
|
||||
[(default is no)]),
|
||||
[case "${enableval}" in
|
||||
yes)
|
||||
CFLAGS=$(CFLAGS) -m32
|
||||
usem32=true
|
||||
;;
|
||||
no)
|
||||
usem32=false
|
||||
;;
|
||||
*)
|
||||
AC_MSG_ERROR(bad value ${enableval} for --enable-m32)
|
||||
;;
|
||||
esac],
|
||||
[usem32=false])
|
||||
|
||||
AC_ARG_ENABLE(selftest,
|
||||
AS_HELP_STRING([--enable-selftest],
|
||||
|
|
|
@ -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
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -27,6 +27,7 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
@ -44,7 +45,6 @@ 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;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -80,6 +80,9 @@
|
|||
#include <ucontext.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/linux_syscall_support.h"
|
||||
#include "common/linux/memory.h"
|
||||
|
@ -88,7 +91,7 @@
|
|||
|
||||
// A wrapper for the tgkill syscall: send a signal to a specific thread.
|
||||
static int tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
syscall(__NR_tgkill, tgid, tid, sig);
|
||||
return syscall(__NR_tgkill, tgid, tid, sig);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -145,7 +148,6 @@ void ExceptionHandler::Init(const std::string &dump_path,
|
|||
const int server_fd)
|
||||
{
|
||||
crash_handler_ = NULL;
|
||||
|
||||
if (0 <= server_fd)
|
||||
crash_generation_client_
|
||||
.reset(CrashGenerationClient::TryCreate(server_fd));
|
||||
|
@ -209,7 +211,11 @@ void ExceptionHandler::UninstallHandlers() {
|
|||
sigaction(old_handlers_[i].first, action, NULL);
|
||||
delete action;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&handler_stack_mutex_);
|
||||
std::vector<ExceptionHandler*>::iterator handler =
|
||||
std::find(handler_stack_->begin(), handler_stack_->end(), this);
|
||||
handler_stack_->erase(handler);
|
||||
pthread_mutex_unlock(&handler_stack_mutex_);
|
||||
old_handlers_.clear();
|
||||
}
|
||||
|
||||
|
@ -231,12 +237,15 @@ void ExceptionHandler::UpdateNextID() {
|
|||
}
|
||||
}
|
||||
|
||||
// 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_);
|
||||
|
||||
if (!handler_stack_->size()) {
|
||||
|
@ -288,18 +297,25 @@ bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
|
|||
|
||||
// Allow ourselves to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
|
||||
CrashContext context;
|
||||
memcpy(&context.siginfo, info, sizeof(siginfo_t));
|
||||
memcpy(&context.context, uc, sizeof(struct ucontext));
|
||||
memcpy(&context.float_state, ((struct ucontext *)uc)->uc_mcontext.fpregs,
|
||||
sizeof(context.float_state));
|
||||
#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 = sys_gettid();
|
||||
|
||||
if (crash_handler_ && crash_handler_(&context, sizeof(context),
|
||||
callback_context_))
|
||||
return true;
|
||||
|
||||
if (crash_handler_ != NULL) {
|
||||
if (crash_handler_(&context, sizeof(context),
|
||||
callback_context_)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return GenerateDump(&context);
|
||||
}
|
||||
|
||||
|
@ -364,6 +380,7 @@ bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
|
|||
}
|
||||
|
||||
bool ExceptionHandler::WriteMinidump() {
|
||||
#if !defined(__ARM_EABI__)
|
||||
// Allow ourselves to be dumped.
|
||||
sys_prctl(PR_SET_DUMPABLE, 1);
|
||||
|
||||
|
@ -378,6 +395,9 @@ bool ExceptionHandler::WriteMinidump() {
|
|||
bool success = GenerateDump(&context);
|
||||
UpdateNextID();
|
||||
return success;
|
||||
#else
|
||||
return false;
|
||||
#endif // !defined(__ARM_EABI__)
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -34,6 +34,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "client/linux/crash_generation/crash_generation_client.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
@ -42,6 +43,8 @@ struct sigaction;
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExceptionHandler;
|
||||
|
||||
// ExceptionHandler
|
||||
//
|
||||
// ExceptionHandler can write a minidump file when an exception occurs,
|
||||
|
@ -163,7 +166,10 @@ class ExceptionHandler {
|
|||
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.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -36,7 +36,7 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "client/linux/handler//exception_handler.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/linux_libc_support.h"
|
||||
|
@ -112,8 +112,8 @@ TEST(ExceptionHandlerTest, ChildCrash) {
|
|||
ASSERT_TRUE(pfd.revents & POLLIN);
|
||||
|
||||
uint32_t len;
|
||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len));
|
||||
ASSERT_LT(len, 2048);
|
||||
ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len));
|
||||
ASSERT_LT(len, (uint32_t)2048);
|
||||
char* filename = reinterpret_cast<char*>(malloc(len + 1));
|
||||
ASSERT_EQ(read(fds[0], filename, len), len);
|
||||
filename[len] = 0;
|
||||
|
@ -137,12 +137,10 @@ CrashHandler(const void* crash_context, size_t crash_context_size,
|
|||
const int fd = (intptr_t) context;
|
||||
int fds[2];
|
||||
pipe(fds);
|
||||
|
||||
struct kernel_msghdr msg = {0};
|
||||
struct kernel_iovec iov;
|
||||
iov.iov_base = const_cast<void*>(crash_context);
|
||||
iov.iov_len = crash_context_size;
|
||||
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
char cmsg[kControlMsgSize];
|
||||
|
@ -183,11 +181,10 @@ TEST(ExceptionHandlerTest, ExternalDumper) {
|
|||
const pid_t child = fork();
|
||||
if (child == 0) {
|
||||
close(fds[0]);
|
||||
ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true);
|
||||
ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true);
|
||||
handler.set_crash_handler(CrashHandler);
|
||||
*reinterpret_cast<int*>(NULL) = 0;
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
struct msghdr msg = {0};
|
||||
struct iovec iov;
|
||||
|
|
|
@ -69,9 +69,9 @@ TEST(LineReaderTest, OneLineTerminated) {
|
|||
LineReader reader(fd);
|
||||
|
||||
const char *line;
|
||||
unsigned len;
|
||||
unsigned int len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned int)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
@ -90,7 +90,7 @@ TEST(LineReaderTest, OneLine) {
|
|||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
@ -109,13 +109,13 @@ TEST(LineReaderTest, TwoLinesTerminated) {
|
|||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'b');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
@ -134,13 +134,13 @@ TEST(LineReaderTest, TwoLines) {
|
|||
const char *line;
|
||||
unsigned len;
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'a');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
||||
ASSERT_TRUE(reader.GetNextLine(&line, &len));
|
||||
ASSERT_EQ(len, 1);
|
||||
ASSERT_EQ(len, (unsigned)1);
|
||||
ASSERT_EQ(line[0], 'b');
|
||||
ASSERT_EQ(line[1], 0);
|
||||
reader.PopLine(len);
|
||||
|
|
|
@ -85,7 +85,7 @@ namespace google_breakpad {
|
|||
|
||||
LinuxDumper::LinuxDumper(int pid)
|
||||
: pid_(pid),
|
||||
threads_suspened_(false),
|
||||
threads_suspended_(false),
|
||||
threads_(&allocator_, 8),
|
||||
mappings_(&allocator_) {
|
||||
}
|
||||
|
@ -96,22 +96,22 @@ bool LinuxDumper::Init() {
|
|||
}
|
||||
|
||||
bool LinuxDumper::ThreadsSuspend() {
|
||||
if (threads_suspened_)
|
||||
if (threads_suspended_)
|
||||
return true;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= SuspendThread(threads_[i]);
|
||||
threads_suspened_ = true;
|
||||
threads_suspended_ = true;
|
||||
return good;
|
||||
}
|
||||
|
||||
bool LinuxDumper::ThreadsResume() {
|
||||
if (!threads_suspened_)
|
||||
if (!threads_suspended_)
|
||||
return false;
|
||||
bool good = true;
|
||||
for (size_t i = 0; i < threads_.size(); ++i)
|
||||
good &= ResumeThread(threads_[i]);
|
||||
threads_suspened_ = false;
|
||||
threads_suspended_ = false;
|
||||
return good;
|
||||
}
|
||||
|
||||
|
@ -312,9 +312,9 @@ bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t>* result) const {
|
|||
}
|
||||
|
||||
// Read thread info from /proc/$pid/status.
|
||||
// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible,
|
||||
// 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
|
||||
// availible.
|
||||
// available.
|
||||
bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
||||
assert(info != NULL);
|
||||
char status_path[80];
|
||||
|
@ -371,6 +371,8 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
|||
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.uregs[R13], sizeof(info->regs.uregs[R13]));
|
||||
#else
|
||||
#error "This code hasn't been ported to your platform yet."
|
||||
#endif
|
||||
|
@ -387,13 +389,9 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) {
|
|||
// 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) {
|
||||
#if defined(__i386) || defined(__x86_64)
|
||||
static const bool stack_grows_down = true;
|
||||
static const uintptr_t page_size = 4096;
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
// 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));
|
||||
|
||||
|
@ -403,26 +401,19 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
|
|||
const MappingInfo* mapping = FindMapping(stack_pointer);
|
||||
if (!mapping)
|
||||
return false;
|
||||
if (stack_grows_down) {
|
||||
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;
|
||||
} else {
|
||||
const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
|
||||
*stack_len = offset > kStackToCapture ? kStackToCapture : offset;
|
||||
*stack = stack_pointer - *stack_len;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// static
|
||||
void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
|
||||
size_t length) {
|
||||
unsigned long tmp;
|
||||
unsigned long tmp = 55;
|
||||
size_t done = 0;
|
||||
static const size_t word_size = sizeof(tmp);
|
||||
uint8_t* const local = (uint8_t*) dest;
|
||||
|
@ -430,8 +421,9 @@ void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* 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)
|
||||
if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) {
|
||||
tmp = 0;
|
||||
}
|
||||
memcpy(local + done, &tmp, l);
|
||||
done += l;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ namespace google_breakpad {
|
|||
typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t;
|
||||
|
||||
// Typedef for our parsing of the auxv variables in /proc/pid/auxv.
|
||||
#if defined(__i386)
|
||||
#if defined(__i386) || defined(__ARM_EABI__)
|
||||
typedef Elf32_auxv_t elf_aux_entry;
|
||||
#elif defined(__x86_64__)
|
||||
typedef Elf64_auxv_t elf_aux_entry;
|
||||
|
@ -64,16 +64,20 @@ struct ThreadInfo {
|
|||
const void* stack; // pointer to the stack area
|
||||
size_t stack_len; // length of the stack to copy
|
||||
|
||||
user_regs_struct regs;
|
||||
user_fpregs_struct fpregs;
|
||||
#if defined(__i386)
|
||||
user_fpxregs_struct fpxregs;
|
||||
#endif
|
||||
|
||||
#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
|
||||
};
|
||||
|
||||
|
@ -141,7 +145,7 @@ class LinuxDumper {
|
|||
|
||||
mutable PageAllocator allocator_;
|
||||
|
||||
bool threads_suspened_;
|
||||
bool threads_suspended_;
|
||||
wasteful_vector<pid_t> threads_; // the ids of all the threads
|
||||
wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
|
||||
};
|
||||
|
|
|
@ -29,10 +29,13 @@
|
|||
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/memory.h"
|
||||
|
||||
using namespace google_breakpad;
|
||||
|
||||
|
@ -67,7 +70,7 @@ TEST(LinuxDumperTest, ThreadList) {
|
|||
LinuxDumper dumper(getpid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
ASSERT_GE(dumper.threads().size(), 1);
|
||||
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()) {
|
||||
|
@ -77,6 +80,55 @@ TEST(LinuxDumperTest, ThreadList) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) {
|
||||
static const int kNumberOfThreadsInHelperProgram = 5;
|
||||
char kNumberOfThreadsArgument[2];
|
||||
sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
|
||||
|
||||
pid_t child_pid = fork();
|
||||
if (child_pid == 0) {
|
||||
// Set the number of threads
|
||||
execl("src/client/linux/linux_dumper_unittest_helper",
|
||||
"linux_dumper_unittest_helper",
|
||||
kNumberOfThreadsArgument,
|
||||
NULL);
|
||||
// Kill if we get here.
|
||||
printf("Errno from exec: %d", errno);
|
||||
FAIL() << "Exec failed: " << strerror(errno);
|
||||
exit(0);
|
||||
}
|
||||
// The sleep is flaky, but prevents us from reading
|
||||
// the child process before all threads have been created.
|
||||
sleep(1);
|
||||
LinuxDumper dumper(child_pid);
|
||||
EXPECT_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.ThreadInfoGet(dumper.threads()[i], &one_thread));
|
||||
// We know the threads are in a function which has allocated exactly
|
||||
// one word off the stack to store its thread id.
|
||||
#if defined(__ARM_EABI__)
|
||||
void* process_tid_location = (void *)(one_thread.regs.uregs[11] - 8);
|
||||
#elif defined(__i386)
|
||||
void* process_tid_location = (void *)(one_thread.regs.ebp - 4);
|
||||
#elif defined(__x86_64)
|
||||
void* process_tid_location = (void *)(one_thread.regs.rbp - 4);
|
||||
#else
|
||||
#error Platform not supported!
|
||||
#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);
|
||||
}
|
||||
kill(child_pid, SIGKILL);
|
||||
}
|
||||
|
||||
TEST(LinuxDumperTest, BuildProcPath) {
|
||||
const pid_t pid = getpid();
|
||||
LinuxDumper dumper(pid);
|
||||
|
@ -106,28 +158,29 @@ TEST(LinuxDumperTest, BuildProcPath) {
|
|||
#endif
|
||||
}
|
||||
|
||||
#if !defined(__ARM_EABI__)
|
||||
TEST(LinuxDumperTest, MappingsIncludeLinuxGate) {
|
||||
LinuxDumper dumper(getpid());
|
||||
ASSERT_TRUE(dumper.Init());
|
||||
|
||||
void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid());
|
||||
if (linux_gate_loc) {
|
||||
bool found_linux_gate = false;
|
||||
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;
|
||||
}
|
||||
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));
|
||||
}
|
||||
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));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(LinuxDumperTest, FileIDsMatch) {
|
||||
// Calculate the File ID of our binary using both
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
// 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#pragma GCC optimize ("O0")
|
||||
void *thread_function(void *data) __attribute__((noinline, optimize("O2")));
|
||||
|
||||
void *thread_function(void *data) {
|
||||
pid_t thread_id = syscall(SYS_gettid);
|
||||
while (true) ;
|
||||
asm("");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
int num_threads = atoi(argv[1]);
|
||||
if (num_threads < 1) {
|
||||
fprintf(stderr, "ERROR: number of threads is 0");
|
||||
return 1;
|
||||
}
|
||||
pthread_t threads[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, NULL);
|
||||
}
|
||||
thread_function(NULL);
|
||||
return 0;
|
||||
}
|
|
@ -61,7 +61,7 @@
|
|||
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
#include "client/linux/minidump_writer/line_reader.h"
|
||||
#include "client/linux/minidump_writer//linux_dumper.h"
|
||||
#include "client/linux/minidump_writer/linux_dumper.h"
|
||||
#include "common/linux/linux_libc_support.h"
|
||||
#include "common/linux/linux_syscall_support.h"
|
||||
|
||||
|
@ -307,6 +307,54 @@ static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc,
|
|||
memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
|
||||
}
|
||||
|
||||
#elif defined(__ARMEL__)
|
||||
typedef MDRawContextARM RawContextCPU;
|
||||
|
||||
static void CPUFillFromThreadInfo(MDRawContextARM *out,
|
||||
const google_breakpad::ThreadInfo &info) {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i)
|
||||
out->iregs[i] = info.regs.uregs[i];
|
||||
// No CPSR register in ThreadInfo(it's not accessible via ptrace)
|
||||
out->cpsr = 0;
|
||||
out->float_save.fpscr = info.fpregs.fpsr |
|
||||
(static_cast<u_int64_t>(info.fpregs.fpcr) << 32);
|
||||
//TODO: sort this out, actually collect floating point registers
|
||||
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
}
|
||||
|
||||
static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc,
|
||||
const struct _libc_fpstate* fpregs) {
|
||||
out->context_flags = MD_CONTEXT_ARM_FULL;
|
||||
|
||||
out->iregs[0] = uc->uc_mcontext.arm_r0;
|
||||
out->iregs[1] = uc->uc_mcontext.arm_r1;
|
||||
out->iregs[2] = uc->uc_mcontext.arm_r2;
|
||||
out->iregs[3] = uc->uc_mcontext.arm_r3;
|
||||
out->iregs[4] = uc->uc_mcontext.arm_r4;
|
||||
out->iregs[5] = uc->uc_mcontext.arm_r5;
|
||||
out->iregs[6] = uc->uc_mcontext.arm_r6;
|
||||
out->iregs[7] = uc->uc_mcontext.arm_r7;
|
||||
out->iregs[8] = uc->uc_mcontext.arm_r8;
|
||||
out->iregs[9] = uc->uc_mcontext.arm_r9;
|
||||
out->iregs[10] = uc->uc_mcontext.arm_r10;
|
||||
|
||||
out->iregs[11] = uc->uc_mcontext.arm_fp;
|
||||
out->iregs[12] = uc->uc_mcontext.arm_ip;
|
||||
out->iregs[13] = uc->uc_mcontext.arm_sp;
|
||||
out->iregs[14] = uc->uc_mcontext.arm_lr;
|
||||
out->iregs[15] = uc->uc_mcontext.arm_pc;
|
||||
|
||||
out->cpsr = uc->uc_mcontext.arm_cpsr;
|
||||
|
||||
//TODO: fix this after fixing ExceptionHandler
|
||||
out->float_save.fpscr = 0;
|
||||
memset(&out->float_save.regs, 0, sizeof(out->float_save.regs));
|
||||
memset(&out->float_save.extra, 0, sizeof(out->float_save.extra));
|
||||
}
|
||||
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
@ -321,7 +369,12 @@ class MinidumpWriter {
|
|||
: filename_(filename),
|
||||
siginfo_(&context->siginfo),
|
||||
ucontext_(&context->context),
|
||||
#if !defined(__ARM_EABI__)
|
||||
float_state_(&context->float_state),
|
||||
#else
|
||||
//TODO: fix this after fixing ExceptionHandler
|
||||
float_state_(NULL),
|
||||
#endif
|
||||
crashing_tid_(context->tid),
|
||||
dumper_(crashing_pid) {
|
||||
}
|
||||
|
@ -612,6 +665,10 @@ class MinidumpWriter {
|
|||
uintptr_t GetStackPointer() {
|
||||
return ucontext_->uc_mcontext.gregs[REG_RSP];
|
||||
}
|
||||
#elif defined(__ARM_EABI__)
|
||||
uintptr_t GetStackPointer() {
|
||||
return ucontext_->uc_mcontext.arm_sp;
|
||||
}
|
||||
#else
|
||||
#error "This code has not been ported to your platform yet."
|
||||
#endif
|
||||
|
@ -644,6 +701,8 @@ class MinidumpWriter {
|
|||
MD_CPU_ARCHITECTURE_X86;
|
||||
#elif defined(__x86_64)
|
||||
MD_CPU_ARCHITECTURE_AMD64;
|
||||
#elif defined(__arm__)
|
||||
MD_CPU_ARCHITECTURE_ARM;
|
||||
#else
|
||||
#error "Unknown CPU arch"
|
||||
#endif
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
|
Двоичный файл не отображается.
|
@ -612,6 +612,12 @@ NSString *const kDefaultServerType = @"google";
|
|||
float emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit];
|
||||
[emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta];
|
||||
|
||||
// Localize the placeholder text.
|
||||
[[commentsEntryField_ cell]
|
||||
setPlaceholderString:NSLocalizedString(@"commentsPlaceholder", @"")];
|
||||
[[emailEntryField_ cell]
|
||||
setPlaceholderString:NSLocalizedString(@"emailPlaceholder", @"")];
|
||||
|
||||
// Localize the privacy policy label, and keep it right-aligned to the arrow.
|
||||
[privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")];
|
||||
float privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit];
|
||||
|
@ -896,8 +902,8 @@ doCommandBySelector:(SEL)commandSelector {
|
|||
forKey:@BREAKPAD_VERSION];
|
||||
[socorroDictionary_ setObject:@"ProductName"
|
||||
forKey:@BREAKPAD_PRODUCT];
|
||||
[socorroDictionary_ setObject:@"ProductName"
|
||||
forKey:@BREAKPAD_PRODUCT];
|
||||
[socorroDictionary_ setObject:@"Email"
|
||||
forKey:@BREAKPAD_EMAIL];
|
||||
}
|
||||
|
||||
- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType {
|
||||
|
|
|
@ -205,9 +205,8 @@ void ExceptionHandler::Initialize(const wstring& dump_path,
|
|||
}
|
||||
handler_stack_->push_back(this);
|
||||
|
||||
if (handler_types & HANDLER_EXCEPTION) {
|
||||
if (handler_types & HANDLER_EXCEPTION)
|
||||
previous_filter_ = SetUnhandledExceptionFilter(HandleException);
|
||||
}
|
||||
|
||||
#if _MSC_VER >= 1400 // MSVC 2005/8
|
||||
if (handler_types & HANDLER_INVALID_PARAMETER)
|
||||
|
|
|
@ -144,6 +144,32 @@ inline uint64 ByteReader::ReadAddress(const char* buffer) const {
|
|||
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__
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2006 Google Inc. All Rights Reserved.
|
||||
// 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
|
||||
|
@ -27,6 +27,7 @@
|
|||
// 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"
|
||||
|
@ -35,8 +36,9 @@ namespace dwarf2reader {
|
|||
|
||||
ByteReader::ByteReader(enum Endianness endian)
|
||||
:offset_reader_(NULL), address_reader_(NULL), endian_(endian),
|
||||
address_size_(0), offset_size_(0)
|
||||
{ }
|
||||
address_size_(0), offset_size_(0),
|
||||
have_section_base_(), have_text_base_(), have_data_base_(),
|
||||
have_function_base_() { }
|
||||
|
||||
ByteReader::~ByteReader() { }
|
||||
|
||||
|
@ -60,4 +62,184 @@ void ByteReader::SetAddressSize(uint8 size) {
|
|||
}
|
||||
}
|
||||
|
||||
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.
|
||||
size_t skew = section_base_ & (AddressSize() - 1);
|
||||
// Now find the offset from that aligned address to buffer.
|
||||
size_t offset = skew + (buffer - buffer_base_);
|
||||
// Round up to the next boundary.
|
||||
size_t 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
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2006 Google Inc. All Rights Reserved.
|
||||
// -*- 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
|
||||
|
@ -31,6 +33,7 @@
|
|||
|
||||
#include <string>
|
||||
#include "common/dwarf/types.h"
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
|
||||
namespace dwarf2reader {
|
||||
|
||||
|
@ -41,69 +44,238 @@ enum Endianness {
|
|||
ENDIANNESS_LITTLE
|
||||
};
|
||||
|
||||
// Class that knows how to read both big endian and little endian
|
||||
// numbers, for use in DWARF2/3 reader.
|
||||
// Takes an endianness argument.
|
||||
// To read addresses and offsets, SetAddressSize and SetOffsetSize
|
||||
// must be called first.
|
||||
// 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:
|
||||
explicit ByteReader(enum Endianness endian);
|
||||
// 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();
|
||||
|
||||
// Set the address size to SIZE, which sets up the ReadAddress member
|
||||
// so that it works.
|
||||
void SetAddressSize(uint8 size);
|
||||
|
||||
// Set the offset size to SIZE, which sets up the ReadOffset member
|
||||
// so that it works.
|
||||
void SetOffsetSize(uint8 size);
|
||||
|
||||
// Return the current offset size
|
||||
uint8 OffsetSize() const { return offset_size_; }
|
||||
|
||||
// Return the current address size
|
||||
uint8 AddressSize() const { return address_size_; }
|
||||
|
||||
// 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 it as an unsigned 16 bit
|
||||
// number.
|
||||
// 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 it as an unsigned 32 bit
|
||||
// number. 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.
|
||||
// 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 it as an unsigned 64 bit
|
||||
// number
|
||||
// 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. LEN is set
|
||||
// to the length read. Everybody seems to reinvent LEB128 as a
|
||||
// variable size integer encoding, DWARF has had it for a long time.
|
||||
// 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. LEN is set to the length read.
|
||||
// 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;
|
||||
|
||||
// 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.
|
||||
uint64 ReadOffset(const char* buffer) 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. 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.
|
||||
// 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.
|
||||
|
@ -125,6 +297,12 @@ class ByteReader {
|
|||
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
|
||||
|
|
|
@ -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"
|
||||
|
||||
using dwarf2reader::ByteReader;
|
||||
using dwarf2reader::DwarfPointerEncoding;
|
||||
using dwarf2reader::ENDIANNESS_BIG;
|
||||
using dwarf2reader::ENDIANNESS_LITTLE;
|
||||
using google_breakpad::CFISection;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::kBigEndian;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using std::string;
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
// 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 <cassert>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common/dwarf/cfi_assembler.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::DwarfPointerEncoding;
|
||||
|
||||
CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor,
|
||||
int data_alignment_factor,
|
||||
unsigned return_address_register,
|
||||
u_int8_t version,
|
||||
const string &augmentation,
|
||||
bool dwarf64) {
|
||||
assert(!entry_length_);
|
||||
entry_length_ = new PendingLength();
|
||||
in_fde_ = false;
|
||||
|
||||
if (dwarf64) {
|
||||
D32(0xffffffff);
|
||||
D64(entry_length_->length);
|
||||
entry_length_->start = Here();
|
||||
// Write the CIE distinguished value. In .debug_frame sections, it's
|
||||
// ~0; in .eh_frame sections, it's zero.
|
||||
D64(eh_frame_ ? 0 : ~(u_int64_t)0);
|
||||
} else {
|
||||
D32(entry_length_->length);
|
||||
entry_length_->start = Here();
|
||||
// Write the CIE distinguished value. In .debug_frame sections, it's
|
||||
// ~0; in .eh_frame sections, it's zero.
|
||||
D32(eh_frame_ ? 0 : ~(u_int32_t)0);
|
||||
}
|
||||
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,
|
||||
u_int64_t initial_location,
|
||||
u_int64_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(u_int64_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.
|
||||
u_int64_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;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,256 @@
|
|||
// -*- 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 "google_breakpad/common/breakpad_types.h"
|
||||
#include "processor/test_assembler.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::DwarfPointerEncoding;
|
||||
using google_breakpad::TestAssembler::Endianness;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using std::string;
|
||||
|
||||
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.
|
||||
u_int64_t cfi;
|
||||
|
||||
// The starting address of this file's .text section, for DW_EH_PE_textrel.
|
||||
u_int64_t text;
|
||||
|
||||
// The starting address of this file's .got or .eh_frame_hdr section,
|
||||
// for DW_EH_PE_datarel.
|
||||
u_int64_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(u_int64_t code_alignment_factor,
|
||||
int data_alignment_factor,
|
||||
unsigned return_address_register,
|
||||
u_int8_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,
|
||||
u_int64_t initial_location,
|
||||
u_int64_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(u_int64_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(u_int64_t address) {
|
||||
return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_);
|
||||
}
|
||||
CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding) {
|
||||
return EncodedPointer(address, encoding, encoded_pointer_bases_);
|
||||
}
|
||||
CFISection &EncodedPointer(u_int64_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(u_int8_t v) { Section::D8(v); return *this; }
|
||||
CFISection &D16(u_int16_t v) { Section::D16(v); return *this; }
|
||||
CFISection &D16(Label v) { Section::D16(v); return *this; }
|
||||
CFISection &D32(u_int32_t v) { Section::D32(v); return *this; }
|
||||
CFISection &D32(const Label &v) { Section::D32(v); return *this; }
|
||||
CFISection &D64(u_int64_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(u_int64_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;
|
||||
};
|
||||
|
||||
// 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.
|
||||
u_int64_t fde_start_address_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_CFI_ASSEMBLER_H_
|
|
@ -0,0 +1,186 @@
|
|||
// 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 <cassert>
|
||||
|
||||
#include "common/dwarf/dwarf2diehandler.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,
|
||||
const AttributeList& attrs) {
|
||||
// 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, attrs);
|
||||
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, attrs))
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace dwarf2reader
|
|
@ -0,0 +1,358 @@
|
|||
// -*- 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 "common/dwarf/types.h"
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
#include "common/dwarf/dwarf2reader.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) { }
|
||||
|
||||
// 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; and ATTRS is the list of attributes the DIE will have, and
|
||||
// their forms (their values are not provided).
|
||||
//
|
||||
// The default definition skips all children.
|
||||
virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList &attrs) {
|
||||
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,
|
||||
const AttributeList& attrs) { 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,
|
||||
const AttributeList &attrs);
|
||||
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 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.
|
||||
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__
|
|
@ -0,0 +1,560 @@
|
|||
// -*- 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 "breakpad_googletest_includes.h"
|
||||
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
|
||||
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::AttributeList;
|
||||
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_METHOD0(EndAttributes, bool());
|
||||
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
||||
const AttributeList &));
|
||||
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_METHOD0(EndAttributes, bool());
|
||||
MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag,
|
||||
const AttributeList &));
|
||||
MOCK_METHOD0(Finish, void());
|
||||
MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8));
|
||||
MOCK_METHOD3(StartRootDIE, bool(uint64, DwarfTag, const AttributeList &));
|
||||
};
|
||||
|
||||
// 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);
|
||||
|
||||
AttributeList mock_attribute_list;
|
||||
mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
|
||||
dwarf2reader::DW_FORM_string));
|
||||
|
||||
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,
|
||||
ContainerEq(mock_attribute_list)))
|
||||
.InSequence(s)
|
||||
.WillOnce(Return(false));
|
||||
|
||||
EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,
|
||||
0xf4, 0x02,
|
||||
0xb00febffa76e2b2bLL, 0x5c));
|
||||
EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL,
|
||||
(DwarfTag) 0xb4f98da6,
|
||||
mock_attribute_list));
|
||||
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);
|
||||
|
||||
AttributeList mock_attribute_list;
|
||||
|
||||
{
|
||||
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,
|
||||
ContainerEq(mock_attribute_list)))
|
||||
.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,
|
||||
mock_attribute_list));
|
||||
EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL,
|
||||
(DwarfTag) 0xc3a17bba,
|
||||
mock_attribute_list));
|
||||
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);
|
||||
|
||||
AttributeList mock_attribute_list;
|
||||
mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name,
|
||||
dwarf2reader::DW_FORM_string));
|
||||
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,
|
||||
ContainerEq(mock_attribute_list)))
|
||||
.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, 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,
|
||||
mock_attribute_list));
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
AttributeList root_attribute_list;
|
||||
root_attribute_list.push_back(make_pair((DwarfAttribute) 0xb01185df,
|
||||
(DwarfForm) 0xbc97cee8));
|
||||
AttributeList child1_attribute_list;
|
||||
child1_attribute_list.push_back(make_pair((DwarfAttribute) 0x41014e43,
|
||||
(DwarfForm) 0x63155f4c));
|
||||
AttributeList grandchild1_attribute_list;
|
||||
grandchild1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf72f823c,
|
||||
(DwarfForm) 0x0ff6a201));
|
||||
AttributeList greatgrandchild1_attribute_list;
|
||||
greatgrandchild1_attribute_list
|
||||
.push_back(make_pair((DwarfAttribute) 0xbe66e5f0, (DwarfForm) 0xb4b24ff7));
|
||||
AttributeList child2_attribute_list;
|
||||
child1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf22df14c,
|
||||
(DwarfForm) 0x20676e7d));
|
||||
AttributeList child3_attribute_list;
|
||||
child3_attribute_list.push_back(make_pair((DwarfAttribute) 0xe8bf1201,
|
||||
(DwarfForm) 0x53a5b7a8));
|
||||
|
||||
{
|
||||
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,
|
||||
ContainerEq(root_attribute_list)))
|
||||
.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,
|
||||
ContainerEq(child1_attribute_list)))
|
||||
.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,
|
||||
ContainerEq(child2_attribute_list)))
|
||||
.WillOnce(Return((DIEHandler *) NULL));
|
||||
|
||||
// Third child DIE.
|
||||
EXPECT_CALL(mock_root_handler,
|
||||
FindChildHandler(0x753c964c8ab538aeLL,
|
||||
(DwarfTag) 0x8c22970e,
|
||||
ContainerEq(child3_attribute_list)))
|
||||
.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,
|
||||
root_attribute_list));
|
||||
die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL,
|
||||
(DwarfAttribute) 0xf779a642,
|
||||
(DwarfForm) 0x2cb63027,
|
||||
0x18e744661769d08fLL);
|
||||
|
||||
// First child DIE.
|
||||
{
|
||||
EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL,
|
||||
(DwarfTag) 0xac2cbd8c,
|
||||
child1_attribute_list));
|
||||
die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL,
|
||||
(DwarfAttribute) 0xa6fd6f65,
|
||||
(DwarfForm) 0xe4f64c41,
|
||||
0x1b04e5444a55fe67LL);
|
||||
|
||||
// First grandchild DIE. Will be skipped.
|
||||
{
|
||||
EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL,
|
||||
(DwarfTag) 0x22f05a15,
|
||||
grandchild1_attribute_list));
|
||||
// First great-grandchild DIE. Will be skipped without being
|
||||
// mentioned to any handler.
|
||||
{
|
||||
EXPECT_FALSE(die_dispatcher
|
||||
.StartDIE(0xb3076285d25cac25LL,
|
||||
(DwarfTag) 0xcff4061b,
|
||||
greatgrandchild1_attribute_list));
|
||||
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,
|
||||
child2_attribute_list));
|
||||
die_dispatcher.EndDIE(0x97412be24875de9dLL);
|
||||
}
|
||||
|
||||
// Third child DIE.
|
||||
{
|
||||
EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL,
|
||||
(DwarfTag) 0x8c22970e,
|
||||
child3_attribute_list));
|
||||
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);
|
||||
AttributeList empty_attribute_list;
|
||||
|
||||
{
|
||||
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,
|
||||
ContainerEq(empty_attribute_list)))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_root_handler, EndAttributes())
|
||||
.WillOnce(Return(true));
|
||||
|
||||
// Child DIE.
|
||||
EXPECT_CALL(mock_root_handler,
|
||||
FindChildHandler(0x058f09240c5fc8c9LL,
|
||||
(DwarfTag) 0x898bf0d0,
|
||||
ContainerEq(empty_attribute_list)))
|
||||
.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,
|
||||
ContainerEq(empty_attribute_list)))
|
||||
.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,
|
||||
empty_attribute_list));
|
||||
|
||||
// Child DIE.
|
||||
{
|
||||
EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL,
|
||||
(DwarfTag) 0x898bf0d0,
|
||||
empty_attribute_list));
|
||||
|
||||
// Grandchild DIE.
|
||||
{
|
||||
EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL,
|
||||
(DwarfTag) 0x2802d007,
|
||||
empty_attribute_list));
|
||||
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...
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2006 Google Inc. All Rights Reserved.
|
||||
// -*- 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
|
||||
|
@ -486,5 +488,126 @@ enum DwarfOpcode {
|
|||
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 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__
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2006 Google Inc. All Rights Reserved.
|
||||
// -*- 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
|
||||
|
@ -26,6 +28,8 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// CFI reader author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// This file contains definitions related to the DWARF2/3 reader and
|
||||
// it's handler interfaces.
|
||||
// The DWARF2/3 specification can be found at
|
||||
|
@ -42,6 +46,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/dwarf2enums.h"
|
||||
#include "common/dwarf/types.h"
|
||||
|
||||
|
@ -49,7 +54,6 @@ using namespace std;
|
|||
|
||||
namespace dwarf2reader {
|
||||
struct LineStateMachine;
|
||||
class ByteReader;
|
||||
class Dwarf2Handler;
|
||||
class LineInfoHandler;
|
||||
|
||||
|
@ -167,12 +171,13 @@ class LineInfoHandler {
|
|||
uint64 length) { }
|
||||
|
||||
// Called when the line info reader has a new line, address pair
|
||||
// ready for us. ADDRESS is the address of the code, 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, uint32 file_num, uint32 line_num,
|
||||
uint32 column_num) { }
|
||||
// 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) { }
|
||||
};
|
||||
|
||||
// The base of DWARF2/3 debug info is a DIE (Debugging Information
|
||||
|
@ -358,6 +363,16 @@ class Dwarf2Handler {
|
|||
enum DwarfForm form,
|
||||
int64 data) { }
|
||||
|
||||
// Called when we have an attribute whose value is a reference to
|
||||
// another DIE. The attribute belongs to the DIE at OFFSET from the
|
||||
// beginning of the .debug_info section. Its name is ATTR, its form
|
||||
// is FORM, and the offset of the DIE being referred to from the
|
||||
// beginning of the .debug_info section is DATA.
|
||||
virtual void ProcessAttributeReference(uint64 offset,
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) { }
|
||||
|
||||
// Called when we have an attribute with a buffer of data to give to our
|
||||
// handler. The attribute is for the DIE at OFFSET from the beginning of the
|
||||
// .debug_info section. Its name is ATTR, its form is FORM, DATA points to
|
||||
|
@ -387,6 +402,640 @@ class Dwarf2Handler {
|
|||
|
||||
};
|
||||
|
||||
// This class is a reader for DWARF's Call Frame Information. CFI
|
||||
// describes how to unwind stack frames --- even for functions that do
|
||||
// not follow fixed conventions for saving registers, whose frame size
|
||||
// varies as they execute, etc.
|
||||
//
|
||||
// CFI describes, at each machine instruction, how to compute the
|
||||
// stack frame's base address, how to find the return address, and
|
||||
// where to find the saved values of the caller's registers (if the
|
||||
// callee has stashed them somewhere to free up the registers for its
|
||||
// own use).
|
||||
//
|
||||
// For example, suppose we have a function whose machine code looks
|
||||
// like this (imagine an assembly language that looks like C, for a
|
||||
// machine with 32-bit registers, and a stack that grows towards lower
|
||||
// addresses):
|
||||
//
|
||||
// func: ; entry point; return address at sp
|
||||
// func+0: sp = sp - 16 ; allocate space for stack frame
|
||||
// func+1: sp[12] = r0 ; save r0 at sp+12
|
||||
// ... ; other code, not frame-related
|
||||
// func+10: sp -= 4; *sp = x ; push some x on the stack
|
||||
// ... ; other code, not frame-related
|
||||
// func+20: r0 = sp[16] ; restore saved r0
|
||||
// func+21: sp += 20 ; pop whole stack frame
|
||||
// func+22: pc = *sp; sp += 4 ; pop return address and jump to it
|
||||
//
|
||||
// DWARF CFI is (a very compressed representation of) a table with a
|
||||
// row for each machine instruction address and a column for each
|
||||
// register showing how to restore it, if possible.
|
||||
//
|
||||
// A special column named "CFA", for "Canonical Frame Address", tells how
|
||||
// to compute the base address of the frame; registers' entries may
|
||||
// refer to the CFA in describing where the registers are saved.
|
||||
//
|
||||
// Another special column, named "RA", represents the return address.
|
||||
//
|
||||
// For example, here is a complete (uncompressed) table describing the
|
||||
// function above:
|
||||
//
|
||||
// insn cfa r0 r1 ... ra
|
||||
// =======================================
|
||||
// func+0: sp cfa[0]
|
||||
// func+1: sp+16 cfa[0]
|
||||
// func+2: sp+16 cfa[-4] cfa[0]
|
||||
// func+11: sp+20 cfa[-4] cfa[0]
|
||||
// func+21: sp+20 cfa[0]
|
||||
// func+22: sp cfa[0]
|
||||
//
|
||||
// Some things to note here:
|
||||
//
|
||||
// - Each row describes the state of affairs *before* executing the
|
||||
// instruction at the given address. Thus, the row for func+0
|
||||
// describes the state before we allocate the stack frame. In the
|
||||
// next row, the formula for computing the CFA has changed,
|
||||
// reflecting that allocation.
|
||||
//
|
||||
// - The other entries are written in terms of the CFA; this allows
|
||||
// them to remain unchanged as the stack pointer gets bumped around.
|
||||
// For example, the rule for recovering the return address (the "ra"
|
||||
// column) remains unchanged throughout the function, even as the
|
||||
// stack pointer takes on three different offsets from the return
|
||||
// address.
|
||||
//
|
||||
// - Although we haven't shown it, most calling conventions designate
|
||||
// "callee-saves" and "caller-saves" registers. The callee must
|
||||
// preserve the values of callee-saves registers; if it uses them,
|
||||
// it must save their original values somewhere, and restore them
|
||||
// before it returns. In contrast, the callee is free to trash
|
||||
// caller-saves registers; if the callee uses these, it will
|
||||
// probably not bother to save them anywhere, and the CFI will
|
||||
// probably mark their values as "unrecoverable".
|
||||
//
|
||||
// (However, since the caller cannot assume the callee was going to
|
||||
// save them, caller-saves registers are probably dead in the caller
|
||||
// anyway, so compilers usually don't generate CFA for caller-saves
|
||||
// registers.)
|
||||
//
|
||||
// - Exactly where the CFA points is a matter of convention that
|
||||
// depends on the architecture and ABI in use. In the example, the
|
||||
// CFA is the value the stack pointer had upon entry to the
|
||||
// function, pointing at the saved return address. But on the x86,
|
||||
// the call frame information generated by GCC follows the
|
||||
// convention that the CFA is the address *after* the saved return
|
||||
// address.
|
||||
//
|
||||
// But by definition, the CFA remains constant throughout the
|
||||
// lifetime of the frame. This makes it a useful value for other
|
||||
// columns to refer to. It is also gives debuggers a useful handle
|
||||
// for identifying a frame.
|
||||
//
|
||||
// If you look at the table above, you'll notice that a given entry is
|
||||
// often the same as the one immediately above it: most instructions
|
||||
// change only one or two aspects of the stack frame, if they affect
|
||||
// it at all. The DWARF format takes advantage of this fact, and
|
||||
// reduces the size of the data by mentioning only the addresses and
|
||||
// columns at which changes take place. So for the above, DWARF CFI
|
||||
// data would only actually mention the following:
|
||||
//
|
||||
// insn cfa r0 r1 ... ra
|
||||
// =======================================
|
||||
// func+0: sp cfa[0]
|
||||
// func+1: sp+16
|
||||
// func+2: cfa[-4]
|
||||
// func+11: sp+20
|
||||
// func+21: r0
|
||||
// func+22: sp
|
||||
//
|
||||
// In fact, this is the way the parser reports CFI to the consumer: as
|
||||
// a series of statements of the form, "At address X, column Y changed
|
||||
// to Z," and related conventions for describing the initial state.
|
||||
//
|
||||
// Naturally, it would be impractical to have to scan the entire
|
||||
// program's CFI, noting changes as we go, just to recover the
|
||||
// unwinding rules in effect at one particular instruction. To avoid
|
||||
// this, CFI data is grouped into "entries", each of which covers a
|
||||
// specified range of addresses and begins with a complete statement
|
||||
// of the rules for all recoverable registers at that starting
|
||||
// address. Each entry typically covers a single function.
|
||||
//
|
||||
// Thus, to compute the contents of a given row of the table --- that
|
||||
// is, rules for recovering the CFA, RA, and registers at a given
|
||||
// instruction --- the consumer should find the entry that covers that
|
||||
// instruction's address, start with the initial state supplied at the
|
||||
// beginning of the entry, and work forward until it has processed all
|
||||
// the changes up to and including those for the present instruction.
|
||||
//
|
||||
// There are seven kinds of rules that can appear in an entry of the
|
||||
// table:
|
||||
//
|
||||
// - "undefined": The given register is not preserved by the callee;
|
||||
// its value cannot be recovered.
|
||||
//
|
||||
// - "same value": This register has the same value it did in the callee.
|
||||
//
|
||||
// - offset(N): The register is saved at offset N from the CFA.
|
||||
//
|
||||
// - val_offset(N): The value the register had in the caller is the
|
||||
// CFA plus offset N. (This is usually only useful for describing
|
||||
// the stack pointer.)
|
||||
//
|
||||
// - register(R): The register's value was saved in another register R.
|
||||
//
|
||||
// - expression(E): Evaluating the DWARF expression E using the
|
||||
// current frame's registers' values yields the address at which the
|
||||
// register was saved.
|
||||
//
|
||||
// - val_expression(E): Evaluating the DWARF expression E using the
|
||||
// current frame's registers' values yields the value the register
|
||||
// had in the caller.
|
||||
|
||||
class CallFrameInfo {
|
||||
public:
|
||||
// The different kinds of entries one finds in CFI. Used internally,
|
||||
// and for error reporting.
|
||||
enum EntryKind { kUnknown, kCIE, kFDE, kTerminator };
|
||||
|
||||
// The handler class to which the parser hands the parsed call frame
|
||||
// information. Defined below.
|
||||
class Handler;
|
||||
|
||||
// A reporter class, which CallFrameInfo uses to report errors
|
||||
// encountered while parsing call frame information. Defined below.
|
||||
class Reporter;
|
||||
|
||||
// Create a DWARF CFI parser. BUFFER points to the contents of the
|
||||
// .debug_frame section to parse; BUFFER_LENGTH is its length in bytes.
|
||||
// REPORTER is an error reporter the parser should use to report
|
||||
// problems. READER is a ByteReader instance that has the endianness and
|
||||
// address size set properly. Report the data we find to HANDLER.
|
||||
//
|
||||
// This class can also parse Linux C++ exception handling data, as found
|
||||
// in '.eh_frame' sections. This data is a variant of DWARF CFI that is
|
||||
// placed in loadable segments so that it is present in the program's
|
||||
// address space, and is interpreted by the C++ runtime to search the
|
||||
// call stack for a handler interested in the exception being thrown,
|
||||
// actually pop the frames, and find cleanup code to run.
|
||||
//
|
||||
// There are two differences between the call frame information described
|
||||
// in the DWARF standard and the exception handling data Linux places in
|
||||
// the .eh_frame section:
|
||||
//
|
||||
// - Exception handling data uses uses a different format for call frame
|
||||
// information entry headers. The distinguished CIE id, the way FDEs
|
||||
// refer to their CIEs, and the way the end of the series of entries is
|
||||
// determined are all slightly different.
|
||||
//
|
||||
// If the constructor's EH_FRAME argument is true, then the
|
||||
// CallFrameInfo parses the entry headers as Linux C++ exception
|
||||
// handling data. If EH_FRAME is false or omitted, the CallFrameInfo
|
||||
// parses standard DWARF call frame information.
|
||||
//
|
||||
// - Linux C++ exception handling data uses CIE augmentation strings
|
||||
// beginning with 'z' to specify the presence of additional data after
|
||||
// the CIE and FDE headers and special encodings used for addresses in
|
||||
// frame description entries.
|
||||
//
|
||||
// CallFrameInfo can handle 'z' augmentations in either DWARF CFI or
|
||||
// exception handling data if you have supplied READER with the base
|
||||
// addresses needed to interpret the pointer encodings that 'z'
|
||||
// augmentations can specify. See the ByteReader interface for details
|
||||
// about the base addresses. See the CallFrameInfo::Handler interface
|
||||
// for details about the additional information one might find in
|
||||
// 'z'-augmented data.
|
||||
//
|
||||
// Thus:
|
||||
//
|
||||
// - If you are parsing standard DWARF CFI, as found in a .debug_frame
|
||||
// section, you should pass false for the EH_FRAME argument, or omit
|
||||
// it, and you need not worry about providing READER with the
|
||||
// additional base addresses.
|
||||
//
|
||||
// - If you want to parse Linux C++ exception handling data from a
|
||||
// .eh_frame section, you should pass EH_FRAME as true, and call
|
||||
// READER's Set*Base member functions before calling our Start method.
|
||||
//
|
||||
// - If you want to parse DWARF CFI that uses the 'z' augmentations
|
||||
// (although I don't think any toolchain ever emits such data), you
|
||||
// could pass false for EH_FRAME, but call READER's Set*Base members.
|
||||
//
|
||||
// The extensions the Linux C++ ABI makes to DWARF for exception
|
||||
// handling are described here, rather poorly:
|
||||
// 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
|
||||
//
|
||||
// The mechanics of C++ exception handling, personality routines,
|
||||
// and language-specific data areas are described here, rather nicely:
|
||||
// http://www.codesourcery.com/public/cxx-abi/abi-eh.html
|
||||
CallFrameInfo(const char *buffer, size_t buffer_length,
|
||||
ByteReader *reader, Handler *handler, Reporter *reporter,
|
||||
bool eh_frame = false)
|
||||
: buffer_(buffer), buffer_length_(buffer_length),
|
||||
reader_(reader), handler_(handler), reporter_(reporter),
|
||||
eh_frame_(eh_frame) { }
|
||||
|
||||
~CallFrameInfo() { }
|
||||
|
||||
// Parse the entries in BUFFER, reporting what we find to HANDLER.
|
||||
// Return true if we reach the end of the section successfully, or
|
||||
// false if we encounter an error.
|
||||
bool Start();
|
||||
|
||||
// Return the textual name of KIND. For error reporting.
|
||||
static const char *KindName(EntryKind kind);
|
||||
|
||||
private:
|
||||
|
||||
struct CIE;
|
||||
|
||||
// A CFI entry, either an FDE or a CIE.
|
||||
struct Entry {
|
||||
// The starting offset of the entry in the section, for error
|
||||
// reporting.
|
||||
size_t offset;
|
||||
|
||||
// The start of this entry in the buffer.
|
||||
const char *start;
|
||||
|
||||
// Which kind of entry this is.
|
||||
//
|
||||
// We want to be able to use this for error reporting even while we're
|
||||
// in the midst of parsing. Error reporting code may assume that kind,
|
||||
// offset, and start fields are valid, although kind may be kUnknown.
|
||||
EntryKind kind;
|
||||
|
||||
// The end of this entry's common prologue (initial length and id), and
|
||||
// the start of this entry's kind-specific fields.
|
||||
const char *fields;
|
||||
|
||||
// The start of this entry's instructions.
|
||||
const char *instructions;
|
||||
|
||||
// The address past the entry's last byte in the buffer. (Note that
|
||||
// since offset points to the entry's initial length field, and the
|
||||
// length field is the number of bytes after that field, this is not
|
||||
// simply buffer_ + offset + length.)
|
||||
const char *end;
|
||||
|
||||
// For both DWARF CFI and .eh_frame sections, this is the CIE id in a
|
||||
// CIE, and the offset of the associated CIE in an FDE.
|
||||
uint64 id;
|
||||
|
||||
// The CIE that applies to this entry, if we've parsed it. If this is a
|
||||
// CIE, then this field points to this structure.
|
||||
CIE *cie;
|
||||
};
|
||||
|
||||
// A common information entry (CIE).
|
||||
struct CIE: public Entry {
|
||||
uint8 version; // CFI data version number
|
||||
string augmentation; // vendor format extension markers
|
||||
uint64 code_alignment_factor; // scale for code address adjustments
|
||||
int data_alignment_factor; // scale for stack pointer adjustments
|
||||
unsigned return_address_register; // which register holds the return addr
|
||||
|
||||
// True if this CIE includes Linux C++ ABI 'z' augmentation data.
|
||||
bool has_z_augmentation;
|
||||
|
||||
// Parsed 'z' augmentation data. These are meaningful only if
|
||||
// has_z_augmentation is true.
|
||||
bool has_z_lsda; // The 'z' augmentation included 'L'.
|
||||
bool has_z_personality; // The 'z' augmentation included 'P'.
|
||||
bool has_z_signal_frame; // The 'z' augmentation included 'S'.
|
||||
|
||||
// If has_z_lsda is true, this is the encoding to be used for language-
|
||||
// specific data area pointers in FDEs.
|
||||
DwarfPointerEncoding lsda_encoding;
|
||||
|
||||
// If has_z_personality is true, this is the encoding used for the
|
||||
// personality routine pointer in the augmentation data.
|
||||
DwarfPointerEncoding personality_encoding;
|
||||
|
||||
// If has_z_personality is true, this is the address of the personality
|
||||
// routine --- or, if personality_encoding & DW_EH_PE_indirect, the
|
||||
// address where the personality routine's address is stored.
|
||||
uint64 personality_address;
|
||||
|
||||
// This is the encoding used for addresses in the FDE header and
|
||||
// in DW_CFA_set_loc instructions. This is always valid, whether
|
||||
// or not we saw a 'z' augmentation string; its default value is
|
||||
// DW_EH_PE_absptr, which is what normal DWARF CFI uses.
|
||||
DwarfPointerEncoding pointer_encoding;
|
||||
};
|
||||
|
||||
// A frame description entry (FDE).
|
||||
struct FDE: public Entry {
|
||||
uint64 address; // start address of described code
|
||||
uint64 size; // size of described code, in bytes
|
||||
|
||||
// If cie->has_z_lsda is true, then this is the language-specific data
|
||||
// area's address --- or its address's address, if cie->lsda_encoding
|
||||
// has the DW_EH_PE_indirect bit set.
|
||||
uint64 lsda_address;
|
||||
};
|
||||
|
||||
// Internal use.
|
||||
class Rule;
|
||||
class UndefinedRule;
|
||||
class SameValueRule;
|
||||
class OffsetRule;
|
||||
class ValOffsetRule;
|
||||
class RegisterRule;
|
||||
class ExpressionRule;
|
||||
class ValExpressionRule;
|
||||
class RuleMap;
|
||||
class State;
|
||||
|
||||
// Parse the initial length and id of a CFI entry, either a CIE, an FDE,
|
||||
// or a .eh_frame end-of-data mark. CURSOR points to the beginning of the
|
||||
// data to parse. On success, populate ENTRY as appropriate, and return
|
||||
// true. On failure, report the problem, and return false. Even if we
|
||||
// return false, set ENTRY->end to the first byte after the entry if we
|
||||
// were able to figure that out, or NULL if we weren't.
|
||||
bool ReadEntryPrologue(const char *cursor, Entry *entry);
|
||||
|
||||
// Parse the fields of a CIE after the entry prologue, including any 'z'
|
||||
// augmentation data. Assume that the 'Entry' fields of CIE are
|
||||
// populated; use CIE->fields and CIE->end as the start and limit for
|
||||
// parsing. On success, populate the rest of *CIE, and return true; on
|
||||
// failure, report the problem and return false.
|
||||
bool ReadCIEFields(CIE *cie);
|
||||
|
||||
// Parse the fields of an FDE after the entry prologue, including any 'z'
|
||||
// augmentation data. Assume that the 'Entry' fields of *FDE are
|
||||
// initialized; use FDE->fields and FDE->end as the start and limit for
|
||||
// parsing. Assume that FDE->cie is fully initialized. On success,
|
||||
// populate the rest of *FDE, and return true; on failure, report the
|
||||
// problem and return false.
|
||||
bool ReadFDEFields(FDE *fde);
|
||||
|
||||
// Report that ENTRY is incomplete, and return false. This is just a
|
||||
// trivial wrapper for invoking reporter_->Incomplete; it provides a
|
||||
// little brevity.
|
||||
bool ReportIncomplete(Entry *entry);
|
||||
|
||||
// Return true if ENCODING has the DW_EH_PE_indirect bit set.
|
||||
static bool IsIndirectEncoding(DwarfPointerEncoding encoding) {
|
||||
return encoding & DW_EH_PE_indirect;
|
||||
}
|
||||
|
||||
// The contents of the DWARF .debug_info section we're parsing.
|
||||
const char *buffer_;
|
||||
size_t buffer_length_;
|
||||
|
||||
// For reading multi-byte values with the appropriate endianness.
|
||||
ByteReader *reader_;
|
||||
|
||||
// The handler to which we should report the data we find.
|
||||
Handler *handler_;
|
||||
|
||||
// For reporting problems in the info we're parsing.
|
||||
Reporter *reporter_;
|
||||
|
||||
// True if we are processing .eh_frame-format data.
|
||||
bool eh_frame_;
|
||||
};
|
||||
|
||||
// The handler class for CallFrameInfo. The a CFI parser calls the
|
||||
// member functions of a handler object to report the data it finds.
|
||||
class CallFrameInfo::Handler {
|
||||
public:
|
||||
// The pseudo-register number for the canonical frame address.
|
||||
enum { kCFARegister = -1 };
|
||||
|
||||
Handler() { }
|
||||
virtual ~Handler() { }
|
||||
|
||||
// The parser has found CFI for the machine code at ADDRESS,
|
||||
// extending for LENGTH bytes. OFFSET is the offset of the frame
|
||||
// description entry in the section, for use in error messages.
|
||||
// VERSION is the version number of the CFI format. AUGMENTATION is
|
||||
// a string describing any producer-specific extensions present in
|
||||
// the data. RETURN_ADDRESS is the number of the register that holds
|
||||
// the address to which the function should return.
|
||||
//
|
||||
// Entry should return true to process this CFI, or false to skip to
|
||||
// the next entry.
|
||||
//
|
||||
// The parser invokes Entry for each Frame Description Entry (FDE)
|
||||
// it finds. The parser doesn't report Common Information Entries
|
||||
// to the handler explicitly; instead, if the handler elects to
|
||||
// process a given FDE, the parser reiterates the appropriate CIE's
|
||||
// contents at the beginning of the FDE's rules.
|
||||
virtual bool Entry(size_t offset, uint64 address, uint64 length,
|
||||
uint8 version, const string &augmentation,
|
||||
unsigned return_address) = 0;
|
||||
|
||||
// When the Entry function returns true, the parser calls these
|
||||
// handler functions repeatedly to describe the rules for recovering
|
||||
// registers at each instruction in the given range of machine code.
|
||||
// Immediately after a call to Entry, the handler should assume that
|
||||
// the rule for each callee-saves register is "unchanged" --- that
|
||||
// is, that the register still has the value it had in the caller.
|
||||
//
|
||||
// If a *Rule function returns true, we continue processing this entry's
|
||||
// instructions. If a *Rule function returns false, we stop evaluating
|
||||
// instructions, and skip to the next entry. Either way, we call End
|
||||
// before going on to the next entry.
|
||||
//
|
||||
// In all of these functions, if the REG parameter is kCFARegister, then
|
||||
// the rule describes how to find the canonical frame address.
|
||||
// kCFARegister may be passed as a BASE_REGISTER argument, meaning that
|
||||
// the canonical frame address should be used as the base address for the
|
||||
// computation. All other REG values will be positive.
|
||||
|
||||
// At ADDRESS, register REG's value is not recoverable.
|
||||
virtual bool UndefinedRule(uint64 address, int reg) = 0;
|
||||
|
||||
// At ADDRESS, register REG's value is the same as that it had in
|
||||
// the caller.
|
||||
virtual bool SameValueRule(uint64 address, int reg) = 0;
|
||||
|
||||
// At ADDRESS, register REG has been saved at offset OFFSET from
|
||||
// BASE_REGISTER.
|
||||
virtual bool OffsetRule(uint64 address, int reg,
|
||||
int base_register, long offset) = 0;
|
||||
|
||||
// At ADDRESS, the caller's value of register REG is the current
|
||||
// value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an
|
||||
// address at which the register's value is saved.)
|
||||
virtual bool ValOffsetRule(uint64 address, int reg,
|
||||
int base_register, long offset) = 0;
|
||||
|
||||
// At ADDRESS, register REG has been saved in BASE_REGISTER. This differs
|
||||
// from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that
|
||||
// BASE_REGISTER is the "home" for REG's saved value: if you want to
|
||||
// assign to a variable whose home is REG in the calling frame, you
|
||||
// should put the value in BASE_REGISTER.
|
||||
virtual bool RegisterRule(uint64 address, int reg, int base_register) = 0;
|
||||
|
||||
// At ADDRESS, the DWARF expression EXPRESSION yields the address at
|
||||
// which REG was saved.
|
||||
virtual bool ExpressionRule(uint64 address, int reg,
|
||||
const string &expression) = 0;
|
||||
|
||||
// At ADDRESS, the DWARF expression EXPRESSION yields the caller's
|
||||
// value for REG. (This rule doesn't provide an address at which the
|
||||
// register's value is saved.)
|
||||
virtual bool ValExpressionRule(uint64 address, int reg,
|
||||
const string &expression) = 0;
|
||||
|
||||
// Indicate that the rules for the address range reported by the
|
||||
// last call to Entry are complete. End should return true if
|
||||
// everything is okay, or false if an error has occurred and parsing
|
||||
// should stop.
|
||||
virtual bool End() = 0;
|
||||
|
||||
// Handler functions for Linux C++ exception handling data. These are
|
||||
// only called if the data includes 'z' augmentation strings.
|
||||
|
||||
// The Linux C++ ABI uses an extension of the DWARF CFI format to
|
||||
// walk the stack to propagate exceptions from the throw to the
|
||||
// appropriate catch, and do the appropriate cleanups along the way.
|
||||
// CFI entries used for exception handling have two additional data
|
||||
// associated with them:
|
||||
//
|
||||
// - The "language-specific data area" describes which exception
|
||||
// types the function has 'catch' clauses for, and indicates how
|
||||
// to go about re-entering the function at the appropriate catch
|
||||
// clause. If the exception is not caught, it describes the
|
||||
// destructors that must run before the frame is popped.
|
||||
//
|
||||
// - The "personality routine" is responsible for interpreting the
|
||||
// language-specific data area's contents, and deciding whether
|
||||
// the exception should continue to propagate down the stack,
|
||||
// perhaps after doing some cleanup for this frame, or whether the
|
||||
// exception will be caught here.
|
||||
//
|
||||
// In principle, the language-specific data area is opaque to
|
||||
// everybody but the personality routine. In practice, these values
|
||||
// may be useful or interesting to readers with extra context, and
|
||||
// we have to at least skip them anyway, so we might as well report
|
||||
// them to the handler.
|
||||
|
||||
// This entry's exception handling personality routine's address is
|
||||
// ADDRESS. If INDIRECT is true, then ADDRESS is the address at
|
||||
// which the routine's address is stored. The default definition for
|
||||
// this handler function simply returns true, allowing parsing of
|
||||
// the entry to continue.
|
||||
virtual bool PersonalityRoutine(uint64 address, bool indirect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This entry's language-specific data area (LSDA) is located at
|
||||
// ADDRESS. If INDIRECT is true, then ADDRESS is the address at
|
||||
// which the area's address is stored. The default definition for
|
||||
// this handler function simply returns true, allowing parsing of
|
||||
// the entry to continue.
|
||||
virtual bool LanguageSpecificDataArea(uint64 address, bool indirect) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// This entry describes a signal trampoline --- this frame is the
|
||||
// caller of a signal handler. The default definition for this
|
||||
// handler function simply returns true, allowing parsing of the
|
||||
// entry to continue.
|
||||
//
|
||||
// The best description of the rationale for and meaning of signal
|
||||
// trampoline CFI entries seems to be in the GCC bug database:
|
||||
// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26208
|
||||
virtual bool SignalHandler() { return true; }
|
||||
};
|
||||
|
||||
// The CallFrameInfo class makes calls on an instance of this class to
|
||||
// report errors or warn about problems in the data it is parsing. The
|
||||
// default definitions of these methods print a message to stderr, but
|
||||
// you can make a derived class that overrides them.
|
||||
class CallFrameInfo::Reporter {
|
||||
public:
|
||||
// Create an error reporter which attributes troubles to the section
|
||||
// named SECTION in FILENAME.
|
||||
//
|
||||
// Normally SECTION would be .debug_frame, but the Mac puts CFI data
|
||||
// in a Mach-O section named __debug_frame. If we support
|
||||
// Linux-style exception handling data, we could be reading an
|
||||
// .eh_frame section.
|
||||
Reporter(const string &filename,
|
||||
const string §ion = ".debug_frame")
|
||||
: filename_(filename), section_(section) { }
|
||||
virtual ~Reporter() { }
|
||||
|
||||
// The CFI entry at OFFSET ends too early to be well-formed. KIND
|
||||
// indicates what kind of entry it is; KIND can be kUnknown if we
|
||||
// haven't parsed enough of the entry to tell yet.
|
||||
virtual void Incomplete(uint64 offset, CallFrameInfo::EntryKind kind);
|
||||
|
||||
// The .eh_frame data has a four-byte zero at OFFSET where the next
|
||||
// entry's length would be; this is a terminator. However, the buffer
|
||||
// length as given to the CallFrameInfo constructor says there should be
|
||||
// more data.
|
||||
virtual void EarlyEHTerminator(uint64 offset);
|
||||
|
||||
// The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the
|
||||
// section is not that large.
|
||||
virtual void CIEPointerOutOfRange(uint64 offset, uint64 cie_offset);
|
||||
|
||||
// The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry
|
||||
// there is not a CIE.
|
||||
virtual void BadCIEId(uint64 offset, uint64 cie_offset);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with version number VERSION,
|
||||
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
||||
// a version number we don't recognize.
|
||||
virtual void UnrecognizedVersion(uint64 offset, int version);
|
||||
|
||||
// The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION,
|
||||
// which we don't recognize. We cannot parse DWARF CFI if it uses
|
||||
// augmentations we don't recognize.
|
||||
virtual void UnrecognizedAugmentation(uint64 offset,
|
||||
const string &augmentation);
|
||||
|
||||
// The pointer encoding ENCODING, specified by the CIE at OFFSET, is not
|
||||
// a valid encoding.
|
||||
virtual void InvalidPointerEncoding(uint64 offset, uint8 encoding);
|
||||
|
||||
// The pointer encoding ENCODING, specified by the CIE at OFFSET, depends
|
||||
// on a base address which has not been supplied.
|
||||
virtual void UnusablePointerEncoding(uint64 offset, uint8 encoding);
|
||||
|
||||
// The CIE at OFFSET contains a DW_CFA_restore instruction at
|
||||
// INSN_OFFSET, which may not appear in a CIE.
|
||||
virtual void RestoreInCIE(uint64 offset, uint64 insn_offset);
|
||||
|
||||
// The entry at OFFSET, of kind KIND, has an unrecognized
|
||||
// instruction at INSN_OFFSET.
|
||||
virtual void BadInstruction(uint64 offset, CallFrameInfo::EntryKind kind,
|
||||
uint64 insn_offset);
|
||||
|
||||
// The instruction at INSN_OFFSET in the entry at OFFSET, of kind
|
||||
// KIND, establishes a rule that cites the CFA, but we have not
|
||||
// established a CFA rule yet.
|
||||
virtual void NoCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
|
||||
uint64 insn_offset);
|
||||
|
||||
// The instruction at INSN_OFFSET in the entry at OFFSET, of kind
|
||||
// KIND, is a DW_CFA_restore_state instruction, but the stack of
|
||||
// saved states is empty.
|
||||
virtual void EmptyStateStack(uint64 offset, CallFrameInfo::EntryKind kind,
|
||||
uint64 insn_offset);
|
||||
|
||||
// The DW_CFA_remember_state instruction at INSN_OFFSET in the entry
|
||||
// at OFFSET, of kind KIND, would restore a state that has no CFA
|
||||
// rule, whereas the current state does have a CFA rule. This is
|
||||
// bogus input, which the CallFrameInfo::Handler interface doesn't
|
||||
// (and shouldn't) have any way to report.
|
||||
virtual void ClearingCFARule(uint64 offset, CallFrameInfo::EntryKind kind,
|
||||
uint64 insn_offset);
|
||||
|
||||
protected:
|
||||
// The name of the file whose CFI we're reading.
|
||||
string filename_;
|
||||
|
||||
// The name of the CFI section in that file.
|
||||
string section_;
|
||||
};
|
||||
|
||||
} // namespace dwarf2reader
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2006 Google Inc. All Rights Reserved.
|
||||
// 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
|
||||
|
@ -42,25 +42,6 @@
|
|||
|
||||
namespace dwarf2reader {
|
||||
|
||||
// Given an offset value, its form, and the base offset of the
|
||||
// compilation unit containing this value, return an absolute offset
|
||||
// within the .debug_info section.
|
||||
uint64 GetAbsoluteOffset(uint64 offset,
|
||||
enum DwarfForm form,
|
||||
uint64 compilation_unit_base) {
|
||||
switch (form) {
|
||||
case DW_FORM_ref1:
|
||||
case DW_FORM_ref2:
|
||||
case DW_FORM_ref4:
|
||||
case DW_FORM_ref8:
|
||||
case DW_FORM_ref_udata:
|
||||
return offset + compilation_unit_base;
|
||||
case DW_FORM_ref_addr:
|
||||
default:
|
||||
return offset;
|
||||
}
|
||||
}
|
||||
|
||||
CULineInfoHandler::CULineInfoHandler(vector<SourceFileInfo>* files,
|
||||
vector<string>* dirs,
|
||||
LineMap* linemap):linemap_(linemap),
|
||||
|
@ -108,7 +89,7 @@ void CULineInfoHandler::DefineFile(const string& name,
|
|||
}
|
||||
}
|
||||
|
||||
void CULineInfoHandler::AddLine(uint64 address, uint32 file_num,
|
||||
void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num,
|
||||
uint32 line_num, uint32 column_num) {
|
||||
if (file_num < files_->size()) {
|
||||
linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(),
|
||||
|
@ -198,6 +179,18 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
|
|||
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
|
||||
|
@ -205,14 +198,13 @@ void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset,
|
|||
// 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.
|
||||
uint64 abs_offset = GetAbsoluteOffset(data, form, current_compilation_unit_offset_);
|
||||
FunctionMap::iterator iter = offset_to_funcinfo_->find(abs_offset);
|
||||
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", abs_offset, offset);
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2006 Google Inc. All Rights Reserved.
|
||||
// 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
|
||||
|
@ -92,13 +92,13 @@ class CULineInfoHandler: public LineInfoHandler {
|
|||
|
||||
|
||||
// Called when the line info reader has a new line, address pair
|
||||
// ready for us. ADDRESS is the address of the code, 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, uint32 file_num, uint32 line_num,
|
||||
uint32 column_num);
|
||||
|
||||
// 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_;
|
||||
|
@ -146,6 +146,16 @@ class CUFunctionInfoHandler: public Dwarf2Handler {
|
|||
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
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
// 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>
|
||||
|
||||
// dump_stabs.cc --- implement the DumpStabsHandler class.
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cxxabi.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "common/linux/dump_stabs.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
// Demangle using abi call.
|
||||
// Older GCC may not support it.
|
||||
static string Demangle(const string &mangled) {
|
||||
int status = 0;
|
||||
char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
|
||||
if (status == 0 && demangled != NULL) {
|
||||
string str(demangled);
|
||||
free(demangled);
|
||||
return str;
|
||||
}
|
||||
return string(mangled);
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address,
|
||||
const char *build_directory) {
|
||||
assert(!in_compilation_unit_);
|
||||
in_compilation_unit_ = true;
|
||||
current_source_file_name_ = name;
|
||||
current_source_file_ = module_->FindFile(name);
|
||||
comp_unit_base_address_ = address;
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::EndCompilationUnit(uint64_t address) {
|
||||
assert(in_compilation_unit_);
|
||||
in_compilation_unit_ = false;
|
||||
comp_unit_base_address_ = 0;
|
||||
current_source_file_ = NULL;
|
||||
current_source_file_name_ = NULL;
|
||||
if (address)
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::StartFunction(const string &name,
|
||||
uint64_t address) {
|
||||
assert(!current_function_);
|
||||
Module::Function *f = new Module::Function;
|
||||
f->name = Demangle(name);
|
||||
f->address = address;
|
||||
f->size = 0; // We compute this in DumpStabsHandler::Finalize().
|
||||
f->parameter_size = 0; // We don't provide this information.
|
||||
current_function_ = f;
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::EndFunction(uint64_t address) {
|
||||
assert(current_function_);
|
||||
// Functions in this compilation unit should have address bigger
|
||||
// than the compilation unit's starting address. There may be a lot
|
||||
// of duplicated entries for functions in the STABS data; only one
|
||||
// entry can meet this requirement.
|
||||
//
|
||||
// (I don't really understand the above comment; just bringing it
|
||||
// along from the previous code, and leaving the behaivor unchanged.
|
||||
// If you know the whole story, please patch this comment. --jimb)
|
||||
if (current_function_->address >= comp_unit_base_address_)
|
||||
functions_.push_back(current_function_);
|
||||
else
|
||||
delete current_function_;
|
||||
current_function_ = NULL;
|
||||
if (address)
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) {
|
||||
assert(current_function_);
|
||||
assert(current_source_file_);
|
||||
if (name != current_source_file_name_) {
|
||||
current_source_file_ = module_->FindFile(name);
|
||||
current_source_file_name_ = name;
|
||||
}
|
||||
Module::Line line;
|
||||
line.address = address;
|
||||
line.size = 0; // We compute this in DumpStabsHandler::Finalize().
|
||||
line.file = current_source_file_;
|
||||
line.number = number;
|
||||
current_function_->lines.push_back(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpStabsHandler::Warning(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void DumpStabsHandler::Finalize() {
|
||||
// Sort our boundary list, so we can search it quickly.
|
||||
sort(boundaries_.begin(), boundaries_.end());
|
||||
// Sort all functions by address, just for neatness.
|
||||
sort(functions_.begin(), functions_.end(),
|
||||
Module::Function::CompareByAddress);
|
||||
for (vector<Module::Function *>::iterator func_it = functions_.begin();
|
||||
func_it != functions_.end();
|
||||
func_it++) {
|
||||
Module::Function *f = *func_it;
|
||||
// Compute the function f's size.
|
||||
vector<Module::Address>::iterator boundary
|
||||
= std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address);
|
||||
if (boundary != boundaries_.end())
|
||||
f->size = *boundary - f->address;
|
||||
else
|
||||
// If this is the last function in the module, and the STABS
|
||||
// reader was unable to give us its ending address, then assign
|
||||
// it a bogus, very large value. This will happen at most once
|
||||
// per module: since we've added all functions' addresses to the
|
||||
// boundary table, only one can be the last.
|
||||
f->size = kFallbackSize;
|
||||
|
||||
// Compute sizes for each of the function f's lines --- if it has any.
|
||||
if (!f->lines.empty()) {
|
||||
stable_sort(f->lines.begin(), f->lines.end(),
|
||||
Module::Line::CompareByAddress);
|
||||
vector<Module::Line>::iterator last_line = f->lines.end() - 1;
|
||||
for (vector<Module::Line>::iterator line_it = f->lines.begin();
|
||||
line_it != last_line; line_it++)
|
||||
line_it[0].size = line_it[1].address - line_it[0].address;
|
||||
// Compute the size of the last line from f's end address.
|
||||
last_line->size = (f->address + f->size) - last_line->address;
|
||||
}
|
||||
}
|
||||
// Now that everything has a size, add our functions to the module, and
|
||||
// dispose of our private list.
|
||||
module_->AddFunctions(functions_.begin(), functions_.end());
|
||||
functions_.clear();
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,139 @@
|
|||
// -*- 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>
|
||||
|
||||
// dump_stabs.h: Define the DumpStabsHandler class, which receives
|
||||
// STABS debugging information from a parser and adds it to a Breakpad
|
||||
// symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DUMP_STABS_H__
|
||||
#define COMMON_LINUX_DUMP_STABS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/module.h"
|
||||
#include "common/linux/stabs_reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// A DumpStabsHandler is a handler that receives parsed STABS
|
||||
// debugging information from a StabsReader, and uses that to populate
|
||||
// a Module. (All classes are in the google_breakpad namespace.) A
|
||||
// Module represents the contents of a Breakpad symbol file, and knows
|
||||
// how to write itself out as such. A DumpStabsHandler thus acts as
|
||||
// the bridge between STABS and Breakpad data.
|
||||
class DumpStabsHandler: public google_breakpad::StabsHandler {
|
||||
public:
|
||||
// Receive parsed debugging information from a StabsReader, and
|
||||
// store it all in MODULE.
|
||||
DumpStabsHandler(Module *module) :
|
||||
module_(module),
|
||||
in_compilation_unit_(false),
|
||||
comp_unit_base_address_(0),
|
||||
current_function_(NULL),
|
||||
current_source_file_(NULL),
|
||||
current_source_file_name_(NULL) { }
|
||||
|
||||
// The standard StabsHandler virtual member functions.
|
||||
bool StartCompilationUnit(const char *name, uint64_t address,
|
||||
const char *build_directory);
|
||||
bool EndCompilationUnit(uint64_t address);
|
||||
bool StartFunction(const string &name, uint64_t address);
|
||||
bool EndFunction(uint64_t address);
|
||||
bool Line(uint64_t address, const char *name, int number);
|
||||
void Warning(const char *format, ...);
|
||||
|
||||
// Do any final processing necessary to make module_ contain all the
|
||||
// data provided by the STABS reader.
|
||||
//
|
||||
// Because STABS does not provide reliable size information for
|
||||
// functions and lines, we need to make a pass over the data after
|
||||
// processing all the STABS to compute those sizes. We take care of
|
||||
// that here.
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
|
||||
// An arbitrary, but very large, size to use for functions whose
|
||||
// size we can't compute properly.
|
||||
static const uint64_t kFallbackSize = 0x10000000;
|
||||
|
||||
// The module we're contributing debugging info to.
|
||||
Module *module_;
|
||||
|
||||
// The functions we've generated so far. We don't add these to
|
||||
// module_ as we parse them. Instead, we wait until we've computed
|
||||
// their ending address, and their lines' ending addresses.
|
||||
//
|
||||
// We could just stick them in module_ from the outset, but if
|
||||
// module_ already contains data gathered from other debugging
|
||||
// formats, that would complicate the size computation.
|
||||
vector<Module::Function *> functions_;
|
||||
|
||||
// Boundary addresses. STABS doesn't necessarily supply sizes for
|
||||
// functions and lines, so we need to compute them ourselves by
|
||||
// finding the next object.
|
||||
vector<Module::Address> boundaries_;
|
||||
|
||||
// True if we are currently within a compilation unit: we have gotten a
|
||||
// StartCompilationUnit call, but no matching EndCompilationUnit call
|
||||
// yet. We use this for sanity checks.
|
||||
bool in_compilation_unit_;
|
||||
|
||||
// The base address of the current compilation unit. We use this to
|
||||
// recognize functions we should omit from the symbol file. (If you
|
||||
// know the details of why we omit these, please patch this
|
||||
// comment.)
|
||||
Module::Address comp_unit_base_address_;
|
||||
|
||||
// The function we're currently contributing lines to.
|
||||
Module::Function *current_function_;
|
||||
|
||||
// The last Module::File we got a line number in.
|
||||
Module::File *current_source_file_;
|
||||
|
||||
// The pointer in the .stabstr section of the name that
|
||||
// current_source_file_ is built from. This allows us to quickly
|
||||
// recognize when the current line is in the same file as the
|
||||
// previous one (which it usually is).
|
||||
const char *current_source_file_name_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DUMP_STABS_H__
|
|
@ -0,0 +1,193 @@
|
|||
// 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>
|
||||
|
||||
// dump_stabs_unittest.cc: Unit tests for DumpStabsHandler.
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/dump_stabs.h"
|
||||
|
||||
using google_breakpad::DumpStabsHandler;
|
||||
using google_breakpad::Module;
|
||||
using std::vector;
|
||||
|
||||
TEST(DumpStabsHandler, SimpleCU) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// Feed in a simple compilation unit that defines a function with
|
||||
// one line.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0x9f4d1271e50db93bLL,
|
||||
"build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("function", 0xfde4abbed390c394LL));
|
||||
EXPECT_TRUE(h.Line(0xfde4abbed390c394LL, "source-file-name", 174823314));
|
||||
EXPECT_TRUE(h.EndFunction(0xfde4abbed390c3a4LL));
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0xfee4abbed390c3a4LL));
|
||||
h.Finalize();
|
||||
|
||||
// Now check to see what has been added to the Module.
|
||||
Module::File *file = m.FindExistingFile("source-file-name");
|
||||
ASSERT_TRUE(file != NULL);
|
||||
|
||||
vector<Module::Function *> functions;
|
||||
m.GetFunctions(&functions, functions.end());
|
||||
ASSERT_EQ((size_t) 1, functions.size());
|
||||
Module::Function *function = functions[0];
|
||||
EXPECT_STREQ("function", function->name.c_str());
|
||||
EXPECT_EQ(0xfde4abbed390c394LL, function->address);
|
||||
EXPECT_EQ(0x10U, function->size);
|
||||
EXPECT_EQ(0U, function->parameter_size);
|
||||
ASSERT_EQ((size_t) 1, function->lines.size());
|
||||
Module::Line *line = &function->lines[0];
|
||||
EXPECT_EQ(0xfde4abbed390c394LL, line->address);
|
||||
EXPECT_EQ(0x10U, line->size); // derived from EndFunction
|
||||
EXPECT_TRUE(line->file == file);
|
||||
EXPECT_EQ(174823314, line->number);
|
||||
}
|
||||
|
||||
TEST(InferSizes, LineSize) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// Feed in a simple compilation unit that defines a function with
|
||||
// one line.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xb4513962eff94e92LL,
|
||||
"build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("function", 0xb4513962eff94e92LL));
|
||||
EXPECT_TRUE(h.Line(0xb4513962eff94e92LL, "source-file-name-1", 77396614));
|
||||
EXPECT_TRUE(h.Line(0xb4513963eff94e92LL, "source-file-name-2", 87660088));
|
||||
EXPECT_TRUE(h.EndFunction(0)); // unknown function end address
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0)); // unknown CU end address
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit-2", 0xb4523963eff94e92LL,
|
||||
"build-directory-2")); // next boundary
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0));
|
||||
h.Finalize();
|
||||
|
||||
// Now check to see what has been added to the Module.
|
||||
Module::File *file1 = m.FindExistingFile("source-file-name-1");
|
||||
ASSERT_TRUE(file1 != NULL);
|
||||
Module::File *file2 = m.FindExistingFile("source-file-name-2");
|
||||
ASSERT_TRUE(file2 != NULL);
|
||||
|
||||
vector<Module::Function *> functions;
|
||||
m.GetFunctions(&functions, functions.end());
|
||||
ASSERT_EQ((size_t) 1, functions.size());
|
||||
|
||||
Module::Function *function = functions[0];
|
||||
EXPECT_STREQ("function", function->name.c_str());
|
||||
EXPECT_EQ(0xb4513962eff94e92LL, function->address);
|
||||
EXPECT_EQ(0x1000100000000ULL, function->size); // inferred from CU end
|
||||
EXPECT_EQ(0U, function->parameter_size);
|
||||
ASSERT_EQ((size_t) 2, function->lines.size());
|
||||
|
||||
Module::Line *line1 = &function->lines[0];
|
||||
EXPECT_EQ(0xb4513962eff94e92LL, line1->address);
|
||||
EXPECT_EQ(0x100000000ULL, line1->size); // derived from EndFunction
|
||||
EXPECT_TRUE(line1->file == file1);
|
||||
EXPECT_EQ(77396614, line1->number);
|
||||
|
||||
Module::Line *line2 = &function->lines[1];
|
||||
EXPECT_EQ(0xb4513963eff94e92LL, line2->address);
|
||||
EXPECT_EQ(0x1000000000000ULL, line2->size); // derived from EndFunction
|
||||
EXPECT_TRUE(line2->file == file2);
|
||||
EXPECT_EQ(87660088, line2->number);
|
||||
}
|
||||
|
||||
TEST(FunctionNames, Mangled) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// Compilation unit with one function, mangled name.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda63cef7f46cLL,
|
||||
"build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("_ZNSt6vectorIySaIyEE9push_backERKy",
|
||||
0xf2cfda63cef7f46dLL));
|
||||
EXPECT_TRUE(h.EndFunction(0));
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0));
|
||||
|
||||
h.Finalize();
|
||||
|
||||
// Now check to see what has been added to the Module.
|
||||
Module::File *file = m.FindExistingFile("compilation-unit");
|
||||
ASSERT_TRUE(file != NULL);
|
||||
|
||||
vector<Module::Function *> functions;
|
||||
m.GetFunctions(&functions, functions.end());
|
||||
ASSERT_EQ(1U, functions.size());
|
||||
|
||||
Module::Function *function = functions[0];
|
||||
// This is GCC-specific, but we shouldn't be seeing STABS data anywhere
|
||||
// but Linux.
|
||||
EXPECT_STREQ("std::vector<unsigned long long, "
|
||||
"std::allocator<unsigned long long> >::"
|
||||
"push_back(unsigned long long const&)",
|
||||
function->name.c_str());
|
||||
EXPECT_EQ(0xf2cfda63cef7f46dLL, function->address);
|
||||
EXPECT_LT(0U, function->size); // should have used dummy size
|
||||
EXPECT_EQ(0U, function->parameter_size);
|
||||
ASSERT_EQ(0U, function->lines.size());
|
||||
}
|
||||
|
||||
// The GNU toolchain can omit functions that are not used; however,
|
||||
// when it does so, it doesn't clean up the debugging information that
|
||||
// refers to them. In STABS, this results in compilation units whose
|
||||
// SO addresses are zero.
|
||||
TEST(Omitted, Function) {
|
||||
Module m("name", "os", "arch", "id");
|
||||
DumpStabsHandler h(&m);
|
||||
|
||||
// The StartCompilationUnit and EndCompilationUnit calls may both have an
|
||||
// address of zero if the compilation unit has had sections removed.
|
||||
EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0, "build-directory"));
|
||||
EXPECT_TRUE(h.StartFunction("function", 0x2a133596));
|
||||
EXPECT_TRUE(h.EndFunction(0));
|
||||
EXPECT_TRUE(h.EndCompilationUnit(0));
|
||||
}
|
||||
|
||||
// TODO --- if we actually cared about STABS. Even without these we've
|
||||
// got full coverage of non-failure source lines in dump_stabs.cc.
|
||||
|
||||
// Line size from next line
|
||||
// Line size from function end
|
||||
// Line size from next function start
|
||||
// line size from cu end
|
||||
// line size from next cu start
|
||||
// fallback size is something plausible
|
||||
|
||||
// function size from function end
|
||||
// function size from next function start
|
||||
// function size from cu end
|
||||
// function size from next cu start
|
||||
// fallback size is something plausible
|
||||
|
||||
// omitting functions outside the compilation unit's address range
|
||||
// zero-line, one-line, many-line functions
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -27,30 +27,32 @@
|
|||
// (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 <cxxabi.h>
|
||||
// Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// dump_symbols.cc: implement google_breakpad::WriteSymbolFile:
|
||||
// Find all the debugging info in a file and dump it as a Breakpad symbol file.
|
||||
|
||||
#include <elf.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <link.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <cassert>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/dwarf/bytereader-inl.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/linux/dump_stabs.h"
|
||||
#include "common/linux/dump_symbols.h"
|
||||
#include "common/linux/dwarf_cfi_to_module.h"
|
||||
#include "common/linux/dwarf_cu_to_module.h"
|
||||
#include "common/linux/dwarf_line_to_module.h"
|
||||
#include "common/linux/file_id.h"
|
||||
#include "common/linux/module.h"
|
||||
#include "common/linux/stabs_reader.h"
|
||||
|
@ -58,24 +60,11 @@
|
|||
// This namespace contains helper functions.
|
||||
namespace {
|
||||
|
||||
using google_breakpad::DumpStabsHandler;
|
||||
using google_breakpad::DwarfCFIToModule;
|
||||
using google_breakpad::DwarfCUToModule;
|
||||
using google_breakpad::DwarfLineToModule;
|
||||
using google_breakpad::Module;
|
||||
using std::vector;
|
||||
|
||||
// Stab section name.
|
||||
static const char *kStabName = ".stab";
|
||||
|
||||
// Demangle using abi call.
|
||||
// Older GCC may not support it.
|
||||
static std::string Demangle(const std::string &mangled) {
|
||||
int status = 0;
|
||||
char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status);
|
||||
if (status == 0 && demangled != NULL) {
|
||||
std::string str(demangled);
|
||||
free(demangled);
|
||||
return str;
|
||||
}
|
||||
return std::string(mangled);
|
||||
}
|
||||
|
||||
// Fix offset into virtual address by adding the mapped base into offsets.
|
||||
// Make life easier when want to find something by offset.
|
||||
|
@ -109,7 +98,7 @@ static bool IsValidElf(const ElfW(Ehdr) *elf_header) {
|
|||
|
||||
static const ElfW(Shdr) *FindSectionByName(const char *name,
|
||||
const ElfW(Shdr) *sections,
|
||||
const ElfW(Shdr) *strtab,
|
||||
const ElfW(Shdr) *section_names,
|
||||
int nsection) {
|
||||
assert(name != NULL);
|
||||
assert(sections != NULL);
|
||||
|
@ -119,207 +108,24 @@ static const ElfW(Shdr) *FindSectionByName(const char *name,
|
|||
if (name_len == 0)
|
||||
return NULL;
|
||||
|
||||
// Find the end of the section name section, to make sure that
|
||||
// comparisons don't run off the end of the section.
|
||||
const char *names_end =
|
||||
reinterpret_cast<char*>(section_names->sh_offset + section_names->sh_size);
|
||||
|
||||
for (int i = 0; i < nsection; ++i) {
|
||||
const char *section_name =
|
||||
reinterpret_cast<char*>(strtab->sh_offset + sections[i].sh_name);
|
||||
if (!strncmp(name, section_name, name_len))
|
||||
reinterpret_cast<char*>(section_names->sh_offset + sections[i].sh_name);
|
||||
if (names_end - section_name >= name_len + 1 &&
|
||||
strcmp(name, section_name) == 0)
|
||||
return sections + i;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Our handler class for STABS data.
|
||||
class DumpStabsHandler: public google_breakpad::StabsHandler {
|
||||
public:
|
||||
DumpStabsHandler(Module *module) :
|
||||
module_(module),
|
||||
comp_unit_base_address_(0),
|
||||
current_function_(NULL),
|
||||
current_source_file_(NULL),
|
||||
current_source_file_name_(NULL) { }
|
||||
|
||||
bool StartCompilationUnit(const char *name, uint64_t address,
|
||||
const char *build_directory);
|
||||
bool EndCompilationUnit(uint64_t address);
|
||||
bool StartFunction(const std::string &name, uint64_t address);
|
||||
bool EndFunction(uint64_t address);
|
||||
bool Line(uint64_t address, const char *name, int number);
|
||||
void Warning(const char *format, ...);
|
||||
|
||||
// Do any final processing necessary to make module_ contain all the
|
||||
// data provided by the STABS reader.
|
||||
//
|
||||
// Because STABS does not provide reliable size information for
|
||||
// functions and lines, we need to make a pass over the data after
|
||||
// processing all the STABS to compute those sizes. We take care of
|
||||
// that here.
|
||||
void Finalize();
|
||||
|
||||
private:
|
||||
|
||||
// An arbitrary, but very large, size to use for functions whose
|
||||
// size we can't compute properly.
|
||||
static const uint64_t kFallbackSize = 0x10000000;
|
||||
|
||||
// The module we're contributing debugging info to.
|
||||
Module *module_;
|
||||
|
||||
// The functions we've generated so far. We don't add these to
|
||||
// module_ as we parse them. Instead, we wait until we've computed
|
||||
// their ending address, and their lines' ending addresses.
|
||||
//
|
||||
// We could just stick them in module_ from the outset, but if
|
||||
// module_ already contains data gathered from other debugging
|
||||
// formats, that would complicate the size computation.
|
||||
vector<Module::Function *> functions_;
|
||||
|
||||
// Boundary addresses. STABS doesn't necessarily supply sizes for
|
||||
// functions and lines, so we need to compute them ourselves by
|
||||
// finding the next object.
|
||||
vector<Module::Address> boundaries_;
|
||||
|
||||
// The base address of the current compilation unit. We use this to
|
||||
// recognize functions we should omit from the symbol file. (If you
|
||||
// know the details of why we omit these, please patch this
|
||||
// comment.)
|
||||
Module::Address comp_unit_base_address_;
|
||||
|
||||
// The function we're currently contributing lines to.
|
||||
Module::Function *current_function_;
|
||||
|
||||
// The last Module::File we got a line number in.
|
||||
Module::File *current_source_file_;
|
||||
|
||||
// The pointer in the .stabstr section of the name that
|
||||
// current_source_file_ is built from. This allows us to quickly
|
||||
// recognize when the current line is in the same file as the
|
||||
// previous one (which it usually is).
|
||||
const char *current_source_file_name_;
|
||||
};
|
||||
|
||||
bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address,
|
||||
const char *build_directory) {
|
||||
assert(! comp_unit_base_address_);
|
||||
current_source_file_name_ = name;
|
||||
current_source_file_ = module_->FindFile(name);
|
||||
comp_unit_base_address_ = address;
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::EndCompilationUnit(uint64_t address) {
|
||||
assert(comp_unit_base_address_);
|
||||
comp_unit_base_address_ = 0;
|
||||
current_source_file_ = NULL;
|
||||
current_source_file_name_ = NULL;
|
||||
if (address)
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::StartFunction(const std::string &name,
|
||||
uint64_t address) {
|
||||
assert(! current_function_);
|
||||
Module::Function *f = new Module::Function;
|
||||
f->name_ = Demangle(name);
|
||||
f->address_ = address;
|
||||
f->size_ = 0; // We compute this in DumpStabsHandler::Finalize().
|
||||
f->parameter_size_ = 0; // We don't provide this information.
|
||||
current_function_ = f;
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::EndFunction(uint64_t address) {
|
||||
assert(current_function_);
|
||||
// Functions in this compilation unit should have address bigger
|
||||
// than the compilation unit's starting address. There may be a lot
|
||||
// of duplicated entries for functions in the STABS data; only one
|
||||
// entry can meet this requirement.
|
||||
//
|
||||
// (I don't really understand the above comment; just bringing it
|
||||
// along from the previous code, and leaving the behaivor unchanged.
|
||||
// If you know the whole story, please patch this comment. --jimb)
|
||||
if (current_function_->address_ >= comp_unit_base_address_)
|
||||
functions_.push_back(current_function_);
|
||||
else
|
||||
delete current_function_;
|
||||
current_function_ = NULL;
|
||||
if (address)
|
||||
boundaries_.push_back(static_cast<Module::Address>(address));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) {
|
||||
assert(current_function_);
|
||||
assert(current_source_file_);
|
||||
if (name != current_source_file_name_) {
|
||||
current_source_file_ = module_->FindFile(name);
|
||||
current_source_file_name_ = name;
|
||||
}
|
||||
Module::Line line;
|
||||
line.address_ = address;
|
||||
line.size_ = 0; // We compute this in DumpStabsHandler::Finalize().
|
||||
line.file_ = current_source_file_;
|
||||
line.number_ = number;
|
||||
current_function_->lines_.push_back(line);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpStabsHandler::Warning(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
vfprintf(stderr, format, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void DumpStabsHandler::Finalize() {
|
||||
// Sort our boundary list, so we can search it quickly.
|
||||
sort(boundaries_.begin(), boundaries_.end());
|
||||
// Sort all functions by address, just for neatness.
|
||||
sort(functions_.begin(), functions_.end(),
|
||||
Module::Function::CompareByAddress);
|
||||
for (vector<Module::Function *>::iterator func_it = functions_.begin();
|
||||
func_it != functions_.end();
|
||||
func_it++) {
|
||||
Module::Function *f = *func_it;
|
||||
// Compute the function f's size.
|
||||
vector<Module::Address>::iterator boundary
|
||||
= std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address_);
|
||||
if (boundary != boundaries_.end())
|
||||
f->size_ = *boundary - f->address_;
|
||||
else
|
||||
// If this is the last function in the module, and the STABS
|
||||
// reader was unable to give us its ending address, then assign
|
||||
// it a bogus, very large value. This will happen at most once
|
||||
// per module: since we've added all functions' addresses to the
|
||||
// boundary table, only one can be the last.
|
||||
f->size_ = kFallbackSize;
|
||||
|
||||
// Compute sizes for each of the function f's lines --- if it has any.
|
||||
if (! f->lines_.empty()) {
|
||||
stable_sort(f->lines_.begin(), f->lines_.end(),
|
||||
Module::Line::CompareByAddress);
|
||||
vector<Module::Line>::iterator last_line = f->lines_.end() - 1;
|
||||
for (vector<Module::Line>::iterator line_it = f->lines_.begin();
|
||||
line_it != last_line; line_it++)
|
||||
line_it[0].size_ = line_it[1].address_ - line_it[0].address_;
|
||||
// Compute the size of the last line from f's end address.
|
||||
last_line->size_ = (f->address_ + f->size_) - last_line->address_;
|
||||
}
|
||||
}
|
||||
// Now that everything has a size, add our functions to the module, and
|
||||
// dispose of our private list.
|
||||
module_->AddFunctions(functions_.begin(), functions_.end());
|
||||
functions_.clear();
|
||||
}
|
||||
|
||||
static bool LoadSymbols(const ElfW(Shdr) *stab_section,
|
||||
const ElfW(Shdr) *stabstr_section,
|
||||
Module *module) {
|
||||
if (stab_section == NULL || stabstr_section == NULL)
|
||||
return false;
|
||||
|
||||
static bool LoadStabs(const ElfW(Shdr) *stab_section,
|
||||
const ElfW(Shdr) *stabstr_section,
|
||||
Module *module) {
|
||||
// A callback object to handle data from the STABS reader.
|
||||
DumpStabsHandler handler(module);
|
||||
// Find the addresses of the STABS data, and create a STABS reader object.
|
||||
|
@ -329,13 +135,218 @@ static bool LoadSymbols(const ElfW(Shdr) *stab_section,
|
|||
stabstr, stabstr_section->sh_size,
|
||||
&handler);
|
||||
// Read the STABS data, and do post-processing.
|
||||
if (! reader.Process())
|
||||
if (!reader.Process())
|
||||
return false;
|
||||
handler.Finalize();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) {
|
||||
// A line-to-module loader that accepts line number info parsed by
|
||||
// dwarf2reader::LineInfo and populates a Module and a line vector
|
||||
// with the results.
|
||||
class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor {
|
||||
public:
|
||||
// Create a line-to-module converter using BYTE_READER.
|
||||
DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
|
||||
: byte_reader_(byte_reader) { }
|
||||
void operator()(const char *program, uint64 length,
|
||||
Module *module, vector<Module::Line> *lines) {
|
||||
DwarfLineToModule handler(module, lines);
|
||||
dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
|
||||
parser.Start();
|
||||
}
|
||||
private:
|
||||
dwarf2reader::ByteReader *byte_reader_;
|
||||
};
|
||||
|
||||
static bool LoadDwarf(const string &dwarf_filename,
|
||||
const ElfW(Ehdr) *elf_header,
|
||||
Module *module) {
|
||||
// Figure out what endianness this file is.
|
||||
dwarf2reader::Endianness endianness;
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_LITTLE;
|
||||
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_BIG;
|
||||
else {
|
||||
fprintf(stderr, "bad data encoding in ELF header: %d\n",
|
||||
elf_header->e_ident[EI_DATA]);
|
||||
return false;
|
||||
}
|
||||
dwarf2reader::ByteReader byte_reader(endianness);
|
||||
|
||||
// Construct a context for this file.
|
||||
DwarfCUToModule::FileContext file_context(dwarf_filename, module);
|
||||
|
||||
// Build a map of the ELF file's sections.
|
||||
const ElfW(Shdr) *sections
|
||||
= reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
int num_sections = elf_header->e_shnum;
|
||||
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
|
||||
for (int i = 0; i < num_sections; i++) {
|
||||
const ElfW(Shdr) *section = §ions[i];
|
||||
string name = reinterpret_cast<const char *>(section_names->sh_offset
|
||||
+ section->sh_name);
|
||||
const char *contents = reinterpret_cast<const char *>(section->sh_offset);
|
||||
uint64 length = section->sh_size;
|
||||
file_context.section_map[name] = std::make_pair(contents, length);
|
||||
}
|
||||
|
||||
// Parse all the compilation units in the .debug_info section.
|
||||
DumperLineToModule line_to_module(&byte_reader);
|
||||
std::pair<const char *, uint64> debug_info_section
|
||||
= file_context.section_map[".debug_info"];
|
||||
// We should never have been called if the file doesn't have a
|
||||
// .debug_info section.
|
||||
assert(debug_info_section.first);
|
||||
uint64 debug_info_length = debug_info_section.second;
|
||||
for (uint64 offset = 0; offset < debug_info_length;) {
|
||||
// Make a handler for the root DIE that populates MODULE with the
|
||||
// data we find.
|
||||
DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset);
|
||||
DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter);
|
||||
// Make a Dwarf2Handler that drives our DIEHandler.
|
||||
dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
|
||||
// Make a DWARF parser for the compilation unit at OFFSET.
|
||||
dwarf2reader::CompilationUnit reader(file_context.section_map,
|
||||
offset,
|
||||
&byte_reader,
|
||||
&die_dispatcher);
|
||||
// Process the entire compilation unit; get the offset of the next.
|
||||
offset += reader.Start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Fill REGISTER_NAMES with the register names appropriate to the
|
||||
// machine architecture given in HEADER, indexed by the register
|
||||
// numbers used in DWARF call frame information. Return true on
|
||||
// success, or false if we don't recognize HEADER's machine
|
||||
// architecture.
|
||||
static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header,
|
||||
vector<string> *register_names)
|
||||
{
|
||||
static const char *const i386_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",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *const x86_64_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",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char *const arm_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",
|
||||
NULL
|
||||
};
|
||||
|
||||
const char * const *name_table;
|
||||
switch (elf_header->e_machine) {
|
||||
case EM_386: name_table = i386_names; break;
|
||||
case EM_ARM: name_table = arm_names; break;
|
||||
case EM_X86_64: name_table = x86_64_names; break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
register_names->clear();
|
||||
for (int i = 0; name_table[i]; i++)
|
||||
register_names->push_back(name_table[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LoadDwarfCFI(const string &dwarf_filename,
|
||||
const ElfW(Ehdr) *elf_header,
|
||||
const char *section_name,
|
||||
const ElfW(Shdr) *section,
|
||||
bool eh_frame,
|
||||
const ElfW(Shdr) *got_section,
|
||||
const ElfW(Shdr) *text_section,
|
||||
Module *module) {
|
||||
// Find the appropriate set of register names for this file's
|
||||
// architecture.
|
||||
vector<string> register_names;
|
||||
if (!DwarfCFIRegisterNames(elf_header, ®ister_names)) {
|
||||
fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';"
|
||||
" cannot convert DWARF call frame information\n",
|
||||
dwarf_filename.c_str(), elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Figure out what endianness this file is.
|
||||
dwarf2reader::Endianness endianness;
|
||||
if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_LITTLE;
|
||||
else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
endianness = dwarf2reader::ENDIANNESS_BIG;
|
||||
else {
|
||||
fprintf(stderr, "%s: bad data encoding in ELF header: %d\n",
|
||||
dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the call frame information and its size.
|
||||
const char *cfi = reinterpret_cast<const char *>(section->sh_offset);
|
||||
size_t cfi_size = section->sh_size;
|
||||
|
||||
// Plug together the parser, handler, and their entourages.
|
||||
DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name);
|
||||
DwarfCFIToModule handler(module, register_names, &module_reporter);
|
||||
dwarf2reader::ByteReader byte_reader(endianness);
|
||||
// Since we're using the ElfW macro, we're not actually capable of
|
||||
// processing both ELF32 and ELF64 files with the same program; that
|
||||
// would take a bit more work. But this will work out well enough.
|
||||
if (elf_header->e_ident[EI_CLASS] == ELFCLASS32)
|
||||
byte_reader.SetAddressSize(4);
|
||||
else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64)
|
||||
byte_reader.SetAddressSize(8);
|
||||
else {
|
||||
fprintf(stderr, "%s: bad file class in ELF header: %d\n",
|
||||
dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]);
|
||||
return false;
|
||||
}
|
||||
// Provide the base addresses for .eh_frame encoded pointers, if
|
||||
// possible.
|
||||
byte_reader.SetCFIDataBase(section->sh_addr, cfi);
|
||||
if (got_section)
|
||||
byte_reader.SetDataBase(got_section->sh_addr);
|
||||
if (text_section)
|
||||
byte_reader.SetTextBase(got_section->sh_addr);
|
||||
|
||||
dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
|
||||
section_name);
|
||||
dwarf2reader::CallFrameInfo parser(cfi, cfi_size,
|
||||
&byte_reader, &handler, &dwarf_reporter,
|
||||
eh_frame);
|
||||
parser.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header,
|
||||
Module *module) {
|
||||
// Translate all offsets in section headers into address.
|
||||
FixAddress(elf_header);
|
||||
ElfW(Addr) loading_addr = GetLoadingAddress(
|
||||
|
@ -344,18 +355,72 @@ static bool LoadSymbols(ElfW(Ehdr) *elf_header, Module *module) {
|
|||
module->SetLoadAddress(loading_addr);
|
||||
|
||||
const ElfW(Shdr) *sections =
|
||||
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
const ElfW(Shdr) *strtab = sections + elf_header->e_shstrndx;
|
||||
const ElfW(Shdr) *stab_section =
|
||||
FindSectionByName(kStabName, sections, strtab, elf_header->e_shnum);
|
||||
if (stab_section == NULL) {
|
||||
fprintf(stderr, "Stab section not found.\n");
|
||||
reinterpret_cast<ElfW(Shdr) *>(elf_header->e_shoff);
|
||||
const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx;
|
||||
bool found_debug_info_section = false;
|
||||
|
||||
// Look for STABS debugging information, and load it if present.
|
||||
const ElfW(Shdr) *stab_section
|
||||
= FindSectionByName(".stab", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (stab_section) {
|
||||
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
|
||||
if (stabstr_section) {
|
||||
found_debug_info_section = true;
|
||||
if (!LoadStabs(stab_section, stabstr_section, module))
|
||||
fprintf(stderr, "\".stab\" section found, but failed to load STABS"
|
||||
" debugging information\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Look for DWARF debugging information, and load it if present.
|
||||
const ElfW(Shdr) *dwarf_section
|
||||
= FindSectionByName(".debug_info", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (dwarf_section) {
|
||||
found_debug_info_section = true;
|
||||
if (!LoadDwarf(obj_file, elf_header, module))
|
||||
fprintf(stderr, "\".debug_info\" section found, but failed to load "
|
||||
"DWARF debugging information\n");
|
||||
}
|
||||
|
||||
// Dwarf Call Frame Information (CFI) is actually independent from
|
||||
// the other DWARF debugging information, and can be used alone.
|
||||
const ElfW(Shdr) *dwarf_cfi_section =
|
||||
FindSectionByName(".debug_frame", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (dwarf_cfi_section) {
|
||||
// Ignore the return value of this function; even without call frame
|
||||
// information, the other debugging information could be perfectly
|
||||
// useful.
|
||||
LoadDwarfCFI(obj_file, elf_header, ".debug_frame",
|
||||
dwarf_cfi_section, false, 0, 0, module);
|
||||
}
|
||||
|
||||
// Linux C++ exception handling information can also provide
|
||||
// unwinding data.
|
||||
const ElfW(Shdr) *eh_frame_section =
|
||||
FindSectionByName(".eh_frame", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
if (eh_frame_section) {
|
||||
// Pointers in .eh_frame data may be relative to the base addresses of
|
||||
// certain sections. Provide those sections if present.
|
||||
const ElfW(Shdr) *got_section =
|
||||
FindSectionByName(".got", sections, section_names, elf_header->e_shnum);
|
||||
const ElfW(Shdr) *text_section =
|
||||
FindSectionByName(".text", sections, section_names,
|
||||
elf_header->e_shnum);
|
||||
// As above, ignore the return value of this function.
|
||||
LoadDwarfCFI(obj_file, elf_header, ".eh_frame",
|
||||
eh_frame_section, true, got_section, text_section, module);
|
||||
}
|
||||
|
||||
if (!found_debug_info_section) {
|
||||
fprintf(stderr, "file contains no debugging information"
|
||||
" (no \".stab\" or \".debug_info\" sections)\n");
|
||||
return false;
|
||||
}
|
||||
const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections;
|
||||
|
||||
// Load symbols.
|
||||
return LoadSymbols(stab_section, stabstr_section, module);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -414,12 +479,18 @@ class MmapWrapper {
|
|||
// ELF_HEADER.
|
||||
const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) {
|
||||
ElfW(Half) arch = elf_header->e_machine;
|
||||
if (arch == EM_386)
|
||||
return "x86";
|
||||
else if (arch == EM_X86_64)
|
||||
return "x86_64";
|
||||
else
|
||||
return NULL;
|
||||
switch (arch) {
|
||||
case EM_386: return "x86";
|
||||
case EM_ARM: return "arm";
|
||||
case EM_MIPS: return "mips";
|
||||
case EM_PPC64: return "ppc64";
|
||||
case EM_PPC: return "ppc";
|
||||
case EM_S390: return "s390";
|
||||
case EM_SPARC: return "sparc";
|
||||
case EM_SPARCV9: return "sparcv9";
|
||||
case EM_X86_64: return "x86_64";
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Format the Elf file identifier in IDENTIFIER as a UUID with the
|
||||
|
@ -456,39 +527,54 @@ std::string BaseFileName(const std::string &filename) {
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
bool DumpSymbols::WriteSymbolFile(const std::string &obj_file,
|
||||
FILE *sym_file) {
|
||||
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) {
|
||||
int obj_fd = open(obj_file.c_str(), O_RDONLY);
|
||||
if (obj_fd < 0)
|
||||
if (obj_fd < 0) {
|
||||
fprintf(stderr, "Failed to open ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
FDWrapper obj_fd_wrapper(obj_fd);
|
||||
struct stat st;
|
||||
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0)
|
||||
if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) {
|
||||
fprintf(stderr, "Unable to fstat ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
void *obj_base = mmap(NULL, st.st_size,
|
||||
PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0);
|
||||
if (obj_base == MAP_FAILED)
|
||||
if (obj_base == MAP_FAILED) {
|
||||
fprintf(stderr, "Failed to mmap ELF file '%s': %s\n",
|
||||
obj_file.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
MmapWrapper map_wrapper(obj_base, st.st_size);
|
||||
ElfW(Ehdr) *elf_header = reinterpret_cast<ElfW(Ehdr) *>(obj_base);
|
||||
if (!IsValidElf(elf_header))
|
||||
if (!IsValidElf(elf_header)) {
|
||||
fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char identifier[16];
|
||||
google_breakpad::FileID file_id(obj_file.c_str());
|
||||
if (! file_id.ElfFileIdentifier(identifier))
|
||||
if (!file_id.ElfFileIdentifier(identifier)) {
|
||||
fprintf(stderr, "Unable to generate file identifier\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *architecture = ElfArchitecture(elf_header);
|
||||
if (! architecture)
|
||||
if (!architecture) {
|
||||
fprintf(stderr, "Unrecognized ELF machine architecture: %d\n",
|
||||
elf_header->e_machine);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name = BaseFileName(obj_file);
|
||||
std::string os = "Linux";
|
||||
std::string id = FormatIdentifier(identifier);
|
||||
|
||||
Module module(name, os, architecture, id);
|
||||
if (!LoadSymbols(elf_header, &module))
|
||||
if (!LoadSymbols(obj_file, elf_header, &module))
|
||||
return false;
|
||||
if (!module.Write(sym_file))
|
||||
return false;
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -26,9 +28,9 @@
|
|||
// 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.
|
||||
//
|
||||
// dump_symbols.cc: Implements a linux stab debugging format dumper.
|
||||
//
|
||||
|
||||
// dump_symbols.h: Read debugging information from an ELF file, and write
|
||||
// it out as a Breakpad symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
#define COMMON_LINUX_DUMP_SYMBOLS_H__
|
||||
|
@ -38,11 +40,10 @@
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
class DumpSymbols {
|
||||
public:
|
||||
bool WriteSymbolFile(const std::string &obj_file,
|
||||
FILE *sym_file);
|
||||
};
|
||||
// Find all the debugging information in OBJ_FILE, an ELF executable
|
||||
// or shared library, and write it to SYM_FILE in the Breakpad symbol
|
||||
// file format.
|
||||
bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file);
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
// -*- 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/linux/dwarf_cfi_to_module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::ostringstream;
|
||||
|
||||
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"] = register_names_[return_address_];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
string DwarfCFIToModule::RegisterName(int i) {
|
||||
assert(entry_);
|
||||
if (i < 0) {
|
||||
assert(i == kCFARegister);
|
||||
return ".cfa";
|
||||
}
|
||||
unsigned reg = i;
|
||||
if (reg == return_address_)
|
||||
return ".ra";
|
||||
|
||||
if (0 <= reg && reg < register_names_.size())
|
||||
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_);
|
||||
// Is this one of this entry's initial rules?
|
||||
if (address == entry_->address)
|
||||
entry_->initial_rules[RegisterName(reg)] = rule;
|
||||
// File it under the appropriate address.
|
||||
else
|
||||
entry_->rule_changes[address][RegisterName(reg)] = 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
|
|
@ -0,0 +1,154 @@
|
|||
// -*- 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 <cassert>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/linux/module.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::CallFrameInfo;
|
||||
using google_breakpad::Module;
|
||||
using std::string;
|
||||
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_;
|
||||
};
|
||||
|
||||
// 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) { }
|
||||
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_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H
|
|
@ -0,0 +1,260 @@
|
|||
// 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 "breakpad_googletest_includes.h"
|
||||
#include "common/linux/dwarf_cfi_to_module.h"
|
||||
|
||||
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");
|
||||
|
||||
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, 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(_, 10));
|
||||
StartEntry();
|
||||
ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7,
|
||||
DwarfCFIToModule::kCFARegister,
|
||||
10, 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_register10 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));
|
||||
}
|
||||
|
|
@ -0,0 +1,877 @@
|
|||
// 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>
|
||||
|
||||
// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "common/linux/dwarf_cu_to_module.h"
|
||||
#include "common/linux/dwarf_line_to_module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
||||
// Data provided by a DWARF specification DIE.
|
||||
//
|
||||
// In DWARF, the DIE for a definition may contain a DW_AT_specification
|
||||
// attribute giving the offset of the corresponding declaration DIE, and
|
||||
// the definition DIE may omit information given in the declaration. For
|
||||
// example, it's common for a function's address range to appear only in
|
||||
// its definition DIE, but its name to appear only in its declaration
|
||||
// DIE.
|
||||
//
|
||||
// The dumper needs to be able to follow DW_AT_specification links to
|
||||
// bring all this information together in a FUNC record. Conveniently,
|
||||
// DIEs that are the target of such links have a DW_AT_declaration flag
|
||||
// set, so we can identify them when we first see them, and record their
|
||||
// contents for later reference.
|
||||
//
|
||||
// A Specification holds information gathered from a declaration DIE that
|
||||
// we may need if we find a DW_AT_specification link pointing to it.
|
||||
struct DwarfCUToModule::Specification {
|
||||
// The name of the enclosing scope, or the empty string if there is none.
|
||||
string enclosing_name;
|
||||
|
||||
// The name for the specification DIE itself, without any enclosing
|
||||
// name components.
|
||||
string unqualified_name;
|
||||
};
|
||||
|
||||
// An abstract origin -- base definition of an inline function.
|
||||
struct AbstractOrigin {
|
||||
AbstractOrigin() : name() {}
|
||||
AbstractOrigin(const string& name) : name(name) {}
|
||||
|
||||
string name;
|
||||
};
|
||||
|
||||
typedef map<uint64, AbstractOrigin> AbstractOriginByOffset;
|
||||
|
||||
// Data global to the DWARF-bearing file that is private to the
|
||||
// DWARF-to-Module process.
|
||||
struct DwarfCUToModule::FilePrivate {
|
||||
// A map from offsets of DIEs within the .debug_info section to
|
||||
// Specifications describing those DIEs. Specification references can
|
||||
// cross compilation unit boundaries.
|
||||
SpecificationByOffset specifications;
|
||||
|
||||
AbstractOriginByOffset origins;
|
||||
};
|
||||
|
||||
DwarfCUToModule::FileContext::FileContext(const string &filename_arg,
|
||||
Module *module_arg)
|
||||
: filename(filename_arg), module(module_arg) {
|
||||
file_private = new FilePrivate();
|
||||
}
|
||||
|
||||
DwarfCUToModule::FileContext::~FileContext() {
|
||||
delete file_private;
|
||||
}
|
||||
|
||||
// Information global to the particular compilation unit we're
|
||||
// parsing. This is for data shared across the CU's entire DIE tree,
|
||||
// and parameters from the code invoking the CU parser.
|
||||
struct DwarfCUToModule::CUContext {
|
||||
CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg)
|
||||
: file_context(file_context_arg),
|
||||
reporter(reporter_arg),
|
||||
language(Language::CPlusPlus) { }
|
||||
~CUContext() {
|
||||
for (vector<Module::Function *>::iterator it = functions.begin();
|
||||
it != functions.end(); it++)
|
||||
delete *it;
|
||||
};
|
||||
|
||||
// The DWARF-bearing file into which this CU was incorporated.
|
||||
FileContext *file_context;
|
||||
|
||||
// For printing error messages.
|
||||
WarningReporter *reporter;
|
||||
|
||||
// The source language of this compilation unit.
|
||||
const Language *language;
|
||||
|
||||
// The functions defined in this compilation unit. We accumulate
|
||||
// them here during parsing. Then, in DwarfCUToModule::Finish, we
|
||||
// assign them lines and add them to file_context->module.
|
||||
//
|
||||
// Destroying this destroys all the functions this vector points to.
|
||||
vector<Module::Function *> functions;
|
||||
};
|
||||
|
||||
// Information about the context of a particular DIE. This is for
|
||||
// information that changes as we descend the tree towards the leaves:
|
||||
// the containing classes/namespaces, etc.
|
||||
struct DwarfCUToModule::DIEContext {
|
||||
// The fully-qualified name of the context. For example, for a
|
||||
// tree like:
|
||||
//
|
||||
// DW_TAG_namespace Foo
|
||||
// DW_TAG_class Bar
|
||||
// DW_TAG_subprogram Baz
|
||||
//
|
||||
// in a C++ compilation unit, the DIEContext's name for the
|
||||
// DW_TAG_subprogram DIE would be "Foo::Bar". The DIEContext's
|
||||
// name for the DW_TAG_namespace DIE would be "".
|
||||
string name;
|
||||
};
|
||||
|
||||
// An abstract base class for all the dumper's DIE handlers.
|
||||
class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler {
|
||||
public:
|
||||
// Create a handler for the DIE at OFFSET whose compilation unit is
|
||||
// described by CU_CONTEXT, and whose immediate context is described
|
||||
// by PARENT_CONTEXT.
|
||||
GenericDIEHandler(CUContext *cu_context, DIEContext *parent_context,
|
||||
uint64 offset)
|
||||
: cu_context_(cu_context),
|
||||
parent_context_(parent_context),
|
||||
offset_(offset),
|
||||
declaration_(false),
|
||||
specification_(NULL) { }
|
||||
|
||||
// Derived classes' ProcessAttributeUnsigned can defer to this to
|
||||
// handle DW_AT_declaration, or simply not override it.
|
||||
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
|
||||
// Derived classes' ProcessAttributeReference can defer to this to
|
||||
// handle DW_AT_specification, or simply not override it.
|
||||
void ProcessAttributeReference(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
|
||||
// Derived classes' ProcessAttributeReference can defer to this to
|
||||
// handle DW_AT_specification, or simply not override it.
|
||||
void ProcessAttributeString(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data);
|
||||
|
||||
protected:
|
||||
// Compute and return the fully-qualified name of the DIE. If this
|
||||
// DIE is a declaration DIE, to be cited by other DIEs'
|
||||
// DW_AT_specification attributes, record its enclosing name and
|
||||
// unqualified name in the specification table.
|
||||
//
|
||||
// Use this from EndAttributes member functions, not ProcessAttribute*
|
||||
// functions; only the former can be sure that all the DIE's attributes
|
||||
// have been seen.
|
||||
string ComputeQualifiedName();
|
||||
|
||||
CUContext *cu_context_;
|
||||
DIEContext *parent_context_;
|
||||
uint64 offset_;
|
||||
|
||||
// If this DIE has a DW_AT_declaration attribute, this is its value.
|
||||
// It is false on DIEs with no DW_AT_declaration attribute.
|
||||
bool declaration_;
|
||||
|
||||
// If this DIE has a DW_AT_specification attribute, this is the
|
||||
// Specification structure for the DIE the attribute refers to.
|
||||
// Otherwise, this is NULL.
|
||||
Specification *specification_;
|
||||
|
||||
// The value of the DW_AT_name attribute, or the empty string if the
|
||||
// DIE has no such attribute.
|
||||
string name_attribute_;
|
||||
};
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_declaration: declaration_ = (data != 0); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_specification: {
|
||||
// Find the Specification to which this attribute refers, and
|
||||
// set specification_ appropriately. We could do more processing
|
||||
// here, but it's better to leave the real work to our
|
||||
// EndAttribute member function, at which point we know we have
|
||||
// seen all the DIE's attributes.
|
||||
FileContext *file_context = cu_context_->file_context;
|
||||
SpecificationByOffset *specifications
|
||||
= &file_context->file_private->specifications;
|
||||
SpecificationByOffset::iterator spec = specifications->find(data);
|
||||
if (spec != specifications->end()) {
|
||||
specification_ = &spec->second;
|
||||
} else {
|
||||
// Technically, there's no reason a DW_AT_specification
|
||||
// couldn't be a forward reference, but supporting that would
|
||||
// be a lot of work (changing to a two-pass structure), and I
|
||||
// don't think any producers we care about ever emit such
|
||||
// things.
|
||||
cu_context_->reporter->UnknownSpecification(offset_, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_name: name_attribute_ = data; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() {
|
||||
// Find our unqualified name. If the DIE has its own DW_AT_name
|
||||
// attribute, then use that; otherwise, check our specification.
|
||||
const string *unqualified_name;
|
||||
if (name_attribute_.empty() && specification_)
|
||||
unqualified_name = &specification_->unqualified_name;
|
||||
else
|
||||
unqualified_name = &name_attribute_;
|
||||
|
||||
// Find the name of our enclosing context. If we have a
|
||||
// specification, it's the specification's enclosing context that
|
||||
// counts; otherwise, use this DIE's context.
|
||||
const string *enclosing_name;
|
||||
if (specification_)
|
||||
enclosing_name = &specification_->enclosing_name;
|
||||
else
|
||||
enclosing_name = &parent_context_->name;
|
||||
|
||||
// If this DIE was marked as a declaration, record its names in the
|
||||
// specification table.
|
||||
if (declaration_) {
|
||||
FileContext *file_context = cu_context_->file_context;
|
||||
Specification spec;
|
||||
spec.enclosing_name = *enclosing_name;
|
||||
spec.unqualified_name = *unqualified_name;
|
||||
file_context->file_private->specifications[offset_] = spec;
|
||||
}
|
||||
|
||||
// Combine the enclosing name and unqualified name to produce our
|
||||
// own fully-qualified name.
|
||||
return cu_context_->language->MakeQualifiedName(*enclosing_name,
|
||||
*unqualified_name);
|
||||
}
|
||||
|
||||
// A handler class for DW_TAG_subprogram DIEs.
|
||||
class DwarfCUToModule::FuncHandler: public GenericDIEHandler {
|
||||
public:
|
||||
FuncHandler(CUContext *cu_context, DIEContext *parent_context,
|
||||
uint64 offset)
|
||||
: GenericDIEHandler(cu_context, parent_context, offset),
|
||||
low_pc_(0), high_pc_(0), abstract_origin_(NULL), inline_(false) { }
|
||||
void ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
void ProcessAttributeSigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data);
|
||||
void ProcessAttributeReference(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data);
|
||||
|
||||
bool EndAttributes();
|
||||
void Finish();
|
||||
|
||||
private:
|
||||
// The fully-qualified name, as derived from name_attribute_,
|
||||
// specification_, parent_context_. Computed in EndAttributes.
|
||||
string name_;
|
||||
uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc
|
||||
const AbstractOrigin* abstract_origin_;
|
||||
bool inline_;
|
||||
};
|
||||
|
||||
void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
// If this attribute is present at all --- even if its value is
|
||||
// DW_INL_not_inlined --- then GCC may cite it as someone else's
|
||||
// DW_AT_abstract_origin attribute.
|
||||
case dwarf2reader::DW_AT_inline: inline_ = true; break;
|
||||
|
||||
case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break;
|
||||
case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break;
|
||||
default:
|
||||
GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::FuncHandler::ProcessAttributeSigned(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data) {
|
||||
switch (attr) {
|
||||
// If this attribute is present at all --- even if its value is
|
||||
// DW_INL_not_inlined --- then GCC may cite it as someone else's
|
||||
// DW_AT_abstract_origin attribute.
|
||||
case dwarf2reader::DW_AT_inline: inline_ = true; break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::FuncHandler::ProcessAttributeReference(
|
||||
enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch(attr) {
|
||||
case dwarf2reader::DW_AT_abstract_origin: {
|
||||
const AbstractOriginByOffset& origins =
|
||||
cu_context_->file_context->file_private->origins;
|
||||
AbstractOriginByOffset::const_iterator origin = origins.find(data);
|
||||
if (origin != origins.end()) {
|
||||
abstract_origin_ = &(origin->second);
|
||||
} else {
|
||||
cu_context_->reporter->UnknownAbstractOrigin(offset_, data);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GenericDIEHandler::ProcessAttributeReference(attr, form, data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::FuncHandler::EndAttributes() {
|
||||
// Compute our name, and record a specification, if appropriate.
|
||||
name_ = ComputeQualifiedName();
|
||||
if (name_.empty() && abstract_origin_) {
|
||||
name_ = abstract_origin_->name;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::FuncHandler::Finish() {
|
||||
// Did we collect the information we need? Not all DWARF function
|
||||
// entries have low and high addresses (for example, inlined
|
||||
// functions that were never used), but all the ones we're
|
||||
// interested in cover a non-empty range of bytes.
|
||||
if (low_pc_ < high_pc_) {
|
||||
// Create a Module::Function based on the data we've gathered, and
|
||||
// add it to the functions_ list.
|
||||
Module::Function *func = new Module::Function;
|
||||
func->name = name_;
|
||||
func->address = low_pc_;
|
||||
func->size = high_pc_ - low_pc_;
|
||||
func->parameter_size = 0;
|
||||
cu_context_->functions.push_back(func);
|
||||
} else if (inline_) {
|
||||
AbstractOrigin origin(name_);
|
||||
cu_context_->file_context->file_private->origins[offset_] = origin;
|
||||
}
|
||||
}
|
||||
|
||||
// A handler for DIEs that contain functions and contribute a
|
||||
// component to their names: namespaces, classes, etc.
|
||||
class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler {
|
||||
public:
|
||||
NamedScopeHandler(CUContext *cu_context, DIEContext *parent_context,
|
||||
uint64 offset)
|
||||
: GenericDIEHandler(cu_context, parent_context, offset) { }
|
||||
bool EndAttributes();
|
||||
DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList &attrs);
|
||||
|
||||
private:
|
||||
DIEContext child_context_; // A context for our children.
|
||||
};
|
||||
|
||||
bool DwarfCUToModule::NamedScopeHandler::EndAttributes() {
|
||||
child_context_.name = ComputeQualifiedName();
|
||||
return true;
|
||||
}
|
||||
|
||||
dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler(
|
||||
uint64 offset,
|
||||
enum DwarfTag tag,
|
||||
const AttributeList &attrs) {
|
||||
switch (tag) {
|
||||
case dwarf2reader::DW_TAG_subprogram:
|
||||
return new FuncHandler(cu_context_, &child_context_, offset);
|
||||
case dwarf2reader::DW_TAG_namespace:
|
||||
case dwarf2reader::DW_TAG_class_type:
|
||||
case dwarf2reader::DW_TAG_structure_type:
|
||||
case dwarf2reader::DW_TAG_union_type:
|
||||
return new NamedScopeHandler(cu_context_, &child_context_, offset);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
void DwarfCUToModule::WarningReporter::CUHeading() {
|
||||
if (printed_cu_header_)
|
||||
return;
|
||||
fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%llx):\n",
|
||||
filename_.c_str(), cu_name_.c_str(), cu_offset_);
|
||||
printed_cu_header_ = true;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset,
|
||||
uint64 target) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_specification"
|
||||
" attribute referring to the die at offset 0x%llx, which either"
|
||||
" was not marked as a declaration, or comes later in the file\n",
|
||||
filename_.c_str(), offset, target);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64 offset,
|
||||
uint64 target) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_abstract_origin"
|
||||
" attribute referring to the die at offset 0x%llx, which either"
|
||||
" was not marked as an inline, or comes later in the file\n",
|
||||
filename_.c_str(), offset, target);
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::MissingSection(const string &name) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n",
|
||||
filename_.c_str(), name.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::BadLineInfoOffset(uint64 offset) {
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: line number data offset beyond end"
|
||||
" of '.debug_line' section\n",
|
||||
filename_.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UncoveredHeading() {
|
||||
if (printed_unpaired_header_)
|
||||
return;
|
||||
CUHeading();
|
||||
fprintf(stderr, "%s: warning: skipping unpaired lines/functions:\n",
|
||||
filename_.c_str());
|
||||
printed_unpaired_header_ = true;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UncoveredFunction(
|
||||
const Module::Function &function) {
|
||||
UncoveredHeading();
|
||||
fprintf(stderr, " function%s: %s\n",
|
||||
function.size == 0 ? " (zero-length)" : "",
|
||||
function.name.c_str());
|
||||
}
|
||||
|
||||
void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) {
|
||||
UncoveredHeading();
|
||||
fprintf(stderr, " line%s: %s:%d at 0x%llx\n",
|
||||
(line.size == 0 ? " (zero-length)" : ""),
|
||||
line.file->name.c_str(), line.number, line.address);
|
||||
}
|
||||
|
||||
DwarfCUToModule::DwarfCUToModule(FileContext *file_context,
|
||||
LineToModuleFunctor *line_reader,
|
||||
WarningReporter *reporter)
|
||||
: line_reader_(line_reader), has_source_line_info_(false) {
|
||||
cu_context_ = new CUContext(file_context, reporter);
|
||||
child_context_ = new DIEContext();
|
||||
}
|
||||
|
||||
DwarfCUToModule::~DwarfCUToModule() {
|
||||
delete cu_context_;
|
||||
delete child_context_;
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
int64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_language: // source language of this CU
|
||||
SetLanguage(static_cast<DwarfLanguage>(data));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
uint64 data) {
|
||||
switch (attr) {
|
||||
case dwarf2reader::DW_AT_stmt_list: // Line number information.
|
||||
has_source_line_info_ = true;
|
||||
source_line_offset_ = data;
|
||||
break;
|
||||
case dwarf2reader::DW_AT_language: // source language of this CU
|
||||
SetLanguage(static_cast<DwarfLanguage>(data));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr,
|
||||
enum DwarfForm form,
|
||||
const string &data) {
|
||||
if (attr == dwarf2reader::DW_AT_name)
|
||||
cu_context_->reporter->SetCUName(data);
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::EndAttributes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler(
|
||||
uint64 offset,
|
||||
enum DwarfTag tag,
|
||||
const AttributeList &attrs) {
|
||||
switch (tag) {
|
||||
case dwarf2reader::DW_TAG_subprogram:
|
||||
return new FuncHandler(cu_context_, child_context_, offset);
|
||||
case dwarf2reader::DW_TAG_namespace:
|
||||
case dwarf2reader::DW_TAG_class_type:
|
||||
case dwarf2reader::DW_TAG_structure_type:
|
||||
case dwarf2reader::DW_TAG_union_type:
|
||||
return new NamedScopeHandler(cu_context_, child_context_, offset);
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::SetLanguage(DwarfLanguage language) {
|
||||
switch (language) {
|
||||
case dwarf2reader::DW_LANG_Java:
|
||||
cu_context_->language = Language::Java;
|
||||
break;
|
||||
|
||||
// DWARF has no generic language code for assembly language; this is
|
||||
// what the GNU toolchain uses.
|
||||
case dwarf2reader::DW_LANG_Mips_Assembler:
|
||||
cu_context_->language = Language::Assembler;
|
||||
break;
|
||||
|
||||
// C++ covers so many cases that it probably has some way to cope
|
||||
// with whatever the other languages throw at us. So make it the
|
||||
// default.
|
||||
//
|
||||
// Objective C and Objective C++ seem to create entries for
|
||||
// methods whose DW_AT_name values are already fully-qualified:
|
||||
// "-[Classname method:]". These appear at the top level.
|
||||
//
|
||||
// DWARF data for C should never include namespaces or functions
|
||||
// nested in struct types, but if it ever does, then C++'s
|
||||
// notation is probably not a bad choice for that.
|
||||
default:
|
||||
case dwarf2reader::DW_LANG_ObjC:
|
||||
case dwarf2reader::DW_LANG_ObjC_plus_plus:
|
||||
case dwarf2reader::DW_LANG_C:
|
||||
case dwarf2reader::DW_LANG_C89:
|
||||
case dwarf2reader::DW_LANG_C99:
|
||||
case dwarf2reader::DW_LANG_C_plus_plus:
|
||||
cu_context_->language = Language::CPlusPlus;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::ReadSourceLines(uint64 offset) {
|
||||
const dwarf2reader::SectionMap §ion_map
|
||||
= cu_context_->file_context->section_map;
|
||||
dwarf2reader::SectionMap::const_iterator map_entry
|
||||
= section_map.find(".debug_line");
|
||||
if (map_entry == section_map.end()) {
|
||||
cu_context_->reporter->MissingSection(".debug_line");
|
||||
return;
|
||||
}
|
||||
const char *section_start = map_entry->second.first;
|
||||
uint64 section_length = map_entry->second.second;
|
||||
if (offset >= section_length) {
|
||||
cu_context_->reporter->BadLineInfoOffset(offset);
|
||||
return;
|
||||
}
|
||||
(*line_reader_)(section_start + offset, section_length - offset,
|
||||
cu_context_->file_context->module, &lines_);
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Return true if ADDRESS falls within the range of ITEM.
|
||||
template <class T>
|
||||
inline bool within(const T &item, Module::Address address) {
|
||||
// Because Module::Address is unsigned, and unsigned arithmetic
|
||||
// wraps around, this will be false if ADDRESS falls before the
|
||||
// start of ITEM, or if it falls after ITEM's end.
|
||||
return address - item.address < item.size;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::AssignLinesToFunctions() {
|
||||
vector<Module::Function *> *functions = &cu_context_->functions;
|
||||
WarningReporter *reporter = cu_context_->reporter;
|
||||
|
||||
// This would be simpler if we assumed that source line entries
|
||||
// don't cross function boundaries. However, there's no real reason
|
||||
// to assume that (say) a series of function definitions on the same
|
||||
// line wouldn't get coalesced into one line number entry. The
|
||||
// DWARF spec certainly makes no such promises.
|
||||
//
|
||||
// So treat the functions and lines as peers, and take the trouble
|
||||
// to compute their ranges' intersections precisely. In any case,
|
||||
// the hair here is a constant factor for performance; the
|
||||
// complexity from here on out is linear.
|
||||
|
||||
// Put both our functions and lines in order by address.
|
||||
sort(functions->begin(), functions->end(),
|
||||
Module::Function::CompareByAddress);
|
||||
sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress);
|
||||
|
||||
// The last line that we used any piece of. We use this only for
|
||||
// generating warnings.
|
||||
const Module::Line *last_line_used = NULL;
|
||||
|
||||
// The last function and line we warned about --- so we can avoid
|
||||
// doing so more than once.
|
||||
const Module::Function *last_function_cited = NULL;
|
||||
const Module::Line *last_line_cited = NULL;
|
||||
|
||||
// Make a single pass through both vectors from lower to higher
|
||||
// addresses, populating each Function's lines vector with lines
|
||||
// from our lines_ vector that fall within the function's address
|
||||
// range.
|
||||
vector<Module::Function *>::iterator func_it = functions->begin();
|
||||
vector<Module::Line>::const_iterator line_it = lines_.begin();
|
||||
|
||||
Module::Address current;
|
||||
|
||||
// Pointers to the referents of func_it and line_it, or NULL if the
|
||||
// iterator is at the end of the sequence.
|
||||
Module::Function *func;
|
||||
const Module::Line *line;
|
||||
|
||||
// Start current at the beginning of the first line or function,
|
||||
// whichever is earlier.
|
||||
if (func_it != functions->end() && line_it != lines_.end()) {
|
||||
func = *func_it;
|
||||
line = &*line_it;
|
||||
current = std::min(func->address, line->address);
|
||||
} else if (line_it != lines_.end()) {
|
||||
func = NULL;
|
||||
line = &*line_it;
|
||||
current = line->address;
|
||||
} else if (func_it != functions->end()) {
|
||||
func = *func_it;
|
||||
line = NULL;
|
||||
current = (*func_it)->address;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
while (func || line) {
|
||||
// This loop has two invariants that hold at the top.
|
||||
//
|
||||
// First, at least one of the iterators is not at the end of its
|
||||
// sequence, and those that are not refer to the earliest
|
||||
// function or line that contains or starts after CURRENT.
|
||||
//
|
||||
// Note that every byte is in one of four states: it is covered
|
||||
// or not covered by a function, and, independently, it is
|
||||
// covered or not covered by a line.
|
||||
//
|
||||
// The second invariant is that CURRENT refers to a byte whose
|
||||
// state is different from its predecessor, or it refers to the
|
||||
// first byte in the address space. In other words, CURRENT is
|
||||
// always the address of a transition.
|
||||
//
|
||||
// Note that, although each iteration advances CURRENT from one
|
||||
// transition address to the next in each iteration, it might
|
||||
// not advance the iterators. Suppose we have a function that
|
||||
// starts with a line, has a gap, and then a second line, and
|
||||
// suppose that we enter an iteration with CURRENT at the end of
|
||||
// the first line. The next transition address is the start of
|
||||
// the second line, after the gap, so the iteration should
|
||||
// advance CURRENT to that point. At the head of that iteration,
|
||||
// the invariants require that the line iterator be pointing at
|
||||
// the second line. But this is also true at the head of the
|
||||
// next. And clearly, the iteration must not change the function
|
||||
// iterator. So neither iterator moves.
|
||||
|
||||
// Assert the first invariant (see above).
|
||||
assert(!func || current < func->address || within(*func, current));
|
||||
assert(!line || current < line->address || within(*line, current));
|
||||
|
||||
// The next transition after CURRENT.
|
||||
Module::Address next_transition;
|
||||
|
||||
// Figure out which state we're in, add lines or warn, and compute
|
||||
// the next transition address.
|
||||
if (func && current >= func->address) {
|
||||
if (line && current >= line->address) {
|
||||
// Covered by both a line and a function.
|
||||
Module::Address func_left = func->size - (current - func->address);
|
||||
Module::Address line_left = line->size - (current - line->address);
|
||||
// This may overflow, but things work out.
|
||||
next_transition = current + std::min(func_left, line_left);
|
||||
Module::Line l = *line;
|
||||
l.address = current;
|
||||
l.size = next_transition - current;
|
||||
func->lines.push_back(l);
|
||||
last_line_used = line;
|
||||
} else {
|
||||
// Covered by a function, but no line.
|
||||
if (func != last_function_cited) {
|
||||
reporter->UncoveredFunction(*func);
|
||||
last_function_cited = func;
|
||||
}
|
||||
if (line && within(*func, line->address))
|
||||
next_transition = line->address;
|
||||
else
|
||||
// If this overflows, we'll catch it below.
|
||||
next_transition = func->address + func->size;
|
||||
}
|
||||
} else {
|
||||
if (line && current >= line->address) {
|
||||
// Covered by a line, but no function.
|
||||
//
|
||||
// If GCC emits padding after one function to align the start
|
||||
// of the next, then it will attribute the padding
|
||||
// instructions to the last source line of function (to reduce
|
||||
// the size of the line number info), but omit it from the
|
||||
// DW_AT_{low,high}_pc range given in .debug_info (since it
|
||||
// costs nothing to be precise there). If we did use at least
|
||||
// some of the line we're about to skip, and it ends at the
|
||||
// start of the next function, then assume this is what
|
||||
// happened, and don't warn.
|
||||
if (line != last_line_cited
|
||||
&& !(func
|
||||
&& line == last_line_used
|
||||
&& func->address - line->address == line->size)) {
|
||||
reporter->UncoveredLine(*line);
|
||||
last_line_cited = line;
|
||||
}
|
||||
if (func && within(*line, func->address))
|
||||
next_transition = func->address;
|
||||
else
|
||||
// If this overflows, we'll catch it below.
|
||||
next_transition = line->address + line->size;
|
||||
} else {
|
||||
// Covered by neither a function nor a line. By the invariant,
|
||||
// both func and line begin after CURRENT. The next transition
|
||||
// is the start of the next function or next line, whichever
|
||||
// is earliest.
|
||||
assert (func || line);
|
||||
if (func && line)
|
||||
next_transition = std::min(func->address, line->address);
|
||||
else if (func)
|
||||
next_transition = func->address;
|
||||
else
|
||||
next_transition = line->address;
|
||||
}
|
||||
}
|
||||
|
||||
// If a function or line abuts the end of the address space, then
|
||||
// next_transition may end up being zero, in which case we've completed
|
||||
// our pass. Handle that here, instead of trying to deal with it in
|
||||
// each place we compute next_transition.
|
||||
if (!next_transition)
|
||||
break;
|
||||
|
||||
// Advance iterators as needed. If lines overlap or functions overlap,
|
||||
// then we could go around more than once. We don't worry too much
|
||||
// about what result we produce in that case, just as long as we don't
|
||||
// hang or crash.
|
||||
while (func_it != functions->end()
|
||||
&& current >= (*func_it)->address
|
||||
&& !within(**func_it, next_transition))
|
||||
func_it++;
|
||||
func = (func_it != functions->end()) ? *func_it : NULL;
|
||||
while (line_it != lines_.end()
|
||||
&& current >= line_it->address
|
||||
&& !within(*line_it, next_transition))
|
||||
line_it++;
|
||||
line = (line_it != lines_.end()) ? &*line_it : NULL;
|
||||
|
||||
// We must make progress.
|
||||
assert(next_transition > current);
|
||||
current = next_transition;
|
||||
}
|
||||
}
|
||||
|
||||
void DwarfCUToModule::Finish() {
|
||||
// Assembly language files have no function data, and that gives us
|
||||
// no place to store our line numbers (even though the GNU toolchain
|
||||
// will happily produce source line info for assembly language
|
||||
// files). To avoid spurious warnings about lines we can't assign
|
||||
// to functions, skip CUs in languages that lack functions.
|
||||
if (!cu_context_->language->HasFunctions())
|
||||
return;
|
||||
|
||||
// Read source line info, if we have any.
|
||||
if (has_source_line_info_)
|
||||
ReadSourceLines(source_line_offset_);
|
||||
|
||||
vector<Module::Function *> *functions = &cu_context_->functions;
|
||||
|
||||
// Dole out lines to the appropriate functions.
|
||||
AssignLinesToFunctions();
|
||||
|
||||
// Add our functions, which now have source lines assigned to them,
|
||||
// to module_.
|
||||
cu_context_->file_context->module->AddFunctions(functions->begin(),
|
||||
functions->end());
|
||||
|
||||
// Ownership of the function objects has shifted from cu_context to
|
||||
// the Module.
|
||||
functions->clear();
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::StartCompilationUnit(uint64 offset,
|
||||
uint8 address_size,
|
||||
uint8 offset_size,
|
||||
uint64 cu_length,
|
||||
uint8 dwarf_version) {
|
||||
return dwarf_version >= 2;
|
||||
}
|
||||
|
||||
bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag,
|
||||
const AttributeList& attrs) {
|
||||
// We don't deal with partial compilation units (the only other tag
|
||||
// likely to be used for root DIE).
|
||||
return tag == dwarf2reader::DW_TAG_compile_unit;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,259 @@
|
|||
// -*- 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 <elf.h>
|
||||
#include <link.h>
|
||||
#include "common/linux/language.h"
|
||||
#include "common/linux/module.h"
|
||||
#include "common/dwarf/bytereader.h"
|
||||
#include "common/dwarf/dwarf2diehandler.h"
|
||||
#include "common/dwarf/dwarf2reader.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using dwarf2reader::AttributeList;
|
||||
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::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.
|
||||
struct FileContext {
|
||||
FileContext(const string &filename_arg, Module *module_arg);
|
||||
~FileContext();
|
||||
|
||||
// The name of this file, for use in error messages.
|
||||
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;
|
||||
|
||||
// Inter-compilation unit data used internally by the handlers.
|
||||
FilePrivate *file_private;
|
||||
};
|
||||
|
||||
// An abstract base class for functors 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 LineToModuleFunctor {
|
||||
public:
|
||||
LineToModuleFunctor() { }
|
||||
virtual ~LineToModuleFunctor() { }
|
||||
|
||||
// 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 operator()(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) { }
|
||||
virtual ~WarningReporter() { }
|
||||
|
||||
// Set the name of the compilation unit we're processing to NAME.
|
||||
virtual void SetCUName(const string &name) { cu_name_ = name; }
|
||||
|
||||
// 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);
|
||||
|
||||
protected:
|
||||
string filename_;
|
||||
uint64 cu_offset_;
|
||||
string cu_name_;
|
||||
bool printed_cu_header_;
|
||||
bool printed_unpaired_header_;
|
||||
|
||||
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,
|
||||
LineToModuleFunctor *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,
|
||||
const AttributeList &attrs);
|
||||
|
||||
// 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,
|
||||
const AttributeList& attrs);
|
||||
|
||||
private:
|
||||
|
||||
// Used internally by the handler. Full definitions are in
|
||||
// dwarf_cu_to_module.cc.
|
||||
struct FilePrivate;
|
||||
struct Specification;
|
||||
struct CUContext;
|
||||
struct DIEContext;
|
||||
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 functor to use to handle line number data.
|
||||
LineToModuleFunctor *line_reader_;
|
||||
|
||||
// This compilation unit's context.
|
||||
CUContext *cu_context_;
|
||||
|
||||
// A context for our children.
|
||||
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__
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,132 @@
|
|||
// 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 "common/linux/dwarf_line_to_module.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] == '/');
|
||||
}
|
||||
|
||||
// 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))
|
||||
return path;
|
||||
return 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] = name;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
std::string full_name;
|
||||
if (dir_num != 0) {
|
||||
DirectoryTable::const_iterator directory_it = directories_.find(dir_num);
|
||||
if (directory_it != directories_.end()) {
|
||||
full_name = ExpandPath(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;
|
||||
}
|
||||
full_name = name; // just treat name as relative
|
||||
}
|
||||
} else {
|
||||
// Directory number zero is the compilation directory; we just report
|
||||
// relative paths in that case.
|
||||
full_name = 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
|
|
@ -0,0 +1,179 @@
|
|||
// -*- 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 "common/linux/module.h"
|
||||
#include "common/dwarf/dwarf2reader.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, vector<Module::Line> *lines)
|
||||
: module_(module),
|
||||
lines_(lines),
|
||||
highest_file_number_(-1),
|
||||
omitted_line_end_(0),
|
||||
warned_bad_file_number_(false),
|
||||
warned_bad_directory_number_(false) { }
|
||||
|
||||
~DwarfLineToModule() { }
|
||||
|
||||
void DefineDir(const std::string &name, uint32 dir_num);
|
||||
void DefineFile(const std::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, std::string> DirectoryTable;
|
||||
typedef std::map<uint32, Module::File *> FileTable;
|
||||
|
||||
// The module we're contributing debugging info to. Owned by our
|
||||
// client.
|
||||
Module *module_;
|
||||
|
||||
// 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
|
|
@ -0,0 +1,339 @@
|
|||
// 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: Unit tests for google_breakpad::DwarfLineToModule.
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/dwarf_line_to_module.h"
|
||||
|
||||
using google_breakpad::DwarfLineToModule;
|
||||
using google_breakpad::Module;
|
||||
using google_breakpad::Module;
|
||||
|
||||
TEST(Simple, One) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("file1", 0x30bf0f27, 0, 0, 0);
|
||||
h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27,
|
||||
0x4c090cbf, 0x1cf9fe0d);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
EXPECT_EQ(1U, files.size());
|
||||
EXPECT_STREQ("file1", files[0]->name.c_str());
|
||||
|
||||
EXPECT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address);
|
||||
EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size);
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
EXPECT_EQ(0x4c090cbf, lines[0].number);
|
||||
}
|
||||
|
||||
TEST(Simple, Many) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 0x838299ab);
|
||||
h.DefineDir("directory2", 0xf85de023);
|
||||
h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0);
|
||||
h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0);
|
||||
h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0);
|
||||
h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0);
|
||||
h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a,
|
||||
0x15b0f0a9U, 0x3ff5abd6U);
|
||||
h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4,
|
||||
0x4d259ce9U, 0x41c5ee32U);
|
||||
h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56,
|
||||
0x1ee9fa4fU, 0xbf70e46aU);
|
||||
h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c,
|
||||
0x77fc280eU, 0x2c4a728cU);
|
||||
h.DefineFile("file3", -1, 0, 0, 0);
|
||||
h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5,
|
||||
0x75047044U, 0xb6a0016cU);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
ASSERT_EQ(5U, files.size());
|
||||
EXPECT_STREQ("directory1/file1", files[0]->name.c_str());
|
||||
EXPECT_STREQ("directory1/file2", files[1]->name.c_str());
|
||||
EXPECT_STREQ("directory2/file1", files[2]->name.c_str());
|
||||
EXPECT_STREQ("directory2/file2", files[3]->name.c_str());
|
||||
EXPECT_STREQ("file3", files[4]->name.c_str());
|
||||
|
||||
ASSERT_EQ(5U, lines.size());
|
||||
|
||||
EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address);
|
||||
EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size);
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
EXPECT_EQ(0x15b0f0a9, lines[0].number);
|
||||
|
||||
EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address);
|
||||
EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size);
|
||||
EXPECT_TRUE(lines[1].file == files[2]);
|
||||
EXPECT_EQ(0x4d259ce9, lines[1].number);
|
||||
|
||||
EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address);
|
||||
EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size);
|
||||
EXPECT_TRUE(lines[2].file == files[1]);
|
||||
EXPECT_EQ(0x1ee9fa4f, lines[2].number);
|
||||
|
||||
EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address);
|
||||
EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size);
|
||||
EXPECT_TRUE(lines[3].file == files[3]);
|
||||
EXPECT_EQ(0x77fc280e, lines[3].number);
|
||||
|
||||
EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address);
|
||||
EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size);
|
||||
EXPECT_TRUE(lines[4].file == files[4]);
|
||||
EXPECT_EQ(0x75047044, lines[4].number);
|
||||
}
|
||||
|
||||
TEST(Filenames, Absolute) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("/absolute", 1, 1, 0, 0);
|
||||
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
ASSERT_EQ(1U, files.size());
|
||||
EXPECT_STREQ("/absolute", files[0]->name.c_str());
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
}
|
||||
|
||||
TEST(Filenames, Relative) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("relative", 1, 1, 0, 0);
|
||||
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
vector<Module::File *> files;
|
||||
m.GetFiles(&files);
|
||||
ASSERT_EQ(1U, files.size());
|
||||
EXPECT_STREQ("directory1/relative", files[0]->name.c_str());
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_TRUE(lines[0].file == files[0]);
|
||||
}
|
||||
|
||||
TEST(Filenames, StrangeFile) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("", 1, 1, 0, 0);
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("directory1/", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
TEST(Filenames, StrangeDirectory) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("", 1);
|
||||
h.DefineFile("file1", 1, 1, 0, 0);
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("/file1", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
TEST(Filenames, StrangeDirectoryAndFile) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("", 1);
|
||||
h.DefineFile("", 1, 1, 0, 0);
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("/", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
// We should silently ignore attempts to define directory number zero,
|
||||
// since that is always the compilation directory.
|
||||
TEST(Errors, DirectoryZero) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory0", 0); // should be ignored
|
||||
h.DefineFile("relative", 1, 0, 0, 0);
|
||||
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("relative", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
// We should refuse to add lines with bogus file numbers. We should
|
||||
// produce only one warning, however.
|
||||
TEST(Errors, BadFileNumber) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("relative", 1, 0, 0, 0);
|
||||
h.AddLine(1, 1, 2, 0, 0); // bad file number
|
||||
h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning)
|
||||
|
||||
EXPECT_EQ(0U, lines.size());
|
||||
}
|
||||
|
||||
// We should treat files with bogus directory numbers as relative to
|
||||
// the compilation unit.
|
||||
TEST(Errors, BadDirectoryNumber) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineDir("directory1", 1);
|
||||
h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number
|
||||
h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning)
|
||||
h.AddLine(1, 1, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str());
|
||||
}
|
||||
|
||||
// We promise not to report empty lines.
|
||||
TEST(Errors, EmptyLine) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(1, 0, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(0U, lines.size());
|
||||
}
|
||||
|
||||
// We are supposed to clip lines that extend beyond the end of the
|
||||
// address space.
|
||||
TEST(Errors, BigLine) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0);
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(1U, lines[0].size);
|
||||
}
|
||||
|
||||
// The 'Omitted' tests verify that we correctly omit line information
|
||||
// for code in sections that the linker has dropped. See "GNU
|
||||
// toolchain omitted sections support" at the top of the
|
||||
// DwarfLineToModule class.
|
||||
|
||||
TEST(Omitted, DroppedThenGood) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0, 10, 1, 83816211, 0); // should be omitted
|
||||
h.AddLine(20, 10, 1, 13059195, 0); // should be recorded
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(13059195, lines[0].number);
|
||||
}
|
||||
|
||||
TEST(Omitted, GoodThenDropped) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded
|
||||
h.AddLine(0, 10, 1, 44793413, 0); // should be omitted
|
||||
|
||||
ASSERT_EQ(1U, lines.size());
|
||||
EXPECT_EQ(41454594, lines[0].number);
|
||||
}
|
||||
|
||||
TEST(Omitted, Mix1) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded
|
||||
h.AddLine(0xdfb5a72d, 10, 1, 39847385, 0); // should be recorded
|
||||
h.AddLine(0, 0x78, 1, 23053829, 0); // should be omitted
|
||||
h.AddLine(0x78, 0x6a, 1, 65317783, 0); // should be omitted
|
||||
h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0); // should be omitted
|
||||
h.AddLine(0x9fe0cea5, 10, 1, 91806582, 0); // should be recorded
|
||||
h.AddLine(0x7e41a109, 10, 1, 56169221, 0); // should be recorded
|
||||
|
||||
ASSERT_EQ(4U, lines.size());
|
||||
EXPECT_EQ(58932642, lines[0].number);
|
||||
EXPECT_EQ(39847385, lines[1].number);
|
||||
EXPECT_EQ(91806582, lines[2].number);
|
||||
EXPECT_EQ(56169221, lines[3].number);
|
||||
}
|
||||
|
||||
TEST(Omitted, Mix2) {
|
||||
Module m("name", "os", "architecture", "id");
|
||||
vector<Module::Line> lines;
|
||||
DwarfLineToModule h(&m, &lines);
|
||||
|
||||
h.DefineFile("filename1", 1, 0, 0, 0);
|
||||
h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted
|
||||
h.AddLine(0xf2, 0xb9, 1, 78958222, 0); // should be omitted
|
||||
h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0); // should be omitted
|
||||
h.AddLine(0x4e4d271e, 9, 1, 67355743, 0); // should be recorded
|
||||
h.AddLine(0xdfb5a72d, 30, 1, 23365776, 0); // should be recorded
|
||||
h.AddLine(0, 0x64, 1, 76196762, 0); // should be omitted
|
||||
h.AddLine(0x64, 0x33, 1, 71066611, 0); // should be omitted
|
||||
h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0); // should be omitted
|
||||
|
||||
ASSERT_EQ(2U, lines.size());
|
||||
EXPECT_EQ(67355743, lines[0].number);
|
||||
EXPECT_EQ(23365776, lines[1].number);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
// 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>
|
||||
|
||||
// language.cc: Subclasses and singletons for google_breakpad::Language.
|
||||
// See language.h for details.
|
||||
|
||||
#include "common/linux/language.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// C++ language-specific operations.
|
||||
class CPPLanguage: public Language {
|
||||
public:
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "::" + name;
|
||||
}
|
||||
};
|
||||
|
||||
const CPPLanguage CPPLanguageSingleton;
|
||||
|
||||
// Java language-specific operations.
|
||||
class JavaLanguage: public Language {
|
||||
public:
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
if (parent_name.empty())
|
||||
return name;
|
||||
else
|
||||
return parent_name + "." + name;
|
||||
}
|
||||
};
|
||||
|
||||
JavaLanguage JavaLanguageSingleton;
|
||||
|
||||
// Assembler language-specific operations.
|
||||
class AssemblerLanguage: public Language {
|
||||
bool HasFunctions() const { return false; }
|
||||
string MakeQualifiedName(const string &parent_name,
|
||||
const string &name) const {
|
||||
return name;
|
||||
}
|
||||
};
|
||||
|
||||
AssemblerLanguage AssemblerLanguageSingleton;
|
||||
|
||||
const Language * const Language::CPlusPlus = &CPPLanguageSingleton;
|
||||
const Language * const Language::Java = &JavaLanguageSingleton;
|
||||
const Language * const Language::Assembler = &AssemblerLanguageSingleton;
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,84 @@
|
|||
// -*- 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>
|
||||
|
||||
// language.h: Define google_breakpad::Language. Instances of
|
||||
// subclasses of this class provide language-appropriate operations
|
||||
// for the Breakpad symbol dumper.
|
||||
|
||||
#ifndef COMMON_LINUX_LANGUAGE_H__
|
||||
#define COMMON_LINUX_LANGUAGE_H__
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::string;
|
||||
|
||||
// An abstract base class for language-specific operations. We choose
|
||||
// an instance of a subclass of this when we find the CU's language.
|
||||
// This class's definitions are appropriate for CUs with no specified
|
||||
// language.
|
||||
class Language {
|
||||
public:
|
||||
// Return true if this language has functions to which we can assign
|
||||
// line numbers. (Debugging info for assembly language, for example,
|
||||
// can have source location information, but does not have functions
|
||||
// recorded using DW_TAG_subprogram DIEs.)
|
||||
virtual bool HasFunctions() const { return true; }
|
||||
|
||||
// Construct a fully-qualified, language-appropriate form of NAME,
|
||||
// given that PARENT_NAME is the name of the construct enclosing
|
||||
// NAME. If PARENT_NAME is the empty string, then NAME is a
|
||||
// top-level name.
|
||||
//
|
||||
// This API sort of assumes that a fully-qualified name is always
|
||||
// some simple textual composition of the unqualified name and its
|
||||
// parent's name, and that we don't need to know anything else about
|
||||
// the parent or the child (say, their DIEs' tags) to do the job.
|
||||
// This is true for the languages we support at the moment, and
|
||||
// keeps things concrete. Perhaps a more refined operation would
|
||||
// take into account the parent and child DIE types, allow languages
|
||||
// to use their own data type for complex parent names, etc. But if
|
||||
// C++ doesn't need all that, who would?
|
||||
virtual string MakeQualifiedName (const string &parent_name,
|
||||
const string &name) const = 0;
|
||||
|
||||
// Instances for specific languages.
|
||||
static const Language * const CPlusPlus,
|
||||
* const Java,
|
||||
* const Assembler;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // COMMON_LINUX_LANGUAGE_H__
|
|
@ -77,7 +77,8 @@
|
|||
* Porting to other related platforms should not be difficult.
|
||||
*/
|
||||
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
|
||||
defined(__mips__) || defined(__PPC__)) && defined(__linux)
|
||||
defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__)) \
|
||||
&& defined(__linux)
|
||||
|
||||
#ifndef SYS_CPLUSPLUS
|
||||
#ifdef __cplusplus
|
||||
|
@ -105,7 +106,6 @@ extern "C" {
|
|||
/* Include definitions of the ABI currently in use. */
|
||||
#include <sgidefs.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
/* As glibc often provides subtly incompatible data structures (and implicit
|
||||
|
@ -217,7 +217,8 @@ struct kernel_rusage {
|
|||
};
|
||||
|
||||
struct siginfo;
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__)
|
||||
#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \
|
||||
|| defined(__PPC__)
|
||||
|
||||
/* include/asm-{arm,i386,mips,ppc}/signal.h */
|
||||
struct kernel_old_sigaction {
|
||||
|
@ -354,7 +355,7 @@ struct kernel_stat64 {
|
|||
#endif
|
||||
|
||||
/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__)
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
|
||||
struct kernel_stat {
|
||||
/* The kernel headers suggest that st_dev and st_rdev should be 32bit
|
||||
* quantities encoding 12bit major and 20bit minor numbers in an interleaved
|
||||
|
@ -520,7 +521,7 @@ struct kernel_statfs {
|
|||
|
||||
/* Definitions missing from the standard header files */
|
||||
#ifndef O_DIRECTORY
|
||||
#if defined(__ARM_ARCH_3__)
|
||||
#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
|
||||
#define O_DIRECTORY 0040000
|
||||
#else
|
||||
#define O_DIRECTORY 0200000
|
||||
|
@ -641,7 +642,7 @@ struct kernel_statfs {
|
|||
#define __NR_move_pages 317
|
||||
#endif
|
||||
/* End of i386 definitions */
|
||||
#elif defined(__ARM_ARCH_3__)
|
||||
#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
|
||||
#ifndef __NR_setresuid
|
||||
#define __NR_setresuid (__NR_SYSCALL_BASE + 164)
|
||||
#define __NR_setresgid (__NR_SYSCALL_BASE + 170)
|
||||
|
@ -715,7 +716,7 @@ struct kernel_statfs {
|
|||
#ifndef __NR_move_pages
|
||||
#define __NR_move_pages (__NR_SYSCALL_BASE + 344)
|
||||
#endif
|
||||
/* End of ARM 3 definitions */
|
||||
/* End of ARM 3/EABI definitions */
|
||||
#elif defined(__x86_64__)
|
||||
#ifndef __NR_setresuid
|
||||
#define __NR_setresuid 117
|
||||
|
@ -1087,7 +1088,8 @@ struct kernel_statfs {
|
|||
#endif
|
||||
|
||||
#undef LSS_RETURN
|
||||
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__))
|
||||
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \
|
||||
|| defined(__ARM_EABI__))
|
||||
/* Failing system calls return a negative result in the range of
|
||||
* -1..-4095. These are "errno" values with the sign inverted.
|
||||
*/
|
||||
|
@ -1526,11 +1528,6 @@ struct kernel_statfs {
|
|||
return res;
|
||||
}
|
||||
#elif defined(__ARM_ARCH_3__)
|
||||
/* Most definitions of _syscallX() neglect to mark "memory" as being
|
||||
* clobbered. This causes problems with compilers, that do a better job
|
||||
* at optimizing across __asm__ calls.
|
||||
* So, we just have to redefine all fo the _syscallX() macros.
|
||||
*/
|
||||
#undef LSS_REG
|
||||
#define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
|
||||
#undef LSS_BODY
|
||||
|
@ -1646,6 +1643,135 @@ struct kernel_statfs {
|
|||
}
|
||||
LSS_RETURN(int, __res);
|
||||
}
|
||||
#elif defined(__ARM_EABI__)
|
||||
/* Most definitions of _syscallX() neglect to mark "memory" as being
|
||||
* clobbered. This causes problems with compilers, that do a better job
|
||||
* at optimizing across __asm__ calls.
|
||||
* So, we just have to redefine all fo the _syscallX() macros.
|
||||
*/
|
||||
#undef LSS_REG
|
||||
#define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
|
||||
#undef LSS_BODY
|
||||
#define LSS_BODY(type,name,args...) \
|
||||
register long __res_r0 __asm__("r0"); \
|
||||
long __res; \
|
||||
__asm__ __volatile__ ("push {r7}\n" \
|
||||
"mov r7, %1\n" \
|
||||
"swi 0x0\n" \
|
||||
"pop {r7}\n" \
|
||||
: "=r"(__res_r0) \
|
||||
: "i"(__NR_##name) , ## args \
|
||||
: "lr", "memory"); \
|
||||
__res = __res_r0; \
|
||||
LSS_RETURN(type, __res)
|
||||
#undef _syscall0
|
||||
#define _syscall0(type, name) \
|
||||
type LSS_NAME(name)() { \
|
||||
LSS_BODY(type, name); \
|
||||
}
|
||||
#undef _syscall1
|
||||
#define _syscall1(type, name, type1, arg1) \
|
||||
type LSS_NAME(name)(type1 arg1) { \
|
||||
LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \
|
||||
}
|
||||
#undef _syscall2
|
||||
#define _syscall2(type, name, type1, arg1, type2, arg2) \
|
||||
type LSS_NAME(name)(type1 arg1, type2 arg2) { \
|
||||
LSS_REG(0, arg1); LSS_REG(1, arg2); \
|
||||
LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \
|
||||
}
|
||||
#undef _syscall3
|
||||
#define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
|
||||
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
|
||||
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
|
||||
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \
|
||||
}
|
||||
#undef _syscall4
|
||||
#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
|
||||
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
|
||||
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
|
||||
LSS_REG(3, arg4); \
|
||||
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \
|
||||
}
|
||||
#undef _syscall5
|
||||
#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
|
||||
type5,arg5) \
|
||||
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
|
||||
type5 arg5) { \
|
||||
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
|
||||
LSS_REG(3, arg4); LSS_REG(4, arg5); \
|
||||
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
|
||||
"r"(__r4)); \
|
||||
}
|
||||
#undef _syscall6
|
||||
#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
|
||||
type5,arg5,type6,arg6) \
|
||||
type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
|
||||
type5 arg5, type6 arg6) { \
|
||||
LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
|
||||
LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \
|
||||
LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
|
||||
"r"(__r4), "r"(__r5)); \
|
||||
}
|
||||
LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
|
||||
int flags, void *arg, int *parent_tidptr,
|
||||
void *newtls, int *child_tidptr) {
|
||||
long __res;
|
||||
{
|
||||
register int __flags __asm__("r0") = flags;
|
||||
register void *__stack __asm__("r1") = child_stack;
|
||||
register void *__ptid __asm__("r2") = parent_tidptr;
|
||||
register void *__tls __asm__("r3") = newtls;
|
||||
register int *__ctid __asm__("r4") = child_tidptr;
|
||||
__asm__ __volatile__(/* if (fn == NULL || child_stack == NULL)
|
||||
* return -EINVAL;
|
||||
*/
|
||||
"cmp %2,#0\n"
|
||||
"cmpne %3,#0\n"
|
||||
"moveq %0,%1\n"
|
||||
"beq 1f\n"
|
||||
|
||||
/* Push "arg" and "fn" onto the stack that will be
|
||||
* used by the child.
|
||||
*/
|
||||
"str %5,[%3,#-4]!\n"
|
||||
"str %2,[%3,#-4]!\n"
|
||||
|
||||
/* %r0 = syscall(%r0 = flags,
|
||||
* %r1 = child_stack,
|
||||
* %r2 = parent_tidptr,
|
||||
* %r3 = newtls,
|
||||
* %r4 = child_tidptr)
|
||||
*/
|
||||
"mov r7, %9\n"
|
||||
"swi 0x0\n"
|
||||
|
||||
/* if (%r0 != 0)
|
||||
* return %r0;
|
||||
*/
|
||||
"movs %0,r0\n"
|
||||
"bne 1f\n"
|
||||
|
||||
/* In the child, now. Call "fn(arg)".
|
||||
*/
|
||||
"ldr r0,[sp, #4]\n"
|
||||
"mov lr,pc\n"
|
||||
"ldr pc,[sp]\n"
|
||||
|
||||
/* Call _exit(%r0).
|
||||
*/
|
||||
"mov r7, %10\n"
|
||||
"swi 0x0\n"
|
||||
"1:\n"
|
||||
: "=r" (__res)
|
||||
: "i"(-EINVAL),
|
||||
"r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
|
||||
"r"(__ptid), "r"(__tls), "r"(__ctid),
|
||||
"i"(__NR_clone), "i"(__NR_exit)
|
||||
: "lr", "memory");
|
||||
}
|
||||
LSS_RETURN(int, __res);
|
||||
}
|
||||
#elif defined(__mips__)
|
||||
#undef LSS_REG
|
||||
#define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \
|
||||
|
@ -2082,8 +2208,10 @@ struct kernel_statfs {
|
|||
LSS_INLINE _syscall0(pid_t, getppid)
|
||||
LSS_INLINE _syscall2(int, getpriority, int, a,
|
||||
int, b)
|
||||
#if !defined(__ARM_EABI__)
|
||||
LSS_INLINE _syscall2(int, getrlimit, int, r,
|
||||
struct kernel_rlimit*, l)
|
||||
#endif
|
||||
LSS_INLINE _syscall1(pid_t, getsid, pid_t, p)
|
||||
LSS_INLINE _syscall0(pid_t, _gettid)
|
||||
LSS_INLINE _syscall5(int, setxattr, const char *,p,
|
||||
|
@ -2237,6 +2365,7 @@ struct kernel_statfs {
|
|||
}
|
||||
#endif
|
||||
#if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
|
||||
defined(__ARM_EABI__) || \
|
||||
(defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32)
|
||||
LSS_INLINE _syscall4(pid_t, wait4, pid_t, p,
|
||||
int*, s, int, o,
|
||||
|
@ -2250,13 +2379,15 @@ struct kernel_statfs {
|
|||
LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m)
|
||||
LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f)
|
||||
#endif
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__)
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__)
|
||||
#define __NR__setfsgid32 __NR_setfsgid32
|
||||
#define __NR__setfsuid32 __NR_setfsuid32
|
||||
#define __NR__setresgid32 __NR_setresgid32
|
||||
#define __NR__setresuid32 __NR_setresuid32
|
||||
#if defined(__ARM_EABI__)
|
||||
LSS_INLINE _syscall2(int, ugetrlimit, int, r,
|
||||
struct kernel_rlimit*, l)
|
||||
#endif
|
||||
LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f)
|
||||
LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f)
|
||||
LSS_INLINE _syscall3(int, _setresgid32, gid_t, r,
|
||||
|
@ -2365,6 +2496,7 @@ struct kernel_statfs {
|
|||
}
|
||||
}
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
|
||||
defined(__ARM_EABI__) || \
|
||||
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__)
|
||||
#define __NR__sigaction __NR_sigaction
|
||||
#define __NR__sigpending __NR_sigpending
|
||||
|
@ -2375,7 +2507,9 @@ struct kernel_statfs {
|
|||
struct kernel_stat64 *, b)
|
||||
LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
|
||||
loff_t *, res, uint, wh)
|
||||
#if !defined(__ARM_EABI__)
|
||||
LSS_INLINE _syscall1(void*, mmap, void*, a)
|
||||
#endif
|
||||
LSS_INLINE _syscall6(void*, mmap2, void*, s,
|
||||
size_t, l, int, p,
|
||||
int, f, int, d,
|
||||
|
@ -2590,12 +2724,24 @@ struct kernel_statfs {
|
|||
LSS_SC_BODY(4, int, 8, d, type, protocol, sv);
|
||||
}
|
||||
#endif
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
|
||||
#if defined(__ARM_EABI__)
|
||||
LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg,
|
||||
int, flags);
|
||||
LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*,
|
||||
msg, int, flags);
|
||||
LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t, len,
|
||||
int, falgs, const struct kernel_sockaddr*, to,
|
||||
unsigned int, tolen);
|
||||
LSS_INLINE _syscall2(int, shutdown, int, s, int, how);
|
||||
LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol);
|
||||
LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol,
|
||||
int*, sv);
|
||||
#endif
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
|
||||
(defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
|
||||
#define __NR__socketcall __NR_socketcall
|
||||
LSS_INLINE _syscall2(int, _socketcall, int, c,
|
||||
va_list, a)
|
||||
|
||||
LSS_INLINE int LSS_NAME(socketcall)(int op, ...) {
|
||||
int rc;
|
||||
va_list ap;
|
||||
|
@ -2604,29 +2750,24 @@ struct kernel_statfs {
|
|||
va_end(ap);
|
||||
return rc;
|
||||
}
|
||||
|
||||
LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
|
||||
int flags){
|
||||
return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags);
|
||||
}
|
||||
|
||||
LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
|
||||
const struct kernel_msghdr *msg,
|
||||
int flags) {
|
||||
return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags);
|
||||
}
|
||||
|
||||
LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
|
||||
int flags,
|
||||
const struct kernel_sockaddr *to,
|
||||
unsigned int tolen) {
|
||||
return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen);
|
||||
}
|
||||
|
||||
LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
|
||||
return LSS_NAME(socketcall)(13, s, how);
|
||||
}
|
||||
|
||||
LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
|
||||
return LSS_NAME(socketcall)(1, domain, type, protocol);
|
||||
}
|
||||
|
@ -2673,6 +2814,7 @@ struct kernel_statfs {
|
|||
#endif
|
||||
/* TODO(csilvers): see if ppc can/should support this as well */
|
||||
#if defined(__i386__) || defined(__ARM_ARCH_3__) || \
|
||||
defined(__ARM_EABI__) || \
|
||||
(defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64)
|
||||
#define __NR__statfs64 __NR_statfs64
|
||||
#define __NR__fstatfs64 __NR_fstatfs64
|
||||
|
@ -2743,8 +2885,13 @@ struct kernel_statfs {
|
|||
switch (name) {
|
||||
case _SC_OPEN_MAX: {
|
||||
struct kernel_rlimit limit;
|
||||
#if defined(__ARM_EABI__)
|
||||
return LSS_NAME(ugetrlimit)(RLIMIT_NOFILE, &limit) < 0
|
||||
? 8192 : limit.rlim_cur;
|
||||
#else
|
||||
return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0
|
||||
? 8192 : limit.rlim_cur;
|
||||
? 8192 : limit.rlim_cur;
|
||||
#endif
|
||||
}
|
||||
case _SC_PAGESIZE:
|
||||
return __getpagesize();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -27,8 +27,13 @@
|
|||
// (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>
|
||||
|
||||
// module.cc: Implement google_breakpad::Module. See module.h.
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
#include "common/linux/module.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
@ -47,6 +52,9 @@ Module::~Module() {
|
|||
for (vector<Function *>::iterator it = functions_.begin();
|
||||
it != functions_.end(); it++)
|
||||
delete *it;
|
||||
for (vector<StackFrameEntry *>::iterator it = stack_frame_entries_.begin();
|
||||
it != stack_frame_entries_.end(); it++)
|
||||
delete *it;
|
||||
}
|
||||
|
||||
void Module::SetLoadAddress(Address address) {
|
||||
|
@ -62,6 +70,15 @@ void Module::AddFunctions(vector<Function *>::iterator begin,
|
|||
functions_.insert(functions_.end(), begin, end);
|
||||
}
|
||||
|
||||
void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) {
|
||||
stack_frame_entries_.push_back(stack_frame_entry);
|
||||
}
|
||||
|
||||
void Module::GetFunctions(vector<Function *> *vec,
|
||||
vector<Function *>::iterator i) {
|
||||
vec->insert(i, functions_.begin(), functions_.end());
|
||||
}
|
||||
|
||||
Module::File *Module::FindFile(const string &name) {
|
||||
// A tricky bit here. The key of each map entry needs to be a
|
||||
// pointer to the entry's File's name string. This means that we
|
||||
|
@ -77,10 +94,10 @@ Module::File *Module::FindFile(const string &name) {
|
|||
if (destiny == files_.end()
|
||||
|| *destiny->first != name) { // Repeated string comparison, boo hoo.
|
||||
File *file = new File;
|
||||
file->name_ = name;
|
||||
file->source_id_ = -1;
|
||||
file->name = name;
|
||||
file->source_id = -1;
|
||||
destiny = files_.insert(destiny,
|
||||
FileByNameMap::value_type(&file->name_, file));
|
||||
FileByNameMap::value_type(&file->name, file));
|
||||
}
|
||||
return destiny->second;
|
||||
}
|
||||
|
@ -90,20 +107,35 @@ Module::File *Module::FindFile(const char *name) {
|
|||
return FindFile(name_string);
|
||||
}
|
||||
|
||||
Module::File *Module::FindExistingFile(const string &name) {
|
||||
FileByNameMap::iterator it = files_.find(&name);
|
||||
return (it == files_.end()) ? NULL : it->second;
|
||||
}
|
||||
|
||||
void Module::GetFiles(vector<File *> *vec) {
|
||||
vec->clear();
|
||||
for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++)
|
||||
vec->push_back(it->second);
|
||||
}
|
||||
|
||||
void Module::GetStackFrameEntries(vector<StackFrameEntry *> *vec) {
|
||||
*vec = stack_frame_entries_;
|
||||
}
|
||||
|
||||
void Module::AssignSourceIds() {
|
||||
// First, give every source file an id of -1.
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++)
|
||||
file_it->second->source_id_ = -1;
|
||||
file_it->second->source_id = -1;
|
||||
|
||||
// Next, mark all files actually cited by our functions' line number
|
||||
// info, by setting each one's source id to zero.
|
||||
for (vector<Function *>::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end(); func_it++) {
|
||||
Function *func = *func_it;
|
||||
for (vector<Line>::iterator line_it = func->lines_.begin();
|
||||
line_it != func->lines_.end(); line_it++)
|
||||
line_it->file_->source_id_ = 0;
|
||||
for (vector<Line>::iterator line_it = func->lines.begin();
|
||||
line_it != func->lines.end(); line_it++)
|
||||
line_it->file->source_id = 0;
|
||||
}
|
||||
|
||||
// Finally, assign source ids to those files that have been marked.
|
||||
|
@ -113,8 +145,8 @@ void Module::AssignSourceIds() {
|
|||
int next_source_id = 0;
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++)
|
||||
if (! file_it->second->source_id_)
|
||||
file_it->second->source_id_ = next_source_id++;
|
||||
if (! file_it->second->source_id)
|
||||
file_it->second->source_id = next_source_id++;
|
||||
}
|
||||
|
||||
bool Module::ReportError() {
|
||||
|
@ -123,20 +155,33 @@ bool Module::ReportError() {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Module::WriteRuleMap(const RuleMap &rule_map, FILE *stream) {
|
||||
for (RuleMap::const_iterator it = rule_map.begin();
|
||||
it != rule_map.end(); it++) {
|
||||
if (it != rule_map.begin() &&
|
||||
0 > putc(' ', stream))
|
||||
return false;
|
||||
if (0 > fprintf(stream, "%s: %s", it->first.c_str(), it->second.c_str()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Module::Write(FILE *stream) {
|
||||
if (0 > fprintf(stream, "MODULE %s %s %s %s\n",
|
||||
os_.c_str(), architecture_.c_str(), id_.c_str(),
|
||||
name_.c_str()))
|
||||
return ReportError();
|
||||
|
||||
// Write out files.
|
||||
AssignSourceIds();
|
||||
|
||||
// Write out files.
|
||||
for (FileByNameMap::iterator file_it = files_.begin();
|
||||
file_it != files_.end(); file_it++) {
|
||||
File *file = file_it->second;
|
||||
if (file->source_id_ >= 0) {
|
||||
if (file->source_id >= 0) {
|
||||
if (0 > fprintf(stream, "FILE %d %s\n",
|
||||
file->source_id_, file->name_.c_str()))
|
||||
file->source_id, file->name.c_str()))
|
||||
return ReportError();
|
||||
}
|
||||
}
|
||||
|
@ -145,22 +190,45 @@ bool Module::Write(FILE *stream) {
|
|||
for (vector<Function *>::const_iterator func_it = functions_.begin();
|
||||
func_it != functions_.end(); func_it++) {
|
||||
Function *func = *func_it;
|
||||
if (0 > fprintf(stream, "FUNC %lx %lx %lu %s\n",
|
||||
(unsigned long) (func->address_ - load_address_),
|
||||
(unsigned long) func->size_,
|
||||
(unsigned long) func->parameter_size_,
|
||||
func->name_.c_str()))
|
||||
if (0 > fprintf(stream, "FUNC %llx %llx %llx %s\n",
|
||||
(unsigned long long) (func->address - load_address_),
|
||||
(unsigned long long) func->size,
|
||||
(unsigned long long) func->parameter_size,
|
||||
func->name.c_str()))
|
||||
return ReportError();
|
||||
for (vector<Line>::iterator line_it = func->lines_.begin();
|
||||
line_it != func->lines_.end(); line_it++)
|
||||
if (0 > fprintf(stream, "%lx %lx %d %d\n",
|
||||
(unsigned long) (line_it->address_ - load_address_),
|
||||
(unsigned long) line_it->size_,
|
||||
line_it->number_,
|
||||
line_it->file_->source_id_))
|
||||
for (vector<Line>::iterator line_it = func->lines.begin();
|
||||
line_it != func->lines.end(); line_it++)
|
||||
if (0 > fprintf(stream, "%llx %llx %d %d\n",
|
||||
(unsigned long long) (line_it->address - load_address_),
|
||||
(unsigned long long) line_it->size,
|
||||
line_it->number,
|
||||
line_it->file->source_id))
|
||||
return ReportError();
|
||||
}
|
||||
|
||||
// Write out 'STACK CFI INIT' and 'STACK CFI' records.
|
||||
vector<StackFrameEntry *>::const_iterator frame_it;
|
||||
for (frame_it = stack_frame_entries_.begin();
|
||||
frame_it != stack_frame_entries_.end(); frame_it++) {
|
||||
StackFrameEntry *entry = *frame_it;
|
||||
if (0 > fprintf(stream, "STACK CFI INIT %llx %llx ",
|
||||
(unsigned long long) entry->address - load_address_,
|
||||
(unsigned long long) entry->size)
|
||||
|| !WriteRuleMap(entry->initial_rules, stream)
|
||||
|| 0 > putc('\n', stream))
|
||||
return ReportError();
|
||||
|
||||
// Write out this entry's delta rules as 'STACK CFI' records.
|
||||
for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin();
|
||||
delta_it != entry->rule_changes.end(); delta_it++) {
|
||||
if (0 > fprintf(stream, "STACK CFI %llx ",
|
||||
(unsigned long long) delta_it->first - load_address_)
|
||||
|| !WriteRuleMap(delta_it->second, stream)
|
||||
|| 0 > putc('\n', stream))
|
||||
return ReportError();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright (c) 2009, Google Inc. -*- mode: c++ -*-
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -27,7 +29,11 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// module.h: defines google_breakpad::Module, for writing breakpad symbol files
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// module.h: Define google_breakpad::Module. A Module holds debugging
|
||||
// information, and can write that information out as a Breakpad
|
||||
// symbol file.
|
||||
|
||||
#ifndef COMMON_LINUX_MODULE_H__
|
||||
#define COMMON_LINUX_MODULE_H__
|
||||
|
@ -65,12 +71,12 @@ class Module {
|
|||
// A source file.
|
||||
struct File {
|
||||
// The name of the source file.
|
||||
string name_;
|
||||
string name;
|
||||
|
||||
// The file's source id. The Write member function clears this
|
||||
// field and assigns source ids a fresh, so any value placed here
|
||||
// before calling Write will be lost.
|
||||
int source_id_;
|
||||
int source_id;
|
||||
};
|
||||
|
||||
// A function.
|
||||
|
@ -78,21 +84,21 @@ class Module {
|
|||
// For sorting by address. (Not style-guide compliant, but it's
|
||||
// stupid not to put this in the struct.)
|
||||
static bool CompareByAddress(const Function *x, const Function *y) {
|
||||
return x->address_ < y->address_;
|
||||
return x->address < y->address;
|
||||
}
|
||||
|
||||
// The function's name.
|
||||
string name_;
|
||||
string name;
|
||||
|
||||
// The start address and length of the function's code.
|
||||
Address address_, size_;
|
||||
Address address, size;
|
||||
|
||||
// The function's parameter size.
|
||||
Address parameter_size_;
|
||||
Address parameter_size;
|
||||
|
||||
// Source lines belonging to this function, sorted by increasing
|
||||
// address.
|
||||
vector<Line> lines_;
|
||||
vector<Line> lines;
|
||||
};
|
||||
|
||||
// A source line.
|
||||
|
@ -100,12 +106,41 @@ class Module {
|
|||
// For sorting by address. (Not style-guide compliant, but it's
|
||||
// stupid not to put this in the struct.)
|
||||
static bool CompareByAddress(const Module::Line &x, const Module::Line &y) {
|
||||
return x.address_ < y.address_;
|
||||
return x.address < y.address;
|
||||
}
|
||||
|
||||
Address address_, size_; // The address and size of the line's code.
|
||||
File *file_; // The source file.
|
||||
int number_; // The source line number.
|
||||
Address address, size; // The address and size of the line's code.
|
||||
File *file; // The source file.
|
||||
int number; // The source line number.
|
||||
};
|
||||
|
||||
// A map from register names to postfix expressions that recover
|
||||
// their their values. This can represent a complete set of rules to
|
||||
// follow at some address, or a set of changes to be applied to an
|
||||
// extant set of rules.
|
||||
typedef map<string, string> RuleMap;
|
||||
|
||||
// A map from addresses to RuleMaps, representing changes that take
|
||||
// effect at given addresses.
|
||||
typedef map<Address, RuleMap> RuleChangeMap;
|
||||
|
||||
// A range of 'STACK CFI' stack walking information. An instance of
|
||||
// this structure corresponds to a 'STACK CFI INIT' record and the
|
||||
// subsequent 'STACK CFI' records that fall within its range.
|
||||
struct StackFrameEntry {
|
||||
// The starting address and number of bytes of machine code this
|
||||
// entry covers.
|
||||
Address address, size;
|
||||
|
||||
// The initial register recovery rules, in force at the starting
|
||||
// address.
|
||||
RuleMap initial_rules;
|
||||
|
||||
// A map from addresses to rule changes. To find the rules in
|
||||
// force at a given address, start with initial_rules, and then
|
||||
// apply the changes given in this map for all addresses up to and
|
||||
// including the address you're interested in.
|
||||
RuleChangeMap rule_changes;
|
||||
};
|
||||
|
||||
// Create a new module with the given name, operating system,
|
||||
|
@ -123,26 +158,64 @@ class Module {
|
|||
void SetLoadAddress(Address load_address);
|
||||
|
||||
// Add FUNCTION to the module.
|
||||
// Destroying this module frees all Function objects that have been
|
||||
// added with this function.
|
||||
// This module owns all Function objects added with this function:
|
||||
// destroying the module destroys them as well.
|
||||
void AddFunction(Function *function);
|
||||
|
||||
// Add all the functions in [BEGIN,END) to the module.
|
||||
// Destroying this module frees all Function objects that have been
|
||||
// added with this function.
|
||||
// This module owns all Function objects added with this function:
|
||||
// destroying the module destroys them as well.
|
||||
void AddFunctions(vector<Function *>::iterator begin,
|
||||
vector<Function *>::iterator end);
|
||||
|
||||
// If this module has a file named NAME, return a pointer to it. If
|
||||
// Add STACK_FRAME_ENTRY to the module.
|
||||
//
|
||||
// This module owns all StackFrameEntry objects added with this
|
||||
// function: destroying the module destroys them as well.
|
||||
void AddStackFrameEntry(StackFrameEntry *stack_frame_entry);
|
||||
|
||||
// If this module has a file named NAME, return a pointer to it. If
|
||||
// it has none, then create one and return a pointer to the new
|
||||
// file. Destroying this module frees all File objects that have
|
||||
// been created using this function, or with Insert.
|
||||
// file. This module owns all File objects created using these
|
||||
// functions; destroying the module destroys them as well.
|
||||
File *FindFile(const string &name);
|
||||
File *FindFile(const char *name);
|
||||
|
||||
// Write this module to STREAM in the breakpad symbol format.
|
||||
// Return true if all goes well, or false if an error occurs. This
|
||||
// method writes out:
|
||||
// If this module has a file named NAME, return a pointer to it.
|
||||
// Otherwise, return NULL.
|
||||
File *FindExistingFile(const string &name);
|
||||
|
||||
// Insert pointers to the functions added to this module at I in
|
||||
// VEC. The pointed-to Functions are still owned by this module.
|
||||
// (Since this is effectively a copy of the function list, this is
|
||||
// mostly useful for testing; other uses should probably get a more
|
||||
// appropriate interface.)
|
||||
void GetFunctions(vector<Function *> *vec, vector<Function *>::iterator i);
|
||||
|
||||
// Clear VEC and fill it with pointers to the Files added to this
|
||||
// module, sorted by name. The pointed-to Files are still owned by
|
||||
// this module. (Since this is effectively a copy of the file list,
|
||||
// this is mostly useful for testing; other uses should probably get
|
||||
// a more appropriate interface.)
|
||||
void GetFiles(vector<File *> *vec);
|
||||
|
||||
// Clear VEC and fill it with pointers to the StackFrameEntry
|
||||
// objects that have been added to this module. (Since this is
|
||||
// effectively a copy of the stack frame entry list, this is mostly
|
||||
// useful for testing; other uses should probably get
|
||||
// a more appropriate interface.)
|
||||
void GetStackFrameEntries(vector<StackFrameEntry *> *vec);
|
||||
|
||||
// Find those files in this module that are actually referred to by
|
||||
// functions' line number data, and assign them source id numbers.
|
||||
// Set the source id numbers for all other files --- unused by the
|
||||
// source line data --- to -1. We do this before writing out the
|
||||
// symbol file, at which point we omit any unused files.
|
||||
void AssignSourceIds();
|
||||
|
||||
// Call AssignSourceIds, and write this module to STREAM in the
|
||||
// breakpad symbol format. Return true if all goes well, or false if
|
||||
// an error occurs. This method writes out:
|
||||
// - a header based on the values given to the constructor,
|
||||
// - the source files added via FindFile, and finally
|
||||
// - the functions added via AddFunctions, each with its lines.
|
||||
|
@ -152,17 +225,15 @@ class Module {
|
|||
|
||||
private:
|
||||
|
||||
// Find those files in this module that are actually referred to by
|
||||
// functions' line number data, and assign them source id numbers.
|
||||
// Set the source id numbers for all other files --- unused by the
|
||||
// source line data --- to -1. We do this before writing out the
|
||||
// symbol file, at which point we omit any unused files.
|
||||
void AssignSourceIds();
|
||||
|
||||
// Report an error that has occurred writing the symbol file, using
|
||||
// errno to find the appropriate cause. Return false.
|
||||
static bool ReportError();
|
||||
|
||||
// Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI'
|
||||
// records, without a final newline. Return true if all goes well;
|
||||
// if an error occurs, return false, and leave errno set.
|
||||
static bool WriteRuleMap(const RuleMap &rule_map, FILE *stream);
|
||||
|
||||
// Module header entries.
|
||||
string name_, os_, architecture_, id_;
|
||||
|
||||
|
@ -186,6 +257,10 @@ private:
|
|||
// point to.
|
||||
FileByNameMap files_; // This module's source files.
|
||||
vector<Function *> functions_; // This module's functions.
|
||||
|
||||
// The module owns all the call frame info entries that have been
|
||||
// added to it.
|
||||
vector<StackFrameEntry *> stack_frame_entries_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
|
|
@ -0,0 +1,396 @@
|
|||
// 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>
|
||||
|
||||
// module_unittest.cc: Unit tests for google_breakpad::Module.
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "common/linux/module.h"
|
||||
|
||||
using google_breakpad::Module;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using testing::ContainerEq;
|
||||
|
||||
// Return a FILE * referring to a temporary file that will be deleted
|
||||
// automatically when the stream is closed or the program exits.
|
||||
FILE *checked_tmpfile() {
|
||||
FILE *f = tmpfile();
|
||||
if (!f) {
|
||||
fprintf(stderr, "error creating temporary file: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
// Read from STREAM until end of file, and return the contents as a
|
||||
// string.
|
||||
string checked_read(FILE *stream) {
|
||||
string contents;
|
||||
int c;
|
||||
while ((c = getc(stream)) != EOF)
|
||||
contents.push_back(c);
|
||||
if (ferror(stream)) {
|
||||
fprintf(stderr, "error reading temporary file contents: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
// Apply 'fflush' to STREAM, and check for errors.
|
||||
void checked_fflush(FILE *stream) {
|
||||
if (fflush(stream) == EOF) {
|
||||
fprintf(stderr, "error flushing temporary file stream: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply 'fclose' to STREAM, and check for errors.
|
||||
void checked_fclose(FILE *stream) {
|
||||
if (fclose(stream) == EOF) {
|
||||
fprintf(stderr, "error closing temporary file stream: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#define MODULE_NAME "name with spaces"
|
||||
#define MODULE_OS "os-name"
|
||||
#define MODULE_ARCH "architecture"
|
||||
#define MODULE_ID "id-string"
|
||||
|
||||
TEST(Write, Header) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, OneLineFunc) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
Module::File *file = m.FindFile("file_name.cc");
|
||||
Module::Function *function = new(Module::Function);
|
||||
function->name = "function_name";
|
||||
function->address = 0xe165bf8023b9d9abLL;
|
||||
function->size = 0x1e4bb0eb1cbf5b09LL;
|
||||
function->parameter_size = 0x772beee89114358aLL;
|
||||
Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL,
|
||||
file, 67519080 };
|
||||
function->lines.push_back(line);
|
||||
m.AddFunction(function);
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FILE 0 file_name.cc\n"
|
||||
"FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a"
|
||||
" function_name\n"
|
||||
"e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, RelativeLoadAddress) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
m.SetLoadAddress(0x2ab698b0b6407073LL);
|
||||
|
||||
// Some source files. We will expect to see them in lexicographic order.
|
||||
Module::File *file1 = m.FindFile("filename-b.cc");
|
||||
Module::File *file2 = m.FindFile("filename-a.cc");
|
||||
|
||||
// A function.
|
||||
Module::Function *function = new(Module::Function);
|
||||
function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)";
|
||||
function->address = 0xbec774ea5dd935f3LL;
|
||||
function->size = 0x2922088f98d3f6fcLL;
|
||||
function->parameter_size = 0xe5e9aa008bd5f0d0LL;
|
||||
|
||||
// Some source lines. The module should not sort these.
|
||||
Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL,
|
||||
file1, 41676901 };
|
||||
Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL,
|
||||
file2, 67519080 };
|
||||
function->lines.push_back(line2);
|
||||
function->lines.push_back(line1);
|
||||
|
||||
m.AddFunction(function);
|
||||
|
||||
// Some stack information.
|
||||
Module::StackFrameEntry *entry = new Module::StackFrameEntry();
|
||||
entry->address = 0x30f9e5c83323973dULL;
|
||||
entry->size = 0x49fc9ca7c7c13dc2ULL;
|
||||
entry->initial_rules[".cfa"] = "he was a handsome man";
|
||||
entry->initial_rules["and"] = "what i want to know is";
|
||||
entry->rule_changes[0x30f9e5c83323973eULL]["how"] =
|
||||
"do you like your blueeyed boy";
|
||||
entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death";
|
||||
m.AddStackFrameEntry(entry);
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FILE 0 filename-a.cc\n"
|
||||
"FILE 1 filename-b.cc\n"
|
||||
"FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0"
|
||||
" A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n"
|
||||
"b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n"
|
||||
"9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n"
|
||||
"STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2"
|
||||
" .cfa: he was a handsome man"
|
||||
" and: what i want to know is\n"
|
||||
"STACK CFI 6434d177ce326cb"
|
||||
" Mister: Death"
|
||||
" how: do you like your blueeyed boy\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Write, OmitUnusedFiles) {
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// Create some source files.
|
||||
Module::File *file1 = m.FindFile("filename1");
|
||||
m.FindFile("filename2"); // not used by any line
|
||||
Module::File *file3 = m.FindFile("filename3");
|
||||
|
||||
// Create a function.
|
||||
Module::Function *function = new(Module::Function);
|
||||
function->name = "function_name";
|
||||
function->address = 0x9b926d464f0b9384LL;
|
||||
function->size = 0x4f524a4ba795e6a6LL;
|
||||
function->parameter_size = 0xbbe8133a6641c9b7LL;
|
||||
|
||||
// Source files that refer to some files, but not others.
|
||||
Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL,
|
||||
file1, 137850127 };
|
||||
Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL,
|
||||
file3, 28113549 };
|
||||
function->lines.push_back(line1);
|
||||
function->lines.push_back(line2);
|
||||
m.AddFunction(function);
|
||||
|
||||
m.AssignSourceIds();
|
||||
|
||||
vector<Module::File *> vec;
|
||||
m.GetFiles(&vec);
|
||||
EXPECT_EQ((size_t) 3, vec.size());
|
||||
EXPECT_STREQ("filename1", vec[0]->name.c_str());
|
||||
EXPECT_NE(-1, vec[0]->source_id);
|
||||
// Expect filename2 not to be used.
|
||||
EXPECT_STREQ("filename2", vec[1]->name.c_str());
|
||||
EXPECT_EQ(-1, vec[1]->source_id);
|
||||
EXPECT_STREQ("filename3", vec[2]->name.c_str());
|
||||
EXPECT_NE(-1, vec[2]->source_id);
|
||||
|
||||
FILE *f = checked_tmpfile();
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FILE 0 filename1\n"
|
||||
"FILE 1 filename3\n"
|
||||
"FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7"
|
||||
" function_name\n"
|
||||
"595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n"
|
||||
"401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n",
|
||||
contents.c_str());
|
||||
}
|
||||
|
||||
TEST(Construct, AddFunctions) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// Two functions.
|
||||
Module::Function *function1 = new(Module::Function);
|
||||
function1->name = "_without_form";
|
||||
function1->address = 0xd35024aa7ca7da5cLL;
|
||||
function1->size = 0x200b26e605f99071LL;
|
||||
function1->parameter_size = 0xf14ac4fed48c4a99LL;
|
||||
|
||||
Module::Function *function2 = new(Module::Function);
|
||||
function2->name = "_and_void";
|
||||
function2->address = 0x2987743d0b35b13fLL;
|
||||
function2->size = 0xb369db048deb3010LL;
|
||||
function2->parameter_size = 0x938e556cb5a79988LL;
|
||||
|
||||
// Put them in a vector.
|
||||
vector<Module::Function *> vec;
|
||||
vec.push_back(function1);
|
||||
vec.push_back(function2);
|
||||
|
||||
m.AddFunctions(vec.begin(), vec.end());
|
||||
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99"
|
||||
" _without_form\n"
|
||||
"FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988"
|
||||
" _and_void\n",
|
||||
contents.c_str());
|
||||
|
||||
// Check that m.GetFunctions returns the functions we expect.
|
||||
vec.clear();
|
||||
m.GetFunctions(&vec, vec.end());
|
||||
EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1));
|
||||
EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2));
|
||||
EXPECT_EQ((size_t) 2, vec.size());
|
||||
}
|
||||
|
||||
TEST(Construct, AddFrames) {
|
||||
FILE *f = checked_tmpfile();
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
|
||||
// First STACK CFI entry, with no initial rules or deltas.
|
||||
Module::StackFrameEntry *entry1 = new Module::StackFrameEntry();
|
||||
entry1->address = 0xddb5f41285aa7757ULL;
|
||||
entry1->size = 0x1486493370dc5073ULL;
|
||||
m.AddStackFrameEntry(entry1);
|
||||
|
||||
// Second STACK CFI entry, with initial rules but no deltas.
|
||||
Module::StackFrameEntry *entry2 = new Module::StackFrameEntry();
|
||||
entry2->address = 0x8064f3af5e067e38ULL;
|
||||
entry2->size = 0x0de2a5ee55509407ULL;
|
||||
entry2->initial_rules[".cfa"] = "I think that I shall never see";
|
||||
entry2->initial_rules["stromboli"] = "a poem lovely as a tree";
|
||||
entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest";
|
||||
m.AddStackFrameEntry(entry2);
|
||||
|
||||
// Third STACK CFI entry, with initial rules and deltas.
|
||||
Module::StackFrameEntry *entry3 = new Module::StackFrameEntry();
|
||||
entry3->address = 0x5e8d0db0a7075c6cULL;
|
||||
entry3->size = 0x1c7edb12a7aea229ULL;
|
||||
entry3->initial_rules[".cfa"] = "Whose woods are these";
|
||||
entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] =
|
||||
"the village though";
|
||||
entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
|
||||
"he will not see me stopping here";
|
||||
entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] =
|
||||
"his house is in";
|
||||
entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] =
|
||||
"I think I know";
|
||||
m.AddStackFrameEntry(entry3);
|
||||
|
||||
// Check that Write writes STACK CFI records properly.
|
||||
m.Write(f);
|
||||
checked_fflush(f);
|
||||
rewind(f);
|
||||
string contents = checked_read(f);
|
||||
checked_fclose(f);
|
||||
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
|
||||
"STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n"
|
||||
"STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407"
|
||||
" .cfa: I think that I shall never see"
|
||||
" cannoli: a tree whose hungry mouth is prest"
|
||||
" stromboli: a poem lovely as a tree\n"
|
||||
"STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229"
|
||||
" .cfa: Whose woods are these\n"
|
||||
"STACK CFI 36682fad3763ffff"
|
||||
" .cfa: I think I know"
|
||||
" stromboli: his house is in\n"
|
||||
"STACK CFI 47ceb0f63c269d7f"
|
||||
" calzone: the village though"
|
||||
" cannoli: he will not see me stopping here\n",
|
||||
contents.c_str());
|
||||
|
||||
// Check that GetStackFrameEntries works.
|
||||
vector<Module::StackFrameEntry *> entries;
|
||||
m.GetStackFrameEntries(&entries);
|
||||
ASSERT_EQ(3U, entries.size());
|
||||
// Check first entry.
|
||||
EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address);
|
||||
EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size);
|
||||
ASSERT_EQ(0U, entries[0]->initial_rules.size());
|
||||
ASSERT_EQ(0U, entries[0]->rule_changes.size());
|
||||
// Check second entry.
|
||||
EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address);
|
||||
EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size);
|
||||
ASSERT_EQ(3U, entries[1]->initial_rules.size());
|
||||
Module::RuleMap entry2_initial;
|
||||
entry2_initial[".cfa"] = "I think that I shall never see";
|
||||
entry2_initial["stromboli"] = "a poem lovely as a tree";
|
||||
entry2_initial["cannoli"] = "a tree whose hungry mouth is prest";
|
||||
EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial));
|
||||
ASSERT_EQ(0U, entries[1]->rule_changes.size());
|
||||
// Check third entry.
|
||||
EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address);
|
||||
EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size);
|
||||
Module::RuleMap entry3_initial;
|
||||
entry3_initial[".cfa"] = "Whose woods are these";
|
||||
EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial));
|
||||
Module::RuleChangeMap entry3_changes;
|
||||
entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know";
|
||||
entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in";
|
||||
entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though";
|
||||
entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] =
|
||||
"he will not see me stopping here";
|
||||
EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes));
|
||||
}
|
||||
|
||||
TEST(Construct, UniqueFiles) {
|
||||
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
|
||||
Module::File *file1 = m.FindFile("foo");
|
||||
Module::File *file2 = m.FindFile(string("bar"));
|
||||
Module::File *file3 = m.FindFile(string("foo"));
|
||||
Module::File *file4 = m.FindFile("bar");
|
||||
EXPECT_NE(file1, file2);
|
||||
EXPECT_EQ(file1, file3);
|
||||
EXPECT_EQ(file2, file4);
|
||||
EXPECT_EQ(file1, m.FindExistingFile("foo"));
|
||||
EXPECT_TRUE(m.FindExistingFile("baz") == NULL);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright 2009 Google Inc. All Rights Reserved.
|
||||
// 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
|
||||
|
@ -26,7 +26,10 @@
|
|||
// (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>
|
||||
|
||||
// This file implements the google_breakpad::StabsReader class.
|
||||
// See stabs_reader.h.
|
||||
|
||||
#include <a.out.h>
|
||||
#include <stab.h>
|
||||
|
@ -43,6 +46,8 @@ StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
|
|||
stabstr_(stabstr),
|
||||
stabstr_size_(stabstr_size),
|
||||
handler_(handler),
|
||||
string_offset_(0),
|
||||
next_cu_string_offset_(0),
|
||||
symbol_(NULL),
|
||||
current_source_file_(NULL) {
|
||||
symbols_ = reinterpret_cast<const struct nlist *>(stab);
|
||||
|
@ -50,7 +55,7 @@ StabsReader::StabsReader(const uint8_t *stab, size_t stab_size,
|
|||
}
|
||||
|
||||
const char *StabsReader::SymbolString() {
|
||||
ptrdiff_t offset = symbol_->n_un.n_strx;
|
||||
ptrdiff_t offset = string_offset_ + symbol_->n_un.n_strx;
|
||||
if (offset < 0 || (size_t) offset >= stabstr_size_) {
|
||||
handler_->Warning("symbol %d: name offset outside the string section",
|
||||
symbol_ - symbols_);
|
||||
|
@ -67,6 +72,21 @@ bool StabsReader::Process() {
|
|||
if (symbol_->n_type == N_SO) {
|
||||
if (! ProcessCompilationUnit())
|
||||
return false;
|
||||
} else if (symbol_->n_type == N_UNDF) {
|
||||
// At the head of each compilation unit's entries there is an
|
||||
// N_UNDF stab giving the number of symbols in the compilation
|
||||
// unit, and the number of bytes that compilation unit's strings
|
||||
// take up in the .stabstr section. Each CU's strings are
|
||||
// separate; the n_strx values are offsets within the current
|
||||
// CU's portion of the .stabstr section.
|
||||
//
|
||||
// As an optimization, the GNU linker combines all the
|
||||
// compilation units into one, with a single N_UNDF at the
|
||||
// beginning. However, other linkers, like Gold, do not perform
|
||||
// this optimization.
|
||||
string_offset_ = next_cu_string_offset_;
|
||||
next_cu_string_offset_ = SymbolValue();
|
||||
symbol_++;
|
||||
} else
|
||||
symbol_++;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright 2009 Google Inc. All Rights Reserved. -*- mode: c++ -*-
|
||||
// -*- 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
|
||||
|
@ -26,15 +28,20 @@
|
|||
// (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 definitions related to the STABS reader and
|
||||
// its handler interfaces.
|
||||
// A description of the STABS debugging format can be found at
|
||||
// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// stabs_reader.h: Define StabsReader, a parser for STABS debugging
|
||||
// information. A description of the STABS debugging format can be
|
||||
// found at:
|
||||
//
|
||||
// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html
|
||||
//
|
||||
// The comments here assume you understand the format.
|
||||
//
|
||||
// This reader assumes that the system's <a.out.h> and <stab.h>
|
||||
// This parser assumes that the system's <a.out.h> and <stab.h>
|
||||
// headers accurately describe the layout of the STABS data; this code
|
||||
// is not cross-platform safe.
|
||||
// will not parse STABS data for a system with a different address
|
||||
// size or endianness.
|
||||
|
||||
#ifndef COMMON_LINUX_STABS_READER_H__
|
||||
#define COMMON_LINUX_STABS_READER_H__
|
||||
|
@ -94,6 +101,13 @@ class StabsReader {
|
|||
|
||||
StabsHandler *handler_;
|
||||
|
||||
// The offset of the current compilation unit's strings within stabstr_.
|
||||
size_t string_offset_;
|
||||
|
||||
// The value string_offset_ should have for the next compilation unit,
|
||||
// as established by N_UNDF entries.
|
||||
size_t next_cu_string_offset_;
|
||||
|
||||
// The current symbol we're processing.
|
||||
const struct nlist *symbol_;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -27,7 +27,9 @@
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// stabs_reader_unittest.cc: Unit tests for StabsReader.
|
||||
// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com>
|
||||
|
||||
// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader.
|
||||
|
||||
#include <a.out.h>
|
||||
#include <cassert>
|
||||
|
@ -53,6 +55,7 @@ using std::ostringstream;
|
|||
using std::string;
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Eq;
|
||||
using ::testing::InSequence;
|
||||
using ::testing::Return;
|
||||
using ::testing::Sequence;
|
||||
|
@ -70,7 +73,8 @@ namespace {
|
|||
// sections, and then pass those to StabsReader to look at. The
|
||||
// human-readable file is called a "mock stabs file".
|
||||
//
|
||||
// Each line of a mock stabs file should have the following form:
|
||||
// Except for compilation unit boundary lines (described below), each
|
||||
// line of a mock stabs file should have the following form:
|
||||
//
|
||||
// TYPE OTHER DESC VALUE NAME
|
||||
//
|
||||
|
@ -91,6 +95,10 @@ namespace {
|
|||
// is misleading, but that's how it is. For SO, this may be a
|
||||
// filename; for FUN, this is the function name, plus type data; and
|
||||
// so on.
|
||||
//
|
||||
// A compilation unit boundary line has the form:
|
||||
//
|
||||
// cu-boundary FILENAME
|
||||
|
||||
// I don't know if the whole parser/handler pattern is really worth
|
||||
// the bureaucracy in this case. But just writing it out as
|
||||
|
@ -108,6 +116,11 @@ class MockStabsHandler {
|
|||
// stops, and its Process member function returns false.
|
||||
virtual bool Entry(enum __stab_debug_code type, char other, short desc,
|
||||
unsigned long value, const string &name) { return true; }
|
||||
// Report a compilation unit boundary whose filename is FILENAME. As
|
||||
// for the Entry function, this should return true to continue
|
||||
// parsing, or false to stop processing.
|
||||
virtual bool CUBoundary(const string &filename) { return true; }
|
||||
|
||||
// Report an error in parsing the mock stabs data. If this returns true,
|
||||
// the parser continues; if it returns false, the parser stops and
|
||||
// its Process member function returns false.
|
||||
|
@ -159,7 +172,7 @@ bool MockStabsParser::Process() {
|
|||
// terminating newline.
|
||||
for(;;) {
|
||||
string line;
|
||||
std::getline(*stream_, line, '\n');
|
||||
getline(*stream_, line, '\n');
|
||||
if (line.empty() && stream_->eof())
|
||||
break;
|
||||
line_number_++;
|
||||
|
@ -190,24 +203,32 @@ bool MockStabsParser::ParseLine(const string &line) {
|
|||
// Parse and validate the stabs type.
|
||||
string typeName;
|
||||
linestream >> typeName;
|
||||
StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName);
|
||||
if (typeIt == type_names_.end())
|
||||
return handler_->Error("%s:%d: unrecognized stab type: %s\n",
|
||||
filename_.c_str(), line_number_, typeName.c_str());
|
||||
// These are int, not char and unsigned char, to ensure they're parsed
|
||||
// as decimal numbers, not characters.
|
||||
int otherInt, descInt;
|
||||
unsigned long value;
|
||||
linestream >> otherInt >> descInt >> value;
|
||||
if (linestream.fail())
|
||||
return handler_->Error("%s:%d: malformed mock stabs input line\n",
|
||||
filename_.c_str(), line_number_);
|
||||
if (linestream.peek() == ' ')
|
||||
linestream.get();
|
||||
string name;
|
||||
getline(linestream, name, '\n');
|
||||
return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second),
|
||||
otherInt, descInt, value, name);
|
||||
if (typeName == "cu-boundary") {
|
||||
if (linestream.peek() == ' ')
|
||||
linestream.get();
|
||||
string filename;
|
||||
getline(linestream, filename, '\n');
|
||||
return handler_->CUBoundary(filename);
|
||||
} else {
|
||||
StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName);
|
||||
if (typeIt == type_names_.end())
|
||||
return handler_->Error("%s:%d: unrecognized stab type: %s\n",
|
||||
filename_.c_str(), line_number_, typeName.c_str());
|
||||
// These are int, not char and unsigned char, to ensure they're parsed
|
||||
// as decimal numbers, not characters.
|
||||
int otherInt, descInt;
|
||||
unsigned long value;
|
||||
linestream >> otherInt >> descInt >> value;
|
||||
if (linestream.fail())
|
||||
return handler_->Error("%s:%d: malformed mock stabs input line\n",
|
||||
filename_.c_str(), line_number_);
|
||||
if (linestream.peek() == ' ')
|
||||
linestream.get();
|
||||
string name;
|
||||
getline(linestream, name, '\n');
|
||||
return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second),
|
||||
otherInt, descInt, value, name);
|
||||
}
|
||||
}
|
||||
|
||||
// A class for constructing .stab sections.
|
||||
|
@ -227,9 +248,15 @@ class StabSection {
|
|||
// call to Append. The caller should initialize the returned entry
|
||||
// as needed.
|
||||
struct nlist *Append();
|
||||
// Set the first entry's n_desc field to COUNT, and set its n_value field
|
||||
// to STRING_SIZE.
|
||||
void SetHeader(short count, unsigned long string_size);
|
||||
// Set SECTION to the contents of a .stab section holding the
|
||||
// accumulated list of entries added with Append.
|
||||
void GetSection(string *section);
|
||||
// Clear the array, and prepare this StabSection to accumulate a fresh
|
||||
// set of entries.
|
||||
void Clear();
|
||||
|
||||
private:
|
||||
// The array of stabs entries,
|
||||
|
@ -248,11 +275,23 @@ struct nlist *StabSection::Append() {
|
|||
return &entries_[used_++];
|
||||
}
|
||||
|
||||
void StabSection::SetHeader(short count, unsigned long string_size) {
|
||||
assert(used_ >= 1);
|
||||
entries_[0].n_desc = count;
|
||||
entries_[0].n_value = string_size;
|
||||
}
|
||||
|
||||
void StabSection::GetSection(string *section) {
|
||||
section->assign(reinterpret_cast<char *>(entries_),
|
||||
sizeof(*entries_) * used_);
|
||||
}
|
||||
|
||||
void StabSection::Clear() {
|
||||
used_ = 0;
|
||||
size_ = 1;
|
||||
entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_);
|
||||
}
|
||||
|
||||
// A class for building .stabstr sections.
|
||||
//
|
||||
// A .stabstr section is an array of characters containing a bunch of
|
||||
|
@ -273,6 +312,9 @@ class StabstrSection {
|
|||
// Set SECTION to the contents of a .stabstr section in which the
|
||||
// strings passed to Insert appear at the indices we promised.
|
||||
void GetSection(string *section);
|
||||
// Clear the contents of this StabstrSection, and prepare it to
|
||||
// accumulate a new set of strings.
|
||||
void Clear();
|
||||
private:
|
||||
// Maps from strings to .stabstr indices and back.
|
||||
typedef map<string, size_t> StringToIndex;
|
||||
|
@ -317,6 +359,12 @@ void StabstrSection::GetSection(string *section) {
|
|||
}
|
||||
}
|
||||
|
||||
void StabstrSection::Clear() {
|
||||
string_indices_.clear();
|
||||
string_indices_[""] = 0;
|
||||
next_byte_ = 1;
|
||||
}
|
||||
|
||||
// A mock stabs parser handler class that builds .stab and .stabstr
|
||||
// sections.
|
||||
class StabsSectionsBuilder: public MockStabsHandler {
|
||||
|
@ -325,13 +373,15 @@ class StabsSectionsBuilder: public MockStabsHandler {
|
|||
// and construct .stab and .stabstr sections. FILENAME should be
|
||||
// the name of the mock stabs input file; we use it in error
|
||||
// messages.
|
||||
StabsSectionsBuilder(const string &filename):
|
||||
filename_(filename), error_count_(0) { }
|
||||
StabsSectionsBuilder(const string &filename)
|
||||
: filename_(filename), error_count_(0), has_header_(false),
|
||||
entry_count_(0) { }
|
||||
|
||||
// Overridden virtual member functions.
|
||||
bool Entry(enum __stab_debug_code type, char other, short desc,
|
||||
unsigned long value, const string &name);
|
||||
virtual bool Error(const char *format, ...);
|
||||
bool CUBoundary(const string &filename);
|
||||
bool Error(const char *format, ...);
|
||||
|
||||
// Set SECTION to the contents of a .stab or .stabstr section
|
||||
// reflecting the entries that have been passed to us via Entry.
|
||||
|
@ -339,10 +389,24 @@ class StabsSectionsBuilder: public MockStabsHandler {
|
|||
void GetStabstr(string *section);
|
||||
|
||||
private:
|
||||
StabSection stab_; // stabs entries we've seen
|
||||
StabstrSection stabstr_; // and the strings they love
|
||||
const string &filename_; // input filename, for error messages
|
||||
int error_count_; // number of errors we've seen so far
|
||||
// Finish a compilation unit. If there are any entries accumulated in
|
||||
// stab_ and stabstr_, add them as a new compilation unit to
|
||||
// finished_cu_stabs_ and finished_cu_stabstr_, and then clear stab_ and
|
||||
// stabstr_.
|
||||
void FinishCU();
|
||||
|
||||
const string &filename_; // input filename, for error messages
|
||||
int error_count_; // number of errors we've seen so far
|
||||
|
||||
// The following members accumulate the contents of a single compilation
|
||||
// unit, possibly headed by an N_UNDF stab.
|
||||
bool has_header_; // true if we have an N_UNDF header
|
||||
int entry_count_; // the number of entries we've seen
|
||||
StabSection stab_; // 'struct nlist' entries
|
||||
StabstrSection stabstr_; // and the strings they love
|
||||
|
||||
// Accumulated .stab and .stabstr content for all compilation units.
|
||||
string finished_cu_stab_, finished_cu_stabstr_;
|
||||
};
|
||||
|
||||
bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other,
|
||||
|
@ -354,9 +418,48 @@ bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other,
|
|||
entry->n_desc = desc;
|
||||
entry->n_value = value;
|
||||
entry->n_un.n_strx = stabstr_.Insert(name);
|
||||
entry_count_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StabsSectionsBuilder::CUBoundary(const string &filename) {
|
||||
FinishCU();
|
||||
// Add a header for the compilation unit.
|
||||
assert(!has_header_);
|
||||
assert(entry_count_ == 0);
|
||||
struct nlist *entry = stab_.Append();
|
||||
entry->n_type = N_UNDF;
|
||||
entry->n_other = 0;
|
||||
entry->n_desc = 0; // will be set to number of entries
|
||||
entry->n_value = 0; // will be set to size of .stabstr data
|
||||
entry->n_un.n_strx = stabstr_.Insert(filename);
|
||||
has_header_ = true;
|
||||
// The N_UNDF header isn't included in the symbol count, so we
|
||||
// shouldn't bump entry_count_ here.
|
||||
return true;
|
||||
}
|
||||
|
||||
void StabsSectionsBuilder::FinishCU() {
|
||||
if (entry_count_ > 0) {
|
||||
// Get the strings first, so we can record their size in the header.
|
||||
string stabstr;
|
||||
stabstr_.GetSection(&stabstr);
|
||||
finished_cu_stabstr_ += stabstr;
|
||||
|
||||
// Initialize our header, if we have one, and extract the .stab data.
|
||||
if (has_header_)
|
||||
stab_.SetHeader(entry_count_, stabstr.size());
|
||||
string stab;
|
||||
stab_.GetSection(&stab);
|
||||
finished_cu_stab_ += stab;
|
||||
}
|
||||
|
||||
stab_.Clear();
|
||||
stabstr_.Clear();
|
||||
has_header_ = false;
|
||||
entry_count_ = 0;
|
||||
}
|
||||
|
||||
bool StabsSectionsBuilder::Error(const char *format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
|
@ -373,11 +476,13 @@ bool StabsSectionsBuilder::Error(const char *format, ...) {
|
|||
}
|
||||
|
||||
void StabsSectionsBuilder::GetStab(string *section) {
|
||||
stab_.GetSection(section);
|
||||
FinishCU();
|
||||
*section = finished_cu_stab_;
|
||||
}
|
||||
|
||||
void StabsSectionsBuilder::GetStabstr(string *section) {
|
||||
stabstr_.GetSection(section);
|
||||
FinishCU();
|
||||
*section = finished_cu_stabstr_;
|
||||
}
|
||||
|
||||
class MockStabsReaderHandler: public StabsHandler {
|
||||
|
@ -428,7 +533,7 @@ static bool ApplyHandlerToMockStabsData(StabsHandler *handler,
|
|||
return reader.Process();
|
||||
}
|
||||
|
||||
TEST(StabsReaderTestCase, MockStabsInput) {
|
||||
TEST(StabsReader, MockStabsInput) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
|
@ -467,7 +572,7 @@ TEST(StabsReaderTestCase, MockStabsInput) {
|
|||
"common/linux/testdata/stabs_reader_unittest.input1"));
|
||||
}
|
||||
|
||||
TEST(StabsReaderTestCase, AbruptCU) {
|
||||
TEST(StabsReader, AbruptCU) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
|
@ -485,7 +590,7 @@ TEST(StabsReaderTestCase, AbruptCU) {
|
|||
"common/linux/testdata/stabs_reader_unittest.input2"));
|
||||
}
|
||||
|
||||
TEST(StabsReaderTestCase, AbruptFunction) {
|
||||
TEST(StabsReader, AbruptFunction) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
|
@ -507,7 +612,7 @@ TEST(StabsReaderTestCase, AbruptFunction) {
|
|||
"common/linux/testdata/stabs_reader_unittest.input3"));
|
||||
}
|
||||
|
||||
TEST(StabsReaderTestCase, NoCU) {
|
||||
TEST(StabsReader, NoCU) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _))
|
||||
|
@ -521,7 +626,7 @@ TEST(StabsReaderTestCase, NoCU) {
|
|||
|
||||
}
|
||||
|
||||
TEST(StabsReaderTestCase, NoCUEnd) {
|
||||
TEST(StabsReader, NoCUEnd) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
|
@ -545,4 +650,36 @@ TEST(StabsReaderTestCase, NoCUEnd) {
|
|||
|
||||
}
|
||||
|
||||
TEST(StabsReader, MultipleCUs) {
|
||||
MockStabsReaderHandler mock_handler;
|
||||
|
||||
{
|
||||
InSequence s;
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("antimony"), 0x12, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x22))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndFunction(0x32))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(0x32))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler,
|
||||
StartCompilationUnit(StrEq("aluminum"), 0x42, NULL))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0x52))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndFunction(0x62))
|
||||
.WillOnce(Return(true));
|
||||
EXPECT_CALL(mock_handler, EndCompilationUnit(0x62))
|
||||
.WillOnce(Return(true));
|
||||
}
|
||||
|
||||
ASSERT_TRUE(ApplyHandlerToMockStabsData(
|
||||
&mock_handler,
|
||||
"common/linux/testdata/stabs_reader_unittest.input6"));
|
||||
}
|
||||
|
||||
// name duplication
|
||||
|
||||
} // anonymous namespace
|
||||
|
|
676
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/func-line-pairing.h
поставляемый
Normal file
676
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/func-line-pairing.h
поставляемый
Normal file
|
@ -0,0 +1,676 @@
|
|||
// -*- mode: c++ -*-
|
||||
|
||||
// Test data for pairing functions and lines.
|
||||
//
|
||||
// For a pair of functions that are adjacent (10,20),(20,25) and a
|
||||
// pair that are not (10,15),(20,25), we include a test case for every
|
||||
// possible arrangement of two lines relative to those functions. We
|
||||
// include cases only for non-empty ranges, since empty functions and
|
||||
// lines are dropped before we do any pairing.
|
||||
//
|
||||
// Each test case is represented by a macro call of the form:
|
||||
//
|
||||
// PAIRING(func1_start, func1_end, func2_start, func2_end,
|
||||
// line1_start, line1_end, line2_start, line2_end,
|
||||
// func1_num_lines, func2_num_lines,
|
||||
// func1_line1_start, func1_line1_end,
|
||||
// func1_line2_start, func1_line2_end,
|
||||
// func2_line1_start, func2_line1_end,
|
||||
// func2_line2_start, func2_line2_end,
|
||||
// uncovered_funcs, uncovered_lines)
|
||||
//
|
||||
// where:
|
||||
// - funcN_{start,end} is the range of the N'th function
|
||||
// - lineN_{start,end} is the range of the N'th function
|
||||
// - funcN_num_lines is the number of source lines that should be
|
||||
// paired with the N'th function
|
||||
// - funcN_lineM_{start,end} is the range of the M'th line
|
||||
// paired with the N'th function, where 0,0 indicates that
|
||||
// there should be no such line paired
|
||||
// - uncovered_funcs is the number of functions with area that is
|
||||
// uncovered by any line, and
|
||||
// - uncovered_lines is the reverse.
|
||||
|
||||
// func1 func2 line1 line2 num pairing1 pairing2 uncovered
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #0
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #1
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #2
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #3
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #4
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #5
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 7, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #6
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #7
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #8
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #9
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #10
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #11
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #12
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 8, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #13
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #14
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #15
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #16
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #17
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #18
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #19
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #20
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #21
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #22
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #23
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #24
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #25
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #26
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #27
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #28
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #29
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #30
|
||||
PAIRING(10, 20, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #31
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #32
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #33
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #34
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #35
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #36
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #37
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #38
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #39
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #40
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #41
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #42
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #43
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #44
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #45
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #46
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #47
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #48
|
||||
PAIRING(10, 20, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #49
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #50
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 1) // #51
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 1) // #52
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #53
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 2) // #54
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #55
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 1) // #56
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 1) // #57
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #58
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 2) // #59
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #60
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #61
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #62
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #63
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #64
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #65
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #66
|
||||
PAIRING(10, 20, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #67
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #68
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #69
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #70
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 1) // #71
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #72
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 2) // #73
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #74
|
||||
PAIRING(10, 20, 20, 25, 6, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #75
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 1) // #76
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #77
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 2) // #78
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 1) // #79
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #80
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 2) // #81
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #82
|
||||
PAIRING(10, 20, 20, 25, 6, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #83
|
||||
PAIRING(10, 20, 20, 25, 6, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #84
|
||||
PAIRING(10, 20, 20, 25, 6, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #85
|
||||
PAIRING(10, 20, 20, 25, 6, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #86
|
||||
PAIRING(10, 20, 20, 25, 6, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #87
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #88
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 0) // #89
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 0) // #90
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 0) // #91
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #92
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #93
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 0) // #94
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 0) // #95
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 0) // #96
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #97
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #98
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #99
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #100
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #101
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #102
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #103
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #104
|
||||
PAIRING(10, 20, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #105
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 0) // #106
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 0) // #107
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #108
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 0) // #109
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 0) // #110
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #111
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #112
|
||||
PAIRING(10, 20, 20, 25, 10, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #113
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 0) // #114
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 0) // #115
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #116
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 0) // #117
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 0) // #118
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #119
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #120
|
||||
PAIRING(10, 20, 20, 25, 10, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #121
|
||||
PAIRING(10, 20, 20, 25, 10, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #122
|
||||
PAIRING(10, 20, 20, 25, 10, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #123
|
||||
PAIRING(10, 20, 20, 25, 10, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #124
|
||||
PAIRING(10, 20, 20, 25, 10, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #125
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #126
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 20, 0, 0, 0, 0, 2, 0) // #127
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 20, 20, 21, 0, 0, 2, 0) // #128
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 0) // #129
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 1) // #130
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #131
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 20, 0, 0, 0, 0, 2, 0) // #132
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 20, 20, 21, 0, 0, 2, 0) // #133
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 0) // #134
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 1) // #135
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #136
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #137
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #138
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #139
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #140
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #141
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #142
|
||||
PAIRING(10, 20, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #143
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 20, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 0) // #144
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 20, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 0) // #145
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 20, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #146
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 21, 22, 1, 1, 11, 20, 0, 0, 21, 22, 0, 0, 2, 0) // #147
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 21, 25, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 0) // #148
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 21, 26, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 1) // #149
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 25, 26, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #150
|
||||
PAIRING(10, 20, 20, 25, 11, 20, 26, 27, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #151
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 21, 22, 1, 2, 11, 20, 0, 0, 20, 21, 21, 22, 2, 0) // #152
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 21, 25, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 0) // #153
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 21, 26, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 1) // #154
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 22, 23, 1, 2, 11, 20, 0, 0, 20, 21, 22, 23, 2, 0) // #155
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 22, 25, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 0) // #156
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 22, 26, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 1) // #157
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 25, 26, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #158
|
||||
PAIRING(10, 20, 20, 25, 11, 21, 26, 27, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #159
|
||||
PAIRING(10, 20, 20, 25, 11, 25, 25, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #160
|
||||
PAIRING(10, 20, 20, 25, 11, 25, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #161
|
||||
PAIRING(10, 20, 20, 25, 11, 26, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #162
|
||||
PAIRING(10, 20, 20, 25, 11, 26, 27, 28, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #163
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #164
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #165
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #166
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #167
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #168
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #169
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #170
|
||||
PAIRING(10, 20, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #171
|
||||
PAIRING(10, 20, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #172
|
||||
PAIRING(10, 20, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #173
|
||||
PAIRING(10, 20, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #174
|
||||
PAIRING(10, 20, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #175
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #176
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #177
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #178
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #179
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #180
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #181
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #182
|
||||
PAIRING(10, 20, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #183
|
||||
PAIRING(10, 20, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #184
|
||||
PAIRING(10, 20, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #185
|
||||
PAIRING(10, 20, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #186
|
||||
PAIRING(10, 20, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #187
|
||||
PAIRING(10, 20, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #188
|
||||
PAIRING(10, 20, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #189
|
||||
PAIRING(10, 20, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #190
|
||||
PAIRING(10, 20, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #191
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #192
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #193
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #194
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #195
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #196
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #197
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #198
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #199
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 7, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #200
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #201
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #202
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #203
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #204
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #205
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #206
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #207
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #208
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 8, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #209
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #210
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #211
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #212
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #213
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #214
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #215
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #216
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #217
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #218
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #219
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #220
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #221
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #222
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #223
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #224
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #225
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #226
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #227
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #228
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #229
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #230
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #231
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #232
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #233
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #234
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #235
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #236
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #237
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #238
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #239
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #240
|
||||
PAIRING(10, 15, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #241
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #242
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #243
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #244
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #245
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #246
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #247
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #248
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #249
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #250
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #251
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #252
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #253
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #254
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #255
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #256
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #257
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #258
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #259
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #260
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #261
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #262
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #263
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #264
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #265
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #266
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #267
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #268
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #269
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #270
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #271
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #272
|
||||
PAIRING(10, 15, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #273
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #274
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #275
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 2) // #276
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #277
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 2) // #278
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #279
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #280
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #281
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #282
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 2) // #283
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #284
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 2) // #285
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #286
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #287
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #288
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #289
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #290
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #291
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #292
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #293
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #294
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #295
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #296
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #297
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #298
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #299
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #300
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #301
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #302
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #303
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #304
|
||||
PAIRING(10, 15, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #305
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #306
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #307
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #308
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #309
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #310
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #311
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #312
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #313
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #314
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #315
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #316
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #317
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #318
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #319
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #320
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #321
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #322
|
||||
PAIRING(10, 15, 20, 25, 6, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #323
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #324
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #325
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #326
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #327
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #328
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #329
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #330
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #331
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #332
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #333
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #334
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #335
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #336
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #337
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #338
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #339
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #340
|
||||
PAIRING(10, 15, 20, 25, 6, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #341
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #342
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #343
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #344
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #345
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #346
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #347
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #348
|
||||
PAIRING(10, 15, 20, 25, 6, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #349
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #350
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #351
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #352
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #353
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #354
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #355
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #356
|
||||
PAIRING(10, 15, 20, 25, 6, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #357
|
||||
PAIRING(10, 15, 20, 25, 6, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #358
|
||||
PAIRING(10, 15, 20, 25, 6, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #359
|
||||
PAIRING(10, 15, 20, 25, 6, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #360
|
||||
PAIRING(10, 15, 20, 25, 6, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #361
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #362
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #363
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #364
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #365
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 1) // #366
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #367
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #368
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #369
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #370
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #371
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #372
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 1) // #373
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #374
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #375
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #376
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #377
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #378
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #379
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #380
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #381
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #382
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #383
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #384
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #385
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #386
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #387
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #388
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #389
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #390
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #391
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #392
|
||||
PAIRING(10, 15, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #393
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #394
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #395
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #396
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #397
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #398
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #399
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #400
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #401
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #402
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #403
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #404
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #405
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #406
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #407
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #408
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #409
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #410
|
||||
PAIRING(10, 15, 20, 25, 10, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #411
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #412
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #413
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #414
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #415
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #416
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #417
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #418
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #419
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #420
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #421
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #422
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #423
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #424
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #425
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #426
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #427
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #428
|
||||
PAIRING(10, 15, 20, 25, 10, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #429
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #430
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #431
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #432
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #433
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #434
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #435
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #436
|
||||
PAIRING(10, 15, 20, 25, 10, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #437
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #438
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #439
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #440
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #441
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #442
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #443
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #444
|
||||
PAIRING(10, 15, 20, 25, 10, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #445
|
||||
PAIRING(10, 15, 20, 25, 10, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #446
|
||||
PAIRING(10, 15, 20, 25, 10, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #447
|
||||
PAIRING(10, 15, 20, 25, 10, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #448
|
||||
PAIRING(10, 15, 20, 25, 10, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #449
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #450
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 15, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #451
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 16, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 1) // #452
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #453
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 15, 20, 21, 0, 0, 2, 1) // #454
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #455
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #456
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #457
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 15, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #458
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 16, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 1) // #459
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #460
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 15, 20, 21, 0, 0, 2, 1) // #461
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #462
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #463
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 16, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #464
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #465
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #466
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #467
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 15, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #468
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 17, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #469
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #470
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #471
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #472
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 16, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #473
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #474
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #475
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #476
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #477
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #478
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #479
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #480
|
||||
PAIRING(10, 15, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #481
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #482
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #483
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #484
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #485
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 15, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #486
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #487
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #488
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #489
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #490
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #491
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #492
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #493
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #494
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #495
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #496
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #497
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #498
|
||||
PAIRING(10, 15, 20, 25, 11, 15, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #499
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #500
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #501
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #502
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #503
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #504
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 18, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #505
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #506
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #507
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #508
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 17, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #509
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #510
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #511
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #512
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 1) // #513
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #514
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 2) // #515
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #516
|
||||
PAIRING(10, 15, 20, 25, 11, 16, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #517
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #518
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #519
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #520
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #521
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #522
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #523
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #524
|
||||
PAIRING(10, 15, 20, 25, 11, 20, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #525
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 21, 22, 1, 2, 11, 15, 0, 0, 20, 21, 21, 22, 2, 1) // #526
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 21, 25, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 1) // #527
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 21, 26, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 2) // #528
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 22, 23, 1, 2, 11, 15, 0, 0, 20, 21, 22, 23, 2, 1) // #529
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 22, 25, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 1) // #530
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 22, 26, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 2) // #531
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 25, 26, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #532
|
||||
PAIRING(10, 15, 20, 25, 11, 21, 26, 27, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #533
|
||||
PAIRING(10, 15, 20, 25, 11, 25, 25, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #534
|
||||
PAIRING(10, 15, 20, 25, 11, 25, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #535
|
||||
PAIRING(10, 15, 20, 25, 11, 26, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #536
|
||||
PAIRING(10, 15, 20, 25, 11, 26, 27, 28, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #537
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #538
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #539
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #540
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #541
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #542
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #543
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #544
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #545
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #546
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #547
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #548
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #549
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #550
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #551
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #552
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #553
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #554
|
||||
PAIRING(10, 15, 20, 25, 15, 16, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #555
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #556
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #557
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #558
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #559
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #560
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #561
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #562
|
||||
PAIRING(10, 15, 20, 25, 15, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #563
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #564
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #565
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #566
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #567
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #568
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #569
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #570
|
||||
PAIRING(10, 15, 20, 25, 15, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #571
|
||||
PAIRING(10, 15, 20, 25, 15, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #572
|
||||
PAIRING(10, 15, 20, 25, 15, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #573
|
||||
PAIRING(10, 15, 20, 25, 15, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #574
|
||||
PAIRING(10, 15, 20, 25, 15, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #575
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #576
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #577
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #578
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #579
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #580
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #581
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #582
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #583
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #584
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 18, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #585
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #586
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #587
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #588
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #589
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #590
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #591
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #592
|
||||
PAIRING(10, 15, 20, 25, 16, 17, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #593
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #594
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #595
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #596
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #597
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #598
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #599
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #600
|
||||
PAIRING(10, 15, 20, 25, 16, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #601
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #602
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #603
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #604
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #605
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #606
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #607
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #608
|
||||
PAIRING(10, 15, 20, 25, 16, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #609
|
||||
PAIRING(10, 15, 20, 25, 16, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #610
|
||||
PAIRING(10, 15, 20, 25, 16, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #611
|
||||
PAIRING(10, 15, 20, 25, 16, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #612
|
||||
PAIRING(10, 15, 20, 25, 16, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #613
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #614
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #615
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #616
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #617
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #618
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #619
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #620
|
||||
PAIRING(10, 15, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #621
|
||||
PAIRING(10, 15, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #622
|
||||
PAIRING(10, 15, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #623
|
||||
PAIRING(10, 15, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #624
|
||||
PAIRING(10, 15, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #625
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #626
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #627
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #628
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #629
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #630
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #631
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #632
|
||||
PAIRING(10, 15, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #633
|
||||
PAIRING(10, 15, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #634
|
||||
PAIRING(10, 15, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #635
|
||||
PAIRING(10, 15, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #636
|
||||
PAIRING(10, 15, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #637
|
||||
PAIRING(10, 15, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #638
|
||||
PAIRING(10, 15, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #639
|
||||
PAIRING(10, 15, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #640
|
||||
PAIRING(10, 15, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #641
|
8
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input6
поставляемый
Normal file
8
toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input6
поставляемый
Normal file
|
@ -0,0 +1,8 @@
|
|||
cu-boundary antimony
|
||||
SO 10 11 0x12 antimony
|
||||
FUN 20 21 0x22 arsenic
|
||||
SO 30 31 0x32
|
||||
cu-boundary aluminum
|
||||
SO 40 41 0x42 aluminum
|
||||
FUN 50 51 0x52 selenium
|
||||
SO 60 61 0x62
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2007, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#undef HAVE_PTHREAD
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
|
@ -30,6 +33,10 @@
|
|||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#undef HAVE_UNISTD_H
|
||||
|
||||
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
*/
|
||||
#undef LT_OBJDIR
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
|
@ -45,9 +52,16 @@
|
|||
/* Define to the one symbol short name of this package. */
|
||||
#undef PACKAGE_TARNAME
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#undef PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#undef PACKAGE_VERSION
|
||||
|
||||
/* Define to necessary symbol if this constant uses a non-standard name on
|
||||
your system. */
|
||||
#undef PTHREAD_CREATE_JOINABLE
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
|
|
|
@ -116,6 +116,16 @@ typedef struct {
|
|||
|
||||
} MDRawContextARM;
|
||||
|
||||
/* Indices into iregs for registers with a dedicated or conventional
|
||||
* purpose.
|
||||
*/
|
||||
enum MDARMRegisterNumbers {
|
||||
MD_CONTEXT_ARM_REG_FP = 11,
|
||||
MD_CONTEXT_ARM_REG_SP = 13,
|
||||
MD_CONTEXT_ARM_REG_LR = 14,
|
||||
MD_CONTEXT_ARM_REG_PC = 15
|
||||
};
|
||||
|
||||
/* For (MDRawContextARM).context_flags. These values indicate the type of
|
||||
* context stored in the structure. */
|
||||
#define MD_CONTEXT_ARM_INTEGER (MD_CONTEXT_ARM | 0x00000002)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -60,10 +60,10 @@ class BasicSourceLineResolver : public SourceLineResolverInterface {
|
|||
virtual bool LoadModuleUsingMapBuffer(const string &module_name,
|
||||
const string &map_buffer);
|
||||
|
||||
|
||||
virtual bool HasModule(const string &module_name) const;
|
||||
|
||||
virtual StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const;
|
||||
virtual void FillSourceLineInfo(StackFrame *frame) const;
|
||||
virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const;
|
||||
virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const;
|
||||
|
||||
private:
|
||||
template<class T> class MemAddrMap;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -50,10 +50,10 @@ class MemoryRegion {
|
|||
virtual ~MemoryRegion() {}
|
||||
|
||||
// The base address of this memory region.
|
||||
virtual u_int64_t GetBase() = 0;
|
||||
virtual u_int64_t GetBase() const = 0;
|
||||
|
||||
// The size of this memory region.
|
||||
virtual u_int32_t GetSize() = 0;
|
||||
virtual u_int32_t GetSize() const = 0;
|
||||
|
||||
// Access to data of various sizes within the memory region. address
|
||||
// is a pointer to read, and it must lie within the memory region as
|
||||
|
@ -63,10 +63,10 @@ class MemoryRegion {
|
|||
// program. Returns true on success. Fails and returns false if address
|
||||
// is out of the region's bounds (after considering the width of value),
|
||||
// or for other types of errors.
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) = 0;
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) = 0;
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) = 0;
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) = 0;
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const =0;
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const =0;
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const =0;
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const =0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -242,22 +242,22 @@ class MinidumpMemoryRegion : public MinidumpObject,
|
|||
// Returns a pointer to the base of the memory region. Returns the
|
||||
// cached value if available, otherwise, reads the minidump file and
|
||||
// caches the memory region.
|
||||
const u_int8_t* GetMemory();
|
||||
const u_int8_t* GetMemory() const;
|
||||
|
||||
// The address of the base of the memory region.
|
||||
u_int64_t GetBase();
|
||||
u_int64_t GetBase() const;
|
||||
|
||||
// The size, in bytes, of the memory region.
|
||||
u_int32_t GetSize();
|
||||
u_int32_t GetSize() const;
|
||||
|
||||
// Frees the cached memory region, if cached.
|
||||
void FreeMemory();
|
||||
|
||||
// Obtains the value of memory at the pointer specified by address.
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value);
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value);
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value);
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value);
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const;
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const;
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const;
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const;
|
||||
|
||||
// Print a human-readable representation of the object to stdout.
|
||||
void Print();
|
||||
|
@ -274,7 +274,7 @@ class MinidumpMemoryRegion : public MinidumpObject,
|
|||
|
||||
// Implementation for GetMemoryAtAddress
|
||||
template<typename T> bool GetMemoryAtAddressInternal(u_int64_t address,
|
||||
T* value);
|
||||
T* value) const;
|
||||
|
||||
// The largest memory region that will be read from a minidump. The
|
||||
// default is 1MB.
|
||||
|
@ -285,7 +285,7 @@ class MinidumpMemoryRegion : public MinidumpObject,
|
|||
MDMemoryDescriptor* descriptor_;
|
||||
|
||||
// Cached memory.
|
||||
vector<u_int8_t>* memory_;
|
||||
mutable vector<u_int8_t>* memory_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -40,7 +42,8 @@ namespace google_breakpad {
|
|||
using std::string;
|
||||
|
||||
struct StackFrame;
|
||||
struct StackFrameInfo;
|
||||
struct WindowsFrameInfo;
|
||||
struct CFIFrameInfo;
|
||||
|
||||
class SourceLineResolverInterface {
|
||||
public:
|
||||
|
@ -65,12 +68,22 @@ class SourceLineResolverInterface {
|
|||
|
||||
// Fills in the function_base, function_name, source_file_name,
|
||||
// and source_line fields of the StackFrame. The instruction and
|
||||
// module_name fields must already be filled in. Additional debugging
|
||||
// information, if available, is returned. If the information is not
|
||||
// available, returns NULL. A NULL return value does not indicate an
|
||||
// error. The caller takes ownership of any returned StackFrameInfo
|
||||
// object.
|
||||
virtual StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const = 0;
|
||||
// module_name fields must already be filled in.
|
||||
virtual void FillSourceLineInfo(StackFrame *frame) const = 0;
|
||||
|
||||
// If Windows stack walking information is available covering
|
||||
// FRAME's instruction address, return a WindowsFrameInfo structure
|
||||
// describing it. If the information is not available, returns NULL.
|
||||
// A NULL return value does not indicate an error. The caller takes
|
||||
// ownership of any returned WindowsFrameInfo object.
|
||||
virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame)
|
||||
const = 0;
|
||||
|
||||
// If CFI stack walking information is available covering ADDRESS,
|
||||
// return a CFIFrameInfo structure describing it. If the information
|
||||
// is not available, return NULL. The caller takes ownership of any
|
||||
// returned CFIFrameInfo object.
|
||||
virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const = 0;
|
||||
|
||||
protected:
|
||||
// SourceLineResolverInterface cannot be instantiated except by subclasses
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -44,17 +46,27 @@
|
|||
|
||||
namespace google_breakpad {
|
||||
|
||||
struct WindowsFrameInfo;
|
||||
struct CFIFrameInfo;
|
||||
|
||||
struct StackFrameX86 : public StackFrame {
|
||||
// ContextValidity has one entry for each relevant hardware pointer register
|
||||
// (%eip and %esp) and one entry for each nonvolatile (callee-save) register.
|
||||
// ContextValidity has one entry for each relevant hardware pointer
|
||||
// register (%eip and %esp) and one entry for each general-purpose
|
||||
// register. It's worthwhile having validity flags for caller-saves
|
||||
// registers: they are valid in the youngest frame, and such a frame
|
||||
// might save a callee-saves register in a caller-saves register, but
|
||||
// SimpleCFIWalker won't touch registers unless they're marked as valid.
|
||||
enum ContextValidity {
|
||||
CONTEXT_VALID_NONE = 0,
|
||||
CONTEXT_VALID_EIP = 1 << 0,
|
||||
CONTEXT_VALID_ESP = 1 << 1,
|
||||
CONTEXT_VALID_EBP = 1 << 2,
|
||||
CONTEXT_VALID_EBX = 1 << 3,
|
||||
CONTEXT_VALID_ESI = 1 << 4,
|
||||
CONTEXT_VALID_EDI = 1 << 5,
|
||||
CONTEXT_VALID_EAX = 1 << 3,
|
||||
CONTEXT_VALID_EBX = 1 << 4,
|
||||
CONTEXT_VALID_ECX = 1 << 5,
|
||||
CONTEXT_VALID_EDX = 1 << 6,
|
||||
CONTEXT_VALID_ESI = 1 << 7,
|
||||
CONTEXT_VALID_EDI = 1 << 8,
|
||||
CONTEXT_VALID_ALL = -1
|
||||
};
|
||||
|
||||
|
@ -74,7 +86,10 @@ struct StackFrameX86 : public StackFrame {
|
|||
StackFrameX86()
|
||||
: context(),
|
||||
context_validity(CONTEXT_VALID_NONE),
|
||||
trust(FRAME_TRUST_NONE) {}
|
||||
trust(FRAME_TRUST_NONE),
|
||||
windows_frame_info(NULL),
|
||||
cfi_frame_info(NULL) {}
|
||||
~StackFrameX86();
|
||||
|
||||
// Register state. This is only fully valid for the topmost frame in a
|
||||
// stack. In other frames, the values of nonvolatile registers may be
|
||||
|
@ -90,6 +105,11 @@ struct StackFrameX86 : public StackFrame {
|
|||
// Amount of trust the stack walker has in the instruction pointer
|
||||
// of this frame.
|
||||
FrameTrust trust;
|
||||
|
||||
// Any stack walking information we found describing this.instruction.
|
||||
// These may be NULL if there is no such information for that address.
|
||||
WindowsFrameInfo *windows_frame_info;
|
||||
CFIFrameInfo *cfi_frame_info;
|
||||
};
|
||||
|
||||
struct StackFramePPC : public StackFrame {
|
||||
|
@ -119,28 +139,44 @@ struct StackFramePPC : public StackFrame {
|
|||
};
|
||||
|
||||
struct StackFrameAMD64 : public StackFrame {
|
||||
// ContextValidity has one entry for each relevant hardware pointer register
|
||||
// (%rip and %rsp) and one entry for each nonvolatile (callee-save) register.
|
||||
//FIXME: validate this list
|
||||
// ContextValidity has one entry for each register that we might be able
|
||||
// to recover.
|
||||
enum ContextValidity {
|
||||
CONTEXT_VALID_NONE = 0,
|
||||
CONTEXT_VALID_RIP = 1 << 0,
|
||||
CONTEXT_VALID_RSP = 1 << 1,
|
||||
CONTEXT_VALID_RBP = 1 << 2,
|
||||
CONTEXT_VALID_NONE = 0,
|
||||
CONTEXT_VALID_RAX = 1 << 0,
|
||||
CONTEXT_VALID_RDX = 1 << 1,
|
||||
CONTEXT_VALID_RCX = 1 << 2,
|
||||
CONTEXT_VALID_RBX = 1 << 3,
|
||||
CONTEXT_VALID_RSI = 1 << 4,
|
||||
CONTEXT_VALID_RDI = 1 << 5,
|
||||
CONTEXT_VALID_RBP = 1 << 6,
|
||||
CONTEXT_VALID_RSP = 1 << 7,
|
||||
CONTEXT_VALID_R8 = 1 << 8,
|
||||
CONTEXT_VALID_R9 = 1 << 9,
|
||||
CONTEXT_VALID_R10 = 1 << 10,
|
||||
CONTEXT_VALID_R11 = 1 << 11,
|
||||
CONTEXT_VALID_R12 = 1 << 12,
|
||||
CONTEXT_VALID_R13 = 1 << 13,
|
||||
CONTEXT_VALID_R14 = 1 << 14,
|
||||
CONTEXT_VALID_R15 = 1 << 15,
|
||||
CONTEXT_VALID_RIP = 1 << 16,
|
||||
CONTEXT_VALID_ALL = -1
|
||||
};
|
||||
|
||||
StackFrameAMD64() : context(), context_validity(CONTEXT_VALID_NONE) {}
|
||||
|
||||
// Register state. This is only fully valid for the topmost frame in a
|
||||
// stack. In other frames, the values of nonvolatile registers may be
|
||||
// present, given sufficient debugging information. Refer to
|
||||
// context_validity.
|
||||
// Register state. This is only fully valid for the topmost frame in a
|
||||
// stack. In other frames, which registers are present depends on what
|
||||
// debugging information we had available. Refer to context_validity.
|
||||
MDRawContextAMD64 context;
|
||||
|
||||
// context_validity is actually ContextValidity, but int is used because
|
||||
// the OR operator doesn't work well with enumerated types. This indicates
|
||||
// which fields in context are valid.
|
||||
// For each register in context whose value has been recovered, we set
|
||||
// the corresponding CONTEXT_VALID_ bit in context_validity.
|
||||
//
|
||||
// context_validity's type should actually be ContextValidity, but
|
||||
// we use int instead because the bitwise inclusive or operator
|
||||
// yields an int when applied to enum values, and C++ doesn't
|
||||
// silently convert from ints to enums.
|
||||
int context_validity;
|
||||
};
|
||||
|
||||
|
@ -169,29 +205,54 @@ struct StackFrameSPARC : public StackFrame {
|
|||
};
|
||||
|
||||
struct StackFrameARM : public StackFrame {
|
||||
// ContextValidity should eventually contain entries for the validity of
|
||||
// other nonvolatile (callee-save) registers as in
|
||||
// StackFrameX86::ContextValidity. I suspect this list is sufficient
|
||||
// for arm stackwalking.
|
||||
// A flag for each register we might know.
|
||||
enum ContextValidity {
|
||||
CONTEXT_VALID_NONE = 0,
|
||||
CONTEXT_VALID_R13 = 1 << 0,
|
||||
CONTEXT_VALID_R14 = 1 << 1,
|
||||
CONTEXT_VALID_R15 = 1 << 2,
|
||||
CONTEXT_VALID_ALL = -1
|
||||
CONTEXT_VALID_R0 = 1 << 0,
|
||||
CONTEXT_VALID_R1 = 1 << 1,
|
||||
CONTEXT_VALID_R2 = 1 << 2,
|
||||
CONTEXT_VALID_R3 = 1 << 3,
|
||||
CONTEXT_VALID_R4 = 1 << 4,
|
||||
CONTEXT_VALID_R5 = 1 << 5,
|
||||
CONTEXT_VALID_R6 = 1 << 6,
|
||||
CONTEXT_VALID_R7 = 1 << 7,
|
||||
CONTEXT_VALID_R8 = 1 << 8,
|
||||
CONTEXT_VALID_R9 = 1 << 9,
|
||||
CONTEXT_VALID_R10 = 1 << 10,
|
||||
CONTEXT_VALID_R11 = 1 << 11,
|
||||
CONTEXT_VALID_R12 = 1 << 12,
|
||||
CONTEXT_VALID_R13 = 1 << 13,
|
||||
CONTEXT_VALID_R14 = 1 << 14,
|
||||
CONTEXT_VALID_R15 = 1 << 15,
|
||||
CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE,
|
||||
|
||||
// Aliases for registers with dedicated or conventional roles.
|
||||
CONTEXT_VALID_FP = CONTEXT_VALID_R11,
|
||||
CONTEXT_VALID_SP = CONTEXT_VALID_R13,
|
||||
CONTEXT_VALID_LR = CONTEXT_VALID_R14,
|
||||
CONTEXT_VALID_PC = CONTEXT_VALID_R15
|
||||
};
|
||||
|
||||
StackFrameARM() : context(), context_validity(CONTEXT_VALID_NONE) {}
|
||||
|
||||
// Return the ContextValidity flag for register rN.
|
||||
static ContextValidity RegisterValidFlag(int n) {
|
||||
return ContextValidity(1 << n);
|
||||
}
|
||||
|
||||
// Register state. This is only fully valid for the topmost frame in a
|
||||
// stack. In other frames, the values of nonvolatile registers may be
|
||||
// present, given sufficient debugging information. Refer to
|
||||
// context_validity.
|
||||
MDRawContextARM context;
|
||||
|
||||
// context_validity is actually ContextValidity, but int is used because
|
||||
// the OR operator doesn't work well with enumerated types. This indicates
|
||||
// which fields in context are valid.
|
||||
// For each register in context whose value has been recovered, we set
|
||||
// the corresponding CONTEXT_VALID_ bit in context_validity.
|
||||
//
|
||||
// context_validity's type should actually be ContextValidity, but
|
||||
// we use int instead because the bitwise inclusive or operator
|
||||
// yields an int when applied to enum values, and C++ doesn't
|
||||
// silently convert from ints to enums.
|
||||
int context_validity;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -41,23 +41,23 @@
|
|||
#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
|
||||
#define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
class CallStack;
|
||||
class CodeModule;
|
||||
class CodeModules;
|
||||
template<typename T> class linked_ptr;
|
||||
class MemoryRegion;
|
||||
class MinidumpContext;
|
||||
class SourceLineResolverInterface;
|
||||
struct StackFrame;
|
||||
struct StackFrameInfo;
|
||||
class SymbolSupplier;
|
||||
class SystemInfo;
|
||||
|
||||
using std::vector;
|
||||
using std::set;
|
||||
|
||||
|
||||
class Stackwalker {
|
||||
|
@ -118,6 +118,10 @@ class Stackwalker {
|
|||
// This field is optional and may be NULL.
|
||||
const CodeModules *modules_;
|
||||
|
||||
protected:
|
||||
// The SourceLineResolver implementation.
|
||||
SourceLineResolverInterface *resolver_;
|
||||
|
||||
private:
|
||||
// Obtains the context frame, the innermost called procedure in a stack
|
||||
// trace. Returns NULL on failure. GetContextFrame allocates a new
|
||||
|
@ -133,15 +137,15 @@ class Stackwalker {
|
|||
// the end of the stack has been reached). GetCallerFrame allocates a new
|
||||
// StackFrame (or StackFrame subclass), ownership of which is taken by
|
||||
// the caller.
|
||||
virtual StackFrame* GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) = 0;
|
||||
virtual StackFrame* GetCallerFrame(const CallStack *stack) = 0;
|
||||
|
||||
// The optional SymbolSupplier for resolving source line info.
|
||||
SymbolSupplier *supplier_;
|
||||
|
||||
// The SourceLineResolver implementation
|
||||
SourceLineResolverInterface *resolver_;
|
||||
// A list of modules that we haven't found symbols for. We track
|
||||
// this in order to avoid repeatedly looking them up again within
|
||||
// one minidump.
|
||||
set<std::string> no_symbol_modules_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -46,7 +46,8 @@
|
|||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
#include "processor/windows_frame_info.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
|
||||
using std::map;
|
||||
using std::vector;
|
||||
|
@ -114,28 +115,38 @@ class BasicSourceLineResolver::Module {
|
|||
bool LoadMapFromBuffer(const string &map_buffer);
|
||||
|
||||
// Looks up the given relative address, and fills the StackFrame struct
|
||||
// with the result. Additional debugging information, if available, is
|
||||
// returned. If no additional information is available, returns NULL.
|
||||
// A NULL return value is not an error. The caller takes ownership of
|
||||
// any returned StackFrameInfo object.
|
||||
StackFrameInfo* LookupAddress(StackFrame *frame) const;
|
||||
// with the result.
|
||||
void LookupAddress(StackFrame *frame) const;
|
||||
|
||||
// If Windows stack walking information is available covering ADDRESS,
|
||||
// return a WindowsFrameInfo structure describing it. If the information
|
||||
// is not available, returns NULL. A NULL return value does not indicate
|
||||
// an error. The caller takes ownership of any returned WindowsFrameInfo
|
||||
// object.
|
||||
WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const;
|
||||
|
||||
// If CFI stack walking information is available covering ADDRESS,
|
||||
// return a CFIFrameInfo structure describing it. If the information
|
||||
// is not available, return NULL. The caller takes ownership of any
|
||||
// returned CFIFrameInfo object.
|
||||
CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const;
|
||||
|
||||
private:
|
||||
friend class BasicSourceLineResolver;
|
||||
typedef map<int, string> FileMap;
|
||||
|
||||
// The types for stack_info_. This is equivalent to MS DIA's
|
||||
// The types for windows_frame_info_. This is equivalent to MS DIA's
|
||||
// StackFrameTypeEnum. Each identifies a different type of frame
|
||||
// information, although all are represented in the symbol file in the
|
||||
// same format. These are used as indices to the stack_info_ array.
|
||||
enum StackInfoTypes {
|
||||
STACK_INFO_FPO = 0,
|
||||
STACK_INFO_TRAP, // not used here
|
||||
STACK_INFO_TSS, // not used here
|
||||
STACK_INFO_STANDARD,
|
||||
STACK_INFO_FRAME_DATA,
|
||||
STACK_INFO_LAST, // must be the last sequentially-numbered item
|
||||
STACK_INFO_UNKNOWN = -1
|
||||
// same format. These are used as indices to the windows_frame_info_ array.
|
||||
enum WindowsFrameInfoTypes {
|
||||
WINDOWS_FRAME_INFO_FPO = 0,
|
||||
WINDOWS_FRAME_INFO_TRAP, // not used here
|
||||
WINDOWS_FRAME_INFO_TSS, // not used here
|
||||
WINDOWS_FRAME_INFO_STANDARD,
|
||||
WINDOWS_FRAME_INFO_FRAME_DATA,
|
||||
WINDOWS_FRAME_INFO_LAST, // must be the last sequentially-numbered item
|
||||
WINDOWS_FRAME_INFO_UNKNOWN = -1
|
||||
};
|
||||
|
||||
// Splits line into at most max_tokens space-separated tokens, placing
|
||||
|
@ -163,20 +174,49 @@ class BasicSourceLineResolver::Module {
|
|||
// Returns false if an error occurs.
|
||||
bool ParsePublicSymbol(char *public_line);
|
||||
|
||||
// Parses a stack frame info declaration, storing it in stack_info_.
|
||||
// Parses a STACK WIN or STACK CFI frame info declaration, storing
|
||||
// it in the appropriate table.
|
||||
bool ParseStackInfo(char *stack_info_line);
|
||||
|
||||
// Parses a STACK WIN record, storing it in windows_frame_info_.
|
||||
bool ParseWindowsFrameInfo(char *stack_info_line);
|
||||
|
||||
// Parses a STACK CFI record, storing it in cfi_frame_info_.
|
||||
bool ParseCFIFrameInfo(char *stack_info_line);
|
||||
|
||||
// Parse RULE_SET, a series of rules of the sort appearing in STACK
|
||||
// CFI records, and store the given rules in FRAME_INFO.
|
||||
bool ParseCFIRuleSet(const string &rule_set, CFIFrameInfo *frame_info) const;
|
||||
|
||||
string name_;
|
||||
FileMap files_;
|
||||
RangeMap< MemAddr, linked_ptr<Function> > functions_;
|
||||
AddressMap< MemAddr, linked_ptr<PublicSymbol> > public_symbols_;
|
||||
|
||||
// Each element in the array is a ContainedRangeMap for a type listed in
|
||||
// StackInfoTypes. These are split by type because there may be overlaps
|
||||
// between maps of different types, but some information is only available
|
||||
// as certain types.
|
||||
ContainedRangeMap< MemAddr, linked_ptr<StackFrameInfo> >
|
||||
stack_info_[STACK_INFO_LAST];
|
||||
// Each element in the array is a ContainedRangeMap for a type
|
||||
// listed in WindowsFrameInfoTypes. These are split by type because
|
||||
// there may be overlaps between maps of different types, but some
|
||||
// information is only available as certain types.
|
||||
ContainedRangeMap< MemAddr, linked_ptr<WindowsFrameInfo> >
|
||||
windows_frame_info_[WINDOWS_FRAME_INFO_LAST];
|
||||
|
||||
// DWARF CFI stack walking data. The Module stores the initial rule sets
|
||||
// and rule deltas as strings, just as they appear in the symbol file:
|
||||
// although the file may contain hundreds of thousands of STACK CFI
|
||||
// records, walking a stack will only ever use a few of them, so it's
|
||||
// best to delay parsing a record until it's actually needed.
|
||||
|
||||
// STACK CFI INIT records: for each range, an initial set of register
|
||||
// recovery rules. The RangeMap's itself gives the starting and ending
|
||||
// addresses.
|
||||
RangeMap<MemAddr, string> cfi_initial_rules_;
|
||||
|
||||
// STACK CFI records: at a given address, the changes to the register
|
||||
// recovery rules that take effect at that address. The map key is the
|
||||
// starting address; the ending address is the key of the next entry in
|
||||
// this map, or the end of the range as given by the cfi_initial_rules_
|
||||
// entry (which FindCFIFrameInfo looks up first).
|
||||
map<MemAddr, string> cfi_delta_rules_;
|
||||
};
|
||||
|
||||
BasicSourceLineResolver::BasicSourceLineResolver() : modules_(new ModuleMap) {
|
||||
|
@ -236,12 +276,32 @@ bool BasicSourceLineResolver::HasModule(const string &module_name) const {
|
|||
return modules_->find(module_name) != modules_->end();
|
||||
}
|
||||
|
||||
StackFrameInfo* BasicSourceLineResolver::FillSourceLineInfo(
|
||||
StackFrame *frame) const {
|
||||
void BasicSourceLineResolver::FillSourceLineInfo(StackFrame *frame) const {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
return it->second->LookupAddress(frame);
|
||||
it->second->LookupAddress(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WindowsFrameInfo *BasicSourceLineResolver::FindWindowsFrameInfo(
|
||||
const StackFrame *frame) const {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
return it->second->FindWindowsFrameInfo(frame);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFIFrameInfo *BasicSourceLineResolver::FindCFIFrameInfo(
|
||||
const StackFrame *frame) const {
|
||||
if (frame->module) {
|
||||
ModuleMap::const_iterator it = modules_->find(frame->module->code_file());
|
||||
if (it != modules_->end()) {
|
||||
return it->second->FindCFIFrameInfo(frame);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
@ -380,7 +440,7 @@ bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) {
|
|||
return false;
|
||||
}
|
||||
|
||||
BPLOG(ERROR) << "Opening " << map_file;
|
||||
BPLOG(INFO) << "Opening " << map_file;
|
||||
|
||||
FILE *f = fopen(map_file.c_str(), "rt");
|
||||
if (!f) {
|
||||
|
@ -413,42 +473,15 @@ bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) {
|
|||
return LoadMapFromBuffer(map_buffer);
|
||||
}
|
||||
|
||||
StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress(
|
||||
StackFrame *frame) const {
|
||||
void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const {
|
||||
MemAddr address = frame->instruction - frame->module->base_address();
|
||||
|
||||
linked_ptr<StackFrameInfo> retrieved_info;
|
||||
// Check for debugging info first, before any possible early returns.
|
||||
//
|
||||
// We only know about STACK_INFO_FRAME_DATA and STACK_INFO_FPO. Prefer
|
||||
// them in this order. STACK_INFO_FRAME_DATA is the newer type that
|
||||
// includes its own program string. STACK_INFO_FPO is the older type
|
||||
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
|
||||
if (!stack_info_[STACK_INFO_FRAME_DATA].RetrieveRange(address,
|
||||
&retrieved_info)) {
|
||||
stack_info_[STACK_INFO_FPO].RetrieveRange(address, &retrieved_info);
|
||||
}
|
||||
|
||||
scoped_ptr<StackFrameInfo> frame_info;
|
||||
if (retrieved_info.get()) {
|
||||
frame_info.reset(new StackFrameInfo());
|
||||
frame_info->CopyFrom(*retrieved_info.get());
|
||||
}
|
||||
|
||||
// First, look for a matching FUNC range. Use RetrieveNearestRange instead
|
||||
// of RetrieveRange so that the nearest function can be compared to the
|
||||
// nearest PUBLIC symbol if the address does not lie within the function.
|
||||
// Having access to the highest function below address, even when address
|
||||
// is outside of the function, is useful: if the function is higher than
|
||||
// the nearest PUBLIC symbol, then it means that the PUBLIC symbols is not
|
||||
// valid for the address, and no function information should be filled in.
|
||||
// Using RetrieveNearestRange instead of RetrieveRange means that we need
|
||||
// to verify that address is within the range before using a FUNC.
|
||||
//
|
||||
// If no FUNC containing the address is found, look for the nearest PUBLIC
|
||||
// symbol, being careful not to use a public symbol at a lower address than
|
||||
// the nearest FUNC.
|
||||
int parameter_size = 0;
|
||||
// First, look for a FUNC record that covers address. Use
|
||||
// RetrieveNearestRange instead of RetrieveRange so that, if there
|
||||
// is no such function, we can use the next function to bound the
|
||||
// extent of the PUBLIC symbol we find, below. This does mean we
|
||||
// need to check that address indeed falls within the function we
|
||||
// find; do the range comparison in an overflow-friendly way.
|
||||
linked_ptr<Function> func;
|
||||
linked_ptr<PublicSymbol> public_symbol;
|
||||
MemAddr function_base;
|
||||
|
@ -456,9 +489,7 @@ StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress(
|
|||
MemAddr public_address;
|
||||
if (functions_.RetrieveNearestRange(address, &func,
|
||||
&function_base, &function_size) &&
|
||||
address >= function_base && address < function_base + function_size) {
|
||||
parameter_size = func->parameter_size;
|
||||
|
||||
address >= function_base && address - function_base < function_size) {
|
||||
frame->function_name = func->name;
|
||||
frame->function_base = frame->module->base_address() + function_base;
|
||||
|
||||
|
@ -474,27 +505,99 @@ StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress(
|
|||
}
|
||||
} else if (public_symbols_.Retrieve(address,
|
||||
&public_symbol, &public_address) &&
|
||||
(!func.get() || public_address > function_base + function_size)) {
|
||||
parameter_size = public_symbol->parameter_size;
|
||||
|
||||
(!func.get() || public_address > function_base)) {
|
||||
frame->function_name = public_symbol->name;
|
||||
frame->function_base = frame->module->base_address() + public_address;
|
||||
} else {
|
||||
// No FUNC or PUBLIC data available.
|
||||
return frame_info.release();
|
||||
}
|
||||
}
|
||||
|
||||
WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo(
|
||||
const StackFrame *frame) const {
|
||||
MemAddr address = frame->instruction - frame->module->base_address();
|
||||
scoped_ptr<WindowsFrameInfo> result(new WindowsFrameInfo());
|
||||
|
||||
// We only know about WINDOWS_FRAME_INFO_FRAME_DATA and
|
||||
// WINDOWS_FRAME_INFO_FPO. Prefer them in this order.
|
||||
// WINDOWS_FRAME_INFO_FRAME_DATA is the newer type that includes its
|
||||
// own program string. WINDOWS_FRAME_INFO_FPO is the older type
|
||||
// corresponding to the FPO_DATA struct. See stackwalker_x86.cc.
|
||||
linked_ptr<WindowsFrameInfo> frame_info;
|
||||
if ((windows_frame_info_[WINDOWS_FRAME_INFO_FRAME_DATA]
|
||||
.RetrieveRange(address, &frame_info))
|
||||
|| (windows_frame_info_[WINDOWS_FRAME_INFO_FPO]
|
||||
.RetrieveRange(address, &frame_info))) {
|
||||
result->CopyFrom(*frame_info.get());
|
||||
return result.release();
|
||||
}
|
||||
|
||||
if (!frame_info.get()) {
|
||||
// Even without a relevant STACK line, many functions contain information
|
||||
// about how much space their parameters consume on the stack. Prefer
|
||||
// the STACK stuff (above), but if it's not present, take the
|
||||
// information from the FUNC or PUBLIC line.
|
||||
frame_info.reset(new StackFrameInfo());
|
||||
frame_info->parameter_size = parameter_size;
|
||||
frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE;
|
||||
// Even without a relevant STACK line, many functions contain
|
||||
// information about how much space their parameters consume on the
|
||||
// stack. Use RetrieveNearestRange instead of RetrieveRange, so that
|
||||
// we can use the function to bound the extent of the PUBLIC symbol,
|
||||
// below. However, this does mean we need to check that ADDRESS
|
||||
// falls within the retrieved function's range; do the range
|
||||
// comparison in an overflow-friendly way.
|
||||
linked_ptr<Function> function;
|
||||
MemAddr function_base, function_size;
|
||||
if (functions_.RetrieveNearestRange(address, &function,
|
||||
&function_base, &function_size) &&
|
||||
address >= function_base && address - function_base < function_size) {
|
||||
result->parameter_size = function->parameter_size;
|
||||
result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE;
|
||||
return result.release();
|
||||
}
|
||||
|
||||
return frame_info.release();
|
||||
// PUBLIC symbols might have a parameter size. Use the function we
|
||||
// found above to limit the range the public symbol covers.
|
||||
linked_ptr<PublicSymbol> public_symbol;
|
||||
MemAddr public_address;
|
||||
if (public_symbols_.Retrieve(address, &public_symbol, &public_address) &&
|
||||
(!function.get() || public_address > function_base)) {
|
||||
result->parameter_size = public_symbol->parameter_size;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo(
|
||||
const StackFrame *frame) const {
|
||||
MemAddr address = frame->instruction - frame->module->base_address();
|
||||
MemAddr initial_base, initial_size;
|
||||
string initial_rules;
|
||||
|
||||
// Find the initial rule whose range covers this address. That
|
||||
// provides an initial set of register recovery rules. Then, walk
|
||||
// forward from the initial rule's starting address to frame's
|
||||
// instruction address, applying delta rules.
|
||||
if (!cfi_initial_rules_.RetrieveRange(address, &initial_rules,
|
||||
&initial_base, &initial_size)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create a frame info structure, and populate it with the rules from
|
||||
// the STACK CFI INIT record.
|
||||
scoped_ptr<CFIFrameInfo> rules(new CFIFrameInfo());
|
||||
if (!ParseCFIRuleSet(initial_rules, rules.get()))
|
||||
return NULL;
|
||||
|
||||
// Find the first delta rule that falls within the initial rule's range.
|
||||
map<MemAddr, string>::const_iterator delta =
|
||||
cfi_delta_rules_.lower_bound(initial_base);
|
||||
|
||||
// Apply delta rules up to and including the frame's address.
|
||||
while (delta != cfi_delta_rules_.end() && delta->first <= address) {
|
||||
ParseCFIRuleSet(delta->second, rules.get());
|
||||
delta++;
|
||||
}
|
||||
|
||||
return rules.release();
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseCFIRuleSet(
|
||||
const string &rule_set, CFIFrameInfo *frame_info) const {
|
||||
CFIFrameInfoParseHandler handler(frame_info);
|
||||
CFIRuleParser parser(&handler);
|
||||
return parser.Parse(rule_set);
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -616,49 +719,61 @@ bool BasicSourceLineResolver::Module::ParsePublicSymbol(char *public_line) {
|
|||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
|
||||
// STACK WIN <type> <rva> <code_size> <prolog_size> <epliog_size>
|
||||
// <parameter_size> <saved_register_size> <local_size> <max_stack_size>
|
||||
// <has_program_string> <program_string_OR_allocates_base_pointer>
|
||||
//
|
||||
// If has_program_string is 1, the rest of the line is a program string.
|
||||
// Otherwise, the final token tells whether the stack info indicates that
|
||||
// a base pointer has been allocated.
|
||||
//
|
||||
// Expect has_program_string to be 1 when type is STACK_INFO_FRAME_DATA and
|
||||
// 0 when type is STACK_INFO_FPO, but don't enforce this.
|
||||
|
||||
// Skip "STACK " prefix.
|
||||
stack_info_line += 6;
|
||||
|
||||
// Find the token indicating what sort of stack frame walking
|
||||
// information this is.
|
||||
while (*stack_info_line == ' ')
|
||||
stack_info_line++;
|
||||
const char *platform = stack_info_line;
|
||||
while (!strchr(" \r\n", *stack_info_line))
|
||||
stack_info_line++;
|
||||
*stack_info_line++ = '\0';
|
||||
|
||||
// MSVC stack frame info.
|
||||
if (strcmp(platform, "WIN") == 0)
|
||||
return ParseWindowsFrameInfo(stack_info_line);
|
||||
|
||||
// DWARF CFI stack frame info
|
||||
else if (strcmp(platform, "CFI") == 0)
|
||||
return ParseCFIFrameInfo(stack_info_line);
|
||||
|
||||
// Something unrecognized.
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseWindowsFrameInfo(
|
||||
char *stack_info_line) {
|
||||
// The format of a STACK WIN record is documented at:
|
||||
//
|
||||
// http://code.google.com/p/google-breakpad/wiki/SymbolFiles
|
||||
|
||||
vector<char*> tokens;
|
||||
if (!Tokenize(stack_info_line, 12, &tokens))
|
||||
if (!Tokenize(stack_info_line, 11, &tokens))
|
||||
return false;
|
||||
|
||||
// Only MSVC stack frame info is understood for now.
|
||||
const char *platform = tokens[0];
|
||||
if (strcmp(platform, "WIN") != 0)
|
||||
int type = strtol(tokens[0], NULL, 16);
|
||||
if (type < 0 || type > WINDOWS_FRAME_INFO_LAST - 1)
|
||||
return false;
|
||||
|
||||
int type = strtol(tokens[1], NULL, 16);
|
||||
if (type < 0 || type > STACK_INFO_LAST - 1)
|
||||
return false;
|
||||
|
||||
u_int64_t rva = strtoull(tokens[2], NULL, 16);
|
||||
u_int64_t code_size = strtoull(tokens[3], NULL, 16);
|
||||
u_int32_t prolog_size = strtoul(tokens[4], NULL, 16);
|
||||
u_int32_t epilog_size = strtoul(tokens[5], NULL, 16);
|
||||
u_int32_t parameter_size = strtoul(tokens[6], NULL, 16);
|
||||
u_int32_t saved_register_size = strtoul(tokens[7], NULL, 16);
|
||||
u_int32_t local_size = strtoul(tokens[8], NULL, 16);
|
||||
u_int32_t max_stack_size = strtoul(tokens[9], NULL, 16);
|
||||
int has_program_string = strtoul(tokens[10], NULL, 16);
|
||||
u_int64_t rva = strtoull(tokens[1], NULL, 16);
|
||||
u_int64_t code_size = strtoull(tokens[2], NULL, 16);
|
||||
u_int32_t prolog_size = strtoul(tokens[3], NULL, 16);
|
||||
u_int32_t epilog_size = strtoul(tokens[4], NULL, 16);
|
||||
u_int32_t parameter_size = strtoul(tokens[5], NULL, 16);
|
||||
u_int32_t saved_register_size = strtoul(tokens[6], NULL, 16);
|
||||
u_int32_t local_size = strtoul(tokens[7], NULL, 16);
|
||||
u_int32_t max_stack_size = strtoul(tokens[8], NULL, 16);
|
||||
int has_program_string = strtoul(tokens[9], NULL, 16);
|
||||
|
||||
const char *program_string = "";
|
||||
int allocates_base_pointer = 0;
|
||||
if (has_program_string) {
|
||||
program_string = tokens[11];
|
||||
program_string = tokens[10];
|
||||
} else {
|
||||
allocates_base_pointer = strtoul(tokens[11], NULL, 16);
|
||||
allocates_base_pointer = strtoul(tokens[10], NULL, 16);
|
||||
}
|
||||
|
||||
// TODO(mmentovai): I wanted to use StoreRange's return value as this
|
||||
|
@ -681,8 +796,8 @@ bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
|
|||
// if ContainedRangeMap were modified to allow replacement of
|
||||
// already-stored values.
|
||||
|
||||
linked_ptr<StackFrameInfo> stack_frame_info(
|
||||
new StackFrameInfo(prolog_size,
|
||||
linked_ptr<WindowsFrameInfo> stack_frame_info(
|
||||
new WindowsFrameInfo(prolog_size,
|
||||
epilog_size,
|
||||
parameter_size,
|
||||
saved_register_size,
|
||||
|
@ -690,11 +805,46 @@ bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) {
|
|||
max_stack_size,
|
||||
allocates_base_pointer,
|
||||
program_string));
|
||||
stack_info_[type].StoreRange(rva, code_size, stack_frame_info);
|
||||
windows_frame_info_[type].StoreRange(rva, code_size, stack_frame_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::Module::ParseCFIFrameInfo(
|
||||
char *stack_info_line) {
|
||||
char *cursor;
|
||||
|
||||
// Is this an INIT record or a delta record?
|
||||
char *init_or_address = strtok_r(stack_info_line, " \r\n", &cursor);
|
||||
if (!init_or_address)
|
||||
return false;
|
||||
|
||||
if (strcmp(init_or_address, "INIT") == 0) {
|
||||
// This record has the form "STACK INIT <address> <size> <rules...>".
|
||||
char *address_field = strtok_r(NULL, " \r\n", &cursor);
|
||||
if (!address_field) return false;
|
||||
|
||||
char *size_field = strtok_r(NULL, " \r\n", &cursor);
|
||||
if (!size_field) return false;
|
||||
|
||||
char *initial_rules = strtok_r(NULL, "\r\n", &cursor);
|
||||
if (!initial_rules) return false;
|
||||
|
||||
MemAddr address = strtoul(address_field, NULL, 16);
|
||||
MemAddr size = strtoul(size_field, NULL, 16);
|
||||
cfi_initial_rules_.StoreRange(address, size, initial_rules);
|
||||
return true;
|
||||
}
|
||||
|
||||
// This record has the form "STACK <address> <rules...>".
|
||||
char *address_field = init_or_address;
|
||||
char *delta_rules = strtok_r(NULL, "\r\n", &cursor);
|
||||
if (!delta_rules) return false;
|
||||
MemAddr address = strtoul(address_field, NULL, 16);
|
||||
cfi_delta_rules_[address] = delta_rules;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BasicSourceLineResolver::CompareString::operator()(
|
||||
const string &s1, const string &s2) const {
|
||||
return strcmp(s1.c_str(), s2.c_str()) < 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -32,10 +32,12 @@
|
|||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/code_module.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
#include "processor/windows_frame_info.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
|
||||
#define ASSERT_TRUE(cond) \
|
||||
if (!(cond)) { \
|
||||
|
@ -51,11 +53,13 @@ namespace {
|
|||
|
||||
using std::string;
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CFIFrameInfo;
|
||||
using google_breakpad::CodeModule;
|
||||
using google_breakpad::MemoryRegion;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::WindowsFrameInfo;
|
||||
using google_breakpad::linked_ptr;
|
||||
using google_breakpad::scoped_ptr;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::StackFrameInfo;
|
||||
|
||||
class TestCodeModule : public CodeModule {
|
||||
public:
|
||||
|
@ -63,7 +67,7 @@ class TestCodeModule : public CodeModule {
|
|||
virtual ~TestCodeModule() {}
|
||||
|
||||
virtual u_int64_t base_address() const { return 0; }
|
||||
virtual u_int64_t size() const { return 0x4000; }
|
||||
virtual u_int64_t size() const { return 0xb000; }
|
||||
virtual string code_file() const { return code_file_; }
|
||||
virtual string code_identifier() const { return ""; }
|
||||
virtual string debug_file() const { return ""; }
|
||||
|
@ -77,6 +81,70 @@ class TestCodeModule : public CodeModule {
|
|||
string code_file_;
|
||||
};
|
||||
|
||||
// A mock memory region object, for use by the STACK CFI tests.
|
||||
class MockMemoryRegion: public MemoryRegion {
|
||||
u_int64_t GetBase() const { return 0x10000; }
|
||||
u_int32_t GetSize() const { return 0x01000; }
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
|
||||
*value = address & 0xff;
|
||||
return true;
|
||||
}
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
|
||||
*value = address & 0xffff;
|
||||
return true;
|
||||
}
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
|
||||
switch (address) {
|
||||
case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
|
||||
case 0x1000c: *value = 0x878f7524; break; // saved %esi
|
||||
case 0x10010: *value = 0x6312f9a5; break; // saved %edi
|
||||
case 0x10014: *value = 0x10038; break; // caller's %ebp
|
||||
case 0x10018: *value = 0xf6438648; break; // return address
|
||||
default: *value = 0xdeadbeef; break; // junk
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
|
||||
*value = address;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Verify that, for every association in ACTUAL, EXPECTED has the same
|
||||
// association. (That is, ACTUAL's associations should be a subset of
|
||||
// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
|
||||
// ".cfa".
|
||||
static bool VerifyRegisters(
|
||||
const char *file, int line,
|
||||
const CFIFrameInfo::RegisterValueMap<u_int32_t> &expected,
|
||||
const CFIFrameInfo::RegisterValueMap<u_int32_t> &actual) {
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator a;
|
||||
a = actual.find(".cfa");
|
||||
ASSERT_TRUE(a != actual.end());
|
||||
a = actual.find(".ra");
|
||||
ASSERT_TRUE(a != actual.end());
|
||||
for (a = actual.begin(); a != actual.end(); a++) {
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator e =
|
||||
expected.find(a->first);
|
||||
if (e == expected.end()) {
|
||||
fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
|
||||
file, line, a->first.c_str(), a->second);
|
||||
return false;
|
||||
}
|
||||
if (e->second != a->second) {
|
||||
fprintf(stderr,
|
||||
"%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
|
||||
file, line, a->first.c_str(), a->second, e->second);
|
||||
return false;
|
||||
}
|
||||
// Don't complain if this doesn't recover all registers. Although
|
||||
// the DWARF spec says that unmentioned registers are undefined,
|
||||
// GCC uses omission to mean that they are unchanged.
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool VerifyEmpty(const StackFrame &frame) {
|
||||
ASSERT_TRUE(frame.function_name.empty());
|
||||
ASSERT_TRUE(frame.source_file_name.empty());
|
||||
|
@ -104,9 +172,11 @@ static bool RunTests() {
|
|||
TestCodeModule module1("module1");
|
||||
|
||||
StackFrame frame;
|
||||
scoped_ptr<WindowsFrameInfo> windows_frame_info;
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info;
|
||||
frame.instruction = 0x1000;
|
||||
frame.module = NULL;
|
||||
scoped_ptr<StackFrameInfo> frame_info(resolver.FillSourceLineInfo(&frame));
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_FALSE(frame.module);
|
||||
ASSERT_TRUE(frame.function_name.empty());
|
||||
ASSERT_EQ(frame.function_base, 0);
|
||||
|
@ -115,7 +185,7 @@ static bool RunTests() {
|
|||
ASSERT_EQ(frame.source_line_base, 0);
|
||||
|
||||
frame.module = &module1;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Function1_1");
|
||||
ASSERT_TRUE(frame.module);
|
||||
ASSERT_EQ(frame.module->code_file(), "module1");
|
||||
|
@ -123,45 +193,152 @@ static bool RunTests() {
|
|||
ASSERT_EQ(frame.source_file_name, "file1_1.cc");
|
||||
ASSERT_EQ(frame.source_line, 44);
|
||||
ASSERT_EQ(frame.source_line_base, 0x1000);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_FALSE(frame_info->allocates_base_pointer);
|
||||
ASSERT_EQ(frame_info->program_string,
|
||||
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
||||
ASSERT_TRUE(windows_frame_info.get());
|
||||
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
|
||||
ASSERT_EQ(windows_frame_info->program_string,
|
||||
"$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
|
||||
|
||||
ClearSourceLineInfo(&frame);
|
||||
frame.instruction = 0x800;
|
||||
frame.module = &module1;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_TRUE(VerifyEmpty(frame));
|
||||
ASSERT_FALSE(frame_info.get());
|
||||
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
||||
ASSERT_FALSE(windows_frame_info.get());
|
||||
|
||||
frame.instruction = 0x1280;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Function1_3");
|
||||
ASSERT_TRUE(frame.source_file_name.empty());
|
||||
ASSERT_EQ(frame.source_line, 0);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_FALSE(frame_info->allocates_base_pointer);
|
||||
ASSERT_TRUE(frame_info->program_string.empty());
|
||||
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
||||
ASSERT_TRUE(windows_frame_info.get());
|
||||
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
|
||||
ASSERT_TRUE(windows_frame_info->program_string.empty());
|
||||
|
||||
frame.instruction = 0x1380;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Function1_4");
|
||||
ASSERT_TRUE(frame.source_file_name.empty());
|
||||
ASSERT_EQ(frame.source_line, 0);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_FALSE(frame_info->allocates_base_pointer);
|
||||
ASSERT_FALSE(frame_info->program_string.empty());
|
||||
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
||||
ASSERT_TRUE(windows_frame_info.get());
|
||||
ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
|
||||
ASSERT_FALSE(windows_frame_info->program_string.empty());
|
||||
|
||||
frame.instruction = 0x2000;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
ASSERT_FALSE(frame_info.get());
|
||||
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
||||
ASSERT_FALSE(windows_frame_info.get());
|
||||
|
||||
// module1 has STACK CFI records covering 3d40..3def;
|
||||
// module2 has STACK CFI records covering 3df0..3e9f;
|
||||
// check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
|
||||
frame.instruction = 0x3d3f;
|
||||
frame.module = &module1;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_FALSE(cfi_frame_info.get());
|
||||
|
||||
frame.instruction = 0x3e9f;
|
||||
frame.module = &module1;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_FALSE(cfi_frame_info.get());
|
||||
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t> current_registers;
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t> expected_caller_registers;
|
||||
MockMemoryRegion memory;
|
||||
|
||||
// Regardless of which instruction evaluation takes place at, it
|
||||
// should produce the same values for the caller's registers.
|
||||
expected_caller_registers[".cfa"] = 0x1001c;
|
||||
expected_caller_registers[".ra"] = 0xf6438648;
|
||||
expected_caller_registers["$ebp"] = 0x10038;
|
||||
expected_caller_registers["$ebx"] = 0x98ecadc3;
|
||||
expected_caller_registers["$esi"] = 0x878f7524;
|
||||
expected_caller_registers["$edi"] = 0x6312f9a5;
|
||||
|
||||
frame.instruction = 0x3d40;
|
||||
frame.module = &module1;
|
||||
current_registers.clear();
|
||||
current_registers["$esp"] = 0x10018;
|
||||
current_registers["$ebp"] = 0x10038;
|
||||
current_registers["$ebx"] = 0x98ecadc3;
|
||||
current_registers["$esi"] = 0x878f7524;
|
||||
current_registers["$edi"] = 0x6312f9a5;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_TRUE(cfi_frame_info.get());
|
||||
ASSERT_TRUE(cfi_frame_info.get()
|
||||
->FindCallerRegs<u_int32_t>(current_registers, memory,
|
||||
&caller_registers));
|
||||
VerifyRegisters(__FILE__, __LINE__,
|
||||
expected_caller_registers, caller_registers);
|
||||
|
||||
frame.instruction = 0x3d41;
|
||||
current_registers["$esp"] = 0x10014;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_TRUE(cfi_frame_info.get());
|
||||
ASSERT_TRUE(cfi_frame_info.get()
|
||||
->FindCallerRegs<u_int32_t>(current_registers, memory,
|
||||
&caller_registers));
|
||||
VerifyRegisters(__FILE__, __LINE__,
|
||||
expected_caller_registers, caller_registers);
|
||||
|
||||
frame.instruction = 0x3d43;
|
||||
current_registers["$ebp"] = 0x10014;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_TRUE(cfi_frame_info.get());
|
||||
ASSERT_TRUE(cfi_frame_info.get()
|
||||
->FindCallerRegs<u_int32_t>(current_registers, memory,
|
||||
&caller_registers));
|
||||
VerifyRegisters(__FILE__, __LINE__,
|
||||
expected_caller_registers, caller_registers);
|
||||
|
||||
frame.instruction = 0x3d54;
|
||||
current_registers["$ebx"] = 0x6864f054U;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_TRUE(cfi_frame_info.get());
|
||||
ASSERT_TRUE(cfi_frame_info.get()
|
||||
->FindCallerRegs<u_int32_t>(current_registers, memory,
|
||||
&caller_registers));
|
||||
VerifyRegisters(__FILE__, __LINE__,
|
||||
expected_caller_registers, caller_registers);
|
||||
|
||||
frame.instruction = 0x3d5a;
|
||||
current_registers["$esi"] = 0x6285f79aU;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_TRUE(cfi_frame_info.get());
|
||||
ASSERT_TRUE(cfi_frame_info.get()
|
||||
->FindCallerRegs<u_int32_t>(current_registers, memory,
|
||||
&caller_registers));
|
||||
VerifyRegisters(__FILE__, __LINE__,
|
||||
expected_caller_registers, caller_registers);
|
||||
|
||||
frame.instruction = 0x3d84;
|
||||
current_registers["$edi"] = 0x64061449U;
|
||||
cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
|
||||
ASSERT_TRUE(cfi_frame_info.get());
|
||||
ASSERT_TRUE(cfi_frame_info.get()
|
||||
->FindCallerRegs<u_int32_t>(current_registers, memory,
|
||||
&caller_registers));
|
||||
VerifyRegisters(__FILE__, __LINE__,
|
||||
expected_caller_registers, caller_registers);
|
||||
|
||||
frame.instruction = 0x2900;
|
||||
frame.module = &module1;
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, string("PublicSymbol"));
|
||||
|
||||
frame.instruction = 0x4000;
|
||||
frame.module = &module1;
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, string("LargeFunction"));
|
||||
|
||||
TestCodeModule module2("module2");
|
||||
|
||||
frame.instruction = 0x2181;
|
||||
frame.module = &module2;
|
||||
frame_info.reset(resolver.FillSourceLineInfo(&frame));
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Function2_2");
|
||||
ASSERT_EQ(frame.function_base, 0x2170);
|
||||
ASSERT_TRUE(frame.module);
|
||||
|
@ -169,14 +346,13 @@ static bool RunTests() {
|
|||
ASSERT_EQ(frame.source_file_name, "file2_2.cc");
|
||||
ASSERT_EQ(frame.source_line, 21);
|
||||
ASSERT_EQ(frame.source_line_base, 0x2180);
|
||||
ASSERT_TRUE(frame_info.get());
|
||||
ASSERT_EQ(frame_info->prolog_size, 1);
|
||||
windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
|
||||
ASSERT_TRUE(windows_frame_info.get());
|
||||
ASSERT_EQ(windows_frame_info->prolog_size, 1);
|
||||
|
||||
frame.instruction = 0x216f;
|
||||
StackFrameInfo *s;
|
||||
s = resolver.FillSourceLineInfo(&frame);
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Public2_1");
|
||||
delete s;
|
||||
|
||||
ClearSourceLineInfo(&frame);
|
||||
frame.instruction = 0x219f;
|
||||
|
@ -186,11 +362,9 @@ static bool RunTests() {
|
|||
|
||||
frame.instruction = 0x21a0;
|
||||
frame.module = &module2;
|
||||
s = resolver.FillSourceLineInfo(&frame);
|
||||
resolver.FillSourceLineInfo(&frame);
|
||||
ASSERT_EQ(frame.function_name, "Public2_2");
|
||||
|
||||
delete s;
|
||||
|
||||
ASSERT_FALSE(resolver.LoadModule("module3",
|
||||
testdata_dir + "/module3_bad.out"));
|
||||
ASSERT_FALSE(resolver.HasModule("module3"));
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
// -*- 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_frame_info-inl.h: Definitions for cfi_frame_info.h inlined functions.
|
||||
|
||||
#ifndef PROCESSOR_CFI_FRAME_INFO_INL_H_
|
||||
#define PROCESSOR_CFI_FRAME_INFO_INL_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
template <typename RegisterType, class RawContextType>
|
||||
bool SimpleCFIWalker<RegisterType, RawContextType>::FindCallerRegisters(
|
||||
const MemoryRegion &memory,
|
||||
const CFIFrameInfo &cfi_frame_info,
|
||||
const RawContextType &callee_context,
|
||||
int callee_validity,
|
||||
RawContextType *caller_context,
|
||||
int *caller_validity) const {
|
||||
typedef CFIFrameInfo::RegisterValueMap<RegisterType> ValueMap;
|
||||
ValueMap callee_registers;
|
||||
ValueMap caller_registers;
|
||||
// Just for brevity.
|
||||
typename ValueMap::const_iterator caller_none = caller_registers.end();
|
||||
|
||||
// Populate callee_registers with register values from callee_context.
|
||||
for (size_t i = 0; i < map_size_; i++) {
|
||||
const RegisterSet &r = register_map_[i];
|
||||
if (callee_validity & r.validity_flag)
|
||||
callee_registers[r.name] = callee_context.*r.context_member;
|
||||
}
|
||||
|
||||
// Apply the rules, and see what register values they yield.
|
||||
if (!cfi_frame_info.FindCallerRegs<RegisterType>(callee_registers, memory,
|
||||
&caller_registers))
|
||||
return false;
|
||||
|
||||
// Populate *caller_context with the values the rules placed in
|
||||
// caller_registers.
|
||||
memset(caller_context, 0xda, sizeof(caller_context));
|
||||
*caller_validity = 0;
|
||||
for (size_t i = 0; i < map_size_; i++) {
|
||||
const RegisterSet &r = register_map_[i];
|
||||
typename ValueMap::const_iterator caller_entry;
|
||||
|
||||
// Did the rules provide a value for this register by its name?
|
||||
caller_entry = caller_registers.find(r.name);
|
||||
if (caller_entry != caller_none) {
|
||||
caller_context->*r.context_member = caller_entry->second;
|
||||
*caller_validity |= r.validity_flag;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Did the rules provide a value for this register under its
|
||||
// alternate name?
|
||||
if (r.alternate_name) {
|
||||
caller_entry = caller_registers.find(r.alternate_name);
|
||||
if (caller_entry != caller_none) {
|
||||
caller_context->*r.context_member = caller_entry->second;
|
||||
*caller_validity |= r.validity_flag;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Is this a callee-saves register? The walker assumes that these
|
||||
// still hold the caller's value if the CFI doesn't mention them.
|
||||
//
|
||||
// Note that other frame walkers may fail to recover callee-saves
|
||||
// registers; for example, the x86 "traditional" strategy only
|
||||
// recovers %eip, %esp, and %ebp, even though %ebx, %esi, and %edi
|
||||
// are callee-saves, too. It is not correct to blindly set the
|
||||
// valid bit for all callee-saves registers, without first
|
||||
// checking its validity bit in the callee.
|
||||
if (r.callee_saves && (callee_validity & r.validity_flag) != 0) {
|
||||
caller_context->*r.context_member = callee_context.*r.context_member;
|
||||
*caller_validity |= r.validity_flag;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, the register's value is unknown.
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // PROCESSOR_CFI_FRAME_INFO_INL_H_
|
|
@ -0,0 +1,157 @@
|
|||
// 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_frame_info.cc: Implementation of CFIFrameInfo class.
|
||||
// See cfi_frame_info.h for details.
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/postfix_evaluator-inl.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
template<typename V>
|
||||
bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap<V> ®isters,
|
||||
const MemoryRegion &memory,
|
||||
RegisterValueMap<V> *caller_registers) const {
|
||||
// If there are not rules for both .ra and .cfa in effect at this address,
|
||||
// don't use this CFI data for stack walking.
|
||||
if (cfa_rule_.empty() || ra_rule_.empty())
|
||||
return false;
|
||||
|
||||
RegisterValueMap<V> working;
|
||||
PostfixEvaluator<V> evaluator(&working, &memory);
|
||||
|
||||
caller_registers->clear();
|
||||
|
||||
// First, compute the CFA.
|
||||
V cfa;
|
||||
working = registers;
|
||||
if (!evaluator.EvaluateForValue(cfa_rule_, &cfa))
|
||||
return false;
|
||||
|
||||
// Then, compute the return address.
|
||||
V ra;
|
||||
working = registers;
|
||||
working[".cfa"] = cfa;
|
||||
if (!evaluator.EvaluateForValue(ra_rule_, &ra))
|
||||
return false;
|
||||
|
||||
// Now, compute values for all the registers register_rules_ mentions.
|
||||
for (RuleMap::const_iterator it = register_rules_.begin();
|
||||
it != register_rules_.end(); it++) {
|
||||
V value;
|
||||
working = registers;
|
||||
working[".cfa"] = cfa;
|
||||
if (!evaluator.EvaluateForValue(it->second, &value))
|
||||
return false;
|
||||
(*caller_registers)[it->first] = value;
|
||||
}
|
||||
|
||||
(*caller_registers)[".ra"] = ra;
|
||||
(*caller_registers)[".cfa"] = cfa;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Explicit instantiations for 32-bit and 64-bit architectures.
|
||||
template bool CFIFrameInfo::FindCallerRegs<u_int32_t>(
|
||||
const RegisterValueMap<u_int32_t> ®isters,
|
||||
const MemoryRegion &memory,
|
||||
RegisterValueMap<u_int32_t> *caller_registers) const;
|
||||
template bool CFIFrameInfo::FindCallerRegs<u_int64_t>(
|
||||
const RegisterValueMap<u_int64_t> ®isters,
|
||||
const MemoryRegion &memory,
|
||||
RegisterValueMap<u_int64_t> *caller_registers) const;
|
||||
|
||||
bool CFIRuleParser::Parse(const string &rule_set) {
|
||||
size_t rule_set_len = rule_set.size();
|
||||
scoped_array<char> working_copy(new char[rule_set_len + 1]);
|
||||
memcpy(working_copy.get(), rule_set.data(), rule_set_len);
|
||||
working_copy[rule_set_len] = '\0';
|
||||
|
||||
name_.clear();
|
||||
expression_.clear();
|
||||
|
||||
char *cursor;
|
||||
static const char token_breaks[] = " \t\r\n";
|
||||
char *token = strtok_r(working_copy.get(), token_breaks, &cursor);
|
||||
|
||||
for (;;) {
|
||||
// End of rule set?
|
||||
if (!token) return Report();
|
||||
|
||||
// Register/pseudoregister name?
|
||||
size_t token_len = strlen(token);
|
||||
if (token_len >= 1 && token[token_len - 1] == ':') {
|
||||
// Names can't be empty.
|
||||
if (token_len < 2) return false;
|
||||
// If there is any pending content, report it.
|
||||
if (!name_.empty() || !expression_.empty()) {
|
||||
if (!Report()) return false;
|
||||
}
|
||||
name_.assign(token, token_len - 1);
|
||||
expression_.clear();
|
||||
} else {
|
||||
// Another expression component.
|
||||
assert(token_len > 0); // strtok_r guarantees this, I think.
|
||||
if (!expression_.empty())
|
||||
expression_ += ' ';
|
||||
expression_ += token;
|
||||
}
|
||||
token = strtok_r(NULL, token_breaks, &cursor);
|
||||
}
|
||||
}
|
||||
|
||||
bool CFIRuleParser::Report() {
|
||||
if (name_.empty() || expression_.empty()) return false;
|
||||
if (name_ == ".cfa") handler_->CFARule(expression_);
|
||||
else if (name_ == ".ra") handler_->RARule(expression_);
|
||||
else handler_->RegisterRule(name_, expression_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFIFrameInfoParseHandler::CFARule(const string &expression) {
|
||||
frame_info_->SetCFARule(expression);
|
||||
}
|
||||
|
||||
void CFIFrameInfoParseHandler::RARule(const string &expression) {
|
||||
frame_info_->SetRARule(expression);
|
||||
}
|
||||
|
||||
void CFIFrameInfoParseHandler::RegisterRule(const string &name,
|
||||
const string &expression) {
|
||||
frame_info_->SetRegisterRule(name, expression);
|
||||
}
|
||||
|
||||
} // namespace google_breakpad
|
|
@ -0,0 +1,271 @@
|
|||
// -*- 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_frame_info.h: Define the CFIFrameInfo class, which holds the
|
||||
// set of 'STACK CFI'-derived register recovery rules that apply at a
|
||||
// given instruction.
|
||||
|
||||
#ifndef PROCESSOR_CFI_FRAME_INFO_H_
|
||||
#define PROCESSOR_CFI_FRAME_INFO_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
class MemoryRegion;
|
||||
|
||||
// A set of rules for recovering the calling frame's registers'
|
||||
// values, when the PC is at a given address in the current frame's
|
||||
// function. See the description of 'STACK CFI' records at:
|
||||
//
|
||||
// http://code.google.com/p/google-breakpad/wiki/SymbolFiles
|
||||
//
|
||||
// To prepare an instance of CFIFrameInfo for use at a given
|
||||
// instruction, first populate it with the rules from the 'STACK CFI
|
||||
// INIT' record that covers that instruction, and then apply the
|
||||
// changes given by the 'STACK CFI' records up to our instruction's
|
||||
// address. Then, use the FindCallerRegs member function to apply the
|
||||
// rules to the callee frame's register values, yielding the caller
|
||||
// frame's register values.
|
||||
class CFIFrameInfo {
|
||||
public:
|
||||
// A map from register names onto values.
|
||||
template<typename ValueType> class RegisterValueMap:
|
||||
public map<string, ValueType> { };
|
||||
|
||||
// Set the expression for computing a call frame address, return
|
||||
// address, or register's value. At least the CFA rule and the RA
|
||||
// rule must be set before calling FindCallerRegs.
|
||||
void SetCFARule(const string &expression) { cfa_rule_ = expression; }
|
||||
void SetRARule(const string &expression) { ra_rule_ = expression; }
|
||||
void SetRegisterRule(const string ®ister_name, const string &expression) {
|
||||
register_rules_[register_name] = expression;
|
||||
}
|
||||
|
||||
// Compute the values of the calling frame's registers, according to
|
||||
// this rule set. Use ValueType in expression evaluation; this
|
||||
// should be u_int32_t on machines with 32-bit addresses, or
|
||||
// u_int64_t on machines with 64-bit addresses.
|
||||
//
|
||||
// Return true on success, false otherwise.
|
||||
//
|
||||
// MEMORY provides access to the contents of the stack. REGISTERS is
|
||||
// a dictionary mapping the names of registers whose values are
|
||||
// known in the current frame to their values. CALLER_REGISTERS is
|
||||
// populated with the values of the recoverable registers in the
|
||||
// frame that called the current frame.
|
||||
//
|
||||
// In addition, CALLER_REGISTERS[".ra"] will be the return address,
|
||||
// and CALLER_REGISTERS[".cfa"] will be the call frame address.
|
||||
// These may be helpful in computing the caller's PC and stack
|
||||
// pointer, if their values are not explicitly specified.
|
||||
template<typename ValueType>
|
||||
bool FindCallerRegs(const RegisterValueMap<ValueType> ®isters,
|
||||
const MemoryRegion &memory,
|
||||
RegisterValueMap<ValueType> *caller_registers) const;
|
||||
|
||||
private:
|
||||
|
||||
// A map from register names onto evaluation rules.
|
||||
typedef map<string, string> RuleMap;
|
||||
|
||||
// In this type, a "postfix expression" is an expression of the sort
|
||||
// interpreted by google_breakpad::PostfixEvaluator.
|
||||
|
||||
// A postfix expression for computing the current frame's CFA (call
|
||||
// frame address). The CFA is a reference address for the frame that
|
||||
// remains unchanged throughout the frame's lifetime. You should
|
||||
// evaluate this expression with a dictionary initially populated
|
||||
// with the values of the current frame's known registers.
|
||||
string cfa_rule_;
|
||||
|
||||
// The following expressions should be evaluated with a dictionary
|
||||
// initially populated with the values of the current frame's known
|
||||
// registers, and with ".cfa" set to the result of evaluating the
|
||||
// cfa_rule expression, above.
|
||||
|
||||
// A postfix expression for computing the current frame's return
|
||||
// address.
|
||||
string ra_rule_;
|
||||
|
||||
// For a register named REG, rules[REG] is a postfix expression
|
||||
// which leaves the value of REG in the calling frame on the top of
|
||||
// the stack. You should evaluate this expression
|
||||
RuleMap register_rules_;
|
||||
};
|
||||
|
||||
// A parser for STACK CFI-style rule sets.
|
||||
// This may seem bureaucratic: there's no legitimate run-time reason
|
||||
// to use a parser/handler pattern for this, as it's not a likely
|
||||
// reuse boundary. But doing so makes finer-grained unit testing
|
||||
// possible.
|
||||
class CFIRuleParser {
|
||||
public:
|
||||
|
||||
class Handler {
|
||||
public:
|
||||
Handler() { }
|
||||
virtual ~Handler() { }
|
||||
|
||||
// The input specifies EXPRESSION as the CFA/RA computation rule.
|
||||
virtual void CFARule(const string &expression) = 0;
|
||||
virtual void RARule(const string &expression) = 0;
|
||||
|
||||
// The input specifies EXPRESSION as the recovery rule for register NAME.
|
||||
virtual void RegisterRule(const string &name, const string &expression) = 0;
|
||||
};
|
||||
|
||||
// Construct a parser which feeds its results to HANDLER.
|
||||
CFIRuleParser(Handler *handler) : handler_(handler) { }
|
||||
|
||||
// Parse RULE_SET as a set of CFA computation and RA/register
|
||||
// recovery rules, as appearing in STACK CFI records. Report the
|
||||
// results of parsing by making the appropriate calls to handler_.
|
||||
// Return true if parsing was successful, false otherwise.
|
||||
bool Parse(const string &rule_set);
|
||||
|
||||
private:
|
||||
// Report any accumulated rule to handler_
|
||||
bool Report();
|
||||
|
||||
// The handler to which the parser reports its findings.
|
||||
Handler *handler_;
|
||||
|
||||
// Working data.
|
||||
string name_, expression_;
|
||||
};
|
||||
|
||||
// A handler for rule set parsing that populates a CFIFrameInfo with
|
||||
// the results.
|
||||
class CFIFrameInfoParseHandler: public CFIRuleParser::Handler {
|
||||
public:
|
||||
// Populate FRAME_INFO with the results of parsing.
|
||||
CFIFrameInfoParseHandler(CFIFrameInfo *frame_info)
|
||||
: frame_info_(frame_info) { }
|
||||
|
||||
void CFARule(const string &expression);
|
||||
void RARule(const string &expression);
|
||||
void RegisterRule(const string &name, const string &expression);
|
||||
|
||||
private:
|
||||
CFIFrameInfo *frame_info_;
|
||||
};
|
||||
|
||||
// A utility class template for simple 'STACK CFI'-driven stack walkers.
|
||||
// Given a CFIFrameInfo instance, a table describing the architecture's
|
||||
// register set, and a context holding the last frame's registers, an
|
||||
// instance of this class can populate a new context with the caller's
|
||||
// registers.
|
||||
//
|
||||
// This class template doesn't use any internal knowledge of CFIFrameInfo
|
||||
// or the other stack walking structures; it just uses the public interface
|
||||
// of CFIFrameInfo to do the usual things. But the logic it handles should
|
||||
// be common to many different architectures' stack walkers, so wrapping it
|
||||
// up in a class should allow the walkers to share code.
|
||||
//
|
||||
// RegisterType should be the type of this architecture's registers, either
|
||||
// u_int32_t or u_int64_t. RawContextType should be the raw context
|
||||
// structure type for this architecture.
|
||||
template <typename RegisterType, class RawContextType>
|
||||
class SimpleCFIWalker {
|
||||
public:
|
||||
// A structure describing one architecture register.
|
||||
struct RegisterSet {
|
||||
// The register name, as it appears in STACK CFI rules.
|
||||
const char *name;
|
||||
|
||||
// An alternate name that the register's value might be found
|
||||
// under in a register value dictionary, or NULL. When generating
|
||||
// names, prefer NAME to this value. It's common to list ".cfa" as
|
||||
// an alternative name for the stack pointer, and ".ra" as an
|
||||
// alternative name for the instruction pointer.
|
||||
const char *alternate_name;
|
||||
|
||||
// True if the callee is expected to preserve the value of this
|
||||
// register. If this flag is true for some register R, and the STACK
|
||||
// CFI records provide no rule to recover R, then SimpleCFIWalker
|
||||
// assumes that the callee has not changed R's value, and the caller's
|
||||
// value for R is that currently in the callee's context.
|
||||
bool callee_saves;
|
||||
|
||||
// The ContextValidity flag representing the register's presence.
|
||||
int validity_flag;
|
||||
|
||||
// A pointer to the RawContextType member that holds the
|
||||
// register's value.
|
||||
RegisterType RawContextType::*context_member;
|
||||
};
|
||||
|
||||
// Create a simple CFI-based frame walker, given a description of the
|
||||
// architecture's register set. REGISTER_MAP is an array of
|
||||
// RegisterSet structures; MAP_SIZE is the number of elements in the
|
||||
// array.
|
||||
SimpleCFIWalker(const RegisterSet *register_map, size_t map_size)
|
||||
: register_map_(register_map), map_size_(map_size) { }
|
||||
|
||||
// Compute the calling frame's raw context given the callee's raw
|
||||
// context.
|
||||
//
|
||||
// Given:
|
||||
//
|
||||
// - MEMORY, holding the stack's contents,
|
||||
// - CFI_FRAME_INFO, describing the called function,
|
||||
// - CALLEE_CONTEXT, holding the called frame's registers, and
|
||||
// - CALLEE_VALIDITY, indicating which registers in CALLEE_CONTEXT are valid,
|
||||
//
|
||||
// fill in CALLER_CONTEXT with the caller's register values, and set
|
||||
// CALLER_VALIDITY to indicate which registers are valid in
|
||||
// CALLER_CONTEXT. Return true on success, or false on failure.
|
||||
bool FindCallerRegisters(const MemoryRegion &memory,
|
||||
const CFIFrameInfo &cfi_frame_info,
|
||||
const RawContextType &callee_context,
|
||||
int callee_validity,
|
||||
RawContextType *caller_context,
|
||||
int *caller_validity) const;
|
||||
|
||||
private:
|
||||
const RegisterSet *register_map_;
|
||||
size_t map_size_;
|
||||
};
|
||||
|
||||
} // namespace google_breakpad
|
||||
|
||||
#include "cfi_frame_info-inl.h"
|
||||
|
||||
#endif // PROCESSOR_CFI_FRAME_INFO_H_
|
|
@ -0,0 +1,531 @@
|
|||
// 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_frame_info_unittest.cc: Unit tests for CFIFrameInfo,
|
||||
// CFIRuleParser, CFIFrameInfoParseHandler, and SimpleCFIWalker.
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
|
||||
using google_breakpad::CFIFrameInfo;
|
||||
using google_breakpad::CFIFrameInfoParseHandler;
|
||||
using google_breakpad::CFIRuleParser;
|
||||
using google_breakpad::MemoryRegion;
|
||||
using google_breakpad::SimpleCFIWalker;
|
||||
using std::string;
|
||||
using testing::_;
|
||||
using testing::A;
|
||||
using testing::AtMost;
|
||||
using testing::DoAll;
|
||||
using testing::Return;
|
||||
using testing::SetArgumentPointee;
|
||||
using testing::Test;
|
||||
|
||||
class MockMemoryRegion: public MemoryRegion {
|
||||
public:
|
||||
MOCK_CONST_METHOD0(GetBase, u_int64_t());
|
||||
MOCK_CONST_METHOD0(GetSize, u_int32_t());
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int8_t *));
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int16_t *));
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int32_t *));
|
||||
MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int64_t *));
|
||||
};
|
||||
|
||||
// Handy definitions for all tests.
|
||||
struct CFIFixture {
|
||||
|
||||
// Set up the mock memory object to expect no references.
|
||||
void ExpectNoMemoryReferences() {
|
||||
EXPECT_CALL(memory, GetBase()).Times(0);
|
||||
EXPECT_CALL(memory, GetSize()).Times(0);
|
||||
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int8_t *>())).Times(0);
|
||||
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int16_t *>())).Times(0);
|
||||
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int32_t *>())).Times(0);
|
||||
EXPECT_CALL(memory, GetMemoryAtAddress(_, A<u_int64_t *>())).Times(0);
|
||||
}
|
||||
|
||||
CFIFrameInfo cfi;
|
||||
MockMemoryRegion memory;
|
||||
CFIFrameInfo::RegisterValueMap<u_int64_t> registers, caller_registers;
|
||||
};
|
||||
|
||||
class Simple: public CFIFixture, public Test { };
|
||||
|
||||
// FindCallerRegs should fail if no .cfa rule is provided.
|
||||
TEST_F(Simple, NoCFA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetRARule("0");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
}
|
||||
|
||||
// FindCallerRegs should fail if no .ra rule is provided.
|
||||
TEST_F(Simple, NoRA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("0");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
}
|
||||
|
||||
TEST_F(Simple, SetCFAAndRARule) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("330903416631436410");
|
||||
cfi.SetRARule("5870666104170902211");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(2U, caller_registers.size());
|
||||
ASSERT_EQ(330903416631436410ULL, caller_registers[".cfa"]);
|
||||
ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
|
||||
}
|
||||
|
||||
TEST_F(Simple, SetManyRules) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("$temp1 68737028 = $temp2 61072337 = $temp1 $temp2 -");
|
||||
cfi.SetRARule(".cfa 99804755 +");
|
||||
cfi.SetRegisterRule("register1", ".cfa 54370437 *");
|
||||
cfi.SetRegisterRule("vodkathumbscrewingly", "24076308 .cfa +");
|
||||
cfi.SetRegisterRule("pubvexingfjordschmaltzy", ".cfa 29801007 -");
|
||||
cfi.SetRegisterRule("uncopyrightables", "92642917 .cfa /");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(6U, caller_registers.size());
|
||||
ASSERT_EQ(7664691U, caller_registers[".cfa"]);
|
||||
ASSERT_EQ(107469446U, caller_registers[".ra"]);
|
||||
ASSERT_EQ(416732599139967ULL, caller_registers["register1"]);
|
||||
ASSERT_EQ(31740999U, caller_registers["vodkathumbscrewingly"]);
|
||||
ASSERT_EQ(-22136316ULL, caller_registers["pubvexingfjordschmaltzy"]);
|
||||
ASSERT_EQ(12U, caller_registers["uncopyrightables"]);
|
||||
}
|
||||
|
||||
TEST_F(Simple, RulesOverride) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("330903416631436410");
|
||||
cfi.SetRARule("5870666104170902211");
|
||||
cfi.SetCFARule("2828089117179001");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(2U, caller_registers.size());
|
||||
ASSERT_EQ(2828089117179001ULL, caller_registers[".cfa"]);
|
||||
ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]);
|
||||
}
|
||||
|
||||
class Scope: public CFIFixture, public Test { };
|
||||
|
||||
// There should be no value for .cfa in scope when evaluating the CFA rule.
|
||||
TEST_F(Scope, CFALacksCFA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule(".cfa");
|
||||
cfi.SetRARule("0");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
}
|
||||
|
||||
// There should be no value for .ra in scope when evaluating the CFA rule.
|
||||
TEST_F(Scope, CFALacksRA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule(".ra");
|
||||
cfi.SetRARule("0");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
}
|
||||
|
||||
// The current frame's registers should be in scope when evaluating
|
||||
// the CFA rule.
|
||||
TEST_F(Scope, CFASeesCurrentRegs) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
registers[".baraminology"] = 0x06a7bc63e4f13893ULL;
|
||||
registers[".ornithorhynchus"] = 0x5e0bf850bafce9d2ULL;
|
||||
cfi.SetCFARule(".baraminology .ornithorhynchus +");
|
||||
cfi.SetRARule("0");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(2U, caller_registers.size());
|
||||
ASSERT_EQ(0x06a7bc63e4f13893ULL + 0x5e0bf850bafce9d2ULL,
|
||||
caller_registers[".cfa"]);
|
||||
}
|
||||
|
||||
// .cfa should be in scope in the return address expression.
|
||||
TEST_F(Scope, RASeesCFA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("48364076");
|
||||
cfi.SetRARule(".cfa");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(2U, caller_registers.size());
|
||||
ASSERT_EQ(48364076U, caller_registers[".ra"]);
|
||||
}
|
||||
|
||||
// There should be no value for .ra in scope when evaluating the CFA rule.
|
||||
TEST_F(Scope, RALacksRA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("0");
|
||||
cfi.SetRARule(".ra");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
}
|
||||
|
||||
// The current frame's registers should be in scope in the return
|
||||
// address expression.
|
||||
TEST_F(Scope, RASeesCurrentRegs) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
registers["noachian"] = 0x54dc4a5d8e5eb503ULL;
|
||||
cfi.SetCFARule("10359370");
|
||||
cfi.SetRARule("noachian");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(2U, caller_registers.size());
|
||||
ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[".ra"]);
|
||||
}
|
||||
|
||||
// .cfa should be in scope for register rules.
|
||||
TEST_F(Scope, RegistersSeeCFA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("6515179");
|
||||
cfi.SetRARule(".cfa");
|
||||
cfi.SetRegisterRule("rogerian", ".cfa");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(3U, caller_registers.size());
|
||||
ASSERT_EQ(6515179U, caller_registers["rogerian"]);
|
||||
}
|
||||
|
||||
// The return address should not be in scope for register rules.
|
||||
TEST_F(Scope, RegsLackRA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("42740329");
|
||||
cfi.SetRARule("27045204");
|
||||
cfi.SetRegisterRule("$r1", ".ra");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
}
|
||||
|
||||
// Register rules can see the current frame's register values.
|
||||
TEST_F(Scope, RegsSeeRegs) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
registers["$r1"] = 0x6ed3582c4bedb9adULL;
|
||||
registers["$r2"] = 0xd27d9e742b8df6d0ULL;
|
||||
cfi.SetCFARule("88239303");
|
||||
cfi.SetRARule("30503835");
|
||||
cfi.SetRegisterRule("$r1", "$r1 42175211 = $r2");
|
||||
cfi.SetRegisterRule("$r2", "$r2 21357221 = $r1");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(4U, caller_registers.size());
|
||||
ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers["$r1"]);
|
||||
ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers["$r2"]);
|
||||
}
|
||||
|
||||
// Each rule's temporaries are separate.
|
||||
TEST_F(Scope, SeparateTempsRA) {
|
||||
ExpectNoMemoryReferences();
|
||||
|
||||
cfi.SetCFARule("$temp1 76569129 = $temp1");
|
||||
cfi.SetRARule("0");
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
|
||||
cfi.SetCFARule("$temp1 76569129 = $temp1");
|
||||
cfi.SetRARule("$temp1");
|
||||
ASSERT_FALSE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
}
|
||||
|
||||
class MockCFIRuleParserHandler: public CFIRuleParser::Handler {
|
||||
public:
|
||||
MOCK_METHOD1(CFARule, void(const string &));
|
||||
MOCK_METHOD1(RARule, void(const string &));
|
||||
MOCK_METHOD2(RegisterRule, void(const string &, const string &));
|
||||
};
|
||||
|
||||
// A fixture class for testing CFIRuleParser.
|
||||
class CFIParserFixture {
|
||||
public:
|
||||
CFIParserFixture() : parser(&mock_handler) {
|
||||
// Expect no parsing results to be reported to mock_handler. Individual
|
||||
// tests can override this.
|
||||
EXPECT_CALL(mock_handler, CFARule(_)).Times(0);
|
||||
EXPECT_CALL(mock_handler, RARule(_)).Times(0);
|
||||
EXPECT_CALL(mock_handler, RegisterRule(_, _)).Times(0);
|
||||
}
|
||||
|
||||
MockCFIRuleParserHandler mock_handler;
|
||||
CFIRuleParser parser;
|
||||
};
|
||||
|
||||
class Parser: public CFIParserFixture, public Test { };
|
||||
|
||||
TEST_F(Parser, Empty) {
|
||||
EXPECT_FALSE(parser.Parse(""));
|
||||
}
|
||||
|
||||
TEST_F(Parser, LoneColon) {
|
||||
EXPECT_FALSE(parser.Parse(":"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, CFANoExpr) {
|
||||
EXPECT_FALSE(parser.Parse(".cfa:"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, CFANoColonNoExpr) {
|
||||
EXPECT_FALSE(parser.Parse(".cfa"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, RANoExpr) {
|
||||
EXPECT_FALSE(parser.Parse(".ra:"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, RANoColonNoExpr) {
|
||||
EXPECT_FALSE(parser.Parse(".ra"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, RegNoExpr) {
|
||||
EXPECT_FALSE(parser.Parse("reg:"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, NoName) {
|
||||
EXPECT_FALSE(parser.Parse("expr"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, NoNameTwo) {
|
||||
EXPECT_FALSE(parser.Parse("expr1 expr2"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, StartsWithExpr) {
|
||||
EXPECT_FALSE(parser.Parse("expr1 reg: expr2"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, CFA) {
|
||||
EXPECT_CALL(mock_handler, CFARule("spleen")).WillOnce(Return());
|
||||
EXPECT_TRUE(parser.Parse(".cfa: spleen"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, RA) {
|
||||
EXPECT_CALL(mock_handler, RARule("notoriety")).WillOnce(Return());
|
||||
EXPECT_TRUE(parser.Parse(".ra: notoriety"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, Reg) {
|
||||
EXPECT_CALL(mock_handler, RegisterRule("nemo", "mellifluous"))
|
||||
.WillOnce(Return());
|
||||
EXPECT_TRUE(parser.Parse("nemo: mellifluous"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, CFARARegs) {
|
||||
EXPECT_CALL(mock_handler, CFARule("cfa expression")).WillOnce(Return());
|
||||
EXPECT_CALL(mock_handler, RARule("ra expression")).WillOnce(Return());
|
||||
EXPECT_CALL(mock_handler, RegisterRule("galba", "praetorian"))
|
||||
.WillOnce(Return());
|
||||
EXPECT_CALL(mock_handler, RegisterRule("otho", "vitellius"))
|
||||
.WillOnce(Return());
|
||||
EXPECT_TRUE(parser.Parse(".cfa: cfa expression .ra: ra expression "
|
||||
"galba: praetorian otho: vitellius"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, Whitespace) {
|
||||
EXPECT_CALL(mock_handler, RegisterRule("r1", "r1 expression"))
|
||||
.WillOnce(Return());
|
||||
EXPECT_CALL(mock_handler, RegisterRule("r2", "r2 expression"))
|
||||
.WillOnce(Return());
|
||||
EXPECT_TRUE(parser.Parse(" r1:\tr1\nexpression \tr2:\t\rr2\r\n "
|
||||
"expression \n"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, WhitespaceLoneColon) {
|
||||
EXPECT_FALSE(parser.Parse(" \n:\t "));
|
||||
}
|
||||
|
||||
TEST_F(Parser, EmptyName) {
|
||||
EXPECT_CALL(mock_handler, RegisterRule("reg", _))
|
||||
.Times(AtMost(1))
|
||||
.WillRepeatedly(Return());
|
||||
EXPECT_FALSE(parser.Parse("reg: expr1 : expr2"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, RuleLoneColon) {
|
||||
EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
|
||||
.Times(AtMost(1))
|
||||
.WillRepeatedly(Return());
|
||||
EXPECT_FALSE(parser.Parse(" r1: expr :"));
|
||||
}
|
||||
|
||||
TEST_F(Parser, RegNoExprRule) {
|
||||
EXPECT_CALL(mock_handler, RegisterRule("r1", "expr"))
|
||||
.Times(AtMost(1))
|
||||
.WillRepeatedly(Return());
|
||||
EXPECT_FALSE(parser.Parse("r0: r1: expr"));
|
||||
}
|
||||
|
||||
class ParseHandlerFixture: public CFIFixture {
|
||||
public:
|
||||
ParseHandlerFixture() : CFIFixture(), handler(&cfi) { }
|
||||
CFIFrameInfoParseHandler handler;
|
||||
};
|
||||
|
||||
class ParseHandler: public ParseHandlerFixture, public Test { };
|
||||
|
||||
TEST_F(ParseHandler, CFARARule) {
|
||||
handler.CFARule("reg-for-cfa");
|
||||
handler.RARule("reg-for-ra");
|
||||
registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
|
||||
registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
|
||||
ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
|
||||
}
|
||||
|
||||
TEST_F(ParseHandler, RegisterRules) {
|
||||
handler.CFARule("reg-for-cfa");
|
||||
handler.RARule("reg-for-ra");
|
||||
handler.RegisterRule("reg1", "reg-for-reg1");
|
||||
handler.RegisterRule("reg2", "reg-for-reg2");
|
||||
registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL;
|
||||
registers["reg-for-ra"] = 0x6301b475b8b91c02ULL;
|
||||
registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL;
|
||||
registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL;
|
||||
ASSERT_TRUE(cfi.FindCallerRegs<u_int64_t>(registers, memory,
|
||||
&caller_registers));
|
||||
ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]);
|
||||
ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]);
|
||||
ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers["reg1"]);
|
||||
ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers["reg2"]);
|
||||
}
|
||||
|
||||
struct SimpleCFIWalkerFixture {
|
||||
struct RawContext {
|
||||
u_int64_t r0, r1, r2, r3, r4, sp, pc;
|
||||
};
|
||||
enum Validity {
|
||||
R0_VALID = 0x01,
|
||||
R1_VALID = 0x02,
|
||||
R2_VALID = 0x04,
|
||||
R3_VALID = 0x08,
|
||||
R4_VALID = 0x10,
|
||||
SP_VALID = 0x20,
|
||||
PC_VALID = 0x40
|
||||
};
|
||||
typedef SimpleCFIWalker<u_int64_t, RawContext> CFIWalker;
|
||||
|
||||
SimpleCFIWalkerFixture()
|
||||
: walker(register_map,
|
||||
sizeof(register_map) / sizeof(register_map[0])) { }
|
||||
|
||||
static CFIWalker::RegisterSet register_map[7];
|
||||
CFIFrameInfo call_frame_info;
|
||||
CFIWalker walker;
|
||||
MockMemoryRegion memory;
|
||||
RawContext callee_context, caller_context;
|
||||
};
|
||||
|
||||
SimpleCFIWalkerFixture::CFIWalker::RegisterSet
|
||||
SimpleCFIWalkerFixture::register_map[7] = {
|
||||
{ "r0", NULL, true, R0_VALID, &RawContext::r0 },
|
||||
{ "r1", NULL, true, R1_VALID, &RawContext::r1 },
|
||||
{ "r2", NULL, false, R2_VALID, &RawContext::r2 },
|
||||
{ "r3", NULL, false, R3_VALID, &RawContext::r3 },
|
||||
{ "r4", NULL, true, R4_VALID, &RawContext::r4 },
|
||||
{ "sp", ".cfa", true, SP_VALID, &RawContext::sp },
|
||||
{ "pc", ".ra", true, PC_VALID, &RawContext::pc },
|
||||
};
|
||||
|
||||
class SimpleWalker: public SimpleCFIWalkerFixture, public Test { };
|
||||
|
||||
TEST_F(SimpleWalker, Walk) {
|
||||
// Stack_top is the current stack pointer, pointing to the lowest
|
||||
// address of a frame that looks like this (all 64-bit words):
|
||||
//
|
||||
// sp -> saved r0
|
||||
// garbage
|
||||
// return address
|
||||
// cfa ->
|
||||
//
|
||||
// r0 has been saved on the stack.
|
||||
// r1 has been saved in r2.
|
||||
// r2 and r3 are not recoverable.
|
||||
// r4 is not recoverable, even though it is a callee-saves register.
|
||||
// Some earlier frame's unwinder must have failed to recover it.
|
||||
|
||||
u_int64_t stack_top = 0x83254944b20d5512ULL;
|
||||
|
||||
// Saved r0.
|
||||
EXPECT_CALL(memory,
|
||||
GetMemoryAtAddress(stack_top, A<u_int64_t *>()))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0xdc1975eba8602302ULL),
|
||||
Return(true)));
|
||||
// Saved return address.
|
||||
EXPECT_CALL(memory,
|
||||
GetMemoryAtAddress(stack_top + 16, A<u_int64_t *>()))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<1>(0xba5ad6d9acce28deULL),
|
||||
Return(true)));
|
||||
|
||||
call_frame_info.SetCFARule("sp 24 +");
|
||||
call_frame_info.SetRARule(".cfa 8 - ^");
|
||||
call_frame_info.SetRegisterRule("r0", ".cfa 24 - ^");
|
||||
call_frame_info.SetRegisterRule("r1", "r2");
|
||||
|
||||
callee_context.r0 = 0x94e030ca79edd119ULL;
|
||||
callee_context.r1 = 0x937b4d7e95ce52d9ULL;
|
||||
callee_context.r2 = 0x5fe0027416b8b62aULL; // caller's r1
|
||||
// callee_context.r3 is not valid in callee.
|
||||
// callee_context.r4 is not valid in callee.
|
||||
callee_context.sp = stack_top;
|
||||
callee_context.pc = 0x25b21b224311d280ULL;
|
||||
int callee_validity = R0_VALID | R1_VALID | R2_VALID | SP_VALID | PC_VALID;
|
||||
|
||||
memset(&caller_context, 0, sizeof(caller_context));
|
||||
|
||||
int caller_validity;
|
||||
EXPECT_TRUE(walker.FindCallerRegisters(memory, call_frame_info,
|
||||
callee_context, callee_validity,
|
||||
&caller_context, &caller_validity));
|
||||
EXPECT_EQ(R0_VALID | R1_VALID | SP_VALID | PC_VALID, caller_validity);
|
||||
EXPECT_EQ(0xdc1975eba8602302ULL, caller_context.r0);
|
||||
EXPECT_EQ(0x5fe0027416b8b62aULL, caller_context.r1);
|
||||
EXPECT_EQ(stack_top + 24, caller_context.sp);
|
||||
EXPECT_EQ(0xba5ad6d9acce28deULL, caller_context.pc);
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -1063,7 +1063,7 @@ void MinidumpContext::Print() {
|
|||
for (unsigned int fpe_index = 0;
|
||||
fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT;
|
||||
++fpe_index) {
|
||||
printf(" float_save.extra[%2d] = 0x%" PRIx64 "\n",
|
||||
printf(" float_save.extra[%2d] = 0x%" PRIx32 "\n",
|
||||
fpe_index, context_arm->float_save.extra[fpe_index]);
|
||||
}
|
||||
|
||||
|
@ -1106,7 +1106,7 @@ void MinidumpMemoryRegion::SetDescriptor(MDMemoryDescriptor* descriptor) {
|
|||
}
|
||||
|
||||
|
||||
const u_int8_t* MinidumpMemoryRegion::GetMemory() {
|
||||
const u_int8_t* MinidumpMemoryRegion::GetMemory() const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory";
|
||||
return NULL;
|
||||
|
@ -1145,7 +1145,7 @@ const u_int8_t* MinidumpMemoryRegion::GetMemory() {
|
|||
}
|
||||
|
||||
|
||||
u_int64_t MinidumpMemoryRegion::GetBase() {
|
||||
u_int64_t MinidumpMemoryRegion::GetBase() const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase";
|
||||
return static_cast<u_int64_t>(-1);
|
||||
|
@ -1155,7 +1155,7 @@ u_int64_t MinidumpMemoryRegion::GetBase() {
|
|||
}
|
||||
|
||||
|
||||
u_int32_t MinidumpMemoryRegion::GetSize() {
|
||||
u_int32_t MinidumpMemoryRegion::GetSize() const {
|
||||
if (!valid_) {
|
||||
BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize";
|
||||
return 0;
|
||||
|
@ -1173,7 +1173,7 @@ void MinidumpMemoryRegion::FreeMemory() {
|
|||
|
||||
template<typename T>
|
||||
bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(u_int64_t address,
|
||||
T* value) {
|
||||
T* value) const {
|
||||
BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal "
|
||||
"requires |value|";
|
||||
assert(value);
|
||||
|
@ -1215,25 +1215,25 @@ bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(u_int64_t address,
|
|||
|
||||
|
||||
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
|
||||
u_int8_t* value) {
|
||||
u_int8_t* value) const {
|
||||
return GetMemoryAtAddressInternal(address, value);
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
|
||||
u_int16_t* value) {
|
||||
u_int16_t* value) const {
|
||||
return GetMemoryAtAddressInternal(address, value);
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
|
||||
u_int32_t* value) {
|
||||
u_int32_t* value) const {
|
||||
return GetMemoryAtAddressInternal(address, value);
|
||||
}
|
||||
|
||||
|
||||
bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address,
|
||||
u_int64_t* value) {
|
||||
u_int64_t* value) const {
|
||||
return GetMemoryAtAddressInternal(address, value);
|
||||
}
|
||||
|
||||
|
@ -3747,8 +3747,8 @@ bool Minidump::ReadBytes(void* bytes, size_t count) {
|
|||
}
|
||||
stream_->read(static_cast<char*>(bytes), count);
|
||||
size_t bytes_read = stream_->gcount();
|
||||
if (static_cast<size_t>(bytes_read) != count) {
|
||||
if (bytes_read == -1) {
|
||||
if (bytes_read != count) {
|
||||
if (bytes_read == size_t(-1)) {
|
||||
string error_string;
|
||||
int error_code = ErrnoString(&error_string);
|
||||
BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <string>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
|
@ -47,6 +48,8 @@
|
|||
#include "processor/logging.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
|
||||
using std::map;
|
||||
|
||||
namespace google_breakpad {
|
||||
class MockMinidump : public Minidump {
|
||||
public:
|
||||
|
@ -74,6 +77,10 @@ using google_breakpad::scoped_ptr;
|
|||
using google_breakpad::SymbolSupplier;
|
||||
using google_breakpad::SystemInfo;
|
||||
using std::string;
|
||||
using ::testing::_;
|
||||
using ::testing::Mock;
|
||||
using ::testing::Ne;
|
||||
using ::testing::Property;
|
||||
using ::testing::Return;
|
||||
|
||||
static const char *kSystemInfoOS = "Windows NT";
|
||||
|
@ -155,6 +162,19 @@ SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
|
|||
return s;
|
||||
}
|
||||
|
||||
// A mock symbol supplier that always returns NOT_FOUND; one current
|
||||
// use for testing the processor's caching of symbol lookups.
|
||||
class MockSymbolSupplier : public SymbolSupplier {
|
||||
public:
|
||||
MockSymbolSupplier() { }
|
||||
MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule*,
|
||||
const SystemInfo*,
|
||||
string*));
|
||||
MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule*,
|
||||
const SystemInfo*,
|
||||
string*,
|
||||
string*));
|
||||
};
|
||||
|
||||
class MinidumpProcessorTest : public ::testing::Test {
|
||||
|
||||
|
@ -186,6 +206,43 @@ TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) {
|
|||
google_breakpad::PROCESS_ERROR_NO_THREAD_LIST);
|
||||
}
|
||||
|
||||
// This test case verifies that the symbol supplier is only consulted
|
||||
// once per minidump per module.
|
||||
TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) {
|
||||
MockSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
MinidumpProcessor processor(&supplier, &resolver);
|
||||
|
||||
string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
|
||||
"/src/processor/testdata/minidump2.dmp";
|
||||
ProcessState state;
|
||||
EXPECT_CALL(supplier, GetSymbolFile(
|
||||
Property(&google_breakpad::CodeModule::code_file,
|
||||
"c:\\test_app.exe"),
|
||||
_, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
|
||||
EXPECT_CALL(supplier, GetSymbolFile(
|
||||
Property(&google_breakpad::CodeModule::code_file,
|
||||
Ne("c:\\test_app.exe")),
|
||||
_, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
|
||||
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
|
||||
ASSERT_TRUE(Mock::VerifyAndClearExpectations(&supplier));
|
||||
|
||||
// We need to verify that across minidumps, the processor will refetch
|
||||
// symbol files, even with the same symbol supplier.
|
||||
EXPECT_CALL(supplier, GetSymbolFile(
|
||||
Property(&google_breakpad::CodeModule::code_file,
|
||||
"c:\\test_app.exe"),
|
||||
_, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
|
||||
EXPECT_CALL(supplier, GetSymbolFile(
|
||||
Property(&google_breakpad::CodeModule::code_file,
|
||||
Ne("c:\\test_app.exe")),
|
||||
_, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
|
||||
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||
google_breakpad::PROCESS_OK);
|
||||
}
|
||||
|
||||
TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
|
||||
TestSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
|
@ -204,16 +261,16 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
|
|||
ASSERT_EQ(state.system_info()->cpu_info, kSystemInfoCPUInfo);
|
||||
ASSERT_TRUE(state.crashed());
|
||||
ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION");
|
||||
ASSERT_EQ(state.crash_address(), 0x45);
|
||||
ASSERT_EQ(state.threads()->size(), 1);
|
||||
ASSERT_EQ(state.crash_address(), 0x45U);
|
||||
ASSERT_EQ(state.threads()->size(), size_t(1));
|
||||
ASSERT_EQ(state.requesting_thread(), 0);
|
||||
|
||||
CallStack *stack = state.threads()->at(0);
|
||||
ASSERT_TRUE(stack);
|
||||
ASSERT_EQ(stack->frames()->size(), 4);
|
||||
ASSERT_EQ(stack->frames()->size(), 4U);
|
||||
|
||||
ASSERT_TRUE(stack->frames()->at(0)->module);
|
||||
ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000);
|
||||
ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000U);
|
||||
ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(0)->function_name,
|
||||
"`anonymous namespace'::CrashFunction");
|
||||
|
@ -221,7 +278,7 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
|
|||
ASSERT_EQ(stack->frames()->at(0)->source_line, 58);
|
||||
|
||||
ASSERT_TRUE(stack->frames()->at(1)->module);
|
||||
ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000);
|
||||
ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000U);
|
||||
ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
|
||||
ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
|
||||
|
@ -229,7 +286,7 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
|
|||
|
||||
// This comes from the CRT
|
||||
ASSERT_TRUE(stack->frames()->at(2)->module);
|
||||
ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000);
|
||||
ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000U);
|
||||
ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "c:\\test_app.exe");
|
||||
ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup");
|
||||
ASSERT_EQ(stack->frames()->at(2)->source_file_name,
|
||||
|
@ -238,14 +295,14 @@ TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
|
|||
|
||||
// No debug info available for kernel32.dll
|
||||
ASSERT_TRUE(stack->frames()->at(3)->module);
|
||||
ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000);
|
||||
ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000U);
|
||||
ASSERT_EQ(stack->frames()->at(3)->module->code_file(),
|
||||
"C:\\WINDOWS\\system32\\kernel32.dll");
|
||||
ASSERT_TRUE(stack->frames()->at(3)->function_name.empty());
|
||||
ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
|
||||
ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
|
||||
|
||||
ASSERT_EQ(state.modules()->module_count(), 13);
|
||||
ASSERT_EQ(state.modules()->module_count(), 13U);
|
||||
ASSERT_TRUE(state.modules()->GetMainModule());
|
||||
ASSERT_EQ(state.modules()->GetMainModule()->code_file(), "c:\\test_app.exe");
|
||||
ASSERT_FALSE(state.modules()->GetModuleForAddress(0));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -70,6 +70,7 @@ using google_breakpad::StackFramePPC;
|
|||
using google_breakpad::StackFrameSPARC;
|
||||
using google_breakpad::StackFrameX86;
|
||||
using google_breakpad::StackFrameAMD64;
|
||||
using google_breakpad::StackFrameARM;
|
||||
|
||||
// Separator character for machine readable output.
|
||||
static const char kOutputSeparator = '|';
|
||||
|
@ -162,6 +163,7 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
|||
}
|
||||
const char *trust_name;
|
||||
switch (frame_x86->trust) {
|
||||
default:
|
||||
case StackFrameX86::FRAME_TRUST_NONE:
|
||||
trust_name = "unknown";
|
||||
break;
|
||||
|
@ -194,6 +196,16 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
|||
const StackFrameAMD64 *frame_amd64 =
|
||||
reinterpret_cast<const StackFrameAMD64*>(frame);
|
||||
|
||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX)
|
||||
sequence = PrintRegister("rbx", frame_amd64->context.rbx, sequence);
|
||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12)
|
||||
sequence = PrintRegister("r12", frame_amd64->context.r12, sequence);
|
||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13)
|
||||
sequence = PrintRegister("r13", frame_amd64->context.r13, sequence);
|
||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14)
|
||||
sequence = PrintRegister("r14", frame_amd64->context.r14, sequence);
|
||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15)
|
||||
sequence = PrintRegister("r15", frame_amd64->context.r15, sequence);
|
||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP)
|
||||
sequence = PrintRegister("rip", frame_amd64->context.rip, sequence);
|
||||
if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP)
|
||||
|
@ -210,6 +222,35 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
|
|||
sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence);
|
||||
if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC)
|
||||
sequence = PrintRegister("pc", frame_sparc->context.pc, sequence);
|
||||
} else if (cpu == "arm") {
|
||||
const StackFrameARM *frame_arm =
|
||||
reinterpret_cast<const StackFrameARM*>(frame);
|
||||
|
||||
// General-purpose callee-saves registers.
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4)
|
||||
sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5)
|
||||
sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6)
|
||||
sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7)
|
||||
sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8)
|
||||
sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9)
|
||||
sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10)
|
||||
sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence);
|
||||
|
||||
// Registers with a dedicated or conventional purpose.
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP)
|
||||
sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)
|
||||
sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR)
|
||||
sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence);
|
||||
if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC)
|
||||
sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -30,20 +30,39 @@
|
|||
// Unit test for Minidump. Uses a pre-generated minidump and
|
||||
// verifies that certain streams are correct.
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/minidump.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/synth_minidump.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using google_breakpad::Minidump;
|
||||
using google_breakpad::MinidumpContext;
|
||||
using google_breakpad::MinidumpMemoryList;
|
||||
using google_breakpad::MinidumpMemoryRegion;
|
||||
using google_breakpad::MinidumpModule;
|
||||
using google_breakpad::MinidumpModuleList;
|
||||
using google_breakpad::MinidumpSystemInfo;
|
||||
using google_breakpad::MinidumpThread;
|
||||
using google_breakpad::MinidumpThreadList;
|
||||
using google_breakpad::SynthMinidump::Context;
|
||||
using google_breakpad::SynthMinidump::Dump;
|
||||
using google_breakpad::SynthMinidump::Memory;
|
||||
using google_breakpad::SynthMinidump::Module;
|
||||
using google_breakpad::SynthMinidump::Stream;
|
||||
using google_breakpad::SynthMinidump::String;
|
||||
using google_breakpad::SynthMinidump::SystemInfo;
|
||||
using google_breakpad::SynthMinidump::Thread;
|
||||
using google_breakpad::TestAssembler::kBigEndian;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using std::ifstream;
|
||||
using std::istringstream;
|
||||
using std::string;
|
||||
|
@ -65,7 +84,7 @@ TEST_F(MinidumpTest, TestMinidumpFromFile) {
|
|||
ASSERT_TRUE(minidump.Read());
|
||||
const MDRawHeader* header = minidump.header();
|
||||
ASSERT_NE(header, (MDRawHeader*)NULL);
|
||||
ASSERT_EQ(header->signature, MD_HEADER_SIGNATURE);
|
||||
ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
|
||||
//TODO: add more checks here
|
||||
}
|
||||
|
||||
|
@ -91,13 +110,425 @@ TEST_F(MinidumpTest, TestMinidumpFromStream) {
|
|||
ASSERT_TRUE(minidump.Read());
|
||||
const MDRawHeader* header = minidump.header();
|
||||
ASSERT_NE(header, (MDRawHeader*)NULL);
|
||||
ASSERT_EQ(header->signature, MD_HEADER_SIGNATURE);
|
||||
ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE));
|
||||
//TODO: add more checks here
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
TEST(Dump, ReadBackEmpty) {
|
||||
Dump dump(0);
|
||||
dump.Finish();
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
istringstream stream(contents);
|
||||
Minidump minidump(stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
|
||||
}
|
||||
|
||||
TEST(Dump, ReadBackEmptyBigEndian) {
|
||||
Dump big_minidump(0, kBigEndian);
|
||||
big_minidump.Finish();
|
||||
string contents;
|
||||
ASSERT_TRUE(big_minidump.GetContents(&contents));
|
||||
istringstream stream(contents);
|
||||
Minidump minidump(stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(0U, minidump.GetDirectoryEntryCount());
|
||||
}
|
||||
|
||||
TEST(Dump, OneStream) {
|
||||
Dump dump(0, kBigEndian);
|
||||
Stream stream(dump, 0xfbb7fa2bU);
|
||||
stream.Append("stream contents");
|
||||
dump.Add(&stream);
|
||||
dump.Finish();
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
|
||||
ASSERT_TRUE(dir != NULL);
|
||||
EXPECT_EQ(0xfbb7fa2bU, dir->stream_type);
|
||||
|
||||
u_int32_t stream_length;
|
||||
ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length));
|
||||
ASSERT_EQ(15U, stream_length);
|
||||
char stream_contents[15];
|
||||
ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents)));
|
||||
EXPECT_EQ(string("stream contents"),
|
||||
string(stream_contents, sizeof(stream_contents)));
|
||||
|
||||
EXPECT_FALSE(minidump.GetThreadList());
|
||||
EXPECT_FALSE(minidump.GetModuleList());
|
||||
EXPECT_FALSE(minidump.GetMemoryList());
|
||||
EXPECT_FALSE(minidump.GetException());
|
||||
EXPECT_FALSE(minidump.GetAssertion());
|
||||
EXPECT_FALSE(minidump.GetSystemInfo());
|
||||
EXPECT_FALSE(minidump.GetMiscInfo());
|
||||
EXPECT_FALSE(minidump.GetBreakpadInfo());
|
||||
}
|
||||
|
||||
TEST(Dump, OneMemory) {
|
||||
Dump dump(0, kBigEndian);
|
||||
Memory memory(dump, 0x309d68010bd21b2cULL);
|
||||
memory.Append("memory contents");
|
||||
dump.Add(&memory);
|
||||
dump.Finish();
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
|
||||
ASSERT_TRUE(dir != NULL);
|
||||
EXPECT_EQ((u_int32_t) MD_MEMORY_LIST_STREAM, dir->stream_type);
|
||||
|
||||
MinidumpMemoryList *memory_list = minidump.GetMemoryList();
|
||||
ASSERT_TRUE(memory_list != NULL);
|
||||
ASSERT_EQ(1U, memory_list->region_count());
|
||||
|
||||
MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0);
|
||||
ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase());
|
||||
ASSERT_EQ(15U, region1->GetSize());
|
||||
const u_int8_t *region1_bytes = region1->GetMemory();
|
||||
ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0);
|
||||
}
|
||||
|
||||
// One thread --- and its requisite entourage.
|
||||
TEST(Dump, OneThread) {
|
||||
Dump dump(0, kLittleEndian);
|
||||
Memory stack(dump, 0x2326a0fa);
|
||||
stack.Append("stack for thread");
|
||||
|
||||
MDRawContextX86 raw_context;
|
||||
raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL;
|
||||
raw_context.edi = 0x3ecba80d;
|
||||
raw_context.esi = 0x382583b9;
|
||||
raw_context.ebx = 0x7fccc03f;
|
||||
raw_context.edx = 0xf62f8ec2;
|
||||
raw_context.ecx = 0x46a6a6a8;
|
||||
raw_context.eax = 0x6a5025e2;
|
||||
raw_context.ebp = 0xd9fabb4a;
|
||||
raw_context.eip = 0x6913f540;
|
||||
raw_context.cs = 0xbffe6eda;
|
||||
raw_context.eflags = 0xb2ce1e2d;
|
||||
raw_context.esp = 0x659caaa4;
|
||||
raw_context.ss = 0x2e951ef7;
|
||||
Context context(dump, raw_context);
|
||||
|
||||
Thread thread(dump, 0xa898f11b, stack, context,
|
||||
0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL);
|
||||
|
||||
dump.Add(&stack);
|
||||
dump.Add(&context);
|
||||
dump.Add(&thread);
|
||||
dump.Finish();
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
|
||||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(2U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
MinidumpMemoryList *md_memory_list = minidump.GetMemoryList();
|
||||
ASSERT_TRUE(md_memory_list != NULL);
|
||||
ASSERT_EQ(1U, md_memory_list->region_count());
|
||||
|
||||
MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0);
|
||||
ASSERT_EQ(0x2326a0faU, md_region->GetBase());
|
||||
ASSERT_EQ(16U, md_region->GetSize());
|
||||
const u_int8_t *region_bytes = md_region->GetMemory();
|
||||
ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0);
|
||||
|
||||
MinidumpThreadList *thread_list = minidump.GetThreadList();
|
||||
ASSERT_TRUE(thread_list != NULL);
|
||||
ASSERT_EQ(1U, thread_list->thread_count());
|
||||
|
||||
MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0);
|
||||
ASSERT_TRUE(md_thread != NULL);
|
||||
u_int32_t thread_id;
|
||||
ASSERT_TRUE(md_thread->GetThreadID(&thread_id));
|
||||
ASSERT_EQ(0xa898f11bU, thread_id);
|
||||
MinidumpMemoryRegion *md_stack = md_thread->GetMemory();
|
||||
ASSERT_TRUE(md_stack != NULL);
|
||||
ASSERT_EQ(0x2326a0faU, md_stack->GetBase());
|
||||
ASSERT_EQ(16U, md_stack->GetSize());
|
||||
const u_int8_t *md_stack_bytes = md_stack->GetMemory();
|
||||
ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0);
|
||||
|
||||
MinidumpContext *md_context = md_thread->GetContext();
|
||||
ASSERT_TRUE(md_context != NULL);
|
||||
ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU());
|
||||
const MDRawContextX86 *md_raw_context = md_context->GetContextX86();
|
||||
ASSERT_TRUE(md_raw_context != NULL);
|
||||
ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL),
|
||||
(md_raw_context->context_flags
|
||||
& (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL)));
|
||||
EXPECT_EQ(0x3ecba80dU, raw_context.edi);
|
||||
EXPECT_EQ(0x382583b9U, raw_context.esi);
|
||||
EXPECT_EQ(0x7fccc03fU, raw_context.ebx);
|
||||
EXPECT_EQ(0xf62f8ec2U, raw_context.edx);
|
||||
EXPECT_EQ(0x46a6a6a8U, raw_context.ecx);
|
||||
EXPECT_EQ(0x6a5025e2U, raw_context.eax);
|
||||
EXPECT_EQ(0xd9fabb4aU, raw_context.ebp);
|
||||
EXPECT_EQ(0x6913f540U, raw_context.eip);
|
||||
EXPECT_EQ(0xbffe6edaU, raw_context.cs);
|
||||
EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags);
|
||||
EXPECT_EQ(0x659caaa4U, raw_context.esp);
|
||||
EXPECT_EQ(0x2e951ef7U, raw_context.ss);
|
||||
}
|
||||
|
||||
TEST(Dump, OneModule) {
|
||||
static const MDVSFixedFileInfo fixed_file_info = {
|
||||
0xb2fba33a, // signature
|
||||
0x33d7a728, // struct_version
|
||||
0x31afcb20, // file_version_hi
|
||||
0xe51cdab1, // file_version_lo
|
||||
0xd1ea6907, // product_version_hi
|
||||
0x03032857, // product_version_lo
|
||||
0x11bf71d7, // file_flags_mask
|
||||
0x5fb8cdbf, // file_flags
|
||||
0xe45d0d5d, // file_os
|
||||
0x107d9562, // file_type
|
||||
0x5a8844d4, // file_subtype
|
||||
0xa8d30b20, // file_date_hi
|
||||
0x651c3e4e // file_date_lo
|
||||
};
|
||||
|
||||
Dump dump(0, kBigEndian);
|
||||
String module_name(dump, "single module");
|
||||
Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd,
|
||||
module_name,
|
||||
0xb1054d2a,
|
||||
0x34571371,
|
||||
fixed_file_info, // from synth_minidump_unittest_data.h
|
||||
NULL, NULL);
|
||||
|
||||
dump.Add(&module);
|
||||
dump.Add(&module_name);
|
||||
dump.Finish();
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
|
||||
ASSERT_TRUE(dir != NULL);
|
||||
EXPECT_EQ((u_int32_t) MD_MODULE_LIST_STREAM, dir->stream_type);
|
||||
|
||||
MinidumpModuleList *md_module_list = minidump.GetModuleList();
|
||||
ASSERT_TRUE(md_module_list != NULL);
|
||||
ASSERT_EQ(1U, md_module_list->module_count());
|
||||
|
||||
const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0);
|
||||
ASSERT_TRUE(md_module != NULL);
|
||||
ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address());
|
||||
ASSERT_EQ(0xada542bd, md_module->size());
|
||||
ASSERT_EQ("single module", md_module->code_file());
|
||||
|
||||
const MDRawModule *md_raw_module = md_module->module();
|
||||
ASSERT_TRUE(md_raw_module != NULL);
|
||||
ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp);
|
||||
ASSERT_EQ(0x34571371U, md_raw_module->checksum);
|
||||
ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info,
|
||||
sizeof(fixed_file_info)) == 0);
|
||||
}
|
||||
|
||||
TEST(Dump, OneSystemInfo) {
|
||||
Dump dump(0, kLittleEndian);
|
||||
String csd_version(dump, "Petulant Pierogi");
|
||||
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
|
||||
|
||||
dump.Add(&system_info);
|
||||
dump.Add(&csd_version);
|
||||
dump.Finish();
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(1U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0);
|
||||
ASSERT_TRUE(dir != NULL);
|
||||
EXPECT_EQ((u_int32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type);
|
||||
|
||||
MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo();
|
||||
ASSERT_TRUE(md_system_info != NULL);
|
||||
ASSERT_EQ("windows", md_system_info->GetOS());
|
||||
ASSERT_EQ("x86", md_system_info->GetCPU());
|
||||
ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion());
|
||||
ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor());
|
||||
}
|
||||
|
||||
TEST(Dump, BigDump) {
|
||||
Dump dump(0, kLittleEndian);
|
||||
|
||||
// A SystemInfo stream.
|
||||
String csd_version(dump, "Munificent Macaque");
|
||||
SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version);
|
||||
dump.Add(&csd_version);
|
||||
dump.Add(&system_info);
|
||||
|
||||
// Five threads!
|
||||
Memory stack0(dump, 0x70b9ebfc);
|
||||
stack0.Append("stack for thread zero");
|
||||
MDRawContextX86 raw_context0;
|
||||
raw_context0.context_flags = MD_CONTEXT_X86_INTEGER;
|
||||
raw_context0.eip = 0xaf0709e4;
|
||||
Context context0(dump, raw_context0);
|
||||
Thread thread0(dump, 0xbbef4432, stack0, context0,
|
||||
0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL);
|
||||
dump.Add(&stack0);
|
||||
dump.Add(&context0);
|
||||
dump.Add(&thread0);
|
||||
|
||||
Memory stack1(dump, 0xf988cc45);
|
||||
stack1.Append("stack for thread one");
|
||||
MDRawContextX86 raw_context1;
|
||||
raw_context1.context_flags = MD_CONTEXT_X86_INTEGER;
|
||||
raw_context1.eip = 0xe4f56f81;
|
||||
Context context1(dump, raw_context1);
|
||||
Thread thread1(dump, 0x657c3f58, stack1, context1,
|
||||
0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL);
|
||||
dump.Add(&stack1);
|
||||
dump.Add(&context1);
|
||||
dump.Add(&thread1);
|
||||
|
||||
Memory stack2(dump, 0xc8a92e7c);
|
||||
stack2.Append("stack for thread two");
|
||||
MDRawContextX86 raw_context2;
|
||||
raw_context2.context_flags = MD_CONTEXT_X86_INTEGER;
|
||||
raw_context2.eip = 0xb336a438;
|
||||
Context context2(dump, raw_context2);
|
||||
Thread thread2(dump, 0xdf4b8a71, stack2, context2,
|
||||
0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL);
|
||||
dump.Add(&stack2);
|
||||
dump.Add(&context2);
|
||||
dump.Add(&thread2);
|
||||
|
||||
Memory stack3(dump, 0x36d08e08);
|
||||
stack3.Append("stack for thread three");
|
||||
MDRawContextX86 raw_context3;
|
||||
raw_context3.context_flags = MD_CONTEXT_X86_INTEGER;
|
||||
raw_context3.eip = 0xdf99a60c;
|
||||
Context context3(dump, raw_context3);
|
||||
Thread thread3(dump, 0x86e6c341, stack3, context3,
|
||||
0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL);
|
||||
dump.Add(&stack3);
|
||||
dump.Add(&context3);
|
||||
dump.Add(&thread3);
|
||||
|
||||
Memory stack4(dump, 0x1e0ab4fa);
|
||||
stack4.Append("stack for thread four");
|
||||
MDRawContextX86 raw_context4;
|
||||
raw_context4.context_flags = MD_CONTEXT_X86_INTEGER;
|
||||
raw_context4.eip = 0xaa646267;
|
||||
Context context4(dump, raw_context4);
|
||||
Thread thread4(dump, 0x261a28d4, stack4, context4,
|
||||
0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL);
|
||||
dump.Add(&stack4);
|
||||
dump.Add(&context4);
|
||||
dump.Add(&thread4);
|
||||
|
||||
// Three modules!
|
||||
String module1_name(dump, "module one");
|
||||
Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name);
|
||||
dump.Add(&module1_name);
|
||||
dump.Add(&module1);
|
||||
|
||||
String module2_name(dump, "module two");
|
||||
Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name);
|
||||
dump.Add(&module2_name);
|
||||
dump.Add(&module2);
|
||||
|
||||
String module3_name(dump, "module three");
|
||||
Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name);
|
||||
dump.Add(&module3_name);
|
||||
dump.Add(&module3);
|
||||
|
||||
// Add one more memory region, on top of the five stacks.
|
||||
Memory memory5(dump, 0x61979e828040e564ULL);
|
||||
memory5.Append("contents of memory 5");
|
||||
dump.Add(&memory5);
|
||||
|
||||
dump.Finish();
|
||||
|
||||
string contents;
|
||||
ASSERT_TRUE(dump.GetContents(&contents));
|
||||
istringstream minidump_stream(contents);
|
||||
Minidump minidump(minidump_stream);
|
||||
ASSERT_TRUE(minidump.Read());
|
||||
ASSERT_EQ(4U, minidump.GetDirectoryEntryCount());
|
||||
|
||||
// Check the threads.
|
||||
MinidumpThreadList *thread_list = minidump.GetThreadList();
|
||||
ASSERT_TRUE(thread_list != NULL);
|
||||
ASSERT_EQ(5U, thread_list->thread_count());
|
||||
u_int32_t thread_id;
|
||||
ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id));
|
||||
ASSERT_EQ(0xbbef4432U, thread_id);
|
||||
ASSERT_EQ(0x70b9ebfcU,
|
||||
thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase());
|
||||
ASSERT_EQ(0xaf0709e4U,
|
||||
thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86()
|
||||
->eip);
|
||||
|
||||
ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id));
|
||||
ASSERT_EQ(0x657c3f58U, thread_id);
|
||||
ASSERT_EQ(0xf988cc45U,
|
||||
thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase());
|
||||
ASSERT_EQ(0xe4f56f81U,
|
||||
thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86()
|
||||
->eip);
|
||||
|
||||
ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id));
|
||||
ASSERT_EQ(0xdf4b8a71U, thread_id);
|
||||
ASSERT_EQ(0xc8a92e7cU,
|
||||
thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase());
|
||||
ASSERT_EQ(0xb336a438U,
|
||||
thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86()
|
||||
->eip);
|
||||
|
||||
ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id));
|
||||
ASSERT_EQ(0x86e6c341U, thread_id);
|
||||
ASSERT_EQ(0x36d08e08U,
|
||||
thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase());
|
||||
ASSERT_EQ(0xdf99a60cU,
|
||||
thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86()
|
||||
->eip);
|
||||
|
||||
ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id));
|
||||
ASSERT_EQ(0x261a28d4U, thread_id);
|
||||
ASSERT_EQ(0x1e0ab4faU,
|
||||
thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase());
|
||||
ASSERT_EQ(0xaa646267U,
|
||||
thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86()
|
||||
->eip);
|
||||
|
||||
// Check the modules.
|
||||
MinidumpModuleList *md_module_list = minidump.GetModuleList();
|
||||
ASSERT_TRUE(md_module_list != NULL);
|
||||
ASSERT_EQ(3U, md_module_list->module_count());
|
||||
EXPECT_EQ(0xeb77da57b5d4cbdaULL,
|
||||
md_module_list->GetModuleAtIndex(0)->base_address());
|
||||
EXPECT_EQ(0x8675884adfe5ac90ULL,
|
||||
md_module_list->GetModuleAtIndex(1)->base_address());
|
||||
EXPECT_EQ(0x95fc1544da321b6cULL,
|
||||
md_module_list->GetModuleAtIndex(2)->base_address());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// -*- mode: c++ -*-
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -64,11 +66,9 @@ class AutoStackClearer {
|
|||
|
||||
|
||||
template<typename ValueType>
|
||||
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
|
||||
DictionaryValidityType *assigned) {
|
||||
// Ensure that the stack is cleared before returning.
|
||||
AutoStackClearer clearer(&stack_);
|
||||
|
||||
bool PostfixEvaluator<ValueType>::EvaluateInternal(
|
||||
const string &expression,
|
||||
DictionaryValidityType *assigned) {
|
||||
// Tokenize, splitting on whitespace.
|
||||
istringstream stream(expression);
|
||||
string token;
|
||||
|
@ -194,13 +194,46 @@ bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
|
|||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
bool PostfixEvaluator<ValueType>::Evaluate(const string &expression,
|
||||
DictionaryValidityType *assigned) {
|
||||
// Ensure that the stack is cleared before returning.
|
||||
AutoStackClearer clearer(&stack_);
|
||||
|
||||
if (!EvaluateInternal(expression, assigned))
|
||||
return false;
|
||||
|
||||
// If there's anything left on the stack, it indicates incomplete execution.
|
||||
// This is a failure case. If the stack is empty, evalution was complete
|
||||
// and successful.
|
||||
BPLOG_IF(ERROR, !stack_.empty()) << "Incomplete execution: " << expression;
|
||||
return stack_.empty();
|
||||
if (stack_.empty())
|
||||
return true;
|
||||
|
||||
BPLOG(ERROR) << "Incomplete execution: " << expression;
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
bool PostfixEvaluator<ValueType>::EvaluateForValue(const string &expression,
|
||||
ValueType *result) {
|
||||
// Ensure that the stack is cleared before returning.
|
||||
AutoStackClearer clearer(&stack_);
|
||||
|
||||
if (!EvaluateInternal(expression, NULL))
|
||||
return false;
|
||||
|
||||
// A successful execution should leave exactly one value on the stack.
|
||||
if (stack_.size() != 1) {
|
||||
BPLOG(ERROR) << "Expression yielded bad number of results: "
|
||||
<< "'" << expression << "'";
|
||||
return false;
|
||||
}
|
||||
|
||||
return PopValue(result);
|
||||
}
|
||||
|
||||
template<typename ValueType>
|
||||
typename PostfixEvaluator<ValueType>::PopResult
|
||||
|
@ -213,16 +246,30 @@ PostfixEvaluator<ValueType>::PopValueOrIdentifier(
|
|||
string token = stack_.back();
|
||||
stack_.pop_back();
|
||||
|
||||
// First, try to treat the value as a literal. In order for this to
|
||||
// succed, the entire string must be parseable as ValueType. If this
|
||||
// isn't possible, it can't be a literal, so treat it as an identifier
|
||||
// instead.
|
||||
// First, try to treat the value as a literal. Literals may have leading
|
||||
// '-' sign, and the entire remaining string must be parseable as
|
||||
// ValueType. If this isn't possible, it can't be a literal, so treat it
|
||||
// as an identifier instead.
|
||||
//
|
||||
// Some versions of the libstdc++, the GNU standard C++ library, have
|
||||
// stream extractors for unsigned integer values that permit a leading
|
||||
// '-' sign (6.0.13); others do not (6.0.9). Since we require it, we
|
||||
// handle it explicitly here.
|
||||
istringstream token_stream(token);
|
||||
ValueType literal;
|
||||
bool negative;
|
||||
if (token_stream.peek() == '-') {
|
||||
negative = true;
|
||||
token_stream.get();
|
||||
} else {
|
||||
negative = false;
|
||||
}
|
||||
if (token_stream >> literal && token_stream.peek() == EOF) {
|
||||
if (value) {
|
||||
*value = literal;
|
||||
}
|
||||
if (negative)
|
||||
*value = -*value;
|
||||
return POP_RESULT_VALUE;
|
||||
} else {
|
||||
if (identifier) {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -90,18 +92,24 @@ class PostfixEvaluator {
|
|||
// (^) will not be supported. |dictionary| may be NULL, but evaluation
|
||||
// will fail in that case unless set_dictionary is used before calling
|
||||
// Evaluate.
|
||||
PostfixEvaluator(DictionaryType *dictionary, MemoryRegion *memory)
|
||||
PostfixEvaluator(DictionaryType *dictionary, const MemoryRegion *memory)
|
||||
: dictionary_(dictionary), memory_(memory), stack_() {}
|
||||
|
||||
// Evaluate the expression. The results of execution will be stored
|
||||
// in one (or more) variables in the dictionary. Returns false if any
|
||||
// failures occure during execution, leaving variables in the dictionary
|
||||
// in an indeterminate state. If assigned is non-NULL, any keys set in
|
||||
// the dictionary as a result of evaluation will also be set to true in
|
||||
// assigned, providing a way to determine if an expression modifies any
|
||||
// of its input variables.
|
||||
// Evaluate the expression, starting with an empty stack. The results of
|
||||
// execution will be stored in one (or more) variables in the dictionary.
|
||||
// Returns false if any failures occur during execution, leaving
|
||||
// variables in the dictionary in an indeterminate state. If assigned is
|
||||
// non-NULL, any keys set in the dictionary as a result of evaluation
|
||||
// will also be set to true in assigned, providing a way to determine if
|
||||
// an expression modifies any of its input variables.
|
||||
bool Evaluate(const string &expression, DictionaryValidityType *assigned);
|
||||
|
||||
// Like Evaluate, but provides the value left on the stack to the
|
||||
// caller. If evaluation succeeds and leaves exactly one value on
|
||||
// the stack, pop that value, store it in *result, and return true.
|
||||
// Otherwise, return false.
|
||||
bool EvaluateForValue(const string &expression, ValueType *result);
|
||||
|
||||
DictionaryType* dictionary() const { return dictionary_; }
|
||||
|
||||
// Reset the dictionary. PostfixEvaluator does not take ownership.
|
||||
|
@ -137,6 +145,12 @@ class PostfixEvaluator {
|
|||
// Pushes a new value onto the stack.
|
||||
void PushValue(const ValueType &value);
|
||||
|
||||
// Evaluate expression, updating *assigned if it is non-zero. Return
|
||||
// true if evaluation completes successfully. Do not clear the stack
|
||||
// upon successful evaluation.
|
||||
bool EvaluateInternal(const string &expression,
|
||||
DictionaryValidityType *assigned);
|
||||
|
||||
// The dictionary mapping constant and variable identifiers (strings) to
|
||||
// values. Keys beginning with '$' are treated as variable names, and
|
||||
// PostfixEvaluator is free to create and modify these keys. Weak pointer.
|
||||
|
@ -144,7 +158,7 @@ class PostfixEvaluator {
|
|||
|
||||
// If non-NULL, the MemoryRegion used for dereference (^) operations.
|
||||
// If NULL, dereferencing is unsupported and will fail. Weak pointer.
|
||||
MemoryRegion *memory_;
|
||||
const MemoryRegion *memory_;
|
||||
|
||||
// The stack contains state information as execution progresses. Values
|
||||
// are pushed on to it as the expression string is read and as operations
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -56,21 +56,21 @@ using google_breakpad::PostfixEvaluator;
|
|||
// the value.
|
||||
class FakeMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
virtual u_int64_t GetBase() { return 0; }
|
||||
virtual u_int32_t GetSize() { return 0; }
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) {
|
||||
virtual u_int64_t GetBase() const { return 0; }
|
||||
virtual u_int32_t GetSize() const { return 0; }
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) {
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) {
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) {
|
||||
virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
|
||||
*value = address + 1;
|
||||
return true;
|
||||
}
|
||||
|
@ -103,6 +103,18 @@ struct EvaluateTestSet {
|
|||
};
|
||||
|
||||
|
||||
struct EvaluateForValueTest {
|
||||
// Expression passed to PostfixEvaluator::Evaluate.
|
||||
const string expression;
|
||||
|
||||
// True if the expression is expected to be evaluable, false if evaluation
|
||||
// is expected to fail.
|
||||
bool evaluable;
|
||||
|
||||
// If evaluable, the value we expect it to yield.
|
||||
unsigned int value;
|
||||
};
|
||||
|
||||
static bool RunTests() {
|
||||
// The first test set checks the basic operations and failure modes.
|
||||
PostfixEvaluator<unsigned int>::DictionaryType dictionary_0;
|
||||
|
@ -289,6 +301,84 @@ static bool RunTests() {
|
|||
}
|
||||
}
|
||||
|
||||
// EvaluateForValue tests.
|
||||
PostfixEvaluator<unsigned int>::DictionaryType dictionary_2;
|
||||
dictionary_2["$ebp"] = 0xbfff0010;
|
||||
dictionary_2["$eip"] = 0x10000000;
|
||||
dictionary_2["$esp"] = 0xbfff0000;
|
||||
dictionary_2[".cbSavedRegs"] = 4;
|
||||
dictionary_2[".cbParams"] = 4;
|
||||
dictionary_2[".raSearchStart"] = 0xbfff0020;
|
||||
const EvaluateForValueTest evaluate_for_value_tests_2[] = {
|
||||
{ "28907223", true, 28907223 }, // simple constant
|
||||
{ "89854293 40010015 +", true, 89854293 + 40010015 }, // arithmetic
|
||||
{ "-870245 8769343 +", true, 7899098 }, // negative constants
|
||||
{ "$ebp $esp - $eip +", true, 0x10000010 }, // variable references
|
||||
{ "18929794 34015074", false, 0 }, // too many values
|
||||
{ "$ebp $ebp 4 - =", false, 0 }, // too few values
|
||||
{ "$new $eip = $new", true, 0x10000000 }, // make new variable
|
||||
{ "$new 4 +", true, 0x10000004 }, // see prior assignments
|
||||
{ ".cfa 42 = 10", false, 0 } // can't set constants
|
||||
};
|
||||
const int evaluate_for_value_tests_2_size
|
||||
= (sizeof (evaluate_for_value_tests_2)
|
||||
/ sizeof (evaluate_for_value_tests_2[0]));
|
||||
map<string, unsigned int> validate_data_2;
|
||||
validate_data_2["$eip"] = 0x10000000;
|
||||
validate_data_2["$ebp"] = 0xbfff000c;
|
||||
validate_data_2["$esp"] = 0xbfff0000;
|
||||
validate_data_2["$new"] = 0x10000000;
|
||||
validate_data_2[".cbSavedRegs"] = 4;
|
||||
validate_data_2[".cbParams"] = 4;
|
||||
validate_data_2[".raSearchStart"] = 0xbfff0020;
|
||||
|
||||
postfix_evaluator.set_dictionary(&dictionary_2);
|
||||
for (int i = 0; i < evaluate_for_value_tests_2_size; i++) {
|
||||
const EvaluateForValueTest *test = &evaluate_for_value_tests_2[i];
|
||||
unsigned int result;
|
||||
if (postfix_evaluator.EvaluateForValue(test->expression, &result)
|
||||
!= test->evaluable) {
|
||||
fprintf(stderr, "FAIL: evaluate for value test %d, "
|
||||
"expected evaluation to %s, but it %s\n",
|
||||
i, test->evaluable ? "succeed" : "fail",
|
||||
test->evaluable ? "failed" : "succeeded");
|
||||
return false;
|
||||
}
|
||||
if (test->evaluable && result != test->value) {
|
||||
fprintf(stderr, "FAIL: evaluate for value test %d, "
|
||||
"expected value to be 0x%x, but it was 0x%x\n",
|
||||
i, test->value, result);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (map<string, unsigned int>::iterator v = validate_data_2.begin();
|
||||
v != validate_data_2.end(); v++) {
|
||||
map<string, unsigned int>::iterator a = dictionary_2.find(v->first);
|
||||
if (a == dictionary_2.end()) {
|
||||
fprintf(stderr, "FAIL: evaluate for value dictionary check: "
|
||||
"expected dict[\"%s\"] to be 0x%x, but it was unset\n",
|
||||
v->first.c_str(), v->second);
|
||||
return false;
|
||||
} else if (a->second != v->second) {
|
||||
fprintf(stderr, "FAIL: evaluate for value dictionary check: "
|
||||
"expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n",
|
||||
v->first.c_str(), v->second, a->second);
|
||||
return false;
|
||||
}
|
||||
dictionary_2.erase(a);
|
||||
}
|
||||
|
||||
map<string, unsigned int>::iterator remaining = dictionary_2.begin();
|
||||
if (remaining != dictionary_2.end()) {
|
||||
fprintf(stderr, "FAIL: evaluation of test expressions put unexpected "
|
||||
"values in dictionary:\n");
|
||||
for (; remaining != dictionary_2.end(); remaining++)
|
||||
fprintf(stderr, " dict[\"%s\"] == 0x%x\n",
|
||||
remaining->first.c_str(), remaining->second);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -156,7 +156,7 @@ bool RangeMap<AddressType, EntryType>::RetrieveNearestRange(
|
|||
|
||||
*entry = iterator->second.entry();
|
||||
if (entry_base)
|
||||
*entry_base = iterator->first;
|
||||
*entry_base = iterator->second.base();
|
||||
if (entry_size)
|
||||
*entry_size = iterator->first - iterator->second.base() + 1;
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -210,10 +210,11 @@ static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
|
|||
|
||||
linked_ptr<CountedObject> nearest_object;
|
||||
AddressType nearest_base;
|
||||
AddressType nearest_size;
|
||||
bool retrieved_nearest = range_map->RetrieveNearestRange(address,
|
||||
&nearest_object,
|
||||
&nearest_base,
|
||||
NULL);
|
||||
&nearest_size);
|
||||
|
||||
// When checking one greater than the high side, RetrieveNearestRange
|
||||
// should usually return the test range. When a different range begins
|
||||
|
@ -237,6 +238,22 @@ static bool RetrieveTest(TestMap *range_map, const RangeTest *range_test) {
|
|||
observed_nearest ? "true" : "false");
|
||||
return false;
|
||||
}
|
||||
|
||||
// If a range was successfully retrieved, check that the returned
|
||||
// bounds match the range as stored.
|
||||
if (expected_nearest &&
|
||||
(nearest_base != range_test->address ||
|
||||
nearest_size != range_test->size)) {
|
||||
fprintf(stderr, "FAILED: "
|
||||
"RetrieveNearestRange id %d, side %d, offset %d, "
|
||||
"expected base/size %d/%d, observed %d/%d\n",
|
||||
range_test->id,
|
||||
side,
|
||||
offset,
|
||||
range_test->address, range_test->size,
|
||||
nearest_base, nearest_size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -47,7 +47,6 @@
|
|||
#include "processor/linked_ptr.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stack_frame_info.h"
|
||||
#include "processor/stackwalker_ppc.h"
|
||||
#include "processor/stackwalker_sparc.h"
|
||||
#include "processor/stackwalker_x86.h"
|
||||
|
@ -65,8 +64,8 @@ Stackwalker::Stackwalker(const SystemInfo *system_info,
|
|||
: system_info_(system_info),
|
||||
memory_(memory),
|
||||
modules_(modules),
|
||||
supplier_(supplier),
|
||||
resolver_(resolver) {
|
||||
resolver_(resolver),
|
||||
supplier_(supplier) {
|
||||
}
|
||||
|
||||
|
||||
|
@ -75,11 +74,6 @@ bool Stackwalker::Walk(CallStack *stack) {
|
|||
assert(stack);
|
||||
stack->Clear();
|
||||
|
||||
// stack_frame_info parallels the CallStack. The vector is passed to the
|
||||
// GetCallerFrame function. It contains information that may be helpful
|
||||
// for stackwalking.
|
||||
vector< linked_ptr<StackFrameInfo> > stack_frame_info;
|
||||
|
||||
// Begin with the context frame, and keep getting callers until there are
|
||||
// no more.
|
||||
|
||||
|
@ -91,8 +85,6 @@ bool Stackwalker::Walk(CallStack *stack) {
|
|||
// frame_pointer fields. The frame structure comes from either the
|
||||
// context frame (above) or a caller frame (below).
|
||||
|
||||
linked_ptr<StackFrameInfo> frame_info;
|
||||
|
||||
// Resolve the module information, if a module map was provided.
|
||||
if (modules_) {
|
||||
const CodeModule *module =
|
||||
|
@ -101,6 +93,8 @@ bool Stackwalker::Walk(CallStack *stack) {
|
|||
frame->module = module;
|
||||
if (resolver_ &&
|
||||
!resolver_->HasModule(frame->module->code_file()) &&
|
||||
no_symbol_modules_.find(
|
||||
module->code_file()) == no_symbol_modules_.end() &&
|
||||
supplier_) {
|
||||
string symbol_data, symbol_file;
|
||||
SymbolSupplier::SymbolResult symbol_result =
|
||||
|
@ -113,12 +107,13 @@ bool Stackwalker::Walk(CallStack *stack) {
|
|||
symbol_data);
|
||||
break;
|
||||
case SymbolSupplier::NOT_FOUND:
|
||||
no_symbol_modules_.insert(module->code_file());
|
||||
break; // nothing to do
|
||||
case SymbolSupplier::INTERRUPT:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
frame_info.reset(resolver_->FillSourceLineInfo(frame.get()));
|
||||
resolver_->FillSourceLineInfo(frame.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,12 +121,8 @@ bool Stackwalker::Walk(CallStack *stack) {
|
|||
// over the frame, because the stack now owns it.
|
||||
stack->frames_.push_back(frame.release());
|
||||
|
||||
// Add the frame info to the parallel stack.
|
||||
stack_frame_info.push_back(frame_info);
|
||||
frame_info.reset(NULL);
|
||||
|
||||
// Get the next frame and take ownership.
|
||||
frame.reset(GetCallerFrame(stack, stack_frame_info));
|
||||
frame.reset(GetCallerFrame(stack));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2007, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -34,15 +34,61 @@
|
|||
// Author: Mark Mentovai, Ted Mielczarek
|
||||
|
||||
|
||||
#include "processor/stackwalker_amd64.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_amd64.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
||||
const StackwalkerAMD64::CFIWalker::RegisterSet
|
||||
StackwalkerAMD64::cfi_register_map_[] = {
|
||||
// It may seem like $rip and $rsp are callee-saves, because the callee is
|
||||
// responsible for having them restored upon return. But the callee_saves
|
||||
// flags here really means that the walker should assume they're
|
||||
// unchanged if the CFI doesn't mention them --- clearly wrong for $rip
|
||||
// and $rsp.
|
||||
{ "$rax", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_RAX, &MDRawContextAMD64::rax },
|
||||
{ "$rdx", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_RDX, &MDRawContextAMD64::rdx },
|
||||
{ "$rcx", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_RCX, &MDRawContextAMD64::rcx },
|
||||
{ "$rbx", NULL, true,
|
||||
StackFrameAMD64::CONTEXT_VALID_RBX, &MDRawContextAMD64::rbx },
|
||||
{ "$rsi", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_RSI, &MDRawContextAMD64::rsi },
|
||||
{ "$rdi", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_RDI, &MDRawContextAMD64::rdi },
|
||||
{ "$rbp", NULL, true,
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP, &MDRawContextAMD64::rbp },
|
||||
{ "$rsp", ".cfa", false,
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP, &MDRawContextAMD64::rsp },
|
||||
{ "$r8", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_R8, &MDRawContextAMD64::r8 },
|
||||
{ "$r9", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_R9, &MDRawContextAMD64::r9 },
|
||||
{ "$r10", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_R10, &MDRawContextAMD64::r10 },
|
||||
{ "$r11", NULL, false,
|
||||
StackFrameAMD64::CONTEXT_VALID_R11, &MDRawContextAMD64::r11 },
|
||||
{ "$r12", NULL, true,
|
||||
StackFrameAMD64::CONTEXT_VALID_R12, &MDRawContextAMD64::r12 },
|
||||
{ "$r13", NULL, true,
|
||||
StackFrameAMD64::CONTEXT_VALID_R13, &MDRawContextAMD64::r13 },
|
||||
{ "$r14", NULL, true,
|
||||
StackFrameAMD64::CONTEXT_VALID_R14, &MDRawContextAMD64::r14 },
|
||||
{ "$r15", NULL, true,
|
||||
StackFrameAMD64::CONTEXT_VALID_R15, &MDRawContextAMD64::r15 },
|
||||
{ "$rip", ".ra", false,
|
||||
StackFrameAMD64::CONTEXT_VALID_RIP, &MDRawContextAMD64::rip },
|
||||
};
|
||||
|
||||
StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info,
|
||||
const MDRawContextAMD64 *context,
|
||||
MemoryRegion *memory,
|
||||
|
@ -50,7 +96,9 @@ StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info,
|
|||
SymbolSupplier *supplier,
|
||||
SourceLineResolverInterface *resolver)
|
||||
: Stackwalker(system_info, memory, modules, supplier, resolver),
|
||||
context_(context) {
|
||||
context_(context),
|
||||
cfi_walker_(cfi_register_map_,
|
||||
(sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) {
|
||||
}
|
||||
|
||||
|
||||
|
@ -71,60 +119,70 @@ StackFrame* StackwalkerAMD64::GetContextFrame() {
|
|||
return frame;
|
||||
}
|
||||
|
||||
StackFrameAMD64 *StackwalkerAMD64::GetCallerByCFIFrameInfo(
|
||||
const vector<StackFrame *> &frames,
|
||||
CFIFrameInfo *cfi_frame_info) {
|
||||
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
||||
|
||||
StackFrame* StackwalkerAMD64::GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
|
||||
scoped_ptr<StackFrameAMD64> frame(new StackFrameAMD64());
|
||||
if (!cfi_walker_
|
||||
.FindCallerRegisters(*memory_, *cfi_frame_info,
|
||||
last_frame->context, last_frame->context_validity,
|
||||
&frame->context, &frame->context_validity))
|
||||
return NULL;
|
||||
|
||||
// Make sure we recovered all the essentials.
|
||||
static const int essentials = (StackFrameAMD64::CONTEXT_VALID_RIP
|
||||
| StackFrameAMD64::CONTEXT_VALID_RSP);
|
||||
if ((frame->context_validity & essentials) != essentials)
|
||||
return NULL;
|
||||
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack *stack) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64*>(
|
||||
stack->frames()->back());
|
||||
const vector<StackFrame *> &frames = *stack->frames();
|
||||
StackFrameAMD64 *last_frame = static_cast<StackFrameAMD64 *>(frames.back());
|
||||
scoped_ptr<StackFrameAMD64> new_frame;
|
||||
|
||||
//FIXME: this pretty much doesn't work at all due to FPO
|
||||
// being enabled by default.
|
||||
// Brain-dead stackwalking:
|
||||
// %rip_new = *(%rbp_old + 8)
|
||||
// %rsp_new = %rbp_old + 16
|
||||
// %rbp_new = *(%rbp_old)
|
||||
|
||||
// A caller frame must reside higher in memory than its callee frames.
|
||||
// Anything else is an error, or an indication that we've reached the
|
||||
// end of the stack.
|
||||
u_int64_t stack_pointer = last_frame->context.rbp + 16;
|
||||
if (stack_pointer <= last_frame->context.rsp) {
|
||||
return NULL;
|
||||
// If we have DWARF CFI information, use it.
|
||||
if (!new_frame.get()) {
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(resolver_
|
||||
->FindCFIFrameInfo(last_frame));
|
||||
if (cfi_frame_info.get())
|
||||
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
||||
}
|
||||
|
||||
u_int64_t instruction;
|
||||
if (!memory_->GetMemoryAtAddress(last_frame->context.rbp + 8,
|
||||
&instruction) ||
|
||||
instruction <= 1) {
|
||||
// If nothing worked, tell the caller.
|
||||
if (!new_frame.get())
|
||||
return NULL;
|
||||
}
|
||||
|
||||
u_int64_t stack_base;
|
||||
if (!memory_->GetMemoryAtAddress(last_frame->context.rbp,
|
||||
&stack_base) ||
|
||||
stack_base <= 1) {
|
||||
|
||||
// Treat an instruction address of 0 as end-of-stack.
|
||||
if (new_frame->context.rip == 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameAMD64 *frame = new StackFrameAMD64();
|
||||
// If the new stack pointer is at a lower address than the old, then
|
||||
// that's clearly incorrect. Treat this as end-of-stack to enforce
|
||||
// progress and avoid infinite loops.
|
||||
if (new_frame->context.rsp <= last_frame->context.rsp)
|
||||
return NULL;
|
||||
|
||||
frame->context = last_frame->context;
|
||||
frame->context.rip = instruction;
|
||||
frame->context.rsp = stack_pointer;
|
||||
frame->context.rbp = stack_base;
|
||||
frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP;
|
||||
// new_frame->context.rip is the return address, which is one instruction
|
||||
// past the CALL that caused us to arrive at the callee. Set
|
||||
// new_frame->instruction to one less than that. This won't reference the
|
||||
// beginning of the CALL instruction, but it's guaranteed to be within
|
||||
// the CALL, which is sufficient to get the source line information to
|
||||
// match up with the line that contains a function call. Callers that
|
||||
// require the exact return address value may access the context.rip
|
||||
// field of StackFrameAMD64.
|
||||
new_frame->instruction = new_frame->context.rip - 1;
|
||||
|
||||
frame->instruction = frame->context.rip - 1;
|
||||
|
||||
return frame;
|
||||
return new_frame.release();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2007, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -42,6 +42,8 @@
|
|||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/stackwalker.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
@ -61,16 +63,29 @@ class StackwalkerAMD64 : public Stackwalker {
|
|||
SourceLineResolverInterface *resolver);
|
||||
|
||||
private:
|
||||
// A STACK CFI-driven frame walker for the AMD64
|
||||
typedef SimpleCFIWalker<u_int64_t, MDRawContextAMD64> CFIWalker;
|
||||
|
||||
// Implementation of Stackwalker, using amd64 context (stack pointer in %rsp,
|
||||
// stack base in %rbp) and stack conventions (saved stack pointer at 0(%rbp))
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
|
||||
virtual StackFrame* GetCallerFrame(const CallStack *stack);
|
||||
|
||||
// Use cfi_frame_info (derived from STACK CFI records) to construct
|
||||
// the frame that called frames.back(). The caller takes ownership
|
||||
// of the returned frame. Return NULL on failure.
|
||||
StackFrameAMD64 *GetCallerByCFIFrameInfo(const vector<StackFrame *> &frames,
|
||||
CFIFrameInfo *cfi_frame_info);
|
||||
|
||||
// Stores the CPU context corresponding to the innermost stack frame to
|
||||
// be returned by GetContextFrame.
|
||||
const MDRawContextAMD64 *context_;
|
||||
|
||||
// Our register map, for cfi_walker_.
|
||||
static const CFIWalker::RegisterSet cfi_register_map_[];
|
||||
|
||||
// Our CFI frame walker.
|
||||
const CFIWalker cfi_walker_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
// 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>
|
||||
|
||||
// stackwalker_amd64_unittest.cc: Unit tests for StackwalkerAMD64 class.
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/stackwalker_unittest_utils.h"
|
||||
#include "processor/stackwalker_amd64.h"
|
||||
#include "processor/test_assembler.h"
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CallStack;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::StackFrameAMD64;
|
||||
using google_breakpad::StackwalkerAMD64;
|
||||
using google_breakpad::SystemInfo;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using testing::_;
|
||||
using testing::Return;
|
||||
using testing::SetArgumentPointee;
|
||||
using testing::Test;
|
||||
|
||||
class StackwalkerAMD64Fixture {
|
||||
public:
|
||||
StackwalkerAMD64Fixture()
|
||||
: stack_section(kLittleEndian),
|
||||
// Give the two modules reasonable standard locations and names
|
||||
// for tests to play with.
|
||||
module1(0x40000000c0000000ULL, 0x10000, "module1", "version1"),
|
||||
module2(0x50000000b0000000ULL, 0x10000, "module2", "version2") {
|
||||
// Identify the system as a Linux system.
|
||||
system_info.os = "Linux";
|
||||
system_info.os_short = "linux";
|
||||
system_info.os_version = "Horrendous Hippo";
|
||||
system_info.cpu = "x86";
|
||||
system_info.cpu_info = "";
|
||||
|
||||
// Put distinctive values in the raw CPU context.
|
||||
BrandContext(&raw_context);
|
||||
|
||||
// Create some modules with some stock debugging information.
|
||||
modules.Add(&module1);
|
||||
modules.Add(&module2);
|
||||
|
||||
// By default, none of the modules have symbol info; call
|
||||
// SetModuleSymbols to override this.
|
||||
EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _))
|
||||
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
|
||||
}
|
||||
|
||||
// Set the Breakpad symbol information that supplier should return for
|
||||
// MODULE to INFO.
|
||||
void SetModuleSymbols(MockCodeModule *module, const string &info) {
|
||||
EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<3>(info),
|
||||
Return(MockSymbolSupplier::FOUND)));
|
||||
}
|
||||
|
||||
// Populate stack_region with the contents of stack_section. Use
|
||||
// stack_section.start() as the region's starting address.
|
||||
void RegionFromSection() {
|
||||
string contents;
|
||||
ASSERT_TRUE(stack_section.GetContents(&contents));
|
||||
stack_region.Init(stack_section.start().Value(), contents);
|
||||
}
|
||||
|
||||
// Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
|
||||
void BrandContext(MDRawContextAMD64 *raw_context) {
|
||||
u_int8_t x = 173;
|
||||
for (size_t i = 0; i < sizeof(*raw_context); i++)
|
||||
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
|
||||
}
|
||||
|
||||
SystemInfo system_info;
|
||||
MDRawContextAMD64 raw_context;
|
||||
Section stack_section;
|
||||
MockMemoryRegion stack_region;
|
||||
MockCodeModule module1;
|
||||
MockCodeModule module2;
|
||||
MockCodeModules modules;
|
||||
MockSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
CallStack call_stack;
|
||||
const vector<StackFrame *> *frames;
|
||||
};
|
||||
|
||||
class GetContextFrame: public StackwalkerAMD64Fixture, public Test { };
|
||||
|
||||
TEST_F(GetContextFrame, Simple) {
|
||||
// There should be no references to the stack in this walk: we don't
|
||||
// provide any call frame information, so trying to reconstruct the
|
||||
// context frame's caller should fail. So there's no need for us to
|
||||
// provide stack contents.
|
||||
raw_context.rip = 0x40000000c0000200ULL;
|
||||
raw_context.rbp = 0x8000000080000000ULL;
|
||||
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&supplier, &resolver);
|
||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||
frames = call_stack.frames();
|
||||
ASSERT_GE(1U, frames->size());
|
||||
StackFrameAMD64 *frame = static_cast<StackFrameAMD64 *>(frames->at(0));
|
||||
// Check that the values from the original raw context made it
|
||||
// through to the context in the stack frame.
|
||||
EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0);
|
||||
}
|
||||
|
||||
struct CFIFixture: public StackwalkerAMD64Fixture {
|
||||
CFIFixture() {
|
||||
// Provide a bunch of STACK CFI records; we'll walk to the caller
|
||||
// from every point in this series, expecting to find the same set
|
||||
// of register values.
|
||||
SetModuleSymbols(&module1,
|
||||
// The youngest frame's function.
|
||||
"FUNC 4000 1000 10 enchiridion\n"
|
||||
// Initially, just a return address.
|
||||
"STACK CFI INIT 4000 100 .cfa: $rsp 8 + .ra: .cfa 8 - ^\n"
|
||||
// Push %rbx.
|
||||
"STACK CFI 4001 .cfa: $rsp 16 + $rbx: .cfa 16 - ^\n"
|
||||
// Save %r12 in %rbx. Weird, but permitted.
|
||||
"STACK CFI 4002 $r12: $rbx\n"
|
||||
// Allocate frame space, and save %r13.
|
||||
"STACK CFI 4003 .cfa: $rsp 40 + $r13: .cfa 32 - ^\n"
|
||||
// Put the return address in %r13.
|
||||
"STACK CFI 4005 .ra: $r13\n"
|
||||
// Save %rbp, and use it as a frame pointer.
|
||||
"STACK CFI 4006 .cfa: $rbp 16 + $rbp: .cfa 24 - ^\n"
|
||||
|
||||
// The calling function.
|
||||
"FUNC 5000 1000 10 epictetus\n"
|
||||
// Mark it as end of stack.
|
||||
"STACK CFI INIT 5000 1000 .cfa: $rsp .ra 0\n");
|
||||
|
||||
// Provide some distinctive values for the caller's registers.
|
||||
expected.rsp = 0x8000000080000000ULL;
|
||||
expected.rip = 0x40000000c0005510ULL;
|
||||
expected.rbp = 0x68995b1de4700266ULL;
|
||||
expected.rbx = 0x5a5beeb38de23be8ULL;
|
||||
expected.r12 = 0xed1b02e8cc0fc79cULL;
|
||||
expected.r13 = 0x1d20ad8acacbe930ULL;
|
||||
expected.r14 = 0xe94cffc2f7adaa28ULL;
|
||||
expected.r15 = 0xb638d17d8da413b5ULL;
|
||||
|
||||
// By default, registers are unchanged.
|
||||
raw_context = expected;
|
||||
}
|
||||
|
||||
// Walk the stack, using stack_section as the contents of the stack
|
||||
// and raw_context as the current register values. (Set
|
||||
// raw_context.rsp to the stack's starting address.) Expect two
|
||||
// stack frames; in the older frame, expect the callee-saves
|
||||
// registers to have values matching those in 'expected'.
|
||||
void CheckWalk() {
|
||||
RegionFromSection();
|
||||
raw_context.rsp = stack_section.start().Value();
|
||||
|
||||
StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&supplier, &resolver);
|
||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(2U, frames->size());
|
||||
|
||||
StackFrameAMD64 *frame0 = static_cast<StackFrameAMD64 *>(frames->at(0));
|
||||
ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity);
|
||||
EXPECT_EQ("enchiridion", frame0->function_name);
|
||||
EXPECT_EQ(0x40000000c0004000ULL, frame0->function_base);
|
||||
|
||||
StackFrameAMD64 *frame1 = static_cast<StackFrameAMD64 *>(frames->at(1));
|
||||
ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RSP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBP |
|
||||
StackFrameAMD64::CONTEXT_VALID_RBX |
|
||||
StackFrameAMD64::CONTEXT_VALID_R12 |
|
||||
StackFrameAMD64::CONTEXT_VALID_R13 |
|
||||
StackFrameAMD64::CONTEXT_VALID_R14 |
|
||||
StackFrameAMD64::CONTEXT_VALID_R15),
|
||||
frame1->context_validity);
|
||||
EXPECT_EQ(expected.rip, frame1->context.rip);
|
||||
EXPECT_EQ(expected.rsp, frame1->context.rsp);
|
||||
EXPECT_EQ(expected.rbp, frame1->context.rbp);
|
||||
EXPECT_EQ(expected.rbx, frame1->context.rbx);
|
||||
EXPECT_EQ(expected.r12, frame1->context.r12);
|
||||
EXPECT_EQ(expected.r13, frame1->context.r13);
|
||||
EXPECT_EQ(expected.r14, frame1->context.r14);
|
||||
EXPECT_EQ(expected.r15, frame1->context.r15);
|
||||
EXPECT_EQ("epictetus", frame1->function_name);
|
||||
}
|
||||
|
||||
// The values we expect to find for the caller's registers.
|
||||
MDRawContextAMD64 expected;
|
||||
};
|
||||
|
||||
class CFI: public CFIFixture, public Test { };
|
||||
|
||||
TEST_F(CFI, At4000) {
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x40000000c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x40000000c0004000ULL;
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4001) {
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
|
||||
.D64(0x40000000c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x40000000c0004001ULL;
|
||||
raw_context.rbx = 0xbe0487d2f9eafe29ULL; // callee's (distinct) %rbx value
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4002) {
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
|
||||
.D64(0x40000000c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x40000000c0004002ULL;
|
||||
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
|
||||
raw_context.r12 = 0xb0118de918a4bceaULL; // callee's (distinct) %r12 value
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4003) {
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x0e023828dffd4d81ULL) // garbage
|
||||
.D64(0x1d20ad8acacbe930ULL) // saved %r13
|
||||
.D64(0x319e68b49e3ace0fULL) // garbage
|
||||
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
|
||||
.D64(0x40000000c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x40000000c0004003ULL;
|
||||
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
|
||||
raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12
|
||||
raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
// The results here should be the same as those at module offset 0x4003.
|
||||
TEST_F(CFI, At4004) {
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x0e023828dffd4d81ULL) // garbage
|
||||
.D64(0x1d20ad8acacbe930ULL) // saved %r13
|
||||
.D64(0x319e68b49e3ace0fULL) // garbage
|
||||
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
|
||||
.D64(0x40000000c0005510ULL) // return address
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x40000000c0004004ULL;
|
||||
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
|
||||
raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12
|
||||
raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4005) {
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x4b516dd035745953ULL) // garbage
|
||||
.D64(0x1d20ad8acacbe930ULL) // saved %r13
|
||||
.D64(0xa6d445e16ae3d872ULL) // garbage
|
||||
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
|
||||
.D64(0xaa95fa054aedfbaeULL) // garbage
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x40000000c0004005ULL;
|
||||
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
|
||||
raw_context.r12 = 0x46b1b8868891b34aULL; // callee's %r12
|
||||
raw_context.r13 = 0x40000000c0005510ULL; // return address
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4006) {
|
||||
Label frame0_rbp;
|
||||
Label frame1_rsp = expected.rsp;
|
||||
stack_section
|
||||
.D64(0x043c6dfceb91aa34ULL) // garbage
|
||||
.D64(0x1d20ad8acacbe930ULL) // saved %r13
|
||||
.D64(0x68995b1de4700266ULL) // saved %rbp
|
||||
.Mark(&frame0_rbp) // frame pointer points here
|
||||
.D64(0x5a5beeb38de23be8ULL) // saved %rbx
|
||||
.D64(0xf015ee516ad89eabULL) // garbage
|
||||
.Mark(&frame1_rsp); // This effectively sets stack_section.start().
|
||||
raw_context.rip = 0x40000000c0004006ULL;
|
||||
raw_context.rbp = frame0_rbp.Value();
|
||||
raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12
|
||||
raw_context.r12 = 0x26e007b341acfebdULL; // callee's %r12
|
||||
raw_context.r13 = 0x40000000c0005510ULL; // return address
|
||||
CheckWalk();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -31,14 +31,17 @@
|
|||
//
|
||||
// See stackwalker_arm.h for documentation.
|
||||
//
|
||||
// Author: Mark Mentovai, Ted Mielczarek
|
||||
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy
|
||||
|
||||
|
||||
#include "processor/stackwalker_arm.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/cfi_frame_info.h"
|
||||
#include "processor/logging.h"
|
||||
#include "processor/scoped_ptr.h"
|
||||
#include "processor/stackwalker_arm.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
|
@ -50,8 +53,8 @@ StackwalkerARM::StackwalkerARM(const SystemInfo *system_info,
|
|||
SymbolSupplier *supplier,
|
||||
SourceLineResolverInterface *resolver)
|
||||
: Stackwalker(system_info, memory, modules, supplier, resolver),
|
||||
context_(context) {
|
||||
}
|
||||
context_(context),
|
||||
context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { }
|
||||
|
||||
|
||||
StackFrame* StackwalkerARM::GetContextFrame() {
|
||||
|
@ -65,27 +68,117 @@ StackFrame* StackwalkerARM::GetContextFrame() {
|
|||
// The instruction pointer is stored directly in a register (r15), so pull it
|
||||
// straight out of the CPU context structure.
|
||||
frame->context = *context_;
|
||||
frame->context_validity = StackFrameARM::CONTEXT_VALID_ALL;
|
||||
frame->context_validity = context_frame_validity_;
|
||||
frame->instruction = frame->context.iregs[15];
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerARM::GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
|
||||
StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
StackFrameARM *last_frame = static_cast<StackFrameARM*>(
|
||||
stack->frames()->back());
|
||||
const vector<StackFrame *> &frames = *stack->frames();
|
||||
StackFrameARM *last_frame = static_cast<StackFrameARM *>(frames.back());
|
||||
|
||||
// TODO: Can't actually walk the stack on ARM without the CFI data.
|
||||
// Implement this when the CFI symbol dumper changes have landed.
|
||||
return NULL;
|
||||
// See if we have DWARF call frame information covering this address.
|
||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(resolver_
|
||||
->FindCFIFrameInfo(last_frame));
|
||||
if (cfi_frame_info == NULL)
|
||||
// Unfortunately, CFI is our only option on the ARM for now. If we
|
||||
// add a second strategy, we should put each one in its own function.
|
||||
return NULL;
|
||||
|
||||
static const char *register_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",
|
||||
NULL
|
||||
};
|
||||
|
||||
// Populate a dictionary with the valid register values in last_frame.
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t> callee_registers;
|
||||
for (int i = 0; register_names[i]; i++)
|
||||
if (last_frame->context_validity & StackFrameARM::RegisterValidFlag(i))
|
||||
callee_registers[register_names[i]] = last_frame->context.iregs[i];
|
||||
|
||||
// Use the STACK CFI data to recover the caller's register values.
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
|
||||
if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
|
||||
&caller_registers))
|
||||
return NULL;
|
||||
|
||||
// Construct a new stack frame given the values the CFI recovered.
|
||||
scoped_ptr<StackFrameARM> frame(new StackFrameARM());
|
||||
for (int i = 0; register_names[i]; i++) {
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
|
||||
caller_registers.find(register_names[i]);
|
||||
if (entry != caller_registers.end()) {
|
||||
// We recovered the value of this register; fill the context with the
|
||||
// value from caller_registers.
|
||||
frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
|
||||
frame->context.iregs[i] = entry->second;
|
||||
} else if (4 <= i && i <= 11 && (last_frame->context_validity &
|
||||
StackFrameARM::RegisterValidFlag(i))) {
|
||||
// If the STACK CFI data doesn't mention some callee-saves register, and
|
||||
// it is valid in the callee, assume the callee has not yet changed it.
|
||||
// Registers r4 through r11 are callee-saves, according to the Procedure
|
||||
// Call Standard for the ARM Architecture, which the Linux ABI follows.
|
||||
frame->context_validity |= StackFrameARM::RegisterValidFlag(i);
|
||||
frame->context.iregs[i] = last_frame->context.iregs[i];
|
||||
}
|
||||
}
|
||||
// If the CFI doesn't recover the PC explicitly, then use .ra.
|
||||
if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_PC)) {
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
|
||||
caller_registers.find(".ra");
|
||||
if (entry != caller_registers.end()) {
|
||||
frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC;
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second;
|
||||
}
|
||||
}
|
||||
// If the CFI doesn't recover the SP explicitly, then use .cfa.
|
||||
if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) {
|
||||
CFIFrameInfo::RegisterValueMap<u_int32_t>::iterator entry =
|
||||
caller_registers.find(".cfa");
|
||||
if (entry != caller_registers.end()) {
|
||||
frame->context_validity |= StackFrameARM::CONTEXT_VALID_SP;
|
||||
frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = entry->second;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't recover the PC and the SP, then the frame isn't very useful.
|
||||
static const int essentials = (StackFrameARM::CONTEXT_VALID_SP
|
||||
| StackFrameARM::CONTEXT_VALID_PC);
|
||||
if ((frame->context_validity & essentials) != essentials)
|
||||
return NULL;
|
||||
|
||||
// An instruction address of zero marks the end of the stack.
|
||||
if (frame->context.iregs[MD_CONTEXT_ARM_REG_PC] == 0)
|
||||
return NULL;
|
||||
|
||||
// If the new stack pointer is at a lower address than the old, then
|
||||
// that's clearly incorrect. Treat this as end-of-stack to enforce
|
||||
// progress and avoid infinite loops.
|
||||
if (frame->context.iregs[MD_CONTEXT_ARM_REG_SP]
|
||||
< last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP])
|
||||
return NULL;
|
||||
|
||||
// The new frame's context's PC is the return address, which is one
|
||||
// instruction past the instruction that caused us to arrive at the
|
||||
// callee. Set new_frame->instruction to one less than the PC. This won't
|
||||
// reference the beginning of the call instruction, but it's at least
|
||||
// within it, which is sufficient to get the source line information to
|
||||
// match up with the line that contains the function call. Callers that
|
||||
// require the exact return address value may access
|
||||
// frame->context.iregs[MD_CONTEXT_ARM_REG_PC].
|
||||
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 1;
|
||||
|
||||
return frame.release();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
// Copyright (c) 2009, Google Inc.
|
||||
// -*- mode: C++ -*-
|
||||
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -60,17 +62,25 @@ class StackwalkerARM : public Stackwalker {
|
|||
SymbolSupplier *supplier,
|
||||
SourceLineResolverInterface *resolver);
|
||||
|
||||
// Change the context validity mask of the frame returned by
|
||||
// GetContextFrame to VALID. This is only for use by unit tests; the
|
||||
// default behavior is correct for all application code.
|
||||
void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; }
|
||||
|
||||
private:
|
||||
// Implementation of Stackwalker, using arm context and stack conventions.
|
||||
// TODO: currently stubbed out, needs CFI symbol dumper support
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
|
||||
virtual StackFrame* GetCallerFrame(const CallStack *stack);
|
||||
|
||||
// Stores the CPU context corresponding to the innermost stack frame to
|
||||
// Stores the CPU context corresponding to the youngest stack frame, to
|
||||
// be returned by GetContextFrame.
|
||||
const MDRawContextARM *context_;
|
||||
|
||||
// Validity mask for youngest stack frame. This is always
|
||||
// CONTEXT_VALID_ALL in real use; it is only changeable for the sake of
|
||||
// unit tests.
|
||||
int context_frame_validity_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,440 @@
|
|||
// 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>
|
||||
|
||||
// stackwalker_arm_unittest.cc: Unit tests for StackwalkerARM class.
|
||||
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
#include "google_breakpad/common/minidump_format.h"
|
||||
#include "google_breakpad/processor/basic_source_line_resolver.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/source_line_resolver_interface.h"
|
||||
#include "google_breakpad/processor/stack_frame_cpu.h"
|
||||
#include "processor/stackwalker_unittest_utils.h"
|
||||
#include "processor/stackwalker_arm.h"
|
||||
#include "processor/test_assembler.h"
|
||||
#include "processor/windows_frame_info.h"
|
||||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
using google_breakpad::CallStack;
|
||||
using google_breakpad::StackFrame;
|
||||
using google_breakpad::StackFrameARM;
|
||||
using google_breakpad::StackwalkerARM;
|
||||
using google_breakpad::SystemInfo;
|
||||
using google_breakpad::WindowsFrameInfo;
|
||||
using google_breakpad::TestAssembler::kLittleEndian;
|
||||
using google_breakpad::TestAssembler::Label;
|
||||
using google_breakpad::TestAssembler::Section;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using testing::_;
|
||||
using testing::Return;
|
||||
using testing::SetArgumentPointee;
|
||||
using testing::Test;
|
||||
|
||||
class StackwalkerARMFixture {
|
||||
public:
|
||||
StackwalkerARMFixture()
|
||||
: stack_section(kLittleEndian),
|
||||
// Give the two modules reasonable standard locations and names
|
||||
// for tests to play with.
|
||||
module1(0x40000000, 0x10000, "module1", "version1"),
|
||||
module2(0x50000000, 0x10000, "module2", "version2") {
|
||||
// Identify the system as a Linux system.
|
||||
system_info.os = "Linux";
|
||||
system_info.os_short = "linux";
|
||||
system_info.os_version = "Lugubrious Labrador";
|
||||
system_info.cpu = "arm";
|
||||
system_info.cpu_info = "";
|
||||
|
||||
// Put distinctive values in the raw CPU context.
|
||||
BrandContext(&raw_context);
|
||||
|
||||
// Create some modules with some stock debugging information.
|
||||
modules.Add(&module1);
|
||||
modules.Add(&module2);
|
||||
|
||||
// By default, none of the modules have symbol info; call
|
||||
// SetModuleSymbols to override this.
|
||||
EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _))
|
||||
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
|
||||
}
|
||||
|
||||
// Set the Breakpad symbol information that supplier should return for
|
||||
// MODULE to INFO.
|
||||
void SetModuleSymbols(MockCodeModule *module, const string &info) {
|
||||
EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _))
|
||||
.WillRepeatedly(DoAll(SetArgumentPointee<3>(info),
|
||||
Return(MockSymbolSupplier::FOUND)));
|
||||
}
|
||||
|
||||
// Populate stack_region with the contents of stack_section. Use
|
||||
// stack_section.start() as the region's starting address.
|
||||
void RegionFromSection() {
|
||||
string contents;
|
||||
ASSERT_TRUE(stack_section.GetContents(&contents));
|
||||
stack_region.Init(stack_section.start().Value(), contents);
|
||||
}
|
||||
|
||||
// Fill RAW_CONTEXT with pseudo-random data, for round-trip checking.
|
||||
void BrandContext(MDRawContextARM *raw_context) {
|
||||
u_int8_t x = 173;
|
||||
for (size_t i = 0; i < sizeof(*raw_context); i++)
|
||||
reinterpret_cast<u_int8_t *>(raw_context)[i] = (x += 17);
|
||||
}
|
||||
|
||||
SystemInfo system_info;
|
||||
MDRawContextARM raw_context;
|
||||
Section stack_section;
|
||||
MockMemoryRegion stack_region;
|
||||
MockCodeModule module1;
|
||||
MockCodeModule module2;
|
||||
MockCodeModules modules;
|
||||
MockSymbolSupplier supplier;
|
||||
BasicSourceLineResolver resolver;
|
||||
CallStack call_stack;
|
||||
const vector<StackFrame *> *frames;
|
||||
};
|
||||
|
||||
class GetContextFrame: public StackwalkerARMFixture, public Test { };
|
||||
|
||||
TEST_F(GetContextFrame, Simple) {
|
||||
// Since we have no call frame information, and all unwinding
|
||||
// requires call frame information, the stack walk will end after
|
||||
// the first frame.
|
||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&supplier, &resolver);
|
||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
StackFrameARM *frame = static_cast<StackFrameARM *>(frames->at(0));
|
||||
// Check that the values from the original raw context made it
|
||||
// through to the context in the stack frame.
|
||||
EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0);
|
||||
}
|
||||
|
||||
struct CFIFixture: public StackwalkerARMFixture {
|
||||
CFIFixture() {
|
||||
// Provide a bunch of STACK CFI records; we'll walk to the caller
|
||||
// from every point in this series, expecting to find the same set
|
||||
// of register values.
|
||||
SetModuleSymbols(&module1,
|
||||
// The youngest frame's function.
|
||||
"FUNC 4000 1000 10 enchiridion\n"
|
||||
// Initially, nothing has been pushed on the stack,
|
||||
// and the return address is still in the link register.
|
||||
"STACK CFI INIT 4000 100 .cfa: sp .ra: lr\n"
|
||||
// Push r4, the frame pointer, and the link register.
|
||||
"STACK CFI 4001 .cfa: sp 12 + r4: .cfa 12 - ^"
|
||||
" r11: .cfa 8 - ^ .ra: .cfa 4 - ^\n"
|
||||
// Save r4..r7 in r0..r3: verify that we populate
|
||||
// the youngest frame with all the values we have.
|
||||
"STACK CFI 4002 r4: r0 r5: r1 r6: r2 r7: r3\n"
|
||||
// Restore r4..r7. Save the non-callee-saves register r1.
|
||||
"STACK CFI 4003 .cfa: sp 16 + r1: .cfa 16 - ^"
|
||||
" r4: r4 r5: r5 r6: r6 r7: r7\n"
|
||||
// Move the .cfa back four bytes, to point at the return
|
||||
// address, and restore the sp explicitly.
|
||||
"STACK CFI 4005 .cfa: sp 12 + r1: .cfa 12 - ^"
|
||||
" r11: .cfa 4 - ^ .ra: .cfa ^ sp: .cfa 4 +\n"
|
||||
// Recover the PC explicitly from a new stack slot;
|
||||
// provide garbage for the .ra.
|
||||
"STACK CFI 4006 .cfa: sp 16 + pc: .cfa 16 - ^\n"
|
||||
|
||||
// The calling function.
|
||||
"FUNC 5000 1000 10 epictetus\n"
|
||||
// Mark it as end of stack.
|
||||
"STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n"
|
||||
|
||||
// A function whose CFI makes the stack pointer
|
||||
// go backwards.
|
||||
"FUNC 6000 1000 20 palinal\n"
|
||||
"STACK CFI INIT 6000 1000 .cfa: sp 4 - .ra: lr\n"
|
||||
|
||||
// A function with CFI expressions that can't be
|
||||
// evaluated.
|
||||
"FUNC 7000 1000 20 rhetorical\n"
|
||||
"STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n");
|
||||
|
||||
// Provide some distinctive values for the caller's registers.
|
||||
expected.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510;
|
||||
expected.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
|
||||
expected.iregs[4] = 0xb5d55e68;
|
||||
expected.iregs[5] = 0xebd134f3;
|
||||
expected.iregs[6] = 0xa31e74bc;
|
||||
expected.iregs[7] = 0x2dcb16b3;
|
||||
expected.iregs[8] = 0x2ada2137;
|
||||
expected.iregs[9] = 0xbbbb557d;
|
||||
expected.iregs[10] = 0x48bf8ca7;
|
||||
expected.iregs[MD_CONTEXT_ARM_REG_FP] = 0x8112e110;
|
||||
|
||||
// Expect CFI to recover all callee-saves registers. Since CFI is the
|
||||
// only stack frame construction technique we have, aside from the
|
||||
// context frame itself, there's no way for us to have a set of valid
|
||||
// registers smaller than this.
|
||||
expected_validity = (StackFrameARM::CONTEXT_VALID_PC |
|
||||
StackFrameARM::CONTEXT_VALID_SP |
|
||||
StackFrameARM::CONTEXT_VALID_R4 |
|
||||
StackFrameARM::CONTEXT_VALID_R5 |
|
||||
StackFrameARM::CONTEXT_VALID_R6 |
|
||||
StackFrameARM::CONTEXT_VALID_R7 |
|
||||
StackFrameARM::CONTEXT_VALID_R8 |
|
||||
StackFrameARM::CONTEXT_VALID_R9 |
|
||||
StackFrameARM::CONTEXT_VALID_R10 |
|
||||
StackFrameARM::CONTEXT_VALID_FP);
|
||||
|
||||
// By default, context frames provide all registers, as normal.
|
||||
context_frame_validity = StackFrameARM::CONTEXT_VALID_ALL;
|
||||
|
||||
// By default, registers are unchanged.
|
||||
raw_context = expected;
|
||||
}
|
||||
|
||||
// Walk the stack, using stack_section as the contents of the stack
|
||||
// and raw_context as the current register values. (Set the stack
|
||||
// pointer to the stack's starting address.) Expect two stack
|
||||
// frames; in the older frame, expect the callee-saves registers to
|
||||
// have values matching those in 'expected'.
|
||||
void CheckWalk() {
|
||||
RegionFromSection();
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value();
|
||||
|
||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&supplier, &resolver);
|
||||
walker.SetContextFrameValidity(context_frame_validity);
|
||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(2U, frames->size());
|
||||
|
||||
StackFrameARM *frame0 = static_cast<StackFrameARM *>(frames->at(0));
|
||||
ASSERT_EQ(context_frame_validity, frame0->context_validity);
|
||||
EXPECT_EQ("enchiridion", frame0->function_name);
|
||||
EXPECT_EQ(0x40004000U, frame0->function_base);
|
||||
|
||||
StackFrameARM *frame1 = static_cast<StackFrameARM *>(frames->at(1));
|
||||
ASSERT_EQ(expected_validity, frame1->context_validity);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R1)
|
||||
EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R4)
|
||||
EXPECT_EQ(expected.iregs[4], frame1->context.iregs[4]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R5)
|
||||
EXPECT_EQ(expected.iregs[5], frame1->context.iregs[5]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R6)
|
||||
EXPECT_EQ(expected.iregs[6], frame1->context.iregs[6]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R7)
|
||||
EXPECT_EQ(expected.iregs[7], frame1->context.iregs[7]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R8)
|
||||
EXPECT_EQ(expected.iregs[8], frame1->context.iregs[8]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R9)
|
||||
EXPECT_EQ(expected.iregs[9], frame1->context.iregs[9]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_R10)
|
||||
EXPECT_EQ(expected.iregs[10], frame1->context.iregs[10]);
|
||||
if (expected_validity & StackFrameARM::CONTEXT_VALID_FP)
|
||||
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_FP],
|
||||
frame1->context.iregs[MD_CONTEXT_ARM_REG_FP]);
|
||||
|
||||
// We would never have gotten a frame in the first place if the SP
|
||||
// and PC weren't valid or ->instruction weren't set.
|
||||
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_SP],
|
||||
frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]);
|
||||
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
|
||||
frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]);
|
||||
EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC],
|
||||
frame1->instruction + 1);
|
||||
EXPECT_EQ("epictetus", frame1->function_name);
|
||||
}
|
||||
|
||||
// The values we expect to find for the caller's registers.
|
||||
MDRawContextARM expected;
|
||||
|
||||
// The validity mask for expected.
|
||||
int expected_validity;
|
||||
|
||||
// The validity mask to impose on the context frame.
|
||||
int context_frame_validity;
|
||||
};
|
||||
|
||||
class CFI: public CFIFixture, public Test { };
|
||||
|
||||
TEST_F(CFI, At4000) {
|
||||
stack_section.start() = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004000;
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4001) {
|
||||
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
stack_section
|
||||
.D32(0xb5d55e68) // saved r4
|
||||
.D32(0x8112e110) // saved fp
|
||||
.D32(0x40005510) // return address
|
||||
.Mark(&frame1_sp); // This effectively sets stack_section.start().
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
|
||||
raw_context.iregs[4] = 0x635adc9f; // distinct callee r4
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
// As above, but unwind from a context that has only the PC and SP.
|
||||
TEST_F(CFI, At4001LimitedValidity) {
|
||||
context_frame_validity =
|
||||
StackFrameARM::CONTEXT_VALID_PC | StackFrameARM::CONTEXT_VALID_SP;
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001;
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
|
||||
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
stack_section
|
||||
.D32(0xb5d55e68) // saved r4
|
||||
.D32(0x8112e110) // saved fp
|
||||
.D32(0x40005510) // return address
|
||||
.Mark(&frame1_sp); // This effectively sets stack_section.start().
|
||||
expected_validity = (StackFrameARM::CONTEXT_VALID_PC
|
||||
| StackFrameARM::CONTEXT_VALID_SP
|
||||
| StackFrameARM::CONTEXT_VALID_FP
|
||||
| StackFrameARM::CONTEXT_VALID_R4);
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4002) {
|
||||
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
stack_section
|
||||
.D32(0xfb81ff3d) // no longer saved r4
|
||||
.D32(0x8112e110) // saved fp
|
||||
.D32(0x40005510) // return address
|
||||
.Mark(&frame1_sp); // This effectively sets stack_section.start().
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004002;
|
||||
raw_context.iregs[0] = 0xb5d55e68; // saved r4
|
||||
raw_context.iregs[1] = 0xebd134f3; // saved r5
|
||||
raw_context.iregs[2] = 0xa31e74bc; // saved r6
|
||||
raw_context.iregs[3] = 0x2dcb16b3; // saved r7
|
||||
raw_context.iregs[4] = 0xfdd35466; // distinct callee r4
|
||||
raw_context.iregs[5] = 0xf18c946c; // distinct callee r5
|
||||
raw_context.iregs[6] = 0xac2079e8; // distinct callee r6
|
||||
raw_context.iregs[7] = 0xa449829f; // distinct callee r7
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
TEST_F(CFI, At4003) {
|
||||
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
stack_section
|
||||
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
|
||||
.D32(0xcb78040e) // no longer saved r4
|
||||
.D32(0x8112e110) // saved fp
|
||||
.D32(0x40005510) // return address
|
||||
.Mark(&frame1_sp); // This effectively sets stack_section.start().
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004003;
|
||||
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0x0a2857ea; // distinct callee fp
|
||||
expected.iregs[1] = 0x48c8dd5a; // caller's r1
|
||||
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
// We have no new rule at module offset 0x4004, so the results here should
|
||||
// be the same as those at module offset 0x4003.
|
||||
TEST_F(CFI, At4004) {
|
||||
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
stack_section
|
||||
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
|
||||
.D32(0xcb78040e) // no longer saved r4
|
||||
.D32(0x8112e110) // saved fp
|
||||
.D32(0x40005510) // return address
|
||||
.Mark(&frame1_sp); // This effectively sets stack_section.start().
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004004;
|
||||
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
|
||||
expected.iregs[1] = 0x48c8dd5a; // caller's r1
|
||||
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
// Here we move the .cfa, but provide an explicit rule to recover the SP,
|
||||
// so again there should be no change in the registers recovered.
|
||||
TEST_F(CFI, At4005) {
|
||||
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
stack_section
|
||||
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
|
||||
.D32(0xf013f841) // no longer saved r4
|
||||
.D32(0x8112e110) // saved fp
|
||||
.D32(0x40005510) // return address
|
||||
.Mark(&frame1_sp); // This effectively sets stack_section.start().
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004005;
|
||||
raw_context.iregs[1] = 0xfb756319; // distinct callee r1
|
||||
expected.iregs[1] = 0x48c8dd5a; // caller's r1
|
||||
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
// Here we provide an explicit rule for the PC, and have the saved .ra be
|
||||
// bogus.
|
||||
TEST_F(CFI, At4006) {
|
||||
Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP];
|
||||
stack_section
|
||||
.D32(0x40005510) // saved pc
|
||||
.D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves)
|
||||
.D32(0xf013f841) // no longer saved r4
|
||||
.D32(0x8112e110) // saved fp
|
||||
.D32(0xf8d15783) // .ra rule recovers this, which is garbage
|
||||
.Mark(&frame1_sp); // This effectively sets stack_section.start().
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004006;
|
||||
raw_context.iregs[1] = 0xfb756319; // callee's r1, different from caller's
|
||||
expected.iregs[1] = 0x48c8dd5a; // caller's r1
|
||||
expected_validity |= StackFrameARM::CONTEXT_VALID_R1;
|
||||
CheckWalk();
|
||||
}
|
||||
|
||||
// Check that we reject rules that would cause the stack pointer to
|
||||
// move in the wrong direction.
|
||||
TEST_F(CFI, RejectBackwards) {
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000;
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510;
|
||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&supplier, &resolver);
|
||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
}
|
||||
|
||||
// Check that we reject rules whose expressions' evaluation fails.
|
||||
TEST_F(CFI, RejectBadExpressions) {
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000;
|
||||
raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000;
|
||||
StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules,
|
||||
&supplier, &resolver);
|
||||
ASSERT_TRUE(walker.Walk(&call_stack));
|
||||
frames = call_stack.frames();
|
||||
ASSERT_EQ(1U, frames->size());
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -81,9 +81,7 @@ StackFrame* StackwalkerPPC::GetContextFrame() {
|
|||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerPPC::GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
|
||||
StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack *stack) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2006, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -65,9 +65,7 @@ class StackwalkerPPC : public Stackwalker {
|
|||
// saved program counter in %srr0) and stack conventions (saved stack
|
||||
// pointer at 0(%r1), return address at 8(0(%r1)).
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
|
||||
virtual StackFrame* GetCallerFrame(const CallStack *stack);
|
||||
|
||||
// Stores the CPU context corresponding to the innermost stack frame to
|
||||
// be returned by GetContextFrame.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2007, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -72,9 +72,7 @@ StackFrame* StackwalkerSPARC::GetContextFrame() {
|
|||
}
|
||||
|
||||
|
||||
StackFrame* StackwalkerSPARC::GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info) {
|
||||
StackFrame* StackwalkerSPARC::GetCallerFrame(const CallStack *stack) {
|
||||
if (!memory_ || !stack) {
|
||||
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
|
||||
return NULL;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2007, Google Inc.
|
||||
// Copyright (c) 2010 Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
|
@ -61,18 +61,10 @@ class StackwalkerSPARC : public Stackwalker {
|
|||
SourceLineResolverInterface *resolver);
|
||||
|
||||
private:
|
||||
// Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and
|
||||
// stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or
|
||||
// alternate conventions as guided by stack_frame_info_).
|
||||
// Implementation of Stackwalker, using ppc context (stack pointer in %r1,
|
||||
// saved program counter in %srr0) and stack conventions (saved stack
|
||||
// pointer at 0(%r1), return address at 8(0(%r1)).
|
||||
// Implementation of Stackwalker, using sparc context (%fp, %sp, %pc) and
|
||||
// stack conventions (saved %sp at)
|
||||
// stack conventions
|
||||
virtual StackFrame* GetContextFrame();
|
||||
virtual StackFrame* GetCallerFrame(
|
||||
const CallStack *stack,
|
||||
const vector< linked_ptr<StackFrameInfo> > &stack_frame_info);
|
||||
virtual StackFrame* GetCallerFrame(const CallStack *stack);
|
||||
|
||||
// Stores the CPU context corresponding to the innermost stack frame to
|
||||
// be returned by GetContextFrame.
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче