Preliminary commit of DWARF2 debug info reader from
Daniel Berlin <dberlin@dberlin.org>
git-svn-id: svn://svn.valgrind.org/valgrind/trunk@347 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_symtab2.c b/coregrind/vg_symtab2.c
index e5e40c8..c869b6e 100644
--- a/coregrind/vg_symtab2.c
+++ b/coregrind/vg_symtab2.c
@@ -610,6 +610,405 @@
}
+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 @@
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) {