Process minidumps generated on ARM64 in iOS apps.

Patch by Colin Blundell <blundell@chromium.org>

BUG=542

Review URL: https://breakpad.appspot.com/704002/


git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1236 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
mark@chromium.org 2013-11-23 01:45:20 +00:00
parent 77022ac0df
commit e9165f4353
17 changed files with 5388 additions and 2643 deletions

View file

@ -187,12 +187,15 @@ src_libbreakpad_a_SOURCES = \
src/processor/windows_frame_info.h \
src/processor/source_line_resolver_base_types.h \
src/processor/source_line_resolver_base.cc \
src/processor/stack_frame_cpu.cc \
src/processor/stack_frame_symbolizer.cc \
src/processor/stackwalker.cc \
src/processor/stackwalker_amd64.cc \
src/processor/stackwalker_amd64.h \
src/processor/stackwalker_arm.cc \
src/processor/stackwalker_arm.h \
src/processor/stackwalker_arm64.cc \
src/processor/stackwalker_arm64.h \
src/processor/stackwalker_address_list.cc \
src/processor/stackwalker_address_list.h \
src/processor/stackwalker_mips.cc \
@ -293,6 +296,7 @@ check_PROGRAMS += \
src/processor/range_map_unittest \
src/processor/stackwalker_amd64_unittest \
src/processor/stackwalker_arm_unittest \
src/processor/stackwalker_arm64_unittest \
src/processor/stackwalker_address_list_unittest \
src/processor/stackwalker_mips_unittest \
src/processor/stackwalker_x86_unittest \
@ -636,10 +640,12 @@ src_processor_exploitability_unittest_LDADD = \
src/processor/pathname_stripper.o \
src/processor/simple_symbol_supplier.o \
src/processor/source_line_resolver_base.o \
src/processor/stack_frame_cpu.o \
src/processor/stack_frame_symbolizer.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_address_list.o \
src/processor/stackwalker_mips.o \
src/processor/stackwalker_ppc.o \
@ -728,10 +734,12 @@ src_processor_minidump_processor_unittest_LDADD = \
src/processor/pathname_stripper.o \
src/processor/process_state.o \
src/processor/source_line_resolver_base.o \
src/processor/stack_frame_cpu.o \
src/processor/stack_frame_symbolizer.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_address_list.o \
src/processor/stackwalker_mips.o \
src/processor/stackwalker_ppc.o \
@ -856,10 +864,12 @@ src_processor_stackwalker_selftest_LDADD = \
src/processor/minidump.o \
src/processor/pathname_stripper.o \
src/processor/source_line_resolver_base.o \
src/processor/stack_frame_cpu.o \
src/processor/stack_frame_symbolizer.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_address_list.o \
src/processor/stackwalker_mips.o \
src/processor/stackwalker_ppc.o \
@ -901,6 +911,22 @@ src_processor_stackwalker_arm_unittest_CPPFLAGS = \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_stackwalker_arm64_unittest_SOURCES = \
src/common/test_assembler.cc \
src/processor/stackwalker_arm64_unittest.cc \
src/testing/gtest/src/gtest-all.cc \
src/testing/gtest/src/gtest_main.cc \
src/testing/src/gmock-all.cc
src_processor_stackwalker_arm64_unittest_LDADD = \
src/libbreakpad.a \
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
src_processor_stackwalker_arm64_unittest_CPPFLAGS = \
-I$(top_srcdir)/src \
-I$(top_srcdir)/src/testing/include \
-I$(top_srcdir)/src/testing/gtest/include \
-I$(top_srcdir)/src/testing/gtest \
-I$(top_srcdir)/src/testing
src_processor_stackwalker_address_list_unittest_SOURCES = \
src/common/test_assembler.cc \
src/processor/stackwalker_address_list_unittest.cc \
@ -1012,10 +1038,12 @@ src_processor_minidump_stackwalk_LDADD = \
src/processor/process_state.o \
src/processor/simple_symbol_supplier.o \
src/processor/source_line_resolver_base.o \
src/processor/stack_frame_cpu.o \
src/processor/stack_frame_symbolizer.o \
src/processor/stackwalker.o \
src/processor/stackwalker_amd64.o \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_address_list.o \
src/processor/stackwalker_mips.o \
src/processor/stackwalker_ppc.o \

File diff suppressed because it is too large Load diff

601
aclocal.m4 vendored

File diff suppressed because it is too large Load diff

View file

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

347
autotools/compile Executable file
View file

@ -0,0 +1,347 @@
#! /bin/sh
# Wrapper for compilers which do not understand '-c -o'.
scriptversion=2012-10-14.11; # UTC
# Copyright (C) 1999-2013 Free Software Foundation, Inc.
# Written by Tom Tromey <tromey@cygnus.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
nl='
'
# We need space, tab and new line, in precisely that order. Quoting is
# there to prevent tools from complaining about whitespace usage.
IFS=" "" $nl"
file_conv=
# func_file_conv build_file lazy
# Convert a $build file to $host form and store it in $file
# Currently only supports Windows hosts. If the determined conversion
# type is listed in (the comma separated) LAZY, no conversion will
# take place.
func_file_conv ()
{
file=$1
case $file in
/ | /[!/]*) # absolute file, and not a UNC file
if test -z "$file_conv"; then
# lazily determine how to convert abs files
case `uname -s` in
MINGW*)
file_conv=mingw
;;
CYGWIN*)
file_conv=cygwin
;;
*)
file_conv=wine
;;
esac
fi
case $file_conv/,$2, in
*,$file_conv,*)
;;
mingw/*)
file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'`
;;
cygwin/*)
file=`cygpath -m "$file" || echo "$file"`
;;
wine/*)
file=`winepath -w "$file" || echo "$file"`
;;
esac
;;
esac
}
# func_cl_dashL linkdir
# Make cl look for libraries in LINKDIR
func_cl_dashL ()
{
func_file_conv "$1"
if test -z "$lib_path"; then
lib_path=$file
else
lib_path="$lib_path;$file"
fi
linker_opts="$linker_opts -LIBPATH:$file"
}
# func_cl_dashl library
# Do a library search-path lookup for cl
func_cl_dashl ()
{
lib=$1
found=no
save_IFS=$IFS
IFS=';'
for dir in $lib_path $LIB
do
IFS=$save_IFS
if $shared && test -f "$dir/$lib.dll.lib"; then
found=yes
lib=$dir/$lib.dll.lib
break
fi
if test -f "$dir/$lib.lib"; then
found=yes
lib=$dir/$lib.lib
break
fi
if test -f "$dir/lib$lib.a"; then
found=yes
lib=$dir/lib$lib.a
break
fi
done
IFS=$save_IFS
if test "$found" != yes; then
lib=$lib.lib
fi
}
# func_cl_wrapper cl arg...
# Adjust compile command to suit cl
func_cl_wrapper ()
{
# Assume a capable shell
lib_path=
shared=:
linker_opts=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
eat=1
case $2 in
*.o | *.[oO][bB][jJ])
func_file_conv "$2"
set x "$@" -Fo"$file"
shift
;;
*)
func_file_conv "$2"
set x "$@" -Fe"$file"
shift
;;
esac
;;
-I)
eat=1
func_file_conv "$2" mingw
set x "$@" -I"$file"
shift
;;
-I*)
func_file_conv "${1#-I}" mingw
set x "$@" -I"$file"
shift
;;
-l)
eat=1
func_cl_dashl "$2"
set x "$@" "$lib"
shift
;;
-l*)
func_cl_dashl "${1#-l}"
set x "$@" "$lib"
shift
;;
-L)
eat=1
func_cl_dashL "$2"
;;
-L*)
func_cl_dashL "${1#-L}"
;;
-static)
shared=false
;;
-Wl,*)
arg=${1#-Wl,}
save_ifs="$IFS"; IFS=','
for flag in $arg; do
IFS="$save_ifs"
linker_opts="$linker_opts $flag"
done
IFS="$save_ifs"
;;
-Xlinker)
eat=1
linker_opts="$linker_opts $2"
;;
-*)
set x "$@" "$1"
shift
;;
*.cc | *.CC | *.cxx | *.CXX | *.[cC]++)
func_file_conv "$1"
set x "$@" -Tp"$file"
shift
;;
*.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO])
func_file_conv "$1" mingw
set x "$@" "$file"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -n "$linker_opts"; then
linker_opts="-link$linker_opts"
fi
exec "$@" $linker_opts
exit 1
}
eat=
case $1 in
'')
echo "$0: No command. Try '$0 --help' for more information." 1>&2
exit 1;
;;
-h | --h*)
cat <<\EOF
Usage: compile [--help] [--version] PROGRAM [ARGS]
Wrapper for compilers which do not understand '-c -o'.
Remove '-o dest.o' from ARGS, run PROGRAM with the remaining
arguments, and rename the output as expected.
If you are trying to build a whole package this is not the
right script to run: please start by reading the file 'INSTALL'.
Report bugs to <bug-automake@gnu.org>.
EOF
exit $?
;;
-v | --v*)
echo "compile $scriptversion"
exit $?
;;
cl | *[/\\]cl | cl.exe | *[/\\]cl.exe )
func_cl_wrapper "$@" # Doesn't return...
;;
esac
ofile=
cfile=
for arg
do
if test -n "$eat"; then
eat=
else
case $1 in
-o)
# configure might choose to run compile as 'compile cc -o foo foo.c'.
# So we strip '-o arg' only if arg is an object.
eat=1
case $2 in
*.o | *.obj)
ofile=$2
;;
*)
set x "$@" -o "$2"
shift
;;
esac
;;
*.c)
cfile=$1
set x "$@" "$1"
shift
;;
*)
set x "$@" "$1"
shift
;;
esac
fi
shift
done
if test -z "$ofile" || test -z "$cfile"; then
# If no '-o' option was seen then we might have been invoked from a
# pattern rule where we don't need one. That is ok -- this is a
# normal compilation that the losing compiler can handle. If no
# '.c' file was seen then we are probably linking. That is also
# ok.
exec "$@"
fi
# Name of file we expect compiler to create.
cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'`
# Create the lock directory.
# Note: use '[/\\:.-]' here to ensure that we don't use the same name
# that we are using for the .o file. Also, base the name on the expected
# object file name, since that is what matters with a parallel build.
lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d
while true; do
if mkdir "$lockdir" >/dev/null 2>&1; then
break
fi
sleep 1
done
# FIXME: race condition here if user kills between mkdir and trap.
trap "rmdir '$lockdir'; exit 1" 1 2 15
# Run the compile.
"$@"
ret=$?
if test -f "$cofile"; then
test "$cofile" = "$ofile" || mv "$cofile" "$ofile"
elif test -f "${cofile}bj"; then
test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile"
fi
rmdir "$lockdir"
exit $ret
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

127
autotools/test-driver Executable file
View file

@ -0,0 +1,127 @@
#! /bin/sh
# test-driver - basic testsuite driver script.
scriptversion=2012-06-27.10; # UTC
# Copyright (C) 2011-2013 Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# As a special exception to the GNU General Public License, if you
# distribute this file as part of a program that contains a
# configuration script generated by Autoconf, you may include it under
# the same distribution terms that you use for the rest of that program.
# This file is maintained in Automake, please report
# bugs to <bug-automake@gnu.org> or send patches to
# <automake-patches@gnu.org>.
# Make unconditional expansion of undefined variables an error. This
# helps a lot in preventing typo-related bugs.
set -u
usage_error ()
{
echo "$0: $*" >&2
print_usage >&2
exit 2
}
print_usage ()
{
cat <<END
Usage:
test-driver --test-name=NAME --log-file=PATH --trs-file=PATH
[--expect-failure={yes|no}] [--color-tests={yes|no}]
[--enable-hard-errors={yes|no}] [--] TEST-SCRIPT
The '--test-name', '--log-file' and '--trs-file' options are mandatory.
END
}
# TODO: better error handling in option parsing (in particular, ensure
# TODO: $log_file, $trs_file and $test_name are defined).
test_name= # Used for reporting.
log_file= # Where to save the output of the test script.
trs_file= # Where to save the metadata of the test run.
expect_failure=no
color_tests=no
enable_hard_errors=yes
while test $# -gt 0; do
case $1 in
--help) print_usage; exit $?;;
--version) echo "test-driver $scriptversion"; exit $?;;
--test-name) test_name=$2; shift;;
--log-file) log_file=$2; shift;;
--trs-file) trs_file=$2; shift;;
--color-tests) color_tests=$2; shift;;
--expect-failure) expect_failure=$2; shift;;
--enable-hard-errors) enable_hard_errors=$2; shift;;
--) shift; break;;
-*) usage_error "invalid option: '$1'";;
esac
shift
done
if test $color_tests = yes; then
# Keep this in sync with 'lib/am/check.am:$(am__tty_colors)'.
red='' # Red.
grn='' # Green.
lgn='' # Light green.
blu='' # Blue.
mgn='' # Magenta.
std='' # No color.
else
red= grn= lgn= blu= mgn= std=
fi
do_exit='rm -f $log_file $trs_file; (exit $st); exit $st'
trap "st=129; $do_exit" 1
trap "st=130; $do_exit" 2
trap "st=141; $do_exit" 13
trap "st=143; $do_exit" 15
# Test script is run here.
"$@" >$log_file 2>&1
estatus=$?
if test $enable_hard_errors = no && test $estatus -eq 99; then
estatus=1
fi
case $estatus:$expect_failure in
0:yes) col=$red res=XPASS recheck=yes gcopy=yes;;
0:*) col=$grn res=PASS recheck=no gcopy=no;;
77:*) col=$blu res=SKIP recheck=no gcopy=yes;;
99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;;
*:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;;
*:*) col=$red res=FAIL recheck=yes gcopy=yes;;
esac
# Report outcome to console.
echo "${col}${res}${std}: $test_name"
# Register the test result, and other relevant metadata.
echo ":test-result: $res" > $trs_file
echo ":global-test-result: $res" >> $trs_file
echo ":recheck: $recheck" >> $trs_file
echo ":copy-in-global-log: $gcopy" >> $trs_file
# Local Variables:
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'write-file-hooks 'time-stamp)
# time-stamp-start: "scriptversion="
# time-stamp-format: "%:y-%02m-%02d.%02H"
# time-stamp-time-zone: "UTC"
# time-stamp-end: "; # UTC"
# End:

1260
configure vendored

File diff suppressed because it is too large Load diff

View file

@ -33,9 +33,6 @@
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if your C compiler doesn't accept -c and -o together. */
#undef NO_MINUS_C_MINUS_O
/* Name of package */
#undef PACKAGE

View file

@ -187,6 +187,7 @@ class MinidumpContext : public MinidumpStream {
// NULL.
const MDRawContextAMD64* GetContextAMD64() const;
const MDRawContextARM* GetContextARM() const;
const MDRawContextARM64* GetContextARM64() const;
const MDRawContextMIPS* GetContextMIPS() const;
const MDRawContextPPC* GetContextPPC() const;
const MDRawContextPPC64* GetContextPPC64() const;
@ -210,6 +211,7 @@ class MinidumpContext : public MinidumpStream {
// so variables can NOT be named as sparc
MDRawContextSPARC* ctx_sparc;
MDRawContextARM* arm;
MDRawContextARM64* arm64;
MDRawContextMIPS* ctx_mips;
} context_;

View file

@ -270,6 +270,70 @@ struct StackFrameARM : public StackFrame {
int context_validity;
};
struct StackFrameARM64 : public StackFrame {
// A flag for each register we might know. Note that we can't use an enum
// here as there are 33 values to represent.
static const uint64_t CONTEXT_VALID_NONE = 0;
static const uint64_t CONTEXT_VALID_X0 = 1ULL << 0;
static const uint64_t CONTEXT_VALID_X1 = 1ULL << 1;
static const uint64_t CONTEXT_VALID_X2 = 1ULL << 2;
static const uint64_t CONTEXT_VALID_X3 = 1ULL << 3;
static const uint64_t CONTEXT_VALID_X4 = 1ULL << 4;
static const uint64_t CONTEXT_VALID_X5 = 1ULL << 5;
static const uint64_t CONTEXT_VALID_X6 = 1ULL << 6;
static const uint64_t CONTEXT_VALID_X7 = 1ULL << 7;
static const uint64_t CONTEXT_VALID_X8 = 1ULL << 8;
static const uint64_t CONTEXT_VALID_X9 = 1ULL << 9;
static const uint64_t CONTEXT_VALID_X10 = 1ULL << 10;
static const uint64_t CONTEXT_VALID_X11 = 1ULL << 11;
static const uint64_t CONTEXT_VALID_X12 = 1ULL << 12;
static const uint64_t CONTEXT_VALID_X13 = 1ULL << 13;
static const uint64_t CONTEXT_VALID_X14 = 1ULL << 14;
static const uint64_t CONTEXT_VALID_X15 = 1ULL << 15;
static const uint64_t CONTEXT_VALID_X16 = 1ULL << 16;
static const uint64_t CONTEXT_VALID_X17 = 1ULL << 17;
static const uint64_t CONTEXT_VALID_X18 = 1ULL << 18;
static const uint64_t CONTEXT_VALID_X19 = 1ULL << 19;
static const uint64_t CONTEXT_VALID_X20 = 1ULL << 20;
static const uint64_t CONTEXT_VALID_X21 = 1ULL << 21;
static const uint64_t CONTEXT_VALID_X22 = 1ULL << 22;
static const uint64_t CONTEXT_VALID_X23 = 1ULL << 23;
static const uint64_t CONTEXT_VALID_X24 = 1ULL << 24;
static const uint64_t CONTEXT_VALID_X25 = 1ULL << 25;
static const uint64_t CONTEXT_VALID_X26 = 1ULL << 26;
static const uint64_t CONTEXT_VALID_X27 = 1ULL << 27;
static const uint64_t CONTEXT_VALID_X28 = 1ULL << 28;
static const uint64_t CONTEXT_VALID_X29 = 1ULL << 29;
static const uint64_t CONTEXT_VALID_X30 = 1ULL << 30;
static const uint64_t CONTEXT_VALID_X31 = 1ULL << 31;
static const uint64_t CONTEXT_VALID_X32 = 1ULL << 32;
static const uint64_t CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE;
// Aliases for registers with dedicated or conventional roles.
static const uint64_t CONTEXT_VALID_FP = CONTEXT_VALID_X29;
static const uint64_t CONTEXT_VALID_LR = CONTEXT_VALID_X30;
static const uint64_t CONTEXT_VALID_SP = CONTEXT_VALID_X31;
static const uint64_t CONTEXT_VALID_PC = CONTEXT_VALID_X32;
StackFrameARM64() : context(),
context_validity(CONTEXT_VALID_NONE) {}
// Return the validity flag for register xN.
static uint64_t RegisterValidFlag(int n) {
return 1ULL << 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.
MDRawContextARM64 context;
// For each register in context whose value has been recovered, we set
// the corresponding CONTEXT_VALID_ bit in context_validity.
uint64_t context_validity;
};
struct StackFrameMIPS : public StackFrame {
// MIPS callee save registers for o32 ABI (32bit registers) are:
// 1. $s0-$s7,

View file

@ -73,6 +73,30 @@ using std::ifstream;
using std::numeric_limits;
using std::vector;
// Returns true iff |context_size| matches exactly one of the sizes of the
// various MDRawContext* types.
// TODO(blundell): This function can be removed once
// http://code.google.com/p/google-breakpad/issues/detail?id=550 is fixed.
static bool IsContextSizeUnique(uint32_t context_size) {
int num_matching_contexts = 0;
if (context_size == sizeof(MDRawContextX86))
num_matching_contexts++;
if (context_size == sizeof(MDRawContextPPC))
num_matching_contexts++;
if (context_size == sizeof(MDRawContextPPC64))
num_matching_contexts++;
if (context_size == sizeof(MDRawContextAMD64))
num_matching_contexts++;
if (context_size == sizeof(MDRawContextSPARC))
num_matching_contexts++;
if (context_size == sizeof(MDRawContextARM))
num_matching_contexts++;
if (context_size == sizeof(MDRawContextARM64))
num_matching_contexts++;
if (context_size == sizeof(MDRawContextMIPS))
num_matching_contexts++;
return num_matching_contexts == 1;
}
//
// Swapping routines
@ -361,6 +385,23 @@ MinidumpContext::~MinidumpContext() {
bool MinidumpContext::Read(uint32_t expected_size) {
valid_ = false;
// Certain raw context types are currently assumed to have unique sizes.
if (!IsContextSizeUnique(sizeof(MDRawContextAMD64))) {
BPLOG(ERROR) << "sizeof(MDRawContextAMD64) cannot match the size of any "
<< "other raw context";
return false;
}
if (!IsContextSizeUnique(sizeof(MDRawContextPPC64))) {
BPLOG(ERROR) << "sizeof(MDRawContextPPC64) cannot match the size of any "
<< "other raw context";
return false;
}
if (!IsContextSizeUnique(sizeof(MDRawContextARM64))) {
BPLOG(ERROR) << "sizeof(MDRawContextARM64) cannot match the size of any "
<< "other raw context";
return false;
}
FreeContext();
// First, figure out what type of CPU this context structure is for.
@ -390,9 +431,8 @@ bool MinidumpContext::Read(uint32_t expected_size) {
}
if (cpu_type != MD_CONTEXT_AMD64) {
// TODO: fall through to switch below?
// need a Tell method to be able to SeekSet back to beginning
// http://code.google.com/p/google-breakpad/issues/detail?id=224
// TODO: Fall through to switch below.
// http://code.google.com/p/google-breakpad/issues/detail?id=550
BPLOG(ERROR) << "MinidumpContext not actually amd64 context";
return false;
}
@ -483,6 +523,21 @@ bool MinidumpContext::Read(uint32_t expected_size) {
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
scoped_ptr<MDRawContextPPC64> context_ppc64(new MDRawContextPPC64());
if (cpu_type == 0) {
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
context_ppc64->context_flags |= cpu_type;
} else {
BPLOG(ERROR) << "Failed to preserve the current stream position";
return false;
}
}
if (cpu_type != MD_CONTEXT_PPC64) {
// TODO: Fall through to switch below.
// http://code.google.com/p/google-breakpad/issues/detail?id=550
BPLOG(ERROR) << "MinidumpContext not actually ppc64 context";
return false;
}
// Set the context_flags member, which has already been read, and
// read the rest of the structure beginning with the first member
@ -548,6 +603,83 @@ bool MinidumpContext::Read(uint32_t expected_size) {
}
context_.ppc64 = context_ppc64.release();
context_flags_ = context_flags;
} else if (expected_size == sizeof(MDRawContextARM64)) {
// |context_flags| of MDRawContextARM64 is 64 bits, but other MDRawContext
// in the else case have 32 bits |context_flags|, so special case it here.
uint64_t context_flags;
BPLOG(INFO) << "MinidumpContext: looks like ARM64 context";
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
BPLOG(ERROR) << "MinidumpContext could not read context flags";
return false;
}
if (minidump_->swap())
Swap(&context_flags);
scoped_ptr<MDRawContextARM64> context_arm64(new MDRawContextARM64());
uint32_t cpu_type = context_flags & MD_CONTEXT_CPU_MASK;
if (cpu_type == 0) {
if (minidump_->GetContextCPUFlagsFromSystemInfo(&cpu_type)) {
context_arm64->context_flags |= cpu_type;
} else {
BPLOG(ERROR) << "Failed to preserve the current stream position";
return false;
}
}
if (cpu_type != MD_CONTEXT_ARM64) {
// TODO: Fall through to switch below.
// http://code.google.com/p/google-breakpad/issues/detail?id=550
BPLOG(ERROR) << "MinidumpContext not actually arm64 context";
return false;
}
// Set the context_flags member, which has already been read, and
// read the rest of the structure beginning with the first member
// after context_flags.
context_arm64->context_flags = context_flags;
size_t flags_size = sizeof(context_arm64->context_flags);
uint8_t* context_after_flags =
reinterpret_cast<uint8_t*>(context_arm64.get()) + flags_size;
if (!minidump_->ReadBytes(context_after_flags,
sizeof(MDRawContextARM64) - flags_size)) {
BPLOG(ERROR) << "MinidumpContext could not read arm64 context";
return false;
}
// Do this after reading the entire MDRawContext structure because
// GetSystemInfo may seek minidump to a new position.
if (!CheckAgainstSystemInfo(cpu_type)) {
BPLOG(ERROR) << "MinidumpContext arm64 does not match system info";
return false;
}
if (minidump_->swap()) {
// context_arm64->context_flags was already swapped.
for (unsigned int ireg_index = 0;
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
++ireg_index) {
Swap(&context_arm64->iregs[ireg_index]);
}
Swap(&context_arm64->cpsr);
Swap(&context_arm64->float_save.fpsr);
Swap(&context_arm64->float_save.fpcr);
for (unsigned int fpr_index = 0;
fpr_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
++fpr_index) {
// While ARM64 is bi-endian, iOS (currently the only platform
// for which ARM64 support has been brought up) uses ARM64 exclusively
// in little-endian mode.
Normalize128(&context_arm64->float_save.regs[fpr_index], false);
Swap(&context_arm64->float_save.regs[fpr_index]);
}
}
context_.arm64 = context_arm64.release();
context_flags_ = context_flags;
} else {
uint32_t context_flags;
if (!minidump_->ReadBytes(&context_flags, sizeof(context_flags))) {
@ -954,6 +1086,9 @@ bool MinidumpContext::GetInstructionPointer(uint64_t* ip) const {
case MD_CONTEXT_ARM:
*ip = context_.arm->iregs[MD_CONTEXT_ARM_REG_PC];
break;
case MD_CONTEXT_ARM64:
*ip = context_.arm64->iregs[MD_CONTEXT_ARM64_REG_PC];
break;
case MD_CONTEXT_PPC:
*ip = context_.ppc->srr0;
break;
@ -1033,6 +1168,15 @@ const MDRawContextARM* MinidumpContext::GetContextARM() const {
return context_.arm;
}
const MDRawContextARM64* MinidumpContext::GetContextARM64() const {
if (GetContextCPU() != MD_CONTEXT_ARM64) {
BPLOG(ERROR) << "MinidumpContext cannot get arm64 context";
return NULL;
}
return context_.arm64;
}
const MDRawContextMIPS* MinidumpContext::GetContextMIPS() const {
if (GetContextCPU() != MD_CONTEXT_MIPS) {
BPLOG(ERROR) << "MinidumpContext cannot get MIPS context";
@ -1068,6 +1212,10 @@ void MinidumpContext::FreeContext() {
delete context_.arm;
break;
case MD_CONTEXT_ARM64:
delete context_.arm64;
break;
case MD_CONTEXT_MIPS:
delete context_.ctx_mips;
break;
@ -1142,6 +1290,11 @@ bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
return_value = true;
break;
case MD_CONTEXT_ARM64:
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM64)
return_value = true;
break;
case MD_CONTEXT_MIPS:
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS)
return_value = true;
@ -1424,6 +1577,31 @@ void MinidumpContext::Print() {
break;
}
case MD_CONTEXT_ARM64: {
const MDRawContextARM64* context_arm64 = GetContextARM64();
printf("MDRawContextARM64\n");
printf(" context_flags = 0x%llx\n",
context_arm64->context_flags);
for (unsigned int ireg_index = 0;
ireg_index < MD_CONTEXT_ARM64_GPR_COUNT;
++ireg_index) {
printf(" iregs[%2d] = 0x%llx\n",
ireg_index, context_arm64->iregs[ireg_index]);
}
printf(" cpsr = 0x%x\n", context_arm64->cpsr);
printf(" float_save.fpsr = 0x%x\n", context_arm64->float_save.fpsr);
printf(" float_save.fpcr = 0x%x\n", context_arm64->float_save.fpcr);
for (unsigned int freg_index = 0;
freg_index < MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT;
++freg_index) {
uint128_struct fp_value = context_arm64->float_save.regs[freg_index];
printf(" float_save.regs[%2d] = 0x%llx%llx\n",
freg_index, fp_value.high, fp_value.low);
}
break;
}
case MD_CONTEXT_MIPS: {
const MDRawContextMIPS* context_mips = GetContextMIPS();
printf("MDRawContextMIPS\n");
@ -3530,6 +3708,10 @@ string MinidumpSystemInfo::GetCPU() {
cpu = "arm";
break;
case MD_CPU_ARCHITECTURE_ARM64:
cpu = "arm64";
break;
default:
BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " <<
HexString(system_info_.processor_architecture);
@ -4265,6 +4447,9 @@ bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t *context_cpu_flags) {
case MD_CPU_ARCHITECTURE_ARM:
*context_cpu_flags = MD_CONTEXT_ARM;
break;
case MD_CPU_ARCHITECTURE_ARM64:
*context_cpu_flags = MD_CONTEXT_ARM64;
break;
case MD_CPU_ARCHITECTURE_IA64:
*context_cpu_flags = MD_CONTEXT_IA64;
break;

View file

@ -505,6 +505,11 @@ bool MinidumpProcessor::GetCPUInfo(Minidump *dump, SystemInfo *info) {
break;
}
case MD_CPU_ARCHITECTURE_ARM64: {
info->cpu = "arm64";
break;
}
case MD_CPU_ARCHITECTURE_MIPS: {
info->cpu = "mips";
break;
@ -668,7 +673,9 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
default:
// arm and ppc overlap
if (raw_system_info->processor_architecture ==
MD_CPU_ARCHITECTURE_ARM) {
MD_CPU_ARCHITECTURE_ARM ||
raw_system_info->processor_architecture ==
MD_CPU_ARCHITECTURE_ARM64) {
switch (exception_flags) {
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
reason.append("EXC_ARM_DA_ALIGN");
@ -708,7 +715,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
case MD_EXCEPTION_MAC_BAD_INSTRUCTION:
reason = "EXC_BAD_INSTRUCTION / ";
switch (raw_system_info->processor_architecture) {
case MD_CPU_ARCHITECTURE_ARM: {
case MD_CPU_ARCHITECTURE_ARM:
case MD_CPU_ARCHITECTURE_ARM64: {
switch (exception_flags) {
case MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED:
reason.append("EXC_ARM_UNDEFINED");
@ -887,7 +895,8 @@ string MinidumpProcessor::GetCrashReason(Minidump *dump, uint64_t *address) {
case MD_EXCEPTION_MAC_BREAKPOINT:
reason = "EXC_BREAKPOINT / ";
switch (raw_system_info->processor_architecture) {
case MD_CPU_ARCHITECTURE_ARM: {
case MD_CPU_ARCHITECTURE_ARM:
case MD_CPU_ARCHITECTURE_ARM64: {
switch (exception_flags) {
case MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN:
reason.append("EXC_ARM_DA_ALIGN");

View file

@ -72,6 +72,7 @@ using google_breakpad::StackFrameSPARC;
using google_breakpad::StackFrameX86;
using google_breakpad::StackFrameAMD64;
using google_breakpad::StackFrameARM;
using google_breakpad::StackFrameARM64;
using google_breakpad::StackFrameMIPS;
// Separator character for machine readable output.
@ -272,6 +273,144 @@ static void PrintStack(const CallStack *stack, const string &cpu) {
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);
} else if (cpu == "arm64") {
const StackFrameARM64 *frame_arm64 =
reinterpret_cast<const StackFrameARM64*>(frame);
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X0) {
sequence =
PrintRegister64("x0", frame_arm64->context.iregs[0], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X1) {
sequence =
PrintRegister64("x1", frame_arm64->context.iregs[1], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X2) {
sequence =
PrintRegister64("x2", frame_arm64->context.iregs[2], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X3) {
sequence =
PrintRegister64("x3", frame_arm64->context.iregs[3], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X4) {
sequence =
PrintRegister64("x4", frame_arm64->context.iregs[4], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X5) {
sequence =
PrintRegister64("x5", frame_arm64->context.iregs[5], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X6) {
sequence =
PrintRegister64("x6", frame_arm64->context.iregs[6], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X7) {
sequence =
PrintRegister64("x7", frame_arm64->context.iregs[7], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X8) {
sequence =
PrintRegister64("x8", frame_arm64->context.iregs[8], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X9) {
sequence =
PrintRegister64("x9", frame_arm64->context.iregs[9], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X10) {
sequence =
PrintRegister64("x10", frame_arm64->context.iregs[10], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X11) {
sequence =
PrintRegister64("x11", frame_arm64->context.iregs[11], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X12) {
sequence =
PrintRegister64("x12", frame_arm64->context.iregs[12], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X13) {
sequence =
PrintRegister64("x13", frame_arm64->context.iregs[13], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X14) {
sequence =
PrintRegister64("x14", frame_arm64->context.iregs[14], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X15) {
sequence =
PrintRegister64("x15", frame_arm64->context.iregs[15], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X16) {
sequence =
PrintRegister64("x16", frame_arm64->context.iregs[16], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X17) {
sequence =
PrintRegister64("x17", frame_arm64->context.iregs[17], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X18) {
sequence =
PrintRegister64("x18", frame_arm64->context.iregs[18], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X19) {
sequence =
PrintRegister64("x19", frame_arm64->context.iregs[19], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X20) {
sequence =
PrintRegister64("x20", frame_arm64->context.iregs[20], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X21) {
sequence =
PrintRegister64("x21", frame_arm64->context.iregs[21], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X22) {
sequence =
PrintRegister64("x22", frame_arm64->context.iregs[22], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X23) {
sequence =
PrintRegister64("x23", frame_arm64->context.iregs[23], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X24) {
sequence =
PrintRegister64("x24", frame_arm64->context.iregs[24], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X25) {
sequence =
PrintRegister64("x25", frame_arm64->context.iregs[25], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X26) {
sequence =
PrintRegister64("x26", frame_arm64->context.iregs[26], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X27) {
sequence =
PrintRegister64("x27", frame_arm64->context.iregs[27], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_X28) {
sequence =
PrintRegister64("x28", frame_arm64->context.iregs[28], sequence);
}
// Registers with a dedicated or conventional purpose.
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_FP) {
sequence =
PrintRegister64("fp", frame_arm64->context.iregs[29], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_LR) {
sequence =
PrintRegister64("lr", frame_arm64->context.iregs[30], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) {
sequence =
PrintRegister64("sp", frame_arm64->context.iregs[31], sequence);
}
if (frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_PC) {
sequence =
PrintRegister64("pc", frame_arm64->context.iregs[32], sequence);
}
} else if (cpu == "mips") {
const StackFrameMIPS* frame_mips =
reinterpret_cast<const StackFrameMIPS*>(frame);

View file

@ -0,0 +1,79 @@
// Copyright 2013 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// stack_frame_cpu.h: CPU-specific StackFrame extensions.
//
// See google_breakpad/processor/stack_frame_cpu.h for documentation.
//
// Author: Colin Blundell
#include "google_breakpad/processor/stack_frame_cpu.h"
namespace google_breakpad {
const uint64_t StackFrameARM64::CONTEXT_VALID_X0;
const uint64_t StackFrameARM64::CONTEXT_VALID_X1;
const uint64_t StackFrameARM64::CONTEXT_VALID_X2;
const uint64_t StackFrameARM64::CONTEXT_VALID_X3;
const uint64_t StackFrameARM64::CONTEXT_VALID_X4;
const uint64_t StackFrameARM64::CONTEXT_VALID_X5;
const uint64_t StackFrameARM64::CONTEXT_VALID_X6;
const uint64_t StackFrameARM64::CONTEXT_VALID_X7;
const uint64_t StackFrameARM64::CONTEXT_VALID_X8;
const uint64_t StackFrameARM64::CONTEXT_VALID_X9;
const uint64_t StackFrameARM64::CONTEXT_VALID_X10;
const uint64_t StackFrameARM64::CONTEXT_VALID_X11;
const uint64_t StackFrameARM64::CONTEXT_VALID_X12;
const uint64_t StackFrameARM64::CONTEXT_VALID_X13;
const uint64_t StackFrameARM64::CONTEXT_VALID_X14;
const uint64_t StackFrameARM64::CONTEXT_VALID_X15;
const uint64_t StackFrameARM64::CONTEXT_VALID_X16;
const uint64_t StackFrameARM64::CONTEXT_VALID_X17;
const uint64_t StackFrameARM64::CONTEXT_VALID_X18;
const uint64_t StackFrameARM64::CONTEXT_VALID_X19;
const uint64_t StackFrameARM64::CONTEXT_VALID_X20;
const uint64_t StackFrameARM64::CONTEXT_VALID_X21;
const uint64_t StackFrameARM64::CONTEXT_VALID_X22;
const uint64_t StackFrameARM64::CONTEXT_VALID_X23;
const uint64_t StackFrameARM64::CONTEXT_VALID_X24;
const uint64_t StackFrameARM64::CONTEXT_VALID_X25;
const uint64_t StackFrameARM64::CONTEXT_VALID_X26;
const uint64_t StackFrameARM64::CONTEXT_VALID_X27;
const uint64_t StackFrameARM64::CONTEXT_VALID_X28;
const uint64_t StackFrameARM64::CONTEXT_VALID_X29;
const uint64_t StackFrameARM64::CONTEXT_VALID_X30;
const uint64_t StackFrameARM64::CONTEXT_VALID_X31;
const uint64_t StackFrameARM64::CONTEXT_VALID_X32;
const uint64_t StackFrameARM64::CONTEXT_VALID_FP;
const uint64_t StackFrameARM64::CONTEXT_VALID_LR;
const uint64_t StackFrameARM64::CONTEXT_VALID_SP;
const uint64_t StackFrameARM64::CONTEXT_VALID_PC;
const uint64_t StackFrameARM64::CONTEXT_VALID_ALL;
} // namespace google_breakpad

View file

@ -53,6 +53,7 @@
#include "processor/stackwalker_x86.h"
#include "processor/stackwalker_amd64.h"
#include "processor/stackwalker_arm.h"
#include "processor/stackwalker_arm64.h"
#include "processor/stackwalker_mips.h"
namespace google_breakpad {
@ -239,6 +240,7 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
break;
case MD_CONTEXT_ARM:
{
int fp_register = -1;
if (system_info->os_short == "ios")
fp_register = MD_CONTEXT_ARM_REG_IOS_FP;
@ -249,6 +251,14 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
break;
}
case MD_CONTEXT_ARM64:
cpu_stackwalker = new StackwalkerARM64(system_info,
context->GetContextARM64(),
memory, modules,
frame_symbolizer);
break;
}
BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) <<
", can't choose a stackwalker "
"implementation";

View file

@ -0,0 +1,209 @@
// Copyright (c) 2013 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// stackwalker_arm64.cc: arm64-specific stackwalker.
//
// See stackwalker_arm64.h for documentation.
//
// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy, Colin Blundell
#include <vector>
#include "common/scoped_ptr.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/stackwalker_arm64.h"
namespace google_breakpad {
StackwalkerARM64::StackwalkerARM64(const SystemInfo* system_info,
const MDRawContextARM64* context,
MemoryRegion* memory,
const CodeModules* modules,
StackFrameSymbolizer* resolver_helper)
: Stackwalker(system_info, memory, modules, resolver_helper),
context_(context),
context_frame_validity_(StackFrameARM64::CONTEXT_VALID_ALL) { }
StackFrame* StackwalkerARM64::GetContextFrame() {
if (!context_) {
BPLOG(ERROR) << "Can't get context frame without context";
return NULL;
}
StackFrameARM64* frame = new StackFrameARM64();
// The instruction pointer is stored directly in a register (x32), so pull it
// straight out of the CPU context structure.
frame->context = *context_;
frame->context_validity = context_frame_validity_;
frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC];
return frame;
}
StackFrameARM64* StackwalkerARM64::GetCallerByCFIFrameInfo(
const vector<StackFrame*> &frames,
CFIFrameInfo* cfi_frame_info) {
// Obtaining the stack frame from CFI info is not yet supported for ARM64.
return NULL;
}
StackFrameARM64* StackwalkerARM64::GetCallerByStackScan(
const vector<StackFrame*> &frames) {
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
uint64_t last_sp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
uint64_t caller_sp, caller_pc;
if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc,
frames.size() == 1 /* is_context_frame */)) {
// No plausible return address was found.
return NULL;
}
// ScanForReturnAddress found a reasonable return address. Advance
// %sp to the location above the one where the return address was
// found.
caller_sp += 8;
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameARM64* frame = new StackFrameARM64();
frame->trust = StackFrame::FRAME_TRUST_SCAN;
frame->context = last_frame->context;
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] = caller_pc;
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_SP;
return frame;
}
StackFrameARM64* StackwalkerARM64::GetCallerByFramePointer(
const vector<StackFrame*> &frames) {
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
uint64_t last_fp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP];
uint64_t caller_fp = 0;
if (last_fp && !memory_->GetMemoryAtAddress(last_fp, &caller_fp)) {
BPLOG(ERROR) << "Unable to read caller_fp from last_fp: 0x"
<< std::hex << last_fp;
return NULL;
}
uint64_t caller_lr = 0;
if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 8, &caller_lr)) {
BPLOG(ERROR) << "Unable to read caller_lr from last_fp + 8: 0x"
<< std::hex << (last_fp + 8);
return NULL;
}
uint64_t caller_sp = last_fp ? last_fp + 16 :
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP];
// Create a new stack frame (ownership will be transferred to the caller)
// and fill it in.
StackFrameARM64* frame = new StackFrameARM64();
frame->trust = StackFrame::FRAME_TRUST_FP;
frame->context = last_frame->context;
frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] = caller_fp;
frame->context.iregs[MD_CONTEXT_ARM64_REG_SP] = caller_sp;
frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] =
last_frame->context.iregs[MD_CONTEXT_ARM64_REG_LR];
frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] = caller_lr;
frame->context_validity = StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_LR |
StackFrameARM64::CONTEXT_VALID_FP |
StackFrameARM64::CONTEXT_VALID_SP;
return frame;
}
StackFrame* StackwalkerARM64::GetCallerFrame(const CallStack* stack,
bool stack_scan_allowed) {
if (!memory_ || !stack) {
BPLOG(ERROR) << "Can't get caller frame without memory or stack";
return NULL;
}
const vector<StackFrame*> &frames = *stack->frames();
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
scoped_ptr<StackFrameARM64> frame;
// See if there is DWARF call frame information covering this address.
scoped_ptr<CFIFrameInfo> cfi_frame_info(
frame_symbolizer_->FindCFIFrameInfo(last_frame));
if (cfi_frame_info.get())
frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
// If CFI failed, or there wasn't CFI available, fall back to frame pointer.
if (!frame.get())
frame.reset(GetCallerByFramePointer(frames));
// If everything failed, fall back to stack scanning.
if (stack_scan_allowed && !frame.get())
frame.reset(GetCallerByStackScan(frames));
// If nothing worked, tell the caller.
if (!frame.get())
return NULL;
// An instruction address of zero marks the end of the stack.
if (frame->context.iregs[MD_CONTEXT_ARM64_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_ARM64_REG_SP]
< last_frame->context.iregs[MD_CONTEXT_ARM64_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.
// ARM64 instructions have a uniform 4-byte encoding, so subtracting 4 off
// the return address gets back to the beginning of the call instruction.
// Callers that require the exact return address value may access
// frame->context.iregs[MD_CONTEXT_ARM64_REG_PC].
frame->instruction = frame->context.iregs[MD_CONTEXT_ARM64_REG_PC] - 4;
return frame.release();
}
} // namespace google_breakpad

View file

@ -0,0 +1,102 @@
// -*- mode: C++ -*-
// Copyright (c) 2013 Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// stackwalker_arm64.h: arm64-specific stackwalker.
//
// Provides stack frames given arm64 register context and a memory region
// corresponding to an arm64 stack.
//
// Author: Mark Mentovai, Ted Mielczarek, Colin Blundell
#ifndef PROCESSOR_STACKWALKER_ARM64_H__
#define PROCESSOR_STACKWALKER_ARM64_H__
#include "google_breakpad/common/breakpad_types.h"
#include "google_breakpad/common/minidump_format.h"
#include "google_breakpad/processor/stackwalker.h"
namespace google_breakpad {
class CodeModules;
class StackwalkerARM64 : public Stackwalker {
public:
// context is an arm64 context object that gives access to arm64-specific
// register state corresponding to the innermost called frame to be
// included in the stack. The other arguments are passed directly through
// to the base Stackwalker constructor.
StackwalkerARM64(const SystemInfo* system_info,
const MDRawContextARM64* context,
MemoryRegion* memory,
const CodeModules* modules,
StackFrameSymbolizer* frame_symbolizer);
// 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 arm64 context and stack conventions.
virtual StackFrame* GetContextFrame();
virtual StackFrame* GetCallerFrame(const CallStack* stack,
bool stack_scan_allowed);
// 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.
StackFrameARM64* GetCallerByCFIFrameInfo(const vector<StackFrame*> &frames,
CFIFrameInfo* cfi_frame_info);
// Use the frame pointer. The caller takes ownership of the returned frame.
// Return NULL on failure.
StackFrameARM64* GetCallerByFramePointer(const vector<StackFrame*> &frames);
// Scan the stack for plausible return addresses. The caller takes ownership
// of the returned frame. Return NULL on failure.
StackFrameARM64* GetCallerByStackScan(const vector<StackFrame*> &frames);
// Stores the CPU context corresponding to the youngest stack frame, to
// be returned by GetContextFrame.
const MDRawContextARM64* 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.
uint64_t context_frame_validity_;
};
} // namespace google_breakpad
#endif // PROCESSOR_STACKWALKER_ARM64_H__

View file

@ -0,0 +1,536 @@
// 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_arm64_unittest.cc: Unit tests for StackwalkerARM64 class.
#include <string.h>
#include <string>
#include <vector>
#include "breakpad_googletest_includes.h"
#include "common/test_assembler.h"
#include "common/using_std_string.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/code_module.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_arm64.h"
#include "processor/windows_frame_info.h"
using google_breakpad::BasicSourceLineResolver;
using google_breakpad::CallStack;
using google_breakpad::CodeModule;
using google_breakpad::StackFrameSymbolizer;
using google_breakpad::StackFrame;
using google_breakpad::StackFrameARM64;
using google_breakpad::Stackwalker;
using google_breakpad::StackwalkerARM64;
using google_breakpad::SystemInfo;
using google_breakpad::WindowsFrameInfo;
using google_breakpad::test_assembler::kLittleEndian;
using google_breakpad::test_assembler::Label;
using google_breakpad::test_assembler::Section;
using std::vector;
using testing::_;
using testing::AnyNumber;
using testing::Return;
using testing::SetArgumentPointee;
using testing::Test;
class StackwalkerARM64Fixture {
public:
StackwalkerARM64Fixture()
: 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 an iOS system, since that is the only platform
// for which ARM64 support is currently enabled.
system_info.os = "iOS";
system_info.os_short = "ios";
system_info.cpu = "arm64";
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, GetCStringSymbolData(_, _, _, _, _))
.WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND));
// Avoid GMOCK WARNING "Uninteresting mock function call - returning
// directly" for FreeSymbolData().
EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber());
// Reset max_frames_scanned since it's static.
Stackwalker::set_max_frames_scanned(1024);
}
// Set the Breakpad symbol information that supplier should return for
// MODULE to INFO.
void SetModuleSymbols(MockCodeModule *module, const string &info) {
size_t buffer_size;
char *buffer = supplier.CopySymbolDataAndOwnTheCopy(info, &buffer_size);
EXPECT_CALL(supplier, GetCStringSymbolData(module, &system_info, _, _, _))
.WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer),
SetArgumentPointee<4>(buffer_size),
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(MDRawContextARM64 *raw_context) {
uint8_t x = 173;
for (size_t i = 0; i < sizeof(*raw_context); i++)
reinterpret_cast<uint8_t *>(raw_context)[i] = (x += 17);
}
SystemInfo system_info;
MDRawContextARM64 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 SanityCheck: public StackwalkerARM64Fixture, public Test { };
TEST_F(SanityCheck, NoResolver) {
// Since the context's frame pointer is garbage, the stack walk will end after
// the first frame.
StackFrameSymbolizer frame_symbolizer(NULL, NULL);
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
&frame_symbolizer);
// This should succeed even without a resolver or supplier.
vector<const CodeModule*> modules_without_symbols;
vector<const CodeModule*> modules_with_corrupt_symbols;
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
&modules_with_corrupt_symbols));
ASSERT_EQ(0U, modules_without_symbols.size());
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
class GetContextFrame: public StackwalkerARM64Fixture, public Test { };
// The stackwalker should be able to produce the context frame even
// without stack memory present.
TEST_F(GetContextFrame, NoStackMemory) {
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
StackwalkerARM64 walker(&system_info, &raw_context, NULL, &modules,
&frame_symbolizer);
vector<const CodeModule*> modules_without_symbols;
vector<const CodeModule*> modules_with_corrupt_symbols;
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
&modules_with_corrupt_symbols));
ASSERT_EQ(0U, modules_without_symbols.size());
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
StackFrameARM64 *frame = static_cast<StackFrameARM64 *>(frames->at(0));
// Check that the values from the original raw context made it
// through to the context in the stack frame.
EXPECT_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context)));
}
class GetCallerFrame: public StackwalkerARM64Fixture, public Test { };
TEST_F(GetCallerFrame, ScanWithoutSymbols) {
// When the stack walker resorts to scanning the stack,
// only addresses located within loaded modules are
// considered valid return addresses.
// Force scanning through three frames to ensure that the
// stack pointer is set properly in scan-recovered frames.
stack_section.start() = 0x80000000;
uint64_t return_address1 = 0x50000100;
uint64_t return_address2 = 0x50000900;
Label frame1_sp, frame2_sp;
stack_section
// frame 0
.Append(16, 0) // space
.D64(0x40090000) // junk that's not
.D64(0x60000000) // a return address
.D64(return_address1) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(16, 0) // space
.D64(0xF0000000) // more junk
.D64(0x0000000D)
.D64(return_address2) // actual return address
// frame 2
.Mark(&frame2_sp)
.Append(64, 0); // end of stack
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
&frame_symbolizer);
vector<const CodeModule*> modules_without_symbols;
vector<const CodeModule*> modules_with_corrupt_symbols;
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
&modules_with_corrupt_symbols));
ASSERT_EQ(2U, modules_without_symbols.size());
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
frames = call_stack.frames();
ASSERT_EQ(3U, frames->size());
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_SP),
frame1->context_validity);
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_SP),
frame2->context_validity);
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
}
TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
// During stack scanning, if a potential return address
// is located within a loaded module that has symbols,
// it is only considered a valid return address if it
// lies within a function's bounds.
stack_section.start() = 0x80000000;
uint64_t return_address = 0x50000200;
Label frame1_sp;
stack_section
// frame 0
.Append(16, 0) // space
.D64(0x40090000) // junk that's not
.D64(0x60000000) // a return address
.D64(0x40001000) // a couple of plausible addresses
.D64(0x5000F000) // that are not within functions
.D64(return_address) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(64, 0); // end of stack
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40000200;
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
SetModuleSymbols(&module1,
// The youngest frame's function.
"FUNC 100 400 10 monotreme\n");
SetModuleSymbols(&module2,
// The calling frame's function.
"FUNC 100 400 10 marsupial\n");
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
&frame_symbolizer);
vector<const CodeModule*> modules_without_symbols;
vector<const CodeModule*> modules_with_corrupt_symbols;
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
&modules_with_corrupt_symbols));
ASSERT_EQ(0U, modules_without_symbols.size());
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
EXPECT_EQ("monotreme", frame0->function_name);
EXPECT_EQ(0x40000100ULL, frame0->function_base);
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_SP),
frame1->context_validity);
EXPECT_EQ(return_address, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
EXPECT_EQ("marsupial", frame1->function_name);
EXPECT_EQ(0x50000100ULL, frame1->function_base);
}
TEST_F(GetCallerFrame, ScanFirstFrame) {
// If the stackwalker resorts to stack scanning, it will scan much
// farther to find the caller of the context frame.
stack_section.start() = 0x80000000;
uint64_t return_address1 = 0x50000100;
uint64_t return_address2 = 0x50000900;
Label frame1_sp, frame2_sp;
stack_section
// frame 0
.Append(32, 0) // space
.D64(0x40090000) // junk that's not
.D64(0x60000000) // a return address
.Append(96, 0) // more space
.D64(return_address1) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(32, 0) // space
.D64(0xF0000000) // more junk
.D64(0x0000000D)
.Append(256, 0) // more space
.D64(return_address2) // actual return address
// (won't be found)
// frame 2
.Mark(&frame2_sp)
.Append(64, 0); // end of stack
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
&frame_symbolizer);
vector<const CodeModule*> modules_without_symbols;
vector<const CodeModule*> modules_with_corrupt_symbols;
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
&modules_with_corrupt_symbols));
ASSERT_EQ(2U, modules_without_symbols.size());
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
frames = call_stack.frames();
ASSERT_EQ(2U, frames->size());
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_SP),
frame1->context_validity);
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
}
// Test that set_max_frames_scanned prevents using stack scanning
// to find caller frames.
TEST_F(GetCallerFrame, ScanningNotAllowed) {
// When the stack walker resorts to scanning the stack,
// only addresses located within loaded modules are
// considered valid return addresses.
stack_section.start() = 0x80000000;
uint64_t return_address1 = 0x50000100;
uint64_t return_address2 = 0x50000900;
Label frame1_sp, frame2_sp;
stack_section
// frame 0
.Append(16, 0) // space
.D64(0x40090000) // junk that's not
.D64(0x60000000) // a return address
.D64(return_address1) // actual return address
// frame 1
.Mark(&frame1_sp)
.Append(16, 0) // space
.D64(0xF0000000) // more junk
.D64(0x0000000D)
.D64(return_address2) // actual return address
// frame 2
.Mark(&frame2_sp)
.Append(64, 0); // end of stack
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
StackwalkerARM64 walker(&system_info, &raw_context, &stack_region, &modules,
&frame_symbolizer);
Stackwalker::set_max_frames_scanned(0);
vector<const CodeModule*> modules_without_symbols;
vector<const CodeModule*> modules_with_corrupt_symbols;
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
&modules_with_corrupt_symbols));
ASSERT_EQ(1U, modules_without_symbols.size());
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
frames = call_stack.frames();
ASSERT_EQ(1U, frames->size());
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
}
class GetFramesByFramePointer: public StackwalkerARM64Fixture, public Test { };
TEST_F(GetFramesByFramePointer, OnlyFramePointer) {
stack_section.start() = 0x80000000;
uint64_t return_address1 = 0x50000100;
uint64_t return_address2 = 0x50000900;
Label frame1_sp, frame2_sp;
Label frame1_fp, frame2_fp;
stack_section
// frame 0
.Append(64, 0) // Whatever values on the stack.
.D64(0x0000000D) // junk that's not
.D64(0xF0000000) // a return address.
.Mark(&frame1_fp) // Next fp will point to the next value.
.D64(frame2_fp) // Save current frame pointer.
.D64(return_address2) // Save current link register.
.Mark(&frame1_sp)
// frame 1
.Append(64, 0) // Whatever values on the stack.
.D64(0x0000000D) // junk that's not
.D64(0xF0000000) // a return address.
.Mark(&frame2_fp)
.D64(0)
.D64(0)
.Mark(&frame2_sp)
// frame 2
.Append(64, 0) // Whatever values on the stack.
.D64(0x0000000D) // junk that's not
.D64(0xF0000000); // a return address.
RegionFromSection();
raw_context.iregs[MD_CONTEXT_ARM64_REG_PC] = 0x40005510;
raw_context.iregs[MD_CONTEXT_ARM64_REG_LR] = return_address1;
raw_context.iregs[MD_CONTEXT_ARM64_REG_FP] = frame1_fp.Value();
raw_context.iregs[MD_CONTEXT_ARM64_REG_SP] = stack_section.start().Value();
StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
StackwalkerARM64 walker(&system_info, &raw_context,
&stack_region, &modules, &frame_symbolizer);
vector<const CodeModule*> modules_without_symbols;
vector<const CodeModule*> modules_with_corrupt_symbols;
ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
&modules_with_corrupt_symbols));
ASSERT_EQ(2U, modules_without_symbols.size());
ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
frames = call_stack.frames();
ASSERT_EQ(3U, frames->size());
StackFrameARM64 *frame0 = static_cast<StackFrameARM64 *>(frames->at(0));
EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
ASSERT_EQ(StackFrameARM64::CONTEXT_VALID_ALL,
frame0->context_validity);
EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
StackFrameARM64 *frame1 = static_cast<StackFrameARM64 *>(frames->at(1));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust);
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_LR |
StackFrameARM64::CONTEXT_VALID_FP |
StackFrameARM64::CONTEXT_VALID_SP),
frame1->context_validity);
EXPECT_EQ(return_address1, frame1->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
EXPECT_EQ(return_address2, frame1->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
EXPECT_EQ(frame2_fp.Value(),
frame1->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
StackFrameARM64 *frame2 = static_cast<StackFrameARM64 *>(frames->at(2));
EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust);
ASSERT_EQ((StackFrameARM64::CONTEXT_VALID_PC |
StackFrameARM64::CONTEXT_VALID_LR |
StackFrameARM64::CONTEXT_VALID_FP |
StackFrameARM64::CONTEXT_VALID_SP),
frame2->context_validity);
EXPECT_EQ(return_address2, frame2->context.iregs[MD_CONTEXT_ARM64_REG_PC]);
EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_LR]);
EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_ARM64_REG_SP]);
EXPECT_EQ(0U, frame2->context.iregs[MD_CONTEXT_ARM64_REG_FP]);
}