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) {