1 /* macho.c -- Get debug data from an Mach-O file for backtraces.
2 Copyright (C) 2012-2016 Free Software Foundation, Inc.
3 Written by John Colanduoni.
5 Pending upstream pull request:
6 https://github.com/ianlancetaylor/libbacktrace/pull/2
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions are
12 (1) Redistributions of source code must retain the above copyright
13 notice, this list of conditions and the following disclaimer.
15 (2) Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in
17 the documentation and/or other materials provided with the
20 (3) The name of the author may not be used to
21 endorse or promote products derived from this software without
22 specific prior written permission.
24 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
25 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
28 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
30 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
32 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
33 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 POSSIBILITY OF SUCH DAMAGE. */
38 /* We can't use autotools to detect the pointer width of our program because
39 we may be building a fat Mach-O file containing both 32-bit and 64-bit
40 variants. However Mach-O runs a limited set of platforms so detection
41 via preprocessor is not difficult. */
45 #define BACKTRACE_BITS 64
47 #define BACKTRACE_BITS 32
50 #error Attempting to build Mach-O support on incorrect platform
53 #if defined(__x86_64__)
54 #define NATIVE_CPU_TYPE CPU_TYPE_X86_64
55 #elif defined(__i386__)
56 #define NATIVE_CPU_TYPE CPU_TYPE_X86
57 #elif defined(__aarch64__)
58 #define NATIVE_CPU_TYPE CPU_TYPE_ARM64
59 #elif defined(__arm__)
60 #define NATIVE_CPU_TYPE CPU_TYPE_ARM
62 #error Could not detect native Mach-O cpu_type_t
65 #include <sys/types.h>
66 #include <sys/syslimits.h>
68 #include <mach-o/loader.h>
69 #include <mach-o/nlist.h>
70 #include <mach-o/fat.h>
71 #include <mach-o/dyld.h>
72 #include <uuid/uuid.h>
76 #include "backtrace.h"
79 struct macho_commands_view
81 struct backtrace_view view;
82 uint32_t commands_count;
83 uint32_t commands_total_size;
98 static const char *const debug_section_names[DEBUG_MAX] =
107 struct found_dwarf_section
109 uint32_t file_offset;
111 const unsigned char *data;
114 /* Mach-O symbols don't have a length. As a result we have to infer it
115 by sorting the symbol addresses for each image and recording the
116 memory range attributed to each image. */
124 struct macho_syminfo_data
126 struct macho_syminfo_data *next;
127 struct macho_symbol *symbols;
134 macho_file_to_host_u16 (int file_bytes_swapped, uint16_t input)
136 if (file_bytes_swapped)
137 return (input >> 8) | (input << 8);
143 macho_file_to_host_u32 (int file_bytes_swapped, uint32_t input)
145 if (file_bytes_swapped)
147 return ((input >> 24) & 0x000000FF)
148 | ((input >> 8) & 0x0000FF00)
149 | ((input << 8) & 0x00FF0000)
150 | ((input << 24) & 0xFF000000);
159 macho_file_to_host_u64 (int file_bytes_swapped, uint64_t input)
161 if (file_bytes_swapped)
163 return macho_file_to_host_u32 (file_bytes_swapped,
164 (uint32_t) (input >> 32))
165 | (((uint64_t) macho_file_to_host_u32 (file_bytes_swapped,
166 (uint32_t) input)) << 32);
174 #if BACKTRACE_BITS == 64
175 #define macho_file_to_host_usize macho_file_to_host_u64
176 typedef struct mach_header_64 mach_header_native_t;
177 #define LC_SEGMENT_NATIVE LC_SEGMENT_64
178 typedef struct segment_command_64 segment_command_native_t;
179 typedef struct nlist_64 nlist_native_t;
180 typedef struct section_64 section_native_t;
181 #else /* BACKTRACE_BITS == 32 */
182 #define macho_file_to_host_usize macho_file_to_host_u32
183 typedef struct mach_header mach_header_native_t;
184 #define LC_SEGMENT_NATIVE LC_SEGMENT
185 typedef struct segment_command segment_command_native_t;
186 typedef struct nlist nlist_native_t;
187 typedef struct section section_native_t;
190 // Gets a view into a Mach-O image, taking any slice offset into account
192 macho_get_view (struct backtrace_state *state, int descriptor,
193 off_t offset, size_t size,
194 backtrace_error_callback error_callback,
195 void *data, struct macho_commands_view *commands_view,
196 struct backtrace_view *view)
198 return backtrace_get_view (state, descriptor,
199 commands_view->base_offset + offset, size,
200 error_callback, data, view);
204 macho_get_commands (struct backtrace_state *state, int descriptor,
205 backtrace_error_callback error_callback,
206 void *data, struct macho_commands_view *commands_view,
211 struct backtrace_view file_header_view;
212 int file_header_view_valid = 0;
213 struct backtrace_view fat_archs_view;
214 int fat_archs_view_valid = 0;
215 const mach_header_native_t *file_header;
216 uint64_t commands_offset;
220 if (!backtrace_get_view (state, descriptor, 0, sizeof (mach_header_native_t),
221 error_callback, data, &file_header_view))
223 file_header_view_valid = 1;
225 switch (*(uint32_t *) file_header_view.data)
228 if (BACKTRACE_BITS == 32)
229 commands_view->bytes_swapped = 0;
237 if (BACKTRACE_BITS == 32)
238 commands_view->bytes_swapped = 1;
246 if (BACKTRACE_BITS == 64)
247 commands_view->bytes_swapped = 0;
255 if (BACKTRACE_BITS == 64)
256 commands_view->bytes_swapped = 1;
265 commands_view->bytes_swapped = 0;
269 commands_view->bytes_swapped = 1;
277 uint32_t native_slice_offset;
278 size_t archs_total_size;
280 const struct fat_header *fat_header;
281 const struct fat_arch *archs;
284 fat_header = file_header_view.data;
286 macho_file_to_host_u32 (commands_view->bytes_swapped,
287 fat_header->nfat_arch);
289 archs_total_size = arch_count * sizeof (struct fat_arch);
291 if (!backtrace_get_view (state, descriptor, sizeof (struct fat_header),
292 archs_total_size, error_callback,
293 data, &fat_archs_view))
295 fat_archs_view_valid = 1;
297 native_slice_offset = 0;
298 archs = fat_archs_view.data;
299 for (i = 0; i < arch_count; i++)
301 const struct fat_arch *raw_arch = archs + i;
303 (int) macho_file_to_host_u32 (commands_view->bytes_swapped,
304 (uint32_t) raw_arch->cputype);
306 if (cpu_type == NATIVE_CPU_TYPE)
308 native_slice_offset =
309 macho_file_to_host_u32 (commands_view->bytes_swapped,
316 if (native_slice_offset == 0)
322 backtrace_release_view (state, &file_header_view, error_callback, data);
323 file_header_view_valid = 0;
324 if (!backtrace_get_view (state, descriptor, native_slice_offset,
325 sizeof (mach_header_native_t), error_callback,
326 data, &file_header_view))
328 file_header_view_valid = 1;
330 // The endianness of the slice may be different than the fat image
331 switch (*(uint32_t *) file_header_view.data)
334 if (BACKTRACE_BITS == 32)
335 commands_view->bytes_swapped = 0;
340 if (BACKTRACE_BITS == 32)
341 commands_view->bytes_swapped = 1;
346 if (BACKTRACE_BITS == 64)
347 commands_view->bytes_swapped = 0;
352 if (BACKTRACE_BITS == 64)
353 commands_view->bytes_swapped = 1;
361 commands_view->base_offset = native_slice_offset;
364 commands_view->base_offset = 0;
366 file_header = file_header_view.data;
367 commands_view->commands_count =
368 macho_file_to_host_u32 (commands_view->bytes_swapped,
370 commands_view->commands_total_size =
371 macho_file_to_host_u32 (commands_view->bytes_swapped,
372 file_header->sizeofcmds);
374 commands_view->base_offset + sizeof (mach_header_native_t);
376 if (!backtrace_get_view (state, descriptor, commands_offset,
377 commands_view->commands_total_size, error_callback,
378 data, &commands_view->view))
384 if (file_header_view_valid)
385 backtrace_release_view (state, &file_header_view, error_callback, data);
386 if (fat_archs_view_valid)
387 backtrace_release_view (state, &fat_archs_view, error_callback, data);
392 macho_get_uuid (struct backtrace_state *state ATTRIBUTE_UNUSED,
393 int descriptor ATTRIBUTE_UNUSED,
394 backtrace_error_callback error_callback,
395 void *data, struct macho_commands_view *commands_view,
401 for (i = 0; i < commands_view->commands_count; i++)
403 const struct load_command *raw_command;
404 struct load_command command;
406 if (offset + sizeof (struct load_command)
407 > commands_view->commands_total_size)
409 error_callback (data,
410 "executable file contains out of range command offset",
416 commands_view->view.data + offset;
417 command.cmd = macho_file_to_host_u32 (commands_view->bytes_swapped,
419 command.cmdsize = macho_file_to_host_u32 (commands_view->bytes_swapped,
420 raw_command->cmdsize);
422 if (command.cmd == LC_UUID)
424 const struct uuid_command *uuid_command;
426 if (offset + sizeof (struct uuid_command)
427 > commands_view->commands_total_size)
429 error_callback (data,
430 "executable file contains out of range command offset",
436 (struct uuid_command *) raw_command;
437 memcpy (uuid, uuid_command->uuid, sizeof (uuid_t));
441 offset += command.cmdsize;
444 error_callback (data, "executable file is missing an identifying UUID", 0);
448 /* Returns the base address of a Mach-O image, as encoded in the file header.
449 * WARNING: This does not take ASLR into account, which is ubiquitous on recent
453 macho_get_addr_range (struct backtrace_state *state ATTRIBUTE_UNUSED,
454 int descriptor ATTRIBUTE_UNUSED,
455 backtrace_error_callback error_callback,
456 void *data, struct macho_commands_view *commands_view,
457 uintptr_t *base_address, uintptr_t *max_address)
465 for (i = 0; i < commands_view->commands_count; i++)
467 const struct load_command *raw_command;
468 struct load_command command;
470 if (offset + sizeof (struct load_command)
471 > commands_view->commands_total_size)
473 error_callback (data,
474 "executable file contains out of range command offset",
479 raw_command = commands_view->view.data + offset;
480 command.cmd = macho_file_to_host_u32 (commands_view->bytes_swapped,
482 command.cmdsize = macho_file_to_host_u32 (commands_view->bytes_swapped,
483 raw_command->cmdsize);
485 if (command.cmd == LC_SEGMENT_NATIVE)
487 const segment_command_native_t *raw_segment;
488 uintptr_t segment_vmaddr;
489 uintptr_t segment_vmsize;
490 uintptr_t segment_maxaddr;
491 uintptr_t text_fileoff;
493 if (offset + sizeof (segment_command_native_t)
494 > commands_view->commands_total_size)
496 error_callback (data,
497 "executable file contains out of range command offset",
502 raw_segment = (segment_command_native_t *) raw_command;
504 segment_vmaddr = macho_file_to_host_usize (
505 commands_view->bytes_swapped, raw_segment->vmaddr);
506 segment_vmsize = macho_file_to_host_usize (
507 commands_view->bytes_swapped, raw_segment->vmsize);
508 segment_maxaddr = segment_vmaddr + segment_vmsize;
510 if (strncmp (raw_segment->segname, "__TEXT",
511 sizeof (raw_segment->segname)) == 0)
513 text_fileoff = macho_file_to_host_usize (
514 commands_view->bytes_swapped, raw_segment->fileoff);
515 *base_address = segment_vmaddr - text_fileoff;
520 if (segment_maxaddr > *max_address)
521 *max_address = segment_maxaddr;
524 offset += command.cmdsize;
531 error_callback (data, "executable is missing __TEXT segment", 0);
537 macho_symbol_compare_addr (const void *left_raw, const void *right_raw)
539 const struct macho_symbol *left = left_raw;
540 const struct macho_symbol *right = right_raw;
542 if (left->addr > right->addr)
544 else if (left->addr < right->addr)
551 macho_symbol_type_relevant (uint8_t type)
553 uint8_t type_field = (uint8_t) (type & N_TYPE);
555 return !(type & N_EXT) &&
556 (type_field == N_ABS || type_field == N_SECT);
560 macho_add_symtab (struct backtrace_state *state,
561 backtrace_error_callback error_callback,
562 void *data, int descriptor,
563 struct macho_commands_view *commands_view,
564 uintptr_t base_address, uintptr_t max_image_address,
565 intptr_t vmslide, int *found_sym)
567 struct macho_syminfo_data *syminfo_data;
571 struct backtrace_view symtab_view;
572 int symtab_view_valid = 0;
573 struct backtrace_view strtab_view;
574 int strtab_view_valid = 0;
575 size_t syminfo_index = 0;
576 size_t function_count = 0;
579 uint32_t symtab_index = 0;
583 for (i = 0; i < commands_view->commands_count; i++)
585 const struct load_command *raw_command;
586 struct load_command command;
588 if (offset + sizeof (struct load_command)
589 > commands_view->commands_total_size)
591 error_callback (data,
592 "executable file contains out of range command offset",
597 raw_command = commands_view->view.data + offset;
598 command.cmd = macho_file_to_host_u32 (commands_view->bytes_swapped,
600 command.cmdsize = macho_file_to_host_u32 (commands_view->bytes_swapped,
601 raw_command->cmdsize);
603 if (command.cmd == LC_SYMTAB)
605 const struct symtab_command *symtab_command;
606 uint32_t symbol_table_offset;
607 uint32_t symbol_count;
608 uint32_t string_table_offset;
609 uint32_t string_table_size;
611 if (offset + sizeof (struct symtab_command)
612 > commands_view->commands_total_size)
614 error_callback (data,
615 "executable file contains out of range command offset",
620 symtab_command = (struct symtab_command *) raw_command;
622 symbol_table_offset = macho_file_to_host_u32 (
623 commands_view->bytes_swapped, symtab_command->symoff);
624 symbol_count = macho_file_to_host_u32 (
625 commands_view->bytes_swapped, symtab_command->nsyms);
626 string_table_offset = macho_file_to_host_u32 (
627 commands_view->bytes_swapped, symtab_command->stroff);
628 string_table_size = macho_file_to_host_u32 (
629 commands_view->bytes_swapped, symtab_command->strsize);
632 if (!macho_get_view (state, descriptor, symbol_table_offset,
633 symbol_count * sizeof (nlist_native_t),
634 error_callback, data, commands_view,
637 symtab_view_valid = 1;
639 if (!macho_get_view (state, descriptor, string_table_offset,
640 string_table_size, error_callback, data,
641 commands_view, &strtab_view))
643 strtab_view_valid = 1;
645 // Count functions first
646 for (j = 0; j < symbol_count; j++)
648 const nlist_native_t *raw_sym =
649 ((const nlist_native_t *) symtab_view.data) + j;
651 if (macho_symbol_type_relevant (raw_sym->n_type))
657 // Allocate space for the:
658 // (a) macho_syminfo_data for this image
659 // (b) macho_symbol entries
661 backtrace_alloc (state,
662 sizeof (struct macho_syminfo_data),
663 error_callback, data);
664 if (syminfo_data == NULL)
667 syminfo_data->symbols = backtrace_alloc (
668 state, function_count * sizeof (struct macho_symbol),
669 error_callback, data);
670 if (syminfo_data->symbols == NULL)
673 syminfo_data->symbol_count = function_count;
674 syminfo_data->next = NULL;
675 syminfo_data->min_addr = base_address;
676 syminfo_data->max_addr = max_image_address;
678 for (symtab_index = 0;
679 symtab_index < symbol_count; symtab_index++)
681 const nlist_native_t *raw_sym =
682 ((const nlist_native_t *) symtab_view.data) +
685 if (macho_symbol_type_relevant (raw_sym->n_type))
689 size_t max_len_plus_one;
691 syminfo_data->symbols[syminfo_index].addr =
692 macho_file_to_host_usize (commands_view->bytes_swapped,
693 raw_sym->n_value) + vmslide;
695 strtab_index = macho_file_to_host_u32 (
696 commands_view->bytes_swapped,
697 raw_sym->n_un.n_strx);
699 // Check the range of the supposed "string" we've been
701 if (strtab_index >= string_table_size)
705 "dSYM file contains out of range string table index",
710 name = strtab_view.data + strtab_index;
711 max_len_plus_one = string_table_size - strtab_index;
713 if (strnlen (name, max_len_plus_one) >= max_len_plus_one)
717 "dSYM file contains unterminated string",
722 // Remove underscore prefixes
728 syminfo_data->symbols[syminfo_index].name = name;
734 backtrace_qsort (syminfo_data->symbols,
735 syminfo_data->symbol_count,
736 sizeof (struct macho_symbol),
737 macho_symbol_compare_addr);
739 // Calculate symbol sizes
740 for (syminfo_index = 0;
741 syminfo_index < syminfo_data->symbol_count; syminfo_index++)
743 if (syminfo_index + 1 < syminfo_data->symbol_count)
745 syminfo_data->symbols[syminfo_index].size =
746 syminfo_data->symbols[syminfo_index + 1].addr -
747 syminfo_data->symbols[syminfo_index].addr;
751 syminfo_data->symbols[syminfo_index].size =
753 syminfo_data->symbols[syminfo_index].addr;
757 if (!state->threaded)
759 struct macho_syminfo_data **pp;
761 for (pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data;
770 struct macho_syminfo_data **pp;
772 pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data;
776 struct macho_syminfo_data *p;
778 p = backtrace_atomic_load_pointer (pp);
786 if (__sync_bool_compare_and_swap (pp, NULL, syminfo_data))
791 strtab_view_valid = 0; // We need to keep string table around
797 offset += command.cmdsize;
800 // No symbol table here
805 if (symtab_view_valid)
806 backtrace_release_view (state, &symtab_view, error_callback, data);
807 if (strtab_view_valid)
808 backtrace_release_view (state, &strtab_view, error_callback, data);
813 macho_try_dwarf (struct backtrace_state *state,
814 backtrace_error_callback error_callback,
815 void *data, fileline *fileline_fn, uuid_t *executable_uuid,
816 uintptr_t base_address, uintptr_t max_image_address,
817 intptr_t vmslide, char *dwarf_filename, int *matched,
818 int *found_sym, int *found_dwarf)
823 int dwarf_descriptor;
824 int dwarf_descriptor_valid = 0;
825 struct macho_commands_view commands_view;
826 int commands_view_valid = 0;
827 struct backtrace_view dwarf_view;
828 int dwarf_view_valid = 0;
830 struct found_dwarf_section dwarf_sections[DEBUG_MAX];
831 uintptr_t min_dwarf_offset = 0;
832 uintptr_t max_dwarf_offset = 0;
841 if ((dwarf_descriptor = backtrace_open (dwarf_filename, error_callback,
844 dwarf_descriptor_valid = 1;
847 if (!macho_get_commands (state, dwarf_descriptor, error_callback, data,
848 &commands_view, &incompatible))
850 // Failing to read the header here is fine, because this dSYM may be
851 // for a different architecture
858 commands_view_valid = 1;
860 // Get dSYM UUID and compare
861 if (!macho_get_uuid (state, dwarf_descriptor, error_callback, data,
862 &commands_view, &dwarf_uuid))
864 error_callback (data, "dSYM file is missing an identifying uuid", 0);
867 if (memcmp (executable_uuid, &dwarf_uuid, sizeof (uuid_t)) != 0)
869 // DWARF doesn't belong to desired executable
877 if (!macho_add_symtab (state, error_callback, data, dwarf_descriptor,
878 &commands_view, base_address, max_image_address,
882 // Get DWARF sections
884 memset (dwarf_sections, 0, sizeof (dwarf_sections));
886 for (i = 0; i < commands_view.commands_count; i++)
888 const struct load_command *raw_command;
889 struct load_command command;
891 if (offset + sizeof (struct load_command)
892 > commands_view.commands_total_size)
894 error_callback (data,
895 "dSYM file contains out of range command offset", 0);
899 raw_command = commands_view.view.data + offset;
900 command.cmd = macho_file_to_host_u32 (commands_view.bytes_swapped,
902 command.cmdsize = macho_file_to_host_u32 (commands_view.bytes_swapped,
903 raw_command->cmdsize);
905 if (command.cmd == LC_SEGMENT_NATIVE)
907 uint32_t section_count;
908 size_t section_offset;
909 const segment_command_native_t *raw_segment;
911 if (offset + sizeof (segment_command_native_t)
912 > commands_view.commands_total_size)
914 error_callback (data,
915 "dSYM file contains out of range command offset",
920 raw_segment = (const segment_command_native_t *) raw_command;
922 if (strncmp (raw_segment->segname, "__DWARF",
923 sizeof (raw_segment->segname)) == 0)
925 section_count = macho_file_to_host_u32 (
926 commands_view.bytes_swapped,
927 raw_segment->nsects);
929 section_offset = offset + sizeof (segment_command_native_t);
931 // Search sections for relevant DWARF section names
932 for (j = 0; j < section_count; j++)
934 const section_native_t *raw_section;
936 if (section_offset + sizeof (section_native_t) >
937 commands_view.commands_total_size)
939 error_callback (data,
940 "dSYM file contains out of range command offset",
945 raw_section = commands_view.view.data + section_offset;
947 for (k = 0; k < DEBUG_MAX; k++)
949 uintptr_t dwarf_section_end;
951 if (strncmp (raw_section->sectname,
952 debug_section_names[k],
953 sizeof (raw_section->sectname)) == 0)
957 dwarf_sections[k].file_offset =
958 macho_file_to_host_u32 (
959 commands_view.bytes_swapped,
960 raw_section->offset);
961 dwarf_sections[k].file_size =
962 macho_file_to_host_usize (
963 commands_view.bytes_swapped,
966 if (min_dwarf_offset == 0 ||
967 dwarf_sections[k].file_offset <
969 min_dwarf_offset = dwarf_sections[k].file_offset;
972 dwarf_sections[k].file_offset +
973 dwarf_sections[k].file_size;
974 if (dwarf_section_end > max_dwarf_offset)
975 max_dwarf_offset = dwarf_section_end;
981 section_offset += sizeof (section_native_t);
988 offset += command.cmdsize;
993 // No DWARF in this file
998 if (!macho_get_view (state, dwarf_descriptor, (off_t) min_dwarf_offset,
999 max_dwarf_offset - min_dwarf_offset, error_callback,
1000 data, &commands_view, &dwarf_view))
1002 dwarf_view_valid = 1;
1004 for (i = 0; i < DEBUG_MAX; i++)
1006 if (dwarf_sections[i].file_offset == 0)
1007 dwarf_sections[i].data = NULL;
1009 dwarf_sections[i].data =
1010 dwarf_view.data + dwarf_sections[i].file_offset - min_dwarf_offset;
1013 if (!backtrace_dwarf_add (state, vmslide,
1014 dwarf_sections[DEBUG_INFO].data,
1015 dwarf_sections[DEBUG_INFO].file_size,
1016 dwarf_sections[DEBUG_LINE].data,
1017 dwarf_sections[DEBUG_LINE].file_size,
1018 dwarf_sections[DEBUG_ABBREV].data,
1019 dwarf_sections[DEBUG_ABBREV].file_size,
1020 dwarf_sections[DEBUG_RANGES].data,
1021 dwarf_sections[DEBUG_RANGES].file_size,
1022 dwarf_sections[DEBUG_STR].data,
1023 dwarf_sections[DEBUG_STR].file_size,
1024 ((__DARWIN_BYTE_ORDER == __DARWIN_BIG_ENDIAN)
1025 ^ commands_view.bytes_swapped),
1026 error_callback, data, fileline_fn))
1029 // Don't release the DWARF view because it is still in use
1030 dwarf_descriptor_valid = 0;
1031 dwarf_view_valid = 0;
1035 if (dwarf_descriptor_valid)
1036 backtrace_close (dwarf_descriptor, error_callback, data);
1037 if (commands_view_valid)
1038 backtrace_release_view (state, &commands_view.view, error_callback, data);
1039 if (dwarf_view_valid)
1040 backtrace_release_view (state, &dwarf_view, error_callback, data);
1045 macho_try_dsym (struct backtrace_state *state,
1046 backtrace_error_callback error_callback,
1047 void *data, fileline *fileline_fn, uuid_t *executable_uuid,
1048 uintptr_t base_address, uintptr_t max_image_address,
1049 intptr_t vmslide, char *dsym_filename, int *matched,
1050 int *found_sym, int *found_dwarf)
1053 char dwarf_image_dir_path[PATH_MAX];
1054 DIR *dwarf_image_dir;
1055 int dwarf_image_dir_valid = 0;
1056 struct dirent *directory_entry;
1057 char dwarf_filename[PATH_MAX];
1060 int dwarf_had_dwarf;
1066 strncpy (dwarf_image_dir_path, dsym_filename, PATH_MAX);
1067 strncat (dwarf_image_dir_path, "/Contents/Resources/DWARF", PATH_MAX);
1069 if (!(dwarf_image_dir = opendir (dwarf_image_dir_path)))
1071 error_callback (data, "could not open DWARF directory in dSYM",
1075 dwarf_image_dir_valid = 1;
1077 while ((directory_entry = readdir (dwarf_image_dir)))
1079 if (directory_entry->d_type != DT_REG)
1082 strncpy (dwarf_filename, dwarf_image_dir_path, PATH_MAX);
1083 strncat (dwarf_filename, "/", PATH_MAX);
1084 strncat (dwarf_filename, directory_entry->d_name, PATH_MAX);
1086 if (!macho_try_dwarf (state, error_callback, data, fileline_fn,
1087 executable_uuid, base_address, max_image_address,
1088 vmslide, dwarf_filename,
1089 &dwarf_matched, &dwarf_had_sym, &dwarf_had_dwarf))
1095 *found_sym = dwarf_had_sym;
1096 *found_dwarf = dwarf_had_dwarf;
1102 // No matching DWARF in this dSYM
1107 if (dwarf_image_dir_valid)
1108 closedir (dwarf_image_dir);
1113 macho_add (struct backtrace_state *state,
1114 backtrace_error_callback error_callback, void *data, int descriptor,
1115 const char *filename, fileline *fileline_fn, intptr_t vmslide,
1116 int *found_sym, int *found_dwarf)
1119 uintptr_t image_file_base_address;
1120 uintptr_t image_file_max_address;
1121 uintptr_t image_actual_base_address = 0;
1122 uintptr_t image_actual_max_address = 0;
1125 struct macho_commands_view commands_view;
1126 int commands_view_valid = 0;
1127 char executable_dirname[PATH_MAX];
1128 size_t filename_len;
1129 DIR *executable_dir = NULL;
1130 int executable_dir_valid = 0;
1131 struct dirent *directory_entry;
1132 char dsym_full_path[PATH_MAX];
1133 static const char *extension;
1134 size_t extension_len;
1140 // Find Mach-O commands list
1142 if (!macho_get_commands (state, descriptor, error_callback, data,
1143 &commands_view, &incompatible))
1145 commands_view_valid = 1;
1147 // First we need to get the uuid of our file so we can hunt down the correct
1149 if (!macho_get_uuid (state, descriptor, error_callback, data, &commands_view,
1153 // Now we need to find the in memory base address. Step one is to find out
1154 // what the executable thinks the base address is
1155 if (!macho_get_addr_range (state, descriptor, error_callback, data,
1157 &image_file_base_address,
1158 &image_file_max_address))
1161 image_actual_base_address =
1162 image_file_base_address + vmslide;
1163 image_actual_max_address =
1164 image_file_max_address + vmslide;
1166 if (image_actual_base_address == 0)
1168 error_callback (data, "executable file is not loaded", 0);
1172 // Look for dSYM in our executable's directory
1173 strncpy (executable_dirname, filename, PATH_MAX);
1174 filename_len = strlen (executable_dirname);
1175 for (i = filename_len - 1; i >= 0; i--)
1177 if (executable_dirname[i] == '/')
1179 executable_dirname[i] = '\0';
1184 executable_dirname[0] = '.';
1185 executable_dirname[1] = '\0';
1190 if (!(executable_dir = opendir (executable_dirname)))
1192 error_callback (data, "could not open directory containing executable",
1196 executable_dir_valid = 1;
1198 extension = ".dSYM";
1199 extension_len = strlen (extension);
1200 while ((directory_entry = readdir (executable_dir)))
1202 if (directory_entry->d_namlen < extension_len)
1204 if (strncasecmp (directory_entry->d_name + directory_entry->d_namlen
1205 - extension_len, extension, extension_len) == 0)
1212 strncpy (dsym_full_path, executable_dirname, PATH_MAX);
1213 strncat (dsym_full_path, "/", PATH_MAX);
1214 strncat (dsym_full_path, directory_entry->d_name, PATH_MAX);
1216 if (!macho_try_dsym (state, error_callback, data,
1217 fileline_fn, &image_uuid,
1218 image_actual_base_address,
1219 image_actual_max_address, vmslide,
1221 &matched, &dsym_had_sym, &dsym_had_dwarf))
1226 *found_sym = dsym_had_sym;
1227 *found_dwarf = dsym_had_dwarf;
1239 if (commands_view_valid)
1240 backtrace_release_view (state, &commands_view.view, error_callback,
1242 if (executable_dir_valid)
1243 closedir (executable_dir);
1248 macho_symbol_search (const void *vkey, const void *ventry)
1250 const uintptr_t *key = (const uintptr_t *) vkey;
1251 const struct macho_symbol *entry = (const struct macho_symbol *) ventry;
1255 if (addr < entry->addr)
1257 else if (addr >= entry->addr + entry->size)
1264 macho_syminfo (struct backtrace_state *state,
1266 backtrace_syminfo_callback callback,
1267 backtrace_error_callback error_callback ATTRIBUTE_UNUSED,
1270 struct macho_syminfo_data *edata;
1271 struct macho_symbol *sym = NULL;
1273 if (!state->threaded)
1275 for (edata = (struct macho_syminfo_data *) state->syminfo_data;
1277 edata = edata->next)
1279 if (addr >= edata->min_addr && addr <= edata->max_addr)
1281 sym = ((struct macho_symbol *)
1282 bsearch (&addr, edata->symbols, edata->symbol_count,
1283 sizeof (struct macho_symbol), macho_symbol_search));
1291 struct macho_syminfo_data **pp;
1293 pp = (struct macho_syminfo_data **) (void *) &state->syminfo_data;
1296 edata = backtrace_atomic_load_pointer (pp);
1300 if (addr >= edata->min_addr && addr <= edata->max_addr)
1302 sym = ((struct macho_symbol *)
1303 bsearch (&addr, edata->symbols, edata->symbol_count,
1304 sizeof (struct macho_symbol), macho_symbol_search));
1314 callback (data, addr, NULL, 0, 0);
1316 callback (data, addr, sym->name, sym->addr, sym->size);
1321 macho_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED,
1322 uintptr_t pc ATTRIBUTE_UNUSED,
1323 backtrace_full_callback callback ATTRIBUTE_UNUSED,
1324 backtrace_error_callback error_callback, void *data)
1326 error_callback (data, "no debug info in Mach-O executable", -1);
1331 macho_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED,
1332 uintptr_t addr ATTRIBUTE_UNUSED,
1333 backtrace_syminfo_callback callback ATTRIBUTE_UNUSED,
1334 backtrace_error_callback error_callback, void *data)
1336 error_callback (data, "no symbol table in Mach-O executable", -1);
1340 backtrace_initialize (struct backtrace_state *state, int descriptor,
1341 backtrace_error_callback error_callback,
1342 void *data, fileline *fileline_fn)
1345 fileline macho_fileline_fn = macho_nodebug;
1347 int found_dwarf = 0;
1349 uint32_t loaded_image_count;
1351 // Add all loaded images
1352 loaded_image_count = _dyld_image_count ();
1353 for (i = 0; i < loaded_image_count; i++)
1355 int current_found_sym;
1356 int current_found_dwarf;
1357 int current_descriptor;
1358 intptr_t current_vmslide;
1359 const char *current_name;
1361 current_vmslide = _dyld_get_image_vmaddr_slide (i);
1362 current_name = _dyld_get_image_name (i);
1364 if (current_name == NULL || (i != 0 && current_vmslide == 0))
1367 if (!(current_descriptor =
1368 backtrace_open (current_name, error_callback, data, NULL)))
1373 if (macho_add (state, error_callback, data, current_descriptor,
1374 current_name, &macho_fileline_fn, current_vmslide,
1375 ¤t_found_sym, ¤t_found_dwarf))
1377 found_sym = found_sym || current_found_sym;
1378 found_dwarf = found_dwarf || current_found_dwarf;
1381 backtrace_close (current_descriptor, error_callback, data);
1384 if (!state->threaded)
1387 state->syminfo_fn = macho_syminfo;
1388 else if (state->syminfo_fn == NULL)
1389 state->syminfo_fn = macho_nosyms;
1394 backtrace_atomic_store_pointer (&state->syminfo_fn, macho_syminfo);
1396 (void) __sync_bool_compare_and_swap (&state->syminfo_fn, NULL,
1400 if (!state->threaded)
1402 if (state->fileline_fn == NULL || state->fileline_fn == macho_nodebug)
1403 *fileline_fn = macho_fileline_fn;
1407 fileline current_fn;
1409 current_fn = backtrace_atomic_load_pointer (&state->fileline_fn);
1410 if (current_fn == NULL || current_fn == macho_nodebug)
1411 *fileline_fn = macho_fileline_fn;