From dea40c941a4d443d1b748bafb8a74f02c360e810 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sun, 16 Feb 2020 05:20:01 -0800 Subject: [PATCH] libbacktrace: add Mach-O support libbacktrace/ PR libbacktrace/88745 * macho.c: New file. * filetype.awk: Recognize Mach-O files. * Makefile.am (FORMAT_FILES): Add macho.c. (check_DATA): New variable. Set to .dSYM if HAVE_DSYMUTIL. (%.dSYM): New pattern target. (test_macho_SOURCES, test_macho_CFLAGS): New targets. (test_macho_LDADD): New target. (BUILDTESTS): Add test_macho. (macho.lo): Add dependencies. * configure.ac: Recognize macho file type. Check for mach-o/dyld.h. Don't try to run objcopy if we don't find it. Look for dsymutil and define a HAVE_DSYMUTIL conditional. * Makefile.in: Regenerate. * configure: Regenerate. * config.h.in: Regenerate. --- libbacktrace/ChangeLog | 19 + libbacktrace/Makefile.am | 68 +- libbacktrace/Makefile.in | 123 +++- libbacktrace/config.h.in | 3 + libbacktrace/configure | 78 ++- libbacktrace/configure.ac | 10 + libbacktrace/filetype.awk | 19 +- libbacktrace/macho.c | 1309 +++++++++++++++++++++++++++++++++++++ 8 files changed, 1580 insertions(+), 49 deletions(-) create mode 100644 libbacktrace/macho.c diff --git a/libbacktrace/ChangeLog b/libbacktrace/ChangeLog index 4b4f574fc75..6fa15fc4e96 100644 --- a/libbacktrace/ChangeLog +++ b/libbacktrace/ChangeLog @@ -1,3 +1,22 @@ +2020-05-09 Ian Lance Taylor + + PR libbacktrace/88745 + * macho.c: New file. + * filetype.awk: Recognize Mach-O files. + * Makefile.am (FORMAT_FILES): Add macho.c. + (check_DATA): New variable. Set to .dSYM if HAVE_DSYMUTIL. + (%.dSYM): New pattern target. + (test_macho_SOURCES, test_macho_CFLAGS): New targets. + (test_macho_LDADD): New target. + (BUILDTESTS): Add test_macho. + (macho.lo): Add dependencies. + * configure.ac: Recognize macho file type. Check for + mach-o/dyld.h. Don't try to run objcopy if we don't find it. + Look for dsymutil and define a HAVE_DSYMUTIL conditional. + * Makefile.in: Regenerate. + * configure: Regenerate. + * config.h.in: Regenerate. + 2020-05-09 Ian Lance Taylor * read.c (backtrace_get_view): Support short read. diff --git a/libbacktrace/Makefile.am b/libbacktrace/Makefile.am index c73f6633a76..d3a9ba8843a 100644 --- a/libbacktrace/Makefile.am +++ b/libbacktrace/Makefile.am @@ -56,6 +56,7 @@ BACKTRACE_FILES = \ FORMAT_FILES = \ elf.c \ + macho.c \ pecoff.c \ unknown.c \ xcoff.c @@ -84,18 +85,28 @@ libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) # Testsuite. -# Add test to this variable, if you want it to be build. +# Add a test to this variable if you want it to be built. check_PROGRAMS = -# Add test to this variable, if you want it to be run. +# Add a test to this variable if you want it to be run. TESTS = -# Add test to this variable, if you want it to be build and run. +# Add a test to this variable if you want it to be built and run. BUILDTESTS = +# Add a file to this variable if you want it to be built for testing. +check_DATA = + # Flags to use when compiling test programs. libbacktrace_TEST_CFLAGS = $(EXTRA_FLAGS) $(WARN_FLAGS) -g +if HAVE_DSYMUTIL + +%.dSYM: % + $(DSYMUTIL) $< + +endif HAVE_DSYMUTIL + if NATIVE check_LTLIBRARIES = libbacktrace_alloc.la @@ -163,6 +174,12 @@ test_elf_64_LDADD = libbacktrace_noformat.la elf_64.lo BUILDTESTS += test_elf_64 +test_macho_SOURCES = test_format.c testlib.c +test_macho_CFLAGS = $(libbacktrace_TEST_CFLAGS) +test_macho_LDADD = libbacktrace_noformat.la macho.lo + +BUILDTESTS += test_macho + test_xcoff_32_SOURCES = test_format.c testlib.c test_xcoff_32_CFLAGS = $(libbacktrace_TEST_CFLAGS) test_xcoff_32_LDADD = libbacktrace_noformat.la xcoff_32.lo @@ -220,6 +237,10 @@ allocfail.sh: allocfail TESTS += allocfail.sh +if HAVE_DSYMUTIL +check_DATA += allocfail.dSYM +endif HAVE_DSYMUTIL + if HAVE_ELF if HAVE_OBJCOPY_DEBUGLINK @@ -252,6 +273,10 @@ btest_LDADD = libbacktrace.la BUILDTESTS += btest +if HAVE_DSYMUTIL +check_DATA += btest.dSYM +endif HAVE_DSYMUTIL + if HAVE_ELF btest_lto_SOURCES = btest.c testlib.c @@ -268,6 +293,10 @@ btest_alloc_LDADD = libbacktrace_alloc.la BUILDTESTS += btest_alloc +if HAVE_DSYMUTIL +check_DATA += btest_alloc.dSYM +endif HAVE_DSYMUTIL + if HAVE_DWZ %_dwz: % @@ -294,12 +323,20 @@ stest_LDADD = libbacktrace.la BUILDTESTS += stest +if HAVE_DSYMUTIL +check_DATA += stest.dSYM +endif HAVE_DSYMUTIL + stest_alloc_SOURCES = $(stest_SOURCES) stest_alloc_CFLAGS = $(libbacktrace_TEST_CFLAGS) stest_alloc_LDADD = libbacktrace_alloc.la BUILDTESTS += stest_alloc +if HAVE_DSYMUTIL +check_DATA += stest_alloc.dSYM +endif HAVE_DSYMUTIL + if HAVE_ELF ztest_SOURCES = ztest.c testlib.c @@ -329,10 +366,18 @@ edtest_LDADD = libbacktrace.la BUILDTESTS += edtest +if HAVE_DSYMUTIL +check_DATA += edtest.dSYM +endif HAVE_DSYMUTIL + edtest_alloc_SOURCES = $(edtest_SOURCES) edtest_alloc_CFLAGS = $(libbacktrace_TEST_CFLAGS) edtest_alloc_LDADD = libbacktrace_alloc.la +if HAVE_DSYMUTIL +check_DATA += edtest_alloc.dSYM +endif HAVE_DSYMUTIL + BUILDTESTS += edtest_alloc edtest2_build.c: gen_edtest2_build; @true @@ -349,12 +394,20 @@ ttest_SOURCES = ttest.c testlib.c ttest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -pthread ttest_LDADD = libbacktrace.la +if HAVE_DSYMUTIL +check_DATA += ttest.dSYM +endif HAVE_DSYMUTIL + BUILDTESTS += ttest_alloc ttest_alloc_SOURCES = $(ttest_SOURCES) ttest_alloc_CFLAGS = $(ttest_CFLAGS) ttest_alloc_LDADD = libbacktrace_alloc.la +if HAVE_DSYMUTIL +check_DATA += ttest_alloc.dSYM +endif HAVE_DSYMUTIL + endif HAVE_PTHREAD if HAVE_OBJCOPY_DEBUGLINK @@ -409,12 +462,20 @@ dwarf5_LDADD = libbacktrace.la BUILDTESTS += dwarf5 +if HAVE_DSYMUTIL +check_DATA += dwarf5.dSYM +endif HAVE_DSYMUTIL + dwarf5_alloc_SOURCES = $(dwarf5_SOURCES) dwarf5_alloc_CFLAGS = $(dwarf5_CFLAGS) dwarf5_alloc_LDADD = libbacktrace_alloc.la BUILDTESTS += dwarf5_alloc +if HAVE_DSYMUTIL +check_DATA += dwarf5_alloc.dSYM +endif HAVE_DSYMUTIL + endif endif NATIVE @@ -448,6 +509,7 @@ dwarf.lo: config.h $(INCDIR)/dwarf2.h $(INCDIR)/dwarf2.def \ $(INCDIR)/filenames.h backtrace.h internal.h elf.lo: config.h backtrace.h internal.h fileline.lo: config.h backtrace.h internal.h +macho.lo: config.h backtrace.h internal.h mmap.lo: config.h backtrace.h internal.h mmapio.lo: config.h backtrace.h internal.h nounwind.lo: config.h internal.h diff --git a/libbacktrace/Makefile.in b/libbacktrace/Makefile.in index 1178ac4cc23..9381adebebc 100644 --- a/libbacktrace/Makefile.in +++ b/libbacktrace/Makefile.in @@ -122,33 +122,41 @@ host_triplet = @host@ target_triplet = @target@ check_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_3) \ $(am__EXEEXT_12) -TESTS = $(am__append_4) $(am__append_6) $(am__append_8) \ - $(am__append_11) $(am__append_12) $(am__append_18) \ +TESTS = $(am__append_4) $(am__append_7) $(am__append_9) \ + $(am__append_12) $(am__append_13) $(am__append_20) \ $(am__EXEEXT_12) @HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_1 = libbacktrace_elf_for_test.la -@NATIVE_TRUE@am__append_2 = test_elf_32 test_elf_64 test_xcoff_32 \ -@NATIVE_TRUE@ test_xcoff_64 test_pecoff test_unknown unittest \ -@NATIVE_TRUE@ unittest_alloc btest +@NATIVE_TRUE@am__append_2 = test_elf_32 test_elf_64 test_macho \ +@NATIVE_TRUE@ test_xcoff_32 test_xcoff_64 test_pecoff \ +@NATIVE_TRUE@ test_unknown unittest unittest_alloc btest @NATIVE_TRUE@am__append_3 = allocfail @NATIVE_TRUE@am__append_4 = allocfail.sh -@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_5 = b2test -@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_6 = b2test_buildid -@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_7 = b3test -@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_8 = b3test_dwz_buildid -@HAVE_ELF_TRUE@@NATIVE_TRUE@am__append_9 = btest_lto -@NATIVE_TRUE@am__append_10 = btest_alloc stest stest_alloc -@HAVE_DWZ_TRUE@@NATIVE_TRUE@am__append_11 = btest_dwz -@HAVE_DWZ_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_12 = btest_dwz_gnudebuglink -@HAVE_ELF_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_13 = -lz +@HAVE_DSYMUTIL_TRUE@@NATIVE_TRUE@am__append_5 = allocfail.dSYM \ +@HAVE_DSYMUTIL_TRUE@@NATIVE_TRUE@ btest.dSYM btest_alloc.dSYM \ +@HAVE_DSYMUTIL_TRUE@@NATIVE_TRUE@ stest.dSYM stest_alloc.dSYM \ +@HAVE_DSYMUTIL_TRUE@@NATIVE_TRUE@ edtest.dSYM edtest_alloc.dSYM +@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_6 = b2test +@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_7 = b2test_buildid +@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_8 = b3test +@HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_9 = b3test_dwz_buildid +@HAVE_ELF_TRUE@@NATIVE_TRUE@am__append_10 = btest_lto +@NATIVE_TRUE@am__append_11 = btest_alloc stest stest_alloc +@HAVE_DWZ_TRUE@@NATIVE_TRUE@am__append_12 = btest_dwz +@HAVE_DWZ_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_13 = btest_dwz_gnudebuglink @HAVE_ELF_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_14 = -lz -@HAVE_ELF_TRUE@@NATIVE_TRUE@am__append_15 = ztest ztest_alloc -@NATIVE_TRUE@am__append_16 = edtest edtest_alloc -@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_17 = ttest ttest_alloc -@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_18 = btest_gnudebuglink -@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__append_19 = ctestg ctesta \ +@HAVE_ELF_TRUE@@HAVE_ZLIB_TRUE@@NATIVE_TRUE@am__append_15 = -lz +@HAVE_ELF_TRUE@@NATIVE_TRUE@am__append_16 = ztest ztest_alloc +@NATIVE_TRUE@am__append_17 = edtest edtest_alloc +@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_18 = ttest ttest_alloc +@HAVE_DSYMUTIL_TRUE@@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@am__append_19 = ttest.dSYM \ +@HAVE_DSYMUTIL_TRUE@@HAVE_PTHREAD_TRUE@@NATIVE_TRUE@ ttest_alloc.dSYM +@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__append_20 = btest_gnudebuglink +@HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@am__append_21 = ctestg ctesta \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctestg_alloc \ @HAVE_COMPRESSED_DEBUG_TRUE@@NATIVE_TRUE@ ctesta_alloc -@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am__append_20 = dwarf5 dwarf5_alloc +@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am__append_22 = dwarf5 dwarf5_alloc +@HAVE_DSYMUTIL_TRUE@@HAVE_DWARF5_TRUE@@NATIVE_TRUE@am__append_23 = dwarf5.dSYM \ +@HAVE_DSYMUTIL_TRUE@@HAVE_DWARF5_TRUE@@NATIVE_TRUE@ dwarf5_alloc.dSYM subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../config/cet.m4 \ @@ -208,10 +216,10 @@ libbacktrace_noformat_la_OBJECTS = \ @HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__EXEEXT_2 = b2test$(EXEEXT) @HAVE_DWZ_TRUE@@HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@am__EXEEXT_3 = b3test$(EXEEXT) @NATIVE_TRUE@am__EXEEXT_4 = test_elf_32$(EXEEXT) test_elf_64$(EXEEXT) \ -@NATIVE_TRUE@ test_xcoff_32$(EXEEXT) test_xcoff_64$(EXEEXT) \ -@NATIVE_TRUE@ test_pecoff$(EXEEXT) test_unknown$(EXEEXT) \ -@NATIVE_TRUE@ unittest$(EXEEXT) unittest_alloc$(EXEEXT) \ -@NATIVE_TRUE@ btest$(EXEEXT) +@NATIVE_TRUE@ test_macho$(EXEEXT) test_xcoff_32$(EXEEXT) \ +@NATIVE_TRUE@ test_xcoff_64$(EXEEXT) test_pecoff$(EXEEXT) \ +@NATIVE_TRUE@ test_unknown$(EXEEXT) unittest$(EXEEXT) \ +@NATIVE_TRUE@ unittest_alloc$(EXEEXT) btest$(EXEEXT) @HAVE_ELF_TRUE@@NATIVE_TRUE@am__EXEEXT_5 = btest_lto$(EXEEXT) @NATIVE_TRUE@am__EXEEXT_6 = btest_alloc$(EXEEXT) stest$(EXEEXT) \ @NATIVE_TRUE@ stest_alloc$(EXEEXT) @@ -378,6 +386,14 @@ test_elf_64_OBJECTS = $(am_test_elf_64_OBJECTS) test_elf_64_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_elf_64_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +@NATIVE_TRUE@am_test_macho_OBJECTS = test_macho-test_format.$(OBJEXT) \ +@NATIVE_TRUE@ test_macho-testlib.$(OBJEXT) +test_macho_OBJECTS = $(am_test_macho_OBJECTS) +@NATIVE_TRUE@test_macho_DEPENDENCIES = libbacktrace_noformat.la \ +@NATIVE_TRUE@ macho.lo +test_macho_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_macho_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ @NATIVE_TRUE@am_test_pecoff_OBJECTS = \ @NATIVE_TRUE@ test_pecoff-test_format.$(OBJEXT) \ @NATIVE_TRUE@ test_pecoff-testlib.$(OBJEXT) @@ -516,10 +532,10 @@ SOURCES = $(libbacktrace_la_SOURCES) $(EXTRA_libbacktrace_la_SOURCES) \ $(dwarf5_alloc_SOURCES) $(edtest_SOURCES) \ $(edtest_alloc_SOURCES) $(stest_SOURCES) \ $(stest_alloc_SOURCES) $(test_elf_32_SOURCES) \ - $(test_elf_64_SOURCES) $(test_pecoff_SOURCES) \ - $(test_unknown_SOURCES) $(test_xcoff_32_SOURCES) \ - $(test_xcoff_64_SOURCES) $(ttest_SOURCES) \ - $(ttest_alloc_SOURCES) $(unittest_SOURCES) \ + $(test_elf_64_SOURCES) $(test_macho_SOURCES) \ + $(test_pecoff_SOURCES) $(test_unknown_SOURCES) \ + $(test_xcoff_32_SOURCES) $(test_xcoff_64_SOURCES) \ + $(ttest_SOURCES) $(ttest_alloc_SOURCES) $(unittest_SOURCES) \ $(unittest_alloc_SOURCES) $(ztest_SOURCES) \ $(ztest_alloc_SOURCES) am__can_run_installinfo = \ @@ -906,6 +922,7 @@ BACKTRACE_FILES = \ FORMAT_FILES = \ elf.c \ + macho.c \ pecoff.c \ unknown.c \ xcoff.c @@ -932,10 +949,13 @@ libbacktrace_la_LIBADD = \ libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) -# Add test to this variable, if you want it to be build and run. -BUILDTESTS = $(am__append_2) $(am__append_9) $(am__append_10) \ - $(am__append_15) $(am__append_16) $(am__append_17) \ - $(am__append_19) $(am__append_20) +# Add a test to this variable if you want it to be built and run. +BUILDTESTS = $(am__append_2) $(am__append_10) $(am__append_11) \ + $(am__append_16) $(am__append_17) $(am__append_18) \ + $(am__append_21) $(am__append_22) + +# Add a file to this variable if you want it to be built for testing. +check_DATA = $(am__append_5) $(am__append_19) $(am__append_23) # Flags to use when compiling test programs. libbacktrace_TEST_CFLAGS = $(EXTRA_FLAGS) $(WARN_FLAGS) -g @@ -959,6 +979,9 @@ libbacktrace_TEST_CFLAGS = $(EXTRA_FLAGS) $(WARN_FLAGS) -g @NATIVE_TRUE@test_elf_64_SOURCES = test_format.c testlib.c @NATIVE_TRUE@test_elf_64_CFLAGS = $(libbacktrace_TEST_CFLAGS) @NATIVE_TRUE@test_elf_64_LDADD = libbacktrace_noformat.la elf_64.lo +@NATIVE_TRUE@test_macho_SOURCES = test_format.c testlib.c +@NATIVE_TRUE@test_macho_CFLAGS = $(libbacktrace_TEST_CFLAGS) +@NATIVE_TRUE@test_macho_LDADD = libbacktrace_noformat.la macho.lo @NATIVE_TRUE@test_xcoff_32_SOURCES = test_format.c testlib.c @NATIVE_TRUE@test_xcoff_32_CFLAGS = $(libbacktrace_TEST_CFLAGS) @NATIVE_TRUE@test_xcoff_32_LDADD = libbacktrace_noformat.la xcoff_32.lo @@ -1013,10 +1036,10 @@ libbacktrace_TEST_CFLAGS = $(EXTRA_FLAGS) $(WARN_FLAGS) -g @HAVE_ELF_TRUE@@NATIVE_TRUE@ztest_SOURCES = ztest.c testlib.c @HAVE_ELF_TRUE@@NATIVE_TRUE@ztest_CFLAGS = $(libbacktrace_TEST_CFLAGS) -DSRCDIR=\"$(srcdir)\" @HAVE_ELF_TRUE@@NATIVE_TRUE@ztest_LDADD = libbacktrace.la \ -@HAVE_ELF_TRUE@@NATIVE_TRUE@ $(am__append_13) \ +@HAVE_ELF_TRUE@@NATIVE_TRUE@ $(am__append_14) \ @HAVE_ELF_TRUE@@NATIVE_TRUE@ $(CLOCK_GETTIME_LINK) @HAVE_ELF_TRUE@@NATIVE_TRUE@ztest_alloc_LDADD = libbacktrace_alloc.la \ -@HAVE_ELF_TRUE@@NATIVE_TRUE@ $(am__append_14) \ +@HAVE_ELF_TRUE@@NATIVE_TRUE@ $(am__append_15) \ @HAVE_ELF_TRUE@@NATIVE_TRUE@ $(CLOCK_GETTIME_LINK) @HAVE_ELF_TRUE@@NATIVE_TRUE@ztest_alloc_SOURCES = $(ztest_SOURCES) @HAVE_ELF_TRUE@@NATIVE_TRUE@ztest_alloc_CFLAGS = $(ztest_CFLAGS) @@ -1251,6 +1274,10 @@ test_elf_64$(EXEEXT): $(test_elf_64_OBJECTS) $(test_elf_64_DEPENDENCIES) $(EXTRA @rm -f test_elf_64$(EXEEXT) $(AM_V_CCLD)$(test_elf_64_LINK) $(test_elf_64_OBJECTS) $(test_elf_64_LDADD) $(LIBS) +test_macho$(EXEEXT): $(test_macho_OBJECTS) $(test_macho_DEPENDENCIES) $(EXTRA_test_macho_DEPENDENCIES) + @rm -f test_macho$(EXEEXT) + $(AM_V_CCLD)$(test_macho_LINK) $(test_macho_OBJECTS) $(test_macho_LDADD) $(LIBS) + test_pecoff$(EXEEXT): $(test_pecoff_OBJECTS) $(test_pecoff_DEPENDENCIES) $(EXTRA_test_pecoff_DEPENDENCIES) @rm -f test_pecoff$(EXEEXT) $(AM_V_CCLD)$(test_pecoff_LINK) $(test_pecoff_OBJECTS) $(test_pecoff_LDADD) $(LIBS) @@ -1522,6 +1549,18 @@ test_elf_64-testlib.o: testlib.c test_elf_64-testlib.obj: testlib.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_elf_64_CFLAGS) $(CFLAGS) -c -o test_elf_64-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` +test_macho-test_format.o: test_format.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_macho_CFLAGS) $(CFLAGS) -c -o test_macho-test_format.o `test -f 'test_format.c' || echo '$(srcdir)/'`test_format.c + +test_macho-test_format.obj: test_format.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_macho_CFLAGS) $(CFLAGS) -c -o test_macho-test_format.obj `if test -f 'test_format.c'; then $(CYGPATH_W) 'test_format.c'; else $(CYGPATH_W) '$(srcdir)/test_format.c'; fi` + +test_macho-testlib.o: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_macho_CFLAGS) $(CFLAGS) -c -o test_macho-testlib.o `test -f 'testlib.c' || echo '$(srcdir)/'`testlib.c + +test_macho-testlib.obj: testlib.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_macho_CFLAGS) $(CFLAGS) -c -o test_macho-testlib.obj `if test -f 'testlib.c'; then $(CYGPATH_W) 'testlib.c'; else $(CYGPATH_W) '$(srcdir)/testlib.c'; fi` + test_pecoff-test_format.o: test_format.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_pecoff_CFLAGS) $(CFLAGS) -c -o test_pecoff-test_format.o `test -f 'test_format.c' || echo '$(srcdir)/'`test_format.c @@ -1840,7 +1879,7 @@ check-TESTS: log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ exit $$?; -recheck: all $(check_LTLIBRARIES) $(check_PROGRAMS) +recheck: all $(check_LTLIBRARIES) $(check_PROGRAMS) $(check_DATA) @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) @set +e; $(am__set_TESTS_bases); \ bases=`for i in $$bases; do echo $$i; done \ @@ -1907,6 +1946,13 @@ test_elf_64.log: test_elf_64$(EXEEXT) --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) +test_macho.log: test_macho$(EXEEXT) + @p='test_macho$(EXEEXT)'; \ + b='test_macho'; \ + $(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) test_xcoff_32.log: test_xcoff_32$(EXEEXT) @p='test_xcoff_32$(EXEEXT)'; \ b='test_xcoff_32'; \ @@ -2083,7 +2129,8 @@ dwarf5_alloc.log: dwarf5_alloc$(EXEEXT) @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) check-am: all-am - $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) $(check_LTLIBRARIES) $(check_PROGRAMS) \ + $(check_DATA) $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile $(LTLIBRARIES) config.h all-local @@ -2218,6 +2265,9 @@ uninstall-am: .PRECIOUS: Makefile +@HAVE_DSYMUTIL_TRUE@%.dSYM: % +@HAVE_DSYMUTIL_TRUE@ $(DSYMUTIL) %< + @HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@elf_for_test.c: elf.c @HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@ SEARCH='^#define SYSTEM_BUILD_ID_DIR.*$$'; \ @HAVE_ELF_TRUE@@HAVE_OBJCOPY_DEBUGLINK_TRUE@@NATIVE_TRUE@ REPLACE="#define SYSTEM_BUILD_ID_DIR \"$(TEST_BUILD_ID_DIR)\""; \ @@ -2281,6 +2331,7 @@ dwarf.lo: config.h $(INCDIR)/dwarf2.h $(INCDIR)/dwarf2.def \ $(INCDIR)/filenames.h backtrace.h internal.h elf.lo: config.h backtrace.h internal.h fileline.lo: config.h backtrace.h internal.h +macho.lo: config.h backtrace.h internal.h mmap.lo: config.h backtrace.h internal.h mmapio.lo: config.h backtrace.h internal.h nounwind.lo: config.h internal.h diff --git a/libbacktrace/config.h.in b/libbacktrace/config.h.in index 034ef81c3ec..f6e3cba18d0 100644 --- a/libbacktrace/config.h.in +++ b/libbacktrace/config.h.in @@ -51,6 +51,9 @@ /* Define to 1 if you have the `lstat' function. */ #undef HAVE_LSTAT +/* Define to 1 if you have the header file. */ +#undef HAVE_MACH_O_DYLD_H + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H diff --git a/libbacktrace/configure b/libbacktrace/configure index 33ce5891e6e..980c9d2bf89 100755 --- a/libbacktrace/configure +++ b/libbacktrace/configure @@ -635,6 +635,8 @@ LTLIBOBJS LIBOBJS NATIVE_FALSE NATIVE_TRUE +HAVE_DSYMUTIL_FALSE +HAVE_DSYMUTIL_TRUE HAVE_OBJCOPY_DEBUGLINK_FALSE HAVE_OBJCOPY_DEBUGLINK_TRUE READELF @@ -800,7 +802,8 @@ LDFLAGS LIBS CPPFLAGS CPP -OBJCOPY' +OBJCOPY +DSYMUTIL' # Initialize some variables set by options. @@ -1454,6 +1457,7 @@ Some influential environment variables: you have headers in a nonstandard directory CPP C preprocessor OBJCOPY location of objcopy + DSYMUTIL location of dsymutil Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -11499,7 +11503,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11502 "configure" +#line 11506 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -11605,7 +11609,7 @@ else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF -#line 11608 "configure" +#line 11612 "configure" #include "confdefs.h" #if HAVE_DLFCN_H @@ -12496,6 +12500,7 @@ FORMAT_FILE= backtrace_supports_data=yes case "$libbacktrace_cv_sys_filetype" in elf*) FORMAT_FILE="elf.lo" ;; +macho) FORMAT_FILE="macho.lo" ;; pecoff) FORMAT_FILE="pecoff.lo" backtrace_supports_data=no ;; @@ -13069,6 +13074,20 @@ $as_echo "#define HAVE_DL_ITERATE_PHDR 1" >>confdefs.h fi +# Check for header file for Mach-O image functions. +for ac_header in mach-o/dyld.h +do : + ac_fn_c_check_header_mongrel "$LINENO" "mach-o/dyld.h" "ac_cv_header_mach_o_dyld_h" "$ac_includes_default" +if test "x$ac_cv_header_mach_o_dyld_h" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_MACH_O_DYLD_H 1 +_ACEOF + +fi + +done + + # Check for loadquery. for ac_header in sys/ldr.h do : @@ -13553,6 +13572,8 @@ if ${libbacktrace_cv_objcopy_debuglink+:} false; then : else if test -n "${with_target_subdir}"; then libbacktrace_cv_objcopy_debuglink=no +elif ! test -n "${OBJCOPY}"; then + libbacktrace_cv_objcopy_debuglink=no elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then rm -f /tmp/ls$$ libbacktrace_cv_objcopy_debuglink=yes @@ -13571,6 +13592,53 @@ else fi + +# Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_DSYMUTIL+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test -n "${DSYMUTIL}"; then + HAVE_DSYMUTIL_TRUE= + HAVE_DSYMUTIL_FALSE='#' +else + HAVE_DSYMUTIL_TRUE='#' + HAVE_DSYMUTIL_FALSE= +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether tests can run" >&5 $as_echo_n "checking whether tests can run... " >&6; } if ${libbacktrace_cv_sys_native+:} false; then : @@ -13783,6 +13851,10 @@ if test -z "${HAVE_OBJCOPY_DEBUGLINK_TRUE}" && test -z "${HAVE_OBJCOPY_DEBUGLINK as_fn_error $? "conditional \"HAVE_OBJCOPY_DEBUGLINK\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HAVE_DSYMUTIL_TRUE}" && test -z "${HAVE_DSYMUTIL_FALSE}"; then + as_fn_error $? "conditional \"HAVE_DSYMUTIL\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${NATIVE_TRUE}" && test -z "${NATIVE_FALSE}"; then as_fn_error $? "conditional \"NATIVE\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/libbacktrace/configure.ac b/libbacktrace/configure.ac index 5beed68ccf6..6f241c5bac0 100644 --- a/libbacktrace/configure.ac +++ b/libbacktrace/configure.ac @@ -238,6 +238,7 @@ FORMAT_FILE= backtrace_supports_data=yes case "$libbacktrace_cv_sys_filetype" in elf*) FORMAT_FILE="elf.lo" ;; +macho) FORMAT_FILE="macho.lo" ;; pecoff) FORMAT_FILE="pecoff.lo" backtrace_supports_data=no ;; @@ -340,6 +341,9 @@ if test "$have_dl_iterate_phdr" = "yes"; then AC_DEFINE(HAVE_DL_ITERATE_PHDR, 1, [Define if dl_iterate_phdr is available.]) fi +# Check for header file for Mach-O image functions. +AC_CHECK_HEADERS(mach-o/dyld.h) + # Check for loadquery. AC_CHECK_HEADERS(sys/ldr.h) if test "$ac_cv_header_sys_ldr_h" = "no"; then @@ -483,6 +487,8 @@ AC_CACHE_CHECK([whether objcopy supports debuglink], [libbacktrace_cv_objcopy_debuglink], [if test -n "${with_target_subdir}"; then libbacktrace_cv_objcopy_debuglink=no +elif ! test -n "${OBJCOPY}"; then + libbacktrace_cv_objcopy_debuglink=no elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then rm -f /tmp/ls$$ libbacktrace_cv_objcopy_debuglink=yes @@ -491,6 +497,10 @@ else fi]) AM_CONDITIONAL(HAVE_OBJCOPY_DEBUGLINK, test "$libbacktrace_cv_objcopy_debuglink" = yes) +AC_ARG_VAR(DSYMUTIL, [location of dsymutil]) +AC_CHECK_PROG(DSYMUTIL, dsymutil, dsymutil) +AM_CONDITIONAL(HAVE_DSYMUTIL, test -n "${DSYMUTIL}") + AC_CACHE_CHECK([whether tests can run], [libbacktrace_cv_sys_native], [AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])], diff --git a/libbacktrace/filetype.awk b/libbacktrace/filetype.awk index cf6e1b695d2..14d91581f7e 100644 --- a/libbacktrace/filetype.awk +++ b/libbacktrace/filetype.awk @@ -1,8 +1,13 @@ # An awk script to determine the type of a file. -/\177ELF\001/ { if (NR == 1) { print "elf32"; exit } } -/\177ELF\002/ { if (NR == 1) { print "elf64"; exit } } -/\114\001/ { if (NR == 1) { print "pecoff"; exit } } -/\144\206/ { if (NR == 1) { print "pecoff"; exit } } -/\001\337/ { if (NR == 1) { print "xcoff32"; exit } } -/\001\367/ { if (NR == 1) { print "xcoff64"; exit } } - +/\177ELF\001/ { if (NR == 1) { print "elf32"; exit } } +/\177ELF\002/ { if (NR == 1) { print "elf64"; exit } } +/\114\001/ { if (NR == 1) { print "pecoff"; exit } } +/\144\206/ { if (NR == 1) { print "pecoff"; exit } } +/\001\337/ { if (NR == 1) { print "xcoff32"; exit } } +/\001\367/ { if (NR == 1) { print "xcoff64"; exit } } +/\376\355\372\316/ { if (NR == 1) { print "macho"; exit } } +/\316\372\355\376/ { if (NR == 1) { print "macho"; exit } } +/\376\355\372\317/ { if (NR == 1) { print "macho"; exit } } +/\317\372\355\376/ { if (NR == 1) { print "macho"; exit } } +/\312\376\272\276/ { if (NR == 1) { print "macho"; exit } } +/\276\272\376\312/ { if (NR == 1) { print "macho"; exit } } diff --git a/libbacktrace/macho.c b/libbacktrace/macho.c new file mode 100644 index 00000000000..3aea70cdbbe --- /dev/null +++ b/libbacktrace/macho.c @@ -0,0 +1,1309 @@ +/* elf.c -- Get debug data from a Mach-O file for backtraces. + Copyright (C) 2020 Free Software Foundation, Inc. + Written by Ian Lance Taylor, Google. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + (1) Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + (2) 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. + + (3) The name of the author may not be used to + endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 "config.h" + +#include +#include +#include +#include + +#ifdef HAVE_MACH_O_DYLD_H +#include +#endif + +#include "backtrace.h" +#include "internal.h" + +/* Mach-O file header for a 32-bit executable. */ + +struct macho_header_32 +{ + uint32_t magic; /* Magic number (MACH_O_MAGIC_32) */ + uint32_t cputype; /* CPU type */ + uint32_t cpusubtype; /* CPU subtype */ + uint32_t filetype; /* Type of file (object, executable) */ + uint32_t ncmds; /* Number of load commands */ + uint32_t sizeofcmds; /* Total size of load commands */ + uint32_t flags; /* Flags for special features */ +}; + +/* Mach-O file header for a 64-bit executable. */ + +struct macho_header_64 +{ + uint32_t magic; /* Magic number (MACH_O_MAGIC_64) */ + uint32_t cputype; /* CPU type */ + uint32_t cpusubtype; /* CPU subtype */ + uint32_t filetype; /* Type of file (object, executable) */ + uint32_t ncmds; /* Number of load commands */ + uint32_t sizeofcmds; /* Total size of load commands */ + uint32_t flags; /* Flags for special features */ + uint32_t reserved; /* Reserved */ +}; + +/* Mach-O file header for a fat executable. */ + +struct macho_header_fat +{ + uint32_t magic; /* Magic number (MACH_O_MH_MAGIC_FAT) */ + uint32_t nfat_arch; /* Number of components */ +}; + +/* Values for the header magic field. */ + +#define MACH_O_MH_MAGIC_32 0xfeedface +#define MACH_O_MH_MAGIC_64 0xfeedfacf +#define MACH_O_MH_MAGIC_FAT 0xcafebabe +#define MACH_O_MH_CIGAM_FAT 0xbebafeca + +/* Value for the header filetype field. */ + +#define MACH_O_MH_EXECUTE 0x02 +#define MACH_O_MH_DYLIB 0x06 +#define MACH_O_MH_DSYM 0x0a + +/* A component of a fat file. A fat file starts with a + macho_header_fat followed by nfat_arch instances of this + struct. */ + +struct macho_fat_arch +{ + uint32_t cputype; /* CPU type */ + uint32_t cpusubtype; /* CPU subtype */ + uint32_t offset; /* File offset of this entry */ + uint32_t size; /* Size of this entry */ + uint32_t align; /* Alignment of this entry */ +}; + +/* Values for the fat_arch cputype field (and the header cputype + field). */ + +#define MACH_O_CPU_ARCH_ABI64 0x01000000 + +#define MACH_O_CPU_TYPE_X86 7 +#define MACH_O_CPU_TYPE_ARM 12 + +#define MACH_O_CPU_TYPE_X86_64 (MACH_O_CPU_TYPE_X86 | MACH_O_CPU_ARCH_ABI64) +#define MACH_O_CPU_TYPE_ARM64 (MACH_O_CPU_TYPE_ARM | MACH_O_CPU_ARCH_ABI64) + +/* The header of a load command. */ + +struct macho_load_command +{ + uint32_t cmd; /* The type of load command */ + uint32_t cmdsize; /* Size in bytes of the entire command */ +}; + +/* Values for the load_command cmd field. */ + +#define MACH_O_LC_SEGMENT 0x01 +#define MACH_O_LC_SYMTAB 0x02 +#define MACH_O_LC_SEGMENT_64 0x19 +#define MACH_O_LC_UUID 0x1b + +/* The length of a section of segment name. */ + +#define MACH_O_NAMELEN (16) + +/* LC_SEGMENT load command. */ + +struct macho_segment_command +{ + uint32_t cmd; /* The type of load command (LC_SEGMENT) */ + uint32_t cmdsize; /* Size in bytes of the entire command */ + char segname[MACH_O_NAMELEN]; /* Segment name */ + uint32_t vmaddr; /* Virtual memory address */ + uint32_t vmsize; /* Virtual memory size */ + uint32_t fileoff; /* Offset of data to be mapped */ + uint32_t filesize; /* Size of data in file */ + uint32_t maxprot; /* Maximum permitted virtual protection */ + uint32_t initprot; /* Initial virtual memory protection */ + uint32_t nsects; /* Number of sections in this segment */ + uint32_t flags; /* Flags */ +}; + +/* LC_SEGMENT_64 load command. */ + +struct macho_segment_64_command +{ + uint32_t cmd; /* The type of load command (LC_SEGMENT) */ + uint32_t cmdsize; /* Size in bytes of the entire command */ + char segname[MACH_O_NAMELEN]; /* Segment name */ + uint64_t vmaddr; /* Virtual memory address */ + uint64_t vmsize; /* Virtual memory size */ + uint64_t fileoff; /* Offset of data to be mapped */ + uint64_t filesize; /* Size of data in file */ + uint32_t maxprot; /* Maximum permitted virtual protection */ + uint32_t initprot; /* Initial virtual memory protection */ + uint32_t nsects; /* Number of sections in this segment */ + uint32_t flags; /* Flags */ +}; + +/* LC_SYMTAB load command. */ + +struct macho_symtab_command +{ + uint32_t cmd; /* The type of load command (LC_SEGMENT) */ + uint32_t cmdsize; /* Size in bytes of the entire command */ + uint32_t symoff; /* File offset of symbol table */ + uint32_t nsyms; /* Number of symbols */ + uint32_t stroff; /* File offset of string table */ + uint32_t strsize; /* String table size */ +}; + +/* The length of a Mach-O uuid. */ + +#define MACH_O_UUID_LEN (16) + +/* LC_UUID load command. */ + +struct macho_uuid_command +{ + uint32_t cmd; /* Type of load command (LC_UUID) */ + uint32_t cmdsize; /* Size in bytes of command */ + unsigned char uuid[MACH_O_UUID_LEN]; /* UUID */ +}; + +/* 32-bit section header within a LC_SEGMENT segment. */ + +struct macho_section +{ + char sectname[MACH_O_NAMELEN]; /* Section name */ + char segment[MACH_O_NAMELEN]; /* Segment of this section */ + uint32_t addr; /* Address in memory */ + uint32_t size; /* Section size */ + uint32_t offset; /* File offset */ + uint32_t align; /* Log2 of section alignment */ + uint32_t reloff; /* File offset of relocations */ + uint32_t nreloc; /* Number of relocs for this section */ + uint32_t flags; /* Flags */ + uint32_t reserved1; + uint32_t reserved2; +}; + +/* 64-bit section header within a LC_SEGMENT_64 segment. */ + +struct macho_section_64 +{ + char sectname[MACH_O_NAMELEN]; /* Section name */ + char segment[MACH_O_NAMELEN]; /* Segment of this section */ + uint64_t addr; /* Address in memory */ + uint64_t size; /* Section size */ + uint32_t offset; /* File offset */ + uint32_t align; /* Log2 of section alignment */ + uint32_t reloff; /* File offset of section relocations */ + uint32_t nreloc; /* Number of relocs for this section */ + uint32_t flags; /* Flags */ + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +}; + +/* 32-bit symbol data. */ + +struct macho_nlist +{ + uint32_t n_strx; /* Index of name in string table */ + uint8_t n_type; /* Type flag */ + uint8_t n_sect; /* Section number */ + uint16_t n_desc; /* Stabs description field */ + uint32_t n_value; /* Value */ +}; + +/* 64-bit symbol data. */ + +struct macho_nlist_64 +{ + uint32_t n_strx; /* Index of name in string table */ + uint8_t n_type; /* Type flag */ + uint8_t n_sect; /* Section number */ + uint16_t n_desc; /* Stabs description field */ + uint64_t n_value; /* Value */ +}; + +/* Value found in nlist n_type field. */ + +#define MACH_O_N_EXT 0x01 /* Extern symbol */ +#define MACH_O_N_ABS 0x02 /* Absolute symbol */ +#define MACH_O_N_SECT 0x0e /* Defined in section */ + +#define MACH_O_N_TYPE 0x0e /* Mask for type bits */ +#define MACH_O_N_STAB 0xe0 /* Stabs debugging symbol */ + +/* Information we keep for a Mach-O symbol. */ + +struct macho_symbol +{ + const char *name; /* Symbol name */ + uintptr_t address; /* Symbol address */ +}; + +/* Information to pass to macho_syminfo. */ + +struct macho_syminfo_data +{ + struct macho_syminfo_data *next; /* Next module */ + struct macho_symbol *symbols; /* Symbols sorted by address */ + size_t count; /* Number of symbols */ +}; + +/* Names of sections, indexed by enum dwarf_section in internal.h. */ + +static const char * const dwarf_section_names[DEBUG_MAX] = +{ + "__debug_info", + "__debug_line", + "__debug_abbrev", + "__debug_ranges", + "__debug_str", + "", /* DEBUG_ADDR */ + "__debug_str_offs", + "", /* DEBUG_LINE_STR */ + "__debug_rnglists" +}; + +/* Forward declaration. */ + +static int macho_add (struct backtrace_state *, const char *, int, off_t, + const unsigned char *, uintptr_t, int, + backtrace_error_callback, void *, fileline *, int *); + +/* A dummy callback function used when we can't find any debug info. */ + +static int +macho_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED, + uintptr_t pc ATTRIBUTE_UNUSED, + backtrace_full_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, "no debug info in Mach-O executable", -1); + return 0; +} + +/* A dummy callback function used when we can't find a symbol + table. */ + +static void +macho_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED, + uintptr_t addr ATTRIBUTE_UNUSED, + backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, + backtrace_error_callback error_callback, void *data) +{ + error_callback (data, "no symbol table in Mach-O executable", -1); +} + +/* Add a single DWARF section to DWARF_SECTIONS, if we need the + section. Returns 1 on success, 0 on failure. */ + +static int +macho_add_dwarf_section (struct backtrace_state *state, int descriptor, + const char *sectname, uint32_t offset, uint64_t size, + backtrace_error_callback error_callback, void *data, + struct dwarf_sections *dwarf_sections) +{ + int i; + + for (i = 0; i < (int) DEBUG_MAX; ++i) + { + if (dwarf_section_names[i][0] != '\0' + && strncmp (sectname, dwarf_section_names[i], MACH_O_NAMELEN) == 0) + { + struct backtrace_view section_view; + + /* FIXME: Perhaps it would be better to try to use a single + view to read all the DWARF data, as we try to do for + ELF. */ + + if (!backtrace_get_view (state, descriptor, offset, size, + error_callback, data, §ion_view)) + return 0; + dwarf_sections->data[i] = (const unsigned char *) section_view.data; + dwarf_sections->size[i] = size; + break; + } + } + return 1; +} + +/* Collect DWARF sections from a DWARF segment. Returns 1 on success, + 0 on failure. */ + +static int +macho_add_dwarf_segment (struct backtrace_state *state, int descriptor, + off_t offset, unsigned int cmd, const char *psecs, + size_t sizesecs, unsigned int nsects, + backtrace_error_callback error_callback, void *data, + struct dwarf_sections *dwarf_sections) +{ + size_t sec_header_size; + size_t secoffset; + unsigned int i; + + switch (cmd) + { + case MACH_O_LC_SEGMENT: + sec_header_size = sizeof (struct macho_section); + break; + case MACH_O_LC_SEGMENT_64: + sec_header_size = sizeof (struct macho_section_64); + break; + default: + abort (); + } + + secoffset = 0; + for (i = 0; i < nsects; ++i) + { + if (secoffset + sec_header_size > sizesecs) + { + error_callback (data, "section overflow withing segment", 0); + return 0; + } + + switch (cmd) + { + case MACH_O_LC_SEGMENT: + { + struct macho_section section; + + memcpy (§ion, psecs + secoffset, sizeof section); + macho_add_dwarf_section (state, descriptor, section.sectname, + offset + section.offset, section.size, + error_callback, data, dwarf_sections); + } + break; + + case MACH_O_LC_SEGMENT_64: + { + struct macho_section_64 section; + + memcpy (§ion, psecs + secoffset, sizeof section); + macho_add_dwarf_section (state, descriptor, section.sectname, + offset + section.offset, section.size, + error_callback, data, dwarf_sections); + } + break; + + default: + abort (); + } + + secoffset += sec_header_size; + } + + return 1; +} + +/* Compare struct macho_symbol for qsort. */ + +static int +macho_symbol_compare (const void *v1, const void *v2) +{ + const struct macho_symbol *m1 = (const struct macho_symbol *) v1; + const struct macho_symbol *m2 = (const struct macho_symbol *) v2; + + if (m1->address < m2->address) + return -1; + else if (m1->address > m2->address) + return 1; + else + return 0; +} + +/* Compare an address against a macho_symbol for bsearch. We allocate + one extra entry in the array so that this can safely look at the + next entry. */ + +static int +macho_symbol_search (const void *vkey, const void *ventry) +{ + const uintptr_t *key = (const uintptr_t *) vkey; + const struct macho_symbol *entry = (const struct macho_symbol *) ventry; + uintptr_t addr; + + addr = *key; + if (addr < entry->address) + return -1; + else if (entry->name[0] == '\0' + && entry->address == ~(uintptr_t) 0) + return -1; + else if ((entry + 1)->name[0] == '\0' + && (entry + 1)->address == ~(uintptr_t) 0) + return -1; + else if (addr >= (entry + 1)->address) + return 1; + else + return 0; +} + +/* Return whether the symbol type field indicates a symbol table entry + that we care about: a function or data symbol. */ + +static int +macho_defined_symbol (uint8_t type) +{ + if ((type & MACH_O_N_STAB) != 0) + return 0; + if ((type & MACH_O_N_EXT) != 0) + return 0; + switch (type & MACH_O_N_TYPE) + { + case MACH_O_N_ABS: + return 1; + case MACH_O_N_SECT: + return 1; + default: + return 0; + } +} + +/* Add symbol table information for a Mach-O file. */ + +static int +macho_add_symtab (struct backtrace_state *state, int descriptor, + uintptr_t base_address, int is_64, + off_t symoff, unsigned int nsyms, off_t stroff, + unsigned int strsize, + backtrace_error_callback error_callback, void *data) +{ + size_t symsize; + struct backtrace_view sym_view; + int sym_view_valid; + struct backtrace_view str_view; + int str_view_valid; + size_t ndefs; + size_t symtaboff; + unsigned int i; + size_t macho_symbol_size; + struct macho_symbol *macho_symbols; + unsigned int j; + struct macho_syminfo_data *sdata; + + sym_view_valid = 0; + str_view_valid = 0; + macho_symbol_size = 0; + macho_symbols = NULL; + + if (is_64) + symsize = sizeof (struct macho_nlist_64); + else + symsize = sizeof (struct macho_nlist); + + if (!backtrace_get_view (state, descriptor, symoff, nsyms * symsize, + error_callback, data, &sym_view)) + goto fail; + sym_view_valid = 1; + + if (!backtrace_get_view (state, descriptor, stroff, strsize, + error_callback, data, &str_view)) + return 0; + str_view_valid = 1; + + ndefs = 0; + symtaboff = 0; + for (i = 0; i < nsyms; ++i, symtaboff += symsize) + { + if (is_64) + { + struct macho_nlist_64 nlist; + + memcpy (&nlist, (const char *) sym_view.data + symtaboff, + sizeof nlist); + if (macho_defined_symbol (nlist.n_type)) + ++ndefs; + } + else + { + struct macho_nlist nlist; + + memcpy (&nlist, (const char *) sym_view.data + symtaboff, + sizeof nlist); + if (macho_defined_symbol (nlist.n_type)) + ++ndefs; + } + } + + /* Add 1 to ndefs to make room for a sentinel. */ + macho_symbol_size = (ndefs + 1) * sizeof (struct macho_symbol); + macho_symbols = ((struct macho_symbol *) + backtrace_alloc (state, macho_symbol_size, error_callback, + data)); + if (macho_symbols == NULL) + goto fail; + + j = 0; + symtaboff = 0; + for (i = 0; i < nsyms; ++i, symtaboff += symsize) + { + uint32_t strx; + uint64_t value; + const char *name; + + strx = 0; + value = 0; + if (is_64) + { + struct macho_nlist_64 nlist; + + memcpy (&nlist, (const char *) sym_view.data + symtaboff, + sizeof nlist); + if (!macho_defined_symbol (nlist.n_type)) + continue; + + strx = nlist.n_strx; + value = nlist.n_value; + } + else + { + struct macho_nlist nlist; + + memcpy (&nlist, (const char *) sym_view.data + symtaboff, + sizeof nlist); + if (!macho_defined_symbol (nlist.n_type)) + continue; + + strx = nlist.n_strx; + value = nlist.n_value; + } + + if (strx >= strsize) + { + error_callback (data, "symbol string index out of range", 0); + goto fail; + } + + name = (const char *) str_view.data + strx; + if (name[0] == '_') + ++name; + macho_symbols[j].name = name; + macho_symbols[j].address = value + base_address; + ++j; + } + + sdata = ((struct macho_syminfo_data *) + backtrace_alloc (state, sizeof *sdata, error_callback, data)); + if (sdata == NULL) + goto fail; + + /* We need to keep the string table since it holds the names, but we + can release the symbol table. */ + + backtrace_release_view (state, &sym_view, error_callback, data); + sym_view_valid = 0; + str_view_valid = 0; + + /* Add a trailing sentinel symbol. */ + macho_symbols[j].name = ""; + macho_symbols[j].address = ~(uintptr_t) 0; + + backtrace_qsort (macho_symbols, ndefs + 1, sizeof (struct macho_symbol), + macho_symbol_compare); + + sdata->next = NULL; + sdata->symbols = macho_symbols; + sdata->count = ndefs; + + if (!state->threaded) + { + struct macho_syminfo_data **pp; + + for (pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data; + *pp != NULL; + pp = &(*pp)->next) + ; + *pp = sdata; + } + else + { + while (1) + { + struct macho_syminfo_data **pp; + + pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data; + + while (1) + { + struct macho_syminfo_data *p; + + p = backtrace_atomic_load_pointer (pp); + + if (p == NULL) + break; + + pp = &p->next; + } + + if (__sync_bool_compare_and_swap (pp, NULL, sdata)) + break; + } + } + + return 1; + + fail: + if (macho_symbols != NULL) + backtrace_free (state, macho_symbols, macho_symbol_size, + error_callback, data); + if (sym_view_valid) + backtrace_release_view (state, &sym_view, error_callback, data); + if (str_view_valid) + backtrace_release_view (state, &str_view, error_callback, data); + return 0; +} + +/* Return the symbol name and value for an ADDR. */ + +static void +macho_syminfo (struct backtrace_state *state, uintptr_t addr, + backtrace_syminfo_callback callback, + backtrace_error_callback error_callback ATTRIBUTE_UNUSED, + void *data) +{ + struct macho_syminfo_data *sdata; + struct macho_symbol *sym; + + sym = NULL; + if (!state->threaded) + { + for (sdata = (struct macho_syminfo_data *) state->syminfo_data; + sdata != NULL; + sdata = sdata->next) + { + sym = ((struct macho_symbol *) + bsearch (&addr, sdata->symbols, sdata->count, + sizeof (struct macho_symbol), macho_symbol_search)); + if (sym != NULL) + break; + } + } + else + { + struct macho_syminfo_data **pp; + + pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data; + while (1) + { + sdata = backtrace_atomic_load_pointer (pp); + if (sdata == NULL) + break; + + sym = ((struct macho_symbol *) + bsearch (&addr, sdata->symbols, sdata->count, + sizeof (struct macho_symbol), macho_symbol_search)); + if (sym != NULL) + break; + + pp = &sdata->next; + } + } + + if (sym == NULL) + callback (data, addr, NULL, 0, 0); + else + callback (data, addr, sym->name, sym->address, 0); +} + +/* Look through a fat file to find the relevant executable. Returns 1 + on success, 0 on failure (in both cases descriptor is closed). */ + +static int +macho_add_fat (struct backtrace_state *state, const char *filename, + int descriptor, int swapped, off_t offset, + const unsigned char *match_uuid, uintptr_t base_address, + int skip_symtab, uint32_t nfat_arch, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym) +{ + int arch_view_valid; + unsigned int cputype; + struct backtrace_view arch_view; + size_t archoffset; + unsigned int i; + + arch_view_valid = 0; + +#if defined (__x86_64__) + cputype = MACH_O_CPU_TYPE_X86_64; +#elif defined (__i386__) + cputype = MACH_O_CPU_TYPE_X86; +#elif defined (__aarch64__) + cputype = MACH_O_CPU_TYPE_ARM64; +#elif defined (__arm__) + cputype = MACH_O_CPU_TYPE_ARM; +#else + error_callback (data, "unknown Mach-O architecture", 0); + goto fail; +#endif + + if (!backtrace_get_view (state, descriptor, offset, + nfat_arch * sizeof (struct macho_fat_arch), + error_callback, data, &arch_view)) + goto fail; + + archoffset = 0; + for (i = 0; i < nfat_arch; ++i) + { + struct macho_fat_arch fat_arch; + uint32_t fcputype; + + memcpy (&fat_arch, + ((const char *) arch_view.data + + i * sizeof (struct macho_fat_arch)), + sizeof fat_arch); + + fcputype = fat_arch.cputype; + if (swapped) + fcputype = __builtin_bswap32 (fcputype); + + if (fcputype == cputype) + { + uint32_t foffset; + + /* FIXME: What about cpusubtype? */ + foffset = fat_arch.offset; + if (swapped) + foffset = __builtin_bswap32 (foffset); + backtrace_release_view (state, &arch_view, error_callback, data); + return macho_add (state, filename, descriptor, foffset, match_uuid, + base_address, skip_symtab, error_callback, data, + fileline_fn, found_sym); + } + + archoffset += sizeof (struct macho_fat_arch); + } + + error_callback (data, "could not find executable in fat file", 0); + + fail: + if (arch_view_valid) + backtrace_release_view (state, &arch_view, error_callback, data); + if (descriptor != -1) + backtrace_close (descriptor, error_callback, data); + return 0; +} + +/* Look for the dsym file for FILENAME. This is called if FILENAME + does not have debug info or a symbol table. Returns 1 on success, + 0 on failure. */ + +static int +macho_add_dsym (struct backtrace_state *state, const char *filename, + uintptr_t base_address, const unsigned char *uuid, + backtrace_error_callback error_callback, void *data, + fileline* fileline_fn) +{ + const char *p; + const char *dirname; + char *diralc; + size_t dirnamelen; + const char *basename; + size_t basenamelen; + const char *dsymsuffixdir; + size_t dsymsuffixdirlen; + size_t dsymlen; + char *dsym; + char *ps; + int d; + int does_not_exist; + int dummy_found_sym; + + diralc = NULL; + dirnamelen = 0; + dsym = NULL; + dsymlen = 0; + + p = strrchr (filename, '/'); + if (p == NULL) + { + dirname = "."; + dirnamelen = 1; + basename = filename; + basenamelen = strlen (basename); + diralc = NULL; + } + else + { + dirnamelen = p - filename; + diralc = backtrace_alloc (state, dirnamelen + 1, error_callback, data); + if (diralc == NULL) + goto fail; + memcpy (diralc, filename, dirnamelen); + diralc[dirnamelen] = '\0'; + dirname = diralc; + basename = p + 1; + basenamelen = strlen (basename); + } + + dsymsuffixdir = ".dSYM/Contents/Resources/DWARF/"; + dsymsuffixdirlen = strlen (dsymsuffixdir); + + dsymlen = (dirnamelen + + basenamelen + + dsymsuffixdirlen + + basenamelen + + 1); + dsym = backtrace_alloc (state, dsymlen, error_callback, data); + if (dsym == NULL) + goto fail; + + ps = dsym; + memcpy (ps, dirname, dirnamelen); + ps += dirnamelen; + *ps++ = '/'; + memcpy (ps, basename, basenamelen); + ps += basenamelen; + memcpy (ps, dsymsuffixdir, dsymsuffixdirlen); + ps += dsymsuffixdirlen; + memcpy (ps, basename, basenamelen); + ps += basenamelen; + *ps = '\0'; + + if (diralc != NULL) + { + backtrace_free (state, diralc, dirnamelen, error_callback, data); + diralc = NULL; + } + + d = backtrace_open (dsym, error_callback, data, &does_not_exist); + if (d < 0) + { + /* The file does not exist, so we can't read the debug info. + Just return success. */ + backtrace_free (state, dsym, dsymlen, error_callback, data); + return 1; + } + + if (!macho_add (state, dsym, d, 0, uuid, base_address, 1, + error_callback, data, fileline_fn, &dummy_found_sym)) + goto fail; + + backtrace_free (state, dsym, dsymlen, error_callback, data); + + return 1; + + fail: + if (dsym != NULL) + backtrace_free (state, dsym, dsymlen, error_callback, data); + if (diralc != NULL) + backtrace_free (state, diralc, dirnamelen, error_callback, data); + return 0; +} + +/* Add the backtrace data for a Macho-O file. Returns 1 on success, 0 + on failure (in both cases descriptor is closed). + + FILENAME: the name of the executable. + DESCRIPTOR: an open descriptor for the executable, closed here. + OFFSET: the offset within the file of this executable, for fat files. + MATCH_UUID: if not NULL, UUID that must match. + BASE_ADDRESS: the load address of the executable. + SKIP_SYMTAB: if non-zero, ignore the symbol table; used for dSYM files. + FILELINE_FN: set to the fileline function, by backtrace_dwarf_add. + FOUND_SYM: set to non-zero if we found the symbol table. +*/ + +static int +macho_add (struct backtrace_state *state, const char *filename, int descriptor, + off_t offset, const unsigned char *match_uuid, + uintptr_t base_address, int skip_symtab, + backtrace_error_callback error_callback, void *data, + fileline *fileline_fn, int *found_sym) +{ + struct backtrace_view header_view; + struct macho_header_32 header; + off_t hdroffset; + int is_64; + struct backtrace_view cmds_view; + int cmds_view_valid; + struct dwarf_sections dwarf_sections; + int have_dwarf; + unsigned char uuid[MACH_O_UUID_LEN]; + int have_uuid; + size_t cmdoffset; + unsigned int i; + + *found_sym = 0; + + cmds_view_valid = 0; + + /* The 32-bit and 64-bit file headers start out the same, so we can + just always read the 32-bit version. A fat header is shorter but + it will always be followed by data, so it's OK to read extra. */ + + if (!backtrace_get_view (state, descriptor, offset, + sizeof (struct macho_header_32), + error_callback, data, &header_view)) + goto fail; + + memcpy (&header, header_view.data, sizeof header); + + backtrace_release_view (state, &header_view, error_callback, data); + + switch (header.magic) + { + case MACH_O_MH_MAGIC_32: + is_64 = 0; + hdroffset = offset + sizeof (struct macho_header_32); + break; + case MACH_O_MH_MAGIC_64: + is_64 = 1; + hdroffset = offset + sizeof (struct macho_header_64); + break; + case MACH_O_MH_MAGIC_FAT: + { + struct macho_header_fat fat_header; + + hdroffset = offset + sizeof (struct macho_header_fat); + memcpy (&fat_header, &header, sizeof fat_header); + return macho_add_fat (state, filename, descriptor, 0, hdroffset, + match_uuid, base_address, skip_symtab, + fat_header.nfat_arch, error_callback, data, + fileline_fn, found_sym); + } + case MACH_O_MH_CIGAM_FAT: + { + struct macho_header_fat fat_header; + uint32_t nfat_arch; + + hdroffset = offset + sizeof (struct macho_header_fat); + memcpy (&fat_header, &header, sizeof fat_header); + nfat_arch = __builtin_bswap32 (fat_header.nfat_arch); + return macho_add_fat (state, filename, descriptor, 1, hdroffset, + match_uuid, base_address, skip_symtab, + nfat_arch, error_callback, data, + fileline_fn, found_sym); + } + default: + error_callback (data, "executable file is not in Mach-O format", 0); + goto fail; + } + + switch (header.filetype) + { + case MACH_O_MH_EXECUTE: + case MACH_O_MH_DYLIB: + case MACH_O_MH_DSYM: + break; + default: + error_callback (data, "executable file is not an executable", 0); + goto fail; + } + + if (!backtrace_get_view (state, descriptor, hdroffset, header.sizeofcmds, + error_callback, data, &cmds_view)) + goto fail; + cmds_view_valid = 1; + + memset (&dwarf_sections, 0, sizeof dwarf_sections); + have_dwarf = 0; + memset (&uuid, 0, sizeof uuid); + have_uuid = 0; + + cmdoffset = 0; + for (i = 0; i < header.ncmds; ++i) + { + const char *pcmd; + struct macho_load_command load_command; + + if (cmdoffset + sizeof load_command > header.sizeofcmds) + break; + + pcmd = (const char *) cmds_view.data + cmdoffset; + memcpy (&load_command, pcmd, sizeof load_command); + + switch (load_command.cmd) + { + case MACH_O_LC_SEGMENT: + { + struct macho_segment_command segcmd; + + memcpy (&segcmd, pcmd, sizeof segcmd); + if (memcmp (segcmd.segname, + "__DWARF\0\0\0\0\0\0\0\0\0", + MACH_O_NAMELEN) == 0) + { + if (!macho_add_dwarf_segment (state, descriptor, offset, + load_command.cmd, + pcmd + sizeof segcmd, + (load_command.cmdsize + - sizeof segcmd), + segcmd.nsects, error_callback, + data, &dwarf_sections)) + goto fail; + have_dwarf = 1; + } + } + break; + + case MACH_O_LC_SEGMENT_64: + { + struct macho_segment_64_command segcmd; + + memcpy (&segcmd, pcmd, sizeof segcmd); + if (memcmp (segcmd.segname, + "__DWARF\0\0\0\0\0\0\0\0\0", + MACH_O_NAMELEN) == 0) + { + if (!macho_add_dwarf_segment (state, descriptor, offset, + load_command.cmd, + pcmd + sizeof segcmd, + (load_command.cmdsize + - sizeof segcmd), + segcmd.nsects, error_callback, + data, &dwarf_sections)) + goto fail; + have_dwarf = 1; + } + } + break; + + case MACH_O_LC_SYMTAB: + if (!skip_symtab) + { + struct macho_symtab_command symcmd; + + memcpy (&symcmd, pcmd, sizeof symcmd); + if (!macho_add_symtab (state, descriptor, base_address, is_64, + offset + symcmd.symoff, symcmd.nsyms, + offset + symcmd.stroff, symcmd.strsize, + error_callback, data)) + goto fail; + + *found_sym = 1; + } + break; + + case MACH_O_LC_UUID: + { + struct macho_uuid_command uuidcmd; + + memcpy (&uuidcmd, pcmd, sizeof uuidcmd); + memcpy (&uuid[0], &uuidcmd.uuid[0], MACH_O_UUID_LEN); + have_uuid = 1; + } + break; + + default: + break; + } + + cmdoffset += load_command.cmdsize; + } + + if (!backtrace_close (descriptor, error_callback, data)) + goto fail; + descriptor = -1; + + backtrace_release_view (state, &cmds_view, error_callback, data); + cmds_view_valid = 0; + + if (match_uuid != NULL) + { + /* If we don't have a UUID, or it doesn't match, just ignore + this file. */ + if (!have_uuid + || memcmp (match_uuid, &uuid[0], MACH_O_UUID_LEN) != 0) + return 1; + } + + if (have_dwarf) + { + int is_big_endian; + + is_big_endian = 0; +#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + is_big_endian = 1; +#endif +#endif + + if (!backtrace_dwarf_add (state, base_address, &dwarf_sections, + is_big_endian, NULL, error_callback, data, + fileline_fn, NULL)) + goto fail; + } + + if (!have_dwarf && have_uuid) + { + if (!macho_add_dsym (state, filename, base_address, &uuid[0], + error_callback, data, fileline_fn)) + goto fail; + } + + return 1; + + fail: + if (cmds_view_valid) + backtrace_release_view (state, &cmds_view, error_callback, data); + if (descriptor != -1) + backtrace_close (descriptor, error_callback, data); + return 0; +} + +#ifdef HAVE_MACH_O_DYLD_H + +/* Initialize the backtrace data we need from a Mach-O executable + using the dyld support functions. This closes descriptor. */ + +int +backtrace_initialize (struct backtrace_state *state, const char *filename, + int descriptor, backtrace_error_callback error_callback, + void *data, fileline *fileline_fn) +{ + uint32_t c; + uint32_t i; + int closed_descriptor; + int found_sym; + fileline macho_fileline_fn; + + closed_descriptor = 0; + found_sym = 0; + macho_fileline_fn = macho_nodebug; + + c = _dyld_image_count (); + for (i = 0; i < c; ++i) + { + uintptr_t base_address; + const char *name; + int d; + fileline mff; + int mfs; + + name = _dyld_get_image_name (i); + if (name == NULL) + continue; + + if (strcmp (name, filename) == 0 && !closed_descriptor) + { + d = descriptor; + closed_descriptor = 1; + } + else + { + int does_not_exist; + + d = backtrace_open (name, error_callback, data, &does_not_exist); + if (d < 0) + continue; + } + + base_address = _dyld_get_image_vmaddr_slide (i); + + mff = macho_nodebug; + if (!macho_add (state, name, d, 0, NULL, base_address, 0, + error_callback, data, &mff, &mfs)) + return 0; + + if (mff != macho_nodebug) + macho_fileline_fn = mff; + if (mfs) + found_sym = 1; + } + + if (!closed_descriptor) + backtrace_close (descriptor, error_callback, data); + + if (!state->threaded) + { + if (found_sym) + state->syminfo_fn = macho_syminfo; + else if (state->syminfo_fn == NULL) + state->syminfo_fn = macho_nosyms; + } + else + { + if (found_sym) + backtrace_atomic_store_pointer (&state->syminfo_fn, macho_syminfo); + else + (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, + macho_nosyms); + } + + if (!state->threaded) + *fileline_fn = state->fileline_fn; + else + *fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); + + if (*fileline_fn == NULL || *fileline_fn == macho_nodebug) + *fileline_fn = macho_fileline_fn; + + return 1; +} + +#else /* !defined (HAVE_MACH_O_DYLD_H) */ + +/* Initialize the backtrace data we need from a Mach-O executable + without using the dyld support functions. This closes + descriptor. */ + +int +backtrace_initialize (struct backtrace_state *state, const char *filename, + int descriptor, backtrace_error_callback error_callback, + void *data, fileline *fileline_fn) +{ + fileline macho_fileline_fn; + int found_sym; + + macho_fileline_fn = macho_nodebug; + if (!macho_add (state, filename, descriptor, 0, NULL, 0, 0, + error_callback, data, &macho_fileline_fn, &found_sym)) + return 0; + + if (!state->threaded) + { + if (found_sym) + state->syminfo_fn = macho_syminfo; + else if (state->syminfo_fn == NULL) + state->syminfo_fn = macho_nosyms; + } + else + { + if (found_sym) + backtrace_atomic_store_pointer (&state->syminfo_fn, macho_syminfo); + else + (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL, + macho_nosyms); + } + + if (!state->threaded) + *fileline_fn = state->fileline_fn; + else + *fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); + + if (*fileline_fn == NULL || *fileline_fn == macho_nodebug) + *fileline_fn = macho_fileline_fn; + + return 1; +} + +#endif /* !defined (HAVE_MACH_O_DYLD_H) */ -- 2.30.2