libbacktrace: add Mach-O support
authorIan Lance Taylor <iant@golang.org>
Sun, 16 Feb 2020 13:20:01 +0000 (05:20 -0800)
committerIan Lance Taylor <iant@golang.org>
Sat, 9 May 2020 23:09:37 +0000 (16:09 -0700)
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
libbacktrace/Makefile.am
libbacktrace/Makefile.in
libbacktrace/config.h.in
libbacktrace/configure
libbacktrace/configure.ac
libbacktrace/filetype.awk
libbacktrace/macho.c [new file with mode: 0644]

index 4b4f574fc754fc8c694c79fc5d24e282ef00232b..6fa15fc4e96a8d90ce96a4de3e7c4ae2eb6ac847 100644 (file)
@@ -1,3 +1,22 @@
+2020-05-09  Ian Lance Taylor  <iant@golang.org>
+
+       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  <iant@golang.org>
 
        * read.c (backtrace_get_view): Support short read.
index c73f6633a7685f81986e45399330cfbfffa2f978..d3a9ba8843ae98025dec5588a900e3fe28df9aa4 100644 (file)
@@ -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
index 1178ac4cc239a4559d8fe4a655ff77ce83a32ddb..9381adebebce7fb8fa20024639b497a6eca3a1d7 100644 (file)
@@ -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
index 034ef81c3ec4870bd3ec266924d3862fe67959eb..f6e3cba18d00e0a65a2d404a55d6ad5ba8d25208 100644 (file)
@@ -51,6 +51,9 @@
 /* Define to 1 if you have the `lstat' function. */
 #undef HAVE_LSTAT
 
+/* Define to 1 if you have the <mach-o/dyld.h> header file. */
+#undef HAVE_MACH_O_DYLD_H
+
 /* Define to 1 if you have the <memory.h> header file. */
 #undef HAVE_MEMORY_H
 
index 33ce5891e6edea38c6c335dd0bc58967bae08e22..980c9d2bf8940f6bd7b263445571becfce998bb5 100755 (executable)
@@ -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 <include dir>
   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
index 5beed68ccf6a85243466463d66913c957b4c99b4..6f241c5bac09fe49b04e745ae3e2ef0f7442178a 100644 (file)
@@ -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;])],
index cf6e1b695d23c0861c0b7bf803df5b9e5a9d50cb..14d91581f7e79f3b22f3676a1da912a6c722973a 100644 (file)
@@ -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 (file)
index 0000000..3aea70c
--- /dev/null
@@ -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 <sys/types.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef HAVE_MACH_O_DYLD_H
+#include <mach-o/dyld.h>
+#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, &section_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 (&section, 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 (&section, 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) */