]> git.ipfire.org Git - thirdparty/elfutils.git/commitdiff
dwarflint: Layout sections of ET_REL files
authorPetr Machata <pmachata@redhat.com>
Mon, 6 Apr 2009 12:44:34 +0000 (14:44 +0200)
committerPetr Machata <pmachata@redhat.com>
Mon, 6 Apr 2009 12:44:24 +0000 (14:44 +0200)
* that's necessary for range overlap analysis.  In ET_REL files, sections
  are placed at address 0, so all ranges appear to overlap each other, plus
  some of these ranges seem to cross section boundaries.
* we can't use libdwfl to do the layout for us, because libdwfl does too
  many smart things behind the scenes, such as relocating the files.

src/dwarflint.c

index a91d1f65655a64ae3e568d01b9302bf77a809d73..1d58e63115036b3426ce09ecd1b92f6c4953c875 100644 (file)
@@ -395,6 +395,108 @@ static bool be_tolerant = false; /* --tolerant */
 static bool show_refs = false; /* --ref */
 static bool do_high_level = true; /* ! --nohl */
 
+int
+layout_rel_file (Elf *elf)
+{
+  GElf_Ehdr ehdr;
+  if (gelf_getehdr (elf, &ehdr) == NULL)
+    return 2;
+
+  if (ehdr.e_type != ET_REL)
+    return 0;
+
+  /* Taken from libdwfl. */
+  GElf_Addr base = 0;
+  GElf_Addr start = 0, end = 0, bias = 0;
+
+  bool first = true;
+  Elf_Scn *scn = NULL;
+  while ((scn = elf_nextscn (elf, scn)) != NULL)
+    {
+      GElf_Shdr shdr_mem;
+      GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+      if (unlikely (shdr == NULL))
+       return 2;
+
+      if (shdr->sh_flags & SHF_ALLOC)
+       {
+         const GElf_Xword align = shdr->sh_addralign ?: 1;
+         const GElf_Addr next = (end + align - 1) & -align;
+         if (shdr->sh_addr == 0
+             /* Once we've started doing layout we have to do it all,
+                unless we just layed out the first section at 0 when
+                it already was at 0.  */
+             || (bias == 0 && end > start && end != next))
+           {
+             shdr->sh_addr = next;
+             if (end == base)
+               /* This is the first section assigned a location.
+                  Use its aligned address as the module's base.  */
+               start = base = shdr->sh_addr;
+             else if (unlikely (base & (align - 1)))
+               {
+                 /* If BASE has less than the maximum alignment of
+                    any section, we eat more than the optimal amount
+                    of padding and so make the module's apparent
+                    size come out larger than it would when placed
+                    at zero.  So reset the layout with a better base.  */
+
+                 start = end = base = (base + align - 1) & -align;
+                 Elf_Scn *prev_scn = NULL;
+                 do
+                   {
+                     prev_scn = elf_nextscn (elf, prev_scn);
+                     GElf_Shdr prev_shdr_mem;
+                     GElf_Shdr *prev_shdr = gelf_getshdr (prev_scn,
+                                                          &prev_shdr_mem);
+                     if (unlikely (prev_shdr == NULL))
+                       return 2;
+                     if (prev_shdr->sh_flags & SHF_ALLOC)
+                       {
+                         const GElf_Xword prev_align
+                           = prev_shdr->sh_addralign ?: 1;
+
+                         prev_shdr->sh_addr
+                           = (end + prev_align - 1) & -prev_align;
+                         end = prev_shdr->sh_addr + prev_shdr->sh_size;
+
+                         if (unlikely (! gelf_update_shdr (prev_scn,
+                                                           prev_shdr)))
+                           return 2;
+                       }
+                   }
+                 while (prev_scn != scn);
+                 continue;
+               }
+
+             end = shdr->sh_addr + shdr->sh_size;
+             if (likely (shdr->sh_addr != 0)
+                 && unlikely (! gelf_update_shdr (scn, shdr)))
+               return 2;
+           }
+         else
+           {
+             /* The address is already assigned.  Just track it.  */
+             if (first || end < shdr->sh_addr + shdr->sh_size)
+               end = shdr->sh_addr + shdr->sh_size;
+             if (first || bias > shdr->sh_addr)
+               /* This is the lowest address in the module.  */
+               bias = shdr->sh_addr;
+
+             if ((shdr->sh_addr - bias + base) & (align - 1))
+               /* This section winds up misaligned using BASE.
+                  Adjust BASE upwards to make it congruent to
+                  the lowest section address in the file modulo ALIGN.  */
+               base = (((base + align - 1) & -align)
+                       + (bias & (align - 1)));
+           }
+
+         first = false;
+       }
+    }
+  return 0;
+}
+
 int
 main (int argc, char *argv[])
 {
@@ -469,14 +571,25 @@ main (int argc, char *argv[])
        }
 
       /* Create an `Elf' descriptor.  */
-      Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
+      Elf *elf = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, NULL);
       if (elf == NULL)
+      invalid_elf:
        wr_error (NULL,
-                 gettext ("cannot generate Elf descriptor: %s\n"),
+                 gettext ("Error processing ELF file: %s\n"),
                  elf_errmsg (-1));
       else
        {
          unsigned int prev_error_count = error_count;
+         switch (layout_rel_file (elf))
+           {
+           case 2:
+             goto invalid_elf;
+           case 1:
+             wr_error (NULL, "Couldn't layout ET_REL file, the range analysis may be inaccurate.\n");
+           default:
+             ;
+           }
+
          Dwarf *dwarf = dwarf_begin_elf (elf, DWARF_C_READ, NULL);
          if (dwarf == NULL)
            {
@@ -2536,12 +2649,24 @@ relocate_one (struct relocation_data *reloc, struct relocation *rel,
          return;
        }
 
-      *value = rel->addend + symbol->st_value;
       uint64_t section_index = symbol->st_shndx;
       /* XXX We should handle SHN_XINDEX here.  Or, instead, maybe it
         would be possible to use dwfl, which already does XINDEX
         translation.  */
 
+      /* For ET_REL files, we do section layout manually.  But we
+        don't update symbol table doing that.  So instead of looking
+        at symbol value, look at section address.  */
+      uint64_t sym_value = symbol->st_value;
+      if (reloc->file->ehdr.e_type == ET_REL
+         && ELF64_ST_TYPE (symbol->st_info) == STT_SECTION)
+       {
+         assert (sym_value == 0);
+         sym_value = reloc->file->sec[section_index].shdr.sh_addr;
+       }
+
+      *value = rel->addend + sym_value;
+
       /* It's target value, not section offset.  */
       if (offset_into == rel_value
          || offset_into == rel_address)