Test Results:
Crash reason: SIGSEGV /SEGV_MAPERR
Crash address: 0x0
Process uptime: not available
Thread 0 (crashed)
0 a.out!crash() [crash_test.cc : 12 + 0x8]
ra = 0x0000000120002d18 tp = 0x000000fff63fcea0
sp = 0x000000fffbacc120 a0 = 0x0000000000000000
a1 = 0x0000000000000000 a2 = 0x000000fff65680a8
a3 = 0x0000000127d24010 a4 = 0x0000000000000000
a5 = 0x0000000000000000 a6 = 0x000000000000270f
a7 = 0x0000000000000004 t0 = 0x0000000000000000
t1 = 0x0000000000000001 t2 = 0x0000000000000001
t3 = 0x0000000000000000 t4 = 0x0000000000000001
t5 = 0x0000000000000001 t6 = 0x7f7f7f7f7f7f7f7f
t7 = 0x0000000000000008 t8 = 0x0000000000000000
x0 = 0x0000000000000000 fp = 0x000000fffbacc140
s0 = 0x0000000000000000 s1 = 0x0000000120017920
s2 = 0x000000fff67bf8e8 s3 = 0x0000000000000000
s4 = 0x0000000122bd67a0 s5 = 0x0000000122ba7650
s6 = 0x0000000122bd6990 s7 = 0x0000000122ba4920
s8 = 0x0000000122b8cd10 pc = 0x0000000120002cf0
Found by: given as instruction pointer in context
1 a.out!fun1() [crash_test.cc : 15 + 0xc]
ra = 0x0000000120002d18 sp = 0x000000fffbacc140
fp = 0x000000fffbacc150 s0 = 0x0000000000000000
s1 = 0x0000000120017920 s2 = 0x000000fff67bf8e8
s3 = 0x0000000000000000 s4 = 0x0000000122bd67a0
s5 = 0x0000000122ba7650 s6 = 0x0000000122bd6990
s7 = 0x0000000122ba4920 s8 = 0x0000000122b8cd10
pc = 0x0000000120002d10
Found by: call frame info
2 a.out!main [crash_test.cc : 23 + 0x28]
ra = 0x0000000120002dd4 sp = 0x000000fffbacc150
fp = 0x000000fffbacc320 s0 = 0x0000000000000000
s1 = 0x0000000120017920 s2 = 0x000000fff67bf8e8
s3 = 0x0000000000000000 s4 = 0x0000000122bd67a0
s5 = 0x0000000122ba7650 s6 = 0x0000000122bd6990
s7 = 0x0000000122ba4920 s8 = 0x0000000122b8cd10
pc = 0x0000000120002dcc
Found by: call frame info
3 libc.so.6!__libc_start_main + 0xdc
ra = 0x000000fff6428774 sp = 0x000000fffbacc320
fp = 0x0000000000000000 s0 = 0x0000000000000000
s1 = 0x0000000120017920 s2 = 0x000000fff67bf8e8
s3 = 0x0000000000000000 s4 = 0x0000000122bd67a0
s5 = 0x0000000122ba7650 s6 = 0x0000000122bd6990
s7 = 0x0000000122ba4920 s8 = 0x0000000122b8cd10
pc = 0x000000fff642876c
Found by: call frame info
4 a.out!_start + 0x64
ra = 0x0000000120002bb4 sp = 0x000000fffbacc450
fp = 0x0000000000000000 s0 = 0x0000000120002b48
s1 = 0x0000000122bea0c0 s2 = 0x000000fff67bf8e8
s3 = 0xffffffffffffffff s4 = 0x0000000122bd67a0
s5 = 0x0000000122ba7650 s6 = 0x0000000122bd6990
s7 = 0x0000000122ba4920 s8 = 0x0000000122b8cd10
pc = 0x0000000120002bac
Found by: call frame info
Loaded modules:
0x120000000 - 0x12001bfff a.out ??? (main)
0xfff6404000 - 0xfff6553fff libc.so.6 ???
0xfff6570000 - 0xfff65affff libgcc_s.so.1 ???
0xfff65bc000 - 0xfff666ffff libm.so.6 ???
0xfff6678000 - 0xfff67e3fff libstdc++.so.6 ???
0xfff6830000 - 0xfff684bfff libpthread.so.0 ???
0xfff6878000 - 0xfff6897fff ld.so.1 ???
0xffff488000 - 0xffff48bfff linux-gate.so ???
2021-08-25 09:06:47: minidump.cc:5119: INFO: Minidump closing minidump
Signed-off-by: Jinyang He <heji...@loongson.cn>
Signed-off-by: Qing Zhang <zhan...@loongson.cn>
---
Makefile.am | 20 +
Makefile.in | 108 +++
.../dump_writer_common/raw_context_cpu.h | 2 +
.../linux/dump_writer_common/thread_info.cc | 31 +-
.../linux/dump_writer_common/thread_info.h | 3 +
.../dump_writer_common/ucontext_reader.cc | 22 +
src/client/linux/handler/exception_handler.cc | 7 +-
src/client/linux/handler/exception_handler.h | 4 +-
.../microdump_writer/microdump_writer.cc | 8 +-
.../microdump_writer_unittest.cc | 6 +
.../minidump_writer/linux_core_dumper.cc | 18 +-
.../linux/minidump_writer/linux_dumper.h | 3 +-
.../linux_dumper_unittest_helper.cc | 2 +
.../minidump_writer/linux_ptrace_dumper.cc | 13 +
.../linux_ptrace_dumper_unittest.cc | 5 +
.../linux/minidump_writer/minidump_writer.cc | 10 +-
.../linux/minidump_writer/minidump_writer.h | 2 +-
.../minidump_writer_unittest.cc | 3 +
src/common/dwarf_cfi_to_module.cc | 16 +
src/common/dwarf_cfi_to_module.h | 3 +
src/common/linux/breakpad_getcontext.S | 98 ++-
src/common/linux/dump_symbols.cc | 7 +
src/common/linux/memory_mapped_file.cc | 3 +-
.../linux/memory_mapped_file_unittest.cc | 4 +
src/common/linux/ucontext_constants.h | 13 +
src/common/memory_allocator_unittest.cc | 2 +-
.../common/minidump_cpu_loongarch64.h | 146 ++++
src/google_breakpad/common/minidump_format.h | 2 +
src/google_breakpad/processor/dump_context.h | 3 +
.../processor/stack_frame_cpu.h | 68 +-
src/processor/basic_source_line_resolver.cc | 2 +-
src/processor/dump_context.cc | 45 ++
src/processor/minidump.cc | 63 ++
src/processor/minidump_processor.cc | 7 +
src/processor/stackwalk_common.cc | 141 ++++
src/processor/stackwalker.cc | 7 +
src/processor/stackwalker_loongarch64.cc | 290 +++++++
src/processor/stackwalker_loongarch64.h | 83 ++
.../stackwalker_loongarch64_unittest.cc | 717 ++++++++++++++++++
src/processor/synth_minidump.h | 1 +
src/tools/linux/md2core/minidump-2-core.cc | 25 +-
41 files changed, 1981 insertions(+), 32 deletions(-)
create mode 100644 src/google_breakpad/common/minidump_cpu_loongarch64.h
create mode 100644 src/processor/stackwalker_loongarch64.cc
create mode 100644 src/processor/stackwalker_loongarch64.h
create mode 100644 src/processor/stackwalker_loongarch64_unittest.cc
diff --git a/Makefile.am b/Makefile.am
index 0602314d..9fea65d6 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -291,6 +291,8 @@ src_libbreakpad_a_SOURCES = \
src/processor/stackwalker_address_list.h \
src/processor/stackwalker_mips.cc \
src/processor/stackwalker_mips.h \
+ src/processor/stackwalker_loongarch64.cc \
+ src/processor/stackwalker_loongarch64.h \
src/processor/stackwalker_ppc.cc \
src/processor/stackwalker_ppc.h \
src/processor/stackwalker_ppc64.cc \
@@ -408,6 +410,7 @@ check_PROGRAMS += \
src/processor/stackwalker_address_list_unittest \
src/processor/stackwalker_mips_unittest \
src/processor/stackwalker_mips64_unittest \
+ src/processor/stackwalker_loongarch64_unittest \
src/processor/stackwalker_x86_unittest \
src/processor/synth_minidump_unittest
endif
@@ -880,6 +883,7 @@ src_processor_exploitability_unittest_LDADD = \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_mips.o \
+ src/processor/stackwalker_loongarch64.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_ppc64.o \
src/processor/stackwalker_sparc.o \
@@ -953,6 +957,7 @@ src_processor_microdump_processor_unittest_LDADD = \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_mips.o \
+ src/processor/stackwalker_loongarch64.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_ppc64.o \
src/processor/stackwalker_sparc.o \
@@ -992,6 +997,7 @@ src_processor_minidump_processor_unittest_LDADD = \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_mips.o \
+ src/processor/stackwalker_loongarch64.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_ppc64.o \
src/processor/stackwalker_sparc.o \
@@ -1135,6 +1141,7 @@ src_processor_stackwalker_selftest_LDADD = \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_mips.o \
+ src/processor/stackwalker_loongarch64.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_ppc64.o \
src/processor/stackwalker_sparc.o \
@@ -1202,6 +1209,16 @@ src_processor_stackwalker_mips64_unittest_LDADD = \
src_processor_stackwalker_mips64_unittest_CPPFLAGS = \
$(AM_CPPFLAGS) $(TEST_CFLAGS)
+src_processor_stackwalker_loongarch64_unittest_SOURCES = \
+ src/common/test_assembler.cc \
+ src/processor/stackwalker_loongarch64_unittest.cc
+src_processor_stackwalker_loongarch64_unittest_LDADD = \
+ src/libbreakpad.a \
+ $(TEST_LIBS) \
+ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
+src_processor_stackwalker_loongarch64_unittest_CPPFLAGS = \
+ $(AM_CPPFLAGS) $(TEST_CFLAGS)
+
src_processor_stackwalker_x86_unittest_SOURCES = \
src/common/test_assembler.cc \
src/processor/stackwalker_x86_unittest.cc
@@ -1303,6 +1320,7 @@ src_processor_microdump_stackwalk_LDADD = \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_mips.o \
+ src/processor/stackwalker_loongarch64.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_ppc64.o \
src/processor/stackwalker_sparc.o \
@@ -1342,6 +1360,7 @@ src_processor_minidump_stackwalk_LDADD = \
src/processor/stackwalker_arm.o \
src/processor/stackwalker_arm64.o \
src/processor/stackwalker_mips.o \
+ src/processor/stackwalker_loongarch64.o \
src/processor/stackwalker_ppc.o \
src/processor/stackwalker_ppc64.o \
src/processor/stackwalker_sparc.o \
@@ -1467,6 +1486,7 @@ EXTRA_DIST = \
src/processor/testdata/microdump-arm.dmp \
src/processor/testdata/microdump-mips32.dmp \
src/processor/testdata/microdump-mips64.dmp \
+ src/processor/testdata/microdump-loongarch64.dmp \
src/processor/testdata/microdump-multiple.dmp \
src/processor/testdata/microdump.stackwalk-arm64.out \
src/processor/testdata/microdump.stackwalk-arm.out \
diff --git a/Makefile.in b/Makefile.in
index 3c82fc95..3cfd0130 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -199,6 +199,7 @@ 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_loongarch64_unittest \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest \
@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest
@@ -299,6 +300,7 @@ 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_loongarch64_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) \
@@ -490,6 +492,8 @@ am__src_libbreakpad_a_SOURCES_DIST = \
src/processor/stackwalker_address_list.h \
src/processor/stackwalker_mips.cc \
src/processor/stackwalker_mips.h \
+ src/processor/stackwalker_loongarch64.cc \
+ src/processor/stackwalker_loongarch64.h \
src/processor/stackwalker_ppc.cc \
src/processor/stackwalker_ppc.h \
src/processor/stackwalker_ppc64.cc \
@@ -543,6 +547,7 @@ am__src_libbreakpad_a_SOURCES_DIST = \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.$(OBJEXT) \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.$(OBJEXT) \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.$(OBJEXT) \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.$(OBJEXT) \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.$(OBJEXT) \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.$(OBJEXT) \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.$(OBJEXT) \
@@ -979,6 +984,7 @@ src_processor_exploitability_unittest_OBJECTS = \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -1043,6 +1049,7 @@ src_processor_microdump_processor_unittest_OBJECTS = \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -1082,6 +1089,7 @@ src_processor_microdump_stackwalk_OBJECTS = \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -1135,6 +1143,7 @@ src_processor_minidump_processor_unittest_OBJECTS = \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -1180,6 +1189,7 @@ src_processor_minidump_stackwalk_OBJECTS = \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -1345,6 +1355,18 @@ 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_loongarch64_unittest_SOURCES_DIST = \
+ src/common/test_assembler.cc \
+ src/processor/stackwalker_loongarch64_unittest.cc
+@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_loongarch64_unittest_OBJECTS = src/common/processor_stackwalker_loongarch64_unittest-test_assembler.$(OBJEXT) \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.$(OBJEXT)
+src_processor_stackwalker_loongarch64_unittest_OBJECTS = \
+ $(am_src_processor_stackwalker_loongarch64_unittest_OBJECTS)
+@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_loongarch64_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)
@@ -1371,6 +1393,7 @@ src_processor_stackwalker_selftest_OBJECTS = \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -1693,6 +1716,7 @@ 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_loongarch64_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)/string_conversion.Po \
@@ -1873,6 +1897,8 @@ am__depfiles_remade = src/client/$(DEPDIR)/minidump_file_writer.Po \
src/processor/$(DEPDIR)/stackwalker_mips.Po \
src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po \
src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po \
+ src/processor/$(DEPDIR)/stackwalker_loongarch64.Po \
+ src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Po \
src/processor/$(DEPDIR)/stackwalker_ppc.Po \
src/processor/$(DEPDIR)/stackwalker_ppc64.Po \
src/processor/$(DEPDIR)/stackwalker_selftest.Po \
@@ -1991,6 +2017,7 @@ 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_loongarch64_unittest_SOURCES) \
$(src_processor_stackwalker_selftest_SOURCES) \
$(src_processor_stackwalker_x86_unittest_SOURCES) \
$(src_processor_static_address_map_unittest_SOURCES) \
@@ -2047,6 +2074,7 @@ 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_loongarch64_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) \
@@ -2629,6 +2657,8 @@ CLEANFILES = $(am__append_12)
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.h \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.cc \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.h \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.cc \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.h \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.cc \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.h \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.cc \
@@ -3105,6 +3135,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -3186,6 +3217,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -3227,6 +3259,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -3390,6 +3423,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -3469,6 +3503,18 @@ 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_loongarch64_unittest_SOURCES = \
+@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64_unittest.cc
+
+@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_loongarch64_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_loongarch64_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
@@ -3579,6 +3625,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -3619,6 +3666,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \
+@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_loongarch64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \
@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \
@@ -3737,6 +3785,7 @@ EXTRA_DIST = \
src/processor/testdata/microdump-arm.dmp \
src/processor/testdata/microdump-mips32.dmp \
src/processor/testdata/microdump-mips64.dmp \
+ src/processor/testdata/microdump-loongarch64.dmp \
src/processor/testdata/microdump-multiple.dmp \
src/processor/testdata/microdump.stackwalk-arm64.out \
src/processor/testdata/microdump.stackwalk-arm.out \
@@ -4302,6 +4351,9 @@ src/processor/stackwalker_address_list.$(OBJEXT): \
src/processor/stackwalker_mips.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
+src/processor/stackwalker_loongarch64.$(OBJEXT): \
+ src/processor/$(am__dirstamp) \
+ src/processor/$(DEPDIR)/$(am__dirstamp)
src/processor/stackwalker_ppc.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
@@ -5004,6 +5056,18 @@ 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_loongarch64_unittest-test_assembler.$(OBJEXT): \
+ src/common/$(am__dirstamp) \
+ src/common/$(DEPDIR)/$(am__dirstamp)
+src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.$(OBJEXT): \
+ src/processor/$(am__dirstamp) \
+ src/processor/$(DEPDIR)/$(am__dirstamp)
+
+src/processor/stackwalker_loongarch64_unittest$(EXEEXT): $(src_processor_stackwalker_loongarch64_unittest_OBJECTS) $(src_processor_stackwalker_loongarch64_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_loongarch64_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
+ @rm -f src/processor/stackwalker_loongarch64_unittest$(EXEEXT)
+ $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_loongarch64_unittest_OBJECTS) $(src_processor_stackwalker_loongarch64_unittest_LDADD) $(LIBS)
+
src/processor/stackwalker_selftest.$(OBJEXT): \
src/processor/$(am__dirstamp) \
src/processor/$(DEPDIR)/$(am__dirstamp)
@@ -5405,6 +5469,7 @@ 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_loongarch64_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)/string_conversion.Po@am__quote@ # am--include-marker
@@ -5585,6 +5650,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_mips.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po@am__quote@ # am--include-marker
@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_loongarch64.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_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_selftest.Po@am__quote@ # am--include-marker
@@ -7629,6 +7696,34 @@ 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_loongarch64_unittest-test_assembler.o: src/common/test_assembler.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_loongarch64_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_loongarch64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_loongarch64_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_loongarch64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_loongarch64_unittest-test_assembler.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_loongarch64_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_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_loongarch64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc
+
+src/common/processor_stackwalker_loongarch64_unittest-test_assembler.obj: src/common/test_assembler.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_loongarch64_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_loongarch64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_loongarch64_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_loongarch64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_loongarch64_unittest-test_assembler.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_loongarch64_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_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_loongarch64_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_loongarch64_unittest-stackwalker_loongarch64_unittest.o: src/processor/stackwalker_loongarch64_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Tpo -c -o src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.o `test -f 'src/processor/stackwalker_loongarch64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_loongarch64_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_loongarch64_unittest.cc' object='src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_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_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.o `test -f 'src/processor/stackwalker_loongarch64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_loongarch64_unittest.cc
+
+src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.obj: src/processor/stackwalker_loongarch64_unittest.cc
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Tpo -c -o src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.obj `if test -f 'src/processor/stackwalker_loongarch64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_loongarch64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_loongarch64_unittest.cc'; fi`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Po
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_loongarch64_unittest.cc' object='src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_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_loongarch64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.obj `if test -f 'src/processor/stackwalker_loongarch64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_loongarch64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_loongarch64_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
@@ -9026,6 +9121,13 @@ 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_loongarch64_unittest.log: src/processor/stackwalker_loongarch64_unittest$(EXEEXT)
+ @p='src/processor/stackwalker_loongarch64_unittest$(EXEEXT)'; \
+ b='src/processor/stackwalker_loongarch64_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'; \
@@ -9475,6 +9577,7 @@ 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_loongarch64_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)/string_conversion.Po
@@ -9655,6 +9758,8 @@ distclean: distclean-am
-rm -f src/processor/$(DEPDIR)/stackwalker_mips.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po
+ -rm -f src/processor/$(DEPDIR)/stackwalker_loongarch64.Po
+ -rm -f src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_ppc.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_ppc64.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_selftest.Po
@@ -9818,6 +9923,7 @@ 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_loongarch64_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)/string_conversion.Po
@@ -9998,6 +10104,8 @@ maintainer-clean: maintainer-clean-am
-rm -f src/processor/$(DEPDIR)/stackwalker_mips.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po
+ -rm -f src/processor/$(DEPDIR)/stackwalker_loongarch64.Po
+ -rm -f src/processor/$(DEPDIR)/stackwalker_loongarch64_unittest-stackwalker_loongarch64_unittest.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_ppc.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_ppc64.Po
-rm -f src/processor/$(DEPDIR)/stackwalker_selftest.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 07d9171a..48e0f089 100644
--- a/src/client/linux/dump_writer_common/raw_context_cpu.h
+++ b/src/client/linux/dump_writer_common/raw_context_cpu.h
@@ -44,6 +44,8 @@ typedef MDRawContextARM RawContextCPU;
typedef MDRawContextARM64_Old RawContextCPU;
#elif defined(__mips__)
typedef MDRawContextMIPS RawContextCPU;
+#elif defined(__loongarch64)
+typedef MDRawContextLOONGARCH64 RawContextCPU;
#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 aae1dc13..140061df 100644
--- a/src/client/linux/dump_writer_common/thread_info.cc
+++ b/src/client/linux/dump_writer_common/thread_info.cc
@@ -270,7 +270,26 @@ void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
out->float_save.fir = mcontext.fpc_eir;
#endif
}
-#endif // __mips__
+
+#elif defined(__loongarch64)
+
+uintptr_t ThreadInfo::GetInstructionPointer() const {
+ return mcontext.__pc;
+}
+
+void ThreadInfo::FillCPUContext(RawContextCPU* out) const {
+ out->context_flags = MD_CONTEXT_LOONGARCH64_FULL;
+
+ for (int i = 0; i < MD_CONTEXT_LOONGARCH64_GPR_COUNT; ++i)
+ out->iregs[i] = mcontext.__gregs[i];
+
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_LOONGARCH64_FPR_COUNT; ++i)
+ out->float_save.regs[i] = mcontext.__fpregs[i].__val64[0]; // FIXME: union?
+
+ out->csr_epc = mcontext.__pc;
+}
+
+#endif // __loongarch64
void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) {
assert(gp_regs || size);
@@ -279,6 +298,11 @@ void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) {
*gp_regs = mcontext.gregs;
if (size)
*size = sizeof(mcontext.gregs);
+#elif defined(__loongarch64)
+ if (gp_regs)
+ *gp_regs = mcontext.__gregs;
+ if (size)
+ *size = sizeof(mcontext.__gregs);
#else
if (gp_regs)
*gp_regs = ®s;
@@ -294,6 +318,11 @@ void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) {
*fp_regs = &mcontext.fpregs;
if (size)
*size = sizeof(mcontext.fpregs);
+#elif defined(__loongarch64)
+ if (fp_regs)
+ *fp_regs = &mcontext.__fpregs;
+ if (size)
+ *size = sizeof(mcontext.__fpregs);
#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 fb216fa6..b3b24c69 100644
--- a/src/client/linux/dump_writer_common/thread_info.h
+++ b/src/client/linux/dump_writer_common/thread_info.h
@@ -71,6 +71,9 @@ struct ThreadInfo {
#elif defined(__mips__)
// Use the structure defined in <sys/ucontext.h>.
mcontext_t mcontext;
+#elif defined(__loongarch64)
+ // Use the structure defined in <sys/ucontext.h>.
+ mcontext_t mcontext;
#endif
// Returns the instruction pointer (platform-dependent impl.).
diff --git a/src/client/linux/dump_writer_common/ucontext_reader.cc b/src/client/linux/dump_writer_common/ucontext_reader.cc
index 6eec1be2..b8abb4d4 100644
--- a/src/client/linux/dump_writer_common/ucontext_reader.cc
+++ b/src/client/linux/dump_writer_common/ucontext_reader.cc
@@ -254,6 +254,28 @@ void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc) {
out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused.
#endif
}
+
+#elif defined(__loongarch64)
+
+uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.__gregs[MD_CONTEXT_LOONGARCH64_REG_SP];
+}
+
+uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) {
+ return uc->uc_mcontext.__pc;
+}
+
+void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc) {
+ out->context_flags = MD_CONTEXT_LOONGARCH64_FULL;
+
+ for (int i = 0; i < MD_CONTEXT_LOONGARCH64_GPR_COUNT; ++i)
+ out->iregs[i] = uc->uc_mcontext.__gregs[i];
+
+ for (int i = 0; i < MD_FLOATINGSAVEAREA_LOONGARCH64_FPR_COUNT; ++i)
+ out->float_save.regs[i] = uc->uc_mcontext.__fpregs[i].__val64[0]; // FIXME: union?
+
+ out->csr_epc = uc->uc_mcontext.__pc;
+}
#endif
} // namespace google_breakpad
diff --git a/src/client/linux/handler/exception_handler.cc b/src/client/linux/handler/exception_handler.cc
index ca353c40..0a9f5609 100644
--- a/src/client/linux/handler/exception_handler.cc
+++ b/src/client/linux/handler/exception_handler.cc
@@ -461,7 +461,7 @@ bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) {
memcpy(&g_crash_context_.float_state, fp_ptr,
sizeof(g_crash_context_.float_state));
}
-#elif !defined(__ARM_EABI__) && !defined(__mips__)
+#elif !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
// 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.
@@ -701,7 +701,7 @@ bool ExceptionHandler::WriteMinidump() {
}
#endif
-#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__) && !defined(__loongarch64)
// FPU state is not part of ARM EABI ucontext_t.
memcpy(&context.float_state, context.context.uc_mcontext.fpregs,
sizeof(context.float_state));
@@ -726,6 +726,9 @@ bool ExceptionHandler::WriteMinidump() {
#elif defined(__mips__)
context.siginfo.si_addr =
reinterpret_cast<void*>(context.context.uc_mcontext.pc);
+#elif defined(__loongarch64)
+ context.siginfo.si_addr =
+ reinterpret_cast<void*>(context.context.uc_mcontext.__pc);
#else
#error "This code has not been ported to your platform yet."
#endif
diff --git a/src/client/linux/handler/exception_handler.h b/src/client/linux/handler/exception_handler.h
index f80843ea..c5fbc41f 100644
--- a/src/client/linux/handler/exception_handler.h
+++ b/src/client/linux/handler/exception_handler.h
@@ -192,10 +192,10 @@ class ExceptionHandler {
siginfo_t siginfo;
pid_t tid; // the crashing thread.
ucontext_t context;
-#if !defined(__ARM_EABI__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
// #ifdef this out because FP state is not part of user ABI for Linux ARM.
// In case of MIPS Linux FP state is already part of ucontext_t so
- // 'float_state' is not required.
+ // 'float_state' is not required. It's same as loongarch64.
fpstate_t float_state;
#endif
};
diff --git a/src/client/linux/microdump_writer/microdump_writer.cc b/src/client/linux/microdump_writer/microdump_writer.cc
index fa3c1713..7a66cb45 100644
--- a/src/client/linux/microdump_writer/microdump_writer.cc
+++ b/src/client/linux/microdump_writer/microdump_writer.cc
@@ -138,7 +138,7 @@ class MicrodumpWriter {
const MicrodumpExtraInfo& microdump_extra_info,
LinuxDumper* dumper)
: ucontext_(context ? &context->context : NULL),
-#if !defined(__ARM_EABI__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
float_state_(context ? &context->float_state : NULL),
#endif
dumper_(dumper),
@@ -337,6 +337,8 @@ class MicrodumpWriter {
# else
# error "This mips ABI is currently not supported (n32)"
#endif
+#elif defined(__loongarch64)
+ const char kArch[] = "loongarch64";
#else
#error "This code has not been ported to your platform yet"
#endif
@@ -409,7 +411,7 @@ class MicrodumpWriter {
void DumpCPUState() {
RawContextCPU cpu;
my_memset(&cpu, 0, sizeof(RawContextCPU));
-#if !defined(__ARM_EABI__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
UContextReader::FillCPUContext(&cpu, ucontext_, float_state_);
#else
UContextReader::FillCPUContext(&cpu, ucontext_);
@@ -605,7 +607,7 @@ class MicrodumpWriter {
void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); }
const ucontext_t* const ucontext_;
-#if !defined(__ARM_EABI__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
const google_breakpad::fpstate_t* const float_state_;
#endif
LinuxDumper* dumper_;
diff --git a/src/client/linux/microdump_writer/microdump_writer_unittest.cc b/src/client/linux/microdump_writer/microdump_writer_unittest.cc
index 6339ac0c..41687ff6 100644
--- a/src/client/linux/microdump_writer/microdump_writer_unittest.cc
+++ b/src/client/linux/microdump_writer/microdump_writer_unittest.cc
@@ -280,9 +280,15 @@ TEST(MicrodumpWriterTest, BasicWithMappings) {
ASSERT_TRUE(ContainsMicrodump(buf));
#ifdef __LP64__
+#if defined(__loongarch64)
+ ASSERT_NE(std::string::npos,
+ buf.find("M 0000000000004000 000000000000002A 0000000000004000 "
+ "33221100554477668899AABBCCDDEEFF0 libfoo.so"));
+#else
ASSERT_NE(std::string::npos,
buf.find("M 0000000000001000 000000000000002A 0000000000001000 "
"33221100554477668899AABBCCDDEEFF0 libfoo.so"));
+#endif
#else
ASSERT_NE(std::string::npos,
buf.find("M 00001000 0000002A 00001000 "
diff --git a/src/client/linux/minidump_writer/linux_core_dumper.cc b/src/client/linux/minidump_writer/linux_core_dumper.cc
index 92e3a844..a6cdf368 100644
--- a/src/client/linux/minidump_writer/linux_core_dumper.cc
+++ b/src/client/linux/minidump_writer/linux_core_dumper.cc
@@ -42,6 +42,9 @@
// To get register definitions.
#include <asm/reg.h>
#endif
+#if defined(__loongarch64)
+#include <asm/reg.h>
+#endif
#include "common/linux/elf_gnu_compat.h"
#include "common/linux/linux_libc_support.h"
@@ -112,6 +115,10 @@ bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
#elif defined(__mips__)
stack_pointer =
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
+#elif defined(__loongarch64)
+ memcpy(&stack_pointer,
+ &info->mcontext.__gregs[MD_CONTEXT_LOONGARCH64_REG_SP],
+ sizeof(info->mcontext.__gregs[MD_CONTEXT_LOONGARCH64_REG_SP]));
#else
#error "This code hasn't been ported to your platform yet."
#endif
@@ -218,9 +225,16 @@ bool LinuxCoreDumper::EnumerateThreads() {
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(__loongarch64)
+ memcpy(info.mcontext.__gregs,
+ &status->pr_reg[LOONGARCH64_EF_R0],
+ sizeof(info.mcontext.__gregs));
+ memcpy(&info.mcontext.__pc,
+ &status->pr_reg[LOONGARCH64_EF_CSR_EPC],
+ sizeof(info.mcontext.__pc));
+#else
memcpy(&info.regs, status->pr_reg, sizeof(info.regs));
-#endif // __mips__
+#endif // __loongarch64
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 7bee160f..8f3a440c 100644
--- a/src/client/linux/minidump_writer/linux_dumper.h
+++ b/src/client/linux/minidump_writer/linux_dumper.h
@@ -63,7 +63,8 @@ namespace google_breakpad {
(defined(__mips__) && _MIPS_SIM == _ABIO32)
typedef Elf32_auxv_t elf_aux_entry;
#elif defined(__x86_64) || defined(__aarch64__) || \
- (defined(__mips__) && _MIPS_SIM != _ABIO32)
+ (defined(__mips__) && _MIPS_SIM != _ABIO32) || \
+ defined(__loongarch64)
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 331f4bb3..85e5abf3 100644
--- a/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
+++ b/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc
@@ -51,6 +51,8 @@
#define TID_PTR_REGISTER "rcx"
#elif defined(__mips__)
#define TID_PTR_REGISTER "$1"
+#elif defined(__loongarch64)
+#define TID_PTR_REGISTER "$r1"
#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 e3ddb81a..d7b4dfb1 100644
--- a/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
+++ b/src/client/linux/minidump_writer/linux_ptrace_dumper.cc
@@ -53,6 +53,9 @@
#if defined(__i386)
#include <cpuid.h>
#endif
+#if defined(__loongarch64)
+#include <asm/reg.h>
+#endif
#include "client/linux/minidump_writer/directory_reader.h"
#include "client/linux/minidump_writer/line_reader.h"
@@ -286,6 +289,12 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
reinterpret_cast<void*>(DSP_CONTROL), &info->mcontext.dsp);
#endif
+/* FIXME does it needed */
+#if defined(__loongarch64)
+ sys_ptrace(PTRACE_PEEKUSER, tid,
+ reinterpret_cast<void*>(LOONGARCH64_EF_CSR_EPC), &info->mcontext.__pc);
+#endif
+
const uint8_t* stack_pointer;
#if defined(__i386)
my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
@@ -298,6 +307,10 @@ bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {
#elif defined(__mips__)
stack_pointer =
reinterpret_cast<uint8_t*>(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]);
+#elif defined(__loongarch64)
+ my_memcpy(&stack_pointer,
+ &info->mcontext.__gregs[MD_CONTEXT_LOONGARCH64_REG_SP],
+ sizeof(info->mcontext.__gregs[MD_CONTEXT_LOONGARCH64_REG_SP]));
#else
#error "This code hasn't been ported to your platform yet."
#endif
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 da71e15d..fd7fa366 100644
--- a/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc
+++ b/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc
@@ -462,6 +462,9 @@ TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) {
#elif defined(__mips__)
pid_t* process_tid_location =
reinterpret_cast<pid_t*>(one_thread.mcontext.gregs[1]);
+#elif defined(__loongarch64)
+ pid_t* process_tid_location =
+ reinterpret_cast<pid_t*>(one_thread.mcontext.__gregs[1]);
#else
#error This test has not been ported to this platform.
#endif
@@ -559,6 +562,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(__loongarch64)
+ uintptr_t heap_addr = thread_info.mcontext.__gregs[1];
#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 32634ef0..425b2419 100644
--- a/src/client/linux/minidump_writer/minidump_writer.cc
+++ b/src/client/linux/minidump_writer/minidump_writer.cc
@@ -136,7 +136,7 @@ class MinidumpWriter {
: fd_(minidump_fd),
path_(minidump_path),
ucontext_(context ? &context->context : NULL),
-#if !defined(__ARM_EABI__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
float_state_(context ? &context->float_state : NULL),
#endif
dumper_(dumper),
@@ -468,7 +468,7 @@ class MinidumpWriter {
if (!cpu.Allocate())
return false;
my_memset(cpu.get(), 0, sizeof(RawContextCPU));
-#if !defined(__ARM_EABI__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
UContextReader::FillCPUContext(cpu.get(), ucontext_, float_state_);
#else
UContextReader::FillCPUContext(cpu.get(), ucontext_);
@@ -897,7 +897,7 @@ class MinidumpWriter {
dirent->location.rva = 0;
}
-#if defined(__i386__) || defined(__x86_64__) || defined(__mips__)
+#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) || defined(__loongarch64)
bool WriteCPUInformation(MDRawSystemInfo* sys_info) {
char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0};
static const char vendor_id_name[] = "vendor_id";
@@ -925,6 +925,8 @@ class MinidumpWriter {
# else
# error "This mips ABI is currently not supported (n32)"
#endif
+#elif defined(__loongarch64)
+ MD_CPU_ARCHITECTURE_LOONGARCH64;
#elif defined(__i386__)
MD_CPU_ARCHITECTURE_X86;
#else
@@ -1333,7 +1335,7 @@ class MinidumpWriter {
const char* path_; // Path to the file where the minidum should be written.
const ucontext_t* const ucontext_; // also from the signal handler
-#if !defined(__ARM_EABI__) && !defined(__mips__)
+#if !defined(__ARM_EABI__) && !defined(__mips__) && !defined(__loongarch64)
const google_breakpad::fpstate_t* const float_state_; // ditto
#endif
LinuxDumper* dumper_;
diff --git a/src/client/linux/minidump_writer/minidump_writer.h b/src/client/linux/minidump_writer/minidump_writer.h
index e3b0b16d..b6eb18c5 100644
--- a/src/client/linux/minidump_writer/minidump_writer.h
+++ b/src/client/linux/minidump_writer/minidump_writer.h
@@ -48,7 +48,7 @@ class ExceptionHandler;
#if defined(__aarch64__)
typedef struct fpsimd_context fpstate_t;
-#elif !defined(__ARM_EABI__) && !defined(__mips__)
+#elif !defined(__ARM_EABI__) && !defined(__mips__) && !defined(loongarch64)
typedef std::remove_pointer<fpregset_t>::type fpstate_t;
#endif
diff --git a/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
index d192e5cb..a253324f 100644
--- a/src/client/linux/minidump_writer/minidump_writer_unittest.cc
+++ b/src/client/linux/minidump_writer/minidump_writer_unittest.cc
@@ -715,6 +715,9 @@ TEST(MinidumpWriterTest, InvalidStackPointer) {
#elif defined(__mips__)
context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =
invalid_stack_pointer;
+#elif defined(__loongarch64)
+ context.context.uc_mcontext.__gregs[MD_CONTEXT_LOONGARCH64_REG_SP] =
+ invalid_stack_pointer;
#else
# error "This code has not been ported to your platform yet."
#endif
diff --git a/src/common/dwarf_cfi_to_module.cc b/src/common/dwarf_cfi_to_module.cc
index d7d19834..6f52d928 100644
--- a/src/common/dwarf_cfi_to_module.cc
+++ b/src/common/dwarf_cfi_to_module.cc
@@ -142,6 +142,22 @@ vector<string> DwarfCFIToModule::RegisterNames::MIPS() {
sizeof(kRegisterNames) / sizeof(kRegisterNames[0]));
}
+vector<string> DwarfCFIToModule::RegisterNames::LOONGARCH64() {
+ static const char* const kRegisterNames[] = {
+ "$zero", "$ra", "$tp", "$sp", "$a0", "$a1", "$a2", "$a3",
+ "$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3",
+ "$t4", "$t5", "$t6", "$t7", "$t8", "$x", "$fp", "$s0",
+ "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$s8",
+ "$fa0", "$fa1", "$fa2", "$fa3", "$fa4", "$fa5", "$fa6", "$fa7",
+ "$ft0", "$ft1", "$ft2", "$ft3", "$ft4", "$ft5", "$ft6", "$ft7",
+ "$ft8", "$ft9", "$ft10","$ft11","$ft12", "$ft13","$ft14","$ft15",
+ "$fs0", "$fs1", "$fs2", "$fs3", "$fs4", "$fs5", "$fs6", "$fs7"
+ };
+
+ return MakeVector(kRegisterNames,
+ sizeof(kRegisterNames) / sizeof(kRegisterNames[0]));
+}
+
bool DwarfCFIToModule::Entry(size_t offset, uint64_t address, uint64_t length,
uint8_t version, const string& augmentation,
unsigned return_address) {
diff --git a/src/common/dwarf_cfi_to_module.h b/src/common/dwarf_cfi_to_module.h
index 3e2e6ffe..ab8a390a 100644
--- a/src/common/dwarf_cfi_to_module.h
+++ b/src/common/dwarf_cfi_to_module.h
@@ -114,6 +114,9 @@ class DwarfCFIToModule: public CallFrameInfo::Handler {
// MIPS.
static vector<string> MIPS();
+ //LOONGARCH64
+ static vector<string> LOONGARCH64();
+
private:
// Given STRINGS, an array of C strings with SIZE elements, return an
// equivalent vector<string>.
diff --git a/src/common/linux/breakpad_getcontext.S b/src/common/linux/breakpad_getcontext.S
index 528dba7a..7bd1a940 100644
--- a/src/common/linux/breakpad_getcontext.S
+++ b/src/common/linux/breakpad_getcontext.S
@@ -230,10 +230,10 @@ breakpad_getcontext:
#elif defined(__mips__)
// This implementation is inspired by implementation of getcontext in glibc.
-#include <asm-mips/asm.h>
-#include <asm-mips/regdef.h>
+#include <sys/asm.h>
+#include <sys/regdef.h>
#if _MIPS_SIM == _ABIO32
-#include <asm-mips/fpregdef.h>
+#include <sys/fpregdef.h>
#endif
// from asm-mips/asm.h
@@ -399,6 +399,98 @@ NESTED (breakpad_getcontext, FRAME_SIZE, ra)
END (breakpad_getcontext)
#endif // _MIPS_SIM == _ABIO32
+#elif defined(__loongarch64)
+
+#define _NSIG 128
+#define __NR_rt_sigprocmask 135
+
+ .text
+ .global breakpad_getcontext
+ .hidden breakpad_getcontext
+ .type breakpad_getcontext, @function
+ .align 4
+breakpad_getcontext:
+ STORE_PC
+ STORE_GPR(0)
+ STORE_GPR(1)
+ STORE_GPR(2)
+ STORE_GPR(3)
+ STORE_GPR(4)
+ STORE_GPR(5)
+ STORE_GPR(6)
+ STORE_GPR(7)
+ STORE_GPR(8)
+ STORE_GPR(9)
+ STORE_GPR(10)
+ STORE_GPR(11)
+ STORE_GPR(12)
+ STORE_GPR(13)
+ STORE_GPR(14)
+ STORE_GPR(15)
+ STORE_GPR(16)
+ STORE_GPR(17)
+ STORE_GPR(18)
+ STORE_GPR(19)
+ STORE_GPR(20)
+ STORE_GPR(21)
+ STORE_GPR(22)
+ STORE_GPR(23)
+ STORE_GPR(24)
+ STORE_GPR(25)
+ STORE_GPR(26)
+ STORE_GPR(27)
+ STORE_GPR(28)
+ STORE_GPR(29)
+ STORE_GPR(30)
+ STORE_GPR(31)
+ STORE_FPR(0)
+ STORE_FPR(1)
+ STORE_FPR(2)
+ STORE_FPR(3)
+ STORE_FPR(4)
+ STORE_FPR(5)
+ STORE_FPR(6)
+ STORE_FPR(7)
+ STORE_FPR(8)
+ STORE_FPR(9)
+ STORE_FPR(10)
+ STORE_FPR(11)
+ STORE_FPR(12)
+ STORE_FPR(13)
+ STORE_FPR(14)
+ STORE_FPR(15)
+ STORE_FPR(16)
+ STORE_FPR(17)
+ STORE_FPR(18)
+ STORE_FPR(19)
+ STORE_FPR(20)
+ STORE_FPR(21)
+ STORE_FPR(22)
+ STORE_FPR(23)
+ STORE_FPR(24)
+ STORE_FPR(25)
+ STORE_FPR(26)
+ STORE_FPR(27)
+ STORE_FPR(28)
+ STORE_FPR(29)
+ STORE_FPR(30)
+ STORE_FPR(31)
+
+ /* rt_sigprocmask (SIG_BLOCK, NULL, &ucp->uc_sigmask, _NSIG8) */
+ addi.d $a2, $a0, UCONTEXT_SIGMASK_OFFSET
+ li.w $a1, zero
+ li.w $a0, 0
+ li.w $a3, _NSIG / 8
+ li.w $a7, __NR_rt_sigprocmask
+
+ syscall 0
+
+ /* Return zero for success */
+ move $a0, $zero
+
+ jirl $zero, $ra, 0
+
+ .size breakpad_getcontext, . - breakpad_getcontext
#elif defined(__x86_64__)
/* The x64 implementation of breakpad_getcontext was derived in part
from the implementation of libunwind which requires the following
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index ac53ea28..1f2ebab9 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -105,6 +105,9 @@ using google_breakpad::wasteful_vector;
#define EM_AARCH64 183
#endif
+#ifndef EM_LOONGARCH
+#define EM_LOONGARCH 258
+#endif
//
// FDWrapper
//
@@ -370,6 +373,9 @@ bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header,
case EM_MIPS:
*register_names = DwarfCFIToModule::RegisterNames::MIPS();
return true;
+ case EM_LOONGARCH:
+ *register_names = DwarfCFIToModule::RegisterNames::LOONGARCH64();
+ return true;
case EM_X86_64:
*register_names = DwarfCFIToModule::RegisterNames::X86_64();
return true;
@@ -907,6 +913,7 @@ const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) {
case EM_ARM: return "arm";
case EM_AARCH64: return "arm64";
case EM_MIPS: return "mips";
+ case EM_LOONGARCH: return "loongarch64";
case EM_PPC64: return "ppc64";
case EM_PPC: return "ppc";
case EM_S390: return "s390";
diff --git a/src/common/linux/memory_mapped_file.cc b/src/common/linux/memory_mapped_file.cc
index 99362945..25c76330 100644
--- a/src/common/linux/memory_mapped_file.cc
+++ b/src/common/linux/memory_mapped_file.cc
@@ -65,7 +65,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(__loongarch64)
struct kernel_stat st;
if (sys_fstat(fd, &st) == -1 || st.st_size < 0) {
diff --git a/src/common/linux/memory_mapped_file_unittest.cc b/src/common/linux/memory_mapped_file_unittest.cc
index fad59f40..c18f4f88 100644
--- a/src/common/linux/memory_mapped_file_unittest.cc
+++ b/src/common/linux/memory_mapped_file_unittest.cc
@@ -176,7 +176,11 @@ TEST_F(MemoryMappedFileTest, RemapAfterMap) {
TEST_F(MemoryMappedFileTest, MapWithOffset) {
// Put more data in the test file this time. Offsets can only be
// done on page boundaries, so we need a two page file to test this.
+#if defined(__loongarch64)
+ const int page_size = 16384;
+#else
const int page_size = 4096;
+#endif
char data1[2 * page_size];
size_t data1_size = sizeof(data1);
for (size_t i = 0; i < data1_size; ++i) {
diff --git a/src/common/linux/ucontext_constants.h b/src/common/linux/ucontext_constants.h
index c390508a..0a026092 100644
--- a/src/common/linux/ucontext_constants.h
+++ b/src/common/linux/ucontext_constants.h
@@ -116,6 +116,19 @@
#define UCONTEXT_SIGMASK_OFFSET 640
#endif
+#elif defined(__loongarch64)
+
+#define MCONTEXT_GREG_SIZE 8
+#define MCONTEXT_FPREG_SIZE 8
+#define MCONTEXT_PC_OFFSET 64
+#define MCONTEXT_GREGS_OFFSET 72
+#define MCONTEXT_FPREGS_OFFSET 352
+#define MCONTEXT_SIGMASK_OFFSET 1408
+
+#define STORE_GPR(X) st.d $r##X, $a0, MCONTEXT_GREGS_OFFSET + X * MCONTEXT_GREG_SIZE
+#define STORE_FPR(X) fst.d $f##X, $a0, MCONTEXT_FPREGS_OFFSET + X * MCONTEXT_FPREG_SIZE
+#define STORE_PC st.d $ra, $a0, MCONTEXT_PC_OFFSET
+
#elif defined(__x86_64__)
#define MCONTEXT_GREGS_OFFSET 40
diff --git a/src/common/memory_allocator_unittest.cc b/src/common/memory_allocator_unittest.cc
index 5803b90d..71a8f818 100644
--- a/src/common/memory_allocator_unittest.cc
+++ b/src/common/memory_allocator_unittest.cc
@@ -56,7 +56,7 @@ TEST(PageAllocatorTest, LargeObject) {
PageAllocator allocator;
EXPECT_EQ(0U, allocator.pages_allocated());
- uint8_t* p = reinterpret_cast<uint8_t*>(allocator.Alloc(10000));
+ uint8_t* p = reinterpret_cast<uint8_t*>(allocator.Alloc(2 * getpagesize() + 1));
ASSERT_FALSE(p == NULL);
EXPECT_EQ(3U, allocator.pages_allocated());
for (unsigned i = 1; i < 10; ++i) {
diff --git a/src/google_breakpad/common/minidump_cpu_loongarch64.h b/src/google_breakpad/common/minidump_cpu_loongarch64.h
new file mode 100644
index 00000000..a0945a38
--- /dev/null
+++ b/src/google_breakpad/common/minidump_cpu_loongarch64.h
@@ -0,0 +1,146 @@
+/* Copyright (c) 2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+/* 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 LOONGARCH. 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.
+ */
+
+/*
+ * Loongarch64 support
+ */
+
+#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_LOONGARCH64_H__
+#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_LOONGARCH64_H__
+
+#define MD_CONTEXT_LOONGARCH64_GPR_COUNT 32
+#define MD_FLOATINGSAVEAREA_LOONGARCH64_FPR_COUNT 32
+
+typedef struct {
+ /* 32 64-bit floating point registers, f0..f31 */
+ uint64_t regs[MD_FLOATINGSAVEAREA_LOONGARCH64_FPR_COUNT];
+} MDFloatingSaveAreaLoongarch64;
+
+typedef struct {
+ /* The next field determines the layout of the structure, and which parts
+ * of it are populated.
+ */
+ uint32_t context_flags;
+ uint32_t _pad0;
+
+ /* 32 64-bit integer registers, r0..r31.
+ * Note the following fixed uses:
+ * r3 is the stack pointer.
+ * r1 is the return address.
+ */
+ uint64_t iregs[MD_CONTEXT_LOONGARCH64_GPR_COUNT];
+
+ uint64_t csr_epc;
+
+ /* The next field is included with MD_CONTEXT_LOONGARCH64_FLOATING_POINT. */
+ MDFloatingSaveAreaLoongarch64 float_save;
+
+} MDRawContextLOONGARCH64;
+
+/* Indices into iregs for registers with a dedicated or conventional
+ * purpose.
+ */
+enum MDLoongarchRegisterNumbers {
+ MD_CONTEXT_LOONGARCH64_REG_RA = 1,
+ MD_CONTEXT_LOONGARCH64_REG_TP,
+ MD_CONTEXT_LOONGARCH64_REG_SP,
+ MD_CONTEXT_LOONGARCH64_REG_A0,
+ MD_CONTEXT_LOONGARCH64_REG_A1,
+ MD_CONTEXT_LOONGARCH64_REG_A2,
+ MD_CONTEXT_LOONGARCH64_REG_A3,
+ MD_CONTEXT_LOONGARCH64_REG_A4,
+ MD_CONTEXT_LOONGARCH64_REG_A5,
+ MD_CONTEXT_LOONGARCH64_REG_A6,
+ MD_CONTEXT_LOONGARCH64_REG_A7,
+ MD_CONTEXT_LOONGARCH64_REG_T0,
+ MD_CONTEXT_LOONGARCH64_REG_T1,
+ MD_CONTEXT_LOONGARCH64_REG_T2,
+ MD_CONTEXT_LOONGARCH64_REG_T3,
+ MD_CONTEXT_LOONGARCH64_REG_T4,
+ MD_CONTEXT_LOONGARCH64_REG_T5,
+ MD_CONTEXT_LOONGARCH64_REG_T6,
+ MD_CONTEXT_LOONGARCH64_REG_T7,
+ MD_CONTEXT_LOONGARCH64_REG_T8,
+ MD_CONTEXT_LOONGARCH64_REG_X0,
+ MD_CONTEXT_LOONGARCH64_REG_FP,
+ MD_CONTEXT_LOONGARCH64_REG_S0,
+ MD_CONTEXT_LOONGARCH64_REG_S1,
+ MD_CONTEXT_LOONGARCH64_REG_S2,
+ MD_CONTEXT_LOONGARCH64_REG_S3,
+ MD_CONTEXT_LOONGARCH64_REG_S4,
+ MD_CONTEXT_LOONGARCH64_REG_S5,
+ MD_CONTEXT_LOONGARCH64_REG_S6,
+ MD_CONTEXT_LOONGARCH64_REG_S7,
+ MD_CONTEXT_LOONGARCH64_REG_S8,
+};
+
+/**
+ * Breakpad defines for Loongarch64
+ */
+#define MD_CONTEXT_LOONGARCH64 0x00800000
+#define MD_CONTEXT_LOONGARCH64_INTEGER (MD_CONTEXT_LOONGARCH64| 0x00000002)
+#define MD_CONTEXT_LOONGARCH64_FLOATING_POINT (MD_CONTEXT_LOONGARCH64 | 0x00000004)
+
+#define MD_CONTEXT_LOONGARCH64_FULL (MD_CONTEXT_LOONGARCH64_INTEGER | \
+ MD_CONTEXT_LOONGARCH64_FLOATING_POINT)
+
+#define MD_CONTEXT_LOONGARCH64_ALL (MD_CONTEXT_LOONGARCH64_INTEGER | \
+ MD_CONTEXT_LOONGARCH64_FLOATING_POINT)
+
+#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_LOONGARCH64_H__
diff --git a/src/google_breakpad/common/minidump_format.h b/src/google_breakpad/common/minidump_format.h
index 7b36d112..c3783764 100644
--- a/src/google_breakpad/common/minidump_format.h
+++ b/src/google_breakpad/common/minidump_format.h
@@ -120,6 +120,7 @@ typedef struct {
#include "minidump_cpu_ppc64.h"
#include "minidump_cpu_sparc.h"
#include "minidump_cpu_x86.h"
+#include "minidump_cpu_loongarch64.h"
/*
* WinVer.h
@@ -660,6 +661,7 @@ 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_LOONGARCH64 = 0x8005, /* Breakpad-defined value for LOONGARCH64 */
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 df80bf7e..5bc051b9 100644
--- a/src/google_breakpad/processor/dump_context.h
+++ b/src/google_breakpad/processor/dump_context.h
@@ -62,6 +62,7 @@ class DumpContext : public DumpObject {
const MDRawContextPPC64* GetContextPPC64() const;
const MDRawContextSPARC* GetContextSPARC() const;
const MDRawContextX86* GetContextX86() const;
+ const MDRawContextLOONGARCH64* GetContextLOONGARCH64() const;
// A convenience method to get the instruction pointer out of the
// MDRawContext, since it varies per-CPU architecture.
@@ -87,6 +88,7 @@ class DumpContext : public DumpObject {
void SetContextARM(MDRawContextARM* arm);
void SetContextARM64(MDRawContextARM64* arm64);
void SetContextMIPS(MDRawContextMIPS* ctx_mips);
+ void SetContextLOONGARCH64(MDRawContextLOONGARCH64* loongarch64);
// Free the CPU-specific context structure.
void FreeContext();
@@ -105,6 +107,7 @@ class DumpContext : public DumpObject {
MDRawContextARM* arm;
MDRawContextARM64* arm64;
MDRawContextMIPS* ctx_mips;
+ MDRawContextLOONGARCH64* loongarch64;
} 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 dc5d8ae6..f6774102 100644
--- a/src/google_breakpad/processor/stack_frame_cpu.h
+++ b/src/google_breakpad/processor/stack_frame_cpu.h
@@ -335,14 +335,6 @@ struct StackFrameARM64 : public StackFrame {
};
struct StackFrameMIPS : public StackFrame {
- // MIPS callee save registers for o32 ABI (32bit registers) are:
- // 1. $s0-$s7,
- // 2. $sp, $fp
- // 3. $f20-$f31
- //
- // The register structure is available at
- // http://en.wikipedia.org/wiki/MIPS_architecture#Compiler_register_usage
-
#define INDEX_MIPS_REG_S0 MD_CONTEXT_MIPS_REG_S0 // 16
#define INDEX_MIPS_REG_S7 MD_CONTEXT_MIPS_REG_S7 // 23
#define INDEX_MIPS_REG_GP MD_CONTEXT_MIPS_REG_GP // 28
@@ -400,6 +392,66 @@ struct StackFrameMIPS : public StackFrame {
int context_validity;
};
+struct StackFrameLOONGARCH64 : public StackFrame {
+ enum ContextValidity {
+ CONTEXT_VALID_NONE = 0,
+
+ CONTEXT_VALID_RA = 1UL << 1,
+ CONTEXT_VALID_TP = 1UL << 2,
+ CONTEXT_VALID_SP = 1UL << 3,
+ CONTEXT_VALID_A0 = 1UL << 4,
+ CONTEXT_VALID_A1 = 1UL << 5,
+ CONTEXT_VALID_A2 = 1UL << 6,
+ CONTEXT_VALID_A3 = 1UL << 7,
+ CONTEXT_VALID_A4 = 1UL << 8,
+ CONTEXT_VALID_A5 = 1UL << 9,
+ CONTEXT_VALID_A6 = 1UL << 10,
+ CONTEXT_VALID_A7 = 1UL << 11,
+ CONTEXT_VALID_T0 = 1UL << 12,
+ CONTEXT_VALID_T1 = 1UL << 13,
+ CONTEXT_VALID_T2 = 1UL << 14,
+ CONTEXT_VALID_T3 = 1UL << 15,
+ CONTEXT_VALID_T4 = 1UL << 16,
+ CONTEXT_VALID_T5 = 1UL << 17,
+ CONTEXT_VALID_T6 = 1UL << 18,
+ CONTEXT_VALID_T7 = 1UL << 19,
+ CONTEXT_VALID_T8 = 1UL << 20,
+ CONTEXT_VALID_X0 = 1UL << 21,
+ CONTEXT_VALID_FP = 1UL << 22,
+ CONTEXT_VALID_S0 = 1UL << 23,
+ CONTEXT_VALID_S1 = 1UL << 24,
+ CONTEXT_VALID_S2 = 1UL << 25,
+ CONTEXT_VALID_S3 = 1UL << 26,
+ CONTEXT_VALID_S4 = 1UL << 27,
+ CONTEXT_VALID_S5 = 1UL << 28,
+ CONTEXT_VALID_S6 = 1UL << 29,
+ CONTEXT_VALID_S7 = 1UL << 30,
+ CONTEXT_VALID_S8 = 1UL << 31,
+
+ CONTEXT_VALID_PC = 1UL << 32,
+ CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE
+ };
+
+ // Return the ContextValidity flag for register rN.
+ static ContextValidity RegisterValidFlag(int n) {
+ return ContextValidity(1 << n);
+ }
+
+ StackFrameLOONGARCH64() : 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.
+ MDRawContextLOONGARCH64 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 long is used instead. It contains CONTEXT_VALID_PC.
+ long context_validity;
+};
+
} // namespace google_breakpad
#endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__
diff --git a/src/processor/basic_source_line_resolver.cc b/src/processor/basic_source_line_resolver.cc
index bbaa1331..59f7c953 100644
--- a/src/processor/basic_source_line_resolver.cc
+++ b/src/processor/basic_source_line_resolver.cc
@@ -682,7 +682,7 @@ bool SymbolParseHelper::ParseInline(
inline_line += 7; // skip prefix
vector<char*> tokens;
- Tokenize(inline_line, kWhitespace, std::numeric_limits<int>::max(), &tokens);
+ Tokenize(inline_line, kWhitespace, std::numeric_limits<int>::max() / 2, &tokens);
// The length of the vector should be at least 5 and an odd number.
if (tokens.size() < 5 && tokens.size() % 2 == 0)
diff --git a/src/processor/dump_context.cc b/src/processor/dump_context.cc
index da531b74..df1cbd5c 100644
--- a/src/processor/dump_context.cc
+++ b/src/processor/dump_context.cc
@@ -140,6 +140,15 @@ const MDRawContextMIPS* DumpContext::GetContextMIPS() const {
return context_.ctx_mips;
}
+const MDRawContextLOONGARCH64* DumpContext::GetContextLOONGARCH64() const {
+ if (GetContextCPU() != MD_CONTEXT_LOONGARCH64) {
+ BPLOG(ERROR) << "DumpContext cannot get LOONGARCH64 context";
+ return NULL;
+ }
+
+ return context_.loongarch64;
+}
+
bool DumpContext::GetInstructionPointer(uint64_t* ip) const {
BPLOG_IF(ERROR, !ip) << "DumpContext::GetInstructionPointer requires |ip|";
assert(ip);
@@ -176,6 +185,9 @@ bool DumpContext::GetInstructionPointer(uint64_t* ip) const {
case MD_CONTEXT_MIPS64:
*ip = GetContextMIPS()->epc;
break;
+ case MD_CONTEXT_LOONGARCH64:
+ *ip = GetContextLOONGARCH64()->csr_epc;
+ break;
default:
// This should never happen.
BPLOG(ERROR) << "Unknown CPU architecture in GetInstructionPointer";
@@ -220,6 +232,8 @@ bool DumpContext::GetStackPointer(uint64_t* sp) const {
case MD_CONTEXT_MIPS64:
*sp = GetContextMIPS()->iregs[MD_CONTEXT_MIPS_REG_SP];
break;
+ case MD_CONTEXT_LOONGARCH64:
+ *sp = GetContextLOONGARCH64()->iregs[MD_CONTEXT_LOONGARCH64_REG_SP];
default:
// This should never happen.
BPLOG(ERROR) << "Unknown CPU architecture in GetStackPointer";
@@ -264,6 +278,10 @@ void DumpContext::SetContextMIPS(MDRawContextMIPS* ctx_mips) {
context_.ctx_mips = ctx_mips;
}
+void DumpContext::SetContextLOONGARCH64(MDRawContextLOONGARCH64* loongarch64) {
+ context_.loongarch64 = loongarch64;
+}
+
void DumpContext::FreeContext() {
switch (GetContextCPU()) {
case MD_CONTEXT_X86:
@@ -299,6 +317,10 @@ void DumpContext::FreeContext() {
delete context_.ctx_mips;
break;
+ case MD_CONTEXT_LOONGARCH64:
+ delete context_.loongarch64;
+ 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
@@ -655,6 +677,29 @@ void DumpContext::Print() {
break;
}
+ case MD_CONTEXT_LOONGARCH64: {
+ const MDRawContextLOONGARCH64* context_loongarch = GetContextLOONGARCH64();
+ printf("MDRawContextLOONGARCH64\n");
+ printf(" context_flags = 0x%x\n",
+ context_loongarch->context_flags);
+ for (int ireg_index = 0;
+ ireg_index < MD_CONTEXT_LOONGARCH64_GPR_COUNT;
+ ++ireg_index) {
+ printf(" iregs[%2d] = 0x%" PRIx64 "\n",
+ ireg_index, context_loongarch->iregs[ireg_index]);
+ }
+
+ printf(" csr_epc = 0x%" PRIx64 "\n",
+ context_loongarch->csr_epc);
+ for (int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_LOONGARCH64_FPR_COUNT;
+ ++fpr_index) {
+ printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n",
+ fpr_index, context_loongarch->float_save.regs[fpr_index]);
+ }
+ break;
+ }
+
default: {
break;
}
diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc
index 11a5fc4c..cd708952 100644
--- a/src/processor/minidump.cc
+++ b/src/processor/minidump.cc
@@ -96,6 +96,8 @@ bool IsContextSizeUnique(uint32_t context_size) {
num_matching_contexts++;
if (context_size == sizeof(MDRawContextMIPS))
num_matching_contexts++;
+ if (context_size == sizeof(MDRawContextLOONGARCH64))
+ num_matching_contexts++;
return num_matching_contexts == 1;
}
@@ -1157,6 +1159,57 @@ bool MinidumpContext::Read(uint32_t expected_size) {
break;
}
+ case MD_CONTEXT_LOONGARCH64: {
+ if (expected_size != sizeof(MDRawContextLOONGARCH64)) {
+ BPLOG(ERROR) << "MinidumpContext LOONGARCH64 size mismatch, "
+ << expected_size
+ << " != "
+ << sizeof(MDRawContextLOONGARCH64);
+ return false;
+ }
+
+ scoped_ptr<MDRawContextLOONGARCH64> context_loongarch(new MDRawContextLOONGARCH64());
+
+ // 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_loongarch->context_flags = context_flags;
+
+ size_t flags_size = sizeof(context_loongarch->context_flags);
+ uint8_t* context_after_flags =
+ reinterpret_cast<uint8_t*>(context_loongarch.get()) + flags_size;
+ if (!minidump_->ReadBytes(context_after_flags,
+ sizeof(MDRawContextLOONGARCH64) - flags_size)) {
+ BPLOG(ERROR) << "MinidumpContext could not read LOONGARCH64 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 LOONGARCH64 does not match system info";
+ return false;
+ }
+
+ if (minidump_->swap()) {
+ // context_loongarch->context_flags was already swapped.
+ for (int ireg_index = 0;
+ ireg_index < MD_CONTEXT_LOONGARCH64_GPR_COUNT;
+ ++ireg_index) {
+ Swap(&context_loongarch->iregs[ireg_index]);
+ }
+ Swap(&context_loongarch->csr_epc);
+ for (int fpr_index = 0;
+ fpr_index < MD_FLOATINGSAVEAREA_LOONGARCH64_FPR_COUNT;
+ ++fpr_index) {
+ Swap(&context_loongarch->float_save.regs[fpr_index]);
+ }
+ }
+ SetContextLOONGARCH64(context_loongarch.release());
+
+ break;
+ }
+
default: {
// Unknown context type - Don't log as an error yet. Let the
// caller work that out.
@@ -1249,6 +1302,11 @@ bool MinidumpContext::CheckAgainstSystemInfo(uint32_t context_cpu_type) {
if (system_info_cpu_type == MD_CPU_ARCHITECTURE_MIPS64)
return_value = true;
break;
+
+ case MD_CONTEXT_LOONGARCH64:
+ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_LOONGARCH64)
+ return_value = true;
+ break;
}
BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " <<
@@ -5097,6 +5155,8 @@ bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t* context_cpu_flags) {
GetSystemInfo() ? GetSystemInfo()->system_info() : NULL;
if (system_info != NULL) {
+ BPLOG(INFO) << "system_info" <<
+ system_info;
switch (system_info->processor_architecture) {
case MD_CPU_ARCHITECTURE_X86:
*context_cpu_flags = MD_CONTEXT_X86;
@@ -5107,6 +5167,9 @@ bool Minidump::GetContextCPUFlagsFromSystemInfo(uint32_t* context_cpu_flags) {
case MD_CPU_ARCHITECTURE_MIPS64:
*context_cpu_flags = MD_CONTEXT_MIPS64;
break;
+ case MD_CPU_ARCHITECTURE_LOONGARCH64:
+ *context_cpu_flags = MD_CONTEXT_LOONGARCH64;
+ break;
case MD_CPU_ARCHITECTURE_ALPHA:
*context_cpu_flags = MD_CONTEXT_ALPHA;
break;
diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc
index 04b7e129..0c5e61d0 100644
--- a/src/processor/minidump_processor.cc
+++ b/src/processor/minidump_processor.cc
@@ -383,6 +383,7 @@ static uint64_t GetAddressForArchitecture(const MDCPUArchitecture architecture,
switch (architecture) {
case MD_CPU_ARCHITECTURE_X86:
case MD_CPU_ARCHITECTURE_MIPS:
+ case MD_CPU_ARCHITECTURE_LOONGARCH64:
case MD_CPU_ARCHITECTURE_PPC:
case MD_CPU_ARCHITECTURE_SHX:
case MD_CPU_ARCHITECTURE_ARM:
@@ -591,6 +592,12 @@ bool MinidumpProcessor::GetCPUInfo(Minidump* dump, SystemInfo* info) {
info->cpu = "mips64";
break;
}
+
+ case MD_CPU_ARCHITECTURE_LOONGARCH64: {
+ info->cpu = "loongarch64";
+ break;
+ }
+
default: {
// Assign the numeric architecture ID into the CPU string.
diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc
index 856a6a66..7e953ebe 100644
--- a/src/processor/stackwalk_common.cc
+++ b/src/processor/stackwalk_common.cc
@@ -637,6 +637,147 @@ static void PrintStack(const CallStack* stack,
sequence = PrintRegister64(
"s7", frame_mips->context.iregs[MD_CONTEXT_MIPS_REG_S7],
sequence);
+ } else if ((cpu == "loongarch64")) {
+ const StackFrameLOONGARCH64* frame_loongarch =
+ reinterpret_cast<const StackFrameLOONGARCH64*>(frame);
+
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_RA)
+ sequence = PrintRegister64(
+ "ra", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_TP)
+ sequence = PrintRegister64(
+ "tp", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_TP],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_SP)
+ sequence = PrintRegister64(
+ "sp", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP],
+ sequence);
+
+ // Save registers a0-a7
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A0)
+ sequence = PrintRegister64(
+ "a0", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A0],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A1)
+ sequence = PrintRegister64(
+ "a1", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A1],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A2)
+ sequence = PrintRegister64(
+ "a2", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A2],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A3)
+ sequence = PrintRegister64(
+ "a3", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A3],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A4)
+ sequence = PrintRegister64(
+ "a4", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A4],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A5)
+ sequence = PrintRegister64(
+ "a5", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A5],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A6)
+ sequence = PrintRegister64(
+ "a6", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A6],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_A7)
+ sequence = PrintRegister64(
+ "a7", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_A7],
+ sequence);
+
+ // Save registers t0-s8
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T0)
+ sequence = PrintRegister64(
+ "t0", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T0],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T1)
+ sequence = PrintRegister64(
+ "t1", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T1],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T2)
+ sequence = PrintRegister64(
+ "t2", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T2],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T3)
+ sequence = PrintRegister64(
+ "t3", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T3],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T4)
+ sequence = PrintRegister64(
+ "t4", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T4],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T5)
+ sequence = PrintRegister64(
+ "t5", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T5],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T6)
+ sequence = PrintRegister64(
+ "t6", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T6],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T7)
+ sequence = PrintRegister64(
+ "t7", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T7],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_T8)
+ sequence = PrintRegister64(
+ "t8", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_T8],
+ sequence);
+
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_X0)
+ sequence = PrintRegister64(
+ "x0", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_X0],
+ sequence);
+
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_FP)
+ sequence = PrintRegister64(
+ "fp", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_FP],
+ sequence);
+
+ // Save registers s0-s8
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S0)
+ sequence = PrintRegister64(
+ "s0", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S0],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S1)
+ sequence = PrintRegister64(
+ "s1", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S1],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S2)
+ sequence = PrintRegister64(
+ "s2", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S2],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S3)
+ sequence = PrintRegister64(
+ "s3", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S3],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S4)
+ sequence = PrintRegister64(
+ "s4", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S4],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S5)
+ sequence = PrintRegister64(
+ "s5", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S5],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S6)
+ sequence = PrintRegister64(
+ "s6", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S6],
+ sequence);
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S7)
+ sequence = PrintRegister64(
+ "s7", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S7],
+ sequence);
+
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_S8)
+ sequence = PrintRegister64(
+ "s8", frame_loongarch->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S8],
+ sequence);
+
+ if (frame_loongarch->context_validity & StackFrameLOONGARCH64::CONTEXT_VALID_PC)
+ sequence = PrintRegister64("pc", frame_loongarch->context.csr_epc, 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 d4897d4c..100135d8 100644
--- a/src/processor/stackwalker.cc
+++ b/src/processor/stackwalker.cc
@@ -55,6 +55,7 @@
#include "processor/stackwalker_arm.h"
#include "processor/stackwalker_arm64.h"
#include "processor/stackwalker_mips.h"
+#include "processor/stackwalker_loongarch64.h"
namespace google_breakpad {
@@ -252,6 +253,12 @@ Stackwalker* Stackwalker::StackwalkerForCPU(
memory, modules, frame_symbolizer);
break;
+ case MD_CONTEXT_LOONGARCH64:
+ cpu_stackwalker = new StackwalkerLOONGARCH64(system_info,
+ context->GetContextLOONGARCH64(),
+ memory, modules, frame_symbolizer);
+ break;
+
case MD_CONTEXT_ARM:
{
int fp_register = -1;
diff --git a/src/processor/stackwalker_loongarch64.cc b/src/processor/stackwalker_loongarch64.cc
new file mode 100644
index 00000000..1c97dcfb
--- /dev/null
+++ b/src/processor/stackwalker_loongarch64.cc
@@ -0,0 +1,290 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_loongarch64.cc: LOONGARCH64-specific stackwalker.
+//
+// See stackwalker_loongarch64.h for documentation.
+
+#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/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/cfi_frame_info.h"
+#include "processor/logging.h"
+#include "processor/postfix_evaluator-inl.h"
+#include "processor/stackwalker_loongarch64.h"
+#include "processor/windows_frame_info.h"
+#include "google_breakpad/common/minidump_cpu_loongarch64.h"
+
+namespace google_breakpad {
+
+StackwalkerLOONGARCH64::StackwalkerLOONGARCH64(const SystemInfo* system_info,
+ const MDRawContextLOONGARCH64* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* resolver_helper)
+: Stackwalker(system_info, memory, modules, resolver_helper),
+ context_(context) {
+ if (memory_) {
+ if (0xffffffffffffffff - memory_->GetBase() < memory_->GetSize() - 1) {
+ BPLOG(ERROR) << "Memory out of range for stackwalking loongarch64: "
+ << HexString(memory_->GetBase())
+ << "+"
+ << HexString(memory_->GetSize());
+ memory_ = NULL;
+ }
+ }
+}
+
+StackFrame* StackwalkerLOONGARCH64::GetContextFrame() {
+ if (!context_) {
+ BPLOG(ERROR) << "Can't get context frame without context.";
+ return NULL;
+ }
+
+ StackFrameLOONGARCH64* frame = new StackFrameLOONGARCH64();
+
+ // The instruction pointer is stored directly in a register, so pull it
+ // straight out of the CPU context structure.
+ frame->context = *context_;
+ frame->context_validity = StackFrameLOONGARCH64::CONTEXT_VALID_ALL;
+ frame->trust = StackFrame::FRAME_TRUST_CONTEXT;
+ frame->instruction = frame->context.csr_epc;
+
+ return frame;
+}
+
+// Register names for loongarch.
+static const char* const kRegisterNames[] = {
+ "$zero", "$ra", "$tp", "$sp", "$a0", "$a1", "$a2", "$a3",
+ "$a4", "$a5", "$a6", "$a7", "$t0", "$t1", "$t2", "$t3",
+ "$t4", "$t5", "$t6", "$t7", "$t8", "$x", "$fp", "$s0",
+ "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7", "$s8",
+ "$fa0", "$fa1", "$fa2", "$fa3", "$fa4", "$fa5", "$fa6", "$fa7",
+ "$ft0", "$ft1", "$ft2", "$ft3", "$ft4", "$ft5", "$ft6", "$ft7",
+ "$ft8", "$ft9", "$ft10","$ft11","$ft12","$ft13","$ft14","$ft15",
+ "$fs0", "$fs1", "$fs2", "$fs3", "$fs4", "$fs5", "$fs6", "$fs7",
+ NULL
+ // TODO(gordanac): add float point save registers
+};
+
+StackFrameLOONGARCH64* StackwalkerLOONGARCH64::GetCallerByCFIFrameInfo(
+ const vector<StackFrame*>& frames,
+ CFIFrameInfo* cfi_frame_info) {
+ StackFrameLOONGARCH64* last_frame = static_cast<StackFrameLOONGARCH64*>(frames.back());
+
+ uint64_t pc = 0;
+
+ // Populate a dictionary with the valid register values in last_frame.
+ CFIFrameInfo::RegisterValueMap<uint64_t> callee_registers;
+ // Use the STACK CFI data to recover the caller's register values.
+ CFIFrameInfo::RegisterValueMap<uint64_t> caller_registers;
+
+ for (int i = 0; kRegisterNames[i]; ++i) {
+ caller_registers[kRegisterNames[i]] = last_frame->context.iregs[i];
+ callee_registers[kRegisterNames[i]] = last_frame->context.iregs[i];
+ }
+
+ if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_,
+ &caller_registers)) {
+ return NULL;
+ }
+
+ CFIFrameInfo::RegisterValueMap<uint64_t>::const_iterator entry =
+ caller_registers.find(".cfa");
+
+ if (entry != caller_registers.end()) {
+ caller_registers["$sp"] = entry->second;
+ }
+
+ entry = caller_registers.find(".ra");
+ if (entry != caller_registers.end()) {
+ caller_registers["$ra"] = entry->second;
+ pc = entry->second - sizeof(pc);
+ }
+ caller_registers["$pc"] = pc;
+ // Construct a new stack frame given the values the CFI recovered.
+ scoped_ptr<StackFrameLOONGARCH64> frame(new StackFrameLOONGARCH64());
+
+ for (int i = 0; kRegisterNames[i]; ++i) {
+ CFIFrameInfo::RegisterValueMap<uint64_t>::const_iterator caller_entry =
+ caller_registers.find(kRegisterNames[i]);
+
+ if (caller_entry != caller_registers.end()) {
+ // The value of this register is recovered; fill the context with the
+ // value from caller_registers.
+ frame->context.iregs[i] = caller_entry->second;
+ frame->context_validity |= StackFrameLOONGARCH64::RegisterValidFlag(i);
+ } else {
+ // If the STACK CFI data doesn't mention some callee-save register, and
+ // it is valid in the callee, assume the callee has not yet changed it.
+ // Calee-save registers according to the loongarch psABI specification are:
+ // $s0 to $s8, $fp, $sp
+ frame->context.iregs[i] = last_frame->context.iregs[i];
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S0;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S1;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S2;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S3;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S4;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S5;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S6;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S7;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_S8;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_FP;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_SP;
+ }
+ }
+
+ frame->context.csr_epc = caller_registers["$pc"];
+ frame->instruction = caller_registers["$pc"];
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_PC;
+
+ frame->context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = caller_registers["$ra"];
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_RA;
+
+ frame->trust = StackFrame::FRAME_TRUST_CFI;
+
+ return frame.release();
+}
+
+StackFrame* StackwalkerLOONGARCH64::GetCallerFrame(const CallStack* stack,
+ bool stack_scan_allowed) {
+ if (!memory_ || !stack) {
+ BPLOG(ERROR) << "Can't get caller frame without memory or stack";
+ return NULL;
+ }
+
+ const vector<StackFrame*>& frames = *stack->frames();
+ StackFrameLOONGARCH64* last_frame = static_cast<StackFrameLOONGARCH64*>(frames.back());
+ scoped_ptr<StackFrameLOONGARCH64> new_frame;
+
+ // See if there is DWARF call frame information covering this address.
+ scoped_ptr<CFIFrameInfo> cfi_frame_info(
+ frame_symbolizer_->FindCFIFrameInfo(last_frame));
+ if (cfi_frame_info.get())
+ new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
+
+ // If caller frame is not found in CFI try analyzing the stack.
+ if (stack_scan_allowed && !new_frame.get()) {
+ new_frame.reset(GetCallerByStackScan(frames));
+ }
+
+ // If nothing worked, tell the caller.
+ if (!new_frame.get()) {
+ return NULL;
+ }
+
+ // Should we terminate the stack walk? (end-of-stack or broken invariant)
+ if (TerminateWalk(new_frame->context.csr_epc,
+ new_frame->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP],
+ last_frame->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP],
+ frames.size() == 1)) {
+ return NULL;
+ }
+
+ return new_frame.release();
+}
+
+StackFrameLOONGARCH64* StackwalkerLOONGARCH64::GetCallerByStackScan(
+ const vector<StackFrame*>& frames) {
+ const uint32_t kMaxFrameStackSize = 1024;
+
+ StackFrameLOONGARCH64* last_frame = static_cast<StackFrameLOONGARCH64*>(frames.back());
+
+ uint64_t last_sp = last_frame->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP];
+ uint64_t caller_pc, caller_sp, caller_fp;
+
+ // Return address cannot be obtained directly.
+ // Force stackwalking.
+
+ // We cannot use frame pointer to get the return address.
+ // We'll scan the stack for a
+ // return address. This can happen if last_frame is executing code
+ // for a module for which we don't have symbols.
+ int count = kMaxFrameStackSize / sizeof(caller_pc);
+
+ do {
+ // Scanning for return address from stack pointer of the last frame.
+ if (!ScanForReturnAddress(last_sp, &caller_sp, &caller_pc, count)) {
+ // If we can't find an instruction pointer even with stack scanning,
+ // give up.
+ BPLOG(ERROR) << " ScanForReturnAddress failed ";
+ return NULL;
+ }
+ // Get $fp stored in the stack frame.
+ if (!memory_->GetMemoryAtAddress(caller_sp - sizeof(caller_pc),
+ &caller_fp)) {
+ BPLOG(INFO) << " GetMemoryAtAddress for fp failed " ;
+ return NULL;
+ }
+
+ count = count - (caller_sp - last_sp) / sizeof(caller_pc);
+ // Now scan the next address in the stack.
+ last_sp = caller_sp + sizeof(caller_pc);
+ } while ((caller_fp - caller_sp >= kMaxFrameStackSize) && count > 0);
+
+ if (!count) {
+ BPLOG(INFO) << " No frame 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 += sizeof(caller_pc);
+ // caller_pc is actually containing $ra value;
+ // $pc is two instructions before $ra,
+ // so the caller_pc needs to be decremented accordingly.
+ caller_pc -= sizeof(caller_pc);
+
+ // Create a new stack frame (ownership will be transferred to the caller)
+ // and fill it in.
+ StackFrameLOONGARCH64* frame = new StackFrameLOONGARCH64();
+ frame->trust = StackFrame::FRAME_TRUST_SCAN;
+ frame->context = last_frame->context;
+ frame->context.csr_epc = caller_pc;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_PC;
+ frame->instruction = caller_pc;
+
+ frame->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = caller_sp;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_SP;
+ frame->context.iregs[MD_CONTEXT_LOONGARCH64_REG_FP] = caller_fp;
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_FP;
+
+ frame->context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] =
+ caller_pc + sizeof(caller_pc);
+ frame->context_validity |= StackFrameLOONGARCH64::CONTEXT_VALID_RA;
+
+ return frame;
+}
+
+} // namespace google_breakpad
+
diff --git a/src/processor/stackwalker_loongarch64.h b/src/processor/stackwalker_loongarch64.h
new file mode 100644
index 00000000..710beb70
--- /dev/null
+++ b/src/processor/stackwalker_loongarch64.h
@@ -0,0 +1,83 @@
+// Copyright (c) 2013 Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// stackwalker_loongarch64.h: LOONGARCH64-specific stackwalker.
+//
+// Provides stack frames given LOONGARCH64 register context and a memory region
+// corresponding to a LOONGARCH64 stack.
+
+#ifndef PROCESSOR_STACKWALKER_LOONGARCH64_H__
+#define PROCESSOR_STACKWALKER_LOONGARCH64_H__
+
+#include "google_breakpad/common/breakpad_types.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/stackwalker.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/cfi_frame_info.h"
+
+namespace google_breakpad {
+
+class CodeModules;
+
+class StackwalkerLOONGARCH64 : public Stackwalker {
+ public:
+ // Context is a loongarch64 context object that gives access to
+ // loongarch64-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.
+ StackwalkerLOONGARCH64(const SystemInfo* system_info,
+ const MDRawContextLOONGARCH64* context,
+ MemoryRegion* memory,
+ const CodeModules* modules,
+ StackFrameSymbolizer* frame_symbolizer);
+
+ private:
+ // Implementation of Stackwalker, using loongarch64 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.
+ StackFrameLOONGARCH64* GetCallerByCFIFrameInfo(const vector<StackFrame*>& frames,
+ CFIFrameInfo* cfi_frame_info);
+
+ // Scan the stack for plausible return address and frame pointer pair.
+ // The caller takes ownership of the returned frame. Return NULL on failure.
+ StackFrameLOONGARCH64* GetCallerByStackScan(const vector<StackFrame*>& frames);
+
+ // Stores the CPU context corresponding to the innermost stack frame to
+ // be returned by GetContextFrame.
+ const MDRawContextLOONGARCH64* context_;
+};
+
+} // namespace google_breakpad
+
+#endif // PROCESSOR_STACKWALKER_LOONGARCH64_H__
diff --git a/src/processor/stackwalker_loongarch64_unittest.cc b/src/processor/stackwalker_loongarch64_unittest.cc
new file mode 100644
index 00000000..3b30bb19
--- /dev/null
+++ b/src/processor/stackwalker_loongarch64_unittest.cc
@@ -0,0 +1,717 @@
+// Copyright (c) 2013, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Original author: Veljko Mihailovic <veljko.m...@imgtec.com>
+
+// stackwalker_loongarch64_unittest.cc: Unit tests for StackwalkerLOONGARCH64
+// class for loongarch64 platforms.
+
+#include <string.h>
+#include <string>
+#include <vector>
+
+#include "breakpad_googletest_includes.h"
+#include "common/test_assembler.h"
+#include "common/using_std_string.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/processor/basic_source_line_resolver.h"
+#include "google_breakpad/processor/call_stack.h"
+#include "google_breakpad/processor/code_module.h"
+#include "google_breakpad/processor/source_line_resolver_interface.h"
+#include "google_breakpad/processor/stack_frame_cpu.h"
+#include "processor/stackwalker_unittest_utils.h"
+#include "processor/stackwalker_loongarch64.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::StackFrameLOONGARCH64;
+using google_breakpad::Stackwalker;
+using google_breakpad::StackwalkerLOONGARCH64;
+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 StackwalkerLOONGARCH64Fixture {
+ public:
+ StackwalkerLOONGARCH64Fixture()
+ : stack_section(kLittleEndian),
+ // Give the two modules reasonable standard locations and names
+ // for tests to play with.
+ module1(0x00400000, 0x10000, "module1", "version1"),
+ module2(0x00500000, 0x10000, "module2", "version2") {
+ // Identify the system as a Linux system.
+ system_info.os = "Linux";
+ system_info.os_short = "linux";
+ system_info.os_version = "Observant Opossum"; // Jealous Jellyfish
+ system_info.cpu = "loongarch64";
+ 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(MDRawContextLOONGARCH64* raw_context) {
+ uint8_t x = 173;
+ for (size_t i = 0; i < sizeof(*raw_context); ++i)
+ reinterpret_cast<uint8_t*>(raw_context)[i] = (x += 17);
+ }
+
+ SystemInfo system_info;
+ MDRawContextLOONGARCH64 raw_context;
+ Section stack_section;
+ MockMemoryRegion stack_region;
+ MockCodeModule module1;
+ MockCodeModule module2;
+ MockCodeModules modules;
+ MockSymbolSupplier supplier;
+ BasicSourceLineResolver resolver;
+ CallStack call_stack;
+ const vector<StackFrame*>* frames;
+};
+
+class SanityCheck: public StackwalkerLOONGARCH64Fixture, public Test { };
+
+TEST_F(SanityCheck, NoResolver) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ stack_section.start() = 0x80000000;
+ stack_section.D64(0).D64(0x0);
+ RegionFromSection();
+ raw_context.csr_epc = 0x00400020;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(NULL, NULL);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ // This should succeed, even without a resolver or supplier.
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(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());
+ StackFrameLOONGARCH64* frame = static_cast<StackFrameLOONGARCH64*>(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 StackwalkerLOONGARCH64Fixture, public Test { };
+
+TEST_F(GetContextFrame, Simple) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ stack_section.start() = 0x80000000;
+ stack_section.D64(0).D64(0x0);
+ RegionFromSection();
+ raw_context.csr_epc = 0x00400020;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(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();
+ StackFrameLOONGARCH64* frame = static_cast<StackFrameLOONGARCH64*>(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)));
+}
+
+// The stackwalker should be able to produce the context frame even
+// without stack memory present.
+TEST_F(GetContextFrame, NoStackMemory) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ raw_context.csr_epc = 0x00400020;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = 0x80000000;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, NULL, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(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();
+ StackFrameLOONGARCH64* frame = static_cast<StackFrameLOONGARCH64*>(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 StackwalkerLOONGARCH64Fixture, public Test { };
+
+TEST_F(GetCallerFrame, ScanWithoutSymbols) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ // 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 = 0x00400100;
+ uint64_t return_address2 = 0x00400900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // space
+
+ .D64(0x00490000) // junk that's not
+ .D64(0x00600000) // a return address
+
+ .D64(frame1_sp) // stack pointer
+ .D64(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(32, 0) // space
+
+ .D64(0xF0000000) // more junk
+ .D64(0x0000000D)
+
+ .D64(frame2_sp) // stack pointer
+ .D64(return_address2) // actual return address
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.csr_epc = 0x00405510;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = return_address1;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(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(3U, frames->size());
+
+ StackFrameLOONGARCH64* frame0 = static_cast<StackFrameLOONGARCH64*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameLOONGARCH64::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameLOONGARCH64* frame1 = static_cast<StackFrameLOONGARCH64*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameLOONGARCH64::CONTEXT_VALID_PC |
+ StackFrameLOONGARCH64::CONTEXT_VALID_SP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_FP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_RA),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1 - sizeof(return_address1), frame1->context.csr_epc);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP]);
+
+ StackFrameLOONGARCH64* frame2 = static_cast<StackFrameLOONGARCH64*>(frames->at(2));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame2->trust);
+ ASSERT_EQ((StackFrameLOONGARCH64::CONTEXT_VALID_PC |
+ StackFrameLOONGARCH64::CONTEXT_VALID_SP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_FP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_RA),
+ frame2->context_validity);
+ EXPECT_EQ(return_address2 - sizeof(return_address2), frame2->context.csr_epc);
+ EXPECT_EQ(frame2_sp.Value(), frame2->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP]);
+}
+
+TEST_F(GetCallerFrame, ScanWithFunctionSymbols) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ // 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 = 0x00500200;
+ Label frame1_sp;
+ stack_section
+ // frame 0
+ .Append(16, 0) // space
+
+ .D64(0x00490000) // junk that's not
+ .D64(0x00600000) // a return address
+
+ .D64(0x00401000) // a couple of plausible addresses
+ .D64(0x0050F000) // that are not within functions
+
+ .D64(frame1_sp) // stack pointer
+ .D64(return_address) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.csr_epc = 0x00400200;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = return_address;
+
+ 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);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(2U, frames->size());
+
+ StackFrameLOONGARCH64* frame0 = static_cast<StackFrameLOONGARCH64*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameLOONGARCH64::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(0x00400100U, frame0->function_base);
+
+ StackFrameLOONGARCH64* frame1 = static_cast<StackFrameLOONGARCH64*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameLOONGARCH64::CONTEXT_VALID_PC |
+ StackFrameLOONGARCH64::CONTEXT_VALID_SP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_FP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_RA),
+ frame1->context_validity);
+ EXPECT_EQ(return_address - sizeof(return_address), frame1->context.csr_epc);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP]);
+ EXPECT_EQ("marsupial", frame1->function_name);
+ EXPECT_EQ(0x00500100U, frame1->function_base);
+}
+
+TEST_F(GetCallerFrame, CheckStackFrameSizeLimit) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ // If the stackwalker resorts to stack scanning, it will scan only
+ // 1024 bytes of stack which correspondes to maximum size of stack frame.
+ stack_section.start() = 0x80000000;
+ uint64_t return_address1 = 0x00500100;
+ uint64_t return_address2 = 0x00500900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // space
+
+ .D64(0x00490000) // junk that's not
+ .D64(0x00600000) // a return address
+
+ .Append(96, 0) // more space
+
+ .D64(frame1_sp) // stack pointer
+ .D64(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(128 * 4, 0) // space
+
+ .D64(0x00F00000) // more junk
+ .D64(0x0000000D)
+
+ .Append(128 * 4, 0) // more space
+
+ .D64(frame2_sp) // stack pointer
+ .D64(return_address2) // actual return address
+ // (won't be found)
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.csr_epc = 0x00405510;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = return_address1;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(2U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ("module2", modules_without_symbols[1]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(2U, frames->size());
+
+ StackFrameLOONGARCH64* frame0 = static_cast<StackFrameLOONGARCH64*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameLOONGARCH64::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+
+ StackFrameLOONGARCH64* frame1 = static_cast<StackFrameLOONGARCH64*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_SCAN, frame1->trust);
+ ASSERT_EQ((StackFrameLOONGARCH64::CONTEXT_VALID_PC |
+ StackFrameLOONGARCH64::CONTEXT_VALID_SP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_FP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_RA),
+ frame1->context_validity);
+ EXPECT_EQ(return_address1 - sizeof(return_address1), frame1->context.csr_epc);
+ EXPECT_EQ(frame1_sp.Value(), frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP]);
+}
+
+// Test that set_max_frames_scanned prevents using stack scanning
+// to find caller frames.
+TEST_F(GetCallerFrame, ScanningNotAllowed) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ // When the stack walker resorts to scanning the stack,
+ // only fixed number of frames are allowed to be scanned out from stack
+ stack_section.start() = 0x80000000;
+ uint64_t return_address1 = 0x00500100;
+ uint64_t return_address2 = 0x00500900;
+ Label frame1_sp, frame2_sp;
+ stack_section
+ // frame 0
+ .Append(32, 0) // space
+
+ .D64(0x00490000) // junk that's not
+ .D64(0x00600000) // a return address
+
+ .Append(96, 0) // more space
+
+ .D64(frame1_sp) // stack pointer
+ .D64(return_address1) // actual return address
+ // frame 1
+ .Mark(&frame1_sp)
+ .Append(128 * 4, 0) // space
+
+ .D64(0x00F00000) // more junk
+ .D64(0x0000000D)
+
+ .Append(128 * 4, 0) // more space
+
+ .D64(frame2_sp) // stack pointer
+ .D64(return_address2) // actual return address
+ // (won't be found)
+ // frame 2
+ .Mark(&frame2_sp)
+ .Append(64, 0); // end of stack
+ RegionFromSection();
+
+ raw_context.csr_epc = 0x00405510;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = stack_section.start().Value();
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = return_address1;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ Stackwalker::set_max_frames_scanned(0);
+
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(1U, modules_without_symbols.size());
+ ASSERT_EQ("module1", modules_without_symbols[0]->debug_file());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+
+ StackFrameLOONGARCH64* frame0 = static_cast<StackFrameLOONGARCH64*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameLOONGARCH64::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ(0, memcmp(&raw_context, &frame0->context, sizeof(raw_context)));
+}
+
+struct CFIFixture: public StackwalkerLOONGARCH64Fixture {
+ CFIFixture() {
+ // Provide some STACK CFI records;
+ SetModuleSymbols(&module1,
+ // The youngest frame's function.
+ "FUNC 4000 1000 0 enchiridion\n"
+ // Initially, nothing has been pushed on the stack,
+ // and the return address is still in the $ra register.
+ "STACK CFI INIT 4000 1000 .cfa: $sp 0 + .ra: $ra\n"
+ // Move stack pointer.
+ "STACK CFI 4004 .cfa: $sp 32 +\n"
+ // store $fp and ra
+ "STACK CFI 4008 $fp: .cfa -8 + ^ .ra: .cfa -4 + ^\n"
+ // restore $fp
+ "STACK CFI 400c .cfa: $fp 32 +\n"
+ // restore $sp
+ "STACK CFI 4018 .cfa: $sp 32 +\n"
+
+ "STACK CFI 4020 $fp: $fp .cfa: $sp 0 + .ra: .ra\n"
+
+ // The calling function.
+ "FUNC 5000 1000 0 epictetus\n"
+ // Mark it as end of stack.
+ "STACK CFI INIT 5000 8 .cfa: $sp 0 + .ra: $ra\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.csr_epc = 0x00405500;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S0] = 0x0;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S1] = 0x1;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S2] = 0x2;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S3] = 0x3;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S4] = 0x4;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S5] = 0x5;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S6] = 0x6;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S7] = 0x7;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = 0x80000000;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_FP] = 0x80000000;
+ expected.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = 0x00405508;
+
+ // Expect CFI to recover all callee-save 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 = (StackFrameLOONGARCH64::CONTEXT_VALID_PC |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S0 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S1 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S2 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S3 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S4 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S5 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S6 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S7 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_S8 |
+ StackFrameLOONGARCH64::CONTEXT_VALID_SP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_FP |
+ StackFrameLOONGARCH64::CONTEXT_VALID_RA);
+
+ // By default, context frames provide all registers, as normal.
+ context_frame_validity = StackFrameLOONGARCH64::CONTEXT_VALID_ALL;
+
+ // By default, registers are unchanged.
+ raw_context = expected;
+ }
+
+ // Walk the stack, using stack_section as the contents of the stack
+ // and raw_context as the current register values. (Set the stack
+ // pointer to the stack's starting address.) Expect two stack
+ // frames; in the older frame, expect the callee-saves registers to
+ // have values matching those in 'expected'.
+ void CheckWalk() {
+ RegionFromSection();
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = stack_section.start().Value();
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region,
+ &modules, &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(2U, frames->size());
+
+ StackFrameLOONGARCH64* frame0 = static_cast<StackFrameLOONGARCH64*>(frames->at(0));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CONTEXT, frame0->trust);
+ ASSERT_EQ(StackFrameLOONGARCH64::CONTEXT_VALID_ALL, frame0->context_validity);
+ EXPECT_EQ("enchiridion", frame0->function_name);
+ EXPECT_EQ(0x00404000U, frame0->function_base);
+
+ StackFrameLOONGARCH64* frame1 = static_cast<StackFrameLOONGARCH64*>(frames->at(1));
+ EXPECT_EQ(StackFrame::FRAME_TRUST_CFI, frame1->trust);
+ ASSERT_EQ(expected_validity, frame1->context_validity);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S0],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S0]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S1],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S1]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S2],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S2]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S3],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S3]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S4],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S4]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S5],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S5]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S6],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S6]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_S7],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_S7]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_FP],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_FP]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_RA],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA]);
+ EXPECT_EQ(expected.iregs[MD_CONTEXT_LOONGARCH64_REG_SP],
+ frame1->context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP]);
+ EXPECT_EQ(expected.csr_epc, frame1->context.csr_epc);
+ EXPECT_EQ(expected.csr_epc, frame1->instruction);
+ EXPECT_EQ("epictetus", frame1->function_name);
+ EXPECT_EQ(0x00405000U, frame1->function_base);
+ }
+
+ // The values we expect to find for the caller's registers.
+ MDRawContextLOONGARCH64 expected;
+
+ // The validity mask for expected.
+ long expected_validity;
+
+ // The validity mask to impose on the context frame.
+ int context_frame_validity;
+};
+
+class CFI: public CFIFixture, public Test { };
+
+// TODO(gordanac): add CFI tests
+
+TEST_F(CFI, At4004) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ Label frame1_sp = expected.iregs[MD_CONTEXT_LOONGARCH64_REG_SP];
+ stack_section
+ // frame0
+ .Append(16, 0) // space
+ .D64(frame1_sp) // stack pointer
+ .D64(0x00405510) // return address
+ .Mark(&frame1_sp); // This effectively sets stack_section.start().
+ raw_context.csr_epc = 0x00404004;
+ CheckWalk();
+}
+
+// Check that we reject rules that would cause the stack pointer to
+// move in the wrong direction.
+TEST_F(CFI, RejectBackwards) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ raw_context.csr_epc = 0x40005000;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = 0x80000000;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = 0x00405510;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+}
+
+// Check that we reject rules whose expressions' evaluation fails.
+TEST_F(CFI, RejectBadExpressions) {
+ raw_context.context_flags =
+ raw_context.context_flags | MD_CONTEXT_LOONGARCH64_FULL;
+ raw_context.csr_epc = 0x00407000;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_SP] = 0x80000000;
+ raw_context.iregs[MD_CONTEXT_LOONGARCH64_REG_RA] = 0x00405510;
+
+ StackFrameSymbolizer frame_symbolizer(&supplier, &resolver);
+ StackwalkerLOONGARCH64 walker(&system_info, &raw_context, &stack_region, &modules,
+ &frame_symbolizer);
+ vector<const CodeModule*> modules_without_symbols;
+ vector<const CodeModule*> modules_with_corrupt_symbols;
+ ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols,
+ &modules_with_corrupt_symbols));
+ ASSERT_EQ(0U, modules_without_symbols.size());
+ ASSERT_EQ(0U, modules_with_corrupt_symbols.size());
+ frames = call_stack.frames();
+ ASSERT_EQ(1U, frames->size());
+}
diff --git a/src/processor/synth_minidump.h b/src/processor/synth_minidump.h
index 2da4d5fe..ac69f0fb 100644
--- a/src/processor/synth_minidump.h
+++ b/src/processor/synth_minidump.h
@@ -229,6 +229,7 @@ class Context: public Section {
Context(const Dump& dump, const MDRawContextX86& context);
Context(const Dump& dump, const MDRawContextARM& context);
Context(const Dump& dump, const MDRawContextMIPS& context);
+ Context(const Dump& dump, const MDRawContextLOONGARCH64& context);
// Add an empty context to the dump.
Context(const Dump& dump) : Section(dump) {}
// Add constructors for other architectures here. Remember to byteswap.
diff --git a/src/tools/linux/md2core/minidump-2-core.cc b/src/tools/linux/md2core/minidump-2-core.cc
index 7e351d16..77639539 100644
--- a/src/tools/linux/md2core/minidump-2-core.cc
+++ b/src/tools/linux/md2core/minidump-2-core.cc
@@ -77,6 +77,8 @@
#define ELF_ARCH EM_MIPS
#elif defined(__aarch64__)
#define ELF_ARCH EM_AARCH64
+#elif defined(__loongarch64)
+ #define ELF_ARCH EM_LOONGARCH
#endif
#if defined(__arm__)
@@ -259,7 +261,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(__loongarch64)
uint32_t pr_uid; /* User ID */
uint32_t pr_gid; /* Group ID */
#else
@@ -306,7 +308,7 @@ struct CrashedProcess {
struct Thread {
pid_t tid;
-#if defined(__mips__)
+#if defined(__mips__) || defined(__loongarch64)
mcontext_t mcontext;
#else
user_regs_struct regs;
@@ -533,6 +535,17 @@ ParseThreadRegisters(CrashedProcess::Thread* thread,
thread->mcontext.fpc_eir = rawregs->float_save.fir;
#endif
}
+#elif defined(__loongarch64)
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread,
+ const MinidumpMemoryRange& range) {
+ const MDRawContextLOONGARCH64* rawregs = range.GetData<MDRawContextLOONGARCH64>(0);
+
+ for (int i = 0; i < MD_CONTEXT_LOONGARCH64_GPR_COUNT; ++i)
+ thread->mcontext.__gregs[i] = rawregs->iregs[i];
+
+ thread->mcontext.__pc = rawregs->csr_epc;
+}
#else
#error "This code has not been ported to your platform yet"
#endif
@@ -622,6 +635,12 @@ ParseSystemInfo(const Options& options, CrashedProcess* crashinfo,
# else
# error "This mips ABI is currently not supported (n32)"
# endif
+#elif defined(__loongarch64)
+ if (sysinfo->processor_architecture != MD_CPU_ARCHITECTURE_LOONGARCH64) {
+ fprintf(stderr,
+ "This version of minidump-2-core only supports Loongarch64.\n");
+ exit(1);
+ }
#else
#error "This code has not been ported to your platform yet"
#endif
@@ -926,6 +945,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(__loongarch64)
+ memcpy(&pr.pr_reg, thread.mcontext.__gregs, sizeof(user_regs_struct));
#else
memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct));
#endif
--
2.20.1