summaryrefslogtreecommitdiff
path: root/addr2line.c
diff options
context:
space:
mode:
Diffstat (limited to 'addr2line.c')
-rw-r--r--addr2line.c268
1 files changed, 212 insertions, 56 deletions
diff --git a/addr2line.c b/addr2line.c
index 89da6384aa..1bc3896e5a 100644
--- a/addr2line.c
+++ b/addr2line.c
@@ -28,7 +28,7 @@
#include "missing/stdbool.h"
#endif
-#ifdef USE_ELF
+#if defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)
#include <fcntl.h>
#include <limits.h>
@@ -41,12 +41,6 @@
#include <sys/stat.h>
#include <unistd.h>
-#ifdef __OpenBSD__
-#include <elf_abi.h>
-#else
-#include <elf.h>
-#endif
-
/* Make alloca work the best possible way. */
#ifdef __GNUC__
# ifndef alloca
@@ -70,24 +64,16 @@ void *alloca();
# include <dlfcn.h>
#endif
-#define DW_LNS_copy 0x01
-#define DW_LNS_advance_pc 0x02
-#define DW_LNS_advance_line 0x03
-#define DW_LNS_set_file 0x04
-#define DW_LNS_set_column 0x05
-#define DW_LNS_negate_stmt 0x06
-#define DW_LNS_set_basic_block 0x07
-#define DW_LNS_const_add_pc 0x08
-#define DW_LNS_fixed_advance_pc 0x09
-#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */
-#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */
-#define DW_LNS_set_isa 0x0c /* DWARF3 */
+#ifdef HAVE_MACH_O_LOADER_H
+# include <mach-o/loader.h>
+#endif
-/* Line number extended opcode name. */
-#define DW_LNE_end_sequence 0x01
-#define DW_LNE_set_address 0x02
-#define DW_LNE_define_file 0x03
-#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
+#ifdef USE_ELF
+# ifdef __OpenBSD__
+# include <elf_abi.h>
+# else
+# include <elf.h>
+# endif
#ifndef ElfW
# if SIZEOF_VOIDP == 8
@@ -103,8 +89,6 @@ void *alloca();
# define ELF_ST_TYPE ELF32_ST_TYPE
# endif
#endif
-#ifndef PATH_MAX
-#define PATH_MAX 4096
#endif
#ifdef SHF_COMPRESSED
@@ -117,6 +101,29 @@ void *alloca();
# define SHF_COMPRESSED 0
#endif
+#ifndef PATH_MAX
+#define PATH_MAX 4096
+#endif
+
+#define DW_LNS_copy 0x01
+#define DW_LNS_advance_pc 0x02
+#define DW_LNS_advance_line 0x03
+#define DW_LNS_set_file 0x04
+#define DW_LNS_set_column 0x05
+#define DW_LNS_negate_stmt 0x06
+#define DW_LNS_set_basic_block 0x07
+#define DW_LNS_const_add_pc 0x08
+#define DW_LNS_fixed_advance_pc 0x09
+#define DW_LNS_set_prologue_end 0x0a /* DWARF3 */
+#define DW_LNS_set_epilogue_begin 0x0b /* DWARF3 */
+#define DW_LNS_set_isa 0x0c /* DWARF3 */
+
+/* Line number extended opcode name. */
+#define DW_LNE_end_sequence 0x01
+#define DW_LNE_set_address 0x02
+#define DW_LNE_define_file 0x03
+#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
+
PRINTF_ARGS(static int kprintf(const char *fmt, ...), 1, 2);
typedef struct line_info {
@@ -133,9 +140,9 @@ typedef struct line_info {
} line_info_t;
struct dwarf_section {
- ElfW(Shdr) *shdr;
char *ptr;
size_t size;
+ uint64_t flags;
};
typedef struct obj_info {
@@ -485,6 +492,7 @@ append_obj(obj_info_t **objp)
*objp = newobj;
}
+#ifdef USE_ELF
static void
follow_debuglink(const char *debuglink, int num_traces, void **traces,
obj_info_t **objp, line_info_t *lines, int offset)
@@ -518,6 +526,7 @@ follow_debuglink(const char *debuglink, int num_traces, void **traces,
o2->path = o1->path;
fill_lines(num_traces, traces, 0, objp, lines, offset);
}
+#endif
enum
{
@@ -1371,7 +1380,7 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr)
/* base address selection entry */
base = to;
}
- else if (base + from <= addr && addr <= base + to) {
+ else if (base + from <= addr && addr < base + to) {
return from;
}
}
@@ -1393,22 +1402,21 @@ ranges_inspect(DebugInfoReader *reader, ranges_t *ptr)
fprintf(stderr,"low_pc_set:%d high_pc_set:%d ranges_set:%d\n",ptr->low_pc_set,ptr->high_pc_set,ptr->ranges_set);
exit(1);
}
- fprintf(stderr,"low_pc:%lx high_pc:%lx\n",ptr->low_pc,ptr->high_pc);
+ fprintf(stderr,"low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc);
}
else if (ptr->ranges_set) {
- char *p;
- fprintf(stderr,"low_pc:%lx ranges:%lx ",ptr->low_pc,ptr->ranges);
- p = reader->obj->debug_ranges.ptr + ptr->ranges;
+ char *p = reader->obj->debug_ranges.ptr + ptr->ranges;
+ fprintf(stderr,"low_pc:%"PRIx64" ranges:%"PRIx64" %lx ",ptr->low_pc,ptr->ranges, p-reader->obj->mapped);
for (;;) {
uintptr_t from = read_uintptr(&p);
uintptr_t to = read_uintptr(&p);
if (!from && !to) break;
- fprintf(stderr,"%lx-%lx ",ptr->low_pc+from,ptr->low_pc+to);
+ fprintf(stderr,"%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to);
}
fprintf(stderr,"\n");
}
else if (ptr->low_pc_set) {
- fprintf(stderr,"low_pc:%lx\n",ptr->low_pc);
+ fprintf(stderr,"low_pc:%"PRIx64"\n",ptr->low_pc);
}
else {
fprintf(stderr,"empty\n");
@@ -1565,12 +1573,13 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces,
lines[i].path = reader->obj->path;
lines[i].base_addr = line.base_addr;
lines[i].sname = line.sname;
- lines[i].saddr = reader->obj->base_addr + saddr;
+ lines[i].saddr = saddr + reader->obj->base_addr - reader->obj->vmaddr;
}
}
}
}
+#ifdef USE_ELF
static unsigned long
uncompress_debug_section(ElfW(Shdr) *shdr, char *file, char **ptr)
{
@@ -1605,7 +1614,6 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
{
int i, j;
char *shstr;
- char *section_name;
ElfW(Ehdr) *ehdr;
ElfW(Shdr) *shdr, *shstr_shdr;
ElfW(Shdr) *gnu_debuglink_shdr = NULL;
@@ -1663,7 +1671,7 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
shstr = file + shstr_shdr->sh_offset;
for (i = 0; i < ehdr->e_shnum; i++) {
- section_name = shstr + shdr[i].sh_name;
+ char *section_name = shstr + shdr[i].sh_name;
switch (shdr[i].sh_type) {
case SHT_STRTAB:
if (!strcmp(section_name, ".strtab")) {
@@ -1693,11 +1701,21 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
".debug_ranges",
".debug_str"
};
+
for (j=0; j < DWARF_SECTION_COUNT; j++) {
- if (strcmp(section_name, debug_section_names[j]) == 0) {
- obj_dwarf_section_at(obj, j)->shdr = &shdr[i];
- break;
+ struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+ if (strcmp(section_name, debug_section_names[j]) != 0)
+ continue;
+
+ s->ptr = file + shdr[i].sh_offset;
+ s->size = shdr[i].sh_size;
+ s->flags = shdr[i].sh_flags;
+ if (s->flags & SHF_COMPRESSED) {
+ s->size = uncompress_debug_section(&shdr[i], file, &s->ptr);
+ if (!s->size) goto fail;
}
+ break;
}
}
break;
@@ -1720,6 +1738,7 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
if (ELF_ST_TYPE(sym->st_info) != STT_FUNC || sym->st_size == 0) continue;
s = dlsym(handle, strtab + sym->st_name);
if (s && dladdr(s, &info)) {
+ obj->base_addr = dladdr_fbase;
dladdr_fbase = (uintptr_t)info.dli_fbase;
break;
}
@@ -1736,21 +1755,6 @@ fill_lines(int num_traces, void **traces, int check_debuglink,
}
}
- if (obj->debug_info.shdr) {
- size_t j;
- for (j=0; j < DWARF_SECTION_COUNT; j++) {
- struct dwarf_section *s = obj_dwarf_section_at(obj, j);
- ElfW(Shdr) *shdr = s->shdr;
- if (!shdr) break;
- s->ptr = file + shdr->sh_offset;
- s->size = shdr->sh_size;
- if (shdr->sh_flags & SHF_COMPRESSED) {
- s->size = uncompress_debug_section(shdr, file, &s->ptr);
- if (!s->size) goto fail;
- }
- }
- }
-
if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
DebugInfoReader reader;
debug_info_reader_init(&reader, obj);
@@ -1813,6 +1817,158 @@ finish:
fail:
return (uintptr_t)-1;
}
+#else /* Mach-O */
+/* read file and fill lines */
+static uintptr_t
+fill_lines(int num_traces, void **traces, int check_debuglink,
+ obj_info_t **objp, line_info_t *lines, int offset)
+{
+ int fd;
+ off_t filesize;
+ char *file, *p;
+ obj_info_t *obj = *objp;
+ struct mach_header_64 *header;
+ uintptr_t dladdr_fbase = 0;
+
+ {
+ char *s = binary_filename;
+ char *base = strrchr(binary_filename, '/')+1;
+ size_t max = PATH_MAX;
+ size_t size = strlen(binary_filename);
+ size_t basesize = size - (base - binary_filename);
+ s += size;
+ max -= size;
+ size = strlcpy(s, ".dSYM/Contents/Resources/DWARF/", max);
+ if (size == 0) goto fail;
+ s += size;
+ max -= size;
+ if (max <= basesize) goto fail;
+ memcpy(s, base, basesize);
+ s[basesize] = 0;
+ }
+
+ fd = open(binary_filename, O_RDONLY);
+ if (fd < 0) {
+ goto fail;
+ }
+ filesize = lseek(fd, 0, SEEK_END);
+ if (filesize < 0) {
+ int e = errno;
+ close(fd);
+ kprintf("lseek: %s\n", strerror(e));
+ goto fail;
+ }
+#if SIZEOF_OFF_T > SIZEOF_SIZE_T
+ if (filesize > (off_t)SIZE_MAX) {
+ close(fd);
+ kprintf("Too large file %s\n", binary_filename);
+ goto fail;
+ }
+#endif
+ lseek(fd, 0, SEEK_SET);
+ /* async-signal unsafe */
+ file = (char *)mmap(NULL, (size_t)filesize, PROT_READ, MAP_SHARED, fd, 0);
+ if (file == MAP_FAILED) {
+ int e = errno;
+ close(fd);
+ kprintf("mmap: %s\n", strerror(e));
+ goto fail;
+ }
+ close(fd);
+
+ obj->mapped = file;
+ obj->mapped_size = (size_t)filesize;
+
+ header = (struct mach_header_64 *)file;
+ if (header->magic != MH_MAGIC_64) {
+ /* TODO: universal binaries */
+ kprintf("'%s' is not a 64-bit Mach-O file!\n",binary_filename);
+ close(fd);
+ goto fail;
+ }
+
+ p = file + sizeof(struct mach_header_64);
+ for (uint32_t i = 0; i < (uint32_t)header->ncmds; i++) {
+ struct load_command *lcmd = (struct load_command *)p;
+ switch (lcmd->cmd) {
+ case LC_SEGMENT_64:
+ {
+ static const char *debug_section_names[] = {
+ "__debug_abbrev",
+ "__debug_info",
+ "__debug_line",
+ "__debug_ranges",
+ "__debug_str"
+ };
+ struct segment_command_64 *scmd = (struct segment_command_64 *)lcmd;
+ if (strcmp(scmd->segname, "__TEXT") == 0) {
+ obj->vmaddr = scmd->vmaddr;
+ }
+ else if (strcmp(scmd->segname, "__DWARF") == 0) {
+ p += sizeof(struct segment_command_64);
+ for (uint64_t i = 0; i < scmd->nsects; i++) {
+ struct section_64 *sect = (struct section_64 *)p;
+ p += sizeof(struct section_64);
+ for (int j=0; j < DWARF_SECTION_COUNT; j++) {
+ struct dwarf_section *s = obj_dwarf_section_at(obj, j);
+
+ if (strcmp(sect->sectname, debug_section_names[j]) != 0)
+ continue;
+
+ s->ptr = file + sect->offset;
+ s->size = sect->size;
+ s->flags = sect->flags;
+ if (s->flags & SHF_COMPRESSED) {
+ goto fail;
+ }
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+#if 0
+ case LC_SYMTAB:
+ {
+ struct symtab_command *c = (struct symtab_command *)lcmd;
+ struct nlist_64 *nl = (struct nlist *)(file + c->symoff);
+ char *strtab = file + c->stroff;
+ uint32_t j;
+ kprintf("[%2d]: %x/symtab %lx\n", i, c->cmd, p);
+ for (j = 0; j < c->nsyms; j++) {
+ struct nlist_64 *e = &nl[j];
+ if (!(e->n_type & N_STAB)) continue;
+ /* if (e->n_type != N_FUN) continue; */
+ kprintf("[%2d][%4d]: %02x/%x/%x: %s %lx\n", i, j,
+ e->n_type,e->n_sect,e->n_desc,strtab+e->n_un.n_strx,e->n_value);
+ }
+ }
+#endif
+ }
+ p += lcmd->cmdsize;
+ }
+
+ if (obj->debug_info.ptr && obj->debug_abbrev.ptr) {
+ DebugInfoReader reader;
+ debug_info_reader_init(&reader, obj);
+ while (reader.p < reader.pend) {
+ di_read_cu(&reader);
+ debug_info_read(&reader, num_traces, traces, lines, offset);
+ }
+ }
+
+ if (parse_debug_line(num_traces, traces,
+ obj->debug_line.ptr,
+ obj->debug_line.size,
+ obj, lines, offset) == -1)
+ goto fail;
+
+ return dladdr_fbase;
+fail:
+ return (uintptr_t)-1;
+}
+#endif
#define HAVE_MAIN_EXE_PATH
#if defined(__FreeBSD__)
@@ -1971,7 +2127,7 @@ next_line:
obj_info_t *o = obj;
for (i=0; i < DWARF_SECTION_COUNT; i++) {
struct dwarf_section *s = obj_dwarf_section_at(obj, i);
- if (s->shdr && (s->shdr->sh_flags & SHF_COMPRESSED)) {
+ if (s->flags & SHF_COMPRESSED) {
free(s->ptr);
}
}