libbacktrace patch committed: Add Mach-O support
Ian Lance Taylor
iant@golang.org
Sat May 9 23:12:13 GMT 2020
This patch to libbacktrace adds Mach-O support. Bootstrapped and
tested on macOS. Most tests pass, but a couple still fail. This is
for PR 88745. Committed to master.
Ian
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.
-------------- next part --------------
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/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 <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, §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) */
More information about the Gcc-patches
mailing list