From a7fd11862703e45d2774981a4888bc127d473b06 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Tue, 16 Nov 2021 13:03:57 -0800 Subject: [PATCH] readelf: Support SHT_RELR/DT_RELR for -r The -r output for SHT_RELR looks like: Relocation section '.relr.dyn' at offset 0x530 contains 4 entries: 7 offsets 00000000000028c0 00000000000028c8 0000000000003ad0 0000000000003ad8 0000000000003ae0 0000000000003ae8 0000000000003af0 For --use-dynamic, the header looks like 'RELR' relocation section at offset 0x530 contains 32 bytes: include/ * elf/common.h (DT_ENCODING): Bump to 38. * elf/external.h (Elf32_External_Relr): New. (Elf64_External_Relr): New. binutils/ * readelf.c (enum relocation_type): New. (slurp_relr_relocs): New. (dump_relocations): Change is_rela to rel_type. Dump RELR. (dynamic_relocations): Add DT_RELR. (process_relocs): Check SHT_RELR and DT_RELR. (process_dynamic_section): Store into dynamic_info for DT_RELR/DT_RELRENT/DT_RELRSZ. --- binutils/ChangeLog | 11 +++ binutils/NEWS | 2 + binutils/readelf.c | 154 +++++++++++++++++++++++++++++++++-------- include/ChangeLog | 6 ++ include/elf/common.h | 2 +- include/elf/external.h | 8 +++ 6 files changed, 153 insertions(+), 30 deletions(-) diff --git a/binutils/ChangeLog b/binutils/ChangeLog index 6850edb9dd5..8d2f0413f9f 100644 --- a/binutils/ChangeLog +++ b/binutils/ChangeLog @@ -1,3 +1,14 @@ +2021-11-16 Fangrui Song + + * readelf.c (enum relocation_type): New. + (slurp_relr_relocs): New. + (dump_relocations): Change is_rela to rel_type. + Dump RELR. + (dynamic_relocations): Add DT_RELR. + (process_relocs): Check SHT_RELR and DT_RELR. + (process_dynamic_section): Store into dynamic_info for + DT_RELR/DT_RELRENT/DT_RELRSZ. + 2021-11-09 Nick Clifton * nm.c: Add --unicode option to control how unicode characters are diff --git a/binutils/NEWS b/binutils/NEWS index f948d34f091..f3881250fce 100644 --- a/binutils/NEWS +++ b/binutils/NEWS @@ -11,6 +11,8 @@ using --unicode=highlight will display them as unicode escape sequences highlighted in red (if supported by the output device). +* readelf -r dumps RELR relative relocations now. + Changes in 2.37: * The readelf tool has a new command line option which can be used to specify diff --git a/binutils/readelf.c b/binutils/readelf.c index 19c64918c06..116f879c892 100644 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@ -342,7 +342,14 @@ typedef enum unicode_display_type static unicode_display_type unicode_display = unicode_default; - +typedef enum +{ + reltype_unknown, + reltype_rel, + reltype_rela, + reltype_relr +} relocation_type; + /* Versioned symbol info. */ enum versioned_symbol_info { @@ -1354,6 +1361,76 @@ slurp_rel_relocs (Filedata * filedata, return true; } +static bool +slurp_relr_relocs (Filedata * filedata, + unsigned long relr_offset, + unsigned long relr_size, + bfd_vma ** relrsp, + unsigned long * nrelrsp) +{ + void *relrs; + size_t size = 0, nentries, i; + bfd_vma 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 = (bfd_vma *) xmalloc (size * sizeof (bfd_vma)); + if (*relrsp == NULL) + { + free (relrs); + error (_("out of memory parsing relocs\n")); + return false; + } + + size = 0; + for (i = 0; i < nentries; i++) + { + const bfd_vma 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 @@ -1406,30 +1483,46 @@ dump_relocations (Filedata * filedata, unsigned long nsyms, char * strtab, unsigned long strtablen, - int is_rela, + relocation_type rel_type, bool is_dynsym) { unsigned long i; Elf_Internal_Rela * rels; bool res = true; - if (is_rela == UNKNOWN) - is_rela = guess_is_rela (filedata->file_header.e_machine); + if (rel_type == reltype_unknown) + rel_type = guess_is_rela (filedata->file_header.e_machine) ? reltype_rela : reltype_rel; - if (is_rela) + if (rel_type == reltype_rela) { if (!slurp_rela_relocs (filedata, rel_offset, rel_size, &rels, &rel_size)) return false; } - else + else if (rel_type == reltype_rel) { if (!slurp_rel_relocs (filedata, rel_offset, rel_size, &rels, &rel_size)) return false; } + else if (rel_type == reltype_relr) + { + bfd_vma * relrs; + const char *format + = is_32bit_elf ? "%08" BFD_VMA_FMT "x\n" : "%016" BFD_VMA_FMT "x\n"; + + if (!slurp_relr_relocs (filedata, rel_offset, rel_size, &relrs, + &rel_size)) + return false; + + printf (ngettext (" %lu offset\n", " %lu offsets\n", rel_size), rel_size); + for (i = 0; i < rel_size; i++) + printf (format, relrs[i]); + free (relrs); + return true; + } if (is_32bit_elf) { - if (is_rela) + if (rel_type == reltype_rela) { if (do_wide) printf (_(" Offset Info Type Sym. Value Symbol's Name + Addend\n")); @@ -1446,7 +1539,7 @@ dump_relocations (Filedata * filedata, } else { - if (is_rela) + if (rel_type == reltype_rela) { if (do_wide) printf (_(" Offset Info Type Symbol's Value Symbol's Name + Addend\n")); @@ -1841,7 +1934,7 @@ dump_relocations (Filedata * filedata, if (filedata->file_header.e_machine == EM_ALPHA && rtype != NULL && streq (rtype, "R_ALPHA_LITUSE") - && is_rela) + && rel_type == reltype_rela) { switch (rels[i].r_addend) { @@ -1989,7 +2082,7 @@ dump_relocations (Filedata * filedata, version_string); } - if (is_rela) + if (rel_type == reltype_rela) { bfd_vma off = rels[i].r_addend; @@ -2000,7 +2093,7 @@ dump_relocations (Filedata * filedata, } } } - else if (is_rela) + else if (rel_type == reltype_rela) { bfd_vma off = rels[i].r_addend; @@ -8021,13 +8114,14 @@ static struct const char * name; int reloc; int size; - int rela; + relocation_type rel_type; } dynamic_relocations [] = { - { "REL", DT_REL, DT_RELSZ, false }, - { "RELA", DT_RELA, DT_RELASZ, true }, - { "PLT", DT_JMPREL, DT_PLTRELSZ, UNKNOWN } + { "REL", DT_REL, DT_RELSZ, reltype_rel }, + { "RELA", DT_RELA, DT_RELASZ, reltype_rela }, + { "RELR", DT_RELR, DT_RELRSZ, reltype_relr }, + { "PLT", DT_JMPREL, DT_PLTRELSZ, reltype_unknown } }; /* Process the reloc section. */ @@ -8043,7 +8137,7 @@ process_relocs (Filedata * filedata) if (do_using_dynamic) { - int is_rela; + relocation_type rel_type; const char * name; bool has_dynamic_reloc; unsigned int i; @@ -8052,7 +8146,7 @@ process_relocs (Filedata * filedata) for (i = 0; i < ARRAY_SIZE (dynamic_relocations); i++) { - is_rela = dynamic_relocations [i].rela; + rel_type = dynamic_relocations [i].rel_type; name = dynamic_relocations [i].name; rel_size = filedata->dynamic_info[dynamic_relocations [i].size]; rel_offset = filedata->dynamic_info[dynamic_relocations [i].reloc]; @@ -8060,16 +8154,16 @@ process_relocs (Filedata * filedata) if (rel_size) has_dynamic_reloc = true; - if (is_rela == UNKNOWN) + if (rel_type == reltype_unknown) { if (dynamic_relocations [i].reloc == DT_JMPREL) switch (filedata->dynamic_info[DT_PLTREL]) { case DT_REL: - is_rela = false; + rel_type = reltype_rel; break; case DT_RELA: - is_rela = true; + rel_type = reltype_rela; break; } } @@ -8092,7 +8186,7 @@ process_relocs (Filedata * filedata) filedata->num_dynamic_syms, filedata->dynamic_strings, filedata->dynamic_strings_length, - is_rela, true /* is_dynamic */); + rel_type, true /* is_dynamic */); } } @@ -8120,7 +8214,8 @@ process_relocs (Filedata * filedata) i++, section++) { if ( section->sh_type != SHT_RELA - && section->sh_type != SHT_REL) + && section->sh_type != SHT_REL + && section->sh_type != SHT_RELR) continue; rel_offset = section->sh_offset; @@ -8128,7 +8223,7 @@ process_relocs (Filedata * filedata) if (rel_size) { - int is_rela; + relocation_type rel_type; unsigned long num_rela; if (filedata->is_separate) @@ -8148,7 +8243,8 @@ process_relocs (Filedata * filedata) num_rela), rel_offset, num_rela); - is_rela = section->sh_type == SHT_RELA; + rel_type = section->sh_type == SHT_RELA ? reltype_rela : + section->sh_type == SHT_REL ? reltype_rel : reltype_relr; if (section->sh_link != 0 && section->sh_link < filedata->file_header.e_shnum) @@ -8170,15 +8266,14 @@ process_relocs (Filedata * filedata) dump_relocations (filedata, rel_offset, rel_size, symtab, nsyms, strtab, strtablen, - is_rela, + rel_type, symsec->sh_type == SHT_DYNSYM); free (strtab); free (symtab); } else dump_relocations (filedata, rel_offset, rel_size, - NULL, 0, NULL, 0, is_rela, - false /* is_dynamic */); + NULL, 0, NULL, 0, rel_type, false /* is_dynamic */); found = true; } @@ -11499,6 +11594,7 @@ the .dynstr section doesn't match the DT_STRTAB and DT_STRSZ tags\n")); case DT_RPATH : case DT_SYMBOLIC: case DT_REL : + case DT_RELR : case DT_DEBUG : case DT_TEXTREL : case DT_JMPREL : @@ -11555,6 +11651,8 @@ the .dynstr section doesn't match the DT_STRTAB and DT_STRSZ tags\n")); case DT_STRSZ : case DT_RELSZ : case DT_RELAENT : + case DT_RELRENT : + case DT_RELRSZ : case DT_SYMENT : case DT_RELENT : filedata->dynamic_info[entry->d_tag] = entry->d_un.d_val; @@ -11562,8 +11660,6 @@ the .dynstr section doesn't match the DT_STRTAB and DT_STRSZ tags\n")); case DT_PLTPADSZ: case DT_MOVEENT : case DT_MOVESZ : - case DT_RELRENT : - case DT_RELRSZ : case DT_PREINIT_ARRAYSZ: case DT_INIT_ARRAYSZ: case DT_FINI_ARRAYSZ: diff --git a/include/ChangeLog b/include/ChangeLog index 0e99cf39170..52483238ffb 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,9 @@ +2021-11-16 Fangrui Song + + * elf/common.h (DT_ENCODING): Bump to 38. + * elf/external.h (Elf32_External_Relr): New. + (Elf64_External_Relr): New. + 2021-09-07 Luis Machado Revert: [AArch64] MTE corefile support diff --git a/include/elf/common.h b/include/elf/common.h index 4da28fa24a4..5d31e35ec89 100644 --- a/include/elf/common.h +++ b/include/elf/common.h @@ -1102,13 +1102,13 @@ #define DT_FINI_ARRAYSZ 28 #define DT_RUNPATH 29 #define DT_FLAGS 30 -#define DT_ENCODING 32 #define DT_PREINIT_ARRAY 32 #define DT_PREINIT_ARRAYSZ 33 #define DT_SYMTAB_SHNDX 34 #define DT_RELRSZ 35 #define DT_RELR 36 #define DT_RELRENT 37 +#define DT_ENCODING 38 /* Note, the Oct 4, 1999 draft of the ELF ABI changed the values for DT_LOOS and DT_HIOS. Some implementations however, use diff --git a/include/elf/external.h b/include/elf/external.h index b24985687e6..815e39c2837 100644 --- a/include/elf/external.h +++ b/include/elf/external.h @@ -211,6 +211,10 @@ typedef struct { unsigned char r_addend[4]; /* Constant addend used to compute value */ } Elf32_External_Rela; +typedef struct { + unsigned char r_data[4]; /* RELR entry */ +} Elf32_External_Relr; + typedef struct { unsigned char r_offset[8]; /* Location at which to apply the action */ unsigned char r_info[8]; /* index and type of relocation */ @@ -222,6 +226,10 @@ typedef struct { unsigned char r_addend[8]; /* Constant addend used to compute value */ } Elf64_External_Rela; +typedef struct { + unsigned char r_data[8]; /* RELR entry */ +} Elf64_External_Relr; + /* dynamic section structure */ typedef struct { -- 2.30.2