diff options
Diffstat (limited to 'addr2line.c')
-rw-r--r-- | addr2line.c | 1345 |
1 files changed, 628 insertions, 717 deletions
diff --git a/addr2line.c b/addr2line.c index 8ee4416650..02a3e617a6 100644 --- a/addr2line.c +++ b/addr2line.c @@ -8,10 +8,14 @@ **********************************************************************/ -#if defined(__clang__) +#if defined(__clang__) && defined(__has_warning) +#if __has_warning("-Wgnu-empty-initializer") #pragma clang diagnostic ignored "-Wgnu-empty-initializer" +#endif +#if __has_warning("-Wgcc-compat") #pragma clang diagnostic ignored "-Wgcc-compat" #endif +#endif #include "ruby/internal/config.h" #include "ruby/defines.h" @@ -57,8 +61,21 @@ void *alloca(); # endif # endif /* AIX */ # endif /* HAVE_ALLOCA_H */ +# ifndef UNREACHABLE +# define UNREACHABLE __builtin_unreachable() +# endif +# ifndef UNREACHABLE_RETURN +# define UNREACHABLE_RETURN(_) __builtin_unreachable() +# endif #endif /* __GNUC__ */ +#ifndef UNREACHABLE +# define UNREACHABLE abort() +#endif +#ifndef UNREACHABLE_RETURN +# define UNREACHABLE_RETURN(_) return (abort(), (_)) +#endif + #ifdef HAVE_DLADDR # include <dlfcn.h> #endif @@ -127,7 +144,7 @@ void *alloca(); #define DW_LNE_define_file 0x03 #define DW_LNE_set_discriminator 0x04 /* DWARF4 */ -PRINTF_ARGS(static int kprintf(const char *fmt, ...), 1, 2); +#define kprintf(...) fprintf(errout, "" __VA_ARGS__) typedef struct line_info { const char *dirname; @@ -159,12 +176,15 @@ typedef struct obj_info { struct dwarf_section debug_info; struct dwarf_section debug_line; struct dwarf_section debug_ranges; + struct dwarf_section debug_str_offsets; + struct dwarf_section debug_addr; struct dwarf_section debug_rnglists; struct dwarf_section debug_str; + struct dwarf_section debug_line_str; struct obj_info *next; } obj_info_t; -#define DWARF_SECTION_COUNT 6 +#define DWARF_SECTION_COUNT 9 static struct dwarf_section * obj_dwarf_section_at(obj_info_t *obj, int n) @@ -174,11 +194,14 @@ obj_dwarf_section_at(obj_info_t *obj, int n) &obj->debug_info, &obj->debug_line, &obj->debug_ranges, + &obj->debug_str_offsets, + &obj->debug_addr, &obj->debug_rnglists, - &obj->debug_str + &obj->debug_str, + &obj->debug_line_str }; if (n < 0 || DWARF_SECTION_COUNT <= n) { - abort(); + UNREACHABLE_RETURN(0); } return ary[n]; } @@ -192,12 +215,12 @@ struct debug_section_definition { static char binary_filename[PATH_MAX + 1]; static unsigned long -uleb128(char **p) +uleb128(const char **p) { unsigned long r = 0; int s = 0; for (;;) { - unsigned char b = *(unsigned char *)(*p)++; + unsigned char b = (unsigned char)*(*p)++; if (b < 0x80) { r += (unsigned long)b << s; break; @@ -209,12 +232,12 @@ uleb128(char **p) } static long -sleb128(char **p) +sleb128(const char **p) { long r = 0; int s = 0; for (;;) { - unsigned char b = *(unsigned char *)(*p)++; + unsigned char b = (unsigned char)*(*p)++; if (b < 0x80) { if (b & 0x40) { r -= (0x80 - b) << s; @@ -231,7 +254,7 @@ sleb128(char **p) } static const char * -get_nth_dirname(unsigned long dir, char *p) +get_nth_dirname(unsigned long dir, const char *p, FILE *errout) { if (!dir--) { return ""; @@ -248,40 +271,56 @@ get_nth_dirname(unsigned long dir, char *p) return p; } +static const char *parse_ver5_debug_line_header( + const char *p, int idx, uint8_t format, + obj_info_t *obj, const char **out_path, + uint64_t *out_directory_index, FILE *errout); + static void -fill_filename(int file, char *include_directories, char *filenames, line_info_t *line, obj_info_t *obj) +fill_filename(int file, uint8_t format, uint16_t version, const char *include_directories, + const char *filenames, line_info_t *line, obj_info_t *obj, FILE *errout) { int i; - char *p = filenames; - char *filename; + const char *p = filenames; + const char *filename; unsigned long dir; - for (i = 1; i <= file; i++) { - filename = p; - if (!*p) { - /* Need to output binary file name? */ - kprintf("Unexpected file number %d in %s at %tx\n", - file, binary_filename, filenames - obj->mapped); - return; - } - while (*p) p++; - p++; - dir = uleb128(&p); - /* last modified. */ - uleb128(&p); - /* size of the file. */ - uleb128(&p); - - if (i == file) { - line->filename = filename; - line->dirname = get_nth_dirname(dir, include_directories); - } + if (version >= 5) { + const char *path; + uint64_t directory_index = -1; + parse_ver5_debug_line_header(filenames, file, format, obj, &path, &directory_index, errout); + line->filename = path; + parse_ver5_debug_line_header(include_directories, (int)directory_index, format, obj, &path, NULL, errout); + line->dirname = path; + } + else { + for (i = 1; i <= file; i++) { + filename = p; + if (!*p) { + /* Need to output binary file name? */ + kprintf("Unexpected file number %d in %s at %tx\n", + file, binary_filename, filenames - obj->mapped); + return; + } + while (*p) p++; + p++; + dir = uleb128(&p); + /* last modified. */ + uleb128(&p); + /* size of the file. */ + uleb128(&p); + + if (i == file) { + line->filename = filename; + line->dirname = get_nth_dirname(dir, include_directories, errout); + } + } } } static void fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line, - char *include_directories, char *filenames, - obj_info_t *obj, line_info_t *lines, int offset) + uint8_t format, uint16_t version, const char *include_directories, const char *filenames, + obj_info_t *obj, line_info_t *lines, int offset, FILE *errout) { int i; addr += obj->base_addr - obj->vmaddr; @@ -290,7 +329,7 @@ fill_line(int num_traces, void **traces, uintptr_t addr, int file, int line, /* 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, format, version, include_directories, filenames, &lines[i], obj, errout); lines[i].line = line; } } @@ -315,7 +354,7 @@ struct LineNumberProgramHeader { }; static int -parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header) +parse_debug_line_header(obj_info_t *obj, const char **pp, struct LineNumberProgramHeader *header, FILE *errout) { const char *p = *pp; header->unit_length = *(uint32_t *)p; @@ -332,7 +371,13 @@ 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) { + /* address_size = *(uint8_t *)p++; */ + /* segment_selector_size = *(uint8_t *)p++; */ + p += 2; + } header->header_length = header->format == 4 ? *(uint32_t *)p : *(uint64_t *)p; p += header->format; @@ -353,20 +398,27 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header) /* header->standard_opcode_lengths = (uint8_t *)p - 1; */ p += header->opcode_base - 1; - header->include_directories = p; + if (header->version >= 5) { + header->include_directories = p; + p = parse_ver5_debug_line_header(p, -1, header->format, obj, NULL, NULL, errout); + header->filenames = p; + } + else { + header->include_directories = p; - /* temporary measure for compress-debug-sections */ - if (p >= header->cu_end) return -1; + /* temporary measure for compress-debug-sections */ + if (p >= header->cu_end) return -1; - /* skip include directories */ - while (*p) { - p = memchr(p, '\0', header->cu_end - p); - if (!p) return -1; - p++; - } - p++; + /* skip include directories */ + while (*p) { + p = memchr(p, '\0', header->cu_end - p); + if (!p) return -1; + p++; + } + p++; - header->filenames = p; + header->filenames = p; + } *pp = header->cu_start; @@ -374,8 +426,8 @@ parse_debug_line_header(const char **pp, struct LineNumberProgramHeader *header) } static int -parse_debug_line_cu(int num_traces, void **traces, char **debug_line, - obj_info_t *obj, line_info_t *lines, int offset) +parse_debug_line_cu(int num_traces, void **traces, const char **debug_line, + obj_info_t *obj, line_info_t *lines, int offset, FILE *errout) { const char *p = (const char *)*debug_line; struct LineNumberProgramHeader header; @@ -392,16 +444,18 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line, /* int epilogue_begin = 0; */ /* unsigned int isa = 0; */ - if (parse_debug_line_header(&p, &header)) + if (parse_debug_line_header(obj, &p, &header, errout)) return -1; is_stmt = header.default_is_stmt; #define FILL_LINE() \ do { \ fill_line(num_traces, traces, addr, file, line, \ - (char *)header.include_directories, \ - (char *)header.filenames, \ - obj, lines, offset); \ + header.format, \ + header.version, \ + header.include_directories, \ + header.filenames, \ + obj, lines, offset, errout); \ /*basic_block = prologue_end = epilogue_begin = 0;*/ \ } while (0) @@ -413,19 +467,19 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line, FILL_LINE(); break; case DW_LNS_advance_pc: - a = uleb128((char **)&p) * header.minimum_instruction_length; + a = uleb128(&p) * header.minimum_instruction_length; addr += a; break; case DW_LNS_advance_line: { - long a = sleb128((char **)&p); + long a = sleb128(&p); line += a; break; } case DW_LNS_set_file: - file = (unsigned int)uleb128((char **)&p); + file = (unsigned int)uleb128(&p); break; case DW_LNS_set_column: - /*column = (unsigned int)*/(void)uleb128((char **)&p); + /*column = (unsigned int)*/(void)uleb128(&p); break; case DW_LNS_negate_stmt: is_stmt = !is_stmt; @@ -450,10 +504,10 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line, /* epilogue_begin = 1; */ break; case DW_LNS_set_isa: - /* isa = (unsigned int)*/(void)uleb128((char **)&p); + /* isa = (unsigned int)*/(void)uleb128(&p); break; case 0: - a = uleb128((char **)&p); + a = uleb128(&p); op = *p++; switch (op) { case DW_LNE_end_sequence: @@ -477,7 +531,7 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line, break; case DW_LNE_set_discriminator: /* TODO:currently ignore */ - uleb128((char **)&p); + uleb128(&p); break; default: kprintf("Unknown extended opcode: %d in %s\n", @@ -500,12 +554,12 @@ parse_debug_line_cu(int num_traces, void **traces, char **debug_line, static int parse_debug_line(int num_traces, void **traces, - char *debug_line, unsigned long size, - obj_info_t *obj, line_info_t *lines, int offset) + const char *debug_line, unsigned long size, + obj_info_t *obj, line_info_t *lines, int offset, FILE *errout) { - char *debug_line_end = debug_line + size; + const char *debug_line_end = debug_line + size; while (debug_line < debug_line_end) { - if (parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset)) + if (parse_debug_line_cu(num_traces, traces, &debug_line, obj, lines, offset, errout)) return -1; } if (debug_line != debug_line_end) { @@ -518,7 +572,7 @@ parse_debug_line(int num_traces, void **traces, /* 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); + obj_info_t **objp, line_info_t *lines, int offset, FILE *errout); static void append_obj(obj_info_t **objp) @@ -546,7 +600,7 @@ append_obj(obj_info_t **objp) // check the path pattern of "/usr/lib/debug/usr/bin/ruby.debug" static void follow_debuglink(const char *debuglink, int num_traces, void **traces, - obj_info_t **objp, line_info_t *lines, int offset) + obj_info_t **objp, line_info_t *lines, int offset, FILE *errout) { static const char global_debug_dir[] = "/usr/lib/debug"; const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1; @@ -572,13 +626,13 @@ follow_debuglink(const char *debuglink, int num_traces, void **traces, o2 = *objp; o2->base_addr = o1->base_addr; o2->path = o1->path; - fill_lines(num_traces, traces, 0, objp, lines, offset); + fill_lines(num_traces, traces, 0, objp, lines, offset, errout); } // check the path pattern of "/usr/lib/debug/.build-id/ab/cdef1234.debug" static void follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_traces, void **traces, - obj_info_t **objp, line_info_t *lines, int offset) + obj_info_t **objp, line_info_t *lines, int offset, FILE *errout) { static const char global_debug_dir[] = "/usr/lib/debug/.build-id/"; const size_t global_debug_dir_len = sizeof(global_debug_dir) - 1; @@ -603,7 +657,7 @@ follow_debuglink_build_id(const char *build_id, size_t build_id_size, int num_tr o2 = *objp; o2->base_addr = o1->base_addr; o2->path = o1->path; - fill_lines(num_traces, traces, 0, objp, lines, offset); + fill_lines(num_traces, traces, 0, objp, lines, offset, errout); } #endif @@ -807,7 +861,11 @@ enum DW_FORM_addrx1 = 0x29, DW_FORM_addrx2 = 0x2a, DW_FORM_addrx3 = 0x2b, - DW_FORM_addrx4 = 0x2c + DW_FORM_addrx4 = 0x2c, + + /* GNU extensions for referring to .gnu_debugaltlink dwz-compressed info */ + DW_FORM_GNU_ref_alt = 0x1f20, + DW_FORM_GNU_strp_alt = 0x1f21 }; /* Range list entry encodings */ @@ -827,27 +885,34 @@ enum { VAL_cstr = 1, VAL_data = 2, VAL_uint = 3, - VAL_int = 4 + VAL_int = 4, + VAL_addr = 5 }; # define ABBREV_TABLE_SIZE 256 typedef struct { obj_info_t *obj; - char *file; - char *current_cu; + const char *file; + uint8_t current_version; + const char *current_cu; uint64_t current_low_pc; - char *debug_line_cu_end; - char *debug_line_files; - char *debug_line_directories; - char *p; - char *cu_end; - char *pend; - char *q0; - char *q; + uint64_t current_str_offsets_base; + uint64_t current_addr_base; + uint64_t current_rnglists_base; + const char *debug_line_cu_end; + uint8_t debug_line_format; + uint16_t debug_line_version; + const char *debug_line_files; + const char *debug_line_directories; + const char *p; + const char *cu_end; + const char *pend; + const char *q0; + const char *q; int format; // 4 or 8 uint8_t address_size; int level; - char *abbrev_table[ABBREV_TABLE_SIZE]; + const char *abbrev_table[ABBREV_TABLE_SIZE]; } DebugInfoReader; typedef struct { @@ -858,9 +923,10 @@ typedef struct { typedef struct { union { - char *ptr; + const char *ptr; uint64_t uint64; int64_t int64; + uint64_t addr_idx; } as; uint64_t off; uint64_t at; @@ -869,8 +935,11 @@ typedef struct { int type; } DebugInfoValue; -/* TODO: Big Endian */ +#if defined(WORDS_BIGENDIAN) +#define MERGE_2INTS(a,b,sz) (((uint64_t)(a)<<sz)|(b)) +#else #define MERGE_2INTS(a,b,sz) (((uint64_t)(b)<<sz)|(a)) +#endif static uint16_t get_uint16(const uint8_t *p) @@ -891,39 +960,39 @@ get_uint64(const uint8_t *p) } static uint8_t -read_uint8(char **ptr) +read_uint8(const char **ptr) { - const unsigned char *p = (const unsigned char *)*ptr; - *ptr = (char *)(p + 1); - return *p; + const char *p = *ptr; + *ptr = (p + 1); + return (uint8_t)*p; } static uint16_t -read_uint16(char **ptr) +read_uint16(const char **ptr) { - const unsigned char *p = (const unsigned char *)*ptr; - *ptr = (char *)(p + 2); - return get_uint16(p); + const char *p = *ptr; + *ptr = (p + 2); + return get_uint16((const uint8_t *)p); } static uint32_t -read_uint24(char **ptr) +read_uint24(const char **ptr) { - const unsigned char *p = (const unsigned char *)*ptr; - *ptr = (char *)(p + 3); - return (*p << 16) | get_uint16(p+1); + const char *p = *ptr; + *ptr = (p + 3); + return ((uint8_t)*p << 16) | get_uint16((const uint8_t *)p+1); } static uint32_t -read_uint32(char **ptr) +read_uint32(const char **ptr) { - const unsigned char *p = (const unsigned char *)*ptr; - *ptr = (char *)(p + 4); - return get_uint32(p); + const char *p = *ptr; + *ptr = (p + 4); + return get_uint32((const uint8_t *)p); } static uint64_t -read_uint64(char **ptr) +read_uint64(const char **ptr) { const unsigned char *p = (const unsigned char *)*ptr; *ptr = (char *)(p + 8); @@ -931,7 +1000,7 @@ read_uint64(char **ptr) } static uintptr_t -read_uintptr(char **ptr) +read_uintptr(const char **ptr) { const unsigned char *p = (const unsigned char *)*ptr; *ptr = (char *)(p + SIZEOF_VOIDP); @@ -973,10 +1042,13 @@ debug_info_reader_init(DebugInfoReader *reader, obj_info_t *obj) reader->pend = obj->debug_info.ptr + obj->debug_info.size; reader->debug_line_cu_end = obj->debug_line.ptr; reader->current_low_pc = 0; + reader->current_str_offsets_base = 0; + reader->current_addr_base = 0; + reader->current_rnglists_base = 0; } static void -di_skip_die_attributes(char **p) +di_skip_die_attributes(const char **p) { for (;;) { uint64_t at = uleb128(p); @@ -996,7 +1068,7 @@ static void di_read_debug_abbrev_cu(DebugInfoReader *reader) { uint64_t prev = 0; - char *p = reader->q0; + const char *p = reader->q0; for (;;) { uint64_t abbrev_number = uleb128(&p); if (abbrev_number <= prev) break; @@ -1011,16 +1083,18 @@ di_read_debug_abbrev_cu(DebugInfoReader *reader) } static int -di_read_debug_line_cu(DebugInfoReader *reader) +di_read_debug_line_cu(DebugInfoReader *reader, FILE *errout) { const char *p; struct LineNumberProgramHeader header; p = (const char *)reader->debug_line_cu_end; - if (parse_debug_line_header(&p, &header)) + if (parse_debug_line_header(reader->obj, &p, &header, errout)) return -1; reader->debug_line_cu_end = (char *)header.cu_end; + reader->debug_line_format = header.format; + reader->debug_line_version = header.version; reader->debug_line_directories = (char *)header.include_directories; reader->debug_line_files = (char *)header.filenames; @@ -1028,6 +1102,13 @@ di_read_debug_line_cu(DebugInfoReader *reader) } static void +set_addr_idx_value(DebugInfoValue *v, uint64_t n) +{ + v->as.addr_idx = n; + v->type = VAL_addr; +} + +static void set_uint_value(DebugInfoValue *v, uint64_t n) { v->as.uint64 = n; @@ -1042,7 +1123,7 @@ set_int_value(DebugInfoValue *v, int64_t n) } static void -set_cstr_value(DebugInfoValue *v, char *s) +set_cstr_value(DebugInfoValue *v, const char *s) { v->as.ptr = s; v->off = 0; @@ -1050,7 +1131,7 @@ set_cstr_value(DebugInfoValue *v, char *s) } static void -set_cstrp_value(DebugInfoValue *v, char *s, uint64_t off) +set_cstrp_value(DebugInfoValue *v, const char *s, uint64_t off) { v->as.ptr = s; v->off = off; @@ -1058,7 +1139,7 @@ set_cstrp_value(DebugInfoValue *v, char *s, uint64_t off) } static void -set_data_value(DebugInfoValue *v, char *s) +set_data_value(DebugInfoValue *v, const char *s) { v->as.ptr = s; v->type = VAL_data; @@ -1074,19 +1155,46 @@ get_cstr_value(DebugInfoValue *v) } } -static void -debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v) +static const char * +resolve_strx(DebugInfoReader *reader, uint64_t idx) +{ + const char *p = reader->obj->debug_str_offsets.ptr + reader->current_str_offsets_base; + uint64_t off; + if (reader->format == 4) { + off = ((uint32_t *)p)[idx]; + } + else { + off = ((uint64_t *)p)[idx]; + } + return reader->obj->debug_str.ptr + off; +} + +static bool +debug_info_reader_read_addr_value_member(DebugInfoReader *reader, DebugInfoValue *v, int size) +{ + if (size == 4) { + set_uint_value(v, read_uint32(&reader->p)); + } else if (size == 8) { + set_uint_value(v, read_uint64(&reader->p)); + } else { + return false; + } + return true; +} + +#define debug_info_reader_read_addr_value(reader, v, mem) \ + if (!debug_info_reader_read_addr_value_member((reader), (v), (reader)->mem)) { \ + kprintf("unknown " #mem ":%d", (reader)->mem); \ + return false; \ + } + + +static bool +debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoValue *v, FILE *errout) { switch (form) { case DW_FORM_addr: - if (reader->address_size == 4) { - set_uint_value(v, read_uint32(&reader->p)); - } else if (reader->address_size == 8) { - set_uint_value(v, read_uint64(&reader->p)); - } else { - fprintf(stderr,"unknown address_size:%d", reader->address_size); - abort(); - } + debug_info_reader_read_addr_value(reader, v, address_size); break; case DW_FORM_block2: v->size = read_uint16(&reader->p); @@ -1138,13 +1246,12 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa set_uint_value(v, read_uleb128(reader)); break; case DW_FORM_ref_addr: - if (reader->address_size == 4) { - set_uint_value(v, read_uint32(&reader->p)); - } else if (reader->address_size == 8) { - set_uint_value(v, read_uint64(&reader->p)); + if (reader->current_version <= 2) { + // DWARF Version 2 specifies that references have + // the same size as an address on the target system + debug_info_reader_read_addr_value(reader, v, address_size); } else { - fprintf(stderr,"unknown address_size:%d", reader->address_size); - abort(); + debug_info_reader_read_addr_value(reader, v, format); } break; case DW_FORM_ref1: @@ -1186,11 +1293,10 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa set_uint_value(v, 1); break; case DW_FORM_strx: - set_uint_value(v, uleb128(&reader->p)); + set_cstr_value(v, resolve_strx(reader, uleb128(&reader->p))); break; case DW_FORM_addrx: - /* TODO: read .debug_addr */ - set_uint_value(v, uleb128(&reader->p)); + set_addr_idx_value(v, uleb128(&reader->p)); break; case DW_FORM_ref_sup4: set_uint_value(v, read_uint32(&reader->p)); @@ -1205,8 +1311,7 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa reader->p += v->size; break; case DW_FORM_line_strp: - set_uint_value(v, read_uint(reader)); - /* *p = reader->file + reader->line->sh_offset + ret; */ + set_cstrp_value(v, reader->obj->debug_line_str.ptr, read_uint(reader)); break; case DW_FORM_ref_sig8: set_uint_value(v, read_uint64(&reader->p)); @@ -1224,45 +1329,53 @@ debug_info_reader_read_value(DebugInfoReader *reader, uint64_t form, DebugInfoVa set_uint_value(v, read_uint64(&reader->p)); break; case DW_FORM_strx1: - set_uint_value(v, read_uint8(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint8(&reader->p))); break; case DW_FORM_strx2: - set_uint_value(v, read_uint16(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint16(&reader->p))); break; case DW_FORM_strx3: - set_uint_value(v, read_uint24(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint24(&reader->p))); break; case DW_FORM_strx4: - set_uint_value(v, read_uint32(&reader->p)); + set_cstr_value(v, resolve_strx(reader, read_uint32(&reader->p))); break; case DW_FORM_addrx1: - set_uint_value(v, read_uint8(&reader->p)); + set_addr_idx_value(v, read_uint8(&reader->p)); break; case DW_FORM_addrx2: - set_uint_value(v, read_uint16(&reader->p)); + set_addr_idx_value(v, read_uint16(&reader->p)); break; case DW_FORM_addrx3: - set_uint_value(v, read_uint24(&reader->p)); + set_addr_idx_value(v, read_uint24(&reader->p)); break; case DW_FORM_addrx4: - set_uint_value(v, read_uint32(&reader->p)); + set_addr_idx_value(v, read_uint32(&reader->p)); + break; + /* we have no support for actually reading the real values of these refs out + * of the .gnu_debugaltlink dwz-compressed debuginfo at the moment, but "read" + * them anyway so that we advance the reader by the right amount. */ + case DW_FORM_GNU_ref_alt: + case DW_FORM_GNU_strp_alt: + read_uint(reader); + set_uint_value(v, 0); break; case 0: goto fail; break; } - return; + return true; fail: - fprintf(stderr, "%d: unsupported form: %#"PRIx64"\n", __LINE__, form); - exit(1); + kprintf("%d: unsupported form: %#"PRIx64"\n", __LINE__, form); + return false; } /* find abbrev in current compilation unit */ -static char * -di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number) +static const char * +di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number, FILE *errout) { - char *p; + const char *p; if (abbrev_number < ABBREV_TABLE_SIZE) { return reader->abbrev_table[abbrev_number]; } @@ -1273,8 +1386,8 @@ di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number) di_skip_die_attributes(&p); for (uint64_t n = uleb128(&p); abbrev_number != n; n = uleb128(&p)) { if (n == 0) { - fprintf(stderr,"%d: Abbrev Number %"PRId64" not found\n",__LINE__, abbrev_number); - exit(1); + kprintf("%d: Abbrev Number %"PRId64" not found\n",__LINE__, abbrev_number); + return NULL; } uleb128(&p); /* tag */ p++; /* has_children */ @@ -1285,52 +1398,52 @@ di_find_abbrev(DebugInfoReader *reader, uint64_t abbrev_number) #if 0 static void -hexdump0(const unsigned char *p, size_t n) +hexdump0(const unsigned char *p, size_t n, FILE *errout) { size_t i; - fprintf(stderr, " 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); + kprintf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n"); for (i=0; i < n; i++){ switch (i & 15) { case 0: - fprintf(stderr, "%02zd: %02X ", i/16, p[i]); + kprintf("%02" PRIdSIZE ": %02X ", i/16, p[i]); break; case 15: - fprintf(stderr, "%02X\n", p[i]); + kprintf("%02X\n", p[i]); break; default: - fprintf(stderr, "%02X ", p[i]); + kprintf("%02X ", p[i]); break; } } if ((i & 15) != 15) { - fprintf(stderr, "\n"); + kprintf("\n"); } } -#define hexdump(p,n) hexdump0((const unsigned char *)p, n) +#define hexdump(p,n,e) hexdump0((const unsigned char *)p, n, e) static void -div_inspect(DebugInfoValue *v) +div_inspect(DebugInfoValue *v, FILE *errout) { switch (v->type) { case VAL_uint: - fprintf(stderr,"%d: type:%d size:%zx v:%lx\n",__LINE__,v->type,v->size,v->as.uint64); + kprintf("%d: type:%d size:%" PRIxSIZE " v:%"PRIx64"\n",__LINE__,v->type,v->size,v->as.uint64); break; case VAL_int: - fprintf(stderr,"%d: type:%d size:%zx v:%ld\n",__LINE__,v->type,v->size,(int64_t)v->as.uint64); + kprintf("%d: type:%d size:%" PRIxSIZE " v:%"PRId64"\n",__LINE__,v->type,v->size,(int64_t)v->as.uint64); break; case VAL_cstr: - fprintf(stderr,"%d: type:%d size:%zx v:'%s'\n",__LINE__,v->type,v->size,v->as.ptr); + kprintf("%d: type:%d size:%" PRIxSIZE " v:'%s'\n",__LINE__,v->type,v->size,v->as.ptr); break; case VAL_data: - fprintf(stderr,"%d: type:%d size:%zx v:\n",__LINE__,v->type,v->size); - hexdump(v->as.ptr, 16); + kprintf("%d: type:%d size:%" PRIxSIZE " v:\n",__LINE__,v->type,v->size); + hexdump(v->as.ptr, 16, errout); break; } } #endif static DIE * -di_read_die(DebugInfoReader *reader, DIE *die) +di_read_die(DebugInfoReader *reader, DIE *die, FILE *errout) { uint64_t abbrev_number = uleb128(&reader->p); if (abbrev_number == 0) { @@ -1338,7 +1451,7 @@ di_read_die(DebugInfoReader *reader, DIE *die) return NULL; } - reader->q = di_find_abbrev(reader, abbrev_number); + if (!(reader->q = di_find_abbrev(reader, abbrev_number, errout))) return NULL; die->pos = reader->p - reader->obj->debug_info.ptr - 1; die->tag = (int)uleb128(&reader->q); /* tag */ @@ -1350,27 +1463,109 @@ di_read_die(DebugInfoReader *reader, DIE *die) } static DebugInfoValue * -di_read_record(DebugInfoReader *reader, DebugInfoValue *vp) +di_read_record(DebugInfoReader *reader, DebugInfoValue *vp, FILE *errout) { uint64_t at = uleb128(&reader->q); uint64_t form = uleb128(&reader->q); if (!at || !form) return NULL; vp->at = at; vp->form = form; - debug_info_reader_read_value(reader, form, vp); + if (!debug_info_reader_read_value(reader, form, vp, errout)) return NULL; return vp; } -static void -di_skip_records(DebugInfoReader *reader) +static bool +di_skip_records(DebugInfoReader *reader, FILE *errout) { for (;;) { - DebugInfoValue v = {{}}; + DebugInfoValue v = {{0}}; uint64_t at = uleb128(&reader->q); uint64_t form = uleb128(&reader->q); - if (!at || !form) return; - debug_info_reader_read_value(reader, form, &v); + if (!at || !form) return true; + if (!debug_info_reader_read_value(reader, form, &v, errout)) return false; + } +} + +typedef struct addr_header { + const char *ptr; + uint64_t unit_length; + uint8_t format; + uint8_t address_size; + /* uint8_t segment_selector_size; */ +} addr_header_t; + +static bool +addr_header_init(obj_info_t *obj, addr_header_t *header, FILE *errout) +{ + const char *p = obj->debug_addr.ptr; + + header->ptr = p; + + if (!p) return true; + + header->unit_length = *(uint32_t *)p; + p += sizeof(uint32_t); + + header->format = 4; + if (header->unit_length == 0xffffffff) { + header->unit_length = *(uint64_t *)p; + p += sizeof(uint64_t); + header->format = 8; + } + + p += 2; /* version */ + header->address_size = *p++; + if (header->address_size != 4 && header->address_size != 8) { + kprintf("unknown address_size:%d", header->address_size); + return false; + } + p++; /* segment_selector_size */ + return true; +} + +static uint64_t +read_addr(addr_header_t *header, uint64_t addr_base, uint64_t idx) { + if (header->address_size == 4) { + return ((uint32_t*)(header->ptr + addr_base))[idx]; + } + else { + return ((uint64_t*)(header->ptr + addr_base))[idx]; + } +} + +typedef struct rnglists_header { + uint64_t unit_length; + uint8_t format; + uint8_t address_size; + uint32_t offset_entry_count; +} rnglists_header_t; + +static bool +rnglists_header_init(obj_info_t *obj, rnglists_header_t *header, FILE *errout) +{ + const char *p = obj->debug_rnglists.ptr; + + if (!p) return true; + + header->unit_length = *(uint32_t *)p; + p += sizeof(uint32_t); + + header->format = 4; + if (header->unit_length == 0xffffffff) { + header->unit_length = *(uint64_t *)p; + p += sizeof(uint64_t); + header->format = 8; } + + p += 2; /* version */ + header->address_size = *p++; + if (header->address_size != 4 && header->address_size != 8) { + kprintf("unknown address_size:%d", header->address_size); + return false; + } + p++; /* segment_selector_size */ + header->offset_entry_count = *(uint32_t *)p; + return true; } typedef struct { @@ -1383,50 +1578,54 @@ typedef struct { } ranges_t; static void -ranges_set(ranges_t *ptr, DebugInfoValue *v) +ranges_set(ranges_t *ptr, DebugInfoValue *v, addr_header_t *addr_header, uint64_t addr_base) { + uint64_t n = 0; + if (v->type == VAL_uint) { + n = v->as.uint64; + } + else if (v->type == VAL_addr) { + n = read_addr(addr_header, addr_base, v->as.addr_idx); + } switch (v->at) { case DW_AT_low_pc: - ptr->low_pc = v->as.uint64; + ptr->low_pc = n; ptr->low_pc_set = true; break; case DW_AT_high_pc: if (v->form == DW_FORM_addr) { - ptr->high_pc = v->as.uint64; + ptr->high_pc = n; } else { - ptr->high_pc = ptr->low_pc + v->as.uint64; + ptr->high_pc = ptr->low_pc + n; } ptr->high_pc_set = true; break; case DW_AT_ranges: - ptr->ranges = v->as.uint64; + ptr->ranges = n; ptr->ranges_set = true; break; } } static uint64_t -read_dw_form_addr(DebugInfoReader *reader, char **ptr) +read_dw_form_addr(DebugInfoReader *reader, const char **ptr, FILE *errout) { - char *p = *ptr; - *ptr = p + reader->format; - if (reader->format == 4) { + const char *p = *ptr; + *ptr = p + reader->address_size; + if (reader->address_size == 4) { return read_uint32(&p); - } else if (reader->format == 8) { - return read_uint64(&p); } else { - fprintf(stderr,"unknown address_size:%d", reader->address_size); - abort(); + return read_uint64(&p); } } static uintptr_t -ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) +ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr, rnglists_header_t *rnglists_header, FILE *errout) { if (ptr->high_pc_set) { if (ptr->ranges_set || !ptr->low_pc_set) { - exit(1); + return UINTPTR_MAX; } if (ptr->low_pc <= addr && addr <= ptr->high_pc) { return (uintptr_t)ptr->low_pc; @@ -1434,18 +1633,32 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) } else if (ptr->ranges_set) { /* TODO: support base address selection entry */ - char *p; + const char *p; uint64_t base = ptr->low_pc_set ? ptr->low_pc : reader->current_low_pc; - if (reader->obj->debug_rnglists.ptr) { - p = reader->obj->debug_rnglists.ptr + ptr->ranges; + bool base_valid = true; + if (reader->current_version >= 5) { + if (rnglists_header->offset_entry_count == 0) { + // DW_FORM_sec_offset + p = reader->obj->debug_rnglists.ptr + ptr->ranges + reader->current_rnglists_base; + } + else { + // DW_FORM_rnglistx + const char *offset_array = reader->obj->debug_rnglists.ptr + reader->current_rnglists_base; + if (rnglists_header->format == 4) { + p = offset_array + ((uint32_t *)offset_array)[ptr->ranges]; + } + else { + p = offset_array + ((uint64_t *)offset_array)[ptr->ranges]; + } + } for (;;) { uint8_t rle = read_uint8(&p); - uintptr_t base_address = 0; - uintptr_t from, to; + uintptr_t from = 0, to = 0; if (rle == DW_RLE_end_of_list) break; switch (rle) { case DW_RLE_base_addressx: uleb128(&p); + base_valid = false; /* not supported yet */ break; case DW_RLE_startx_endx: uleb128(&p); @@ -1456,26 +1669,28 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) uleb128(&p); break; case DW_RLE_offset_pair: - from = base_address + uleb128(&p); - to = base_address + uleb128(&p); - if (base + from <= addr && addr < base + to) { - return from; - } + if (!base_valid) break; + from = (uintptr_t)base + uleb128(&p); + to = (uintptr_t)base + uleb128(&p); break; case DW_RLE_base_address: - base_address = (uintptr_t)read_dw_form_addr(reader, &p); + base = read_dw_form_addr(reader, &p, errout); + base_valid = true; break; case DW_RLE_start_end: - read_dw_form_addr(reader, &p); - read_dw_form_addr(reader, &p); + from = (uintptr_t)read_dw_form_addr(reader, &p, errout); + to = (uintptr_t)read_dw_form_addr(reader, &p, errout); break; case DW_RLE_start_length: - read_dw_form_addr(reader, &p); - uleb128(&p); + from = (uintptr_t)read_dw_form_addr(reader, &p, errout); + to = from + uleb128(&p); break; } + if (from <= addr && addr < to) { + return from; + } } - return false; + return 0; } p = reader->obj->debug_ranges.ptr + ptr->ranges; for (;;) { @@ -1487,7 +1702,7 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) base = to; } else if (base + from <= addr && addr < base + to) { - return from; + return (uintptr_t)base + from; } } } @@ -1496,42 +1711,42 @@ ranges_include(DebugInfoReader *reader, ranges_t *ptr, uint64_t addr) return (uintptr_t)ptr->low_pc; } } - return false; + return 0; } #if 0 static void -ranges_inspect(DebugInfoReader *reader, ranges_t *ptr) +ranges_inspect(DebugInfoReader *reader, ranges_t *ptr, FILE *errout) { if (ptr->high_pc_set) { if (ptr->ranges_set || !ptr->low_pc_set) { - 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); + kprintf("low_pc_set:%d high_pc_set:%d ranges_set:%d\n",ptr->low_pc_set,ptr->high_pc_set,ptr->ranges_set); + return; } - fprintf(stderr,"low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc); + kprintf("low_pc:%"PRIx64" high_pc:%"PRIx64"\n",ptr->low_pc,ptr->high_pc); } else if (ptr->ranges_set) { 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); + kprintf("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,"%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to); + kprintf("%"PRIx64"-%"PRIx64" ",ptr->low_pc+from,ptr->low_pc+to); } - fprintf(stderr,"\n"); + kprintf("\n"); } else if (ptr->low_pc_set) { - fprintf(stderr,"low_pc:%"PRIx64"\n",ptr->low_pc); + kprintf("low_pc:%"PRIx64"\n",ptr->low_pc); } else { - fprintf(stderr,"empty\n"); + kprintf("empty\n"); } } #endif static int -di_read_cu(DebugInfoReader *reader) +di_read_cu(DebugInfoReader *reader, FILE *errout) { uint64_t unit_length; uint16_t version; @@ -1545,6 +1760,7 @@ di_read_cu(DebugInfoReader *reader) } reader->cu_end = reader->p + unit_length; version = read_uint16(&reader->p); + reader->current_version = version; if (version > 5) { return -1; } @@ -1557,56 +1773,102 @@ di_read_cu(DebugInfoReader *reader) debug_abbrev_offset = read_uint(reader); reader->address_size = read_uint8(&reader->p); } + if (reader->address_size != 4 && reader->address_size != 8) { + kprintf("unknown address_size:%d", reader->address_size); + return -1; + } reader->q0 = reader->obj->debug_abbrev.ptr + debug_abbrev_offset; reader->level = 0; di_read_debug_abbrev_cu(reader); - if (di_read_debug_line_cu(reader)) return -1; + if (di_read_debug_line_cu(reader, errout)) return -1; -#if defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER_BUILD_DATE) - /* Though DWARF specifies "the applicable base address defaults to the base - address of the compilation unit", but GCC seems to use zero as default */ -#else do { DIE die; - if (!di_read_die(reader, &die)) continue; + if (!di_read_die(reader, &die, errout)) continue; if (die.tag != DW_TAG_compile_unit) { - di_skip_records(reader); + if (!di_skip_records(reader, errout)) return -1; break; } + reader->current_str_offsets_base = 0; + reader->current_addr_base = 0; + reader->current_rnglists_base = 0; + + DebugInfoValue low_pc = {{0}}; /* enumerate abbrev */ for (;;) { - DebugInfoValue v = {{}}; - if (!di_read_record(reader, &v)) break; + DebugInfoValue v = {{0}}; + if (!di_read_record(reader, &v, errout)) break; switch (v.at) { case DW_AT_low_pc: - reader->current_low_pc = v.as.uint64; + // clang may output DW_AT_addr_base after DW_AT_low_pc. + // We need to resolve the DW_FORM_addr* after DW_AT_addr_base is parsed. + low_pc = v; + break; + case DW_AT_str_offsets_base: + reader->current_str_offsets_base = v.as.uint64; + break; + case DW_AT_addr_base: + reader->current_addr_base = v.as.uint64; + break; + case DW_AT_rnglists_base: + reader->current_rnglists_base = v.as.uint64; break; } } + // Resolve the DW_FORM_addr of DW_AT_low_pc + switch (low_pc.type) { + case VAL_uint: + reader->current_low_pc = low_pc.as.uint64; + break; + case VAL_addr: + { + addr_header_t header = {0}; + if (!addr_header_init(reader->obj, &header, errout)) return -1; + reader->current_low_pc = read_addr(&header, reader->current_addr_base, low_pc.as.addr_idx); + } + break; + } } while (0); -#endif + return 0; } static void -read_abstract_origin(DebugInfoReader *reader, uint64_t abstract_origin, line_info_t *line) +read_abstract_origin(DebugInfoReader *reader, uint64_t form, uint64_t abstract_origin, line_info_t *line, FILE *errout) { - char *p = reader->p; - char *q = reader->q; + const char *p = reader->p; + const char *q = reader->q; int level = reader->level; DIE die; - reader->p = reader->current_cu + abstract_origin; - if (!di_read_die(reader, &die)) goto finish; + switch (form) { + case DW_FORM_ref1: + case DW_FORM_ref2: + case DW_FORM_ref4: + case DW_FORM_ref8: + case DW_FORM_ref_udata: + reader->p = reader->current_cu + abstract_origin; + break; + case DW_FORM_ref_addr: + goto finish; /* not supported yet */ + case DW_FORM_ref_sig8: + goto finish; /* not supported yet */ + case DW_FORM_ref_sup4: + case DW_FORM_ref_sup8: + goto finish; /* not supported yet */ + default: + goto finish; + } + if (!di_read_die(reader, &die, errout)) goto finish; /* enumerate abbrev */ for (;;) { - DebugInfoValue v = {{}}; - if (!di_read_record(reader, &v)) break; + DebugInfoValue v = {{0}}; + if (!di_read_record(reader, &v, errout)) break; switch (v.at) { case DW_AT_name: line->sname = get_cstr_value(&v); @@ -1620,36 +1882,44 @@ read_abstract_origin(DebugInfoReader *reader, uint64_t abstract_origin, line_inf reader->level = level; } -static void +static bool debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, - line_info_t *lines, int offset) { + line_info_t *lines, int offset, FILE *errout) +{ + + addr_header_t addr_header = {0}; + if (!addr_header_init(reader->obj, &addr_header, errout)) return false; + + rnglists_header_t rnglists_header = {0}; + if (!rnglists_header_init(reader->obj, &rnglists_header, errout)) return false; + while (reader->p < reader->cu_end) { DIE die; - ranges_t ranges = {}; - line_info_t line = {}; + ranges_t ranges = {0}; + line_info_t line = {0}; - if (!di_read_die(reader, &die)) continue; - /* fprintf(stderr,"%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */ + if (!di_read_die(reader, &die, errout)) continue; + /* kprintf("%d:%tx: <%d>\n",__LINE__,die.pos,reader->level,die.tag); */ if (die.tag != DW_TAG_subprogram && die.tag != DW_TAG_inlined_subroutine) { skip_die: - di_skip_records(reader); + if (!di_skip_records(reader, errout)) return false; continue; } /* enumerate abbrev */ for (;;) { - DebugInfoValue v = {{}}; + DebugInfoValue v = {{0}}; /* ptrdiff_t pos = reader->p - reader->p0; */ - if (!di_read_record(reader, &v)) break; - /* fprintf(stderr,"\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */ - /* div_inspect(&v); */ + if (!di_read_record(reader, &v, errout)) break; + /* kprintf("\n%d:%tx: AT:%lx FORM:%lx\n",__LINE__,pos,v.at,v.form); */ + /* div_inspect(&v, errout); */ switch (v.at) { case DW_AT_name: 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_format, reader->debug_line_version, reader->debug_line_directories, reader->debug_line_files, &line, reader->obj, errout); break; case DW_AT_call_line: line.line = (int)v.as.uint64; @@ -1657,7 +1927,7 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, case DW_AT_low_pc: case DW_AT_high_pc: case DW_AT_ranges: - ranges_set(&ranges, &v); + ranges_set(&ranges, &v, &addr_header, reader->current_addr_base); break; case DW_AT_declaration: goto skip_die; @@ -1665,18 +1935,19 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, /* 1 or 3 */ break; /* goto skip_die; */ case DW_AT_abstract_origin: - read_abstract_origin(reader, v.as.uint64, &line); + read_abstract_origin(reader, v.form, v.as.uint64, &line, errout); break; /* goto skip_die; */ } } - /* ranges_inspect(reader, &ranges); */ - /* fprintf(stderr,"%d:%tx: %x ",__LINE__,diepos,die.tag); */ + /* ranges_inspect(reader, &ranges, errout); */ + /* kprintf("%d:%tx: %x ",__LINE__,diepos,die.tag); */ for (int i=offset; i < num_traces; i++) { uintptr_t addr = (uintptr_t)traces[i]; uintptr_t offset = addr - reader->obj->base_addr + reader->obj->vmaddr; - uintptr_t saddr = ranges_include(reader, &ranges, offset); + uintptr_t saddr = ranges_include(reader, &ranges, offset, &rnglists_header, errout); + if (saddr == UINTPTR_MAX) return false; if (saddr) { - /* fprintf(stderr, "%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */ + /* kprintf("%d:%tx: %d %lx->%lx %x %s: %s/%s %d %s %s %s\n",__LINE__,die.pos, i,addr,offset, die.tag,line.sname,line.dirname,line.filename,line.line,reader->obj->path,line.sname,lines[i].sname); */ if (lines[i].sname) { line_info_t *lp = malloc(sizeof(line_info_t)); memcpy(lp, &lines[i], sizeof(line_info_t)); @@ -1693,6 +1964,58 @@ debug_info_read(DebugInfoReader *reader, int num_traces, void **traces, } } } + return true; +} + +// This function parses the following attributes of Line Number Program Header in DWARF 5: +// +// * directory_entry_format_count +// * directory_entry_format +// * directories_count +// * directories +// +// or +// +// * file_name_entry_format_count +// * file_name_entry_format +// * file_names_count +// * file_names +// +// It records DW_LNCT_path and DW_LNCT_directory_index at the index "idx". +static const char * +parse_ver5_debug_line_header(const char *p, int idx, uint8_t format, + obj_info_t *obj, const char **out_path, + uint64_t *out_directory_index, FILE *errout) +{ + int i, j; + int entry_format_count = *(uint8_t *)p++; + const char *entry_format = p; + + /* skip the part of entry_format */ + for (i = 0; i < entry_format_count * 2; i++) uleb128(&p); + + int entry_count = (int)uleb128(&p); + + DebugInfoReader reader = {0}; + debug_info_reader_init(&reader, obj); + reader.format = format; + reader.p = p; + for (j = 0; j < entry_count; j++) { + const char *format = entry_format; + for (i = 0; i < entry_format_count; i++) { + DebugInfoValue v = {{0}}; + unsigned long dw_lnct = uleb128(&format); + unsigned long dw_form = uleb128(&format); + if (!debug_info_reader_read_value(&reader, dw_form, &v, errout)) return 0; + if (dw_lnct == 1 /* DW_LNCT_path */ && v.type == VAL_cstr && out_path) + *out_path = v.as.ptr + v.off; + if (dw_lnct == 2 /* DW_LNCT_directory_index */ && v.type == VAL_uint && out_directory_index) + *out_directory_index = v.as.uint64; + } + if (j == idx) return 0; + } + + return reader.p; } #ifdef USE_ELF @@ -1728,7 +2051,7 @@ fail: /* 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) + obj_info_t **objp, line_info_t *lines, int offset, FILE *errout) { int i, j; char *shstr; @@ -1823,8 +2146,11 @@ fill_lines(int num_traces, void **traces, int check_debuglink, ".debug_info", ".debug_line", ".debug_ranges", + ".debug_str_offsets", + ".debug_addr", ".debug_rnglists", - ".debug_str" + ".debug_str", + ".debug_line_str" }; for (j=0; j < DWARF_SECTION_COUNT; j++) { @@ -1885,9 +2211,10 @@ fill_lines(int num_traces, void **traces, int check_debuglink, debug_info_reader_init(&reader, obj); i = 0; while (reader.p < reader.pend) { - /* fprintf(stderr, "%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */ - if (di_read_cu(&reader)) goto use_symtab; - debug_info_read(&reader, num_traces, traces, lines, offset); + /* kprintf("%d:%tx: CU[%d]\n", __LINE__, reader.p - reader.obj->debug_info.ptr, i++); */ + if (di_read_cu(&reader, errout)) goto use_symtab; + if (!debug_info_read(&reader, num_traces, traces, lines, offset, errout)) + goto use_symtab; } } else { @@ -1927,14 +2254,14 @@ use_symtab: if (gnu_debuglink_shdr && check_debuglink) { follow_debuglink(file + gnu_debuglink_shdr->sh_offset, num_traces, traces, - objp, lines, offset); + objp, lines, offset, errout); } if (note_gnu_build_id && check_debuglink) { ElfW(Nhdr) *nhdr = (ElfW(Nhdr)*) (file + note_gnu_build_id->sh_offset); const char *build_id = (char *)(nhdr + 1) + nhdr->n_namesz; follow_debuglink_build_id(build_id, nhdr->n_descsz, num_traces, traces, - objp, lines, offset); + objp, lines, offset, errout); } goto finish; } @@ -1942,7 +2269,7 @@ use_symtab: if (parse_debug_line(num_traces, traces, obj->debug_line.ptr, obj->debug_line.size, - obj, lines, offset) == -1) + obj, lines, offset, errout) == -1) goto fail; finish: @@ -1954,7 +2281,7 @@ fail: /* 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) + obj_info_t **objp, line_info_t *lines, int offset, FILE *errout) { # ifdef __LP64__ # define LP(x) x##_64 @@ -2033,13 +2360,13 @@ fill_lines(int num_traces, void **traces, int check_debuglink, struct fat_header *fat = (struct fat_header *)file; char *q = file + sizeof(*fat); uint32_t nfat_arch = __builtin_bswap32(fat->nfat_arch); - /* fprintf(stderr,"%d: fat:%s %d\n",__LINE__, binary_filename,nfat_arch); */ + /* kprintf("%d: fat:%s %d\n",__LINE__, binary_filename,nfat_arch); */ for (uint32_t i = 0; i < nfat_arch; i++) { struct fat_arch *arch = (struct fat_arch *)q; cpu_type_t cputype = __builtin_bswap32(arch->cputype); cpu_subtype_t cpusubtype = __builtin_bswap32(arch->cpusubtype); uint32_t offset = __builtin_bswap32(arch->offset); - /* fprintf(stderr,"%d: fat %d %x/%x %x/%x\n",__LINE__, i, mhp->cputype,mhp->cpusubtype, cputype,cpusubtype); */ + /* kprintf("%d: fat %d %x/%x %x/%x\n",__LINE__, i, mhp->cputype,mhp->cpusubtype, cputype,cpusubtype); */ if (mhp->cputype == cputype && (cpu_subtype_t)(mhp->cpusubtype & ~CPU_SUBTYPE_MASK) == cpusubtype) { p = file + offset; @@ -2057,13 +2384,14 @@ fill_lines(int num_traces, void **traces, int check_debuglink, goto fail; } else { - kprintf("'%s' is not a " # ifdef __LP64__ - "64" +# define bitsize "64" # else - "32" +# define bitsize "32" # endif + kprintf("'%s' is not a " bitsize "-bit Mach-O file!\n",binary_filename); +# undef bitsize close(fd); goto fail; } @@ -2080,8 +2408,11 @@ found_mach_header: "__debug_info", "__debug_line", "__debug_ranges", + "__debug_str_offsets", + "__debug_addr", "__debug_rnglists", - "__debug_str" + "__debug_str", + "__debug_line_str", }; struct LP(segment_command) *scmd = (struct LP(segment_command) *)lcmd; if (strcmp(scmd->segname, "__TEXT") == 0) { @@ -2118,7 +2449,7 @@ found_mach_header: char *strtab = file + cmd->stroff, *sname = 0; uint32_t j; uintptr_t saddr = 0; - /* kprintf("[%2d]: %x/symtab %p\n", i, cmd->cmd, p); */ + /* kprintf("[%2d]: %x/symtab %p\n", i, cmd->cmd, (void *)p); */ for (j = 0; j < cmd->nsyms; j++) { uintptr_t symsize, d; struct LP(nlist) *e = &nl[j]; @@ -2152,15 +2483,16 @@ found_mach_header: DebugInfoReader reader; debug_info_reader_init(&reader, obj); while (reader.p < reader.pend) { - if (di_read_cu(&reader)) goto fail; - debug_info_read(&reader, num_traces, traces, lines, offset); + if (di_read_cu(&reader, errout)) goto fail; + if (!debug_info_read(&reader, num_traces, traces, lines, offset, errout)) + goto fail; } } if (parse_debug_line(num_traces, traces, obj->debug_line.ptr, obj->debug_line.size, - obj, lines, offset) == -1) + obj, lines, offset, errout) == -1) goto fail; return dladdr_fbase; @@ -2170,10 +2502,10 @@ fail: #endif #define HAVE_MAIN_EXE_PATH -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__DragonFly__) # include <sys/sysctl.h> #endif -/* ssize_t main_exe_path(void) +/* ssize_t main_exe_path(FILE *errout) * * store the path of the main executable to `binary_filename`, * and returns strlen(binary_filename). @@ -2181,7 +2513,7 @@ fail: */ #if defined(__linux__) || defined(__NetBSD__) static ssize_t -main_exe_path(void) +main_exe_path(FILE *errout) { # if defined(__linux__) # define PROC_SELF_EXE "/proc/self/exe" @@ -2193,9 +2525,9 @@ main_exe_path(void) binary_filename[len] = 0; return len; } -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) static ssize_t -main_exe_path(void) +main_exe_path(FILE *errout) { int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1}; size_t len = PATH_MAX; @@ -2209,7 +2541,7 @@ main_exe_path(void) } #elif defined(HAVE_LIBPROC_H) static ssize_t -main_exe_path(void) +main_exe_path(FILE *errout) { int len = proc_pidpath(getpid(), binary_filename, PATH_MAX); if (len == 0) return 0; @@ -2221,7 +2553,7 @@ main_exe_path(void) #endif static void -print_line0(line_info_t *line, void *address) +print_line0(line_info_t *line, void *address, FILE *errout) { uintptr_t addr = (uintptr_t)address; uintptr_t d = addr - line->saddr; @@ -2237,9 +2569,12 @@ print_line0(line_info_t *line, void *address) else if (!line->path) { kprintf("[0x%"PRIxPTR"]\n", addr); } - else if (!line->saddr || !line->sname) { + else if (!line->sname) { kprintf("%s(0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, addr-line->base_addr, addr); } + else if (!line->saddr) { + kprintf("%s(%s) [0x%"PRIxPTR"]\n", line->path, line->sname, addr); + } else if (line->line <= 0) { kprintf("%s(%s+0x%"PRIxPTR") [0x%"PRIxPTR"]\n", line->path, line->sname, d, addr); @@ -2259,16 +2594,16 @@ print_line0(line_info_t *line, void *address) } static void -print_line(line_info_t *line, void *address) +print_line(line_info_t *line, void *address, FILE *errout) { - print_line0(line, address); + print_line0(line, address, errout); if (line->next) { - print_line(line->next, NULL); + print_line(line->next, NULL, errout); } } void -rb_dump_backtrace_with_lines(int num_traces, void **traces) +rb_dump_backtrace_with_lines(int num_traces, void **traces, FILE *errout) { int i; /* async-signal unsafe */ @@ -2276,17 +2611,18 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces) obj_info_t *obj = NULL; /* 2 is NULL + main executable */ void **dladdr_fbases = (void **)calloc(num_traces+2, sizeof(void *)); + #ifdef HAVE_MAIN_EXE_PATH char *main_path = NULL; /* used on printing backtrace */ ssize_t len; - if ((len = main_exe_path()) > 0) { + if ((len = main_exe_path(errout)) > 0) { main_path = (char *)alloca(len + 1); if (main_path) { uintptr_t addr; memcpy(main_path, binary_filename, len+1); append_obj(&obj); obj->path = main_path; - addr = fill_lines(num_traces, traces, 1, &obj, lines, -1); + addr = fill_lines(num_traces, traces, 1, &obj, lines, -1, errout); if (addr != (uintptr_t)-1) { dladdr_fbases[0] = (void *)addr; } @@ -2306,8 +2642,8 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces) /* if the binary is strip-ed, this may effect */ for (p=dladdr_fbases; *p; p++) { if (*p == info.dli_fbase) { - lines[i].path = info.dli_fname; - lines[i].sname = info.dli_sname; + if (info.dli_fname) lines[i].path = info.dli_fname; + if (info.dli_sname) lines[i].sname = info.dli_sname; goto next_line; } } @@ -2317,11 +2653,13 @@ rb_dump_backtrace_with_lines(int num_traces, void **traces) obj->base_addr = (uintptr_t)info.dli_fbase; path = info.dli_fname; obj->path = path; - lines[i].path = path; - lines[i].sname = info.dli_sname; - lines[i].saddr = (uintptr_t)info.dli_saddr; + if (path) lines[i].path = path; + if (info.dli_sname) { + lines[i].sname = info.dli_sname; + lines[i].saddr = (uintptr_t)info.dli_saddr; + } strlcpy(binary_filename, path, PATH_MAX); - if (fill_lines(num_traces, traces, 1, &obj, lines, i) == (uintptr_t)-1) + if (fill_lines(num_traces, traces, 1, &obj, lines, i, errout) == (uintptr_t)-1) break; } next_line: @@ -2330,7 +2668,7 @@ next_line: /* output */ for (i = 0; i < num_traces; i++) { - print_line(&lines[i], traces[i]); + print_line(&lines[i], traces[i], errout); /* FreeBSD's backtrace may show _start and so on */ if (lines[i].sname && strcmp("main", lines[i].sname) == 0) @@ -2364,435 +2702,8 @@ next_line: free(dladdr_fbases); } -/* From FreeBSD's lib/libstand/printf.c */ -/*- - * Copyright (c) 1986, 1988, 1991, 1993 - * The Regents of the University of California. All rights reserved. - * (c) UNIX System Laboratories, Inc. - * All or some portions of this file are derived from material licensed - * to the University of California by American Telephone and Telegraph - * Co. or Unix System Laboratories, Inc. and are reproduced herein with - * the permission of UNIX System Laboratories, Inc. - * - * 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. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. - * - * @(#)subr_prf.c 8.3 (Berkeley) 1/21/94 - */ - -#include <stdarg.h> -#define MAXNBUF (sizeof(intmax_t) * CHAR_BIT + 1) -static inline int toupper(int c) { return ('A' <= c && c <= 'Z') ? (c&0x5f) : c; } -#define hex2ascii(hex) (hex2ascii_data[hex]) -static const char hex2ascii_data[] = "0123456789abcdefghijklmnopqrstuvwxyz"; -static inline int imax(int a, int b) { return (a > b ? a : b); } -static int kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap); - -static void putce(int c) -{ - char s[1]; - ssize_t ret; +#undef kprintf - s[0] = (char)c; - ret = write(2, s, 1); - (void)ret; -} - -static int -kprintf(const char *fmt, ...) -{ - va_list ap; - int retval; - - va_start(ap, fmt); - retval = kvprintf(fmt, putce, NULL, 10, ap); - va_end(ap); - return retval; -} - -/* - * Put a NUL-terminated ASCII number (base <= 36) in a buffer in reverse - * order; return an optional length and a pointer to the last character - * written in the buffer (i.e., the first character of the string). - * The buffer pointed to by `nbuf' must have length >= MAXNBUF. - */ -static char * -ksprintn(char *nbuf, uintmax_t num, int base, int *lenp, int upper) -{ - char *p, c; - - p = nbuf; - *p = '\0'; - do { - c = hex2ascii(num % base); - *++p = upper ? toupper(c) : c; - } while (num /= base); - if (lenp) - *lenp = (int)(p - nbuf); - return (p); -} - -/* - * Scaled down version of printf(3). - * - * Two additional formats: - * - * The format %b is supported to decode error registers. - * Its usage is: - * - * printf("reg=%b\n", regval, "<base><arg>*"); - * - * where <base> is the output base expressed as a control character, e.g. - * \10 gives octal; \20 gives hex. Each arg is a sequence of characters, - * the first of which gives the bit number to be inspected (origin 1), and - * the next characters (up to a control character, i.e. a character <= 32), - * give the name of the register. Thus: - * - * kvprintf("reg=%b\n", 3, "\10\2BITTWO\1BITONE\n"); - * - * would produce output: - * - * reg=3<BITTWO,BITONE> - * - * XXX: %D -- Hexdump, takes pointer and separator string: - * ("%6D", ptr, ":") -> XX:XX:XX:XX:XX:XX - * ("%*D", len, ptr, " " -> XX XX XX XX ... - */ -static int -kvprintf(char const *fmt, void (*func)(int), void *arg, int radix, va_list ap) -{ -#define PCHAR(c) {int cc=(c); if (func) (*func)(cc); else *d++ = cc; retval++; } - char nbuf[MAXNBUF]; - char *d; - const char *p, *percent, *q; - unsigned char *up; - int ch, n; - uintmax_t num; - int base, lflag, qflag, tmp, width, ladjust, sharpflag, neg, sign, dot; - int cflag, hflag, jflag, tflag, zflag; - int dwidth, upper; - char padc; - int stop = 0, retval = 0; - - num = 0; - if (!func) - d = (char *) arg; - else - d = NULL; - - if (fmt == NULL) - fmt = "(fmt null)\n"; - - if (radix < 2 || radix > 36) - radix = 10; - - for (;;) { - padc = ' '; - width = 0; - while ((ch = (unsigned char)*fmt++) != '%' || stop) { - if (ch == '\0') - return (retval); - PCHAR(ch); - } - percent = fmt - 1; - qflag = 0; lflag = 0; ladjust = 0; sharpflag = 0; neg = 0; - sign = 0; dot = 0; dwidth = 0; upper = 0; - cflag = 0; hflag = 0; jflag = 0; tflag = 0; zflag = 0; -reswitch: switch (ch = (unsigned char)*fmt++) { - case '.': - dot = 1; - goto reswitch; - case '#': - sharpflag = 1; - goto reswitch; - case '+': - sign = 1; - goto reswitch; - case '-': - ladjust = 1; - goto reswitch; - case '%': - PCHAR(ch); - break; - case '*': - if (!dot) { - width = va_arg(ap, int); - if (width < 0) { - ladjust = !ladjust; - width = -width; - } - } else { - dwidth = va_arg(ap, int); - } - goto reswitch; - case '0': - if (!dot) { - padc = '0'; - goto reswitch; - } - case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - for (n = 0;; ++fmt) { - n = n * 10 + ch - '0'; - ch = *fmt; - if (ch < '0' || ch > '9') - break; - } - if (dot) - dwidth = n; - else - width = n; - goto reswitch; - case 'b': - num = (unsigned int)va_arg(ap, int); - p = va_arg(ap, char *); - for (q = ksprintn(nbuf, num, *p++, NULL, 0); *q;) - PCHAR(*q--); - - if (num == 0) - break; - - for (tmp = 0; *p;) { - n = *p++; - if (num & (1 << (n - 1))) { - PCHAR(tmp ? ',' : '<'); - for (; (n = *p) > ' '; ++p) - PCHAR(n); - tmp = 1; - } else - for (; *p > ' '; ++p) - continue; - } - if (tmp) - PCHAR('>'); - break; - case 'c': - PCHAR(va_arg(ap, int)); - break; - case 'D': - up = va_arg(ap, unsigned char *); - p = va_arg(ap, char *); - if (!width) - width = 16; - while(width--) { - PCHAR(hex2ascii(*up >> 4)); - PCHAR(hex2ascii(*up & 0x0f)); - up++; - if (width) - for (q=p;*q;q++) - PCHAR(*q); - } - break; - case 'd': - case 'i': - base = 10; - sign = 1; - goto handle_sign; - case 'h': - if (hflag) { - hflag = 0; - cflag = 1; - } else - hflag = 1; - goto reswitch; - case 'j': - jflag = 1; - goto reswitch; - case 'l': - if (lflag) { - lflag = 0; - qflag = 1; - } else - lflag = 1; - goto reswitch; - case 'n': - if (jflag) - *(va_arg(ap, intmax_t *)) = retval; - else if (qflag) - *(va_arg(ap, int64_t *)) = retval; - else if (lflag) - *(va_arg(ap, long *)) = retval; - else if (zflag) - *(va_arg(ap, size_t *)) = retval; - else if (hflag) - *(va_arg(ap, short *)) = retval; - else if (cflag) - *(va_arg(ap, char *)) = retval; - else - *(va_arg(ap, int *)) = retval; - break; - case 'o': - base = 8; - goto handle_nosign; - case 'p': - base = 16; - sharpflag = (width == 0); - sign = 0; - num = (uintptr_t)va_arg(ap, void *); - goto number; - case 'q': - qflag = 1; - goto reswitch; - case 'r': - base = radix; - if (sign) - goto handle_sign; - goto handle_nosign; - case 's': - p = va_arg(ap, char *); - if (p == NULL) - p = "(null)"; - if (!dot) - n = (int)strlen (p); - else - for (n = 0; n < dwidth && p[n]; n++) - continue; - - width -= n; - - if (!ladjust && width > 0) - while (width--) - PCHAR(padc); - while (n--) - PCHAR(*p++); - if (ladjust && width > 0) - while (width--) - PCHAR(padc); - break; - case 't': - tflag = 1; - goto reswitch; - case 'u': - base = 10; - goto handle_nosign; - case 'X': - upper = 1; - case 'x': - base = 16; - goto handle_nosign; - case 'y': - base = 16; - sign = 1; - goto handle_sign; - case 'z': - zflag = 1; - goto reswitch; -handle_nosign: - sign = 0; - if (jflag) - num = va_arg(ap, uintmax_t); - else if (qflag) - num = va_arg(ap, uint64_t); - else if (tflag) - num = va_arg(ap, ptrdiff_t); - else if (lflag) - num = va_arg(ap, unsigned long); - else if (zflag) - num = va_arg(ap, size_t); - else if (hflag) - num = (unsigned short)va_arg(ap, int); - else if (cflag) - num = (unsigned char)va_arg(ap, int); - else - num = va_arg(ap, unsigned int); - goto number; -handle_sign: - if (jflag) - num = va_arg(ap, intmax_t); - else if (qflag) - num = va_arg(ap, int64_t); - else if (tflag) - num = va_arg(ap, ptrdiff_t); - else if (lflag) - num = va_arg(ap, long); - else if (zflag) - num = va_arg(ap, ssize_t); - else if (hflag) - num = (short)va_arg(ap, int); - else if (cflag) - num = (char)va_arg(ap, int); - else - num = va_arg(ap, int); -number: - if (sign && (intmax_t)num < 0) { - neg = 1; - num = -(intmax_t)num; - } - p = ksprintn(nbuf, num, base, &n, upper); - tmp = 0; - if (sharpflag && num != 0) { - if (base == 8) - tmp++; - else if (base == 16) - tmp += 2; - } - if (neg) - tmp++; - - if (!ladjust && padc == '0') - dwidth = width - tmp; - width -= tmp + imax(dwidth, n); - dwidth -= n; - if (!ladjust) - while (width-- > 0) - PCHAR(' '); - if (neg) - PCHAR('-'); - if (sharpflag && num != 0) { - if (base == 8) { - PCHAR('0'); - } else if (base == 16) { - PCHAR('0'); - PCHAR('x'); - } - } - while (dwidth-- > 0) - PCHAR('0'); - - while (*p) - PCHAR(*p--); - - if (ladjust) - while (width-- > 0) - PCHAR(' '); - - break; - default: - while (percent < fmt) - PCHAR(*percent++); - /* - * Since we ignore an formatting argument it is no - * longer safe to obey the remaining formatting - * arguments as the arguments will no longer match - * the format specs. - */ - stop = 1; - break; - } - } -#undef PCHAR -} #else /* defined(USE_ELF) */ #error not supported #endif |