aboutsummaryrefslogtreecommitdiff
path: root/binutils
diff options
context:
space:
mode:
authorNick Clifton <nickc@redhat.com>2024-04-11 16:57:18 +0100
committerNick Clifton <nickc@redhat.com>2024-04-11 16:57:18 +0100
commitfcf8f3237cbaa0f97e57a161d7354cdb89a1ffa4 (patch)
tree9023c556f44b1b7eb2578de0f1ac5f8386bebd73 /binutils
parent8e8d0b63ff15896cc2c228c01f18dfcf2a4a9305 (diff)
downloadgdb-fcf8f3237cbaa0f97e57a161d7354cdb89a1ffa4.zip
gdb-fcf8f3237cbaa0f97e57a161d7354cdb89a1ffa4.tar.gz
gdb-fcf8f3237cbaa0f97e57a161d7354cdb89a1ffa4.tar.bz2
Improve readelf's display of RELR relocs.
Diffstat (limited to 'binutils')
-rw-r--r--binutils/NEWS2
-rw-r--r--binutils/readelf.c414
-rw-r--r--binutils/testsuite/binutils-all/readelf.exp67
-rw-r--r--binutils/testsuite/binutils-all/relr.s13
4 files changed, 367 insertions, 129 deletions
diff --git a/binutils/NEWS b/binutils/NEWS
index be744e3..5c31953 100644
--- a/binutils/NEWS
+++ b/binutils/NEWS
@@ -1,5 +1,7 @@
-*- text -*-
+* Readelf now displays RELR relocations in full detail.
+
* Readelf now has a -j/--display-section option which takes the name or index
of a section and displays its contents according to its type. The option can
be used multiple times on the command line to display multiple sections.
diff --git a/binutils/readelf.c b/binutils/readelf.c
index fa0de3a..fcf95ee 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -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);
+ Elf_Internal_Shdr * symsec;
+ Elf_Internal_Sym * symtab = NULL;
+ uint64_t nsyms = 0;
+ uint64_t strtablen = 0;
+ char * strtab = NULL;
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;
+ {
+ /* 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;
- Elf_Internal_Sym * symtab;
- uint64_t nsyms;
- uint64_t strtablen = 0;
- char * strtab = NULL;
+ if (symsec->sh_type != SHT_SYMTAB
+ && symsec->sh_type != SHT_DYNSYM)
+ return false;
+ }
- if (!get_symtab (filedata, symsec, &symtab, &nsyms, &strtab, &strtablen))
+ 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"
diff --git a/binutils/testsuite/binutils-all/readelf.exp b/binutils/testsuite/binutils-all/readelf.exp
index b91134b..09ed75f 100644
--- a/binutils/testsuite/binutils-all/readelf.exp
+++ b/binutils/testsuite/binutils-all/readelf.exp
@@ -642,3 +642,70 @@ readelf_test {-j .rela.debug_info --display-section=.rel.debug_info} $tempfile d
readelf_test --display-section=0 $tempfile display-section.0
+# Test that RELR relocations are display correctly.
+proc readelf_relr_test {} {
+ global srcdir
+ global subdir
+ global READELF
+ global READELFFLAGS
+
+ set testname "readelf -r (RELR)"
+
+ # Assemble the RELR test file (using magic to work for both 32-bit and
+ # 64-bit targets).
+ if {![binutils_assemble $srcdir/$subdir/relr.s tmpdir/relr.o]} then {
+ unsupported "$testname: failed to assemble RELR test file"
+ return
+ }
+
+ # Download it.
+ set tempfile [remote_download host tmpdir/relr.o]
+
+ # Run "readelf -r" on it.
+ set got [remote_exec host "$READELF $READELFFLAGS -r $tempfile" "" "/dev/null" "readelf.out"]
+ set got [lindex $got 1]
+
+ # Upload the results.
+ set output [remote_upload host readelf.out]
+
+ # Check for something going wrong.
+ if ![string match "" $got] then {
+ fail "$testname: unexpected output"
+ send_log $got
+ send_log "\n"
+ return
+ }
+
+ # Search for strings that should be in the output.
+ # There will also be these strings:
+ # readelf: Error: Section 4 has invalid sh_entsize of 0
+ # readelf: Error: (Using the expected size of 8 for the rest of this dump)
+ # But we ignore them...
+
+ set sought {
+ "0000: 0+01000 0+01000 .*"
+ "0001: 0+00003 0+0100. .*"
+ }
+
+ foreach looked_for $sought {
+ set lines [grep $output $looked_for]
+ if ![llength $lines] then {
+ fail "$testname: missing: $looked_for"
+ send_log readelf.out
+ return
+ }
+ }
+
+ file_on_host delete $tempfile
+ file_on_host delete $output
+
+ # All done.
+ pass "$testname"
+}
+
+# The AVR, H8300, IP2K and Z80 targets' .dc.a pseudo-op creates a
+# 16-bit entry rather than a 32-bit entry. Thus creating an
+# invalid RELR relocation.
+setup_xfail "avr-*-*" "h8300-*-*" "ip2k-*-*" "z80-*-*"
+
+readelf_relr_test
diff --git a/binutils/testsuite/binutils-all/relr.s b/binutils/testsuite/binutils-all/relr.s
new file mode 100644
index 0000000..ca9f7dd
--- /dev/null
+++ b/binutils/testsuite/binutils-all/relr.s
@@ -0,0 +1,13 @@
+
+# Note - in theory we should set the entsize field. But since
+# we want this file to be assembled for both 32-bit and 64-bit
+# targets we leave it empty. Readelf will complain, but will
+# carry on and (helpfully) it will set the entsize field for us.
+#
+# We also use the magic .dc.a pseudo-op set the correctly sized
+# entries in the RELR array. This works for most, but not all
+# ELF based targets.
+
+ .section .relr.foo, "a", %19
+ .dc.a 0x1000
+ .dc.a 0x0003