]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: Consolidate checking and relocation of attribute values
authorPetr Machata <pmachata@redhat.com>
Tue, 18 Aug 2009 18:32:26 +0000 (20:32 +0200)
committerPetr Machata <pmachata@redhat.com>
Tue, 18 Aug 2009 18:32:26 +0000 (20:32 +0200)
* This was done to add DW_FORM_sec_offset, but the structure of the code
  wasn't such that would make this addition easy.  The revamp ended up
  being quite extensive, and I never got around to checking whether the
  support for the new form really works.
* Checked on all elfutils ELF files that old and new dwarflint emit the
  same messages.

src/dwarflint.c
src/dwarfstrings.c

index 5acee4ec65efb6172bcfcb235024dc19439607a1..b342b38a763701efb97acf51d0febb7eee9b4195 100644 (file)
@@ -1414,7 +1414,7 @@ read_ctx_read_form (struct read_ctx *ctx, bool addr_64, uint8_t form,
 static bool
 attrib_form_valid (uint64_t form)
 {
-  return form > 0 && form <= DW_FORM_indirect;
+  return form > 0 && form <= DW_FORM_ref_sig8;
 }
 
 static int
@@ -1451,6 +1451,7 @@ check_abbrev_location_form (uint64_t form)
       /* loclistptr */
     case DW_FORM_data4:
     case DW_FORM_data8:
+    case DW_FORM_sec_offset: // DWARF 4
 
       /* block */
     case DW_FORM_block1:
@@ -1715,7 +1716,7 @@ abbrev_table_load (struct read_ctx *ctx)
            {
              if (!check_abbrev_location_form (attrib_form))
                wr_error (&where,
-                         ": %s with invalid form \"%s\".\n",
+                         ": location attribute %s with invalid form \"%s\".\n",
                          dwarf_attr_string (attrib_name),
                          dwarf_form_string (attrib_form));
            }
@@ -2795,30 +2796,6 @@ read_die_chain (struct elf_file *file,
        {
          where.ref = &it->where;
 
-         void record_ref (uint64_t addr, struct where *who, bool local)
-         {
-           struct ref_record *record = &cu->die_refs;
-           if (local)
-             {
-               assert (ctx->end > ctx->begin);
-               if (addr > (uint64_t)(ctx->end - ctx->begin))
-                 {
-                   wr_error (&where,
-                             ": invalid reference outside the CU: 0x%" PRIx64 ".\n",
-                             addr);
-                   return;
-                 }
-
-               /* Address holds a CU-local reference, so add CU
-                  offset to turn it into section offset.  */
-               addr += cu->offset;
-               record = local_die_refs;
-             }
-
-           if (record != NULL)
-             ref_record_add (record, addr, who);
-         }
-
          uint8_t form = it->form;
          bool indirect = form == DW_FORM_indirect;
          if (indirect)
@@ -2851,51 +2828,117 @@ read_die_chain (struct elf_file *file,
                  };
            }
 
-         enum check_what_t
+         void (*value_check_cb) (uint64_t, struct where *) = NULL;
+
+         /* For checking lineptr, rangeptr, locptr.  */
+         bool check_someptr = false;
+         enum message_category extra_mc = mc_none;
+
+         /* Callback for local DIE references.  */
+         void check_die_ref_local (uint64_t addr, struct where *who)
          {
-           check_nothing = 0,
-           check_locptr,
-           check_lineptr,
-           check_rangeptr
-         };
-         static enum message_category mc_check[] =
-           {
-             [check_nothing] = mc_none,
-             [check_locptr] = mc_loc,
-             [check_lineptr] = mc_line,
-             [check_rangeptr] = mc_ranges
-           };
+           assert (ctx->end > ctx->begin);
+           if (addr > (uint64_t)(ctx->end - ctx->begin))
+             {
+               wr_error (&where,
+                         ": invalid reference outside the CU: 0x%" PRIx64 ".\n",
+                         addr);
+               return;
+             }
+
+           if (local_die_refs != NULL)
+             /* Address holds a CU-local reference, so add CU offset
+                to turn it into section offset.  */
+             ref_record_add (local_die_refs, addr += cu->offset, who);
+         }
+
+         /* Callback for global DIE references.  */
+         void check_die_ref_global (uint64_t addr, struct where *who)
+         {
+           ref_record_add (&cu->die_refs, addr, who);
+         }
 
-         void do_check_ptr (enum check_what_t what, uint64_t value)
+         /* Callback for strp values.  */
+         void check_strp (uint64_t addr, struct where *who)
          {
-           assert (what != check_nothing);
+           if (strings == NULL)
+             wr_error (who, ": strp attribute, but no .debug_str data.\n");
+           else if (addr >= strings->d_size)
+             wr_error (who,
+                       ": Invalid offset outside .debug_str: 0x%" PRIx64 ".\n",
+                       addr);
+           else
+             {
+               /* Record used part of .debug_str.  */
+               const char *startp = (const char *)strings->d_buf + addr;
+               const char *data_end = strings->d_buf + strings->d_size;
+               const char *strp = startp;
+               while (strp < data_end && *strp != 0)
+                 ++strp;
+               if (strp == data_end)
+                 wr_error (who,
+                           ": String at .debug_str: 0x%" PRIx64
+                           " is not zero-terminated.\n", addr);
+
+               if (strings_coverage != NULL)
+                 coverage_add (strings_coverage, addr, strp - startp + 1);
+             }
+         }
 
-           if (what == check_rangeptr && ((value % cu->address_size) != 0))
-             wr_message (mc_ranges | mc_impact_2, &where,
+         /* Callback for rangeptr values.  */
+         void check_rangeptr (uint64_t value, struct where *who)
+         {
+           if ((value % cu->address_size) != 0)
+             wr_message (mc_ranges | mc_impact_2, who,
                          ": rangeptr value %#" PRIx64
                          " not aligned to CU address size.\n", value);
+           cu_coverage->need_ranges = true;
+           ref_record_add (&cu->range_refs, value, who);
+         }
 
-           struct ref_record *ref = NULL;
-           switch (what)
-             {
-             case check_rangeptr:
-               ref = &cu->range_refs;
-               cu_coverage->need_ranges = true;
-               break;
-             case check_lineptr:
-               ref = &cu->line_refs;
-               break;
-             case check_locptr:
-               ref = &cu->loc_refs;
-             case check_nothing:
-               break;
-             };
+         /* Callback for lineptr values.  */
+         void check_lineptr (uint64_t value, struct where *who)
+         {
+           ref_record_add (&cu->line_refs, value, who);
+         }
 
-           ref_record_add (ref, value, &where);
+         /* Callback for locptr values.  */
+         void check_locptr (uint64_t value, struct where *who)
+         {
+           ref_record_add (&cu->loc_refs, value, who);
          }
 
-         enum check_what_t check_ptr = check_nothing;
+         uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->offset;
+         bool type_is_rel = file->ehdr.e_type == ET_REL;
+
+         /* Attribute value.  */
+         uint64_t value = 0;
 
+         /* Whether the value should be relocated first.  Note that
+            relocations are really required only in REL files, so
+            missing relocations are not warned on even with
+            rel_require, unless type_is_rel.  */
+         enum
+         {
+           rel_no,             // don't allow a relocation
+           rel_require,        // require a relocation
+           rel_nonzero,        // require a relocation if value != 0
+         } relocate = rel_no;
+         size_t width = 0;
+
+         /* Point to variable that you want to copy relocated value
+            to.  */
+         uint64_t *valuep = NULL;
+
+         /* Point to variable that you want set to `true' in case the
+            value was relocated.  */
+         bool *relocatedp = NULL;
+
+         /* Point to variable that you want set to symbol that the
+            relocation was made against.  */
+         GElf_Sym **symbolp = NULL;
+
+         /* Setup locptr checking.  */
          if (is_location_attrib (it->name))
            {
              switch (form)
@@ -2906,9 +2949,14 @@ read_die_chain (struct elf_file *file,
                              ": location attribute with form \"%s\" in 32-bit CU.\n",
                              dwarf_form_string (form));
                  /* fall-through */
+
                case DW_FORM_data4:
-                 check_ptr = check_locptr;
-                 /* fall-through */
+               case DW_FORM_sec_offset:
+                 value_check_cb = check_locptr;
+                 extra_mc = mc_loc;
+                 check_someptr = true;
+                 break;
+
                case DW_FORM_block1:
                case DW_FORM_block2:
                case DW_FORM_block4:
@@ -2924,6 +2972,7 @@ read_die_chain (struct elf_file *file,
                              dwarf_form_string (form));
                };
            }
+         /* Setup rangeptr or lineptr checking.  */
          else if (it->name == DW_AT_ranges
                   || it->name == DW_AT_stmt_list)
            switch (form)
@@ -2934,13 +2983,20 @@ read_die_chain (struct elf_file *file,
                            ": %s with form DW_FORM_data8 in 32-bit CU.\n",
                            dwarf_attr_string (it->name));
                /* fall-through */
+
              case DW_FORM_data4:
+             case DW_FORM_sec_offset:
+               check_someptr = true;
                if (it->name == DW_AT_ranges)
-                 check_ptr = check_rangeptr;
+                 {
+                   value_check_cb = check_rangeptr;
+                   extra_mc = mc_ranges;
+                 }
                else
                  {
                    assert (it->name == DW_AT_stmt_list);
-                   check_ptr = check_lineptr;
+                   value_check_cb = check_lineptr;
+                   extra_mc = mc_line;
                  }
                break;
 
@@ -2952,257 +3008,133 @@ read_die_chain (struct elf_file *file,
                            ": %s with invalid (indirect) form \"%s\".\n",
                            dwarf_attr_string (it->name),
                            dwarf_form_string (form));
-             };
-
-         uint64_t ctx_offset = read_ctx_get_offset (ctx) + cu->offset;
-         struct relocation *rel;
-         bool type_is_rel = file->ehdr.e_type == ET_REL;
+             }
+         /* Setup low_pc and high_pc checking.  */
+         else if (it->name == DW_AT_low_pc)
+           {
+             relocatedp = &low_pc_relocated;
+             symbolp = &low_pc_symbol;
+             valuep = &low_pc;
+           }
+         else if (it->name == DW_AT_high_pc)
+           {
+             relocatedp = &high_pc_relocated;
+             symbolp = &high_pc_symbol;
+             valuep = &high_pc;
+           }
 
+         /* Load attribute value and setup per-form checking.  */
          switch (form)
            {
            case DW_FORM_strp:
-             {
-               uint64_t addr;
-               if (!read_ctx_read_offset (ctx, dwarf_64, &addr))
-                 {
-                 cant_read:
-                   wr_error (&where, ": can't read attribute value.\n");
-                   return -1;
-                 }
-
-               if ((rel = relocation_next (reloc, ctx_offset,
-                                           &where, skip_mismatched)))
-                 relocate_one (file, reloc, rel, dwarf_64 ? 8 : 4,
-                               &addr, &where, sec_str, NULL);
-               else if (type_is_rel)
-                 wr_message (mc_impact_2 | mc_die_other | mc_reloc | mc_strings,
-                             &where, PRI_LACK_RELOCATION, "DW_FORM_strp");
-
-               if (strings == NULL)
-                 wr_error (&where,
-                           ": strp attribute, but no .debug_str data.\n");
-               else if (addr >= strings->d_size)
-                 wr_error (&where,
-                           ": Invalid offset outside .debug_str: 0x%" PRIx64 ".\n",
-                           addr);
-               else
-                 {
-                   /* Record used part of .debug_str.  */
-                   const char *strp = (const char *)strings->d_buf + addr;
-
-                   if (strings_coverage != NULL)
-                     coverage_add (strings_coverage, addr, strlen (strp) + 1);
-                 }
+             if (!read_ctx_read_offset (ctx, dwarf_64, &value))
+               {
+               cant_read:
+                 wr_error (&where, ": can't read attribute value.\n");
+                 return -1;
+               }
 
-               break;
-             }
+             relocate = rel_require;
+             width = dwarf_64 ? 8 : 4;
+             value_check_cb = check_strp;
+             break;
 
            case DW_FORM_string:
-             {
-               if (!read_ctx_read_str (ctx))
-                 goto cant_read;
-               break;
-             }
+             if (!read_ctx_read_str (ctx))
+               goto cant_read;
+             break;
 
-           case DW_FORM_addr:
            case DW_FORM_ref_addr:
-             {
-               bool is_64 = form == DW_FORM_addr ? addr_64 : ref_64;
-               uint64_t addr;
-               if (!read_ctx_read_offset (ctx, is_64, &addr))
-                 goto cant_read;
-
-               uint64_t *addrp = NULL;
-               bool *relocatedp = NULL;
-               GElf_Sym **symbolp = NULL;
-
-               switch (it->name)
-                 {
-                 case DW_AT_low_pc:
-                   relocatedp = &low_pc_relocated;
-                   symbolp = &low_pc_symbol;
-                   addrp = &low_pc;
-                   break;
-
-                 case DW_AT_high_pc:
-                   relocatedp = &high_pc_relocated;
-                   symbolp = &high_pc_symbol;
-                   addrp = &high_pc;
-                 };
-
-               if ((rel = relocation_next (reloc, ctx_offset,
-                                           &where, skip_mismatched)))
-                 {
-                   relocate_one (file, reloc, rel, is_64 ? 8 : 4, &addr,
-                                 &where, reloc_target (form, it), symbolp);
-                   if (relocatedp != NULL)
-                     *relocatedp = true;
-                 }
-               else
-                 {
-                   if (symbolp != NULL)
-                     WIPE (*symbolp);
-                   if (type_is_rel && addr != 0)
-                     /* In non-rel files, neither addr, nor ref_addr
-                        /need/ a relocation.  We at least check that
-                        ref_addr points to sensible datum by recording
-                        the reference below.  */
-                     wr_message (mc_impact_2 | mc_die_rel | mc_reloc, &where,
-                                 PRI_LACK_RELOCATION, dwarf_form_string (form));
-                 }
-               if (addrp != NULL)
-                 *addrp = addr;
-
-               if (form == DW_FORM_ref_addr)
-                 record_ref (addr, &where, false);
-
-               if (abbrev->tag == DW_TAG_compile_unit
-                   || abbrev->tag == DW_TAG_partial_unit)
-                 {
-                   if (it->name == DW_AT_low_pc)
-                     cu->low_pc = addr;
-
-                   if (low_pc != (uint64_t)-1 && high_pc != (uint64_t)-1)
-                     coverage_add (&cu_coverage->cov, low_pc, high_pc - low_pc);
-                 }
-
-               break;
-             }
+             value_check_cb = check_die_ref_global;
+           case DW_FORM_sec_offset:
+             width = dwarf_64 ? 8 : 4;
+             if (false)
+           case DW_FORM_addr:
+               width = addr_64 ? 8 : 4;
+             if (!read_ctx_read_offset (ctx, width == 8, &value))
+               goto cant_read;
+
+             /* In non-rel files, neither addr, nor ref_addr /need/ a
+                relocation.  We at least check that ref_addr points
+                to sensible datum by recording the reference below.  */
+             relocate = rel_nonzero;
+             break;
 
-           case DW_FORM_udata:
            case DW_FORM_ref_udata:
-             {
-               uint64_t value;
-               if (!checked_read_uleb128 (ctx, &value, &where,
-                                          "attribute value"))
-                 return -1;
-
-               if (it->name == DW_AT_sibling)
-                 sibling_addr = value;
-               else if (form == DW_FORM_ref_udata)
-                 record_ref (value, &where, true);
-               break;
-             }
+             value_check_cb = check_die_ref_local;
+           case DW_FORM_udata:
+             if (!checked_read_uleb128 (ctx, &value, &where,
+                                        "attribute value"))
+               return -1;
+             break;
 
+           case DW_FORM_ref1:
+             value_check_cb = check_die_ref_local;
            case DW_FORM_flag:
            case DW_FORM_data1:
-           case DW_FORM_ref1:
-             {
-               /* Neither of these should be relocated.  */
-               uint8_t value;
-               if (!read_ctx_read_ubyte (ctx, &value))
-                 goto cant_read;
-
-               if (it->name == DW_AT_sibling)
-                 sibling_addr = value;
-               else if (form == DW_FORM_ref1)
-                 record_ref (value, &where, true);
-               break;
-             }
+             if (!read_ctx_read_var (ctx, 1, &value))
+               goto cant_read;
+             break;
 
-           case DW_FORM_data2:
            case DW_FORM_ref2:
-             {
-               /* Neither of these should be relocated.  */
-               uint16_t value;
-               if (!read_ctx_read_2ubyte (ctx, &value))
-                 goto cant_read;
-
-               if (it->name == DW_AT_sibling)
-                 sibling_addr = value;
-               else if (form == DW_FORM_ref2)
-                 record_ref (value, &where, true);
-               break;
-             }
+             value_check_cb = check_die_ref_local;
+           case DW_FORM_data2:
+             if (!read_ctx_read_var (ctx, 2, &value))
+               goto cant_read;
+             break;
 
            case DW_FORM_data4:
+             if (check_someptr)
+               {
+                 relocate = rel_require;
+                 width = 4;
+               }
+             if (false)
            case DW_FORM_ref4:
-             {
-               uint32_t raw_value;
-               if (!read_ctx_read_4ubyte (ctx, &raw_value))
-                 goto cant_read;
-
-               /* DW_FORM_ref4 shouldn't be relocated.  */
-               uint64_t value = raw_value;
-               if (form == DW_FORM_data4)
-                 {
-                   if ((rel = relocation_next (reloc, ctx_offset,
-                                               &where, skip_mismatched)))
-                     relocate_one (file, reloc, rel, 4, &value, &where,
-                                   reloc_target (form, it), NULL);
-                   else if (type_is_rel && check_ptr != check_nothing)
-                     wr_message (mc_impact_2 | mc_die_other | mc_reloc
-                                 | mc_check[check_ptr],
-                                 &where, PRI_LACK_RELOCATION,
-                                 dwarf_form_string (form));
-                 }
-
-               if (it->name == DW_AT_sibling)
-                 sibling_addr = value;
-               else if (check_ptr != check_nothing)
-                 do_check_ptr (check_ptr, value);
-               else if (form == DW_FORM_ref4)
-                 record_ref (value, &where, true);
-               break;
-             }
+               value_check_cb = check_die_ref_local;
+             if (!read_ctx_read_var (ctx, 4, &value))
+               goto cant_read;
+             break;
 
            case DW_FORM_data8:
+             if (check_someptr)
+               {
+                 relocate = rel_require;
+                 width = 8;
+               }
+             if (false)
            case DW_FORM_ref8:
-             {
-               uint64_t value;
-               if (!read_ctx_read_8ubyte (ctx, &value))
-                 goto cant_read;
-
-               /* DW_FORM_ref8 shouldn't be relocated.  */
-               if (form == DW_FORM_data8)
-                 {
-                   if ((rel = relocation_next (reloc, ctx_offset,
-                                               &where, skip_mismatched)))
-                     relocate_one (file, reloc, rel, 8, &value, &where,
-                                   reloc_target (form, it), NULL);
-                   else if (type_is_rel && check_ptr != check_nothing)
-                     wr_message (mc_impact_2 | mc_die_other | mc_reloc
-                                 | mc_check[check_ptr],
-                                 &where, PRI_LACK_RELOCATION,
-                                 dwarf_form_string (form));
-                 }
-
-               if (it->name == DW_AT_sibling)
-                 sibling_addr = value;
-               else if (check_ptr != check_nothing)
-                 do_check_ptr (check_ptr, value);
-               else if (form == DW_FORM_ref8)
-                 record_ref (value, &where, true);
-               break;
-             }
+               value_check_cb = check_die_ref_local;
+             if (!read_ctx_read_8ubyte (ctx, &value))
+               goto cant_read;
+             break;
 
            case DW_FORM_sdata:
              {
-               int64_t value;
-               if (!checked_read_sleb128 (ctx, &value, &where,
+               int64_t value64;
+               if (!checked_read_sleb128 (ctx, &value64, &where,
                                           "attribute value"))
                  return -1;
+               value = (uint64_t) value64;
                break;
              }
 
            case DW_FORM_block:
              {
-               int width = 0;
                uint64_t length;
-               goto process_DW_FORM_block;
 
+               if (false)
            case DW_FORM_block1:
-               width = 1;
-               goto process_DW_FORM_block;
+                 width = 1;
 
+               if (false)
            case DW_FORM_block2:
-               width = 2;
-               goto process_DW_FORM_block;
+                 width = 2;
 
+               if (false)
            case DW_FORM_block4:
-               width = 4;
+                 width = 4;
 
-             process_DW_FORM_block:
                if (width == 0)
                  {
                    if (!checked_read_uleb128 (ctx, &length, &where,
@@ -3221,13 +3153,14 @@ read_die_chain (struct elf_file *file,
                      return -1;
                  }
                else
+                 /* xxx really skip_mismatched?  We just don't know
+                    how to process these...  */
                  relocation_skip (reloc,
                                   read_ctx_get_offset (ctx) + length,
                                   &where, skip_mismatched);
 
                if (!read_ctx_skip (ctx, length))
                  goto cant_read;
-
                break;
              }
 
@@ -3239,6 +3172,64 @@ read_die_chain (struct elf_file *file,
              wr_error (&where,
                        ": internal error: unhandled form 0x%x.\n", form);
            }
+
+         /* Relocate the value if appropriate.  */
+         struct relocation *rel;
+         if ((rel = relocation_next (reloc, ctx_offset,
+                                     &where, skip_mismatched)))
+           {
+             if (relocate == rel_no)
+               wr_message (mc_impact_4 | mc_die_other | mc_reloc | extra_mc,
+                           &where, ": unexpected relocation of %s.\n",
+                           dwarf_form_string (form));
+
+             relocate_one (file, reloc, rel, width, &value, &where,
+                           reloc_target (form, it), symbolp);
+
+             if (relocatedp != NULL)
+               *relocatedp = true;
+           }
+         else
+           {
+             if (symbolp != NULL)
+               WIPE (*symbolp);
+             if (type_is_rel
+                 && (relocate == rel_require
+                     || (relocate == rel_nonzero
+                         && value != 0)))
+               wr_message (mc_impact_2 | mc_die_other | mc_reloc | extra_mc,
+                           &where, PRI_LACK_RELOCATION,
+                           dwarf_form_string (form));
+           }
+
+         /* Dispatch value checking.  */
+         if (it->name == DW_AT_sibling)
+           {
+             /* Full-blown DIE reference checking is too heavy-weight
+                and not practical (error messages wise) for checking
+                siblings.  */
+             assert (value_check_cb == check_die_ref_local
+                     || value_check_cb == check_die_ref_global);
+             valuep = &sibling_addr;
+           }
+         else if (value_check_cb != NULL)
+           value_check_cb (value, &where);
+
+         /* Store the relocated value.  Note valuep may point to
+            low_pc or high_pc.  */
+         if (valuep != NULL)
+           *valuep = value;
+
+         /* Check PC coverage.  */
+         if (abbrev->tag == DW_TAG_compile_unit
+             || abbrev->tag == DW_TAG_partial_unit)
+           {
+             if (it->name == DW_AT_low_pc)
+               cu->low_pc = value;
+
+             if (low_pc != (uint64_t)-1 && high_pc != (uint64_t)-1)
+               coverage_add (&cu_coverage->cov, low_pc, high_pc - low_pc);
+           }
        }
       where.ref = NULL;
 
index 34c86d34c34be2c66ca8e76d07f4624cbfa7189c..fdb3ede418fe1c1e9e59ce83f98b519b3fe00f25 100644 (file)
@@ -353,7 +353,11 @@ dwarf_form_string (unsigned int form)
       [DW_FORM_ref4] = "ref4",
       [DW_FORM_ref8] = "ref8",
       [DW_FORM_ref_udata] = "ref_udata",
-      [DW_FORM_indirect] = "indirect"
+      [DW_FORM_indirect] = "indirect",
+      [DW_FORM_sec_offset] = "sec_offset",
+      [DW_FORM_exprloc] = "exprloc",
+      [DW_FORM_flag_present] = "flag_present",
+      [DW_FORM_ref_sig8] = "ref_sig8",
     };
   const unsigned int nknown_forms = (sizeof (known_forms)
                                     / sizeof (known_forms[0]));
@@ -365,7 +369,7 @@ dwarf_form_string (unsigned int form)
 
   if (unlikely (result == NULL))
     {
-      snprintf (buf, sizeof buf, gettext ("unknown form %" PRIx64),
+      snprintf (buf, sizeof buf, gettext ("unknown form %#" PRIx64),
                (uint64_t) form);
       result = buf;
     }