Improve readelf's display of RELR relocs.

This commit is contained in:
Nick Clifton
2024-04-11 16:57:18 +01:00
parent 8e8d0b63ff
commit fcf8f3237c
17 changed files with 422 additions and 170 deletions

View File

@@ -338,6 +338,7 @@ typedef enum print_mode
PREFIX_HEX_5,
FULL_HEX,
LONG_HEX,
ZERO_HEX,
OCTAL,
OCTAL_5
}
@@ -580,6 +581,11 @@ print_vma (uint64_t vma, print_mode mode)
return nc + printf ("%16.16" PRIx64, vma);
return nc + printf ("%8.8" PRIx64, vma);
case ZERO_HEX:
if (is_32bit_elf)
return printf ("%08" PRIx64, vma);
return printf ("%016" PRIx64, vma);
case DEC_5:
if (vma <= 99999)
return printf ("%5" PRId64, vma);
@@ -1109,6 +1115,26 @@ find_section_by_type (Filedata * filedata, unsigned int type)
return NULL;
}
static Elf_Internal_Shdr *
find_section_by_name (Filedata * filedata, const char * name)
{
unsigned int i;
if (filedata->section_headers == NULL || filedata->string_table_length == 0)
return NULL;
for (i = 0; i < filedata->file_header.e_shnum; i++)
{
Elf_Internal_Shdr *sec = filedata->section_headers + i;
if (sec->sh_name < filedata->string_table_length
&& streq (name, filedata->string_table + sec->sh_name))
return sec;
}
return NULL;
}
/* Return a pointer to section NAME, or NULL if no such section exists,
restricted to the list of sections given in SET. */
@@ -1467,76 +1493,6 @@ slurp_rel_relocs (Filedata *filedata,
return true;
}
static bool
slurp_relr_relocs (Filedata *filedata,
uint64_t relr_offset,
uint64_t relr_size,
uint64_t **relrsp,
uint64_t *nrelrsp)
{
void *relrs;
size_t size = 0, nentries, i;
uint64_t base = 0, addr, entry;
relrs = get_data (NULL, filedata, relr_offset, 1, relr_size,
_("RELR relocation data"));
if (!relrs)
return false;
if (is_32bit_elf)
nentries = relr_size / sizeof (Elf32_External_Relr);
else
nentries = relr_size / sizeof (Elf64_External_Relr);
for (i = 0; i < nentries; i++)
{
if (is_32bit_elf)
entry = BYTE_GET (((Elf32_External_Relr *)relrs)[i].r_data);
else
entry = BYTE_GET (((Elf64_External_Relr *)relrs)[i].r_data);
if ((entry & 1) == 0)
size++;
else
while ((entry >>= 1) != 0)
if ((entry & 1) == 1)
size++;
}
*relrsp = malloc (size * sizeof (**relrsp));
if (*relrsp == NULL)
{
free (relrs);
error (_("out of memory parsing relocs\n"));
return false;
}
size = 0;
for (i = 0; i < nentries; i++)
{
const uint64_t entry_bytes = is_32bit_elf ? 4 : 8;
if (is_32bit_elf)
entry = BYTE_GET (((Elf32_External_Relr *)relrs)[i].r_data);
else
entry = BYTE_GET (((Elf64_External_Relr *)relrs)[i].r_data);
if ((entry & 1) == 0)
{
(*relrsp)[size++] = entry;
base = entry + entry_bytes;
}
else
{
for (addr = base; (entry >>= 1) != 0; addr += entry_bytes)
if ((entry & 1) != 0)
(*relrsp)[size++] = addr;
base += entry_bytes * (entry_bytes * CHAR_BIT - 1);
}
}
*nrelrsp = size;
free (relrs);
return true;
}
/* Returns the reloc type extracted from the reloc info field. */
static unsigned int
@@ -1578,19 +1534,227 @@ uses_msp430x_relocs (Filedata * filedata)
|| (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_NONE));
}
static const char *
get_symbol_at (Elf_Internal_Sym * symtab,
uint64_t nsyms,
char * strtab,
uint64_t strtablen,
uint64_t where,
uint64_t * offset_return)
{
Elf_Internal_Sym * beg = symtab;
Elf_Internal_Sym * end = symtab + nsyms;
Elf_Internal_Sym * best = NULL;
uint64_t dist = 0x100000;
/* FIXME: Since this function is likely to be called repeatedly with
slightly increasing addresses each time, we could speed things up by
caching the last returned value and starting our search from there. */
while (beg < end)
{
Elf_Internal_Sym * sym;
uint64_t value;
sym = beg + (end - beg) / 2;
value = sym->st_value;
if (sym->st_name != 0
&& where >= value
&& where - value < dist)
{
best = sym;
dist = where - value;
if (dist == 0)
break;
}
if (where < value)
end = sym;
else
beg = sym + 1;
}
if (best == NULL)
return NULL;
if (best->st_name >= strtablen)
return NULL;
if (offset_return != NULL)
* offset_return = dist;
return strtab + best->st_name;
}
static void
print_relr_addr_and_sym (Elf_Internal_Sym * symtab,
uint64_t nsyms,
char * strtab,
uint64_t strtablen,
uint64_t where)
{
const char * symname = NULL;
uint64_t offset = 0;
print_vma (where, ZERO_HEX);
printf (" ");
symname = get_symbol_at (symtab, nsyms, strtab, strtablen, where, & offset);
if (symname == NULL)
printf ("<no sym>");
else if (offset == 0)
print_symbol_name (38, symname);
else
{
print_symbol_name (28, symname);
printf (" + ");
print_vma (offset, PREFIX_HEX);
}
}
static /* signed */ int
symcmp (const void *p, const void *q)
{
Elf_Internal_Sym *sp = (Elf_Internal_Sym *) p;
Elf_Internal_Sym *sq = (Elf_Internal_Sym *) q;
return sp->st_value > sq->st_value ? 1 : (sp->st_value < sq->st_value ? -1 : 0);
}
static bool
dump_relr_relocations (Filedata * filedata,
Elf_Internal_Shdr * section,
Elf_Internal_Sym * symtab,
uint64_t nsyms,
char * strtab,
uint64_t strtablen)
{
uint64_t * relrs;
uint64_t nentries, i;
uint64_t relr_size = section->sh_size;
int relr_entsize = section->sh_entsize;
uint64_t relr_offset = section->sh_offset;
uint64_t where = 0;
int num_bits_in_entry;
relrs = get_data (NULL, filedata, relr_offset, 1, relr_size, _("RELR relocation data"));
if (relrs == NULL)
return false;
if (relr_entsize == 0)
relr_entsize = is_32bit_elf ? 4 : 8;
nentries = relr_size / relr_entsize;
if (relr_entsize == sizeof (Elf32_External_Relr))
num_bits_in_entry = 31;
else if (relr_entsize == sizeof (Elf64_External_Relr))
num_bits_in_entry = 63;
else
{
warn (_("Unexpected entsize for RELR section\n"));
return false;
}
/* Symbol tables are not sorted on address, but we want a quick lookup
for the symbol associated with each address computed below, so sort
the table now. FIXME: This assumes that the symbol table will not
be used later on for some other purpose. */
qsort (symtab, nsyms, sizeof (Elf_Internal_Sym), symcmp);
if (do_wide)
{
if (relr_entsize == 4)
printf (_("Index: Entry: Address Symbolic Address Notes\n"));
else
printf (_("Index: Entry: Address relocated Symbolic Address Notes\n"));
}
else
{
if (relr_entsize == 4)
printf (_("Index: Entry: Address Symbolic Address\n"));
else
printf (_("Index: Entry: Address relocated Symbolic Address\n"));
}
for (i = 0; i < nentries; i++)
{
uint64_t entry;
if (relr_entsize == 4)
entry = BYTE_GET (((Elf32_External_Relr *)relrs)[i].r_data);
else
entry = BYTE_GET (((Elf64_External_Relr *)relrs)[i].r_data);
/* We assume that there will never be more than 9999 entries. */
printf (_("%04u: "), (unsigned int) i);
print_vma (entry, ZERO_HEX);
printf (" ");
if ((entry & 1) == 0)
{
where = entry;
print_relr_addr_and_sym (symtab, nsyms, strtab, strtablen, where);
if (do_wide)
printf (_(" (new starting address)"));
printf ("\n");
where += relr_entsize;
}
else
{
bool first = true;
int j;
/* The least significant bit is ignored. */
if (entry == 1)
warn (_("Malformed RELR bitmap - no significant bits are set\n"));
else if (i == 0)
warn (_("Unusual RELR bitmap - no previous entry to set the base address\n"));
for (j = 0; entry >>= 1; j++)
if ((entry & 1) == 1)
{
uint64_t addr = where + (j * relr_entsize);
if (first)
{
print_relr_addr_and_sym (symtab, nsyms, strtab, strtablen, addr);
if (do_wide)
printf (_(" (start of bitmap)"));
first = false;
}
else
{
printf (_("\n%*s "), relr_entsize == 4 ? 15 : 23, " ");
print_relr_addr_and_sym (symtab, nsyms, strtab, strtablen, addr);
}
}
printf ("\n");
where += num_bits_in_entry * relr_entsize;
}
}
free (relrs);
return true;
}
/* Display the contents of the relocation data found at the specified
offset. */
static bool
dump_relocations (Filedata *filedata,
uint64_t rel_offset,
uint64_t rel_size,
Elf_Internal_Sym *symtab,
uint64_t nsyms,
char *strtab,
uint64_t strtablen,
relocation_type rel_type,
bool is_dynsym)
dump_relocations (Filedata * filedata,
uint64_t rel_offset,
uint64_t rel_size,
Elf_Internal_Sym * symtab,
uint64_t nsyms,
char * strtab,
uint64_t strtablen,
relocation_type rel_type,
bool is_dynsym)
{
size_t i;
Elf_Internal_Rela * rels;
@@ -1611,21 +1775,8 @@ dump_relocations (Filedata *filedata,
}
else if (rel_type == reltype_relr)
{
uint64_t * relrs;
const char *format
= is_32bit_elf ? "%08" PRIx64 "\n" : "%016" PRIx64 "\n";
if (!slurp_relr_relocs (filedata, rel_offset, rel_size, &relrs,
&rel_size))
return false;
printf (ngettext (" %" PRIu64 " offset\n",
" %" PRIu64 " offsets\n", rel_size),
rel_size);
for (i = 0; i < rel_size; i++)
printf (format, relrs[i]);
free (relrs);
return true;
/* This should have been handled by display_relocations(). */
return false;
}
if (is_32bit_elf)
@@ -7996,6 +8147,7 @@ process_section_headers (Filedata * filedata)
switch (section->sh_type)
{
case SHT_REL:
case SHT_RELR:
case SHT_RELA:
if (section->sh_link == 0
&& (filedata->file_header.e_type == ET_EXEC
@@ -8349,9 +8501,12 @@ process_section_headers (Filedata * filedata)
}
static bool
get_symtab (Filedata *filedata, Elf_Internal_Shdr *symsec,
Elf_Internal_Sym **symtab, uint64_t *nsyms,
char **strtab, uint64_t *strtablen)
get_symtab (Filedata * filedata,
Elf_Internal_Shdr * symsec,
Elf_Internal_Sym ** symtab,
uint64_t * nsyms,
char ** strtab,
uint64_t * strtablen)
{
*strtab = NULL;
*strtablen = 0;
@@ -8912,9 +9067,9 @@ static bool
display_relocations (Elf_Internal_Shdr * section,
Filedata * filedata)
{
if (section->sh_type != SHT_RELA
&& section->sh_type != SHT_REL
&& section->sh_type != SHT_RELR)
relocation_type rel_type = rel_type_from_sh_type (section->sh_type);
if (rel_type == reltype_unknown)
return false;
uint64_t rel_size = section->sh_size;
@@ -8943,33 +9098,43 @@ display_relocations (Elf_Internal_Shdr * section,
num_rela),
rel_offset, num_rela);
relocation_type rel_type = rel_type_from_sh_type (section->sh_type);
if (section->sh_link == 0
|| section->sh_link >= filedata->file_header.e_shnum)
/* Symbol data not available. */
return dump_relocations (filedata, rel_offset, rel_size,
NULL, 0, NULL, 0, rel_type,
false /* is_dynamic */);
Elf_Internal_Shdr * symsec = filedata->section_headers + section->sh_link;
if (symsec->sh_type != SHT_SYMTAB
&& symsec->sh_type != SHT_DYNSYM)
return false;
Elf_Internal_Sym * symtab;
uint64_t nsyms;
Elf_Internal_Shdr * symsec;
Elf_Internal_Sym * symtab = NULL;
uint64_t nsyms = 0;
uint64_t strtablen = 0;
char * strtab = NULL;
if (!get_symtab (filedata, symsec, &symtab, &nsyms, &strtab, &strtablen))
if (section->sh_link == 0
|| section->sh_link >= filedata->file_header.e_shnum)
{
/* Symbol data not available.
This can happen, especially with RELR relocs.
See if there is a .symtab section present.
If so then use it. */
symsec = find_section_by_name (filedata, ".symtab");
}
else
{
symsec = filedata->section_headers + section->sh_link;
if (symsec->sh_type != SHT_SYMTAB
&& symsec->sh_type != SHT_DYNSYM)
return false;
}
if (symsec != NULL
&& !get_symtab (filedata, symsec, &symtab, &nsyms, &strtab, &strtablen))
return false;
bool res = dump_relocations (filedata, rel_offset, rel_size,
symtab, nsyms, strtab, strtablen,
rel_type,
symsec->sh_type == SHT_DYNSYM);
bool res;
if (rel_type == reltype_relr)
res = dump_relr_relocations (filedata, section, symtab, nsyms, strtab, strtablen);
else
res = dump_relocations (filedata, rel_offset, rel_size,
symtab, nsyms, strtab, strtablen,
rel_type,
symsec == NULL ? false : symsec->sh_type == SHT_DYNSYM);
free (strtab);
free (symtab);
@@ -9173,15 +9338,6 @@ find_symbol_for_address (Filedata *filedata,
*offset = addr.offset;
}
static /* signed */ int
symcmp (const void *p, const void *q)
{
Elf_Internal_Sym *sp = (Elf_Internal_Sym *) p;
Elf_Internal_Sym *sq = (Elf_Internal_Sym *) q;
return sp->st_value > sq->st_value ? 1 : (sp->st_value < sq->st_value ? -1 : 0);
}
/* Process the unwind section. */
#include "unwind-ia64.h"