Bug #17585 » ruby-dwarf5-debug_line.patch
addr2line.c | ||
---|---|---|
struct dwarf_section debug_line;
|
||
struct dwarf_section debug_ranges;
|
||
struct dwarf_section debug_str;
|
||
struct dwarf_section debug_line_str;
|
||
struct obj_info *next;
|
||
} obj_info_t;
|
||
#define DWARF_SECTION_COUNT 5
|
||
#define DWARF_SECTION_COUNT 6
|
||
static struct dwarf_section *
|
||
obj_dwarf_section_at(obj_info_t *obj, int n)
|
||
... | ... | |
&obj->debug_info,
|
||
&obj->debug_line,
|
||
&obj->debug_ranges,
|
||
&obj->debug_str
|
||
&obj->debug_str,
|
||
&obj->debug_line_str,
|
||
};
|
||
if (n < 0 || DWARF_SECTION_COUNT <= n) {
|
||
abort();
|
||
... | ... | |
return r;
|
||
}
|
||
static const char *get_nth_dirname_5(unsigned long, uint16_t, uint8_t, uint8_t, obj_info_t *, char *);
|
||
static const char *
|
||
get_nth_dirname(unsigned long dir, char *p)
|
||
get_nth_dirname(unsigned long dir, uint16_t version, uint8_t format, uint8_t address_size, obj_info_t *obj, char *p)
|
||
{
|
||
if (version >= 5) {
|
||
return get_nth_dirname_5(dir, version, format, address_size, obj, p);
|
||
}
|
||
if (!dir--) {
|
||
return "";
|
||
}
|
||
... | ... | |
return p;
|
||
}
|
||
static void fill_filename_5(int, uint16_t, uint8_t, uint8_t, char *, char *, line_info_t *, obj_info_t *);
|
||
static void
|
||
fill_filename(int file, char *include_directories, char *filenames, line_info_t *line, obj_info_t *obj)
|
||
fill_filename(int file, uint16_t version, uint8_t format, uint8_t address_size, char *include_directories, char *filenames, line_info_t *line, obj_info_t *obj)
|
||
{
|
||
int i;
|
||
char *p = filenames;
|
||
char *filename;
|
||
unsigned long dir;
|
||
if (version >= 5) {
|
||
fill_filename_5(file, version, format, address_size, include_directories, filenames, line, obj);
|
||
return;
|
||
}
|
||
for (i = 1; i <= file; i++) {
|
||
filename = p;
|
||
if (!*p) {
|
||
... | ... | |
if (i == file) {
|
||
line->filename = filename;
|
||
line->dirname = get_nth_dirname(dir, include_directories);
|
||
line->dirname = get_nth_dirname(dir, version, format, address_size, obj, include_directories);
|
||
}
|
||
}
|
||
}
|
||
static void
|
||
fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line,
|
||
uint16_t version, uint8_t format, uint8_t address_size,
|
||
char *include_directories, char *filenames,
|
||
obj_info_t *obj, line_info_t *lines, int offset)
|
||
{
|
||
... | ... | |
/* We assume one line code doesn't result >100 bytes of native code.
|
||
We may want more reliable way eventually... */
|
||
if (addr < a && a < addr + 100) {
|
||
fill_filename(file, include_directories, filenames, &lines[i], obj);
|
||
fill_filename(file, version, format, address_size, include_directories, filenames, &lines[i], obj);
|
||
lines[i].line = line;
|
||
}
|
||
}
|
||
... | ... | |
uint64_t unit_length;
|
||
uint16_t version;
|
||
uint8_t format; /* 4 or 8 */
|
||
uint8_t address_size;
|
||
uint8_t segment_selector_size;
|
||
uint64_t header_length;
|
||
uint8_t minimum_instruction_length;
|
||
uint8_t maximum_operations_per_instruction;
|
||
... | ... | |
const char *cu_end;
|
||
};
|
||
static int debug_line_header_skip_directories_5(char **, struct LineNumberProgramHeader *);
|
||
static int
|
||
parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header)
|
||
{
|
||
... | ... | |
header->version = *(uint16_t *)p;
|
||
p += sizeof(uint16_t);
|
||
if (header->version > 4) return -1;
|
||
if (header->version > 5) return -1;
|
||
if (header->version >= 5) {
|
||
header->address_size = *(uint8_t *)p++;
|
||
/* header->segment_selector_size = *(uint8_t *)p; */
|
||
if (*p != 0) return -1;
|
||
p++;
|
||
} else {
|
||
header->address_size = 0;
|
||
}
|
||
header->header_length = header->format == 4 ? *(uint32_t *)p : *(uint64_t *)p;
|
||
p += header->format;
|
||
... | ... | |
if (p >= header->cu_end) return -1;
|
||
/* skip include directories */
|
||
while (*p) {
|
||
p = memchr(p, '\0', header->cu_end - p);
|
||
if (!p) return -1;
|
||
if (header->version >= 5) {
|
||
if (debug_line_header_skip_directories_5((char **)&p, header)) return -1;
|
||
} else {
|
||
while (*p) {
|
||
p = memchr(p, '\0', header->cu_end - p);
|
||
if (!p) return -1;
|
||
p++;
|
||
}
|
||
p++;
|
||
}
|
||
p++;
|
||
}
|
||
header->filenames = p;
|
||
... | ... | |
#define FILL_LINE() \
|
||
do { \
|
||
fill_line(num_traces, traces, addr, file, line, \
|
||
header.version, \
|
||
header.format, \
|
||
header.address_size, \
|
||
(char *)header.include_directories, \
|
||
(char *)header.filenames, \
|
||
obj, lines, offset); \
|
||
... | ... | |
DW_FORM_addrx4 = 0x2c
|
||
};
|
||
/* Content type codes */
|
||
enum
|
||
{
|
||
DW_LNCT_path = 0x01,
|
||
DW_LNCT_directory_index = 0x02,
|
||
DW_LNCT_timestamp = 0x03,
|
||
DW_LNCT_size = 0x04,
|
||
DW_LNCT_MD5 = 0x05
|
||
};
|
||
enum {
|
||
VAL_none = 0,
|
||
VAL_cstr = 1,
|
||
... | ... | |
char *file;
|
||
char *current_cu;
|
||
uint64_t current_low_pc;
|
||
uint16_t debug_line_version;
|
||
uint8_t debug_line_format;
|
||
uint8_t debug_line_address_size;
|
||
char *debug_line_cu_end;
|
||
char *debug_line_files;
|
||
char *debug_line_directories;
|
||
... | ... | |
if (parse_debug_line_header(&p, &header))
|
||
return -1;
|
||
reader->debug_line_version = header.version;
|
||
reader->debug_line_format = header.format;
|
||
reader->debug_line_address_size = header.address_size;
|
||
reader->debug_line_cu_end = (char *)header.cu_end;
|
||
reader->debug_line_directories = (char *)header.include_directories;
|
||
reader->debug_line_files = (char *)header.filenames;
|
||
... | ... | |
static const char *
|
||
get_cstr_value(DebugInfoValue *v)
|
||
{
|
||
if (v->as.ptr) {
|
||
return v->as.ptr + v->off;
|
||
} else {
|
||
return NULL;
|
||
switch (v->form) {
|
||
case DW_FORM_strp:
|
||
if (v->as.ptr) {
|
||
return v->as.ptr + v->off;
|
||
} else {
|
||
return NULL;
|
||
}
|
||
default:
|
||
return NULL;
|
||
}
|
||
}
|
||
... | ... | |
line.sname = get_cstr_value(&v);
|
||
break;
|
||
case DW_AT_call_file:
|
||
fill_filename((int)v.as.uint64, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
|
||
fill_filename((int)v.as.uint64, reader->debug_line_version, reader->debug_line_format, reader->debug_line_address_size, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj);
|
||
break;
|
||
case DW_AT_call_line:
|
||
line.line = (int)v.as.uint64;
|
||
... | ... | |
".debug_info",
|
||
".debug_line",
|
||
".debug_ranges",
|
||
".debug_str"
|
||
".debug_str",
|
||
".debug_line_str",
|
||
};
|
||
for (j=0; j < DWARF_SECTION_COUNT; j++) {
|
||
... | ... | |
if (di_read_cu(&reader)) goto use_symtab;
|
||
debug_info_read(&reader, num_traces, traces, lines, offset);
|
||
}
|
||
if (reader.debug_line_version >= 5) goto use_symtab;
|
||
}
|
||
else {
|
||
/* This file doesn't have dwarf, use symtab or dynsym */
|
||
... | ... | |
"__debug_info",
|
||
"__debug_line",
|
||
"__debug_ranges",
|
||
"__debug_str"
|
||
"__debug_str",
|
||
"__debug_line_str",
|
||
};
|
||
struct LP(segment_command) *scmd = (struct LP(segment_command) *)lcmd;
|
||
if (strcmp(scmd->segname, "__TEXT") == 0) {
|
||
... | ... | |
free(dladdr_fbases);
|
||
}
|
||
static const char *
|
||
get_nth_dirname_5(unsigned long dir, uint16_t version, uint8_t format, uint8_t address_size, obj_info_t *obj, char *p)
|
||
{
|
||
const char *ret = "";
|
||
uint64_t i, j;
|
||
DebugInfoReader reader = {
|
||
.format = format,
|
||
.address_size = address_size,
|
||
.p = p
|
||
};
|
||
uint8_t format_count = read_uint8(&reader.p);
|
||
uint64_t *content_types = calloc(format_count, sizeof(uint64_t));
|
||
uint64_t *forms = calloc(format_count, sizeof(uint64_t));
|
||
if (content_types == NULL || forms == NULL) return "";
|
||
for (i = 0; i < format_count; ++i) {
|
||
content_types[i] = uleb128(&reader.p);
|
||
forms[i] = uleb128(&reader.p);
|
||
}
|
||
uint64_t directories_count = uleb128(&reader.p);
|
||
DebugInfoValue v = {{}};
|
||
for (j = 0; j < directories_count; ++j) {
|
||
for (i = 0; i < format_count; ++i) {
|
||
debug_info_reader_read_value(&reader, forms[i], &v);
|
||
if (j == dir && content_types[i] == DW_LNCT_path) {
|
||
switch (forms[i]) {
|
||
case DW_FORM_line_strp:
|
||
ret = &obj->debug_line_str.ptr[v.as.uint64];
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
free(forms);
|
||
free(content_types);
|
||
return ret;
|
||
}
|
||
static void
|
||
fill_filename_5(int file, uint16_t version, uint8_t format, uint8_t address_size, char *include_directories, char *filenames, line_info_t *line, obj_info_t *obj)
|
||
{
|
||
char *p = filenames;
|
||
unsigned long dir;
|
||
uint64_t i, j;
|
||
DebugInfoReader reader = {
|
||
.format = format,
|
||
.address_size = address_size,
|
||
.p = p
|
||
};
|
||
uint8_t format_count = read_uint8(&reader.p);
|
||
uint64_t *content_types = calloc(format_count, sizeof(uint64_t));
|
||
uint64_t *forms = calloc(format_count, sizeof(uint64_t));
|
||
if (content_types == NULL || forms == NULL) return;
|
||
for (i = 0; i < format_count; ++i) {
|
||
content_types[i] = uleb128(&reader.p);
|
||
forms[i] = uleb128(&reader.p);
|
||
}
|
||
uint64_t file_names_count = uleb128(&reader.p);
|
||
DebugInfoValue v = {{}};
|
||
for (j = 0; j < file_names_count; ++j) {
|
||
for (i = 0; i < format_count; ++i) {
|
||
debug_info_reader_read_value(&reader, forms[i], &v);
|
||
if ((int)j == file) {
|
||
switch (content_types[i]) {
|
||
case DW_LNCT_path:
|
||
switch (forms[i]) {
|
||
case DW_FORM_line_strp:
|
||
line->filename = &obj->debug_line_str.ptr[v.as.uint64];
|
||
break;
|
||
}
|
||
break;
|
||
case DW_LNCT_directory_index:
|
||
switch (forms[i]) {
|
||
case DW_FORM_udata:
|
||
dir = v.as.uint64;
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
free(forms);
|
||
free(content_types);
|
||
line->dirname = get_nth_dirname_5(dir, version, format, address_size, obj, include_directories);
|
||
return;
|
||
}
|
||
static int
|
||
debug_line_header_skip_directories_5(char **p, struct LineNumberProgramHeader *header)
|
||
{
|
||
uint64_t i, j;
|
||
DebugInfoReader reader = {
|
||
.format = header->format,
|
||
.address_size = header->address_size,
|
||
.p = *p
|
||
};
|
||
uint8_t format_count = read_uint8(&reader.p);
|
||
uint64_t *forms = calloc(format_count, sizeof(uint64_t));
|
||
if (forms == NULL) return -1;
|
||
for (i = 0; i < format_count; ++i) {
|
||
uleb128(&reader.p); /* content type code */
|
||
forms[i] = uleb128(&reader.p);
|
||
}
|
||
uint64_t directories_count = uleb128(&reader.p);
|
||
DebugInfoValue v = {{}};
|
||
for (j = 0; j < directories_count; ++j) {
|
||
for (i = 0; i < format_count; ++i) {
|
||
debug_info_reader_read_value(&reader, forms[i], &v);
|
||
}
|
||
}
|
||
free(forms);
|
||
*p = reader.p;
|
||
return 0;
|
||
}
|
||
/* From FreeBSD's lib/libstand/printf.c */
|
||
/*-
|
||
* Copyright (c) 1986, 1988, 1991, 1993
|