diff --git a/Makefile.am b/Makefile.am index a1c366a2..938bd959 100644 --- a/Makefile.am +++ b/Makefile.am @@ -139,6 +139,7 @@ check_PROGRAMS = \ src/processor/pathname_stripper_unittest \ src/processor/postfix_evaluator_unittest \ src/processor/range_map_unittest \ + src/processor/stackwalker_arm_unittest \ src/processor/stackwalker_x86_unittest \ src/processor/synth_minidump_unittest \ src/processor/test_assembler_unittest @@ -302,6 +303,20 @@ src_processor_stackwalker_selftest_LDADD = \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo +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 \ diff --git a/Makefile.in b/Makefile.in index 0126cdf2..048b22ca 100644 --- a/Makefile.in +++ b/Makefile.in @@ -79,6 +79,7 @@ check_PROGRAMS = src/client/linux/linux_client_unittest$(EXEEXT) \ src/processor/pathname_stripper_unittest$(EXEEXT) \ src/processor/postfix_evaluator_unittest$(EXEEXT) \ src/processor/range_map_unittest$(EXEEXT) \ + src/processor/stackwalker_arm_unittest$(EXEEXT) \ src/processor/stackwalker_x86_unittest$(EXEEXT) \ src/processor/synth_minidump_unittest$(EXEEXT) \ src/processor/test_assembler_unittest$(EXEEXT) $(am__EXEEXT_1) @@ -280,6 +281,15 @@ src_processor_range_map_unittest_OBJECTS = \ $(am_src_processor_range_map_unittest_OBJECTS) src_processor_range_map_unittest_DEPENDENCIES = \ src/processor/logging.lo src/processor/pathname_stripper.lo +am_src_processor_stackwalker_arm_unittest_OBJECTS = src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.$(OBJEXT) \ + src/processor/src_processor_stackwalker_arm_unittest-test_assembler.$(OBJEXT) \ + src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.$(OBJEXT) \ + src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.$(OBJEXT) \ + src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.$(OBJEXT) +src_processor_stackwalker_arm_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_arm_unittest_OBJECTS) +src_processor_stackwalker_arm_unittest_DEPENDENCIES = \ + src/libbreakpad.la am_src_processor_stackwalker_selftest_OBJECTS = \ src/processor/stackwalker_selftest.$(OBJEXT) src_processor_stackwalker_selftest_OBJECTS = \ @@ -359,6 +369,7 @@ SOURCES = $(src_client_linux_libbreakpad_client_la_SOURCES) \ $(src_processor_pathname_stripper_unittest_SOURCES) \ $(src_processor_postfix_evaluator_unittest_SOURCES) \ $(src_processor_range_map_unittest_SOURCES) \ + $(src_processor_stackwalker_arm_unittest_SOURCES) \ $(src_processor_stackwalker_selftest_SOURCES) \ $(src_processor_stackwalker_x86_unittest_SOURCES) \ $(src_processor_synth_minidump_unittest_SOURCES) \ @@ -378,6 +389,7 @@ DIST_SOURCES = $(src_client_linux_libbreakpad_client_la_SOURCES) \ $(src_processor_pathname_stripper_unittest_SOURCES) \ $(src_processor_postfix_evaluator_unittest_SOURCES) \ $(src_processor_range_map_unittest_SOURCES) \ + $(src_processor_stackwalker_arm_unittest_SOURCES) \ $(src_processor_stackwalker_selftest_SOURCES) \ $(src_processor_stackwalker_x86_unittest_SOURCES) \ $(src_processor_synth_minidump_unittest_SOURCES) \ @@ -770,6 +782,22 @@ src_processor_stackwalker_selftest_LDADD = \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo +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 \ @@ -1375,6 +1403,24 @@ src/processor/range_map_unittest.$(OBJEXT): \ src/processor/range_map_unittest$(EXEEXT): $(src_processor_range_map_unittest_OBJECTS) $(src_processor_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) @rm -f src/processor/range_map_unittest$(EXEEXT) $(CXXLINK) $(src_processor_range_map_unittest_OBJECTS) $(src_processor_range_map_unittest_LDADD) $(LIBS) +src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/src_processor_stackwalker_arm_unittest-test_assembler.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.$(OBJEXT): \ + src/testing/gtest/src/$(am__dirstamp) \ + src/testing/gtest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.$(OBJEXT): \ + src/testing/gtest/src/$(am__dirstamp) \ + src/testing/gtest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.$(OBJEXT): \ + src/testing/src/$(am__dirstamp) \ + src/testing/src/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_arm_unittest$(EXEEXT): $(src_processor_stackwalker_arm_unittest_OBJECTS) $(src_processor_stackwalker_arm_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_arm_unittest$(EXEEXT) + $(CXXLINK) $(src_processor_stackwalker_arm_unittest_OBJECTS) $(src_processor_stackwalker_arm_unittest_LDADD) $(LIBS) src/processor/stackwalker_selftest.$(OBJEXT): \ src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) @@ -1500,6 +1546,8 @@ mostlyclean-compile: -rm -f src/processor/src_processor_minidump_unittest-minidump_unittest.$(OBJEXT) -rm -f src/processor/src_processor_minidump_unittest-synth_minidump.$(OBJEXT) -rm -f src/processor/src_processor_minidump_unittest-test_assembler.$(OBJEXT) + -rm -f src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.$(OBJEXT) + -rm -f src/processor/src_processor_stackwalker_arm_unittest-test_assembler.$(OBJEXT) -rm -f src/processor/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.$(OBJEXT) -rm -f src/processor/src_processor_stackwalker_x86_unittest-test_assembler.$(OBJEXT) -rm -f src/processor/src_processor_synth_minidump_unittest-synth_minidump.$(OBJEXT) @@ -1527,6 +1575,8 @@ mostlyclean-compile: -rm -f src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.$(OBJEXT) -rm -f src/testing/gtest/src/src_processor_minidump_unittest-gtest-all.$(OBJEXT) -rm -f src/testing/gtest/src/src_processor_minidump_unittest-gtest_main.$(OBJEXT) + -rm -f src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.$(OBJEXT) + -rm -f src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.$(OBJEXT) -rm -f src/testing/gtest/src/src_processor_stackwalker_x86_unittest-gtest-all.$(OBJEXT) -rm -f src/testing/gtest/src/src_processor_stackwalker_x86_unittest-gtest_main.$(OBJEXT) -rm -f src/testing/gtest/src/src_processor_synth_minidump_unittest-gtest-all.$(OBJEXT) @@ -1537,6 +1587,7 @@ mostlyclean-compile: -rm -f src/testing/src/src_processor_cfi_frame_info_unittest-gmock-all.$(OBJEXT) -rm -f src/testing/src/src_processor_minidump_processor_unittest-gmock-all.$(OBJEXT) -rm -f src/testing/src/src_processor_minidump_unittest-gmock-all.$(OBJEXT) + -rm -f src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.$(OBJEXT) -rm -f src/testing/src/src_processor_stackwalker_x86_unittest-gmock-all.$(OBJEXT) -rm -f src/testing/src/src_processor_synth_minidump_unittest-gmock-all.$(OBJEXT) -rm -f src/testing/src/src_processor_test_assembler_unittest-gmock-all.$(OBJEXT) @@ -1583,6 +1634,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_minidump_unittest-minidump_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_minidump_unittest-synth_minidump.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_minidump_unittest-test_assembler.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_stackwalker_x86_unittest-test_assembler.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_synth_minidump_unittest-synth_minidump.Po@am__quote@ @@ -1604,6 +1657,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_unittest-gtest-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_unittest-gtest_main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest-all.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest_main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_x86_unittest-gtest-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_x86_unittest-gtest_main.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_synth_minidump_unittest-gtest-all.Po@am__quote@ @@ -1614,6 +1669,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_cfi_frame_info_unittest-gmock-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_minidump_unittest-gmock-all.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gmock-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_stackwalker_x86_unittest-gmock-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_synth_minidump_unittest-gmock-all.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_test_assembler_unittest-gmock-all.Po@am__quote@ @@ -1974,6 +2030,76 @@ src/testing/src/src_processor_minidump_unittest-gmock-all.obj: src/testing/src/g @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/src/src_processor_minidump_unittest-gmock-all.obj `if test -f 'src/testing/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/src/gmock-all.cc'; fi` +src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.o: src/processor/stackwalker_arm_unittest.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo -c -o src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.o `test -f 'src/processor/stackwalker_arm_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_arm_unittest.cc +@am__fastdepCXX_TRUE@ $(am__mv) src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/processor/stackwalker_arm_unittest.cc' object='src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.o `test -f 'src/processor/stackwalker_arm_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_arm_unittest.cc + +src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.obj: src/processor/stackwalker_arm_unittest.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo -c -o src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.obj `if test -f 'src/processor/stackwalker_arm_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_arm_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_arm_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/processor/stackwalker_arm_unittest.cc' object='src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/src_processor_stackwalker_arm_unittest-stackwalker_arm_unittest.obj `if test -f 'src/processor/stackwalker_arm_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_arm_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_arm_unittest.cc'; fi` + +src/processor/src_processor_stackwalker_arm_unittest-test_assembler.o: src/processor/test_assembler.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/src_processor_stackwalker_arm_unittest-test_assembler.o -MD -MP -MF src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Tpo -c -o src/processor/src_processor_stackwalker_arm_unittest-test_assembler.o `test -f 'src/processor/test_assembler.cc' || echo '$(srcdir)/'`src/processor/test_assembler.cc +@am__fastdepCXX_TRUE@ $(am__mv) src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Tpo src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/processor/test_assembler.cc' object='src/processor/src_processor_stackwalker_arm_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/src_processor_stackwalker_arm_unittest-test_assembler.o `test -f 'src/processor/test_assembler.cc' || echo '$(srcdir)/'`src/processor/test_assembler.cc + +src/processor/src_processor_stackwalker_arm_unittest-test_assembler.obj: src/processor/test_assembler.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/src_processor_stackwalker_arm_unittest-test_assembler.obj -MD -MP -MF src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Tpo -c -o src/processor/src_processor_stackwalker_arm_unittest-test_assembler.obj `if test -f 'src/processor/test_assembler.cc'; then $(CYGPATH_W) 'src/processor/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Tpo src/processor/$(DEPDIR)/src_processor_stackwalker_arm_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/processor/test_assembler.cc' object='src/processor/src_processor_stackwalker_arm_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/src_processor_stackwalker_arm_unittest-test_assembler.obj `if test -f 'src/processor/test_assembler.cc'; then $(CYGPATH_W) 'src/processor/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/test_assembler.cc'; fi` + +src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.o: src/testing/gtest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.o -MD -MP -MF src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest-all.Tpo -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.o `test -f 'src/testing/gtest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/gtest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(am__mv) src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest-all.Tpo src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/gtest/src/gtest-all.cc' object='src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.o `test -f 'src/testing/gtest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/gtest/src/gtest-all.cc + +src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.obj: src/testing/gtest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.obj -MD -MP -MF src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest-all.Tpo -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.obj `if test -f 'src/testing/gtest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/gtest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/gtest/src/gtest-all.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest-all.Tpo src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/gtest/src/gtest-all.cc' object='src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest-all.obj `if test -f 'src/testing/gtest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/gtest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/gtest/src/gtest-all.cc'; fi` + +src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.o: src/testing/gtest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.o -MD -MP -MF src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest_main.Tpo -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.o `test -f 'src/testing/gtest/src/gtest_main.cc' || echo '$(srcdir)/'`src/testing/gtest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(am__mv) src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest_main.Tpo src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/gtest/src/gtest_main.cc' object='src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.o `test -f 'src/testing/gtest/src/gtest_main.cc' || echo '$(srcdir)/'`src/testing/gtest/src/gtest_main.cc + +src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.obj: src/testing/gtest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.obj -MD -MP -MF src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest_main.Tpo -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.obj `if test -f 'src/testing/gtest/src/gtest_main.cc'; then $(CYGPATH_W) 'src/testing/gtest/src/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/gtest/src/gtest_main.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest_main.Tpo src/testing/gtest/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/gtest/src/gtest_main.cc' object='src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/gtest/src/src_processor_stackwalker_arm_unittest-gtest_main.obj `if test -f 'src/testing/gtest/src/gtest_main.cc'; then $(CYGPATH_W) 'src/testing/gtest/src/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/gtest/src/gtest_main.cc'; fi` + +src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.o: src/testing/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.o -MD -MP -MF src/testing/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gmock-all.Tpo -c -o src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.o `test -f 'src/testing/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(am__mv) src/testing/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gmock-all.Tpo src/testing/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/src/gmock-all.cc' object='src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.o `test -f 'src/testing/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/src/gmock-all.cc + +src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.obj: src/testing/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.obj -MD -MP -MF src/testing/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gmock-all.Tpo -c -o src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.obj `if test -f 'src/testing/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/src/gmock-all.cc'; fi` +@am__fastdepCXX_TRUE@ $(am__mv) src/testing/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gmock-all.Tpo src/testing/src/$(DEPDIR)/src_processor_stackwalker_arm_unittest-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/src/gmock-all.cc' object='src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/src/src_processor_stackwalker_arm_unittest-gmock-all.obj `if test -f 'src/testing/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/src/gmock-all.cc'; fi` + src/processor/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.o: src/processor/stackwalker_x86_unittest.cc @am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.Tpo -c -o src/processor/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.o `test -f 'src/processor/stackwalker_x86_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_x86_unittest.cc @am__fastdepCXX_TRUE@ $(am__mv) src/processor/$(DEPDIR)/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.Tpo src/processor/$(DEPDIR)/src_processor_stackwalker_x86_unittest-stackwalker_x86_unittest.Po diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 3df31372..3d3e75e0 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -255,16 +255,19 @@ static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header, 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_X86_64: - name_table = x86_64_names; - break; - + 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; } diff --git a/src/google_breakpad/common/minidump_cpu_arm.h b/src/google_breakpad/common/minidump_cpu_arm.h index bd3d934f..14d81460 100644 --- a/src/google_breakpad/common/minidump_cpu_arm.h +++ b/src/google_breakpad/common/minidump_cpu_arm.h @@ -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) diff --git a/src/google_breakpad/processor/stack_frame_cpu.h b/src/google_breakpad/processor/stack_frame_cpu.h index 8b88fdc6..c5bddffb 100644 --- a/src/google_breakpad/processor/stack_frame_cpu.h +++ b/src/google_breakpad/processor/stack_frame_cpu.h @@ -189,29 +189,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; }; diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc index 1d0a913a..9fe3b547 100644 --- a/src/processor/minidump_stackwalk.cc +++ b/src/processor/minidump_stackwalk.cc @@ -216,12 +216,31 @@ static void PrintStack(const CallStack *stack, const string &cpu) { const StackFrameARM *frame_arm = reinterpret_cast(frame); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R13) - sequence = PrintRegister("r13", frame_arm->context.iregs[13], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R14) - sequence = PrintRegister("r14", frame_arm->context.iregs[14], sequence); - if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R15) - sequence = PrintRegister("r15", frame_arm->context.iregs[15], sequence); + // 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"); } diff --git a/src/processor/stackwalker_arm.cc b/src/processor/stackwalker_arm.cc index 4b15593d..0af27a32 100644 --- a/src/processor/stackwalker_arm.cc +++ b/src/processor/stackwalker_arm.cc @@ -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,7 +68,7 @@ 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; @@ -78,9 +81,104 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) { return NULL; } - // 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; + const vector &frames = *stack->frames(); + StackFrameARM *last_frame = static_cast(frames.back()); + + // See if we have DWARF call frame information covering this address. + scoped_ptr 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 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 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 frame(new StackFrameARM()); + for (int i = 0; register_names[i]; i++) { + CFIFrameInfo::RegisterValueMap::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::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::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(); } diff --git a/src/processor/stackwalker_arm.h b/src/processor/stackwalker_arm.h index 5dfde5ab..3768cc2c 100644 --- a/src/processor/stackwalker_arm.h +++ b/src/processor/stackwalker_arm.h @@ -1,3 +1,5 @@ +// -*- mode: C++ -*- + // Copyright (c) 2010 Google Inc. // All rights reserved. // @@ -60,15 +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); - // 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_; }; diff --git a/src/processor/stackwalker_arm_unittest.cc b/src/processor/stackwalker_arm_unittest.cc new file mode 100644 index 00000000..37103241 --- /dev/null +++ b/src/processor/stackwalker_arm_unittest.cc @@ -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 + +// stackwalker_arm_unittest.cc: Unit tests for StackwalkerARM class. + +#include +#include +#include + +#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(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 *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(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(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(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()); +} +