diff --git a/Makefile.am b/Makefile.am index fd89ea51..281bc522 100644 --- a/Makefile.am +++ b/Makefile.am @@ -296,6 +296,10 @@ src_libbreakpad_a_SOURCES = \ src/processor/stackwalker_ppc.h \ src/processor/stackwalker_ppc64.cc \ src/processor/stackwalker_ppc64.h \ + src/processor/stackwalker_riscv.cc \ + src/processor/stackwalker_riscv.h \ + src/processor/stackwalker_riscv64.cc \ + src/processor/stackwalker_riscv64.h \ src/processor/stackwalker_sparc.cc \ src/processor/stackwalker_sparc.h \ src/processor/stackwalker_x86.cc \ @@ -409,6 +413,8 @@ check_PROGRAMS += \ src/processor/stackwalker_address_list_unittest \ src/processor/stackwalker_mips_unittest \ src/processor/stackwalker_mips64_unittest \ + src/processor/stackwalker_riscv_unittest \ + src/processor/stackwalker_riscv64_unittest \ src/processor/stackwalker_x86_unittest \ src/processor/synth_minidump_unittest endif @@ -893,6 +899,8 @@ src_processor_exploitability_unittest_LDADD = \ src/processor/stackwalker_mips.o \ src/processor/stackwalker_ppc.o \ src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_riscv.o \ + src/processor/stackwalker_riscv64.o \ src/processor/stackwalker_sparc.o \ src/processor/stackwalker_x86.o \ src/processor/symbolic_constants_win.o \ @@ -966,6 +974,8 @@ src_processor_microdump_processor_unittest_LDADD = \ src/processor/stackwalker_mips.o \ src/processor/stackwalker_ppc.o \ src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_riscv.o \ + src/processor/stackwalker_riscv64.o \ src/processor/stackwalker_sparc.o \ src/processor/stackwalker_x86.o \ src/processor/tokenize.o \ @@ -1005,6 +1015,8 @@ src_processor_minidump_processor_unittest_LDADD = \ src/processor/stackwalker_mips.o \ src/processor/stackwalker_ppc.o \ src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_riscv.o \ + src/processor/stackwalker_riscv64.o \ src/processor/stackwalker_sparc.o \ src/processor/stackwalker_x86.o \ src/processor/symbolic_constants_win.o \ @@ -1148,6 +1160,8 @@ src_processor_stackwalker_selftest_LDADD = \ src/processor/stackwalker_mips.o \ src/processor/stackwalker_ppc.o \ src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_riscv.o \ + src/processor/stackwalker_riscv64.o \ src/processor/stackwalker_sparc.o \ src/processor/stackwalker_x86.o \ src/processor/tokenize.o \ @@ -1213,6 +1227,26 @@ src_processor_stackwalker_mips64_unittest_LDADD = \ src_processor_stackwalker_mips64_unittest_CPPFLAGS = \ $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_stackwalker_riscv_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_riscv_unittest.cc +src_processor_stackwalker_riscv_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_riscv_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_stackwalker_riscv64_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_riscv64_unittest.cc +src_processor_stackwalker_riscv64_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_riscv64_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + src_processor_stackwalker_x86_unittest_SOURCES = \ src/common/test_assembler.cc \ src/processor/stackwalker_x86_unittest.cc @@ -1316,6 +1350,8 @@ src_processor_microdump_stackwalk_LDADD = \ src/processor/stackwalker_mips.o \ src/processor/stackwalker_ppc.o \ src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_riscv.o \ + src/processor/stackwalker_riscv64.o \ src/processor/stackwalker_sparc.o \ src/processor/stackwalker_x86.o \ src/processor/tokenize.o \ @@ -1355,6 +1391,8 @@ src_processor_minidump_stackwalk_LDADD = \ src/processor/stackwalker_mips.o \ src/processor/stackwalker_ppc.o \ src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_riscv.o \ + src/processor/stackwalker_riscv64.o \ src/processor/stackwalker_sparc.o \ src/processor/stackwalker_x86.o \ src/processor/symbolic_constants_win.o \ diff --git a/Makefile.in b/Makefile.in index abe7daae..72949882 100644 --- a/Makefile.in +++ b/Makefile.in @@ -199,6 +199,8 @@ EXTRA_PROGRAMS = $(am__EXEEXT_1) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list_unittest \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips_unittest \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips64_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64_unittest \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest \ @DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest @@ -299,6 +301,8 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list_unittest$(EXEEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips_unittest$(EXEEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips64_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64_unittest$(EXEEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest$(EXEEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest$(EXEEXT) @LINUX_HOST_TRUE@am__EXEEXT_6 = src/client/linux/linux_client_unittest$(EXEEXT) \ @@ -496,6 +500,10 @@ am__src_libbreakpad_a_SOURCES_DIST = \ src/processor/stackwalker_ppc.h \ src/processor/stackwalker_ppc64.cc \ src/processor/stackwalker_ppc64.h \ + src/processor/stackwalker_riscv.cc \ + src/processor/stackwalker_riscv.h \ + src/processor/stackwalker_riscv64.cc \ + src/processor/stackwalker_riscv64.h \ src/processor/stackwalker_sparc.cc \ src/processor/stackwalker_sparc.h \ src/processor/stackwalker_x86.cc \ @@ -547,6 +555,8 @@ am__src_libbreakpad_a_SOURCES_DIST = \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.$(OBJEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.$(OBJEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.$(OBJEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.$(OBJEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.$(OBJEXT) \ @DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.$(OBJEXT) \ @@ -994,6 +1004,8 @@ src_processor_exploitability_unittest_OBJECTS = \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ @@ -1058,6 +1070,8 @@ src_processor_microdump_processor_unittest_OBJECTS = \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ @@ -1097,6 +1111,8 @@ src_processor_microdump_stackwalk_OBJECTS = \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ @@ -1150,6 +1166,8 @@ src_processor_minidump_processor_unittest_OBJECTS = \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ @@ -1195,6 +1213,8 @@ src_processor_minidump_stackwalk_OBJECTS = \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ @@ -1358,6 +1378,30 @@ src_processor_stackwalker_mips_unittest_OBJECTS = \ @DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ @DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ @DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_riscv64_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_riscv64_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_riscv64_unittest_OBJECTS = src/common/processor_stackwalker_riscv64_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.$(OBJEXT) +src_processor_stackwalker_riscv64_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_riscv64_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv64_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_riscv_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_riscv_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_riscv_unittest_OBJECTS = src/common/processor_stackwalker_riscv_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.$(OBJEXT) +src_processor_stackwalker_riscv_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_riscv_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) am__src_processor_stackwalker_selftest_SOURCES_DIST = \ src/processor/stackwalker_selftest.cc @DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_selftest_OBJECTS = src/processor/stackwalker_selftest.$(OBJEXT) @@ -1386,6 +1430,8 @@ src_processor_stackwalker_selftest_OBJECTS = \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ @@ -1708,6 +1754,8 @@ am__depfiles_remade = src/client/$(DEPDIR)/minidump_file_writer.Po \ src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po \ src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po \ src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Po \ src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po \ src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po \ src/common/$(DEPDIR)/safe_math_unittest-safe_math_unittest.Po \ @@ -1891,6 +1939,10 @@ am__depfiles_remade = src/client/$(DEPDIR)/minidump_file_writer.Po \ src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po \ src/processor/$(DEPDIR)/stackwalker_ppc.Po \ src/processor/$(DEPDIR)/stackwalker_ppc64.Po \ + src/processor/$(DEPDIR)/stackwalker_riscv.Po \ + src/processor/$(DEPDIR)/stackwalker_riscv64.Po \ + src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Po \ + src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Po \ src/processor/$(DEPDIR)/stackwalker_selftest.Po \ src/processor/$(DEPDIR)/stackwalker_sparc.Po \ src/processor/$(DEPDIR)/stackwalker_x86.Po \ @@ -2008,6 +2060,8 @@ SOURCES = $(src_client_linux_libbreakpad_client_a_SOURCES) \ $(src_processor_stackwalker_arm_unittest_SOURCES) \ $(src_processor_stackwalker_mips64_unittest_SOURCES) \ $(src_processor_stackwalker_mips_unittest_SOURCES) \ + $(src_processor_stackwalker_riscv64_unittest_SOURCES) \ + $(src_processor_stackwalker_riscv_unittest_SOURCES) \ $(src_processor_stackwalker_selftest_SOURCES) \ $(src_processor_stackwalker_x86_unittest_SOURCES) \ $(src_processor_static_address_map_unittest_SOURCES) \ @@ -2065,6 +2119,8 @@ DIST_SOURCES = \ $(am__src_processor_stackwalker_arm_unittest_SOURCES_DIST) \ $(am__src_processor_stackwalker_mips64_unittest_SOURCES_DIST) \ $(am__src_processor_stackwalker_mips_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_riscv64_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_riscv_unittest_SOURCES_DIST) \ $(am__src_processor_stackwalker_selftest_SOURCES_DIST) \ $(am__src_processor_stackwalker_x86_unittest_SOURCES_DIST) \ $(am__src_processor_static_address_map_unittest_SOURCES_DIST) \ @@ -2652,6 +2708,10 @@ CLEANFILES = $(am__append_12) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.h \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.cc \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.h \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.cc \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.h \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.cc \ @@ -3138,6 +3198,8 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ @@ -3219,6 +3281,8 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ @@ -3260,6 +3324,8 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ @@ -3423,6 +3489,8 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ @@ -3500,6 +3568,30 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips64_unittest_CPPFLAGS = \ @DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv64_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv64_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_riscv64_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + @DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_x86_unittest_SOURCES = \ @DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest.cc @@ -3612,6 +3704,8 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ @@ -3652,6 +3746,8 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_riscv64.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ @DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ @@ -4342,6 +4438,12 @@ src/processor/stackwalker_ppc.$(OBJEXT): \ src/processor/stackwalker_ppc64.$(OBJEXT): \ src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_riscv.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_riscv64.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) src/processor/stackwalker_sparc.$(OBJEXT): \ src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) @@ -5048,6 +5150,26 @@ src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.$(OBJEXT): \ src/processor/stackwalker_mips_unittest$(EXEEXT): $(src_processor_stackwalker_mips_unittest_OBJECTS) $(src_processor_stackwalker_mips_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_mips_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) @rm -f src/processor/stackwalker_mips_unittest$(EXEEXT) $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_mips_unittest_OBJECTS) $(src_processor_stackwalker_mips_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_riscv64_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_riscv64_unittest$(EXEEXT): $(src_processor_stackwalker_riscv64_unittest_OBJECTS) $(src_processor_stackwalker_riscv64_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_riscv64_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_riscv64_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_riscv64_unittest_OBJECTS) $(src_processor_stackwalker_riscv64_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_riscv_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_riscv_unittest$(EXEEXT): $(src_processor_stackwalker_riscv_unittest_OBJECTS) $(src_processor_stackwalker_riscv_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_riscv_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_riscv_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_riscv_unittest_OBJECTS) $(src_processor_stackwalker_riscv_unittest_LDADD) $(LIBS) src/processor/stackwalker_selftest.$(OBJEXT): \ src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) @@ -5451,6 +5573,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/safe_math_unittest-safe_math_unittest.Po@am__quote@ # am--include-marker @@ -5634,6 +5758,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_ppc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_ppc64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_riscv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_riscv64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_selftest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_sparc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_x86.Po@am__quote@ # am--include-marker @@ -7704,6 +7832,62 @@ src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.obj: src/proce @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.obj `if test -f 'src/processor/stackwalker_mips_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_mips_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_mips_unittest.cc'; fi` +src/common/processor_stackwalker_riscv64_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_riscv64_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_riscv64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_riscv64_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_riscv64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_riscv64_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_riscv64_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_riscv64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_riscv64_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_riscv64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.o: src/processor/stackwalker_riscv64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Tpo -c -o src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.o `test -f 'src/processor/stackwalker_riscv64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_riscv64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_riscv64_unittest.cc' object='src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.o `test -f 'src/processor/stackwalker_riscv64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_riscv64_unittest.cc + +src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.obj: src/processor/stackwalker_riscv64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Tpo -c -o src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.obj `if test -f 'src/processor/stackwalker_riscv64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_riscv64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_riscv64_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_riscv64_unittest.cc' object='src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.obj `if test -f 'src/processor/stackwalker_riscv64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_riscv64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_riscv64_unittest.cc'; fi` + +src/common/processor_stackwalker_riscv_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_riscv_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_riscv_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_riscv_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_riscv_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_riscv_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_riscv_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_riscv_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_riscv_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_riscv_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.o: src/processor/stackwalker_riscv_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Tpo -c -o src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.o `test -f 'src/processor/stackwalker_riscv_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_riscv_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_riscv_unittest.cc' object='src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.o `test -f 'src/processor/stackwalker_riscv_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_riscv_unittest.cc + +src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.obj: src/processor/stackwalker_riscv_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Tpo -c -o src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.obj `if test -f 'src/processor/stackwalker_riscv_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_riscv_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_riscv_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_riscv_unittest.cc' object='src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_riscv_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_riscv_unittest-stackwalker_riscv_unittest.obj `if test -f 'src/processor/stackwalker_riscv_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_riscv_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_riscv_unittest.cc'; fi` + src/common/processor_stackwalker_x86_unittest-test_assembler.o: src/common/test_assembler.cc @am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_x86_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_x86_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc @am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po @@ -9108,6 +9292,20 @@ src/processor/stackwalker_mips64_unittest.log: src/processor/stackwalker_mips64_ --log-file $$b.log --trs-file $$b.trs \ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_riscv_unittest.log: src/processor/stackwalker_riscv_unittest$(EXEEXT) + @p='src/processor/stackwalker_riscv_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_riscv_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_riscv64_unittest.log: src/processor/stackwalker_riscv64_unittest$(EXEEXT) + @p='src/processor/stackwalker_riscv64_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_riscv64_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) src/processor/stackwalker_x86_unittest.log: src/processor/stackwalker_x86_unittest$(EXEEXT) @p='src/processor/stackwalker_x86_unittest$(EXEEXT)'; \ b='src/processor/stackwalker_x86_unittest'; \ @@ -9558,6 +9756,8 @@ distclean: distclean-am -rm -f src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/safe_math_unittest-safe_math_unittest.Po @@ -9741,6 +9941,10 @@ distclean: distclean-am -rm -f src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po -rm -f src/processor/$(DEPDIR)/stackwalker_ppc.Po -rm -f src/processor/$(DEPDIR)/stackwalker_ppc64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Po -rm -f src/processor/$(DEPDIR)/stackwalker_selftest.Po -rm -f src/processor/$(DEPDIR)/stackwalker_sparc.Po -rm -f src/processor/$(DEPDIR)/stackwalker_x86.Po @@ -9904,6 +10108,8 @@ maintainer-clean: maintainer-clean-am -rm -f src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_riscv64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_riscv_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po -rm -f src/common/$(DEPDIR)/safe_math_unittest-safe_math_unittest.Po @@ -10087,6 +10293,10 @@ maintainer-clean: maintainer-clean-am -rm -f src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po -rm -f src/processor/$(DEPDIR)/stackwalker_ppc.Po -rm -f src/processor/$(DEPDIR)/stackwalker_ppc64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv64_unittest-stackwalker_riscv64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_riscv_unittest-stackwalker_riscv_unittest.Po -rm -f src/processor/$(DEPDIR)/stackwalker_selftest.Po -rm -f src/processor/$(DEPDIR)/stackwalker_sparc.Po -rm -f src/processor/$(DEPDIR)/stackwalker_x86.Po diff --git a/src/client/linux/dump_writer_common/raw_context_cpu.h b/src/client/linux/dump_writer_common/raw_context_cpu.h index 58243dfe..ea4b6f6a 100644 --- a/src/client/linux/dump_writer_common/raw_context_cpu.h +++ b/src/client/linux/dump_writer_common/raw_context_cpu.h @@ -43,6 +43,14 @@ typedef MDRawContextARM RawContextCPU; typedef MDRawContextARM64_Old RawContextCPU; #elif defined(__mips__) typedef MDRawContextMIPS RawContextCPU; +#elif defined(__riscv) +# if __riscv_xlen == 32 +typedef MDRawContextRISCV RawContextCPU; +# elif __riscv_xlen == 64 +typedef MDRawContextRISCV64 RawContextCPU; +# else +# error "Unexpected __riscv_xlen" +# endif #else #error "This code has not been ported to your platform yet." #endif diff --git a/src/client/linux/dump_writer_common/thread_info.cc b/src/client/linux/dump_writer_common/thread_info.cc index 5f32c031..d8bf80b0 100644 --- a/src/client/linux/dump_writer_common/thread_info.cc +++ b/src/client/linux/dump_writer_common/thread_info.cc @@ -269,7 +269,74 @@ void ThreadInfo::FillCPUContext(RawContextCPU* out) const { out->float_save.fir = mcontext.fpc_eir; #endif } -#endif // __mips__ + +#elif defined(__riscv) + +uintptr_t ThreadInfo::GetInstructionPointer() const { + return mcontext.__gregs[0]; +} + +void ThreadInfo::FillCPUContext(RawContextCPU* out) const { +# if __riscv__xlen == 32 + out->context_flags = MD_CONTEXT_RISCV_FULL; +# elif __riscv_xlen == 64 + out->context_flags = MD_CONTEXT_RISCV64_FULL; +# else +# error "Unexpected __riscv_xlen" +# endif + + out->pc = mcontext.__gregs[0]; + out->ra = mcontext.__gregs[1]; + out->sp = mcontext.__gregs[2]; + out->gp = mcontext.__gregs[3]; + out->tp = mcontext.__gregs[4]; + out->t0 = mcontext.__gregs[5]; + out->t1 = mcontext.__gregs[6]; + out->t2 = mcontext.__gregs[7]; + out->s0 = mcontext.__gregs[8]; + out->s1 = mcontext.__gregs[9]; + out->a0 = mcontext.__gregs[10]; + out->a1 = mcontext.__gregs[11]; + out->a2 = mcontext.__gregs[12]; + out->a3 = mcontext.__gregs[13]; + out->a4 = mcontext.__gregs[14]; + out->a5 = mcontext.__gregs[15]; + out->a6 = mcontext.__gregs[16]; + out->a7 = mcontext.__gregs[17]; + out->s2 = mcontext.__gregs[18]; + out->s3 = mcontext.__gregs[19]; + out->s4 = mcontext.__gregs[20]; + out->s5 = mcontext.__gregs[21]; + out->s6 = mcontext.__gregs[22]; + out->s7 = mcontext.__gregs[23]; + out->s8 = mcontext.__gregs[24]; + out->s9 = mcontext.__gregs[25]; + out->s10 = mcontext.__gregs[26]; + out->s11 = mcontext.__gregs[27]; + out->t3 = mcontext.__gregs[28]; + out->t4 = mcontext.__gregs[29]; + out->t5 = mcontext.__gregs[30]; + out->t6 = mcontext.__gregs[31]; + +# if __riscv_flen == 32 + for(int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; i++) + out->float_save.regs[i] = mcontext.__fpregs.__f.__f[i]; + out->float_save.fpcsr = mcontext.__fpregs.__f.__fcsr; +# elif __riscv_flen == 64 + for(int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; i++) + out->float_save.regs[i] = mcontext.__fpregs.__d.__f[i]; + out->float_save.fpcsr = mcontext.__fpregs.__d.__fcsr; +# elif __riscv_flen == 128 + for(int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; i++) { + out->float_save.regs[i].high = mcontext.__fpregs.__q.__f[2*i]; + out->float_save.regs[i].low = mcontext.__fpregs.__q.__f[2*i+1]; + } + out->float_save.fpcsr = mcontext.__fpregs.__q.__fcsr; +# else +# error "Unexpected __riscv_flen" +# endif +} +#endif // __riscv void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) { assert(gp_regs || size); @@ -278,6 +345,11 @@ void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) { *gp_regs = mcontext.gregs; if (size) *size = sizeof(mcontext.gregs); +#elif defined(__riscv) + if (gp_regs) + *gp_regs = mcontext.__gregs; + if (size) + *size = sizeof(mcontext.__gregs); #else if (gp_regs) *gp_regs = ®s; @@ -293,6 +365,25 @@ void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) { *fp_regs = &mcontext.fpregs; if (size) *size = sizeof(mcontext.fpregs); +#elif defined(__riscv) +# if __riscv_flen == 32 + if (fp_regs) + *fp_regs = &mcontext.__fpregs.__f.__f; + if (size) + *size = sizeof(mcontext.__fpregs.__f.__f); +# elif __riscv_flen == 64 + if (fp_regs) + *fp_regs = &mcontext.__fpregs.__d.__f; + if (size) + *size = sizeof(mcontext.__fpregs.__d.__f); +# elif __riscv_flen == 128 + if (fp_regs) + *fp_regs = &mcontext.__fpregs.__q.__f; + if (size) + *size = sizeof(mcontext.__fpregs.__q.__f); +# else +# error "Unexpected __riscv_flen" +# endif #else if (fp_regs) *fp_regs = &fpregs; diff --git a/src/client/linux/dump_writer_common/thread_info.h b/src/client/linux/dump_writer_common/thread_info.h index 9e1b454b..af786bcc 100644 --- a/src/client/linux/dump_writer_common/thread_info.h +++ b/src/client/linux/dump_writer_common/thread_info.h @@ -67,7 +67,7 @@ struct ThreadInfo { // Use the structures defined in struct user_regs_struct regs; struct user_fpsimd_struct fpregs; -#elif defined(__mips__) +#elif defined(__mips__) || defined(__riscv) // Use the structure defined in . mcontext_t mcontext; #endif diff --git a/src/client/linux/dump_writer_common/ucontext_reader.cc b/src/client/linux/dump_writer_common/ucontext_reader.cc index 7de82457..97ed2a9f 100644 --- a/src/client/linux/dump_writer_common/ucontext_reader.cc +++ b/src/client/linux/dump_writer_common/ucontext_reader.cc @@ -253,6 +253,75 @@ void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc) { out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused. #endif } + +#elif defined(__riscv) + +uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { + return uc->uc_mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]; +} + +uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { + return uc->uc_mcontext.__gregs[MD_CONTEXT_RISCV_REG_PC]; +} + +void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc) { +# if __riscv__xlen == 32 + out->context_flags = MD_CONTEXT_RISCV_FULL; +# elif __riscv_xlen == 64 + out->context_flags = MD_CONTEXT_RISCV64_FULL; +# else +# error "Unexpected __riscv_xlen" +# endif + + out->pc = uc->uc_mcontext.__gregs[0]; + out->ra = uc->uc_mcontext.__gregs[1]; + out->sp = uc->uc_mcontext.__gregs[2]; + out->gp = uc->uc_mcontext.__gregs[3]; + out->tp = uc->uc_mcontext.__gregs[4]; + out->t0 = uc->uc_mcontext.__gregs[5]; + out->t1 = uc->uc_mcontext.__gregs[6]; + out->t2 = uc->uc_mcontext.__gregs[7]; + out->s0 = uc->uc_mcontext.__gregs[8]; + out->s1 = uc->uc_mcontext.__gregs[9]; + out->a0 = uc->uc_mcontext.__gregs[10]; + out->a1 = uc->uc_mcontext.__gregs[11]; + out->a2 = uc->uc_mcontext.__gregs[12]; + out->a3 = uc->uc_mcontext.__gregs[13]; + out->a4 = uc->uc_mcontext.__gregs[14]; + out->a5 = uc->uc_mcontext.__gregs[15]; + out->a6 = uc->uc_mcontext.__gregs[16]; + out->a7 = uc->uc_mcontext.__gregs[17]; + out->s2 = uc->uc_mcontext.__gregs[18]; + out->s3 = uc->uc_mcontext.__gregs[19]; + out->s4 = uc->uc_mcontext.__gregs[20]; + out->s5 = uc->uc_mcontext.__gregs[21]; + out->s6 = uc->uc_mcontext.__gregs[22]; + out->s7 = uc->uc_mcontext.__gregs[23]; + out->s8 = uc->uc_mcontext.__gregs[24]; + out->s9 = uc->uc_mcontext.__gregs[25]; + out->s10 = uc->uc_mcontext.__gregs[26]; + out->s11 = uc->uc_mcontext.__gregs[27]; + out->t3 = uc->uc_mcontext.__gregs[28]; + out->t4 = uc->uc_mcontext.__gregs[29]; + out->t5 = uc->uc_mcontext.__gregs[30]; + out->t6 = uc->uc_mcontext.__gregs[31]; + +# if __riscv_flen == 32 + for(int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; i++) + out->float_save.regs[i] = uc->uc_mcontext.__fpregs.__f.__f[i]; + out->float_save.fpcsr = uc->uc_mcontext.__fpregs.__f.__fcsr; +# elif __riscv_flen == 64 + for(int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; i++) + out->float_save.regs[i] = uc->uc_mcontext.__fpregs.__d.__f[i]; + out->float_save.fpcsr = uc->uc_mcontext.__fpregs.__d.__fcsr; +# elif __riscv_flen == 128 + for(int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; i++) { + out->float_save.regs[i].high = uc->uc_mcontext.__fpregs.__q.__f[2*i]; + out->float_save.regs[i].low = uc->uc_mcontext.__fpregs.__q.__f[2*i+1]; + } + out->float_save.fpcsr = uc->uc_mcontext.__fpregs.__q.__fcsr; +# endif +} #endif } // namespace google_breakpad diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc index 36de9be3..bbdb798b 100644 --- a/src/client/linux/handler/exception_handler.cc +++ b/src/client/linux/handler/exception_handler.cc @@ -461,9 +461,6 @@ bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) { sizeof(g_crash_context_.float_state)); } #elif GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE - // FP state is not part of user ABI on ARM Linux. - // In case of MIPS Linux FP state is already part of ucontext_t - // and 'float_state' is not a member of CrashContext. ucontext_t* uc_ptr = (ucontext_t*)uc; if (uc_ptr->uc_mcontext.fpregs) { memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs, @@ -701,7 +698,6 @@ bool ExceptionHandler::WriteMinidump() { #endif #if GOOGLE_BREAKPAD_CRASH_CONTEXT_HAS_FLOAT_STATE && !defined(__aarch64__) - // FPU state is not part of ARM EABI ucontext_t. memcpy(&context.float_state, context.context.uc_mcontext.fpregs, sizeof(context.float_state)); #endif @@ -725,8 +721,11 @@ bool ExceptionHandler::WriteMinidump() { #elif defined(__mips__) context.siginfo.si_addr = reinterpret_cast(context.context.uc_mcontext.pc); +#elif defined(__riscv) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.__gregs[REG_PC]); #else -#error "This code has not been ported to your platform yet." +# error "This code has not been ported to your platform yet." #endif return GenerateDump(&context); diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h index 67041548..f8bc1ead 100644 --- a/src/client/linux/handler/exception_handler.h +++ b/src/client/linux/handler/exception_handler.h @@ -43,7 +43,7 @@ #include "common/using_std_string.h" #include "google_breakpad/common/minidump_format.h" -#if !defined(__ARM_EABI__) && !defined(__mips__) +#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__riscv) // FP state is not part of user ABI for Linux ARM. // In case of MIPS and RISCV Linux FP state is already part of ucontext_t // so 'float_state' is not required. diff --git a/src/client/linux/microdump_writer/microdump_writer.cc b/src/client/linux/microdump_writer/microdump_writer.cc index ea161b6d..1f19d3bb 100644 --- a/src/client/linux/microdump_writer/microdump_writer.cc +++ b/src/client/linux/microdump_writer/microdump_writer.cc @@ -335,9 +335,17 @@ class MicrodumpWriter { const char kArch[] = "mips64"; # else # error "This mips ABI is currently not supported (n32)" -#endif +# endif +#elif defined(__riscv) +# if __riscv_xlen == 32 + const char kArch[] = "riscv32"; +# elif __riscv_xlen == 64 + const char kArch[] = "riscv64"; +# else +# error "Unexpected __riscv_xlen" +# endif #else -#error "This code has not been ported to your platform yet" +# error "This code has not been ported to your platform yet" #endif LogAppend("O "); diff --git a/src/client/linux/minidump_writer/linux_core_dumper.cc b/src/client/linux/minidump_writer/linux_core_dumper.cc index 9420e051..2c507c1b 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper.cc +++ b/src/client/linux/minidump_writer/linux_core_dumper.cc @@ -111,8 +111,11 @@ bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { #elif defined(__mips__) stack_pointer = reinterpret_cast(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); +#elif defined(__riscv) + stack_pointer = reinterpret_cast( + info->mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]); #else -#error "This code hasn't been ported to your platform yet." +# error "This code hasn't been ported to your platform yet." #endif info->stack_pointer = reinterpret_cast(stack_pointer); return true; @@ -207,19 +210,22 @@ bool LinuxCoreDumper::EnumerateThreads() { info.tgid = status->pr_pgrp; info.ppid = status->pr_ppid; #if defined(__mips__) -#if defined(__ANDROID__) +# if defined(__ANDROID__) for (int i = EF_R0; i <= EF_R31; i++) info.mcontext.gregs[i - EF_R0] = status->pr_reg[i]; -#else // __ANDROID__ +# else // __ANDROID__ for (int i = EF_REG0; i <= EF_REG31; i++) info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i]; -#endif // __ANDROID__ +# endif // __ANDROID__ info.mcontext.mdlo = status->pr_reg[EF_LO]; info.mcontext.mdhi = status->pr_reg[EF_HI]; info.mcontext.pc = status->pr_reg[EF_CP0_EPC]; -#else // __mips__ +#elif defined(__riscv) + memcpy(&info.mcontext.__gregs, status->pr_reg, + sizeof(info.mcontext.__gregs)); +#else // __riscv memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); -#endif // __mips__ +#endif if (first_thread) { crash_thread_ = pid; crash_signal_ = status->pr_info.si_signo; diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index db8f36ee..2d5b2e52 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -59,10 +59,12 @@ namespace google_breakpad { // Typedef for our parsing of the auxv variables in /proc/pid/auxv. #if defined(__i386) || defined(__ARM_EABI__) || \ - (defined(__mips__) && _MIPS_SIM == _ABIO32) + (defined(__mips__) && _MIPS_SIM == _ABIO32) || \ + (defined(__riscv) && __riscv_xlen == 32) typedef Elf32_auxv_t elf_aux_entry; #elif defined(__x86_64) || defined(__aarch64__) || \ - (defined(__mips__) && _MIPS_SIM != _ABIO32) + (defined(__mips__) && _MIPS_SIM != _ABIO32) || \ + (defined(__riscv) && __riscv_xlen == 64) typedef Elf64_auxv_t elf_aux_entry; #endif diff --git a/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc index 1cd86faf..bc1e4fbe 100644 --- a/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +++ b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc @@ -50,6 +50,8 @@ #define TID_PTR_REGISTER "rcx" #elif defined(__mips__) #define TID_PTR_REGISTER "$1" +#elif defined(__riscv) +#define TID_PTR_REGISTER "x4" #else #error This test has not been ported to this platform. #endif diff --git a/src/client/linux/minidump_writer/linux_ptrace_dumper.cc b/src/client/linux/minidump_writer/linux_ptrace_dumper.cc index 28d77b8a..718fab7c 100644 --- a/src/client/linux/minidump_writer/linux_ptrace_dumper.cc +++ b/src/client/linux/minidump_writer/linux_ptrace_dumper.cc @@ -297,8 +297,11 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { #elif defined(__mips__) stack_pointer = reinterpret_cast(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); +#elif defined(__riscv) + stack_pointer = reinterpret_cast( + info->mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP]); #else -#error "This code hasn't been ported to your platform yet." +# error "This code hasn't been ported to your platform yet." #endif info->stack_pointer = reinterpret_cast(stack_pointer); diff --git a/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc index 7e760e99..a8455165 100644 --- a/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc +++ b/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc @@ -463,6 +463,9 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { #elif defined(__mips__) pid_t* process_tid_location = reinterpret_cast(one_thread.mcontext.gregs[1]); +#elif defined(__riscv) + pid_t* process_tid_location = + reinterpret_cast(one_thread.mcontext.__gregs[4]); #else #error This test has not been ported to this platform. #endif @@ -560,6 +563,8 @@ TEST_F(LinuxPtraceDumperTest, SanitizeStackCopy) { uintptr_t heap_addr = thread_info.regs.rcx; #elif defined(__mips__) uintptr_t heap_addr = thread_info.mcontext.gregs[1]; +#elif defined(__riscv) + uintptr_t heap_addr = thread_info.mcontext.__gregs[4]; #else #error This test has not been ported to this platform. #endif diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index d722be94..a5f9b841 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -1137,9 +1137,7 @@ class MinidumpWriter { sys_close(fd); cpus_present.IntersectWith(cpus_possible); - int cpu_count = cpus_present.GetCount(); - if (cpu_count > 255) - cpu_count = 255; + int cpu_count = std::min(255, cpus_present.GetCount()); sys_info->number_of_processors = static_cast(cpu_count); } } @@ -1255,6 +1253,59 @@ class MinidumpWriter { sys_close(fd); } + return true; + } +#elif defined(__riscv) + bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + // processor_architecture should always be set, do this first +# if __riscv_xlen == 32 + sys_info->processor_architecture = MD_CPU_ARCHITECTURE_RISCV; +# elif __riscv_xlen == 64 + sys_info->processor_architecture = MD_CPU_ARCHITECTURE_RISCV64; +# else +# error "Unexpected __riscv_xlen" +# endif + + // /proc/cpuinfo is not readable under various sandboxed environments + // (e.g. Android services with the android:isolatedProcess attribute) + // prepare for this by setting default values now, which will be + // returned when this happens. + // + // Note: Bogus values are used to distinguish between failures (to + // read /sys and /proc files) and really badly configured kernels. + sys_info->number_of_processors = 0; + sys_info->processor_level = 0U; + sys_info->processor_revision = 42; + sys_info->cpu.other_cpu_info.processor_features[0] = 0; + sys_info->cpu.other_cpu_info.processor_features[1] = 0; + + // Counting the number of CPUs involves parsing two sysfs files, + // because the content of /proc/cpuinfo will only mirror the number + // of 'online' cores, and thus will vary with time. + // See http://www.kernel.org/doc/Documentation/cputopology.txt + { + CpuSet cpus_present; + CpuSet cpus_possible; + + int fd = sys_open("/sys/devices/system/cpu/present", + O_RDONLY | O_CLOEXEC, 0); + if (fd >= 0) { + cpus_present.ParseSysFile(fd); + sys_close(fd); + + fd = sys_open("/sys/devices/system/cpu/possible", + O_RDONLY | O_CLOEXEC, 0); + if (fd >= 0) { + cpus_possible.ParseSysFile(fd); + sys_close(fd); + + cpus_present.IntersectWith(cpus_possible); + int cpu_count = std::min(255, cpus_present.GetCount()); + sys_info->number_of_processors = static_cast(cpu_count); + } + } + } + return true; } #else diff --git a/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/client/linux/minidump_writer/minidump_writer_unittest.cc index ab37a88b..2601d29b 100644 --- a/src/client/linux/minidump_writer/minidump_writer_unittest.cc +++ b/src/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -716,6 +716,9 @@ TEST(MinidumpWriterTest, InvalidStackPointer) { #elif defined(__mips__) context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] = invalid_stack_pointer; +#elif defined(__riscv) + context.context.uc_mcontext.__gregs[MD_CONTEXT_RISCV_REG_SP] = + invalid_stack_pointer; #else # error "This code has not been ported to your platform yet." #endif diff --git a/src/common/linux/breakpad_getcontext.S b/src/common/linux/breakpad_getcontext.S index e261b302..286047bf 100644 --- a/src/common/linux/breakpad_getcontext.S +++ b/src/common/linux/breakpad_getcontext.S @@ -526,8 +526,102 @@ breakpad_getcontext: .cfi_endproc .size breakpad_getcontext, . - breakpad_getcontext +#elif defined(__riscv) + +# define SIG_BLOCK 0 +# define _NSIG8 8 +# define __NR_rt_sigprocmask 135 + + .text + .globl breakpad_getcontext + .type breakpad_getcontext, @function + .align 0 + .cfi_startproc +breakpad_getcontext: + REG_S ra, MCONTEXT_GREGS_PC(a0) + REG_S ra, MCONTEXT_GREGS_RA(a0) + REG_S sp, MCONTEXT_GREGS_SP(a0) + REG_S gp, MCONTEXT_GREGS_SP(a0) + REG_S tp, MCONTEXT_GREGS_TP(a0) + REG_S t0, MCONTEXT_GREGS_T0(a0) + REG_S t1, MCONTEXT_GREGS_T1(a0) + REG_S t2, MCONTEXT_GREGS_T2(a0) + REG_S s0, MCONTEXT_GREGS_S0(a0) + REG_S s1, MCONTEXT_GREGS_S1(a0) + REG_S a0, MCONTEXT_GREGS_A0(a0) + REG_S a1, MCONTEXT_GREGS_A1(a0) + REG_S a2, MCONTEXT_GREGS_A2(a0) + REG_S a3, MCONTEXT_GREGS_A3(a0) + REG_S a4, MCONTEXT_GREGS_A4(a0) + REG_S a5, MCONTEXT_GREGS_A5(a0) + REG_S a6, MCONTEXT_GREGS_A6(a0) + REG_S a7, MCONTEXT_GREGS_A7(a0) + REG_S s2, MCONTEXT_GREGS_S2(a0) + REG_S s3, MCONTEXT_GREGS_S3(a0) + REG_S s4, MCONTEXT_GREGS_S4(a0) + REG_S s5, MCONTEXT_GREGS_S5(a0) + REG_S s6, MCONTEXT_GREGS_S6(a0) + REG_S s7, MCONTEXT_GREGS_S7(a0) + REG_S s8, MCONTEXT_GREGS_S8(a0) + REG_S s9, MCONTEXT_GREGS_S9(a0) + REG_S s10, MCONTEXT_GREGS_S10(a0) + REG_S s11, MCONTEXT_GREGS_S11(a0) + REG_S t3, MCONTEXT_GREGS_T3(a0) + REG_S t4, MCONTEXT_GREGS_T4(a0) + REG_S t5, MCONTEXT_GREGS_T5(a0) + REG_S t6 , MCONTEXT_GREGS_T6(a0) +# ifndef __riscv_float_abi_soft + frsr a1 + + FREG_S ft0, MCONTEXT_FPREGS_FT0(a0) + FREG_S ft1, MCONTEXT_FPREGS_FT1(a0) + FREG_S ft2, MCONTEXT_FPREGS_FT2(a0) + FREG_S ft3, MCONTEXT_FPREGS_FT3(a0) + FREG_S ft4, MCONTEXT_FPREGS_FT4(a0) + FREG_S ft5, MCONTEXT_FPREGS_FT5(a0) + FREG_S ft6, MCONTEXT_FPREGS_FT6(a0) + FREG_S ft7, MCONTEXT_FPREGS_FT7(a0) + FREG_S fs0, MCONTEXT_FPREGS_FS0(a0) + FREG_S fs1, MCONTEXT_FPREGS_FS1(a0) + FREG_S fa0, MCONTEXT_FPREGS_FA0(a0) + FREG_S fa1, MCONTEXT_FPREGS_FA1(a0) + FREG_S fa2, MCONTEXT_FPREGS_FA2(a0) + FREG_S fa3, MCONTEXT_FPREGS_FA3(a0) + FREG_S fa4, MCONTEXT_FPREGS_FA4(a0) + FREG_S fa5, MCONTEXT_FPREGS_FA5(a0) + FREG_S fa6, MCONTEXT_FPREGS_FA6(a0) + FREG_S fa7, MCONTEXT_FPREGS_FA7(a0) + FREG_S fs2, MCONTEXT_FPREGS_FS2(a0) + FREG_S fs3, MCONTEXT_FPREGS_FS3(a0) + FREG_S fs4, MCONTEXT_FPREGS_FS4(a0) + FREG_S fs5, MCONTEXT_FPREGS_FS5(a0) + FREG_S fs6, MCONTEXT_FPREGS_FS6(a0) + FREG_S fs7, MCONTEXT_FPREGS_FS7(a0) + FREG_S fs8, MCONTEXT_FPREGS_FS8(a0) + FREG_S fs9, MCONTEXT_FPREGS_FS9(a0) + FREG_S fs10, MCONTEXT_FPREGS_FS10(a0) + FREG_S fs11, MCONTEXT_FPREGS_FS11(a0) + FREG_S ft8, MCONTEXT_FPREGS_FT8(a0) + FREG_S ft9, MCONTEXT_FPREGS_FT9(a0) + FREG_S ft10, MCONTEXT_FPREGS_FT10(a0) + FREG_S ft11, MCONTEXT_FPREGS_FT11(a0) + + sw a1, MCONTEXT_FPC_CSR(a0) +# endif // __riscv_float_abi_soft + mv a1, zero + add a2, a0, UCONTEXT_SIGMASK_OFFSET + li a3, _NSIG8 + mv a0, zero + li a7, __NR_rt_sigprocmask + ecall + mv a0, zero + ret + + .cfi_endproc + .size breakpad_getcontext, . - breakpad_getcontext + #else -#error "This file has not been ported for your CPU!" +# error "This file has not been ported for your CPU!" #endif #if defined(__aarch64__) diff --git a/src/common/linux/breakpad_getcontext_unittest.cc b/src/common/linux/breakpad_getcontext_unittest.cc index 9d869f7e..573ddd88 100644 --- a/src/common/linux/breakpad_getcontext_unittest.cc +++ b/src/common/linux/breakpad_getcontext_unittest.cc @@ -113,6 +113,25 @@ TEST(AndroidUContext, GRegsOffset) { ASSERT_EQ(static_cast(MCONTEXT_FPC_CSR), offsetof(ucontext_t,uc_mcontext.fpc_csr)); +#elif defined(__riscv) + ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.__gregs[0])); + +#define CHECK_REG(x) \ + ASSERT_EQ(static_cast(MCONTEXT_##x##_OFFSET), \ + offsetof(ucontext_t,uc_mcontext.__gregs[REG_##x])) + CHECK_REG(PC) + CHECK_REG(RA) + CHECK_REG(SP) + CHECK_REG(S0) + CHECK_REG(S1) + CHECK_REG(S2) + + ASSERT_EQ(static_cast(MCONTEXT_FPREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.__fpregs)); + + ASSERT_EQ(static_cast(MCONTEXT_FPC_CSR), + offsetof(ucontext_t,uc_mcontext.__fpregs.__fcsr)); #elif defined(__x86_64__) COMPILE_ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), diff --git a/src/common/linux/memory_mapped_file.cc b/src/common/linux/memory_mapped_file.cc index 9c2c0ad2..7e444607 100644 --- a/src/common/linux/memory_mapped_file.cc +++ b/src/common/linux/memory_mapped_file.cc @@ -64,7 +64,8 @@ bool MemoryMappedFile::Map(const char* path, size_t offset) { } #if defined(__x86_64__) || defined(__aarch64__) || \ - (defined(__mips__) && _MIPS_SIM == _ABI64) + (defined(__mips__) && _MIPS_SIM == _ABI64) || \ + (defined(__riscv) && __riscv_xlen == 64) struct kernel_stat st; if (sys_fstat(fd, &st) == -1 || st.st_size < 0) { diff --git a/src/common/linux/ucontext_constants.h b/src/common/linux/ucontext_constants.h index 4218bb23..3dcdecb0 100644 --- a/src/common/linux/ucontext_constants.h +++ b/src/common/linux/ucontext_constants.h @@ -145,8 +145,107 @@ #endif #define FPREGS_OFFSET_MXCSR 24 +#elif defined(__riscv) + +#if __riscv_xlen == 32 +#define UCONTEXT_SIGMASK_OFFSET 20 +#define MCONTEXT_GREGS_OFFSET 148 +#define MCONTEXT_GREGS_SIZE 4 +#define REG_S sw +#elif __riscv_xlen == 64 +#define UCONTEXT_SIGMASK_OFFSET 40 +#define MCONTEXT_GREGS_OFFSET 168 +#define MCONTEXT_GREGS_SIZE 8 +#define REG_S sd #else -#error "This header has not been ported for your CPU" +#error "Unexpected __riscv_xlen" +#endif + +#define MCONTEXT_GREGS_PC MCONTEXT_GREGS_OFFSET + 0*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_RA MCONTEXT_GREGS_OFFSET + 1*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_SP MCONTEXT_GREGS_OFFSET + 2*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_GP MCONTEXT_GREGS_OFFSET + 3*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_TP MCONTEXT_GREGS_OFFSET + 4*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_T0 MCONTEXT_GREGS_OFFSET + 5*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_T1 MCONTEXT_GREGS_OFFSET + 6*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_T2 MCONTEXT_GREGS_OFFSET + 7*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S0 MCONTEXT_GREGS_OFFSET + 8*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S1 MCONTEXT_GREGS_OFFSET + 9*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A0 MCONTEXT_GREGS_OFFSET + 10*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A1 MCONTEXT_GREGS_OFFSET + 11*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A2 MCONTEXT_GREGS_OFFSET + 12*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A3 MCONTEXT_GREGS_OFFSET + 13*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A4 MCONTEXT_GREGS_OFFSET + 14*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A5 MCONTEXT_GREGS_OFFSET + 15*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A6 MCONTEXT_GREGS_OFFSET + 16*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_A7 MCONTEXT_GREGS_OFFSET + 17*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S2 MCONTEXT_GREGS_OFFSET + 18*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S3 MCONTEXT_GREGS_OFFSET + 19*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S4 MCONTEXT_GREGS_OFFSET + 20*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S5 MCONTEXT_GREGS_OFFSET + 21*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S6 MCONTEXT_GREGS_OFFSET + 22*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S7 MCONTEXT_GREGS_OFFSET + 23*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S8 MCONTEXT_GREGS_OFFSET + 24*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S9 MCONTEXT_GREGS_OFFSET + 25*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S10 MCONTEXT_GREGS_OFFSET + 26*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_S11 MCONTEXT_GREGS_OFFSET + 27*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_T3 MCONTEXT_GREGS_OFFSET + 28*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_T4 MCONTEXT_GREGS_OFFSET + 29*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_T5 MCONTEXT_GREGS_OFFSET + 30*MCONTEXT_GREGS_SIZE +#define MCONTEXT_GREGS_T6 MCONTEXT_GREGS_OFFSET + 31*MCONTEXT_GREGS_SIZE + +#define MCONTEXT_FPREGS_OFFSET MCONTEXT_GREGS_OFFSET + 32*MCONTEXT_GREGS_SIZE + +#if __riscv_flen == 32 +#define MCONTEXT_FPREGS_SIZE 4 +#define FREG_S fsw +#elif __riscv_flen == 64 +#define MCONTEXT_FPREGS_SIZE 8 +#define FREG_S fsd +#elif __riscv_flen == 128 +#define MCONTEXT_FPREGS_SIZE 16 +#define FREG_S fsq +#else +#error "Unexpected __riscv_flen" +#endif + +#define MCONTEXT_FPREGS_FT0 MCONTEXT_FPREGS_OFFSET + 0*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT1 MCONTEXT_FPREGS_OFFSET + 1*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT2 MCONTEXT_FPREGS_OFFSET + 2*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT3 MCONTEXT_FPREGS_OFFSET + 3*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT4 MCONTEXT_FPREGS_OFFSET + 4*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT5 MCONTEXT_FPREGS_OFFSET + 5*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT6 MCONTEXT_FPREGS_OFFSET + 6*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT7 MCONTEXT_FPREGS_OFFSET + 7*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS0 MCONTEXT_FPREGS_OFFSET + 8*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS1 MCONTEXT_FPREGS_OFFSET + 9*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA0 MCONTEXT_FPREGS_OFFSET + 10*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA1 MCONTEXT_FPREGS_OFFSET + 11*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA2 MCONTEXT_FPREGS_OFFSET + 12*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA3 MCONTEXT_FPREGS_OFFSET + 13*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA4 MCONTEXT_FPREGS_OFFSET + 14*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA5 MCONTEXT_FPREGS_OFFSET + 15*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA6 MCONTEXT_FPREGS_OFFSET + 16*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FA7 MCONTEXT_FPREGS_OFFSET + 17*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS2 MCONTEXT_FPREGS_OFFSET + 18*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS3 MCONTEXT_FPREGS_OFFSET + 19*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS4 MCONTEXT_FPREGS_OFFSET + 20*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS5 MCONTEXT_FPREGS_OFFSET + 21*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS6 MCONTEXT_FPREGS_OFFSET + 22*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS7 MCONTEXT_FPREGS_OFFSET + 23*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS8 MCONTEXT_FPREGS_OFFSET + 24*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS9 MCONTEXT_FPREGS_OFFSET + 25*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS10 MCONTEXT_FPREGS_OFFSET + 26*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FS11 MCONTEXT_FPREGS_OFFSET + 27*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT8 MCONTEXT_FPREGS_OFFSET + 28*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT9 MCONTEXT_FPREGS_OFFSET + 29*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT10 MCONTEXT_FPREGS_OFFSET + 30*MCONTEXT_FPREGS_SIZE +#define MCONTEXT_FPREGS_FT11 MCONTEXT_FPREGS_OFFSET + 31*MCONTEXT_FPREGS_SIZE + +#define MCONTEXT_FPC_CSR MCONTEXT_FPREGS_OFFSET + 32*MCONTEXT_FPREGS_SIZE + +#else +# error "This header has not been ported for your CPU" #endif #endif // GOOGLEBREAKPAD_COMMON_ANDROID_UCONTEXT_CONSTANTS_H diff --git a/src/google_breakpad/common/minidump_cpu_riscv.h b/src/google_breakpad/common/minidump_cpu_riscv.h new file mode 100644 index 00000000..94d06117 --- /dev/null +++ b/src/google_breakpad/common/minidump_cpu_riscv.h @@ -0,0 +1,168 @@ +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on RISCV and RISCV64. These files may be read on any platform + * provided that the alignments of these structures on the processing system + * are identical to the alignments of these structures on the producing + * system. For this reason, precise-sized types are used. The structures + * defined by this file have been laid out to minimize alignment problems by + * ensuring that all members are aligned on their natural boundaries. + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. + * + * Author: Iacopo Colonnelli + */ + +/* + * RISCV and RISCV64 support + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_RISCV_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_RISCV_H__ + +#include "google_breakpad/common/breakpad_types.h" + +#define MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT 32 +#if defined(__riscv) +# if __riscv_flen == 32 +typedef uint32_t riscv_fpr_size; +# elif __riscv_flen == 64 +typedef uint64_t riscv_fpr_size; +# elif __riscv_flen == 128 +typedef uint128_struct riscv_fpr_size; +# else +# error "Unexpected __riscv_flen" +# endif +#else +typedef uint32_t riscv_fpr_size; +#endif + +#define MD_CONTEXT_RISCV_GPR_COUNT 32 + +typedef struct { + /* 32 floating point registers, f0 .. f31. */ + riscv_fpr_size regs[MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT]; + uint32_t fpcsr; +} MDFloatingSaveAreaRISCV; + +enum MDRISCVRegisterNumbers { + MD_CONTEXT_RISCV_REG_PC = 0, + MD_CONTEXT_RISCV_REG_RA = 1, + MD_CONTEXT_RISCV_REG_SP = 2, +}; + +/* For (MDRawContextRISCV).context_flags. These values indicate the type of + * context stored in the structure. */ +#define MD_CONTEXT_RISCV 0x00800000 +#define MD_CONTEXT_RISCV_INTEGER (MD_CONTEXT_RISCV | 0x00000001) +#define MD_CONTEXT_RISCV_FLOATING_POINT (MD_CONTEXT_RISCV | 0x00000004) +#define MD_CONTEXT_RISCV_FULL (MD_CONTEXT_RISCV_INTEGER | \ + MD_CONTEXT_RISCV_FLOATING_POINT) + +typedef struct { + /* Determines which fields of this struct are populated */ + uint32_t context_flags; + + uint32_t pc; + uint32_t ra; + uint32_t sp; + uint32_t gp; + uint32_t tp; + uint32_t t0; + uint32_t t1; + uint32_t t2; + uint32_t s0; + uint32_t s1; + uint32_t a0; + uint32_t a1; + uint32_t a2; + uint32_t a3; + uint32_t a4; + uint32_t a5; + uint32_t a6; + uint32_t a7; + uint32_t s2; + uint32_t s3; + uint32_t s4; + uint32_t s5; + uint32_t s6; + uint32_t s7; + uint32_t s8; + uint32_t s9; + uint32_t s10; + uint32_t s11; + uint32_t t3; + uint32_t t4; + uint32_t t5; + uint32_t t6; + + MDFloatingSaveAreaRISCV float_save; +} MDRawContextRISCV; + +/* For (MDRawContextRISCV64).context_flags. These values indicate the type of + * context stored in the structure. */ +#define MD_CONTEXT_RISCV64 0x08000000 +#define MD_CONTEXT_RISCV64_INTEGER (MD_CONTEXT_RISCV64 | 0x00000001) +#define MD_CONTEXT_RISCV64_FLOATING_POINT (MD_CONTEXT_RISCV64 | 0x00000004) +#define MD_CONTEXT_RISCV64_FULL (MD_CONTEXT_RISCV64_INTEGER | \ + MD_CONTEXT_RISCV64_FLOATING_POINT) + +typedef struct { + /* Determines which fields of this struct are populated */ + uint32_t context_flags; + + uint64_t pc; + uint64_t ra; + uint64_t sp; + uint64_t gp; + uint64_t tp; + uint64_t t0; + uint64_t t1; + uint64_t t2; + uint64_t s0; + uint64_t s1; + uint64_t a0; + uint64_t a1; + uint64_t a2; + uint64_t a3; + uint64_t a4; + uint64_t a5; + uint64_t a6; + uint64_t a7; + uint64_t s2; + uint64_t s3; + uint64_t s4; + uint64_t s5; + uint64_t s6; + uint64_t s7; + uint64_t s8; + uint64_t s9; + uint64_t s10; + uint64_t s11; + uint64_t t3; + uint64_t t4; + uint64_t t5; + uint64_t t6; + + MDFloatingSaveAreaRISCV float_save; +} MDRawContextRISCV64; + + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_RISCV_H__ */ diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h index 3e86f7c8..9edead93 100644 --- a/src/google_breakpad/common/minidump_format.h +++ b/src/google_breakpad/common/minidump_format.h @@ -117,6 +117,7 @@ typedef struct { #include "minidump_cpu_mips.h" #include "minidump_cpu_ppc.h" #include "minidump_cpu_ppc64.h" +#include "minidump_cpu_riscv.h" #include "minidump_cpu_sparc.h" #include "minidump_cpu_x86.h" @@ -683,6 +684,8 @@ typedef enum { MD_CPU_ARCHITECTURE_PPC64 = 0x8002, /* Breakpad-defined value for PPC64 */ MD_CPU_ARCHITECTURE_ARM64_OLD = 0x8003, /* Breakpad-defined value for ARM64 */ MD_CPU_ARCHITECTURE_MIPS64 = 0x8004, /* Breakpad-defined value for MIPS64 */ + MD_CPU_ARCHITECTURE_RISCV = 0x8005, /* Breakpad-defined value for RISCV */ + MD_CPU_ARCHITECTURE_RISCV64 = 0x8006, /* Breakpad-defined value for RISCV64 */ MD_CPU_ARCHITECTURE_UNKNOWN = 0xffff /* PROCESSOR_ARCHITECTURE_UNKNOWN */ } MDCPUArchitecture; diff --git a/src/google_breakpad/processor/dump_context.h b/src/google_breakpad/processor/dump_context.h index 90f066b8..7a1c643e 100644 --- a/src/google_breakpad/processor/dump_context.h +++ b/src/google_breakpad/processor/dump_context.h @@ -53,14 +53,16 @@ class DumpContext : public DumpObject { // Returns raw CPU-specific context data for the named CPU type. If the // context data does not match the CPU type or does not exist, returns 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; - const MDRawContextSPARC* GetContextSPARC() const; - const MDRawContextX86* GetContextX86() const; + const MDRawContextAMD64* GetContextAMD64() const; + const MDRawContextARM* GetContextARM() const; + const MDRawContextARM64* GetContextARM64() const; + const MDRawContextMIPS* GetContextMIPS() const; + const MDRawContextPPC* GetContextPPC() const; + const MDRawContextPPC64* GetContextPPC64() const; + const MDRawContextSPARC* GetContextSPARC() const; + const MDRawContextX86* GetContextX86() const; + const MDRawContextRISCV* GetContextRISCV() const; + const MDRawContextRISCV64* GetContextRISCV64() const; // A convenience method to get the instruction pointer out of the // MDRawContext, since it varies per-CPU architecture. @@ -86,6 +88,8 @@ class DumpContext : public DumpObject { void SetContextARM(MDRawContextARM* arm); void SetContextARM64(MDRawContextARM64* arm64); void SetContextMIPS(MDRawContextMIPS* ctx_mips); + void SetContextRISCV(MDRawContextRISCV* riscv); + void SetContextRISCV64(MDRawContextRISCV64* riscv64); // Free the CPU-specific context structure. void FreeContext(); @@ -93,17 +97,19 @@ class DumpContext : public DumpObject { private: // The CPU-specific context structure. union { - MDRawContextBase* base; - MDRawContextX86* x86; - MDRawContextPPC* ppc; - MDRawContextPPC64* ppc64; - MDRawContextAMD64* amd64; + MDRawContextBase* base; + MDRawContextX86* x86; + MDRawContextPPC* ppc; + MDRawContextPPC64* ppc64; + MDRawContextAMD64* amd64; // on Solaris SPARC, sparc is defined as a numeric constant, // so variables can NOT be named as sparc - MDRawContextSPARC* ctx_sparc; - MDRawContextARM* arm; - MDRawContextARM64* arm64; - MDRawContextMIPS* ctx_mips; + MDRawContextSPARC* ctx_sparc; + MDRawContextARM* arm; + MDRawContextARM64* arm64; + MDRawContextMIPS* ctx_mips; + MDRawContextRISCV* riscv; + MDRawContextRISCV64* riscv64; } context_; // Store this separately because of the weirdo AMD64 context diff --git a/src/google_breakpad/processor/stack_frame_cpu.h b/src/google_breakpad/processor/stack_frame_cpu.h index 363913bf..91f1d0cb 100644 --- a/src/google_breakpad/processor/stack_frame_cpu.h +++ b/src/google_breakpad/processor/stack_frame_cpu.h @@ -402,6 +402,118 @@ struct StackFrameMIPS : public StackFrame { int context_validity; }; +struct StackFrameRISCV : public StackFrame { + + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_PC = 1 << 0, + CONTEXT_VALID_RA = 1 << 1, + CONTEXT_VALID_SP = 1 << 2, + CONTEXT_VALID_GP = 1 << 3, + CONTEXT_VALID_TP = 1 << 4, + CONTEXT_VALID_T0 = 1 << 5, + CONTEXT_VALID_T1 = 1 << 6, + CONTEXT_VALID_T2 = 1 << 7, + CONTEXT_VALID_S0 = 1 << 8, + CONTEXT_VALID_S1 = 1 << 9, + CONTEXT_VALID_A0 = 1 << 10, + CONTEXT_VALID_A1 = 1 << 11, + CONTEXT_VALID_A2 = 1 << 12, + CONTEXT_VALID_A3 = 1 << 13, + CONTEXT_VALID_A4 = 1 << 14, + CONTEXT_VALID_A5 = 1 << 15, + CONTEXT_VALID_A6 = 1 << 16, + CONTEXT_VALID_A7 = 1 << 17, + CONTEXT_VALID_S2 = 1 << 18, + CONTEXT_VALID_S3 = 1 << 19, + CONTEXT_VALID_S4 = 1 << 20, + CONTEXT_VALID_S5 = 1 << 21, + CONTEXT_VALID_S6 = 1 << 22, + CONTEXT_VALID_S7 = 1 << 23, + CONTEXT_VALID_S8 = 1 << 24, + CONTEXT_VALID_S9 = 1 << 25, + CONTEXT_VALID_S10 = 1 << 26, + CONTEXT_VALID_S11 = 1 << 27, + CONTEXT_VALID_T3 = 1 << 28, + CONTEXT_VALID_T4 = 1 << 29, + CONTEXT_VALID_T5 = 1 << 30, + CONTEXT_VALID_T6 = 1 << 31, + CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE + }; + + StackFrameRISCV() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, which registers are present depends on what + // debugging information were available. Refer to 'context_validity' below. + MDRawContextRISCV context; + + // For each register in context whose value has been recovered, + // the corresponding CONTEXT_VALID_ bit in 'context_validity' is set. + // + // context_validity's type should actually be ContextValidity, but + // type int is used 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; +}; + +struct StackFrameRISCV64 : public StackFrame { + + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_PC = 1 << 0, + CONTEXT_VALID_RA = 1 << 1, + CONTEXT_VALID_SP = 1 << 2, + CONTEXT_VALID_GP = 1 << 3, + CONTEXT_VALID_TP = 1 << 4, + CONTEXT_VALID_T0 = 1 << 5, + CONTEXT_VALID_T1 = 1 << 6, + CONTEXT_VALID_T2 = 1 << 7, + CONTEXT_VALID_S0 = 1 << 8, + CONTEXT_VALID_S1 = 1 << 9, + CONTEXT_VALID_A0 = 1 << 10, + CONTEXT_VALID_A1 = 1 << 11, + CONTEXT_VALID_A2 = 1 << 12, + CONTEXT_VALID_A3 = 1 << 13, + CONTEXT_VALID_A4 = 1 << 14, + CONTEXT_VALID_A5 = 1 << 15, + CONTEXT_VALID_A6 = 1 << 16, + CONTEXT_VALID_A7 = 1 << 17, + CONTEXT_VALID_S2 = 1 << 18, + CONTEXT_VALID_S3 = 1 << 19, + CONTEXT_VALID_S4 = 1 << 20, + CONTEXT_VALID_S5 = 1 << 21, + CONTEXT_VALID_S6 = 1 << 22, + CONTEXT_VALID_S7 = 1 << 23, + CONTEXT_VALID_S8 = 1 << 24, + CONTEXT_VALID_S9 = 1 << 25, + CONTEXT_VALID_S10 = 1 << 26, + CONTEXT_VALID_S11 = 1 << 27, + CONTEXT_VALID_T3 = 1 << 28, + CONTEXT_VALID_T4 = 1 << 29, + CONTEXT_VALID_T5 = 1 << 30, + CONTEXT_VALID_T6 = 1 << 31, + CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE + }; + + StackFrameRISCV64() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, which registers are present depends on what + // debugging information were available. Refer to 'context_validity' below. + MDRawContextRISCV64 context; + + // For each register in context whose value has been recovered, + // the corresponding CONTEXT_VALID_ bit in 'context_validity' is set. + // + // context_validity's type should actually be ContextValidity, but + // type int is used 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; +}; + } // namespace google_breakpad #endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ diff --git a/src/processor/dump_context.cc b/src/processor/dump_context.cc index 70798d1e..a8ab0084 100644 --- a/src/processor/dump_context.cc +++ b/src/processor/dump_context.cc @@ -139,6 +139,24 @@ const MDRawContextMIPS* DumpContext::GetContextMIPS() const { return context_.ctx_mips; } +const MDRawContextRISCV* DumpContext::GetContextRISCV() const { + if (GetContextCPU() != MD_CONTEXT_RISCV) { + BPLOG(ERROR) << "DumpContext cannot get RISCV context"; + return NULL; + } + + return context_.riscv; +} + +const MDRawContextRISCV64* DumpContext::GetContextRISCV64() const { + if (GetContextCPU() != MD_CONTEXT_RISCV64) { + BPLOG(ERROR) << "DumpContext cannot get RISCV64 context"; + return NULL; + } + + return context_.riscv64; +} + bool DumpContext::GetInstructionPointer(uint64_t* ip) const { BPLOG_IF(ERROR, !ip) << "DumpContext::GetInstructionPointer requires |ip|"; assert(ip); @@ -175,6 +193,12 @@ bool DumpContext::GetInstructionPointer(uint64_t* ip) const { case MD_CONTEXT_MIPS64: *ip = GetContextMIPS()->epc; break; + case MD_CONTEXT_RISCV: + *ip = GetContextRISCV()->pc; + break; + case MD_CONTEXT_RISCV64: + *ip = GetContextRISCV64()->pc; + break; default: // This should never happen. BPLOG(ERROR) << "Unknown CPU architecture in GetInstructionPointer"; @@ -219,6 +243,12 @@ bool DumpContext::GetStackPointer(uint64_t* sp) const { case MD_CONTEXT_MIPS64: *sp = GetContextMIPS()->iregs[MD_CONTEXT_MIPS_REG_SP]; break; + case MD_CONTEXT_RISCV: + *sp = GetContextRISCV()->sp; + break; + case MD_CONTEXT_RISCV64: + *sp = GetContextRISCV64()->sp; + break; default: // This should never happen. BPLOG(ERROR) << "Unknown CPU architecture in GetStackPointer"; @@ -263,6 +293,14 @@ void DumpContext::SetContextMIPS(MDRawContextMIPS* ctx_mips) { context_.ctx_mips = ctx_mips; } +void DumpContext::SetContextRISCV(MDRawContextRISCV* riscv) { + context_.riscv = riscv; +} + +void DumpContext::SetContextRISCV64(MDRawContextRISCV64* riscv64) { + context_.riscv64 = riscv64; +} + void DumpContext::FreeContext() { switch (GetContextCPU()) { case MD_CONTEXT_X86: @@ -298,6 +336,14 @@ void DumpContext::FreeContext() { delete context_.ctx_mips; break; + case MD_CONTEXT_RISCV: + delete context_.riscv; + break; + + case MD_CONTEXT_RISCV64: + delete context_.riscv64; + break; + default: // There is no context record (valid_ is false) or there's a // context record for an unknown CPU (shouldn't happen, only known @@ -654,6 +700,195 @@ void DumpContext::Print() { break; } + case MD_CONTEXT_RISCV: { + const MDRawContextRISCV* context_riscv = GetContextRISCV(); + printf("MDRawContextRISCV\n"); + printf(" context_flags = 0x%x\n", + context_riscv->context_flags); + + printf(" pc = 0x%" PRIx32 "\n", + context_riscv->pc); + printf(" ra = 0x%" PRIx32 "\n", + context_riscv->ra); + printf(" sp = 0x%" PRIx32 "\n", + context_riscv->sp); + printf(" gp = 0x%" PRIx32 "\n", + context_riscv->gp); + printf(" tp = 0x%" PRIx32 "\n", + context_riscv->tp); + printf(" t0 = 0x%" PRIx32 "\n", + context_riscv->t0); + printf(" t1 = 0x%" PRIx32 "\n", + context_riscv->t1); + printf(" t2 = 0x%" PRIx32 "\n", + context_riscv->t2); + printf(" s0 = 0x%" PRIx32 "\n", + context_riscv->s0); + printf(" s1 = 0x%" PRIx32 "\n", + context_riscv->s1); + printf(" a0 = 0x%" PRIx32 "\n", + context_riscv->a0); + printf(" a1 = 0x%" PRIx32 "\n", + context_riscv->a1); + printf(" a2 = 0x%" PRIx32 "\n", + context_riscv->a2); + printf(" a3 = 0x%" PRIx32 "\n", + context_riscv->a3); + printf(" a4 = 0x%" PRIx32 "\n", + context_riscv->a4); + printf(" a5 = 0x%" PRIx32 "\n", + context_riscv->a5); + printf(" a6 = 0x%" PRIx32 "\n", + context_riscv->a6); + printf(" a7 = 0x%" PRIx32 "\n", + context_riscv->a7); + printf(" s2 = 0x%" PRIx32 "\n", + context_riscv->s2); + printf(" s3 = 0x%" PRIx32 "\n", + context_riscv->s3); + printf(" s4 = 0x%" PRIx32 "\n", + context_riscv->s4); + printf(" s5 = 0x%" PRIx32 "\n", + context_riscv->s5); + printf(" s6 = 0x%" PRIx32 "\n", + context_riscv->s6); + printf(" s7 = 0x%" PRIx32 "\n", + context_riscv->s7); + printf(" s8 = 0x%" PRIx32 "\n", + context_riscv->s8); + printf(" s9 = 0x%" PRIx32 "\n", + context_riscv->s9); + printf(" s10 = 0x%" PRIx32 "\n", + context_riscv->s10); + printf(" s11 = 0x%" PRIx32 "\n", + context_riscv->s11); + printf(" t3 = 0x%" PRIx32 "\n", + context_riscv->t3); + printf(" t4 = 0x%" PRIx32 "\n", + context_riscv->t4); + printf(" t5 = 0x%" PRIx32 "\n", + context_riscv->t5); + printf(" t6 = 0x%" PRIx32 "\n", + context_riscv->t6); + +#if defined(__riscv) + for (unsigned int freg_index = 0; + freg_index < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; ++freg_index) { + riscv_fpr_size fp_value = context_riscv->float_save.regs[freg_index]; +# if __riscv_flen == 32 + printf(" float_save.regs[%2d] = 0x%" PRIx32 "\n", + freg_index, fp_value); +# elif __riscv_flen == 64 + printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", + freg_index, fp_value); +# elif __riscv_flen == 128 + printf(" float_save.regs[%2d] = 0x%" PRIx64 "%" PRIx64 "\n", + freg_index, fp_value.high, fp_value.low); +# else +# error "Unexpected __riscv_flen" +# endif + } + printf(" float_save.fpcsr = 0x%" PRIx32 "\n", + context_riscv->float_save.fpcsr); +#endif + break; + } + + case MD_CONTEXT_RISCV64: { + const MDRawContextRISCV64* context_riscv64 = GetContextRISCV64(); + printf("MDRawContextRISCV64\n"); + printf(" context_flags = 0x%x\n", + context_riscv64->context_flags); + + printf(" pc = 0x%" PRIx64 "\n", + context_riscv64->pc); + printf(" ra = 0x%" PRIx64 "\n", + context_riscv64->ra); + printf(" sp = 0x%" PRIx64 "\n", + context_riscv64->sp); + printf(" gp = 0x%" PRIx64 "\n", + context_riscv64->gp); + printf(" tp = 0x%" PRIx64 "\n", + context_riscv64->tp); + printf(" t0 = 0x%" PRIx64 "\n", + context_riscv64->t0); + printf(" t1 = 0x%" PRIx64 "\n", + context_riscv64->t1); + printf(" t2 = 0x%" PRIx64 "\n", + context_riscv64->t2); + printf(" s0 = 0x%" PRIx64 "\n", + context_riscv64->s0); + printf(" s1 = 0x%" PRIx64 "\n", + context_riscv64->s1); + printf(" a0 = 0x%" PRIx64 "\n", + context_riscv64->a0); + printf(" a1 = 0x%" PRIx64 "\n", + context_riscv64->a1); + printf(" a2 = 0x%" PRIx64 "\n", + context_riscv64->a2); + printf(" a3 = 0x%" PRIx64 "\n", + context_riscv64->a3); + printf(" a4 = 0x%" PRIx64 "\n", + context_riscv64->a4); + printf(" a5 = 0x%" PRIx64 "\n", + context_riscv64->a5); + printf(" a6 = 0x%" PRIx64 "\n", + context_riscv64->a6); + printf(" a7 = 0x%" PRIx64 "\n", + context_riscv64->a7); + printf(" s2 = 0x%" PRIx64 "\n", + context_riscv64->s2); + printf(" s3 = 0x%" PRIx64 "\n", + context_riscv64->s3); + printf(" s4 = 0x%" PRIx64 "\n", + context_riscv64->s4); + printf(" s5 = 0x%" PRIx64 "\n", + context_riscv64->s5); + printf(" s6 = 0x%" PRIx64 "\n", + context_riscv64->s6); + printf(" s7 = 0x%" PRIx64 "\n", + context_riscv64->s7); + printf(" s8 = 0x%" PRIx64 "\n", + context_riscv64->s8); + printf(" s9 = 0x%" PRIx64 "\n", + context_riscv64->s9); + printf(" s10 = 0x%" PRIx64 "\n", + context_riscv64->s10); + printf(" s11 = 0x%" PRIx64 "\n", + context_riscv64->s11); + printf(" t3 = 0x%" PRIx64 "\n", + context_riscv64->t3); + printf(" t4 = 0x%" PRIx64 "\n", + context_riscv64->t4); + printf(" t5 = 0x%" PRIx64 "\n", + context_riscv64->t5); + printf(" t6 = 0x%" PRIx64 "\n", + context_riscv64->t6); + +#if defined(__riscv) + for (unsigned int freg_index = 0; + freg_index < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; ++freg_index) { + riscv_fpr_size fp_value = context_riscv64->float_save.regs[freg_index]; +# if __riscv_flen == 32 + printf(" float_save.regs[%2d] = 0x%" PRIx32 "\n", + freg_index, fp_value); +# elif __riscv_flen == 64 + printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", + freg_index, fp_value); +# elif __riscv_flen == 128 + printf(" float_save.regs[%2d] = 0x%" + PRIx64 "%" PRIx64 "\n", + freg_index, fp_value.high, fp_value.low); +# else +# error "Unexpected __riscv_flen" +# endif + } + printf(" float_save.fpcsr = 0x%" PRIx32 "\n", + context_riscv64->float_save.fpcsr); +#endif + break; + } + default: { break; } diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index d9e3e323..d56db98e 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -95,6 +95,10 @@ bool IsContextSizeUnique(uint32_t context_size) { num_matching_contexts++; if (context_size == sizeof(MDRawContextMIPS)) num_matching_contexts++; + if (context_size == sizeof(MDRawContextRISCV)) + num_matching_contexts++; + if (context_size == sizeof(MDRawContextRISCV64)) + num_matching_contexts++; return num_matching_contexts == 1; } @@ -1169,6 +1173,163 @@ bool MinidumpContext::Read(uint32_t expected_size) { break; } + case MD_CONTEXT_RISCV: { + if (expected_size != sizeof(MDRawContextRISCV)) { + BPLOG(ERROR) << "MinidumpContext RISCV size mismatch, " + << expected_size + << " != " + << sizeof(MDRawContextRISCV); + return false; + } + + scoped_ptr context_riscv(new MDRawContextRISCV()); + + // 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_riscv->context_flags = context_flags; + + size_t flags_size = sizeof(context_riscv->context_flags); + uint8_t* context_after_flags = + reinterpret_cast(context_riscv.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextRISCV) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read RISCV 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 RISCV does not match system info"; + return false; + } + + if (minidump_->swap()) { + Swap(&context_riscv->pc); + Swap(&context_riscv->ra); + Swap(&context_riscv->sp); + Swap(&context_riscv->gp); + Swap(&context_riscv->tp); + Swap(&context_riscv->t0); + Swap(&context_riscv->t1); + Swap(&context_riscv->t2); + Swap(&context_riscv->s0); + Swap(&context_riscv->s1); + Swap(&context_riscv->a0); + Swap(&context_riscv->a1); + Swap(&context_riscv->a2); + Swap(&context_riscv->a3); + Swap(&context_riscv->a4); + Swap(&context_riscv->a5); + Swap(&context_riscv->a6); + Swap(&context_riscv->a7); + Swap(&context_riscv->s2); + Swap(&context_riscv->s3); + Swap(&context_riscv->s4); + Swap(&context_riscv->s5); + Swap(&context_riscv->s6); + Swap(&context_riscv->s7); + Swap(&context_riscv->s8); + Swap(&context_riscv->s9); + Swap(&context_riscv->s10); + Swap(&context_riscv->s11); + Swap(&context_riscv->t3); + Swap(&context_riscv->t4); + Swap(&context_riscv->t5); + Swap(&context_riscv->t6); + + for (int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; + ++fpr_index) { + Swap(&context_riscv->float_save.regs[fpr_index]); + } + Swap(&context_riscv->float_save.fpcsr); + } + SetContextRISCV(context_riscv.release()); + + break; + } + + case MD_CONTEXT_RISCV64: { + if (expected_size != sizeof(MDRawContextRISCV64)) { + BPLOG(ERROR) << "MinidumpContext RISCV64 size mismatch, " + << expected_size + << " != " + << sizeof(MDRawContextRISCV64); + return false; + } + + scoped_ptr context_riscv64( + new MDRawContextRISCV64()); + + // 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_riscv64->context_flags = context_flags; + + size_t flags_size = sizeof(context_riscv64->context_flags); + uint8_t* context_after_flags = + reinterpret_cast(context_riscv64.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextRISCV64) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read RISCV 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 RISCV does not match system info"; + return false; + } + + if (minidump_->swap()) { + Swap(&context_riscv64->pc); + Swap(&context_riscv64->ra); + Swap(&context_riscv64->sp); + Swap(&context_riscv64->gp); + Swap(&context_riscv64->tp); + Swap(&context_riscv64->t0); + Swap(&context_riscv64->t1); + Swap(&context_riscv64->t2); + Swap(&context_riscv64->s0); + Swap(&context_riscv64->s1); + Swap(&context_riscv64->a0); + Swap(&context_riscv64->a1); + Swap(&context_riscv64->a2); + Swap(&context_riscv64->a3); + Swap(&context_riscv64->a4); + Swap(&context_riscv64->a5); + Swap(&context_riscv64->a6); + Swap(&context_riscv64->a7); + Swap(&context_riscv64->s2); + Swap(&context_riscv64->s3); + Swap(&context_riscv64->s4); + Swap(&context_riscv64->s5); + Swap(&context_riscv64->s6); + Swap(&context_riscv64->s7); + Swap(&context_riscv64->s8); + Swap(&context_riscv64->s9); + Swap(&context_riscv64->s10); + Swap(&context_riscv64->s11); + Swap(&context_riscv64->t3); + Swap(&context_riscv64->t4); + Swap(&context_riscv64->t5); + Swap(&context_riscv64->t6); + + for (int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; + ++fpr_index) { + Swap(&context_riscv64->float_save.regs[fpr_index]); + } + Swap(&context_riscv64->float_save.fpcsr); + } + SetContextRISCV64(context_riscv64.release()); + + break; + } + default: { // Unknown context type - Don't log as an error yet. Let the // caller work that out. @@ -1261,6 +1422,16 @@ bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) { if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS64) return_value = true; break; + + case MD_CONTEXT_RISCV: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_RISCV) + return_value = true; + break; + + case MD_CONTEXT_RISCV64: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_RISCV64) + return_value = true; + break; } BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " << @@ -3772,6 +3943,14 @@ string MinidumpSystemInfo::GetCPU() { cpu = "arm64"; break; + case MD_CPU_ARCHITECTURE_RISCV: + cpu = "riscv"; + break; + + case MD_CPU_ARCHITECTURE_RISCV64: + cpu = "riscv64"; + break; + default: BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " << HexString(system_info_.processor_architecture); @@ -5381,6 +5560,12 @@ bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t* context_cpu_flags) { case MD_CPU_ARCHITECTURE_SPARC: *context_cpu_flags = MD_CONTEXT_SPARC; break; + case MD_CPU_ARCHITECTURE_RISCV: + *context_cpu_flags = MD_CONTEXT_RISCV; + break; + case MD_CPU_ARCHITECTURE_RISCV64: + *context_cpu_flags = MD_CONTEXT_RISCV64; + break; case MD_CPU_ARCHITECTURE_UNKNOWN: *context_cpu_flags = 0; break; diff --git a/src/processor/processor.gyp b/src/processor/processor.gyp index f812b1ec..f1e473fc 100644 --- a/src/processor/processor.gyp +++ b/src/processor/processor.gyp @@ -165,6 +165,8 @@ 'stackwalker_arm_unittest.cc', 'stackwalker_mips_unittest.cc', 'stackwalker_mips64_unittest.cc', + 'stackwalker_riscv_unittest.cc', + 'stackwalker_riscv64_unittest.cc', 'stackwalker_unittest_utils.h', 'stackwalker_x86_unittest.cc', 'static_address_map_unittest.cc', diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc index cf1114d9..a1b6364d 100644 --- a/src/processor/stackwalk_common.cc +++ b/src/processor/stackwalk_common.cc @@ -149,7 +149,8 @@ static void PrintStackContents(const string& indent, const StackFrameARM* frame_arm = static_cast(frame); const StackFrameARM* prev_frame_arm = static_cast(prev_frame); - if ((frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) && + if ((frame_arm->context_validity & + StackFrameARM::CONTEXT_VALID_SP) && (prev_frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP)) { stack_begin = frame_arm->context.iregs[13]; stack_end = prev_frame_arm->context.iregs[13]; @@ -160,12 +161,39 @@ static void PrintStackContents(const string& indent, static_cast(frame); const StackFrameARM64* prev_frame_arm64 = static_cast(prev_frame); - if ((frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP) && + if ((frame_arm64->context_validity & + StackFrameARM64::CONTEXT_VALID_SP) && (prev_frame_arm64->context_validity & StackFrameARM64::CONTEXT_VALID_SP)) { stack_begin = frame_arm64->context.iregs[31]; stack_end = prev_frame_arm64->context.iregs[31]; } + } else if (cpu == "riscv") { + word_length = 4; + const StackFrameRISCV* frame_riscv = + static_cast(frame); + const StackFrameRISCV* prev_frame_riscv = + static_cast(prev_frame); + if ((frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_SP) && + (prev_frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_SP)) { + stack_begin = frame_riscv->context.sp; + stack_end = prev_frame_riscv->context.sp; + } + } else if (cpu == "riscv64") { + word_length = 8; + const StackFrameRISCV64* frame_riscv64 = + static_cast(frame); + const StackFrameRISCV64* prev_frame_riscv64 = + static_cast(prev_frame); + if ((frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_SP) && + (prev_frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_SP)) { + stack_begin = frame_riscv64->context.sp; + stack_end = prev_frame_riscv64->context.sp; + } } if (!word_length || !stack_begin || !stack_end) return; @@ -636,6 +664,270 @@ static void PrintStack(const CallStack* stack, sequence = PrintRegister64( "s7", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7], sequence); + } else if (cpu == "riscv") { + const StackFrameRISCV* frame_riscv = + reinterpret_cast(frame); + + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_PC) + sequence = PrintRegister( + "pc", frame_riscv->context.pc, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_RA) + sequence = PrintRegister( + "ra", frame_riscv->context.ra, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_SP) + sequence = PrintRegister( + "sp", frame_riscv->context.sp, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_GP) + sequence = PrintRegister( + "gp", frame_riscv->context.gp, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_TP) + sequence = PrintRegister( + "tp", frame_riscv->context.tp, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_T0) + sequence = PrintRegister( + "t0", frame_riscv->context.t0, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_T1) + sequence = PrintRegister( + "t1", frame_riscv->context.t1, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_T2) + sequence = PrintRegister( + "t2", frame_riscv->context.t2, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S0) + sequence = PrintRegister( + "s0", frame_riscv->context.s0, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S1) + sequence = PrintRegister( + "s1", frame_riscv->context.s1, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A0) + sequence = PrintRegister( + "a0", frame_riscv->context.a0, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A1) + sequence = PrintRegister( + "a1", frame_riscv->context.a1, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A2) + sequence = PrintRegister( + "a2", frame_riscv->context.a2, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A3) + sequence = PrintRegister( + "a3", frame_riscv->context.a3, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A4) + sequence = PrintRegister( + "a4", frame_riscv->context.a4, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A5) + sequence = PrintRegister( + "a5", frame_riscv->context.a5, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A6) + sequence = PrintRegister( + "a6", frame_riscv->context.a6, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_A7) + sequence = PrintRegister( + "a7", frame_riscv->context.a7, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S2) + sequence = PrintRegister( + "s2", frame_riscv->context.s2, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S3) + sequence = PrintRegister( + "s3", frame_riscv->context.s3, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S4) + sequence = PrintRegister( + "s4", frame_riscv->context.s4, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S5) + sequence = PrintRegister( + "s5", frame_riscv->context.s5, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S6) + sequence = PrintRegister( + "s6", frame_riscv->context.s6, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S7) + sequence = PrintRegister( + "s7", frame_riscv->context.s7, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S8) + sequence = PrintRegister( + "s8", frame_riscv->context.s8, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S9) + sequence = PrintRegister( + "s9", frame_riscv->context.s9, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S10) + sequence = PrintRegister( + "s10", frame_riscv->context.s10, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_S11) + sequence = PrintRegister( + "s11", frame_riscv->context.s11, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_T3) + sequence = PrintRegister( + "t3", frame_riscv->context.t3, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_T4) + sequence = PrintRegister( + "t4", frame_riscv->context.t4, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_T5) + sequence = PrintRegister( + "t5", frame_riscv->context.t5, sequence); + if (frame_riscv->context_validity & + StackFrameRISCV::CONTEXT_VALID_T6) + sequence = PrintRegister( + "t6", frame_riscv->context.t6, sequence); + } else if (cpu == "riscv64") { + const StackFrameRISCV64* frame_riscv64 = + reinterpret_cast(frame); + + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_PC) + sequence = PrintRegister64( + "pc", frame_riscv64->context.pc, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_RA) + sequence = PrintRegister64( + "ra", frame_riscv64->context.ra, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_SP) + sequence = PrintRegister64( + "sp", frame_riscv64->context.sp, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_GP) + sequence = PrintRegister64( + "gp", frame_riscv64->context.gp, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_TP) + sequence = PrintRegister64( + "tp", frame_riscv64->context.tp, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_T0) + sequence = PrintRegister64( + "t0", frame_riscv64->context.t0, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_T1) + sequence = PrintRegister64( + "t1", frame_riscv64->context.t1, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_T2) + sequence = PrintRegister64( + "t2", frame_riscv64->context.t2, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S0) + sequence = PrintRegister64( + "s0", frame_riscv64->context.s0, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S1) + sequence = PrintRegister64( + "s1", frame_riscv64->context.s1, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A0) + sequence = PrintRegister64( + "a0", frame_riscv64->context.a0, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A1) + sequence = PrintRegister64( + "a1", frame_riscv64->context.a1, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A2) + sequence = PrintRegister64( + "a2", frame_riscv64->context.a2, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A3) + sequence = PrintRegister64( + "a3", frame_riscv64->context.a3, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A4) + sequence = PrintRegister64( + "a4", frame_riscv64->context.a4, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A5) + sequence = PrintRegister64( + "a5", frame_riscv64->context.a5, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A6) + sequence = PrintRegister64( + "a6", frame_riscv64->context.a6, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_A7) + sequence = PrintRegister64( + "a7", frame_riscv64->context.a7, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S2) + sequence = PrintRegister64( + "s2", frame_riscv64->context.s2, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S3) + sequence = PrintRegister64( + "s3", frame_riscv64->context.s3, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S4) + sequence = PrintRegister64( + "s4", frame_riscv64->context.s4, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S5) + sequence = PrintRegister64( + "s5", frame_riscv64->context.s5, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S6) + sequence = PrintRegister64( + "s6", frame_riscv64->context.s6, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S7) + sequence = PrintRegister64( + "s7", frame_riscv64->context.s7, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S8) + sequence = PrintRegister64( + "s8", frame_riscv64->context.s8, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S9) + sequence = PrintRegister64( + "s9", frame_riscv64->context.s9, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S10) + sequence = PrintRegister64( + "s10", frame_riscv64->context.s10, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S11) + sequence = PrintRegister64( + "s11", frame_riscv64->context.s11, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_T3) + sequence = PrintRegister64( + "t3", frame_riscv64->context.t3, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_T4) + sequence = PrintRegister64( + "t4", frame_riscv64->context.t4, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_T5) + sequence = PrintRegister64( + "t5", frame_riscv64->context.t5, sequence); + if (frame_riscv64->context_validity & + StackFrameRISCV64::CONTEXT_VALID_T6) + sequence = PrintRegister64( + "t6", frame_riscv64->context.t6, sequence); } } printf("\n Found by: %s\n", frame->trust_description().c_str()); diff --git a/src/processor/stackwalker.cc b/src/processor/stackwalker.cc index 13f89b17..e607b721 100644 --- a/src/processor/stackwalker.cc +++ b/src/processor/stackwalker.cc @@ -54,6 +54,8 @@ #include "processor/stackwalker_arm.h" #include "processor/stackwalker_arm64.h" #include "processor/stackwalker_mips.h" +#include "processor/stackwalker_riscv.h" +#include "processor/stackwalker_riscv64.h" namespace google_breakpad { @@ -270,6 +272,20 @@ Stackwalker* Stackwalker::StackwalkerForCPU( memory, modules, frame_symbolizer); break; + + case MD_CONTEXT_RISCV: + cpu_stackwalker = new StackwalkerRISCV(system_info, + context->GetContextRISCV(), + memory, modules, + frame_symbolizer); + break; + + case MD_CONTEXT_RISCV64: + cpu_stackwalker = new StackwalkerRISCV64(system_info, + context->GetContextRISCV64(), + memory, modules, + frame_symbolizer); + break; } BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) << diff --git a/src/processor/stackwalker_riscv.cc b/src/processor/stackwalker_riscv.cc new file mode 100644 index 00000000..3d8a64f4 --- /dev/null +++ b/src/processor/stackwalker_riscv.cc @@ -0,0 +1,535 @@ +// Copyright 2013 Google LLC +// +// 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 LLC 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_riscv.cc: riscv-specific stackwalker. + * + * See stackwalker_riscv.h for documentation. + * + * Author: Iacopo Colonnelli + */ + +#include "common/scoped_ptr.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "google_breakpad/processor/system_info.h" +#include "processor/cfi_frame_info.h" +#include "processor/logging.h" +#include "processor/stackwalker_riscv.h" + +namespace google_breakpad { + +StackwalkerRISCV::StackwalkerRISCV(const SystemInfo* system_info, + const MDRawContextRISCV* context, + MemoryRegion* memory, + const CodeModules* modules, + StackFrameSymbolizer* resolver_helper) + : Stackwalker(system_info, memory, modules, resolver_helper), + context_(context), + context_frame_validity_(StackFrameRISCV::CONTEXT_VALID_ALL) { +} + + +StackFrame* StackwalkerRISCV::GetContextFrame() { + if (!context_) { + BPLOG(ERROR) << "Can't get context frame without context"; + return NULL; + } + + StackFrameRISCV* frame = new StackFrameRISCV(); + + frame->context = *context_; + frame->context_validity = context_frame_validity_; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + frame->instruction = frame->context.pc; + + return frame; +} + +StackFrameRISCV* StackwalkerRISCV::GetCallerByCFIFrameInfo( + const vector& frames, + CFIFrameInfo* cfi_frame_info) { + StackFrameRISCV* last_frame = + static_cast(frames.back()); + + // Populate a dictionary with the valid register values in last_frame. + CFIFrameInfo::RegisterValueMap callee_registers; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_PC) + callee_registers["pc"] = last_frame->context.pc; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_RA) + callee_registers["ra"] = last_frame->context.ra; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_SP) + callee_registers["sp"] = last_frame->context.sp; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_GP) + callee_registers["gp"] = last_frame->context.gp; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_TP) + callee_registers["tp"] = last_frame->context.tp; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_T0) + callee_registers["t0"] = last_frame->context.t0; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_T1) + callee_registers["t1"] = last_frame->context.t1; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_T2) + callee_registers["t2"] = last_frame->context.t2; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S0) + callee_registers["s0"] = last_frame->context.s0; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S1) + callee_registers["s1"] = last_frame->context.s1; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A0) + callee_registers["a0"] = last_frame->context.a0; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A1) + callee_registers["a1"] = last_frame->context.a1; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A2) + callee_registers["a2"] = last_frame->context.a2; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A3) + callee_registers["a3"] = last_frame->context.a3; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A4) + callee_registers["a4"] = last_frame->context.a4; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A5) + callee_registers["a5"] = last_frame->context.a5; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A6) + callee_registers["a6"] = last_frame->context.a6; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_A7) + callee_registers["a7"] = last_frame->context.a7; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S2) + callee_registers["s2"] = last_frame->context.s2; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S3) + callee_registers["s3"] = last_frame->context.s3; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S4) + callee_registers["s4"] = last_frame->context.s4; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S5) + callee_registers["s5"] = last_frame->context.s5; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S6) + callee_registers["s6"] = last_frame->context.s6; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S7) + callee_registers["s7"] = last_frame->context.s7; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S8) + callee_registers["s8"] = last_frame->context.s8; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S9) + callee_registers["s9"] = last_frame->context.s9; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S10) + callee_registers["s10"] = last_frame->context.s10; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_S11) + callee_registers["s11"] = last_frame->context.s11; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_T3) + callee_registers["t3"] = last_frame->context.t3; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_T4) + callee_registers["t4"] = last_frame->context.t4; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_T5) + callee_registers["t5"] = last_frame->context.t5; + if (last_frame->context_validity & StackFrameRISCV::CONTEXT_VALID_T6) + callee_registers["t6"] = last_frame->context.t6; + + // 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. + CFIFrameInfo::RegisterValueMap::iterator entry; + scoped_ptr frame(new StackFrameRISCV()); + entry = caller_registers.find("pc"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_PC; + frame->context.pc = entry->second; + } else{ + // If the CFI doesn't recover the PC explicitly, then use .ra. + entry = caller_registers.find(".ra"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_PC; + frame->context.pc = entry->second; + } + } + entry = caller_registers.find("ra"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_RA; + frame->context.ra = entry->second; + } + entry = caller_registers.find("sp"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_SP; + frame->context.sp = entry->second; + } else { + // If the CFI doesn't recover the SP explicitly, then use .cfa. + entry = caller_registers.find(".cfa"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_SP; + frame->context.sp = entry->second; + } + } + entry = caller_registers.find("gp"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_GP; + frame->context.gp = entry->second; + } + entry = caller_registers.find("tp"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_TP; + frame->context.tp = entry->second; + } + entry = caller_registers.find("t0"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_T0; + frame->context.t0 = entry->second; + } + entry = caller_registers.find("t1"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_T1; + frame->context.t1 = entry->second; + } + entry = caller_registers.find("t2"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_T2; + frame->context.t2 = entry->second; + } + entry = caller_registers.find("s0"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S0; + frame->context.s0 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S0) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S0; + frame->context.s0 = last_frame->context.s0; + } + entry = caller_registers.find("s1"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S1; + frame->context.s1 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S1) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S1; + frame->context.s1 = last_frame->context.s1; + } + entry = caller_registers.find("a0"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A0; + frame->context.a0 = entry->second; + } + entry = caller_registers.find("a1"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A1; + frame->context.a1 = entry->second; + } + entry = caller_registers.find("a2"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A2; + frame->context.a2 = entry->second; + } + entry = caller_registers.find("a3"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A3; + frame->context.a3 = entry->second; + } + entry = caller_registers.find("a4"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A4; + frame->context.a4 = entry->second; + } + entry = caller_registers.find("a5"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A5; + frame->context.a5 = entry->second; + } + entry = caller_registers.find("a6"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A6; + frame->context.a6 = entry->second; + } + entry = caller_registers.find("a7"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_A7; + frame->context.a7 = entry->second; + } + entry = caller_registers.find("s2"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S2; + frame->context.s2 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S2) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S2; + frame->context.s2 = last_frame->context.s2; + } + entry = caller_registers.find("s3"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S3; + frame->context.s3 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S3) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S3; + frame->context.s3 = last_frame->context.s3; + } + entry = caller_registers.find("s4"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S4; + frame->context.s4 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S4) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S4; + frame->context.s4 = last_frame->context.s4; + } + entry = caller_registers.find("s5"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S5; + frame->context.s5 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S5) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S5; + frame->context.s5 = last_frame->context.s5; + } + entry = caller_registers.find("s6"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S6; + frame->context.s6 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S6) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S6; + frame->context.s6 = last_frame->context.s6; + } + entry = caller_registers.find("s7"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S7; + frame->context.s7 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S7) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S7; + frame->context.s7 = last_frame->context.s7; + } + entry = caller_registers.find("s8"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S8; + frame->context.s8 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S8) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S8; + frame->context.s8 = last_frame->context.s8; + } + entry = caller_registers.find("s9"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S9; + frame->context.s9 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S9) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S9; + frame->context.s9 = last_frame->context.s9; + } + entry = caller_registers.find("s10"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S10; + frame->context.s10 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S10) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S10; + frame->context.s10 = last_frame->context.s10; + } + entry = caller_registers.find("s11"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S11; + frame->context.s11 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV::CONTEXT_VALID_S11) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_S11; + frame->context.s11 = last_frame->context.s11; + } + entry = caller_registers.find("t3"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_T3; + frame->context.t3 = entry->second; + } + entry = caller_registers.find("t4"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_T4; + frame->context.t4 = entry->second; + } + entry = caller_registers.find("t5"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_T5; + frame->context.t5 = entry->second; + } + entry = caller_registers.find("t6"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV::CONTEXT_VALID_T6; + frame->context.t6 = entry->second; + } + + // If we didn't recover the PC and the SP, then the frame isn't very useful. + static const uint64_t essentials = (StackFrameRISCV::CONTEXT_VALID_SP + | StackFrameRISCV::CONTEXT_VALID_PC); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + frame->trust = StackFrame::FRAME_TRUST_CFI; + return frame.release(); +} + +StackFrameRISCV* StackwalkerRISCV::GetCallerByStackScan( + const vector& frames) { + StackFrameRISCV* last_frame = + static_cast(frames.back()); + uint32_t last_sp = last_frame->context.sp; + uint32_t caller_sp, caller_pc; + + if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc, + last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT)) { + // 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 += 4; + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameRISCV* frame = new StackFrameRISCV(); + + frame->trust = StackFrame::FRAME_TRUST_SCAN; + frame->context = last_frame->context; + frame->context.pc = caller_pc; + frame->context.sp = caller_sp; + frame->context_validity = StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP; + + return frame; +} + + StackFrameRISCV* StackwalkerRISCV::GetCallerByFramePointer( + const vector& frames) { + StackFrameRISCV* last_frame = + static_cast(frames.back()); + + uint32_t last_fp = last_frame->context.s0; + + uint32_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; + } + + uint32_t caller_ra = 0; + if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 4, &caller_ra)) { + BPLOG(ERROR) << "Unable to read caller_ra from last_fp + 4: 0x" + << std::hex << (last_fp + 4); + return NULL; + } + + uint32_t caller_sp = last_fp ? last_fp + 8 : last_frame->context.s0; + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameRISCV* frame = new StackFrameRISCV(); + + frame->trust = StackFrame::FRAME_TRUST_FP; + frame->context = last_frame->context; + frame->context.s0 = caller_fp; + frame->context.sp = caller_sp; + frame->context.pc = last_frame->context.ra; + frame->context.ra = caller_ra; + frame->context_validity = StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_RA | + StackFrameRISCV::CONTEXT_VALID_S0 | + StackFrameRISCV::CONTEXT_VALID_SP; + return frame; +} + +StackFrame* StackwalkerRISCV::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& frames = *stack->frames(); + StackFrameRISCV* last_frame = + static_cast(frames.back()); + scoped_ptr frame; + + // Try to recover caller information from CFI. + scoped_ptr 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; + + // Should we terminate the stack walk? (end-of-stack or broken invariant) + if (TerminateWalk(frame->context.pc, frame->context.sp, + last_frame->context.sp, + last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT)) { + 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. + // RISCV 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.pc. + frame->instruction = frame->context.pc - 4; + + return frame.release(); +} + +} // namespace google_breakpad diff --git a/src/processor/stackwalker_riscv.h b/src/processor/stackwalker_riscv.h new file mode 100644 index 00000000..863914d8 --- /dev/null +++ b/src/processor/stackwalker_riscv.h @@ -0,0 +1,100 @@ +// Copyright 2013 Google LLC +// +// 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 LLC 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_riscv.h: riscv-specific stackwalker. + * + * Provides stack frames given riscv register context and a memory region + * corresponding to a riscv stack. + * + * Author: Iacopo Colonnelli + */ + +#ifndef PROCESSOR_STACKWALKER_RISCV_H__ +#define PROCESSOR_STACKWALKER_RISCV_H__ + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerRISCV : public Stackwalker { +public: + // Context is a riscv context object that gives access to riscv-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. + StackwalkerRISCV(const SystemInfo* system_info, + const MDRawContextRISCV* 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 riscv 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. + StackFrameRISCV* GetCallerByCFIFrameInfo( + const vector& frames, CFIFrameInfo* cfi_frame_info); + + // Use the frame pointer. The caller takes ownership of the returned frame. + // Return NULL on failure. + StackFrameRISCV* GetCallerByFramePointer( + const vector& frames); + + // Scan the stack for plausible return addresses. The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameRISCV* GetCallerByStackScan( + const vector& frames); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextRISCV* 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_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_STACKWALKER_RISCV_H__ diff --git a/src/processor/stackwalker_riscv64.cc b/src/processor/stackwalker_riscv64.cc new file mode 100644 index 00000000..d97bad63 --- /dev/null +++ b/src/processor/stackwalker_riscv64.cc @@ -0,0 +1,535 @@ +// Copyright 2013 Google LLC +// +// 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 LLC 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_riscv64.cc: riscv64-specific stackwalker. + * + * See stackwalker_riscv64.h for documentation. + * + * Author: Iacopo Colonnelli + */ + +#include "common/scoped_ptr.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "google_breakpad/processor/system_info.h" +#include "processor/cfi_frame_info.h" +#include "processor/logging.h" +#include "processor/stackwalker_riscv64.h" + +namespace google_breakpad { + +StackwalkerRISCV64::StackwalkerRISCV64(const SystemInfo* system_info, + const MDRawContextRISCV64* context, + MemoryRegion* memory, + const CodeModules* modules, + StackFrameSymbolizer* resolver_helper) + : Stackwalker(system_info, memory, modules, resolver_helper), + context_(context), + context_frame_validity_(StackFrameRISCV::CONTEXT_VALID_ALL) { +} + + +StackFrame* StackwalkerRISCV64::GetContextFrame() { + if (!context_) { + BPLOG(ERROR) << "Can't get context frame without context"; + return NULL; + } + + StackFrameRISCV64* frame = new StackFrameRISCV64(); + + frame->context = *context_; + frame->context_validity = context_frame_validity_; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + frame->instruction = frame->context.pc; + + return frame; +} + +StackFrameRISCV64* StackwalkerRISCV64::GetCallerByCFIFrameInfo( + const vector& frames, + CFIFrameInfo* cfi_frame_info) { + StackFrameRISCV64* last_frame = + static_cast(frames.back()); + + // Populate a dictionary with the valid register values in last_frame. + CFIFrameInfo::RegisterValueMap callee_registers; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_PC) + callee_registers["pc"] = last_frame->context.pc; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_RA) + callee_registers["ra"] = last_frame->context.ra; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_SP) + callee_registers["sp"] = last_frame->context.sp; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_GP) + callee_registers["gp"] = last_frame->context.gp; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_TP) + callee_registers["tp"] = last_frame->context.tp; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_T0) + callee_registers["t0"] = last_frame->context.t0; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_T1) + callee_registers["t1"] = last_frame->context.t1; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_T2) + callee_registers["t2"] = last_frame->context.t2; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S0) + callee_registers["s0"] = last_frame->context.s0; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S1) + callee_registers["s1"] = last_frame->context.s1; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A0) + callee_registers["a0"] = last_frame->context.a0; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A1) + callee_registers["a1"] = last_frame->context.a1; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A2) + callee_registers["a2"] = last_frame->context.a2; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A3) + callee_registers["a3"] = last_frame->context.a3; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A4) + callee_registers["a4"] = last_frame->context.a4; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A5) + callee_registers["a5"] = last_frame->context.a5; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A6) + callee_registers["a6"] = last_frame->context.a6; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_A7) + callee_registers["a7"] = last_frame->context.a7; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S2) + callee_registers["s2"] = last_frame->context.s2; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S3) + callee_registers["s3"] = last_frame->context.s3; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S4) + callee_registers["s4"] = last_frame->context.s4; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S5) + callee_registers["s5"] = last_frame->context.s5; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S6) + callee_registers["s6"] = last_frame->context.s6; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S7) + callee_registers["s7"] = last_frame->context.s7; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S8) + callee_registers["s8"] = last_frame->context.s8; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S9) + callee_registers["s9"] = last_frame->context.s9; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S10) + callee_registers["s10"] = last_frame->context.s10; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_S11) + callee_registers["s11"] = last_frame->context.s11; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_T3) + callee_registers["t3"] = last_frame->context.t3; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_T4) + callee_registers["t4"] = last_frame->context.t4; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_T5) + callee_registers["t5"] = last_frame->context.t5; + if (last_frame->context_validity & StackFrameRISCV64::CONTEXT_VALID_T6) + callee_registers["t6"] = last_frame->context.t6; + + // 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. + CFIFrameInfo::RegisterValueMap::iterator entry; + scoped_ptr frame(new StackFrameRISCV64()); + entry = caller_registers.find("pc"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_PC; + frame->context.pc = entry->second; + } else{ + // If the CFI doesn't recover the PC explicitly, then use .ra. + entry = caller_registers.find(".ra"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_PC; + frame->context.pc = entry->second; + } + } + entry = caller_registers.find("ra"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_RA; + frame->context.ra = entry->second; + } + entry = caller_registers.find("sp"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_SP; + frame->context.sp = entry->second; + } else { + // If the CFI doesn't recover the SP explicitly, then use .cfa. + entry = caller_registers.find(".cfa"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_SP; + frame->context.sp = entry->second; + } + } + entry = caller_registers.find("gp"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_GP; + frame->context.gp = entry->second; + } + entry = caller_registers.find("tp"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_TP; + frame->context.tp = entry->second; + } + entry = caller_registers.find("t0"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_T0; + frame->context.t0 = entry->second; + } + entry = caller_registers.find("t1"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_T1; + frame->context.t1 = entry->second; + } + entry = caller_registers.find("t2"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_T2; + frame->context.t2 = entry->second; + } + entry = caller_registers.find("s0"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S0; + frame->context.s0 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S0) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S0; + frame->context.s0 = last_frame->context.s0; + } + entry = caller_registers.find("s1"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S1; + frame->context.s1 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S1) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S1; + frame->context.s1 = last_frame->context.s1; + } + entry = caller_registers.find("a0"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A0; + frame->context.a0 = entry->second; + } + entry = caller_registers.find("a1"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A1; + frame->context.a1 = entry->second; + } + entry = caller_registers.find("a2"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A2; + frame->context.a2 = entry->second; + } + entry = caller_registers.find("a3"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A3; + frame->context.a3 = entry->second; + } + entry = caller_registers.find("a4"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A4; + frame->context.a4 = entry->second; + } + entry = caller_registers.find("a5"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A5; + frame->context.a5 = entry->second; + } + entry = caller_registers.find("a6"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A6; + frame->context.a6 = entry->second; + } + entry = caller_registers.find("a7"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_A7; + frame->context.a7 = entry->second; + } + entry = caller_registers.find("s2"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S2; + frame->context.s2 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S2) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S2; + frame->context.s2 = last_frame->context.s2; + } + entry = caller_registers.find("s3"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S3; + frame->context.s3 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S3) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S3; + frame->context.s3 = last_frame->context.s3; + } + entry = caller_registers.find("s4"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S4; + frame->context.s4 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S4) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S4; + frame->context.s4 = last_frame->context.s4; + } + entry = caller_registers.find("s5"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S5; + frame->context.s5 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S5) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S5; + frame->context.s5 = last_frame->context.s5; + } + entry = caller_registers.find("s6"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S6; + frame->context.s6 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S6) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S6; + frame->context.s6 = last_frame->context.s6; + } + entry = caller_registers.find("s7"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S7; + frame->context.s7 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S7) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S7; + frame->context.s7 = last_frame->context.s7; + } + entry = caller_registers.find("s8"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S8; + frame->context.s8 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S8) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S8; + frame->context.s8 = last_frame->context.s8; + } + entry = caller_registers.find("s9"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S9; + frame->context.s9 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S9) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S9; + frame->context.s9 = last_frame->context.s9; + } + entry = caller_registers.find("s10"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S10; + frame->context.s10 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S10) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S10; + frame->context.s10 = last_frame->context.s10; + } + entry = caller_registers.find("s11"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S11; + frame->context.s11 = entry->second; + } else if (last_frame->context_validity & + StackFrameRISCV64::CONTEXT_VALID_S11) { + // Since the register is callee-saves, assume the callee + // has not yet changed it. + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_S11; + frame->context.s11 = last_frame->context.s11; + } + entry = caller_registers.find("t3"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_T3; + frame->context.t3 = entry->second; + } + entry = caller_registers.find("t4"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_T4; + frame->context.t4 = entry->second; + } + entry = caller_registers.find("t5"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_T5; + frame->context.t5 = entry->second; + } + entry = caller_registers.find("t6"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameRISCV64::CONTEXT_VALID_T6; + frame->context.t6 = entry->second; + } + + // If we didn't recover the PC and the SP, then the frame isn't very useful. + static const uint64_t essentials = (StackFrameRISCV64::CONTEXT_VALID_SP + | StackFrameRISCV64::CONTEXT_VALID_PC); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + frame->trust = StackFrame::FRAME_TRUST_CFI; + return frame.release(); +} + +StackFrameRISCV64* StackwalkerRISCV64::GetCallerByStackScan( + const vector& frames) { + StackFrameRISCV64* last_frame = + static_cast(frames.back()); + uint64_t last_sp = last_frame->context.sp; + uint64_t caller_sp, caller_pc; + + if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc, + last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT)) { + // 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. + StackFrameRISCV64* frame = new StackFrameRISCV64(); + + frame->trust = StackFrame::FRAME_TRUST_SCAN; + frame->context = last_frame->context; + frame->context.pc = caller_pc; + frame->context.sp = caller_sp; + frame->context_validity = StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP; + + return frame; +} + +StackFrameRISCV64* StackwalkerRISCV64::GetCallerByFramePointer( + const vector& frames) { + StackFrameRISCV64* last_frame = + static_cast(frames.back()); + + uint64_t last_fp = last_frame->context.s0; + + 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_ra = 0; + if (last_fp && !memory_->GetMemoryAtAddress(last_fp + 8, &caller_ra)) { + BPLOG(ERROR) << "Unable to read caller_ra from last_fp + 8: 0x" + << std::hex << (last_fp + 8); + return NULL; + } + + uint64_t caller_sp = last_fp ? last_fp + 16 : last_frame->context.s0; + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameRISCV64* frame = new StackFrameRISCV64(); + + frame->trust = StackFrame::FRAME_TRUST_FP; + frame->context = last_frame->context; + frame->context.s0 = caller_fp; + frame->context.sp = caller_sp; + frame->context.pc = last_frame->context.ra; + frame->context.ra = caller_ra; + frame->context_validity = StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_RA | + StackFrameRISCV64::CONTEXT_VALID_S0 | + StackFrameRISCV64::CONTEXT_VALID_SP; + return frame; +} + +StackFrame* StackwalkerRISCV64::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& frames = *stack->frames(); + StackFrameRISCV64* last_frame = + static_cast(frames.back()); + scoped_ptr frame; + + // Try to recover caller information from CFI. + scoped_ptr 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; + + // Should we terminate the stack walk? (end-of-stack or broken invariant) + if (TerminateWalk(frame->context.pc, frame->context.sp, + last_frame->context.sp, + last_frame->trust == StackFrame::FRAME_TRUST_CONTEXT)) { + 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. + // RISCV 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.pc. + frame->instruction = frame->context.pc - 4; + + return frame.release(); +} + +} // namespace google_breakpad diff --git a/src/processor/stackwalker_riscv64.h b/src/processor/stackwalker_riscv64.h new file mode 100644 index 00000000..89ab12ff --- /dev/null +++ b/src/processor/stackwalker_riscv64.h @@ -0,0 +1,100 @@ +// Copyright 2013 Google LLC +// +// 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 LLC 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_riscv64.h: riscv64-specific stackwalker. + * + * Provides stack frames given riscv64 register context and a memory region + * corresponding to a riscv64 stack. + * + * Author: Iacopo Colonnelli + */ + +#ifndef PROCESSOR_STACKWALKER_RISCV64_H__ +#define PROCESSOR_STACKWALKER_RISCV64_H__ + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerRISCV64 : public Stackwalker { +public: + // Context is a riscv context object that gives access to riscv-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. + StackwalkerRISCV64(const SystemInfo* system_info, + const MDRawContextRISCV64* 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 riscv 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. + StackFrameRISCV64* GetCallerByCFIFrameInfo( + const vector& frames, CFIFrameInfo* cfi_frame_info); + + // Use the frame pointer. The caller takes ownership of the returned frame. + // Return NULL on failure. + StackFrameRISCV64* GetCallerByFramePointer( + const vector& frames); + + // Scan the stack for plausible return addresses. The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameRISCV64* GetCallerByStackScan( + const vector& frames); + + // Stores the CPU context corresponding to the innermost stack frame to + // be returned by GetContextFrame. + const MDRawContextRISCV64* 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_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_STACKWALKER_RISCV64_H__ diff --git a/src/processor/stackwalker_riscv64_unittest.cc b/src/processor/stackwalker_riscv64_unittest.cc new file mode 100644 index 00000000..73c06264 --- /dev/null +++ b/src/processor/stackwalker_riscv64_unittest.cc @@ -0,0 +1,883 @@ +// Copyright 2013 Google LLC +// +// 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 LLC 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_riscv64_unittest.cc: Unit tests for StackwalkerRISCV64 class. + * + * Author: Iacopo Colonnelli + */ + +#include +#include +#include + +#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_riscv64.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::StackFrameRISCV64; +using google_breakpad::Stackwalker; +using google_breakpad::StackwalkerRISCV64; +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::DoAll; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerRISCV64Fixture { +public: + StackwalkerRISCV64Fixture() + : 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. + system_info.os = "iOS"; + system_info.os_short = "ios"; + system_info.cpu = "riscv64"; + 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(MDRawContextRISCV64 *raw_context) { + uint8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextRISCV64 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 SanityCheck: public StackwalkerRISCV64Fixture, 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); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, &modules, + &frame_symbolizer); + // This should succeed even without a resolver or supplier. + vector modules_without_symbols; + vector 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()); + StackFrameRISCV64 *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_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetContextFrame: public StackwalkerRISCV64Fixture, 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); + StackwalkerRISCV64 walker(&system_info, &raw_context, NULL, &modules, + &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + StackFrameRISCV64 *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_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetCallerFrame: public StackwalkerRISCV64Fixture, 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.pc = 0x40005510; + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, &modules, + &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV64::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameRISCV64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.pc); + EXPECT_EQ(frame1_sp.Value(), frame1->context.sp); + + StackFrameRISCV64 *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust); + ASSERT_EQ((StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.pc); + EXPECT_EQ(frame2_sp.Value(), frame2->context.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.pc = 0x40000200; + raw_context.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); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV64::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); + + StackFrameRISCV64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address, frame1->context.pc); + EXPECT_EQ(frame1_sp.Value(), frame1->context.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(336, 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.pc = 0x40005510; + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV64::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameRISCV64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.pc); + EXPECT_EQ(frame1_sp.Value(), frame1->context.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.pc = 0x40005510; + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + Stackwalker::set_max_frames_scanned(0); + + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV64::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); +} + +class GetFramesByFramePointer: + public StackwalkerRISCV64Fixture, + 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.pc = 0x40005510; + raw_context.ra = return_address1; + raw_context.s0 = frame1_fp.Value(); + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV64 walker(&system_info, &raw_context, + &stack_region, &modules, &frame_symbolizer); + + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV64::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameRISCV64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_RA | + StackFrameRISCV64::CONTEXT_VALID_S0 | + StackFrameRISCV64::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.pc); + EXPECT_EQ(return_address2, frame1->context.ra); + EXPECT_EQ(frame1_sp.Value(), frame1->context.sp); + EXPECT_EQ(frame2_fp.Value(), frame1->context.s0); + + StackFrameRISCV64 *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust); + ASSERT_EQ((StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_RA | + StackFrameRISCV64::CONTEXT_VALID_S0 | + StackFrameRISCV64::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.pc); + EXPECT_EQ(0U, frame2->context.ra); + EXPECT_EQ(frame2_sp.Value(), frame2->context.sp); + EXPECT_EQ(0U, frame2->context.s0); +} + +struct CFIFixture: public StackwalkerRISCV64Fixture { + 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 return + // address register (ra). + "STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: ra\n" + // Push s1, s2, the frame pointer (s0) and the + // return address register. + "STACK CFI 4001 .cfa: sp 32 + .ra: .cfa -8 + ^" + " s1: .cfa -32 + ^ s2: .cfa -24 + ^ " + " s0: .cfa -16 + ^\n" + // Save s1..s4 in a1..a4: verify that we populate + // the youngest frame with all the values we have. + "STACK CFI 4002 s1: a1 s2: a2 s3: a3 s4: a4\n" + // Restore s1..s4. Save the non-callee-saves register a2. + "STACK CFI 4003 .cfa: sp 40 + a2: .cfa 40 - ^" + " s1: s1 s2: s2 s3: s3 s4: s4\n" + // Move the .cfa back eight bytes, to point at the return + // address, and restore the sp explicitly. + "STACK CFI 4005 .cfa: sp 32 + a2: .cfa 32 - ^" + " s0: .cfa 8 - ^ .ra: .cfa ^ sp: .cfa 8 +\n" + // Recover the PC explicitly from a new stack slot; + // provide garbage for the .ra. + "STACK CFI 4006 .cfa: sp 40 + pc: .cfa 40 - ^\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 8 - .ra: ra\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.pc = 0x0000000040005510L; + expected.sp = 0x0000000080000000L; + expected.s1 = 0x5e68b5d5b5d55e68L; + expected.s2 = 0x34f3ebd1ebd134f3L; + expected.s3 = 0x74bca31ea31e74bcL; + expected.s4 = 0x16b32dcb2dcb16b3L; + expected.s5 = 0x21372ada2ada2137L; + expected.s6 = 0x557dbbbbbbbb557dL; + expected.s7 = 0x8ca748bf48bf8ca7L; + expected.s8 = 0x21f0ab46ab4621f0L; + expected.s9 = 0x146732b732b71467L; + expected.s10 = 0xa673645fa673645fL; + expected.s11 = 0xa673645fa673645fL; + expected.s0 = 0xe11081128112e110L; + + // 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 = (StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP | + StackFrameRISCV64::CONTEXT_VALID_S1 | + StackFrameRISCV64::CONTEXT_VALID_S2 | + StackFrameRISCV64::CONTEXT_VALID_S3 | + StackFrameRISCV64::CONTEXT_VALID_S4 | + StackFrameRISCV64::CONTEXT_VALID_S5 | + StackFrameRISCV64::CONTEXT_VALID_S6 | + StackFrameRISCV64::CONTEXT_VALID_S7 | + StackFrameRISCV64::CONTEXT_VALID_S8 | + StackFrameRISCV64::CONTEXT_VALID_S9 | + StackFrameRISCV64::CONTEXT_VALID_S10 | + StackFrameRISCV64::CONTEXT_VALID_S11 | + StackFrameRISCV64::CONTEXT_VALID_S0); + + // By default, context frames provide all registers, as normal. + context_frame_validity = StackFrameRISCV64::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.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + walker.SetContextFrameValidity(context_frame_validity); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV64 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(context_frame_validity, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x0000000040004000UL, frame0->function_base); + + StackFrameRISCV64 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ(expected_validity, frame1->context_validity); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_A2) + EXPECT_EQ(expected.a2, frame1->context.a2); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S1) + EXPECT_EQ(expected.s1, frame1->context.s1); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S2) + EXPECT_EQ(expected.s2, frame1->context.s2); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S3) + EXPECT_EQ(expected.s3, frame1->context.s3); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S4) + EXPECT_EQ(expected.s4, frame1->context.s4); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S5) + EXPECT_EQ(expected.s5, frame1->context.s5); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S6) + EXPECT_EQ(expected.s6, frame1->context.s6); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S7) + EXPECT_EQ(expected.s7, frame1->context.s7); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S8) + EXPECT_EQ(expected.s8, frame1->context.s8); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S9) + EXPECT_EQ(expected.s9, frame1->context.s9); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S10) + EXPECT_EQ(expected.s10, frame1->context.s10); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S11) + EXPECT_EQ(expected.s11, frame1->context.s11); + if (expected_validity & StackFrameRISCV64::CONTEXT_VALID_S0) + EXPECT_EQ(expected.s0, frame1->context.s0); + + // 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.sp, frame1->context.sp); + EXPECT_EQ(expected.pc, frame1->context.pc); + EXPECT_EQ(expected.pc, frame1->instruction + 4); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values we expect to find for the caller's registers. + MDRawContextRISCV64 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.sp; + raw_context.pc = 0x0000000040004000L; + raw_context.ra = 0x0000000040005510L; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_sp = expected.sp; + stack_section + .D64(0x5e68b5d5b5d55e68L) // saved s1 + .D64(0x34f3ebd1ebd134f3L) // saved s2 + .D64(0xe11081128112e110L) // saved s0 + .D64(0x0000000040005510L) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x0000000040004001L; + // distinct callee s1, s2 and s0 + raw_context.s1 = 0xadc9f635a635adc9L; + raw_context.s2 = 0x623135ac35ac6231L; + raw_context.s0 = 0x5fc4be14be145fc4L; + CheckWalk(); +} + +// As above, but unwind from a context that has only the PC and SP. +TEST_F(CFI, At4001LimitedValidity) { + Label frame1_sp = expected.sp; + stack_section + .D64(0x5e68b5d5b5d55e68L) // saved s1 + .D64(0x34f3ebd1ebd134f3L) // saved s2 + .D64(0xe11081128112e110L) // saved s0 + .D64(0x0000000040005510L) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + context_frame_validity = StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP; + raw_context.pc = 0x0000000040004001L; + raw_context.s0 = 0x5fc4be14be145fc4L; + + expected_validity = (StackFrameRISCV64::CONTEXT_VALID_PC | + StackFrameRISCV64::CONTEXT_VALID_SP | + StackFrameRISCV64::CONTEXT_VALID_S0 | + StackFrameRISCV64::CONTEXT_VALID_S1 | + StackFrameRISCV64::CONTEXT_VALID_S2); + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_sp = expected.sp; + stack_section + .D64(0xff3dfb81fb81ff3dL) // no longer saved s1 + .D64(0x34f3ebd1ebd134f3L) // no longer saved s2 + .D64(0xe11081128112e110L) // saved s0 + .D64(0x0000000040005510L) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x0000000040004002L; + raw_context.a1 = 0x5e68b5d5b5d55e68L; // saved s1 + raw_context.a2 = 0x34f3ebd1ebd134f3L; // saved s2 + raw_context.a3 = 0x74bca31ea31e74bcL; // saved s3 + raw_context.a4 = 0x16b32dcb2dcb16b3L; // saved s4 + raw_context.s1 = 0xadc9f635a635adc9L; // distinct callee s1 + raw_context.s2 = 0x623135ac35ac6231L; // distinct callee s2 + raw_context.s3 = 0xac4543564356ac45L; // distinct callee s3 + raw_context.s4 = 0x2561562f562f2561L; // distinct callee s4 + // distinct callee s0 + raw_context.s0 = 0x5fc4be14be145fc4L; + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_sp = expected.sp; + stack_section + .D64(0xdd5a48c848c8dd5aL) // saved a2 (even though it's not callee-saves) + .D64(0xff3dfb81fb81ff3dL) // no longer saved s1 + .D64(0x34f3ebd1ebd134f3L) // no longer saved s2 + .D64(0xe11081128112e110L) // saved s0 + .D64(0x0000000040005510L) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x0000000040004003L; + // distinct callee a2 and fp + raw_context.a2 = 0xfb756319fb756319L; + raw_context.s0 = 0x5fc4be14be145fc4L; + // caller's a2 + expected.a2 = 0xdd5a48c848c8dd5aL; + expected_validity |= StackFrameRISCV64::CONTEXT_VALID_A2; + 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.sp; + stack_section + .D64(0xdd5a48c848c8dd5aL) // saved a2 (even though it's not callee-saves) + .D64(0xff3dfb81fb81ff3dL) // no longer saved s1 + .D64(0x34f3ebd1ebd134f3L) // no longer saved s2 + .D64(0xe11081128112e110L) // saved s0 + .D64(0x0000000040005510L) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x0000000040004004L; + // distinct callee a2 and s0 + raw_context.a2 = 0xfb756319fb756319L; + raw_context.s0 = 0x5fc4be14be145fc4L; + // caller's a2 + expected.a2 = 0xdd5a48c848c8dd5aL; + expected_validity |= StackFrameRISCV64::CONTEXT_VALID_A2; + 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.sp; + stack_section + .D64(0xdd5a48c848c8dd5aL) // saved a2 (even though it's not callee-saves) + .D64(0xff3dfb81fb81ff3dL) // no longer saved s1 + .D64(0x34f3ebd1ebd134f3L) // no longer saved s2 + .D64(0xe11081128112e110L) // saved s0 + .D64(0x0000000040005510L) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x0000000040004005L; + raw_context.a2 = 0xfb756319fb756319L; // distinct callee a2 + expected.a2 = 0xdd5a48c848c8dd5aL; // caller's a2 + expected_validity |= StackFrameRISCV64::CONTEXT_VALID_A2; + 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.sp; + stack_section + .D64(0x0000000040005510L) // saved pc + .D64(0xdd5a48c848c8dd5aL) // saved a2 (even though it's not callee-saves) + .D64(0xff3dfb81fb81ff3dL) // no longer saved s1 + .D64(0x34f3ebd1ebd134f3L) // no longer saved s2 + .D64(0xe11081128112e110L) // saved s0 + .D64(0xf8d157835783f8d1L) // .ra rule recovers this, which is garbage + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x0000000040004006L; + raw_context.a2 = 0xfb756319fb756319L; // distinct callee a2 + expected.a2 = 0xdd5a48c848c8dd5aL; // caller's a2 + expected_validity |= StackFrameRISCV64::CONTEXT_VALID_A2; + CheckWalk(); +} + +// Check that we reject rules that would cause the stack pointer to +// move in the wrong direction. +TEST_F(CFI, RejectBackwards) { + raw_context.pc = 0x0000000040006000L; + raw_context.sp = 0x0000000080000000L; + raw_context.ra = 0x0000000040005510L; + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); +} + +// Check that we reject rules whose expressions' evaluation fails. +TEST_F(CFI, RejectBadExpressions) { + raw_context.pc = 0x0000000040007000L; + raw_context.sp = 0x0000000080000000L; + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV64 walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); +} diff --git a/src/processor/stackwalker_riscv_unittest.cc b/src/processor/stackwalker_riscv_unittest.cc new file mode 100644 index 00000000..f4a6b79c --- /dev/null +++ b/src/processor/stackwalker_riscv_unittest.cc @@ -0,0 +1,883 @@ +// Copyright 2013 Google LLC +// +// 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 LLC 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_riscv_unittest.cc: Unit tests for StackwalkerRISCV class. + * + * Author: Iacopo Colonnelli + */ + +#include +#include +#include + +#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_riscv.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::StackFrameRISCV; +using google_breakpad::Stackwalker; +using google_breakpad::StackwalkerRISCV; +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::DoAll; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerRISCVFixture { +public: + StackwalkerRISCVFixture() + : 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. + system_info.os = "iOS"; + system_info.os_short = "ios"; + system_info.cpu = "riscv"; + 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(MDRawContextRISCV *raw_context) { + uint8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextRISCV 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 SanityCheck: public StackwalkerRISCVFixture, 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); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + // This should succeed even without a resolver or supplier. + vector modules_without_symbols; + vector 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()); + StackFrameRISCV *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_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetContextFrame: public StackwalkerRISCVFixture, 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); + StackwalkerRISCV walker(&system_info, &raw_context, NULL, &modules, + &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + StackFrameRISCV *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_EQ(0, memcmp(&raw_context, &frame->context, sizeof(raw_context))); +} + +class GetCallerFrame: public StackwalkerRISCVFixture, 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(8, 0) // space + + .D32(0x40090000) // junk that's not + .D32(0x60000000) // a return address + + .D32(return_address1) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(8, 0) // space + + .D32(0xF0000000) // more junk + .D32(0x0000000D) + + .D32(return_address2) // actual return address + // frame 2 + .Mark(&frame2_sp) + .Append(32, 0); // end of stack + RegionFromSection(); + + raw_context.pc = 0x40005510; + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameRISCV *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.pc); + EXPECT_EQ(frame1_sp.Value(), frame1->context.sp); + + StackFrameRISCV *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust); + ASSERT_EQ((StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.pc); + EXPECT_EQ(frame2_sp.Value(), frame2->context.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(8, 0) // space + + .D32(0x40090000) // junk that's not + .D32(0x60000000) // a return address + + .D32(0x40001000) // a couple of plausible addresses + .D32(0x5000F000) // that are not within functions + + .D32(return_address) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(32, 0); // end of stack + RegionFromSection(); + + raw_context.pc = 0x40000200; + raw_context.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); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV::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(0x40000100UL, frame0->function_base); + + StackFrameRISCV *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address, frame1->context.pc); + EXPECT_EQ(frame1_sp.Value(), frame1->context.sp); + EXPECT_EQ("marsupial", frame1->function_name); + EXPECT_EQ(0x50000100UL, 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(16, 0) // space + + .D32(0x40090000) // junk that's not + .D32(0x60000000) // a return address + + .Append(48, 0) // more space + + .D32(return_address1) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(16, 0) // space + + .D32(0xF0000000) // more junk + .D32(0x0000000D) + + .Append(168, 0) // more space + + .D32(return_address2) // actual return address + // (won't be found) + // frame 2 + .Mark(&frame2_sp) + .Append(32, 0); // end of stack + RegionFromSection(); + + raw_context.pc = 0x40005510; + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameRISCV *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust); + ASSERT_EQ((StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.pc); + EXPECT_EQ(frame1_sp.Value(), frame1->context.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(8, 0) // space + + .D32(0x40090000) // junk that's not + .D32(0x60000000) // a return address + + .D32(return_address1) // actual return address + // frame 1 + .Mark(&frame1_sp) + .Append(8, 0) // space + + .D32(0xF0000000) // more junk + .D32(0x0000000D) + + .D32(return_address2) // actual return address + // frame 2 + .Mark(&frame2_sp) + .Append(32, 0); // end of stack + RegionFromSection(); + + raw_context.pc = 0x40005510; + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + Stackwalker::set_max_frames_scanned(0); + + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); +} + +class GetFramesByFramePointer: + public StackwalkerRISCVFixture, + 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(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame1_fp) // Next fp will point to the next value. + .D32(frame2_fp) // Save current frame pointer. + .D32(return_address2) // Save current link register. + .Mark(&frame1_sp) + + // frame 1 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000) // a return address. + + .Mark(&frame2_fp) + .D32(0) + .D32(0) + .Mark(&frame2_sp) + + // frame 2 + .Append(32, 0) // Whatever values on the stack. + .D32(0x0000000D) // junk that's not + .D32(0xF0000000); // a return address. + RegionFromSection(); + + + raw_context.pc = 0x40005510; + raw_context.ra = return_address1; + raw_context.s0 = frame1_fp.Value(); + raw_context.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV walker(&system_info, &raw_context, + &stack_region, &modules, &frame_symbolizer); + + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameRISCV::CONTEXT_VALID_ALL, + frame0->context_validity); + EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context))); + + StackFrameRISCV *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_RA | + StackFrameRISCV::CONTEXT_VALID_S0 | + StackFrameRISCV::CONTEXT_VALID_SP), + frame1->context_validity); + EXPECT_EQ(return_address1, frame1->context.pc); + EXPECT_EQ(return_address2, frame1->context.ra); + EXPECT_EQ(frame1_sp.Value(), frame1->context.sp); + EXPECT_EQ(frame2_fp.Value(), frame1->context.s0); + + StackFrameRISCV *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrame::FRAME_TRUST_FP, frame2->trust); + ASSERT_EQ((StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_RA | + StackFrameRISCV::CONTEXT_VALID_S0 | + StackFrameRISCV::CONTEXT_VALID_SP), + frame2->context_validity); + EXPECT_EQ(return_address2, frame2->context.pc); + EXPECT_EQ(0U, frame2->context.ra); + EXPECT_EQ(frame2_sp.Value(), frame2->context.sp); + EXPECT_EQ(0U, frame2->context.s0); +} + +struct CFIFixture: public StackwalkerRISCVFixture { + 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 return + // address register (ra). + "STACK CFI INIT 4000 100 .cfa: sp 0 + .ra: ra\n" + // Push s1, s2, the frame pointer (s0) and the + // return address register. + "STACK CFI 4001 .cfa: sp 16 + .ra: .cfa -4 + ^" + " s1: .cfa -16 + ^ s2: .cfa -12 + ^ " + " s0: .cfa -8 + ^\n" + // Save s1..s4 in a1..a4: verify that we populate + // the youngest frame with all the values we have. + "STACK CFI 4002 s1: a1 s2: a2 s3: a3 s4: a4\n" + // Restore s1..s4. Save the non-callee-saves register a2. + "STACK CFI 4003 .cfa: sp 20 + a2: .cfa 20 - ^" + " s1: s1 s2: s2 s3: s3 s4: s4\n" + // Move the .cfa back eight bytes, to point at the return + // address, and restore the sp explicitly. + "STACK CFI 4005 .cfa: sp 16 + a2: .cfa 16 - ^" + " s0: .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 20 + pc: .cfa 20 - ^\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: ra\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.pc = 0x40005510; + expected.sp = 0x80000000; + expected.s1 = 0xb5d55e68; + expected.s2 = 0xebd134f3; + expected.s3 = 0xa31e74bc; + expected.s4 = 0x2dcb16b3; + expected.s5 = 0x2ada2137; + expected.s6 = 0xbbbb557d; + expected.s7 = 0x48bf8ca7; + expected.s8 = 0xab4621f0; + expected.s9 = 0x32b71467; + expected.s10 = 0xa673645f; + expected.s11 = 0xa673645f; + expected.s0 = 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 = (StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP | + StackFrameRISCV::CONTEXT_VALID_S1 | + StackFrameRISCV::CONTEXT_VALID_S2 | + StackFrameRISCV::CONTEXT_VALID_S3 | + StackFrameRISCV::CONTEXT_VALID_S4 | + StackFrameRISCV::CONTEXT_VALID_S5 | + StackFrameRISCV::CONTEXT_VALID_S6 | + StackFrameRISCV::CONTEXT_VALID_S7 | + StackFrameRISCV::CONTEXT_VALID_S8 | + StackFrameRISCV::CONTEXT_VALID_S9 | + StackFrameRISCV::CONTEXT_VALID_S10 | + StackFrameRISCV::CONTEXT_VALID_S11 | + StackFrameRISCV::CONTEXT_VALID_S0); + + // By default, context frames provide all registers, as normal. + context_frame_validity = StackFrameRISCV::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.sp = stack_section.start().Value(); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + walker.SetContextFrameValidity(context_frame_validity); + vector modules_without_symbols; + vector 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()); + + StackFrameRISCV *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(context_frame_validity, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40004000U, frame0->function_base); + + StackFrameRISCV *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ(expected_validity, frame1->context_validity); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_A2) + EXPECT_EQ(expected.a2, frame1->context.a2); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S1) + EXPECT_EQ(expected.s1, frame1->context.s1); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S2) + EXPECT_EQ(expected.s2, frame1->context.s2); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S3) + EXPECT_EQ(expected.s3, frame1->context.s3); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S4) + EXPECT_EQ(expected.s4, frame1->context.s4); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S5) + EXPECT_EQ(expected.s5, frame1->context.s5); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S6) + EXPECT_EQ(expected.s6, frame1->context.s6); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S7) + EXPECT_EQ(expected.s7, frame1->context.s7); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S8) + EXPECT_EQ(expected.s8, frame1->context.s8); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S9) + EXPECT_EQ(expected.s9, frame1->context.s9); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S10) + EXPECT_EQ(expected.s10, frame1->context.s10); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S11) + EXPECT_EQ(expected.s11, frame1->context.s11); + if (expected_validity & StackFrameRISCV::CONTEXT_VALID_S0) + EXPECT_EQ(expected.s0, frame1->context.s0); + + // 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.sp, frame1->context.sp); + EXPECT_EQ(expected.pc, frame1->context.pc); + EXPECT_EQ(expected.pc, frame1->instruction + 4); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values we expect to find for the caller's registers. + MDRawContextRISCV 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.sp; + raw_context.pc = 0x40004000; + raw_context.ra = 0x40005510; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_sp = expected.sp; + stack_section + .D32(0xb5d55e68) // saved s1 + .D32(0xebd134f3) // saved s2 + .D32(0x8112e110) // saved s0 + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x40004001; + // distinct callee s1, s2 and s0 + raw_context.s1 = 0xa635adc9; + raw_context.s2 = 0x35ac6231; + raw_context.s0 = 0xbe145fc4; + CheckWalk(); +} + +// As above, but unwind from a context that has only the PC and SP. +TEST_F(CFI, At4001LimitedValidity) { + Label frame1_sp = expected.sp; + stack_section + .D32(0xb5d55e68) // saved s1 + .D32(0xebd134f3) // saved s2 + .D32(0x8112e110) // saved s0 + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + context_frame_validity = StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP; + raw_context.pc = 0x40004001; + raw_context.s0 = 0xbe145fc4; + + expected_validity = (StackFrameRISCV::CONTEXT_VALID_PC | + StackFrameRISCV::CONTEXT_VALID_SP | + StackFrameRISCV::CONTEXT_VALID_S0 | + StackFrameRISCV::CONTEXT_VALID_S1 | + StackFrameRISCV::CONTEXT_VALID_S2); + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_sp = expected.sp; + stack_section + .D32(0xfb81ff3d) // no longer saved s1 + .D32(0xebd134f3) // no longer saved s2 + .D32(0x8112e110) // saved s0 + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x40004002; + raw_context.a1 = 0xb5d55e68; // saved a1 + raw_context.a2 = 0xebd134f3; // saved a2 + raw_context.a3 = 0xa31e74bc; // saved a3 + raw_context.a4 = 0x2dcb16b3; // saved a4 + raw_context.s1 = 0xa635adc9; // distinct callee s1 + raw_context.s2 = 0x35ac6231; // distinct callee s2 + raw_context.s3 = 0x4356ac45; // distinct callee s3 + raw_context.s4 = 0x562f2561; // distinct callee s4 + // distinct callee s0 + raw_context.s0 = 0xbe145fc4; + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_sp = expected.sp; + stack_section + .D32(0x48c8dd5a) // saved a2 (even though it's not callee-saves) + .D32(0xfb81ff3d) // no longer saved s1 + .D32(0xebd134f3) // no longer saved s2 + .D32(0x8112e110) // saved s0 + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x40004003; + // distinct callee a2 and fp + raw_context.a2 = 0xfb756319; + raw_context.s0 = 0xbe145fc4; + // caller's a2 + expected.a2 = 0x48c8dd5a; + expected_validity |= StackFrameRISCV::CONTEXT_VALID_A2; + 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.sp; + stack_section + .D32(0x48c8dd5a) // saved a2 (even though it's not callee-saves) + .D32(0xfb81ff3d) // no longer saved s1 + .D32(0xebd134f3) // no longer saved s2 + .D32(0x8112e110) // saved s0 + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x40004004; + // distinct callee a2 and s0 + raw_context.a2 = 0xfb756319; + raw_context.s0 = 0xbe145fc4; + // caller's a2 + expected.a2 = 0x48c8dd5a; + expected_validity |= StackFrameRISCV::CONTEXT_VALID_A2; + 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.sp; + stack_section + .D32(0x48c8dd5a) // saved a2 (even though it's not callee-saves) + .D32(0xfb81ff3d) // no longer saved s1 + .D32(0xebd134f3) // no longer saved s2 + .D32(0x8112e110) // saved s0 + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x40004005; + raw_context.a2 = 0xfb756319; // distinct callee a2 + expected.a2 = 0x48c8dd5a; // caller's a2 + expected_validity |= StackFrameRISCV::CONTEXT_VALID_A2; + 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.sp; + stack_section + .D32(0x40005510) // saved pc + .D32(0x48c8dd5a) // saved a2 (even though it's not callee-saves) + .D32(0xfb81ff3d) // no longer saved s1 + .D32(0xebd134f3) // no longer saved s2 + .D32(0x8112e110) // saved s0 + .D32(0x5783f8d1) // .ra rule recovers this, which is garbage + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.pc = 0x40004006; + raw_context.a2 = 0xfb756319; // distinct callee a2 + expected.a2 = 0x48c8dd5a; // caller's a2 + expected_validity |= StackFrameRISCV::CONTEXT_VALID_A2; + CheckWalk(); +} + +// Check that we reject rules that would cause the stack pointer to +// move in the wrong direction. +TEST_F(CFI, RejectBackwards) { + raw_context.pc = 0x40006000; + raw_context.sp = 0x80000000; + raw_context.ra = 0x40005510; + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); +} + +// Check that we reject rules whose expressions' evaluation fails. +TEST_F(CFI, RejectBadExpressions) { + raw_context.pc = 0x40007000; + raw_context.sp = 0x80000000; + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerRISCV walker(&system_info, &raw_context, &stack_region, + &modules, &frame_symbolizer); + vector modules_without_symbols; + vector 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()); +} diff --git a/src/third_party/curl/curlbuild.h b/src/third_party/curl/curlbuild.h index 595df4e4..2fb1d020 100644 --- a/src/third_party/curl/curlbuild.h +++ b/src/third_party/curl/curlbuild.h @@ -156,7 +156,8 @@ /* The size of `long', as computed by sizeof. */ #if defined(_M_X64) || (defined(__x86_64__) && !defined(__ILP32__)) || \ defined(__aarch64__) || (defined(__mips__) && _MIPS_SIM == _ABI64) || \ - defined(__powerpc64__) || defined(__s390x__) || defined(__LP64__) + defined(__powerpc64__) || defined(__s390x__) || defined(__LP64__) || \ + (defined(__riscv) && __riscv_xlen == 64) #define CURL_SIZEOF_LONG 8 #else #define CURL_SIZEOF_LONG 4 diff --git a/src/tools/linux/md2core/minidump-2-core.cc b/src/tools/linux/md2core/minidump-2-core.cc index 522b9d1c..a4ddbe8e 100644 --- a/src/tools/linux/md2core/minidump-2-core.cc +++ b/src/tools/linux/md2core/minidump-2-core.cc @@ -76,6 +76,8 @@ #define ELF_ARCH EM_MIPS #elif defined(__aarch64__) #define ELF_ARCH EM_AARCH64 +#elif defined(__riscv) + #define ELF_ARCH EM_RISCV #endif #if defined(__arm__) @@ -83,7 +85,7 @@ // containing core registers, while they use 'user_regs_struct' on other // architectures. This file-local typedef simplifies the source code. typedef user_regs user_regs_struct; -#elif defined (__mips__) +#elif defined (__mips__) || defined(__riscv) // This file-local typedef simplifies the source code. typedef gregset_t user_regs_struct; #endif @@ -258,7 +260,7 @@ typedef struct prpsinfo { /* Information about process */ unsigned char pr_zomb; /* Zombie */ signed char pr_nice; /* Nice val */ unsigned long pr_flag; /* Flags */ -#if defined(__x86_64__) || defined(__mips__) +#if defined(__x86_64__) || defined(__mips__) || defined(__riscv) uint32_t pr_uid; /* User ID */ uint32_t pr_gid; /* Group ID */ #else @@ -305,7 +307,7 @@ struct CrashedProcess { struct Thread { pid_t tid; -#if defined(__mips__) +#if defined(__mips__) || defined(__riscv) mcontext_t mcontext; #else user_regs_struct regs; @@ -532,6 +534,71 @@ ParseThreadRegisters(CrashedProcess::Thread* thread, thread->mcontext.fpc_eir = rawregs->float_save.fir; #endif } +#elif defined(__riscv) +static void +ParseThreadRegisters(CrashedProcess::Thread* thread, + const MinidumpMemoryRange& range) { +# if __riscv_xlen == 32 + const MDRawContextRISCV* rawregs = range.GetData(0); +# elif __riscv_xlen == 64 + const MDRawContextRISCV64* rawregs = range.GetData(0); +# else +# error "Unexpected __riscv_xlen" +# endif + + thread->mcontext.__gregs[0] = rawregs->pc; + thread->mcontext.__gregs[1] = rawregs->ra; + thread->mcontext.__gregs[2] = rawregs->sp; + thread->mcontext.__gregs[3] = rawregs->gp; + thread->mcontext.__gregs[4] = rawregs->tp; + thread->mcontext.__gregs[5] = rawregs->t0; + thread->mcontext.__gregs[6] = rawregs->t1; + thread->mcontext.__gregs[7] = rawregs->t2; + thread->mcontext.__gregs[8] = rawregs->s0; + thread->mcontext.__gregs[9] = rawregs->s1; + thread->mcontext.__gregs[10] = rawregs->a0; + thread->mcontext.__gregs[11] = rawregs->a1; + thread->mcontext.__gregs[12] = rawregs->a2; + thread->mcontext.__gregs[13] = rawregs->a3; + thread->mcontext.__gregs[14] = rawregs->a4; + thread->mcontext.__gregs[15] = rawregs->a5; + thread->mcontext.__gregs[16] = rawregs->a6; + thread->mcontext.__gregs[17] = rawregs->a7; + thread->mcontext.__gregs[18] = rawregs->s2; + thread->mcontext.__gregs[19] = rawregs->s3; + thread->mcontext.__gregs[20] = rawregs->s4; + thread->mcontext.__gregs[21] = rawregs->s5; + thread->mcontext.__gregs[22] = rawregs->s6; + thread->mcontext.__gregs[23] = rawregs->s7; + thread->mcontext.__gregs[24] = rawregs->s8; + thread->mcontext.__gregs[25] = rawregs->s9; + thread->mcontext.__gregs[26] = rawregs->s10; + thread->mcontext.__gregs[27] = rawregs->s11; + thread->mcontext.__gregs[28] = rawregs->t3; + thread->mcontext.__gregs[29] = rawregs->t4; + thread->mcontext.__gregs[30] = rawregs->t5; + thread->mcontext.__gregs[31] = rawregs->t6; + +# if __riscv_flen == 32 + for (int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; ++i) { + thread->mcontext.__fpregs.__f.__f[i] = rawregs->float_save.regs[i]; + } + thread->mcontext.__fpregs.__f.__fcsr = rawregs->float_save.fpcsr; +# elif __riscv_flen == 64 + for (int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; ++i) { + thread->mcontext.__fpregs.__d.__f[i] = rawregs->float_save.regs[i]; + } + thread->mcontext.__fpregs.__d.__fcsr = rawregs->float_save.fpcsr; +# elif __riscv_flen == 128 + for (int i = 0; i < MD_FLOATINGSAVEAREA_RISCV_FPR_COUNT; ++i) { + thread->mcontext.__fpregs.__q.__f[2*i] = rawregs->float_save.regs[i].high; + thread->mcontext.__fpregs.__q.__f[2*i+1] = rawregs->float_save.regs[i].low; + } + thread->mcontext.__fpregs.__q.__fcsr = rawregs->float_save.fpcsr; +# else +# error "Unexpected __riscv_flen" +# endif +} #else #error "This code has not been ported to your platform yet" #endif @@ -621,6 +688,22 @@ ParseSystemInfo(const Options& options, CrashedProcess* crashinfo, # else # error "This mips ABI is currently not supported (n32)" # endif +#elif defined(__riscv) +# if __riscv_xlen == 32 + if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_RISCV) { + fprintf(stderr, + "This version of minidump-2-core only supports RISCV.\n"); + exit(1); + } +# elif __riscv_xlen == 64 + if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_RISCV64) { + fprintf(stderr, + "This version of minidump-2-core only supports RISCV64.\n"); + exit(1); + } +# else +# error "Unexpected __riscv_xlen" +# endif #else #error "This code has not been ported to your platform yet" #endif @@ -648,6 +731,10 @@ ParseSystemInfo(const Options& options, CrashedProcess* crashinfo, ? "MIPS" : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_MIPS64 ? "MIPS64" + : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_RISCV + ? "RISCV" + : sysinfo->processor_architecture == MD_CPU_ARCHITECTURE_RISCV64 + ? "RISCV64" : "???", sysinfo->number_of_processors, sysinfo->processor_level, @@ -925,6 +1012,8 @@ WriteThread(const Options& options, const CrashedProcess::Thread& thread, pr.pr_pid = thread.tid; #if defined(__mips__) memcpy(&pr.pr_reg, &thread.mcontext.gregs, sizeof(user_regs_struct)); +#elif defined(__riscv) + memcpy(&pr.pr_reg, &thread.mcontext.__gregs, sizeof(user_regs_struct)); #else memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); #endif