From: Julian Seward Date: Sat, 1 Jun 2002 14:21:36 +0000 (+0000) Subject: Preliminary commit of DWARF2 debug info reader from X-Git-Tag: svn/VALGRIND_1_0_3~120 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f41a9f35868efbfac92ffdeac15ddf7c6a169e5c;p=thirdparty%2Fvalgrind.git Preliminary commit of DWARF2 debug info reader from Daniel Berlin git-svn-id: svn://svn.valgrind.org/valgrind/trunk@347 --- diff --git a/coregrind/vg_symtab2.c b/coregrind/vg_symtab2.c index e5e40c835a..c869b6e4a7 100644 --- a/coregrind/vg_symtab2.c +++ b/coregrind/vg_symtab2.c @@ -610,6 +610,405 @@ void addLineInfo ( SegInfo* si, } +static +UInt read_leb128 ( UChar* data, Int* length_return, Int sign ) +{ + UInt result = 0; + UInt num_read = 0; + Int shift = 0; + UChar byte; + + do + { + byte = * data ++; + num_read ++; + + result |= (byte & 0x7f) << shift; + + shift += 7; + + } + while (byte & 0x80); + + if (length_return != NULL) + * length_return = num_read; + + if (sign && (shift < 32) && (byte & 0x40)) + result |= -1 << shift; + + return result; +} + +/* Structure found in the .debug_line section. */ +typedef struct +{ + UChar li_length [4]; + UChar li_version [2]; + UChar li_prologue_length [4]; + UChar li_min_insn_length [1]; + UChar li_default_is_stmt [1]; + UChar li_line_base [1]; + UChar li_line_range [1]; + UChar li_opcode_base [1]; +} +DWARF2_External_LineInfo; +typedef struct +{ + UInt li_length; + UShort li_version; + UInt li_prologue_length; + UChar li_min_insn_length; + UChar li_default_is_stmt; + Int li_line_base; + UChar li_line_range; + UChar li_opcode_base; +} +DWARF2_Internal_LineInfo; +/* Line number opcodes. */ +enum dwarf_line_number_ops + { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + /* DWARF 3. */ + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 + }; + +/* Line number extended opcodes. */ +enum dwarf_line_number_x_ops + { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3 + }; + +typedef struct State_Machine_Registers +{ + Addr address; + UInt file; + UInt line; + UInt column; + Int is_stmt; + Int basic_block; + Int end_sequence; +/* This variable hold the number of the last entry seen + in the File Table. */ + UInt last_file_entry; +} SMR; + +static SMR state_machine_regs; + +static void +reset_state_machine (is_stmt) + Int is_stmt; +{ + state_machine_regs.address = 0; + state_machine_regs.file = 1; + state_machine_regs.line = 1; + state_machine_regs.column = 0; + state_machine_regs.is_stmt = is_stmt; + state_machine_regs.basic_block = 0; + state_machine_regs.end_sequence = 0; + state_machine_regs.last_file_entry = 0; +} + +/* Handled an extend line op. Returns true if this is the end + of sequence. */ +static int +process_extended_line_op (si, fnames, data, is_stmt, pointer_size) + SegInfo *si; + UInt **fnames; + UChar * data; + Int is_stmt; + Int pointer_size; +{ + UChar op_code; + Int bytes_read; + UInt len; + UChar * name; + Addr adr; + + len = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + + if (len == 0) + { + VG_(message) (Vg_UserMsg,"badly formed extended line op encountered!\n"); + return bytes_read; + } + + len += bytes_read; + op_code = * data ++; + + + switch (op_code) + { + case DW_LNE_end_sequence: + addLineInfo (si, (*fnames)[state_machine_regs.file], si->offset + (state_machine_regs.address - 1), si->offset + (state_machine_regs.address), 0, 0); + reset_state_machine (is_stmt); + break; + + case DW_LNE_set_address: + /* XXX: Pointer size could be 8 */ + adr = *((Addr *)data); + state_machine_regs.address = adr; + break; + + case DW_LNE_define_file: + + ++ state_machine_regs.last_file_entry; + name = data; + if (*fnames == NULL) + *fnames = VG_(malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2); + else + *fnames = VG_(realloc)(VG_AR_SYMTAB, *fnames, sizeof (UInt) * (state_machine_regs.last_file_entry + 1)); + (*fnames)[state_machine_regs.last_file_entry] = addStr (si,name); + data += VG_(strlen) ((char *) data) + 1; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + break; + + default: + break; + } + + return len; +} + + +static Int +handle_debug_lines (si, section, start) + SegInfo * si; + Elf32_Shdr * section; + UChar * start; +{ + DWARF2_External_LineInfo * external; + DWARF2_Internal_LineInfo info; + UChar * standard_opcodes; + UChar * data = start; + UChar * end = start + section->sh_size; + UChar * end_of_sequence; + UInt *fnames = NULL; + + + while (data < end) + { + external = (DWARF2_External_LineInfo *) data; + + /* Check the length of the block. */ + info.li_length =*((UInt *)(external->li_length)); + + if (info.li_length == 0xffffffff) + { + VG_(message) (Vg_UserMsg,"64-bit DWARF line info is not supported yet."); + break; + } + + if (info.li_length + sizeof (external->li_length) > section->sh_size) + { + VG_(message) + (Vg_UserMsg,"The line info appears to be corrupt - the section is too small"); + return 0; + } + + /* Check its version number. */ + info.li_version =*((UShort *) (external->li_version)); + if (info.li_version != 2) + { + VG_(message) (Vg_UserMsg,"Only DWARF version 2 line info is currently supported."); + return 0; + } + + info.li_prologue_length = *((UInt *)(external->li_prologue_length)); + info.li_min_insn_length = *((UChar *) (external->li_min_insn_length)); + info.li_default_is_stmt = *((UChar *) (external->li_default_is_stmt)); + info.li_line_base = *((Int *) (external->li_line_base)); + info.li_line_range = *((UChar *) (external->li_line_range)); + info.li_opcode_base = *((UChar *) (external->li_opcode_base)); + + /* Sign extend the line base field. */ + info.li_line_base <<= 24; + info.li_line_base >>= 24; + + end_of_sequence = data + info.li_length + sizeof (external->li_length); + + reset_state_machine (info.li_default_is_stmt); + + /* Read the contents of the Opcodes table. */ + standard_opcodes = data + sizeof (* external); + + + + /* Read the contents of the Directory table. */ + data = standard_opcodes + info.li_opcode_base - 1; + + if (* data == 0); + else + { + /* We ignore the directory table, since gcc gives the entire path as part of the filename */ + while (* data != 0) + { + data += VG_(strlen) ((char *) data) + 1; + } + } + + /* Skip the NUL at the end of the table. */ + data ++; + + /* Read the contents of the File Name table. */ + if (* data == 0); + else + { + + while (* data != 0) + { + UChar * name; + Int bytes_read; + + ++ state_machine_regs.last_file_entry; + name = data; + /* Since we don't have realloc (0, ....) == malloc (...) semantics, we need to malloc the first time. */ + + if (fnames == NULL) + fnames = VG_(malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2); + else + fnames = VG_(realloc)(VG_AR_SYMTAB, fnames, sizeof (UInt) * (state_machine_regs.last_file_entry + 1)); + data += VG_(strlen) ((char *) data) + 1; + fnames[state_machine_regs.last_file_entry] = addStr (si,name); + + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + } + } + + /* Skip the NUL at the end of the table. */ + data ++; + + /* Now display the statements. */ + + while (data < end_of_sequence) + { + UChar op_code; + Int adv; + Int bytes_read; + + op_code = * data ++; + + if (op_code >= info.li_opcode_base) + { + Int advAddr; + op_code -= info.li_opcode_base; + adv = (op_code / info.li_line_range) * info.li_min_insn_length; + advAddr = adv; + state_machine_regs.address += adv; + adv = (op_code % info.li_line_range) + info.li_line_base; + addLineInfo (si, fnames[state_machine_regs.file], si->offset + (state_machine_regs.address - advAddr), si->offset + (state_machine_regs.address), state_machine_regs.line, 0); + state_machine_regs.line += adv; + } + else switch (op_code) + { + case DW_LNS_extended_op: + data += process_extended_line_op (si, &fnames, data, info.li_default_is_stmt, + sizeof (Addr)); + break; + + case DW_LNS_copy: + addLineInfo (si, fnames[state_machine_regs.file], si->offset + state_machine_regs.address, si->offset + (state_machine_regs.address + 1), state_machine_regs.line , 0); + break; + + case DW_LNS_advance_pc: + adv = info.li_min_insn_length * read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.address += adv; + break; + + case DW_LNS_advance_line: + adv = read_leb128 (data, & bytes_read, 1); + data += bytes_read; + state_machine_regs.line += adv; + break; + + case DW_LNS_set_file: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.file = adv; + break; + + case DW_LNS_set_column: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.column = adv; + break; + + case DW_LNS_negate_stmt: + adv = state_machine_regs.is_stmt; + adv = ! adv; + state_machine_regs.is_stmt = adv; + break; + + case DW_LNS_set_basic_block: + state_machine_regs.basic_block = 1; + break; + + case DW_LNS_const_add_pc: + adv = (((255 - info.li_opcode_base) / info.li_line_range) + * info.li_min_insn_length); + state_machine_regs.address += adv; + break; + + case DW_LNS_fixed_advance_pc: + /* XXX: Need something to get 2 bytes */ + adv = *((UShort *)data); + data += 2; + state_machine_regs.address += adv; + break; + + case DW_LNS_set_prologue_end: + break; + + case DW_LNS_set_epilogue_begin: + break; + + case DW_LNS_set_isa: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + break; + + default: + { + int j; + for (j = standard_opcodes[op_code - 1]; j > 0 ; --j) + { + read_leb128 (data, &bytes_read, 0); + data += bytes_read; + } + } + break; + } + } + VG_(free)(VG_AR_SYMTAB, fnames); + fnames = NULL; + } + + return 1; +} + /* Read the symbols from the object/exe specified by the SegInfo into the tables within the supplied SegInfo. */ static @@ -919,6 +1318,9 @@ void vg_read_lib_symbols ( SegInfo* si ) stabstr = (UChar*)(oimage + shdr[i].sh_offset); stabstr_sz = shdr[i].sh_size; } + if (0 == VG_(strcmp)(".debug_line",sh_strtab + shdr[i].sh_name)) { + handle_debug_lines (si, &shdr[i], (UChar *)(oimage + shdr[i].sh_offset)); + } } if (stab == NULL || stabstr == NULL) { diff --git a/vg_symtab2.c b/vg_symtab2.c index e5e40c835a..c869b6e4a7 100644 --- a/vg_symtab2.c +++ b/vg_symtab2.c @@ -610,6 +610,405 @@ void addLineInfo ( SegInfo* si, } +static +UInt read_leb128 ( UChar* data, Int* length_return, Int sign ) +{ + UInt result = 0; + UInt num_read = 0; + Int shift = 0; + UChar byte; + + do + { + byte = * data ++; + num_read ++; + + result |= (byte & 0x7f) << shift; + + shift += 7; + + } + while (byte & 0x80); + + if (length_return != NULL) + * length_return = num_read; + + if (sign && (shift < 32) && (byte & 0x40)) + result |= -1 << shift; + + return result; +} + +/* Structure found in the .debug_line section. */ +typedef struct +{ + UChar li_length [4]; + UChar li_version [2]; + UChar li_prologue_length [4]; + UChar li_min_insn_length [1]; + UChar li_default_is_stmt [1]; + UChar li_line_base [1]; + UChar li_line_range [1]; + UChar li_opcode_base [1]; +} +DWARF2_External_LineInfo; +typedef struct +{ + UInt li_length; + UShort li_version; + UInt li_prologue_length; + UChar li_min_insn_length; + UChar li_default_is_stmt; + Int li_line_base; + UChar li_line_range; + UChar li_opcode_base; +} +DWARF2_Internal_LineInfo; +/* Line number opcodes. */ +enum dwarf_line_number_ops + { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + /* DWARF 3. */ + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 + }; + +/* Line number extended opcodes. */ +enum dwarf_line_number_x_ops + { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3 + }; + +typedef struct State_Machine_Registers +{ + Addr address; + UInt file; + UInt line; + UInt column; + Int is_stmt; + Int basic_block; + Int end_sequence; +/* This variable hold the number of the last entry seen + in the File Table. */ + UInt last_file_entry; +} SMR; + +static SMR state_machine_regs; + +static void +reset_state_machine (is_stmt) + Int is_stmt; +{ + state_machine_regs.address = 0; + state_machine_regs.file = 1; + state_machine_regs.line = 1; + state_machine_regs.column = 0; + state_machine_regs.is_stmt = is_stmt; + state_machine_regs.basic_block = 0; + state_machine_regs.end_sequence = 0; + state_machine_regs.last_file_entry = 0; +} + +/* Handled an extend line op. Returns true if this is the end + of sequence. */ +static int +process_extended_line_op (si, fnames, data, is_stmt, pointer_size) + SegInfo *si; + UInt **fnames; + UChar * data; + Int is_stmt; + Int pointer_size; +{ + UChar op_code; + Int bytes_read; + UInt len; + UChar * name; + Addr adr; + + len = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + + if (len == 0) + { + VG_(message) (Vg_UserMsg,"badly formed extended line op encountered!\n"); + return bytes_read; + } + + len += bytes_read; + op_code = * data ++; + + + switch (op_code) + { + case DW_LNE_end_sequence: + addLineInfo (si, (*fnames)[state_machine_regs.file], si->offset + (state_machine_regs.address - 1), si->offset + (state_machine_regs.address), 0, 0); + reset_state_machine (is_stmt); + break; + + case DW_LNE_set_address: + /* XXX: Pointer size could be 8 */ + adr = *((Addr *)data); + state_machine_regs.address = adr; + break; + + case DW_LNE_define_file: + + ++ state_machine_regs.last_file_entry; + name = data; + if (*fnames == NULL) + *fnames = VG_(malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2); + else + *fnames = VG_(realloc)(VG_AR_SYMTAB, *fnames, sizeof (UInt) * (state_machine_regs.last_file_entry + 1)); + (*fnames)[state_machine_regs.last_file_entry] = addStr (si,name); + data += VG_(strlen) ((char *) data) + 1; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + break; + + default: + break; + } + + return len; +} + + +static Int +handle_debug_lines (si, section, start) + SegInfo * si; + Elf32_Shdr * section; + UChar * start; +{ + DWARF2_External_LineInfo * external; + DWARF2_Internal_LineInfo info; + UChar * standard_opcodes; + UChar * data = start; + UChar * end = start + section->sh_size; + UChar * end_of_sequence; + UInt *fnames = NULL; + + + while (data < end) + { + external = (DWARF2_External_LineInfo *) data; + + /* Check the length of the block. */ + info.li_length =*((UInt *)(external->li_length)); + + if (info.li_length == 0xffffffff) + { + VG_(message) (Vg_UserMsg,"64-bit DWARF line info is not supported yet."); + break; + } + + if (info.li_length + sizeof (external->li_length) > section->sh_size) + { + VG_(message) + (Vg_UserMsg,"The line info appears to be corrupt - the section is too small"); + return 0; + } + + /* Check its version number. */ + info.li_version =*((UShort *) (external->li_version)); + if (info.li_version != 2) + { + VG_(message) (Vg_UserMsg,"Only DWARF version 2 line info is currently supported."); + return 0; + } + + info.li_prologue_length = *((UInt *)(external->li_prologue_length)); + info.li_min_insn_length = *((UChar *) (external->li_min_insn_length)); + info.li_default_is_stmt = *((UChar *) (external->li_default_is_stmt)); + info.li_line_base = *((Int *) (external->li_line_base)); + info.li_line_range = *((UChar *) (external->li_line_range)); + info.li_opcode_base = *((UChar *) (external->li_opcode_base)); + + /* Sign extend the line base field. */ + info.li_line_base <<= 24; + info.li_line_base >>= 24; + + end_of_sequence = data + info.li_length + sizeof (external->li_length); + + reset_state_machine (info.li_default_is_stmt); + + /* Read the contents of the Opcodes table. */ + standard_opcodes = data + sizeof (* external); + + + + /* Read the contents of the Directory table. */ + data = standard_opcodes + info.li_opcode_base - 1; + + if (* data == 0); + else + { + /* We ignore the directory table, since gcc gives the entire path as part of the filename */ + while (* data != 0) + { + data += VG_(strlen) ((char *) data) + 1; + } + } + + /* Skip the NUL at the end of the table. */ + data ++; + + /* Read the contents of the File Name table. */ + if (* data == 0); + else + { + + while (* data != 0) + { + UChar * name; + Int bytes_read; + + ++ state_machine_regs.last_file_entry; + name = data; + /* Since we don't have realloc (0, ....) == malloc (...) semantics, we need to malloc the first time. */ + + if (fnames == NULL) + fnames = VG_(malloc)(VG_AR_SYMTAB, sizeof (UInt) * 2); + else + fnames = VG_(realloc)(VG_AR_SYMTAB, fnames, sizeof (UInt) * (state_machine_regs.last_file_entry + 1)); + data += VG_(strlen) ((char *) data) + 1; + fnames[state_machine_regs.last_file_entry] = addStr (si,name); + + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + read_leb128 (data, & bytes_read, 0); + data += bytes_read; + } + } + + /* Skip the NUL at the end of the table. */ + data ++; + + /* Now display the statements. */ + + while (data < end_of_sequence) + { + UChar op_code; + Int adv; + Int bytes_read; + + op_code = * data ++; + + if (op_code >= info.li_opcode_base) + { + Int advAddr; + op_code -= info.li_opcode_base; + adv = (op_code / info.li_line_range) * info.li_min_insn_length; + advAddr = adv; + state_machine_regs.address += adv; + adv = (op_code % info.li_line_range) + info.li_line_base; + addLineInfo (si, fnames[state_machine_regs.file], si->offset + (state_machine_regs.address - advAddr), si->offset + (state_machine_regs.address), state_machine_regs.line, 0); + state_machine_regs.line += adv; + } + else switch (op_code) + { + case DW_LNS_extended_op: + data += process_extended_line_op (si, &fnames, data, info.li_default_is_stmt, + sizeof (Addr)); + break; + + case DW_LNS_copy: + addLineInfo (si, fnames[state_machine_regs.file], si->offset + state_machine_regs.address, si->offset + (state_machine_regs.address + 1), state_machine_regs.line , 0); + break; + + case DW_LNS_advance_pc: + adv = info.li_min_insn_length * read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.address += adv; + break; + + case DW_LNS_advance_line: + adv = read_leb128 (data, & bytes_read, 1); + data += bytes_read; + state_machine_regs.line += adv; + break; + + case DW_LNS_set_file: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.file = adv; + break; + + case DW_LNS_set_column: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + state_machine_regs.column = adv; + break; + + case DW_LNS_negate_stmt: + adv = state_machine_regs.is_stmt; + adv = ! adv; + state_machine_regs.is_stmt = adv; + break; + + case DW_LNS_set_basic_block: + state_machine_regs.basic_block = 1; + break; + + case DW_LNS_const_add_pc: + adv = (((255 - info.li_opcode_base) / info.li_line_range) + * info.li_min_insn_length); + state_machine_regs.address += adv; + break; + + case DW_LNS_fixed_advance_pc: + /* XXX: Need something to get 2 bytes */ + adv = *((UShort *)data); + data += 2; + state_machine_regs.address += adv; + break; + + case DW_LNS_set_prologue_end: + break; + + case DW_LNS_set_epilogue_begin: + break; + + case DW_LNS_set_isa: + adv = read_leb128 (data, & bytes_read, 0); + data += bytes_read; + break; + + default: + { + int j; + for (j = standard_opcodes[op_code - 1]; j > 0 ; --j) + { + read_leb128 (data, &bytes_read, 0); + data += bytes_read; + } + } + break; + } + } + VG_(free)(VG_AR_SYMTAB, fnames); + fnames = NULL; + } + + return 1; +} + /* Read the symbols from the object/exe specified by the SegInfo into the tables within the supplied SegInfo. */ static @@ -919,6 +1318,9 @@ void vg_read_lib_symbols ( SegInfo* si ) stabstr = (UChar*)(oimage + shdr[i].sh_offset); stabstr_sz = shdr[i].sh_size; } + if (0 == VG_(strcmp)(".debug_line",sh_strtab + shdr[i].sh_name)) { + handle_debug_lines (si, &shdr[i], (UChar *)(oimage + shdr[i].sh_offset)); + } } if (stab == NULL || stabstr == NULL) {