mirror of
https://github.com/yuzu-emu/breakpad
synced 2024-11-25 16:25:30 +00:00
Add DisassemblerObjdump.
This extracts the existing objdump-based disassembler engine used in ExploitabilityLinux into a seperate reusable class, and adds support for most common address operand formats. This is a precursor to using DisassemblerObjdump to handle address resolution for non-canonical address dereferences on amd64. Bug: 901847 Change-Id: I1a06a86fc2e7c76b4d0e79eca5f8a6c501379f47 Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3720740 Reviewed-by: Ivan Penkov <ivanpe@google.com> Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
This commit is contained in:
parent
bcffe4fe60
commit
6289830b67
13 changed files with 4102 additions and 2282 deletions
20
Makefile.am
20
Makefile.am
|
@ -237,6 +237,8 @@ src_libbreakpad_a_SOURCES = \
|
|||
src/processor/contained_range_map.h \
|
||||
src/processor/convert_old_arm64_context.cc \
|
||||
src/processor/convert_old_arm64_context.h \
|
||||
src/processor/disassembler_objdump.h \
|
||||
src/processor/disassembler_objdump.cc \
|
||||
src/processor/disassembler_x86.h \
|
||||
src/processor/disassembler_x86.cc \
|
||||
src/processor/dump_context.cc \
|
||||
|
@ -390,6 +392,7 @@ check_PROGRAMS += \
|
|||
src/processor/basic_source_line_resolver_unittest \
|
||||
src/processor/cfi_frame_info_unittest \
|
||||
src/processor/contained_range_map_unittest \
|
||||
src/processor/disassembler_objdump_unittest \
|
||||
src/processor/disassembler_x86_unittest \
|
||||
src/processor/exploitability_unittest \
|
||||
src/processor/fast_source_line_resolver_unittest \
|
||||
|
@ -873,6 +876,7 @@ src_processor_exploitability_unittest_LDADD = \
|
|||
src/processor/convert_old_arm64_context.o \
|
||||
src/processor/minidump_processor.o \
|
||||
src/processor/process_state.o \
|
||||
src/processor/disassembler_objdump.o \
|
||||
src/processor/disassembler_x86.o \
|
||||
src/processor/exploitability.o \
|
||||
src/processor/exploitability_linux.o \
|
||||
|
@ -909,6 +913,19 @@ src_processor_exploitability_unittest_LDADD = \
|
|||
$(TEST_LIBS) \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_processor_disassembler_objdump_unittest_SOURCES = \
|
||||
src/processor/disassembler_objdump_unittest.cc
|
||||
src_processor_disassembler_objdump_unittest_CPPFLAGS = \
|
||||
$(AM_CPPFLAGS) $(TEST_CFLAGS)
|
||||
src_processor_disassembler_objdump_unittest_LDADD = \
|
||||
src/processor/disassembler_objdump.o \
|
||||
src/processor/dump_context.o \
|
||||
src/processor/dump_object.o \
|
||||
src/processor/logging.o \
|
||||
src/processor/pathname_stripper.o \
|
||||
$(TEST_LIBS) \
|
||||
$(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
src_processor_disassembler_x86_unittest_SOURCES = \
|
||||
src/processor/disassembler_x86_unittest.cc
|
||||
src_processor_disassembler_x86_unittest_CPPFLAGS = \
|
||||
|
@ -992,6 +1009,7 @@ src_processor_minidump_processor_unittest_LDADD = \
|
|||
src/processor/call_stack.o \
|
||||
src/processor/cfi_frame_info.o \
|
||||
src/processor/convert_old_arm64_context.o \
|
||||
src/processor/disassembler_objdump.o \
|
||||
src/processor/disassembler_x86.o \
|
||||
src/processor/dump_context.o \
|
||||
src/processor/dump_object.o \
|
||||
|
@ -1141,6 +1159,7 @@ src_processor_stackwalker_selftest_LDADD = \
|
|||
src/processor/basic_code_modules.o \
|
||||
src/processor/basic_source_line_resolver.o \
|
||||
src/processor/call_stack.o \
|
||||
src/processor/disassembler_objdump.o \
|
||||
src/processor/disassembler_x86.o \
|
||||
src/processor/exploitability.o \
|
||||
src/processor/exploitability_linux.o \
|
||||
|
@ -1366,6 +1385,7 @@ src_processor_minidump_stackwalk_LDADD = \
|
|||
src/processor/call_stack.o \
|
||||
src/processor/cfi_frame_info.o \
|
||||
src/processor/convert_old_arm64_context.o \
|
||||
src/processor/disassembler_objdump.o \
|
||||
src/processor/disassembler_x86.o \
|
||||
src/processor/dump_context.o \
|
||||
src/processor/dump_object.o \
|
||||
|
|
85
Makefile.in
85
Makefile.in
|
@ -176,6 +176,7 @@ EXTRA_PROGRAMS = $(am__EXEEXT_1)
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver_unittest \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info_unittest \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map_unittest \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump_unittest \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86_unittest \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_unittest \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver_unittest \
|
||||
|
@ -278,6 +279,7 @@ am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" \
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver_unittest$(EXEEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info_unittest$(EXEEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map_unittest$(EXEEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump_unittest$(EXEEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86_unittest$(EXEEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_unittest$(EXEEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver_unittest$(EXEEXT) \
|
||||
|
@ -449,6 +451,8 @@ am__src_libbreakpad_a_SOURCES_DIST = \
|
|||
src/processor/contained_range_map.h \
|
||||
src/processor/convert_old_arm64_context.cc \
|
||||
src/processor/convert_old_arm64_context.h \
|
||||
src/processor/disassembler_objdump.h \
|
||||
src/processor/disassembler_objdump.cc \
|
||||
src/processor/disassembler_x86.h \
|
||||
src/processor/disassembler_x86.cc \
|
||||
src/processor/dump_context.cc src/processor/dump_object.cc \
|
||||
|
@ -525,6 +529,7 @@ am__src_libbreakpad_a_SOURCES_DIST = \
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.$(OBJEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.$(OBJEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.$(OBJEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.$(OBJEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.$(OBJEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.$(OBJEXT) \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.$(OBJEXT) \
|
||||
|
@ -959,6 +964,20 @@ src_processor_contained_range_map_unittest_OBJECTS = \
|
|||
@DISABLE_PROCESSOR_FALSE@src_processor_contained_range_map_unittest_DEPENDENCIES = \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o
|
||||
am__src_processor_disassembler_objdump_unittest_SOURCES_DIST = \
|
||||
src/processor/disassembler_objdump_unittest.cc
|
||||
@DISABLE_PROCESSOR_FALSE@am_src_processor_disassembler_objdump_unittest_OBJECTS = src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.$(OBJEXT)
|
||||
src_processor_disassembler_objdump_unittest_OBJECTS = \
|
||||
$(am_src_processor_disassembler_objdump_unittest_OBJECTS)
|
||||
@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_objdump_unittest_DEPENDENCIES = \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \
|
||||
@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \
|
||||
@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1)
|
||||
am__src_processor_disassembler_x86_unittest_SOURCES_DIST = \
|
||||
src/processor/disassembler_x86_unittest.cc
|
||||
@DISABLE_PROCESSOR_FALSE@am_src_processor_disassembler_x86_unittest_OBJECTS = src/processor/disassembler_x86_unittest-disassembler_x86_unittest.$(OBJEXT)
|
||||
|
@ -978,6 +997,7 @@ src_processor_exploitability_unittest_OBJECTS = \
|
|||
@DISABLE_PROCESSOR_FALSE@src_processor_exploitability_unittest_DEPENDENCIES = src/processor/convert_old_arm64_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \
|
||||
|
@ -1143,6 +1163,7 @@ src_processor_minidump_processor_unittest_OBJECTS = \
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \
|
||||
|
@ -1188,6 +1209,7 @@ src_processor_minidump_stackwalk_OBJECTS = \
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \
|
||||
|
@ -1411,6 +1433,7 @@ src_processor_stackwalker_selftest_OBJECTS = \
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \
|
||||
|
@ -1885,6 +1908,8 @@ am__depfiles_remade = src/client/$(DEPDIR)/minidump_file_writer.Po \
|
|||
src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po \
|
||||
src/processor/$(DEPDIR)/contained_range_map_unittest.Po \
|
||||
src/processor/$(DEPDIR)/convert_old_arm64_context.Po \
|
||||
src/processor/$(DEPDIR)/disassembler_objdump.Po \
|
||||
src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Po \
|
||||
src/processor/$(DEPDIR)/disassembler_x86.Po \
|
||||
src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po \
|
||||
src/processor/$(DEPDIR)/dump_context.Po \
|
||||
|
@ -2038,6 +2063,7 @@ SOURCES = $(src_client_linux_libbreakpad_client_a_SOURCES) \
|
|||
$(src_processor_basic_source_line_resolver_unittest_SOURCES) \
|
||||
$(src_processor_cfi_frame_info_unittest_SOURCES) \
|
||||
$(src_processor_contained_range_map_unittest_SOURCES) \
|
||||
$(src_processor_disassembler_objdump_unittest_SOURCES) \
|
||||
$(src_processor_disassembler_x86_unittest_SOURCES) \
|
||||
$(src_processor_exploitability_unittest_SOURCES) \
|
||||
$(src_processor_fast_source_line_resolver_unittest_SOURCES) \
|
||||
|
@ -2097,6 +2123,7 @@ DIST_SOURCES = \
|
|||
$(am__src_processor_basic_source_line_resolver_unittest_SOURCES_DIST) \
|
||||
$(am__src_processor_cfi_frame_info_unittest_SOURCES_DIST) \
|
||||
$(am__src_processor_contained_range_map_unittest_SOURCES_DIST) \
|
||||
$(am__src_processor_disassembler_objdump_unittest_SOURCES_DIST) \
|
||||
$(am__src_processor_disassembler_x86_unittest_SOURCES_DIST) \
|
||||
$(am__src_processor_exploitability_unittest_SOURCES_DIST) \
|
||||
$(am__src_processor_fast_source_line_resolver_unittest_SOURCES_DIST) \
|
||||
|
@ -2649,6 +2676,8 @@ CLEANFILES = $(am__append_12)
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map.h \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.cc \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.h \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.h \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.cc \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.h \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.cc \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.cc \
|
||||
|
@ -3172,6 +3201,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \
|
||||
|
@ -3208,6 +3238,21 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
|||
@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \
|
||||
@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_objdump_unittest_SOURCES = \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump_unittest.cc
|
||||
|
||||
@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_objdump_unittest_CPPFLAGS = \
|
||||
@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS)
|
||||
|
||||
@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_objdump_unittest_LDADD = \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \
|
||||
@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
|
||||
|
||||
@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_x86_unittest_SOURCES = \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86_unittest.cc
|
||||
|
||||
|
@ -3301,6 +3346,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \
|
||||
|
@ -3470,6 +3516,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \
|
||||
|
@ -3721,6 +3768,7 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS)
|
|||
@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_objdump.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \
|
||||
@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \
|
||||
|
@ -4356,6 +4404,9 @@ src/processor/cfi_frame_info.$(OBJEXT): src/processor/$(am__dirstamp) \
|
|||
src/processor/convert_old_arm64_context.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/disassembler_objdump.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
src/processor/disassembler_x86.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
|
@ -4970,6 +5021,13 @@ src/processor/contained_range_map_unittest.$(OBJEXT): \
|
|||
src/processor/contained_range_map_unittest$(EXEEXT): $(src_processor_contained_range_map_unittest_OBJECTS) $(src_processor_contained_range_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_contained_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/contained_range_map_unittest$(EXEEXT)
|
||||
$(AM_V_CXXLD)$(CXXLINK) $(src_processor_contained_range_map_unittest_OBJECTS) $(src_processor_contained_range_map_unittest_LDADD) $(LIBS)
|
||||
src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
|
||||
src/processor/disassembler_objdump_unittest$(EXEEXT): $(src_processor_disassembler_objdump_unittest_OBJECTS) $(src_processor_disassembler_objdump_unittest_DEPENDENCIES) $(EXTRA_src_processor_disassembler_objdump_unittest_DEPENDENCIES) src/processor/$(am__dirstamp)
|
||||
@rm -f src/processor/disassembler_objdump_unittest$(EXEEXT)
|
||||
$(AM_V_CXXLD)$(CXXLINK) $(src_processor_disassembler_objdump_unittest_OBJECTS) $(src_processor_disassembler_objdump_unittest_LDADD) $(LIBS)
|
||||
src/processor/disassembler_x86_unittest-disassembler_x86_unittest.$(OBJEXT): \
|
||||
src/processor/$(am__dirstamp) \
|
||||
src/processor/$(DEPDIR)/$(am__dirstamp)
|
||||
|
@ -5704,6 +5762,8 @@ distclean-compile:
|
|||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/contained_range_map_unittest.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/convert_old_arm64_context.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/disassembler_objdump.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/disassembler_x86.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po@am__quote@ # am--include-marker
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/dump_context.Po@am__quote@ # am--include-marker
|
||||
|
@ -7482,6 +7542,20 @@ src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.obj: src/processor
|
|||
@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_cfi_frame_info_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.obj `if test -f 'src/processor/cfi_frame_info_unittest.cc'; then $(CYGPATH_W) 'src/processor/cfi_frame_info_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/cfi_frame_info_unittest.cc'; fi`
|
||||
|
||||
src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.o: src/processor/disassembler_objdump_unittest.cc
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_disassembler_objdump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Tpo -c -o src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.o `test -f 'src/processor/disassembler_objdump_unittest.cc' || echo '$(srcdir)/'`src/processor/disassembler_objdump_unittest.cc
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Tpo src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/disassembler_objdump_unittest.cc' object='src/processor/disassembler_objdump_unittest-disassembler_objdump_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_disassembler_objdump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.o `test -f 'src/processor/disassembler_objdump_unittest.cc' || echo '$(srcdir)/'`src/processor/disassembler_objdump_unittest.cc
|
||||
|
||||
src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.obj: src/processor/disassembler_objdump_unittest.cc
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_disassembler_objdump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Tpo -c -o src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.obj `if test -f 'src/processor/disassembler_objdump_unittest.cc'; then $(CYGPATH_W) 'src/processor/disassembler_objdump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/disassembler_objdump_unittest.cc'; fi`
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Tpo src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/disassembler_objdump_unittest.cc' object='src/processor/disassembler_objdump_unittest-disassembler_objdump_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_disassembler_objdump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/disassembler_objdump_unittest-disassembler_objdump_unittest.obj `if test -f 'src/processor/disassembler_objdump_unittest.cc'; then $(CYGPATH_W) 'src/processor/disassembler_objdump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/disassembler_objdump_unittest.cc'; fi`
|
||||
|
||||
src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o: src/processor/disassembler_x86_unittest.cc
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_disassembler_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Tpo -c -o src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o `test -f 'src/processor/disassembler_x86_unittest.cc' || echo '$(srcdir)/'`src/processor/disassembler_x86_unittest.cc
|
||||
@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Tpo src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po
|
||||
|
@ -9131,6 +9205,13 @@ src/processor/contained_range_map_unittest.log: src/processor/contained_range_ma
|
|||
--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/disassembler_objdump_unittest.log: src/processor/disassembler_objdump_unittest$(EXEEXT)
|
||||
@p='src/processor/disassembler_objdump_unittest$(EXEEXT)'; \
|
||||
b='src/processor/disassembler_objdump_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/disassembler_x86_unittest.log: src/processor/disassembler_x86_unittest$(EXEEXT)
|
||||
@p='src/processor/disassembler_x86_unittest$(EXEEXT)'; \
|
||||
b='src/processor/disassembler_x86_unittest'; \
|
||||
|
@ -9887,6 +9968,8 @@ distclean: distclean-am
|
|||
-rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po
|
||||
-rm -f src/processor/$(DEPDIR)/contained_range_map_unittest.Po
|
||||
-rm -f src/processor/$(DEPDIR)/convert_old_arm64_context.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_objdump.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_x86.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po
|
||||
-rm -f src/processor/$(DEPDIR)/dump_context.Po
|
||||
|
@ -10239,6 +10322,8 @@ maintainer-clean: maintainer-clean-am
|
|||
-rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po
|
||||
-rm -f src/processor/$(DEPDIR)/contained_range_map_unittest.Po
|
||||
-rm -f src/processor/$(DEPDIR)/convert_old_arm64_context.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_objdump.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_objdump_unittest-disassembler_objdump_unittest.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_x86.Po
|
||||
-rm -f src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po
|
||||
-rm -f src/processor/$(DEPDIR)/dump_context.Po
|
||||
|
|
4
aclocal.m4
vendored
4
aclocal.m4
vendored
|
@ -14,8 +14,8 @@
|
|||
m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
|
||||
m4_ifndef([AC_AUTOCONF_VERSION],
|
||||
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
|
||||
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
|
||||
[m4_warning([this file was generated for autoconf 2.69.
|
||||
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],,
|
||||
[m4_warning([this file was generated for autoconf 2.71.
|
||||
You have another version of autoconf. It may work, but is not guaranteed to.
|
||||
If you have problems, you may need to regenerate the build system entirely.
|
||||
To do so, use the procedure documented by the package, typically 'autoreconf'.])])
|
||||
|
|
14
configure.ac
14
configure.ac
|
@ -27,9 +27,9 @@
|
|||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
AC_PREREQ(2.69)
|
||||
AC_PREREQ([2.71])
|
||||
|
||||
AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com)
|
||||
AC_INIT([breakpad],[0.1],[google-breakpad-dev@googlegroups.com])
|
||||
dnl Sanity check: the argument is just a file that should exist.
|
||||
AC_CONFIG_SRCDIR(README.md)
|
||||
AC_CONFIG_AUX_DIR(autotools)
|
||||
|
@ -59,7 +59,15 @@ if test "x$enable_m32" = xyes; then
|
|||
CXXFLAGS="${CXXFLAGS} -m32"
|
||||
fi
|
||||
|
||||
AC_HEADER_STDC
|
||||
m4_warn([obsolete],
|
||||
[The preprocessor macro `STDC_HEADERS' is obsolete.
|
||||
Except in unusual embedded environments, you can safely include all
|
||||
ISO C90 headers unconditionally.])dnl
|
||||
# Autoupdate added the next two lines to ensure that your configure
|
||||
# script's behavior did not change. They are probably safe to remove.
|
||||
AC_CHECK_INCLUDES_DEFAULT
|
||||
AC_PROG_EGREP
|
||||
|
||||
AC_SYS_LARGEFILE
|
||||
AX_PTHREAD
|
||||
AC_CHECK_HEADERS([a.out.h sys/mman.h sys/random.h])
|
||||
|
|
|
@ -18,18 +18,24 @@
|
|||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 if you have the `rustc_demangle' library (-lrustc_demangle). */
|
||||
#undef HAVE_LIBRUSTC_DEMANGLE
|
||||
|
||||
/* Define to 1 if you have the `memfd_create' function. */
|
||||
#undef HAVE_MEMFD_CREATE
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#undef HAVE_PTHREAD
|
||||
|
||||
/* Define to 1 if you have the <rustc_demangle.h> header file. */
|
||||
#undef HAVE_RUSTC_DEMANGLE_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#undef HAVE_STDIO_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
|
@ -82,17 +88,14 @@
|
|||
your system. */
|
||||
#undef PTHREAD_CREATE_JOINABLE
|
||||
|
||||
/* Define to 1 if you have the ANSI C header files. */
|
||||
/* Define to 1 if all of the C90 standard headers exist (not just the ones
|
||||
required in a freestanding environment). This macro is provided for
|
||||
backward compatibility; new code need not use it. */
|
||||
#undef STDC_HEADERS
|
||||
|
||||
/* Version number of package */
|
||||
#undef VERSION
|
||||
|
||||
/* Enable large inode numbers on Mac OS X 10.5. */
|
||||
#ifndef _DARWIN_USE_64_BIT_INODE
|
||||
# define _DARWIN_USE_64_BIT_INODE 1
|
||||
#endif
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
|
|
520
src/processor/disassembler_objdump.cc
Normal file
520
src/processor/disassembler_objdump.cc
Normal file
|
@ -0,0 +1,520 @@
|
|||
// Copyright (c) 2022, Google LLC
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google LLC nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// disassembler_objdump.: Disassembler that invokes objdump for disassembly.
|
||||
//
|
||||
// Author: Mark Brand
|
||||
|
||||
#include "processor/disassembler_objdump.h"
|
||||
|
||||
#ifdef __linux__
|
||||
#include <unistd.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <regex>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
namespace {
|
||||
const size_t kMaxX86InstructionLength = 15;
|
||||
|
||||
// Small RAII wrapper for temporary files.
|
||||
//
|
||||
// Example:
|
||||
// ScopedTmpFile tmp("/tmp/tmpfile-XXXX");
|
||||
// if (tmp.Create()) {
|
||||
// std::cerr << tmp.path() << std::endl;
|
||||
// }
|
||||
class ScopedTmpFile {
|
||||
public:
|
||||
// Initialize the ScopedTmpFile object - this does not create the temporary
|
||||
// file yet.
|
||||
ScopedTmpFile(const char* path_format);
|
||||
~ScopedTmpFile();
|
||||
|
||||
// Creates the temporary file, returns true on success.
|
||||
bool Create();
|
||||
|
||||
// Writes bytes to the temporary file, returns true on success.
|
||||
bool Write(const uint8_t* bytes, unsigned int bytes_len);
|
||||
|
||||
// Returns the path of the temporary file.
|
||||
string path() const { return path_; }
|
||||
|
||||
private:
|
||||
int fd_;
|
||||
string path_;
|
||||
};
|
||||
|
||||
ScopedTmpFile::ScopedTmpFile(const char* path_format) : path_(path_format) {}
|
||||
|
||||
ScopedTmpFile::~ScopedTmpFile() {
|
||||
if (fd_) {
|
||||
close(fd_);
|
||||
unlink(path_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool ScopedTmpFile::Create() {
|
||||
fd_ = mkstemp(path_.data());
|
||||
if (fd_ < 0) {
|
||||
unlink(path_.c_str());
|
||||
fd_ = 0;
|
||||
path_ = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScopedTmpFile::Write(const uint8_t* bytes, unsigned int bytes_len) {
|
||||
if (fd_) {
|
||||
do {
|
||||
ssize_t result = write(fd_, bytes, bytes_len);
|
||||
if (result < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
bytes += result;
|
||||
bytes_len -= result;
|
||||
} while (bytes_len);
|
||||
}
|
||||
|
||||
return bytes_len == 0;
|
||||
}
|
||||
|
||||
bool IsInstructionPrefix(const string& token) {
|
||||
if (token == "lock" || token == "rep" || token == "repz" ||
|
||||
token == "repnz") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsOperandSize(const string& token) {
|
||||
if (token == "BYTE" || token == "WORD" || token == "DWORD" ||
|
||||
token == "QWORD" || token == "PTR") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GetSegmentAddressX86(const DumpContext& context, string segment_name,
|
||||
uint64_t& address) {
|
||||
if (segment_name == "ds") {
|
||||
address = context.GetContextX86()->ds;
|
||||
} else if (segment_name == "es") {
|
||||
address = context.GetContextX86()->es;
|
||||
} else if (segment_name == "fs") {
|
||||
address = context.GetContextX86()->fs;
|
||||
} else if (segment_name == "gs") {
|
||||
address = context.GetContextX86()->gs;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported segment register: " << segment_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSegmentAddressAMD64(const DumpContext& context, string segment_name,
|
||||
uint64_t& address) {
|
||||
if (segment_name == "ds") {
|
||||
address = 0;
|
||||
} else if (segment_name == "es") {
|
||||
address = 0;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported segment register: " << segment_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetSegmentAddress(const DumpContext& context, string segment_name,
|
||||
uint64_t& address) {
|
||||
if (context.GetContextCPU() == MD_CONTEXT_X86) {
|
||||
return GetSegmentAddressX86(context, segment_name, address);
|
||||
} else if (context.GetContextCPU() == MD_CONTEXT_AMD64) {
|
||||
return GetSegmentAddressAMD64(context, segment_name, address);
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported architecture for GetSegmentAddress\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool GetRegisterValueX86(const DumpContext& context, string register_name,
|
||||
uint64_t& value) {
|
||||
if (register_name == "eax") {
|
||||
value = context.GetContextX86()->eax;
|
||||
} else if (register_name == "ebx") {
|
||||
value = context.GetContextX86()->ebx;
|
||||
} else if (register_name == "ecx") {
|
||||
value = context.GetContextX86()->ecx;
|
||||
} else if (register_name == "edx") {
|
||||
value = context.GetContextX86()->edx;
|
||||
} else if (register_name == "edi") {
|
||||
value = context.GetContextX86()->edi;
|
||||
} else if (register_name == "esi") {
|
||||
value = context.GetContextX86()->esi;
|
||||
} else if (register_name == "ebp") {
|
||||
value = context.GetContextX86()->ebp;
|
||||
} else if (register_name == "esp") {
|
||||
value = context.GetContextX86()->esp;
|
||||
} else if (register_name == "eip") {
|
||||
value = context.GetContextX86()->eip;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported register: " << register_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetRegisterValueAMD64(const DumpContext& context, string register_name,
|
||||
uint64_t& value) {
|
||||
if (register_name == "rax") {
|
||||
value = context.GetContextAMD64()->rax;
|
||||
} else if (register_name == "rbx") {
|
||||
value = context.GetContextAMD64()->rbx;
|
||||
} else if (register_name == "rcx") {
|
||||
value = context.GetContextAMD64()->rcx;
|
||||
} else if (register_name == "rdx") {
|
||||
value = context.GetContextAMD64()->rdx;
|
||||
} else if (register_name == "rdi") {
|
||||
value = context.GetContextAMD64()->rdi;
|
||||
} else if (register_name == "rsi") {
|
||||
value = context.GetContextAMD64()->rsi;
|
||||
} else if (register_name == "rbp") {
|
||||
value = context.GetContextAMD64()->rbp;
|
||||
} else if (register_name == "rsp") {
|
||||
value = context.GetContextAMD64()->rsp;
|
||||
} else if (register_name == "r8") {
|
||||
value = context.GetContextAMD64()->r8;
|
||||
} else if (register_name == "r9") {
|
||||
value = context.GetContextAMD64()->r9;
|
||||
} else if (register_name == "r10") {
|
||||
value = context.GetContextAMD64()->r10;
|
||||
} else if (register_name == "r11") {
|
||||
value = context.GetContextAMD64()->r11;
|
||||
} else if (register_name == "r12") {
|
||||
value = context.GetContextAMD64()->r12;
|
||||
} else if (register_name == "r13") {
|
||||
value = context.GetContextAMD64()->r13;
|
||||
} else if (register_name == "r14") {
|
||||
value = context.GetContextAMD64()->r14;
|
||||
} else if (register_name == "r15") {
|
||||
value = context.GetContextAMD64()->r15;
|
||||
} else if (register_name == "rip") {
|
||||
value = context.GetContextAMD64()->rip;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported register: " << register_name;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Lookup the value of `register_name` in `context`, store it into `value` on
|
||||
// success.
|
||||
// Support for non-full-size registers not implemented, since we're only using
|
||||
// this to evaluate address expressions.
|
||||
bool GetRegisterValue(const DumpContext& context, string register_name,
|
||||
uint64_t& value) {
|
||||
if (context.GetContextCPU() == MD_CONTEXT_X86) {
|
||||
return GetRegisterValueX86(context, register_name, value);
|
||||
} else if (context.GetContextCPU() == MD_CONTEXT_AMD64) {
|
||||
return GetRegisterValueAMD64(context, register_name, value);
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported architecture for GetRegisterValue\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool DisassemblerObjdump::DisassembleInstruction(uint32_t cpu,
|
||||
const uint8_t* raw_bytes,
|
||||
unsigned int raw_bytes_len,
|
||||
string& instruction) {
|
||||
// Always initialize outputs
|
||||
instruction = "";
|
||||
|
||||
if (!raw_bytes || raw_bytes_len == 0) {
|
||||
// There's no need to perform any operation in this case, as there's
|
||||
// clearly no instruction there.
|
||||
return false;
|
||||
}
|
||||
|
||||
string architecture;
|
||||
if (cpu == MD_CONTEXT_X86) {
|
||||
architecture = "i386";
|
||||
} else if (cpu == MD_CONTEXT_AMD64) {
|
||||
architecture = "i386:x86-64";
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported architecture.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create two temporary files, one for the raw instruction bytes to pass to
|
||||
// objdump, and one for the output, and write the bytes to the input file.
|
||||
ScopedTmpFile raw_bytes_file("/tmp/breakpad_mem_region-raw_bytes-XXXXXX");
|
||||
ScopedTmpFile disassembly_file("/tmp/breakpad_mem_region-disassembly-XXXXXX");
|
||||
if (!raw_bytes_file.Create() || !disassembly_file.Create() ||
|
||||
!raw_bytes_file.Write(raw_bytes, raw_bytes_len)) {
|
||||
BPLOG(ERROR) << "Failed creating temporary files.";
|
||||
return false;
|
||||
}
|
||||
|
||||
char cmd[1024] = {0};
|
||||
snprintf(cmd, 1024,
|
||||
"objdump -D --no-show-raw-insn -b binary -M intel -m %s %s > %s",
|
||||
architecture.c_str(), raw_bytes_file.path().c_str(),
|
||||
disassembly_file.path().c_str());
|
||||
if (system(cmd)) {
|
||||
BPLOG(ERROR) << "Failed to call objdump.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pipe each output line into the string until the string contains the first
|
||||
// instruction from objdump.
|
||||
std::ifstream objdump_stream(disassembly_file.path());
|
||||
|
||||
// Match the instruction line, from:
|
||||
// 0: lock cmpxchg DWORD PTR [esi+0x10],eax
|
||||
// extract the string "lock cmpxchg DWORD PTR [esi+0x10],eax"
|
||||
std::regex instruction_regex(
|
||||
"^\\s+[0-9a-f]+:\\s+" // " 0:"
|
||||
"((?:\\s*\\S*)+)$"); // "lock cmpxchg..."
|
||||
|
||||
std::string line;
|
||||
std::smatch match;
|
||||
do {
|
||||
if (!getline(objdump_stream, line)) {
|
||||
BPLOG(INFO) << "Failed to find instruction in objdump output.";
|
||||
return false;
|
||||
}
|
||||
} while (!std::regex_match(line, match, instruction_regex));
|
||||
|
||||
instruction = match[1].str();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool DisassemblerObjdump::TokenizeInstruction(const string& instruction,
|
||||
string& operation, string& dest,
|
||||
string& src) {
|
||||
// Always initialize outputs.
|
||||
operation = "";
|
||||
dest = "";
|
||||
src = "";
|
||||
|
||||
// Split the instruction into tokens by either whitespace or comma.
|
||||
std::regex token_regex("((?:[^\\s,]+)|,)(?:\\s)*");
|
||||
std::sregex_iterator tokens_begin(instruction.begin(), instruction.end(),
|
||||
token_regex);
|
||||
|
||||
bool found_comma = false;
|
||||
for (auto tokens_iter = tokens_begin; tokens_iter != std::sregex_iterator();
|
||||
++tokens_iter) {
|
||||
auto token = (*tokens_iter)[1].str();
|
||||
if (operation.size() == 0) {
|
||||
if (IsInstructionPrefix(token))
|
||||
continue;
|
||||
operation = token;
|
||||
} else if (dest.size() == 0) {
|
||||
if (IsOperandSize(token))
|
||||
continue;
|
||||
dest = token;
|
||||
} else if (!found_comma) {
|
||||
if (token == ",") {
|
||||
found_comma = true;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Failed to parse operands from objdump output, expected"
|
||||
" comma but found \""
|
||||
<< token << "\"";
|
||||
return false;
|
||||
}
|
||||
} else if (src.size() == 0) {
|
||||
if (IsOperandSize(token))
|
||||
continue;
|
||||
src = token;
|
||||
} else {
|
||||
if (token == ",") {
|
||||
BPLOG(ERROR) << "Failed to parse operands from objdump output, found "
|
||||
"unexpected comma after last operand.";
|
||||
return false;
|
||||
} else {
|
||||
// We just ignore other junk after the last operand unless it's a
|
||||
// comma, which would indicate we're probably still in the middle
|
||||
// of the operands and something has gone wrong
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_comma && src.size() == 0) {
|
||||
BPLOG(ERROR) << "Failed to parse operands from objdump output, found comma "
|
||||
"but no src operand.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool DisassemblerObjdump::CalculateAddress(const DumpContext& context,
|
||||
const string& expression,
|
||||
uint64_t& address) {
|
||||
address = 0;
|
||||
|
||||
// Extract the components of the expression.
|
||||
// fs:[esi+edi*4+0x80] -> ["fs", "esi", "edi", "4", "-", "0x80"]
|
||||
std::regex expression_regex(
|
||||
"^(?:(\\ws):)?" // "fs:"
|
||||
"\\[(\\w+)" // "[esi"
|
||||
"(?:\\+(\\w+)(?:\\*(\\d+)))?" // "+edi*4"
|
||||
"(?:([\\+-])(0x[0-9a-f]+))?" // "-0x80"
|
||||
"\\]$"); // "]"
|
||||
|
||||
std::smatch match;
|
||||
if (!std::regex_match(expression, match, expression_regex) ||
|
||||
match.size() != 7) {
|
||||
return false;
|
||||
}
|
||||
|
||||
string segment_name = match[1].str();
|
||||
string register_name = match[2].str();
|
||||
string index_name = match[3].str();
|
||||
string index_stride = match[4].str();
|
||||
string offset_sign = match[5].str();
|
||||
string offset = match[6].str();
|
||||
|
||||
uint64_t segment_address = 0;
|
||||
uint64_t register_value = 0;
|
||||
uint64_t index_value = 0;
|
||||
uint64_t index_stride_value = 1;
|
||||
uint64_t offset_value = 0;
|
||||
|
||||
if (segment_name.size() &&
|
||||
!GetSegmentAddress(context, segment_name, segment_address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!GetRegisterValue(context, register_name, register_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index_name.size() &&
|
||||
!GetRegisterValue(context, index_name, index_value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (index_stride.size()) {
|
||||
index_stride_value = strtoull(index_stride.c_str(), nullptr, 0);
|
||||
}
|
||||
|
||||
if (offset.size()) {
|
||||
offset_value = strtoull(offset.c_str(), nullptr, 0);
|
||||
}
|
||||
|
||||
address =
|
||||
segment_address + register_value + (index_value * index_stride_value);
|
||||
if (offset_sign == "+") {
|
||||
address += offset_value;
|
||||
} else if (offset_sign == "-") {
|
||||
address -= offset_value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DisassemblerObjdump::DisassemblerObjdump(const uint32_t cpu,
|
||||
const MemoryRegion* memory_region,
|
||||
uint64_t address) {
|
||||
if (address < memory_region->GetBase() ||
|
||||
memory_region->GetBase() + memory_region->GetSize() <= address) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t ip_bytes[kMaxX86InstructionLength] = {0};
|
||||
size_t ip_bytes_length;
|
||||
for (ip_bytes_length = 0; ip_bytes_length < kMaxX86InstructionLength;
|
||||
++ip_bytes_length) {
|
||||
// We have to read byte-by-byte here, since we still want to try and
|
||||
// disassemble an instruction even if we don't have enough bytes.
|
||||
if (!memory_region->GetMemoryAtAddress(address + ip_bytes_length,
|
||||
&ip_bytes[ip_bytes_length])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
string instruction;
|
||||
if (!DisassembleInstruction(cpu, ip_bytes, kMaxX86InstructionLength,
|
||||
instruction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TokenizeInstruction(instruction, operation_, dest_, src_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool DisassemblerObjdump::CalculateSrcAddress(const DumpContext& context,
|
||||
uint64_t& address) {
|
||||
return CalculateAddress(context, src_, address);
|
||||
}
|
||||
|
||||
bool DisassemblerObjdump::CalculateDestAddress(const DumpContext& context,
|
||||
uint64_t& address) {
|
||||
return CalculateAddress(context, dest_, address);
|
||||
}
|
||||
} // namespace google_breakpad
|
||||
|
||||
#else // __linux__
|
||||
namespace google_breakpad {
|
||||
DisassemblerObjdump::DisassemblerObjdump(const uint32_t cpu,
|
||||
const MemoryRegion* memory_region,
|
||||
uint64_t address) {}
|
||||
|
||||
bool DisassemblerObjdump::CalculateSrcAddress(const DumpContext& context,
|
||||
uint64_t* address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DisassemblerObjdump::CalculateDestAddress(const DumpContext& context,
|
||||
uint64_t* address) {
|
||||
return false;
|
||||
}
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // __linux__
|
142
src/processor/disassembler_objdump.h
Normal file
142
src/processor/disassembler_objdump.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
// Copyright (c) 2022, Google LLC
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google LLC nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// disassembler_objdump.h: Disassembler that invokes objdump for disassembly.
|
||||
//
|
||||
// Author: Mark Brand
|
||||
|
||||
#ifndef GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_OBJDUMP_H_
|
||||
#define GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_OBJDUMP_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/using_std_string.h"
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/processor/dump_context.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
|
||||
// Uses objdump to disassemble a single instruction.
|
||||
//
|
||||
// Currently supports disassembly for x86 and x86_64 on linux hosts only; on
|
||||
// unsupported platform or for unsupported architectures disassembly will fail.
|
||||
//
|
||||
// If disassembly is successful, then this allows extracting the instruction
|
||||
// opcode, source and destination operands, and computing the source and
|
||||
// destination addresses for instructions that operate on memory.
|
||||
//
|
||||
// Example:
|
||||
// DisassemblerObjdump disassembler(context->GetContextCPU(), memory_region,
|
||||
// instruction_ptr);
|
||||
// if (disassembler.IsValid()) {
|
||||
// uint64_t src_address = 0;
|
||||
// std::cerr << disassembler.operation() << " " << disassembler.src()
|
||||
// << ", " << disassembler.dest() << std::endl;
|
||||
// if (disassembler.CalculateSrcAddress(*context, src_address)) {
|
||||
// std::cerr << "[src_address = " << std::hex << src_address << "]\n";
|
||||
// }
|
||||
// }
|
||||
class DisassemblerObjdump {
|
||||
public:
|
||||
// Construct an ObjdumpDisassembler for the provided `cpu` type, where this is
|
||||
// one of MD_CONTEXT_X86 or MD_CONTEXT_AMD64. Provided that `address` is
|
||||
// within `memory_region`, and the memory referenced is a valid instruction,
|
||||
// this will then be initialized with the disassembly for that instruction.
|
||||
DisassemblerObjdump(uint32_t cpu,
|
||||
const MemoryRegion* memory_region,
|
||||
uint64_t address);
|
||||
~DisassemblerObjdump() = default;
|
||||
|
||||
// If the source operand of the instruction is a memory operand, compute the
|
||||
// address referred to by the operand, and store this in `address`. On success
|
||||
// returns true, otherwise (if computation fails, or if the source operand is
|
||||
// not a memory operand) returns false and sets `address` to 0.
|
||||
bool CalculateSrcAddress(const DumpContext& context, uint64_t& address);
|
||||
|
||||
// If the destination operand of the instruction is a memory operand, compute
|
||||
// the address referred to by the operand, and store this in `address`. On
|
||||
// success returns true, otherwise (if computation fails, or if the source
|
||||
// operand is not a memory operand) returns false and sets `address` to 0.
|
||||
bool CalculateDestAddress(const DumpContext& context, uint64_t& address);
|
||||
|
||||
// If the instruction was disassembled successfully, this will be true.
|
||||
bool IsValid() const { return operation_.size() != 0; }
|
||||
|
||||
// Returns the operation part of the disassembly, without any prefixes:
|
||||
// "pop" eax
|
||||
// lock "xchg" eax, edx
|
||||
const string& operation() const { return operation_; }
|
||||
|
||||
// Returns the destination operand of the disassembly, without memory operand
|
||||
// size prefixes:
|
||||
// mov DWORD PTR "[rax + 16]", edx
|
||||
const string& dest() const { return dest_; }
|
||||
|
||||
// Returns the source operand of the disassembly, without memory operand
|
||||
// size prefixes:
|
||||
// mov rax, QWORD PTR "[rdx]"
|
||||
const string& src() const { return src_; }
|
||||
|
||||
private:
|
||||
friend class DisassemblerObjdumpForTest;
|
||||
|
||||
// Writes out the provided `raw_bytes` to a temporary file, and executes objdump
|
||||
// to disassemble according to `cpu`, which must be either MD_CONTEXT_X86 or
|
||||
// MD_CONTEXT_AMD64. Once objdump has completed, parses out the instruction
|
||||
// string from the first instruction in the output and stores it in
|
||||
// `instruction`.
|
||||
static bool DisassembleInstruction(uint32_t cpu, const uint8_t* raw_bytes,
|
||||
unsigned int raw_bytes_len,
|
||||
string& instruction);
|
||||
|
||||
// Splits an `instruction` into three parts, the "main" `operation` and
|
||||
// the `dest` and `src` operands.
|
||||
// Example:
|
||||
// instruction = "lock cmpxchg QWORD PTR [rdi], rsi"
|
||||
// operation = "cmpxchg", dest = "[rdi]", src = "rsi"
|
||||
static bool TokenizeInstruction(const string& instruction, string& operation,
|
||||
string& dest, string& src);
|
||||
|
||||
// Compute the address referenced by `expression` in `context`.
|
||||
// Supports memory operands in the form
|
||||
// (segment:)[base_reg(+index_reg*index_stride)(+-offset)]
|
||||
// Returns false if evaluation fails, or if the operand is not a supported
|
||||
// memory operand.
|
||||
static bool CalculateAddress(const DumpContext& context,
|
||||
const string& expression,
|
||||
uint64_t& address);
|
||||
|
||||
// The parsed components of the disassembly for the instruction.
|
||||
string operation_ = "";
|
||||
string dest_ = "";
|
||||
string src_ = "";
|
||||
};
|
||||
} // namespace google_breakpad
|
||||
|
||||
#endif // GOOGLE_BREAKPAD_PROCESSOR_DISASSEMBLER_OBJDUMP_H_
|
464
src/processor/disassembler_objdump_unittest.cc
Normal file
464
src/processor/disassembler_objdump_unittest.cc
Normal file
|
@ -0,0 +1,464 @@
|
|||
// Copyright (c) 2022, Google LLC
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google LLC nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
#include "breakpad_googletest_includes.h"
|
||||
|
||||
#include "google_breakpad/common/breakpad_types.h"
|
||||
#include "google_breakpad/common/minidump_cpu_amd64.h"
|
||||
#include "google_breakpad/common/minidump_cpu_x86.h"
|
||||
#include "google_breakpad/processor/dump_context.h"
|
||||
#include "google_breakpad/processor/memory_region.h"
|
||||
#include "processor/disassembler_objdump.h"
|
||||
|
||||
namespace google_breakpad {
|
||||
class DisassemblerObjdumpForTest : public DisassemblerObjdump {
|
||||
public:
|
||||
using DisassemblerObjdump::CalculateAddress;
|
||||
using DisassemblerObjdump::DisassembleInstruction;
|
||||
using DisassemblerObjdump::TokenizeInstruction;
|
||||
};
|
||||
|
||||
class TestMemoryRegion : public MemoryRegion {
|
||||
public:
|
||||
TestMemoryRegion(uint64_t base, std::vector<uint8_t> bytes);
|
||||
~TestMemoryRegion() override = default;
|
||||
|
||||
uint64_t GetBase() const override;
|
||||
uint32_t GetSize() const override;
|
||||
|
||||
bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const override;
|
||||
bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const override;
|
||||
bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const override;
|
||||
bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const override;
|
||||
|
||||
void Print() const override;
|
||||
|
||||
private:
|
||||
uint64_t base_;
|
||||
std::vector<uint8_t> bytes_;
|
||||
};
|
||||
|
||||
TestMemoryRegion::TestMemoryRegion(uint64_t address, std::vector<uint8_t> bytes)
|
||||
: base_(address), bytes_(bytes) {}
|
||||
|
||||
uint64_t TestMemoryRegion::GetBase() const {
|
||||
return base_;
|
||||
}
|
||||
|
||||
uint32_t TestMemoryRegion::GetSize() const {
|
||||
return static_cast<uint32_t>(bytes_.size());
|
||||
}
|
||||
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint8_t* value) const {
|
||||
if (address < GetBase() ||
|
||||
address + sizeof(uint8_t) > GetBase() + GetSize()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(value, &bytes_[address - GetBase()], sizeof(uint8_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
// We don't use the following functions, so no need to implement.
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint16_t* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint32_t* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestMemoryRegion::GetMemoryAtAddress(uint64_t address,
|
||||
uint64_t* value) const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void TestMemoryRegion::Print() const {}
|
||||
|
||||
const uint32_t kX86TestDs = 0x01000000;
|
||||
const uint32_t kX86TestEs = 0x02000000;
|
||||
const uint32_t kX86TestFs = 0x03000000;
|
||||
const uint32_t kX86TestGs = 0x04000000;
|
||||
const uint32_t kX86TestEax = 0x00010101;
|
||||
const uint32_t kX86TestEbx = 0x00020202;
|
||||
const uint32_t kX86TestEcx = 0x00030303;
|
||||
const uint32_t kX86TestEdx = 0x00040404;
|
||||
const uint32_t kX86TestEsi = 0x00050505;
|
||||
const uint32_t kX86TestEdi = 0x00060606;
|
||||
const uint32_t kX86TestEsp = 0x00070707;
|
||||
const uint32_t kX86TestEbp = 0x00080808;
|
||||
const uint32_t kX86TestEip = 0x23230000;
|
||||
|
||||
const uint64_t kAMD64TestRax = 0x0000010101010101ul;
|
||||
const uint64_t kAMD64TestRbx = 0x0000020202020202ul;
|
||||
const uint64_t kAMD64TestRcx = 0x0000030303030303ul;
|
||||
const uint64_t kAMD64TestRdx = 0x0000040404040404ul;
|
||||
const uint64_t kAMD64TestRsi = 0x0000050505050505ul;
|
||||
const uint64_t kAMD64TestRdi = 0x0000060606060606ul;
|
||||
const uint64_t kAMD64TestRsp = 0x0000070707070707ul;
|
||||
const uint64_t kAMD64TestRbp = 0x0000080808080808ul;
|
||||
const uint64_t kAMD64TestR8 = 0x0000090909090909ul;
|
||||
const uint64_t kAMD64TestR9 = 0x00000a0a0a0a0a0aul;
|
||||
const uint64_t kAMD64TestR10 = 0x00000b0b0b0b0b0bul;
|
||||
const uint64_t kAMD64TestR11 = 0x00000c0c0c0c0c0cul;
|
||||
const uint64_t kAMD64TestR12 = 0x00000d0d0d0d0d0dul;
|
||||
const uint64_t kAMD64TestR13 = 0x00000e0e0e0e0e0eul;
|
||||
const uint64_t kAMD64TestR14 = 0x00000f0f0f0f0f0ful;
|
||||
const uint64_t kAMD64TestR15 = 0x0000001010101010ul;
|
||||
const uint64_t kAMD64TestRip = 0x0000000023230000ul;
|
||||
|
||||
class TestDumpContext : public DumpContext {
|
||||
public:
|
||||
TestDumpContext(bool x86_64 = false);
|
||||
~TestDumpContext() override;
|
||||
};
|
||||
|
||||
TestDumpContext::TestDumpContext(bool x86_64) {
|
||||
if (!x86_64) {
|
||||
MDRawContextX86* raw_context = new MDRawContextX86();
|
||||
memset(raw_context, 0, sizeof(raw_context));
|
||||
|
||||
raw_context->context_flags = MD_CONTEXT_X86_FULL;
|
||||
|
||||
raw_context->ds = kX86TestDs;
|
||||
raw_context->es = kX86TestEs;
|
||||
raw_context->fs = kX86TestFs;
|
||||
raw_context->gs = kX86TestGs;
|
||||
raw_context->eax = kX86TestEax;
|
||||
raw_context->ebx = kX86TestEbx;
|
||||
raw_context->ecx = kX86TestEcx;
|
||||
raw_context->edx = kX86TestEdx;
|
||||
raw_context->esi = kX86TestEsi;
|
||||
raw_context->edi = kX86TestEdi;
|
||||
raw_context->esp = kX86TestEsp;
|
||||
raw_context->ebp = kX86TestEbp;
|
||||
raw_context->eip = kX86TestEip;
|
||||
|
||||
SetContextFlags(raw_context->context_flags);
|
||||
SetContextX86(raw_context);
|
||||
this->valid_ = true;
|
||||
} else {
|
||||
MDRawContextAMD64* raw_context = new MDRawContextAMD64();
|
||||
memset(raw_context, 0, sizeof(raw_context));
|
||||
|
||||
raw_context->context_flags = MD_CONTEXT_AMD64_FULL;
|
||||
|
||||
raw_context->rax = kAMD64TestRax;
|
||||
raw_context->rbx = kAMD64TestRbx;
|
||||
raw_context->rcx = kAMD64TestRcx;
|
||||
raw_context->rdx = kAMD64TestRdx;
|
||||
raw_context->rsi = kAMD64TestRsi;
|
||||
raw_context->rdi = kAMD64TestRdi;
|
||||
raw_context->rsp = kAMD64TestRsp;
|
||||
raw_context->rbp = kAMD64TestRbp;
|
||||
raw_context->r8 = kAMD64TestR8;
|
||||
raw_context->r9 = kAMD64TestR9;
|
||||
raw_context->r10 = kAMD64TestR10;
|
||||
raw_context->r11 = kAMD64TestR11;
|
||||
raw_context->r12 = kAMD64TestR12;
|
||||
raw_context->r13 = kAMD64TestR13;
|
||||
raw_context->r14 = kAMD64TestR14;
|
||||
raw_context->r15 = kAMD64TestR15;
|
||||
raw_context->rip = kAMD64TestRip;
|
||||
|
||||
SetContextFlags(raw_context->context_flags);
|
||||
SetContextAMD64(raw_context);
|
||||
this->valid_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
TestDumpContext::~TestDumpContext() {
|
||||
FreeContext();
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, DisassembleInstructionX86) {
|
||||
string instruction;
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_X86, nullptr, 0, instruction));
|
||||
std::vector<uint8_t> pop_eax = {0x58};
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_X86, pop_eax.data(), pop_eax.size(), instruction));
|
||||
ASSERT_EQ(instruction, "pop eax");
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, DisassembleInstructionAMD64) {
|
||||
string instruction;
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_AMD64, nullptr, 0, instruction));
|
||||
std::vector<uint8_t> pop_rax = {0x58};
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::DisassembleInstruction(
|
||||
MD_CONTEXT_AMD64, pop_rax.data(), pop_rax.size(), instruction));
|
||||
ASSERT_EQ(instruction, "pop rax");
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, TokenizeInstruction) {
|
||||
string operation, dest, src;
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"pop eax", operation, dest, src));
|
||||
ASSERT_EQ(operation, "pop");
|
||||
ASSERT_EQ(dest, "eax");
|
||||
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov eax, ebx", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "eax");
|
||||
ASSERT_EQ(src, "ebx");
|
||||
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"pop rax", operation, dest, src));
|
||||
ASSERT_EQ(operation, "pop");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax, rbx", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
ASSERT_EQ(src, "rbx");
|
||||
|
||||
// Test the three parsing failure paths
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax,", operation, dest, src));
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax rbx", operation, dest, src));
|
||||
ASSERT_FALSE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax, rbx, rcx", operation, dest, src));
|
||||
|
||||
// This is of course a nonsense instruction, but test that we do remove
|
||||
// multiple instruction prefixes and can handle multiple memory operands.
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"rep lock mov DWORD PTR rax, QWORD PTR rbx", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
ASSERT_EQ(src, "rbx");
|
||||
|
||||
// Test that we ignore junk following a valid instruction
|
||||
ASSERT_TRUE(DisassemblerObjdumpForTest::TokenizeInstruction(
|
||||
"mov rax, rbx ; junk here", operation, dest, src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "rax");
|
||||
ASSERT_EQ(src, "rbx");
|
||||
}
|
||||
|
||||
namespace x86 {
|
||||
const TestMemoryRegion load_reg(kX86TestEip, {0x8b, 0x06}); // mov eax, [esi];
|
||||
|
||||
const TestMemoryRegion load_reg_index(kX86TestEip,
|
||||
{0x8b, 0x04,
|
||||
0xbe}); // mov eax, [esi+edi*4];
|
||||
|
||||
const TestMemoryRegion load_reg_offset(kX86TestEip,
|
||||
{0x8b, 0x46,
|
||||
0x10}); // mov eax, [esi+0x10];
|
||||
|
||||
const TestMemoryRegion load_reg_index_offset(
|
||||
kX86TestEip,
|
||||
{0x8b, 0x44, 0xbe, 0xf0}); // mov eax, [esi+edi*4-0x10];
|
||||
|
||||
const TestMemoryRegion rep_stosb(kX86TestEip, {0xf3, 0xaa}); // rep stosb;
|
||||
|
||||
const TestMemoryRegion lock_cmpxchg(kX86TestEip,
|
||||
{0xf0, 0x0f, 0xb1, 0x46,
|
||||
0x10}); // lock cmpxchg [esi + 0x10], eax;
|
||||
|
||||
const TestMemoryRegion call_reg_offset(kX86TestEip,
|
||||
{0xff, 0x96, 0x99, 0x99, 0x99,
|
||||
0x09}); // call [esi+0x9999999];
|
||||
} // namespace x86
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadReg) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg, kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadRegIndex) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_index,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi + (kX86TestEdi * 4));
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadRegOffset) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_offset,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LoadRegIndexOffset) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::load_reg_index_offset,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kX86TestEsi + (kX86TestEdi * 4) - 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86RepStosb) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::rep_stosb,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kX86TestEs + kX86TestEdi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86LockCmpxchg) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::lock_cmpxchg,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kX86TestEsi + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, X86CallRegOffset) {
|
||||
TestDumpContext context;
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &x86::call_reg_offset,
|
||||
kX86TestEip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kX86TestEsi + 0x9999999);
|
||||
}
|
||||
|
||||
namespace amd64 {
|
||||
const TestMemoryRegion load_reg(kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x06}); // mov rax, [rsi];
|
||||
|
||||
const TestMemoryRegion load_reg_index(kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x04,
|
||||
0xbe}); // mov rax, [rsi+rdi*4];
|
||||
|
||||
const TestMemoryRegion load_rip_relative(kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x05, 0x10, 0x00, 0x00,
|
||||
0x00}); // mov rax, [rip+0x10];
|
||||
|
||||
const TestMemoryRegion load_reg_index_offset(
|
||||
kAMD64TestRip,
|
||||
{0x48, 0x8b, 0x44, 0xbe, 0xf0}); // mov rax, [rsi+rdi*4-0x10];
|
||||
|
||||
const TestMemoryRegion rep_stosb(kAMD64TestRip, {0xf3, 0xaa}); // rep stosb;
|
||||
|
||||
const TestMemoryRegion lock_cmpxchg(kAMD64TestRip,
|
||||
{0xf0, 0x48, 0x0f, 0xb1, 0x46,
|
||||
0x10}); // lock cmpxchg [rsi + 0x10], rax;
|
||||
|
||||
const TestMemoryRegion call_reg_offset(kAMD64TestRip,
|
||||
{0xff, 0x96, 0x99, 0x99, 0x99,
|
||||
0x09}); // call [rsi+0x9999999];
|
||||
} // namespace amd64
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadReg) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_reg,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRsi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadRegIndex) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_reg_index,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRsi + (kAMD64TestRdi * 4));
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadRipRelative) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::load_rip_relative,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRip + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LoadRegIndexOffset) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(),
|
||||
&amd64::load_reg_index_offset, kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_FALSE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_TRUE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(src_address, kAMD64TestRsi + (kAMD64TestRdi * 4) - 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64RepStosb) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::rep_stosb,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kAMD64TestRdi);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64LockCmpxchg) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::lock_cmpxchg,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kAMD64TestRsi + 0x10);
|
||||
}
|
||||
|
||||
TEST(DisassemblerObjdumpTest, AMD64CallRegOffset) {
|
||||
TestDumpContext context(true);
|
||||
DisassemblerObjdump dis(context.GetContextCPU(), &amd64::call_reg_offset,
|
||||
kAMD64TestRip);
|
||||
uint64_t src_address = 0, dest_address = 0;
|
||||
ASSERT_TRUE(dis.CalculateDestAddress(context, dest_address));
|
||||
ASSERT_FALSE(dis.CalculateSrcAddress(context, src_address));
|
||||
ASSERT_EQ(dest_address, kAMD64TestRsi + 0x9999999);
|
||||
}
|
||||
} // namespace google_breakpad
|
|
@ -35,21 +35,13 @@
|
|||
|
||||
#include "processor/exploitability_linux.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <regex.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <iterator>
|
||||
#endif // _WIN32
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "google_breakpad/common/minidump_exception_linux.h"
|
||||
#include "google_breakpad/processor/call_stack.h"
|
||||
#include "google_breakpad/processor/process_state.h"
|
||||
#include "google_breakpad/processor/stack_frame.h"
|
||||
#include "processor/disassembler_objdump.h"
|
||||
#include "processor/logging.h"
|
||||
|
||||
namespace {
|
||||
|
@ -67,11 +59,6 @@ constexpr char kStackCheckFailureFunction[] = "__stack_chk_fail";
|
|||
// can determine that the call would overflow the target buffer.
|
||||
constexpr char kBoundsCheckFailureFunction[] = "__chk_fail";
|
||||
|
||||
#ifndef _WIN32
|
||||
const unsigned int MAX_INSTRUCTION_LEN = 15;
|
||||
const unsigned int MAX_OBJDUMP_BUFFER_LEN = 4096;
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace google_breakpad {
|
||||
|
@ -198,69 +185,30 @@ bool ExploitabilityLinux::EndedOnIllegalWrite(uint64_t instruction_ptr) {
|
|||
BPLOG(INFO) << "No exception or architecture data.";
|
||||
return false;
|
||||
}
|
||||
// Check architecture and set architecture variable to corresponding flag
|
||||
// in objdump.
|
||||
switch (context->GetContextCPU()) {
|
||||
case MD_CONTEXT_X86:
|
||||
architecture = "i386";
|
||||
break;
|
||||
case MD_CONTEXT_AMD64:
|
||||
architecture = "i386:x86-64";
|
||||
break;
|
||||
default:
|
||||
// Unsupported architecture. Note that ARM architectures are not
|
||||
// supported because objdump does not support ARM.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get memory region around instruction pointer and the number of bytes
|
||||
// before and after the instruction pointer in the memory region.
|
||||
const uint8_t* raw_memory = memory_region->GetMemory();
|
||||
const uint64_t base = memory_region->GetBase();
|
||||
if (base > instruction_ptr) {
|
||||
BPLOG(ERROR) << "Memory region base value exceeds instruction pointer.";
|
||||
return false;
|
||||
}
|
||||
const uint64_t offset = instruction_ptr - base;
|
||||
if (memory_region->GetSize() < MAX_INSTRUCTION_LEN + offset) {
|
||||
BPLOG(INFO) << "Not enough bytes left to guarantee complete instruction.";
|
||||
DisassemblerObjdump disassembler(context->GetContextCPU(), memory_region,
|
||||
instruction_ptr);
|
||||
if (!disassembler.IsValid()) {
|
||||
BPLOG(INFO) << "Disassembling fault instruction failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert bytes into objdump output.
|
||||
char objdump_output_buffer[MAX_OBJDUMP_BUFFER_LEN] = {0};
|
||||
DisassembleBytes(architecture,
|
||||
raw_memory + offset,
|
||||
MAX_INSTRUCTION_LEN,
|
||||
MAX_OBJDUMP_BUFFER_LEN,
|
||||
objdump_output_buffer);
|
||||
|
||||
string line;
|
||||
if (!GetObjdumpInstructionLine(objdump_output_buffer, &line)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert objdump instruction line into the operation and operands.
|
||||
string instruction = "";
|
||||
string dest = "";
|
||||
string src = "";
|
||||
TokenizeObjdumpInstruction(line, &instruction, &dest, &src);
|
||||
|
||||
// Check if the operation is a write to memory. First, the instruction
|
||||
// must one that can write to memory. Second, the write destination
|
||||
// must be a spot in memory rather than a register. Since there are no
|
||||
// symbols from objdump, the destination will be enclosed by brackets.
|
||||
if (dest.size() > 2 && dest.at(0) == '[' && dest.at(dest.size() - 1) == ']' &&
|
||||
(!instruction.compare("mov") || !instruction.compare("inc") ||
|
||||
!instruction.compare("dec") || !instruction.compare("and") ||
|
||||
!instruction.compare("or") || !instruction.compare("xor") ||
|
||||
!instruction.compare("not") || !instruction.compare("neg") ||
|
||||
!instruction.compare("add") || !instruction.compare("sub") ||
|
||||
!instruction.compare("shl") || !instruction.compare("shr"))) {
|
||||
// Strip away enclosing brackets from the destination address.
|
||||
dest = dest.substr(1, dest.size() - 2);
|
||||
// Check if the operation is a write to memory.
|
||||
// First, the instruction must one that can write to memory.
|
||||
auto instruction = disassembler.operation();
|
||||
if (!instruction.compare("mov") || !instruction.compare("inc") ||
|
||||
!instruction.compare("dec") || !instruction.compare("and") ||
|
||||
!instruction.compare("or") || !instruction.compare("xor") ||
|
||||
!instruction.compare("not") || !instruction.compare("neg") ||
|
||||
!instruction.compare("add") || !instruction.compare("sub") ||
|
||||
!instruction.compare("shl") || !instruction.compare("shr")) {
|
||||
uint64_t write_address = 0;
|
||||
CalculateAddress(dest, *context, &write_address);
|
||||
|
||||
// Check that the destination is a memory address. CalculateDestAddress will
|
||||
// return false if the destination is not a memory address.
|
||||
if (!disassembler.CalculateDestAddress(*context, write_address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the program crashed as a result of a write, the destination of
|
||||
// the write must have been an address that did not permit writing.
|
||||
|
@ -268,271 +216,14 @@ bool ExploitabilityLinux::EndedOnIllegalWrite(uint64_t instruction_ptr) {
|
|||
// the crash does not suggest exploitability for writes with such a
|
||||
// low target address.
|
||||
return write_address > 4096;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
bool ExploitabilityLinux::CalculateAddress(const string& address_expression,
|
||||
const DumpContext& context,
|
||||
uint64_t* write_address) {
|
||||
// The destination should be the format reg+a or reg-a, where reg
|
||||
// is a register and a is a hexadecimal constant. Although more complex
|
||||
// expressions can make valid instructions, objdump's disassembly outputs
|
||||
// it in this simpler format.
|
||||
// TODO(liuandrew): Handle more complex formats, should they arise.
|
||||
|
||||
if (!write_address) {
|
||||
BPLOG(ERROR) << "Null parameter.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone parameter into a non-const string.
|
||||
string expression = address_expression;
|
||||
|
||||
// Parse out the constant that is added to the address (if it exists).
|
||||
size_t delim = expression.find('+');
|
||||
bool positive_add_constant = true;
|
||||
// Check if constant is subtracted instead of added.
|
||||
if (delim == string::npos) {
|
||||
positive_add_constant = false;
|
||||
delim = expression.find('-');
|
||||
}
|
||||
uint32_t add_constant = 0;
|
||||
// Save constant and remove it from the expression.
|
||||
if (delim != string::npos) {
|
||||
if (!sscanf(expression.substr(delim + 1).c_str(), "%x", &add_constant)) {
|
||||
BPLOG(ERROR) << "Failed to scan constant.";
|
||||
return false;
|
||||
}
|
||||
expression = expression.substr(0, delim);
|
||||
}
|
||||
|
||||
// Set the the write address to the corresponding register.
|
||||
// TODO(liuandrew): Add support for partial registers, such as
|
||||
// the rax/eax/ax/ah/al chain.
|
||||
switch (context.GetContextCPU()) {
|
||||
case MD_CONTEXT_X86:
|
||||
if (!expression.compare("eax")) {
|
||||
*write_address = context.GetContextX86()->eax;
|
||||
} else if (!expression.compare("ebx")) {
|
||||
*write_address = context.GetContextX86()->ebx;
|
||||
} else if (!expression.compare("ecx")) {
|
||||
*write_address = context.GetContextX86()->ecx;
|
||||
} else if (!expression.compare("edx")) {
|
||||
*write_address = context.GetContextX86()->edx;
|
||||
} else if (!expression.compare("edi")) {
|
||||
*write_address = context.GetContextX86()->edi;
|
||||
} else if (!expression.compare("esi")) {
|
||||
*write_address = context.GetContextX86()->esi;
|
||||
} else if (!expression.compare("ebp")) {
|
||||
*write_address = context.GetContextX86()->ebp;
|
||||
} else if (!expression.compare("esp")) {
|
||||
*write_address = context.GetContextX86()->esp;
|
||||
} else if (!expression.compare("eip")) {
|
||||
*write_address = context.GetContextX86()->eip;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported register";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case MD_CONTEXT_AMD64:
|
||||
if (!expression.compare("rax")) {
|
||||
*write_address = context.GetContextAMD64()->rax;
|
||||
} else if (!expression.compare("rbx")) {
|
||||
*write_address = context.GetContextAMD64()->rbx;
|
||||
} else if (!expression.compare("rcx")) {
|
||||
*write_address = context.GetContextAMD64()->rcx;
|
||||
} else if (!expression.compare("rdx")) {
|
||||
*write_address = context.GetContextAMD64()->rdx;
|
||||
} else if (!expression.compare("rdi")) {
|
||||
*write_address = context.GetContextAMD64()->rdi;
|
||||
} else if (!expression.compare("rsi")) {
|
||||
*write_address = context.GetContextAMD64()->rsi;
|
||||
} else if (!expression.compare("rbp")) {
|
||||
*write_address = context.GetContextAMD64()->rbp;
|
||||
} else if (!expression.compare("rsp")) {
|
||||
*write_address = context.GetContextAMD64()->rsp;
|
||||
} else if (!expression.compare("rip")) {
|
||||
*write_address = context.GetContextAMD64()->rip;
|
||||
} else if (!expression.compare("r8")) {
|
||||
*write_address = context.GetContextAMD64()->r8;
|
||||
} else if (!expression.compare("r9")) {
|
||||
*write_address = context.GetContextAMD64()->r9;
|
||||
} else if (!expression.compare("r10")) {
|
||||
*write_address = context.GetContextAMD64()->r10;
|
||||
} else if (!expression.compare("r11")) {
|
||||
*write_address = context.GetContextAMD64()->r11;
|
||||
} else if (!expression.compare("r12")) {
|
||||
*write_address = context.GetContextAMD64()->r12;
|
||||
} else if (!expression.compare("r13")) {
|
||||
*write_address = context.GetContextAMD64()->r13;
|
||||
} else if (!expression.compare("r14")) {
|
||||
*write_address = context.GetContextAMD64()->r14;
|
||||
} else if (!expression.compare("r15")) {
|
||||
*write_address = context.GetContextAMD64()->r15;
|
||||
} else {
|
||||
BPLOG(ERROR) << "Unsupported register";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// This should not occur since the same switch condition
|
||||
// should have terminated this method.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add or subtract constant from write address (if applicable).
|
||||
*write_address =
|
||||
positive_add_constant ?
|
||||
*write_address + add_constant : *write_address - add_constant;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool ExploitabilityLinux::GetObjdumpInstructionLine(
|
||||
const char* objdump_output_buffer,
|
||||
string* instruction_line) {
|
||||
// Put buffer data into stream to output line-by-line.
|
||||
std::stringstream objdump_stream;
|
||||
objdump_stream.str(string(objdump_output_buffer));
|
||||
|
||||
// Pipe each output line into the string until the string contains the first
|
||||
// instruction from objdump. All lines before the "<.data>:" section are
|
||||
// skipped. Loop until the line shows the first instruction or there are no
|
||||
// lines left.
|
||||
bool data_section_seen = false;
|
||||
do {
|
||||
if (!getline(objdump_stream, *instruction_line)) {
|
||||
BPLOG(INFO) << "Objdump instructions not found";
|
||||
return false;
|
||||
}
|
||||
if (instruction_line->find("<.data>:") != string::npos) {
|
||||
data_section_seen = true;
|
||||
}
|
||||
} while (!data_section_seen || instruction_line->find("0:") == string::npos);
|
||||
// This first instruction contains the above substring.
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExploitabilityLinux::TokenizeObjdumpInstruction(const string& line,
|
||||
string* operation,
|
||||
string* dest,
|
||||
string* src) {
|
||||
if (!operation || !dest || !src) {
|
||||
BPLOG(ERROR) << "Null parameters passed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set all pointer values to empty strings.
|
||||
*operation = "";
|
||||
*dest = "";
|
||||
*src = "";
|
||||
|
||||
// Tokenize the objdump line.
|
||||
vector<string> tokens;
|
||||
std::istringstream line_stream(line);
|
||||
copy(std::istream_iterator<string>(line_stream),
|
||||
std::istream_iterator<string>(),
|
||||
std::back_inserter(tokens));
|
||||
|
||||
// Regex for the data in hex form. Each byte is two hex digits.
|
||||
regex_t regex;
|
||||
regcomp(®ex, "^[[:xdigit:]]{2}$", REG_EXTENDED | REG_NOSUB);
|
||||
|
||||
// Find and set the location of the operator. The operator appears
|
||||
// directly after the chain of bytes that define the instruction. The
|
||||
// operands will be the last token, given that the instruction has operands.
|
||||
// If not, the operator is the last token. The loop skips the first token
|
||||
// because the first token is the instruction number (namely "0:").
|
||||
string operands = "";
|
||||
for (size_t i = 1; i < tokens.size(); i++) {
|
||||
// Check if current token no longer is in byte format.
|
||||
if (regexec(®ex, tokens[i].c_str(), 0, NULL, 0)) {
|
||||
// instruction = tokens[i];
|
||||
*operation = tokens[i];
|
||||
// If the operator is the last token, there are no operands.
|
||||
if (i != tokens.size() - 1) {
|
||||
operands = tokens[tokens.size() - 1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
regfree(®ex);
|
||||
|
||||
if (operation->empty()) {
|
||||
BPLOG(ERROR) << "Failed to parse out operation from objdump instruction.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Split operands into source and destination (if applicable).
|
||||
if (!operands.empty()) {
|
||||
size_t delim = operands.find(',');
|
||||
if (delim == string::npos) {
|
||||
*dest = operands;
|
||||
} else {
|
||||
*dest = operands.substr(0, delim);
|
||||
*src = operands.substr(delim + 1);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ExploitabilityLinux::DisassembleBytes(const string& architecture,
|
||||
const uint8_t* raw_bytes,
|
||||
const unsigned int raw_bytes_len,
|
||||
const unsigned int buffer_len,
|
||||
char* objdump_output_buffer) {
|
||||
if (!raw_bytes || !objdump_output_buffer ||
|
||||
raw_bytes_len > MAX_INSTRUCTION_LEN) {
|
||||
BPLOG(ERROR) << "Bad input parameters.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write raw bytes around instruction pointer to a temporary file to
|
||||
// pass as an argument to objdump.
|
||||
char raw_bytes_tmpfile[] = "/tmp/breakpad_mem_region-raw_bytes-XXXXXX";
|
||||
int raw_bytes_fd = mkstemp(raw_bytes_tmpfile);
|
||||
if (raw_bytes_fd < 0) {
|
||||
BPLOG(ERROR) << "Failed to create tempfile.";
|
||||
unlink(raw_bytes_tmpfile);
|
||||
return false;
|
||||
}
|
||||
// Casting raw_bytes_len to `ssize_t` won't cause a sign flip, since we check
|
||||
// its bounds above.
|
||||
if (write(raw_bytes_fd, raw_bytes, raw_bytes_len) != (ssize_t)raw_bytes_len) {
|
||||
BPLOG(ERROR) << "Writing of raw bytes failed.";
|
||||
unlink(raw_bytes_tmpfile);
|
||||
return false;
|
||||
}
|
||||
|
||||
char cmd[1024] = {0};
|
||||
snprintf(cmd,
|
||||
1024,
|
||||
"objdump -D -b binary -M intel -m %s %s",
|
||||
architecture.c_str(),
|
||||
raw_bytes_tmpfile);
|
||||
FILE* objdump_fp = popen(cmd, "r");
|
||||
if (!objdump_fp) {
|
||||
unlink(raw_bytes_tmpfile);
|
||||
BPLOG(ERROR) << "Failed to call objdump.";
|
||||
return false;
|
||||
}
|
||||
if (fread(objdump_output_buffer, 1, buffer_len, objdump_fp) <= 0) {
|
||||
pclose(objdump_fp);
|
||||
unlink(raw_bytes_tmpfile);
|
||||
BPLOG(ERROR) << "Failed to read objdump output.";
|
||||
return false;
|
||||
}
|
||||
pclose(objdump_fp);
|
||||
unlink(raw_bytes_tmpfile);
|
||||
return true;
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
bool ExploitabilityLinux::StackPointerOffStack(uint64_t stack_ptr) {
|
||||
MinidumpLinuxMapsList* linux_maps_list = dump_->GetLinuxMapsList();
|
||||
// Inconclusive if there are no mappings available.
|
||||
|
|
|
@ -75,42 +75,6 @@ class ExploitabilityLinux : public Exploitability {
|
|||
// instruction is at a spot in memory that prohibits writes.
|
||||
bool EndedOnIllegalWrite(uint64_t instruction_ptr);
|
||||
|
||||
#ifndef _WIN32
|
||||
// Disassembles raw bytes via objdump and pipes the output into the provided
|
||||
// buffer, given the desired architecture, the file from which objdump will
|
||||
// read, and the buffer length. The method returns whether the disassembly
|
||||
// was a success, and the caller owns all pointers.
|
||||
static bool DisassembleBytes(const string& architecture,
|
||||
const uint8_t* raw_bytes,
|
||||
const unsigned int raw_bytes_len,
|
||||
const unsigned int MAX_OBJDUMP_BUFFER_LEN,
|
||||
char* objdump_output_buffer);
|
||||
|
||||
// Parses the objdump output given in |objdump_output_buffer| and extracts
|
||||
// the line of the first instruction into |instruction_line|. Returns true
|
||||
// when the instruction line is successfully extracted.
|
||||
static bool GetObjdumpInstructionLine(
|
||||
const char* objdump_output_buffer,
|
||||
string* instruction_line);
|
||||
|
||||
// Tokenizes out the operation and operands from a line of instruction
|
||||
// disassembled by objdump. This method modifies the pointers to match the
|
||||
// tokens of the instruction, and returns if the tokenizing was a success.
|
||||
// The caller owns all pointers.
|
||||
static bool TokenizeObjdumpInstruction(const string& line,
|
||||
string* operation,
|
||||
string* dest,
|
||||
string* src);
|
||||
|
||||
// Calculates the effective address of an expression in the form reg+a or
|
||||
// reg-a, where 'reg' is a register and 'a' is a constant, and writes the
|
||||
// result in the pointer. The method returns whether the calculation was
|
||||
// a success. The caller owns the pointer.
|
||||
static bool CalculateAddress(const string& address_expression,
|
||||
const DumpContext& context,
|
||||
uint64_t* write_address);
|
||||
#endif // _WIN32
|
||||
|
||||
// Checks if the stack pointer points to a memory mapping that is not
|
||||
// labelled as the stack.
|
||||
bool StackPointerOffStack(uint64_t stack_ptr);
|
||||
|
|
|
@ -44,15 +44,6 @@
|
|||
|
||||
#ifdef __linux__
|
||||
namespace google_breakpad {
|
||||
|
||||
class ExploitabilityLinuxTest : public ExploitabilityLinux {
|
||||
public:
|
||||
using ExploitabilityLinux::CalculateAddress;
|
||||
using ExploitabilityLinux::DisassembleBytes;
|
||||
using ExploitabilityLinux::GetObjdumpInstructionLine;
|
||||
using ExploitabilityLinux::TokenizeObjdumpInstruction;
|
||||
};
|
||||
|
||||
class ExploitabilityLinuxTestMinidumpContext : public MinidumpContext {
|
||||
public:
|
||||
explicit ExploitabilityLinuxTestMinidumpContext(
|
||||
|
@ -70,7 +61,6 @@ namespace {
|
|||
|
||||
using google_breakpad::BasicSourceLineResolver;
|
||||
#ifdef __linux__
|
||||
using google_breakpad::ExploitabilityLinuxTest;
|
||||
using google_breakpad::ExploitabilityLinuxTestMinidumpContext;
|
||||
#endif // __linux__
|
||||
using google_breakpad::MinidumpProcessor;
|
||||
|
@ -185,120 +175,4 @@ TEST(ExploitabilityTest, TestLinuxEngine) {
|
|||
#endif // __linux__
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
TEST(ExploitabilityLinuxUtilsTest, DisassembleBytesTest) {
|
||||
ASSERT_FALSE(ExploitabilityLinuxTest::DisassembleBytes("", NULL, 0, 5, NULL));
|
||||
uint8_t bytes[6] = {0xc7, 0x0, 0x5, 0x0, 0x0, 0x0};
|
||||
char buffer[1024] = {0};
|
||||
ASSERT_TRUE(ExploitabilityLinuxTest::DisassembleBytes(
|
||||
"i386:x86-64", bytes, std::extent<decltype(bytes)>::value, 1024, buffer));
|
||||
std::stringstream objdump_stream;
|
||||
objdump_stream.str(string(buffer));
|
||||
string line = "";
|
||||
while (line.find("<.data>") == string::npos)
|
||||
getline(objdump_stream, line);
|
||||
getline(objdump_stream, line);
|
||||
ASSERT_EQ(line, " 0:\tc7 00 05 00 00 00 \tmov DWORD PTR [rax],0x5");
|
||||
}
|
||||
|
||||
TEST(ExploitabilityLinuxUtilsTest, GetObjdumpInstructionLine) {
|
||||
string disassebly =
|
||||
"\n"
|
||||
"/tmp/breakpad_mem_region-raw_bytes-tMmMo0: file format binary\n"
|
||||
"// Trying to confuse the parser 0:\n"
|
||||
"\n"
|
||||
"Disassembly of section .data:\n"
|
||||
"\n"
|
||||
"0000000000000000 <.data>:\n"
|
||||
" 0:\tc7 00 01 00 00 00 \tmov DWORD PTR [rax],0x1\n"
|
||||
" 6:\t5d \tpop rbp\n"
|
||||
" 7:\tc3 \tret \n"
|
||||
" 8:\t55 \tpush rbp\n"
|
||||
" 9:\t48 89 e5 \tmov rbp,rsp\n"
|
||||
" c:\t53 \tpush rbx\n"
|
||||
" d:\t48 \trex.W\n"
|
||||
" e:\t81 \t.byte 0x81\n";
|
||||
string line;
|
||||
EXPECT_TRUE(ExploitabilityLinuxTest::GetObjdumpInstructionLine(
|
||||
disassebly.c_str(), &line));
|
||||
EXPECT_EQ(" 0:\tc7 00 01 00 00 00 \tmov DWORD PTR [rax],0x1", line);
|
||||
|
||||
// There is no "0:" after "<.data>:". Expected to return false.
|
||||
disassebly =
|
||||
"\n"
|
||||
"/tmp/breakpad_mem_region-raw_bytes-tMmMo0: file format binary\n"
|
||||
"// Trying to confuse the parser 0:\n"
|
||||
"\n"
|
||||
"Disassembly of section .data:\n"
|
||||
"\n"
|
||||
" 0:\tc7 00 01 00 00 00 \tmov DWORD PTR [rax],0x1\n"
|
||||
" 6:\t5d \tpop rbp\n"
|
||||
" 7:\tc3 \tret \n"
|
||||
" 8:\t55 \tpush rbp\n"
|
||||
" 9:\t48 89 e5 \tmov rbp,rsp\n"
|
||||
" d:\t48 \trex.W\n"
|
||||
"0000000000000000 <.data>:\n"
|
||||
" c:\t53 \tpush rbx\n";
|
||||
EXPECT_FALSE(ExploitabilityLinuxTest::GetObjdumpInstructionLine(
|
||||
disassebly.c_str(), &line));
|
||||
}
|
||||
|
||||
TEST(ExploitabilityLinuxUtilsTest, TokenizeObjdumpInstructionTest) {
|
||||
ASSERT_FALSE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction("",
|
||||
NULL,
|
||||
NULL,
|
||||
NULL));
|
||||
string line = "0: c7 00 05 00 00 00 mov DWORD PTR [rax],0x5";
|
||||
string operation = "";
|
||||
string dest = "";
|
||||
string src = "";
|
||||
ASSERT_TRUE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction(line,
|
||||
&operation,
|
||||
&dest,
|
||||
&src));
|
||||
ASSERT_EQ(operation, "mov");
|
||||
ASSERT_EQ(dest, "[rax]");
|
||||
ASSERT_EQ(src, "0x5");
|
||||
line = "0: c3 ret";
|
||||
ASSERT_TRUE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction(line,
|
||||
&operation,
|
||||
&dest,
|
||||
&src));
|
||||
ASSERT_EQ(operation, "ret");
|
||||
ASSERT_EQ(dest, "");
|
||||
ASSERT_EQ(src, "");
|
||||
line = "0: 5f pop rdi";
|
||||
ASSERT_TRUE(ExploitabilityLinuxTest::TokenizeObjdumpInstruction(line,
|
||||
&operation,
|
||||
&dest,
|
||||
&src));
|
||||
ASSERT_EQ(operation, "pop");
|
||||
ASSERT_EQ(dest, "rdi");
|
||||
ASSERT_EQ(src, "");
|
||||
}
|
||||
|
||||
TEST(ExploitabilityLinuxUtilsTest, CalculateAddressTest) {
|
||||
MDRawContextAMD64 raw_context;
|
||||
raw_context.rdx = 12345;
|
||||
ExploitabilityLinuxTestMinidumpContext context(raw_context);
|
||||
ASSERT_EQ(context.GetContextAMD64()->rdx, 12345U);
|
||||
ASSERT_FALSE(ExploitabilityLinuxTest::CalculateAddress("", context, NULL));
|
||||
uint64_t write_address = 0;
|
||||
ASSERT_TRUE(ExploitabilityLinuxTest::CalculateAddress("rdx-0x4D2",
|
||||
context,
|
||||
&write_address));
|
||||
ASSERT_EQ(write_address, 11111U);
|
||||
ASSERT_TRUE(ExploitabilityLinuxTest::CalculateAddress("rdx+0x4D2",
|
||||
context,
|
||||
&write_address));
|
||||
ASSERT_EQ(write_address, 13579U);
|
||||
ASSERT_FALSE(ExploitabilityLinuxTest::CalculateAddress("rdx+rax",
|
||||
context,
|
||||
&write_address));
|
||||
ASSERT_FALSE(ExploitabilityLinuxTest::CalculateAddress("0x3482+0x4D2",
|
||||
context,
|
||||
&write_address));
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
} // namespace
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
'contained_range_map.h',
|
||||
'convert_old_arm64_context.cc',
|
||||
'convert_old_arm64_context.h',
|
||||
'disassembler_objdump.cc',
|
||||
'disassembler_objdump.h',
|
||||
'disassembler_x86.cc',
|
||||
'disassembler_x86.h',
|
||||
'dump_context.cc',
|
||||
|
@ -147,6 +149,7 @@
|
|||
'basic_source_line_resolver_unittest.cc',
|
||||
'cfi_frame_info_unittest.cc',
|
||||
'contained_range_map_unittest.cc',
|
||||
'disassembler_objdump_unittest.cc',
|
||||
'disassembler_x86_unittest.cc',
|
||||
'exploitability_unittest.cc',
|
||||
'fast_source_line_resolver_unittest.cc',
|
||||
|
|
Loading…
Reference in a new issue