Handle DWARF4 .debug_line format.
authorRoland McGrath <roland@redhat.com>
Wed, 16 Jun 2010 05:17:04 +0000 (22:17 -0700)
committerRoland McGrath <roland@redhat.com>
Wed, 16 Jun 2010 05:17:04 +0000 (22:17 -0700)
libdw/ChangeLog
libdw/dwarf_getsrclines.c

index 12e70a7..780c747 100644 (file)
@@ -1,3 +1,7 @@
+2010-06-15  Roland McGrath  <roland@redhat.com>
+
+       * dwarf_getsrclines.c: Handle version 4 format.
+
 2010-06-01  Roland McGrath  <roland@redhat.com>
 
        * libdwP.h: Remove unused IDX_debug_*names, add IDX_debug_types.
index dcf3490..c3ba837 100644 (file)
@@ -96,6 +96,7 @@ compare_lines (const void *a, const void *b)
                                                                              \
     /* Set the line information.  */                                         \
     new_line->line.addr = address;                                           \
+    /* new_line->line.op_index = op_index; */                                \
     new_line->line.file = file;                                                      \
     new_line->line.line = line;                                                      \
     new_line->line.column = column;                                          \
@@ -104,6 +105,8 @@ compare_lines (const void *a, const void *b)
     new_line->line.end_sequence = end_seq;                                   \
     new_line->line.prologue_end = prologue_end;                                      \
     new_line->line.epilogue_begin = epilogue_begin;                          \
+    /* new_line->line.isa = isa; */                                          \
+    /* new_line->line.discriminator = discriminator; */                              \
                                                                              \
     new_line->next = linelist;                                               \
     linelist = new_line;                                                     \
@@ -175,7 +178,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
 
       /* The next element of the header is the version identifier.  */
       uint_fast16_t version = read_2ubyte_unaligned_inc (dbg, linep);
-      if (unlikely (version < 2) || unlikely (version > 3))
+      if (unlikely (version < 2) || unlikely (version > 4))
        {
          __libdw_seterrno (DWARF_E_VERSION);
          goto out;
@@ -192,13 +195,23 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
       /* Next the minimum instruction length.  */
       uint_fast8_t minimum_instr_len = *linep++;
 
-        /* Then the flag determining the default value of the is_stmt
-          register.  */
+      /* Next the maximum operations per instruction, in version 4 format.  */
+      uint_fast8_t max_ops_per_instr = 1;
+      if (version >= 4)
+       {
+         if (unlikely (lineendp - linep < 5))
+           goto invalid_data;
+         max_ops_per_instr = *linep++;
+         if (unlikely (max_ops_per_instr == 0))
+           goto invalid_data;
+       }
+
+      /* Then the flag determining the default value of the is_stmt
+        register.  */
       uint_fast8_t default_is_stmt = *linep++;
 
       /* Now the line base.  */
-      int_fast8_t line_base = *((int_fast8_t *) linep);
-      ++linep;
+      int_fast8_t line_base = (int8_t) *linep++;
 
       /* And the line range.  */
       uint_fast8_t line_range = *linep++;
@@ -209,9 +222,9 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
       /* Remember array with the standard opcode length (-1 to account for
         the opcode with value zero not being mentioned).  */
       const uint8_t *standard_opcode_lengths = linep - 1;
-      linep += opcode_base - 1;
-      if (unlikely (linep >= lineendp))
+      if (unlikely (lineendp - linep < opcode_base - 1))
        goto invalid_data;
+      linep += opcode_base - 1;
 
       /* First comes the list of directories.  Add the compilation
         directory first since the index zero is used for it.  */
@@ -339,15 +352,27 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
         /* We are about to process the statement program.  Initialize the
           state machine registers (see 6.2.2 in the v2.1 specification).  */
       Dwarf_Word address = 0;
+      unsigned int op_index = 0;
       size_t file = 1;
       size_t line = 1;
       size_t column = 0;
       uint_fast8_t is_stmt = default_is_stmt;
-      int basic_block = 0;
-      int prologue_end = 0;
-      int epilogue_begin = 0;
+      bool basic_block = false;
+      bool prologue_end = false;
+      bool epilogue_begin = false;
+      unsigned int isa = 0;
+      unsigned int discriminator = 0;
+
+      /* Apply the "operation advance" from a special opcode
+        or DW_LNS_advance_pc (as per DWARF4 6.2.5.1).  */
+      inline void advance_pc (unsigned int op_advance)
+      {
+       address += minimum_instr_len * ((op_index + op_advance)
+                                       / max_ops_per_instr);
+       op_index = (op_index + op_advance) % max_ops_per_instr;
+      }
 
-        /* Process the instructions.  */
+      /* Process the instructions.  */
       struct linelist *linelist = NULL;
       unsigned int nlinelist = 0;
       while (linep < lineendp)
@@ -371,32 +396,30 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
              */
              int line_increment = (line_base
                                    + (opcode - opcode_base) % line_range);
-             unsigned int address_increment = (minimum_instr_len
-                                               * ((opcode - opcode_base)
-                                                  / line_range));
 
              /* Perform the increments.  */
              line += line_increment;
-             address += address_increment;
+             advance_pc ((opcode - opcode_base) / line_range);
 
              /* Add a new line with the current state machine values.  */
              NEW_LINE (0);
 
              /* Reset the flags.  */
-             basic_block = 0;
-             prologue_end = 0;
-             epilogue_begin = 0;
+             basic_block = false;
+             prologue_end = false;
+             epilogue_begin = false;
+             discriminator = 0;
            }
          else if (opcode == 0)
            {
              /* This an extended opcode.  */
-             if (unlikely (linep + 2 > lineendp))
+             if (unlikely (lineendp - linep < 2))
                goto invalid_data;
 
              /* The length.  */
              unsigned int len = *linep++;
 
-             if (unlikely (linep + len > lineendp))
+             if (unlikely (lineendp - linep < len))
                goto invalid_data;
 
              /* The sub-opcode.  */
@@ -411,19 +434,25 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
 
                  /* Reset the registers.  */
                  address = 0;
+                 op_index = 0;
                  file = 1;
                  line = 1;
                  column = 0;
                  is_stmt = default_is_stmt;
-                 basic_block = 0;
-                 prologue_end = 0;
-                 epilogue_begin = 0;
+                 basic_block = false;
+                 prologue_end = false;
+                 epilogue_begin = false;
+                 isa = 0;
+                 discriminator = 0;
                  break;
 
                case DW_LNE_set_address:
                  /* The value is an address.  The size is defined as
                     apporiate for the target machine.  We use the
                     address size field from the CU header.  */
+                 op_index = 0;
+                 if (unlikely (lineendp - linep < cu->address_size))
+                   goto invalid_data;
                  if (__libdw_read_address_inc (dbg, IDX_debug_line, &linep,
                                                cu->address_size, &address))
                    goto out;
@@ -475,13 +504,23 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
                  }
                  break;
 
+               case DW_LNE_set_discriminator:
+                 /* Takes one ULEB128 parameter, the discriminator.  */
+                 if (unlikely (standard_opcode_lengths[opcode] != 1))
+                   goto invalid_data;
+
+                 get_uleb128 (discriminator, linep);
+                 break;
+
                default:
                  /* Unknown, ignore it.  */
+                 if (unlikely (lineendp - (linep - 1)) < len)
+                   goto invalid_data;
                  linep += len - 1;
                  break;
                }
            }
-         else if (opcode <= DW_LNS_set_epilogue_begin)
+         else if (opcode <= DW_LNS_set_isa)
            {
              /* This is a known standard opcode.  */
              switch (opcode)
@@ -495,13 +534,10 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
                  NEW_LINE (0);
 
                  /* Reset the flags.  */
-                 basic_block = 0;
-                 /* XXX Whether the following two lines are necessary is
-                    unclear.  I guess the current v2.1 specification has
-                    a bug in that it says clearing these two registers is
-                    not necessary.  */
-                 prologue_end = 0;
-                 epilogue_begin = 0;
+                 basic_block = false;
+                 prologue_end = false;
+                 epilogue_begin = false;
+                 discriminator = 0;
                  break;
 
                case DW_LNS_advance_pc:
@@ -511,7 +547,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
                    goto invalid_data;
 
                  get_uleb128 (u128, linep);
-                 address += minimum_instr_len * u128;
+                 advance_pc (u128);
                  break;
 
                case DW_LNS_advance_line:
@@ -555,7 +591,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
                  if (unlikely (standard_opcode_lengths[opcode] != 0))
                    goto invalid_data;
 
-                 basic_block = 1;
+                 basic_block = true;
                  break;
 
                case DW_LNS_const_add_pc:
@@ -563,17 +599,18 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
                  if (unlikely (standard_opcode_lengths[opcode] != 0))
                    goto invalid_data;
 
-                 address += (minimum_instr_len
-                             * ((255 - opcode_base) / line_range));
+                 advance_pc ((255 - opcode_base) / line_range);
                  break;
 
                case DW_LNS_fixed_advance_pc:
                  /* Takes one 16 bit parameter which is added to the
                     address.  */
-                 if (unlikely (standard_opcode_lengths[opcode] != 1))
+                 if (unlikely (standard_opcode_lengths[opcode] != 1)
+                     || unlikely (lineendp - linep < 2))
                    goto invalid_data;
 
                  address += read_2ubyte_unaligned_inc (dbg, linep);
+                 op_index = 0;
                  break;
 
                case DW_LNS_set_prologue_end:
@@ -581,7 +618,7 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
                  if (unlikely (standard_opcode_lengths[opcode] != 0))
                    goto invalid_data;
 
-                 prologue_end = 1;
+                 prologue_end = true;
                  break;
 
                case DW_LNS_set_epilogue_begin:
@@ -589,7 +626,15 @@ dwarf_getsrclines (Dwarf_Die *cudie, Dwarf_Lines **lines, size_t *nlines)
                  if (unlikely (standard_opcode_lengths[opcode] != 0))
                    goto invalid_data;
 
-                 epilogue_begin = 1;
+                 epilogue_begin = true;
+                 break;
+
+               case DW_LNS_set_isa:
+                 /* Takes one uleb128 parameter which is stored in isa.  */
+                 if (unlikely (standard_opcode_lengths[opcode] != 1))
+                   goto invalid_data;
+
+                 get_uleb128 (isa, linep);
                  break;
                }
            }