From 538dbdc8f7d84db77b681c1efb1986923086963e Mon Sep 17 00:00:00 2001 From: Petr Machata Date: Mon, 6 Apr 2009 14:44:34 +0200 Subject: [PATCH] dwarflint: Layout sections of ET_REL files * 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 | 131 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 128 insertions(+), 3 deletions(-) diff --git a/src/dwarflint.c b/src/dwarflint.c index a91d1f656..1d58e6311 100644 --- a/src/dwarflint.c +++ b/src/dwarflint.c @@ -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) -- 2.47.2