зеркало из https://github.com/github/ruby.git
Support Mach-O on backtrace with DWARF
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65202 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
fe735bd30f
Коммит
595056135f
286
addr2line.c
286
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,6 +64,47 @@ void *alloca();
|
|||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MACH_O_LOADER_H
|
||||
# include <mach-o/loader.h>
|
||||
#endif
|
||||
|
||||
#ifdef USE_ELF
|
||||
# ifdef __OpenBSD__
|
||||
# include <elf_abi.h>
|
||||
# else
|
||||
# include <elf.h>
|
||||
# endif
|
||||
|
||||
#ifndef ElfW
|
||||
# if SIZEOF_VOIDP == 8
|
||||
# define ElfW(x) Elf64##_##x
|
||||
# else
|
||||
# define ElfW(x) Elf32##_##x
|
||||
# endif
|
||||
#endif
|
||||
#ifndef ELF_ST_TYPE
|
||||
# if SIZEOF_VOIDP == 8
|
||||
# define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
# else
|
||||
# define ELF_ST_TYPE ELF32_ST_TYPE
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SHF_COMPRESSED
|
||||
# if defined(ELFCOMPRESS_ZLIB) && defined(HAVE_LIBZ)
|
||||
/* FreeBSD 11.0 lacks ELFCOMPRESS_ZLIB */
|
||||
# include <zlib.h>
|
||||
# define SUPPORT_COMPRESSED_DEBUG_LINE
|
||||
# endif
|
||||
#else /* compatibility with glibc < 2.22 */
|
||||
# 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
|
||||
|
@ -89,34 +124,6 @@ void *alloca();
|
|||
#define DW_LNE_define_file 0x03
|
||||
#define DW_LNE_set_discriminator 0x04 /* DWARF4 */
|
||||
|
||||
#ifndef ElfW
|
||||
# if SIZEOF_VOIDP == 8
|
||||
# define ElfW(x) Elf64##_##x
|
||||
# else
|
||||
# define ElfW(x) Elf32##_##x
|
||||
# endif
|
||||
#endif
|
||||
#ifndef ELF_ST_TYPE
|
||||
# if SIZEOF_VOIDP == 8
|
||||
# define ELF_ST_TYPE ELF64_ST_TYPE
|
||||
# else
|
||||
# define ELF_ST_TYPE ELF32_ST_TYPE
|
||||
# endif
|
||||
#endif
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX 4096
|
||||
#endif
|
||||
|
||||
#ifdef SHF_COMPRESSED
|
||||
# if defined(ELFCOMPRESS_ZLIB) && defined(HAVE_LIBZ)
|
||||
/* FreeBSD 11.0 lacks ELFCOMPRESS_ZLIB */
|
||||
# include <zlib.h>
|
||||
# define SUPPORT_COMPRESSED_DEBUG_LINE
|
||||
# endif
|
||||
#else /* compatibility with glibc < 2.22 */
|
||||
# define SHF_COMPRESSED 0
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#ifndef RUBY_ADDR2LINE_H
|
||||
#define RUBY_ADDR2LINE_H
|
||||
|
||||
#ifdef USE_ELF
|
||||
#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H))
|
||||
|
||||
void
|
||||
rb_dump_backtrace_with_lines(int num_traces, void **traces);
|
||||
|
|
|
@ -2515,6 +2515,11 @@ AS_IF([test "$rb_cv_binary_elf" = yes], [
|
|||
])
|
||||
])
|
||||
|
||||
AC_CHECK_HEADERS([mach-o/loader.h])
|
||||
AS_IF([test "$ac_cv_header_mach_o_loader_h" = yes], [
|
||||
AC_LIBOBJ([addr2line])
|
||||
])
|
||||
|
||||
AS_CASE(["$target_os"],
|
||||
[linux* | gnu* | k*bsd*-gnu | bsdi* | kopensolaris*-gnu], [
|
||||
AS_IF([test "$rb_cv_binary_elf" = no], [
|
||||
|
|
|
@ -713,7 +713,7 @@ rb_print_backtrace(void)
|
|||
#define MAX_NATIVE_TRACE 1024
|
||||
static void *trace[MAX_NATIVE_TRACE];
|
||||
int n = (int)backtrace(trace, MAX_NATIVE_TRACE);
|
||||
#if defined(USE_ELF) && defined(HAVE_DLADDR) && !defined(__sparc)
|
||||
#if (defined(USE_ELF) || defined(HAVE_MACH_O_LOADER_H)) && defined(HAVE_DLADDR) && !defined(__sparc)
|
||||
rb_dump_backtrace_with_lines(n, trace);
|
||||
#else
|
||||
char **syms = backtrace_symbols(trace, n);
|
||||
|
|
Загрузка…
Ссылка в новой задаче