From a88b2a0d6e139bb3293bf53cf3aa1ddb19b83720 Mon Sep 17 00:00:00 2001 From: Di Chen Date: Thu, 17 Oct 2024 22:38:52 -0400 Subject: [PATCH] libdwelf: add dwelf_elf_remove_debug_relocs Provide a public function for removing debug section relocations. eu-strip previously contained the code to remove debug section relocations. This patch moves that code into dwelf_elf_remove_debug_relocs. https://sourceware.org/bugzilla/show_bug.cgi?id=31447 Signed-off-by: Di Chen Signed-off-by: Aaron Merey --- libdw/libdw.map | 1 + libdwelf/Makefile.am | 5 +- libdwelf/dwelf_elf_remove_debug_relocations.c | 400 ++++++++++++++++++ libdwelf/libdwelf.h | 4 + src/strip.c | 354 +--------------- tests/Makefile.am | 5 +- tests/dwelf_reloc_dbg_scn | Bin 0 -> 19920 bytes tests/remove-relocs | Bin 0 -> 19904 bytes tests/remove-relocs.c | 38 ++ tests/run-remove-relocs.sh | 54 +++ tests/testfile-remove-relocs.bz2 | Bin 0 -> 1088 bytes 11 files changed, 513 insertions(+), 348 deletions(-) create mode 100644 libdwelf/dwelf_elf_remove_debug_relocations.c create mode 100755 tests/dwelf_reloc_dbg_scn create mode 100755 tests/remove-relocs create mode 100644 tests/remove-relocs.c create mode 100755 tests/run-remove-relocs.sh create mode 100644 tests/testfile-remove-relocs.bz2 diff --git a/libdw/libdw.map b/libdw/libdw.map index bc53385f6..b282e886b 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -384,4 +384,5 @@ ELFUTILS_0.192 { dwfl_set_sysroot; dwfl_frame_unwound_source; dwfl_unwound_source_str; + dwelf_elf_remove_debug_relocations; } ELFUTILS_0.191; diff --git a/libdwelf/Makefile.am b/libdwelf/Makefile.am index a35a2873c..6090fd640 100644 --- a/libdwelf/Makefile.am +++ b/libdwelf/Makefile.am @@ -42,13 +42,14 @@ noinst_HEADERS = libdwelfP.h libdwelf_a_SOURCES = dwelf_elf_gnu_debuglink.c dwelf_dwarf_gnu_debugaltlink.c \ dwelf_elf_gnu_build_id.c dwelf_scn_gnu_compressed_size.c \ dwelf_strtab.c dwelf_elf_begin.c \ - dwelf_elf_e_machine_string.c + dwelf_elf_e_machine_string.c \ + dwelf_elf_remove_debug_relocations.c libdwelf = $(libdw) libdw = ../libdw/libdw.so libelf = ../libelf/libelf.so -libebl = ../libebl/libebl.a +libebl = ../libebl/libebl.a ../backends/libebl_backends.a libeu = ../lib/libeu.a libdwelf_pic_a_SOURCES = diff --git a/libdwelf/dwelf_elf_remove_debug_relocations.c b/libdwelf/dwelf_elf_remove_debug_relocations.c new file mode 100644 index 000000000..cbb25fb44 --- /dev/null +++ b/libdwelf/dwelf_elf_remove_debug_relocations.c @@ -0,0 +1,400 @@ +/* Remove relocations from debug sections. + Copyright (C) 2014 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "libdwelfP.h" +#include "libelfP.h" +#include "libebl.h" + +typedef uint8_t GElf_Byte; + +const char * +secndx_name (Elf *elf, size_t ndx) +{ + size_t shstrndx; + GElf_Shdr mem; + Elf_Scn *sec = elf_getscn (elf, ndx); + GElf_Shdr *shdr = gelf_getshdr (sec, &mem); + if (shdr == NULL || elf_getshdrstrndx (elf, &shstrndx) < 0) + return "???"; + return elf_strptr (elf, shstrndx, shdr->sh_name) ?: "???"; +} + + +Elf_Data * +get_xndxdata (Elf *elf, Elf_Scn *symscn) +{ + Elf_Data *xndxdata = NULL; + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem); + if (shdr != NULL && shdr->sh_type == SHT_SYMTAB) + { + size_t scnndx = elf_ndxscn (symscn); + Elf_Scn *xndxscn = NULL; + while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL) + { + GElf_Shdr xndxshdr_mem; + GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); + + if (xndxshdr != NULL + && xndxshdr->sh_type == SHT_SYMTAB_SHNDX + && xndxshdr->sh_link == scnndx) + { + xndxdata = elf_getdata (xndxscn, NULL); + break; + } + } + } + + return xndxdata; +} + + +bool +relocate (Elf *elf, GElf_Addr offset, const GElf_Sxword addend, + Elf_Data *tdata, unsigned int ei_data, bool is_rela, + GElf_Sym *sym, int addsub, Elf_Type type) +{ + /* These are the types we can relocate. */ +#define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \ + DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \ + DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword) + + size_t size; + +#define DO_TYPE(NAME, Name) GElf_##Name Name; + union { TYPES; } tmpbuf; +#undef DO_TYPE + + switch (type) + { +#define DO_TYPE(NAME, Name) \ + case ELF_T_##NAME: \ + size = sizeof (GElf_##Name); \ + tmpbuf.Name = 0; \ + break; + TYPES; +#undef DO_TYPE + default: + return false; + } + + if (offset > tdata->d_size + || tdata->d_size - offset < size) + { + //__libelf_seterrno (ELF_E_INVALID_OFFSET); + return false; + } + + /* When the symbol value is zero then for SHT_REL + sections this is all that needs to be checked. + The addend is contained in the original data at + the offset already. So if the (section) symbol + address is zero and the given addend is zero + just remove the relocation, it isn't needed + anymore. */ + if (addend == 0 && sym->st_value == 0) + return true; + + Elf_Data tmpdata = + { + .d_type = type, + .d_buf = &tmpbuf, + .d_size = size, + .d_version = EV_CURRENT, + }; + Elf_Data rdata = + { + .d_type = type, + .d_buf = tdata->d_buf + offset, + .d_size = size, + .d_version = EV_CURRENT, + }; + + GElf_Addr value = sym->st_value; + if (is_rela) + { + /* For SHT_RELA sections we just take the + given addend and add it to the value. */ + value += addend; + /* For ADD/SUB relocations we need to fetch the + current section contents. */ + if (addsub != 0) + { + Elf_Data *d = gelf_xlatetom (elf, &tmpdata, + &rdata, + ei_data); + if (d == NULL || d != &tmpdata) + { + //__libelf_seterrno (ELF_E_INVALID_HANDLE); + return false; + } + } + } + else + { + /* For SHT_REL sections we have to peek at + what is already in the section at the given + offset to get the addend. */ + Elf_Data *d = gelf_xlatetom (elf, &tmpdata, + &rdata, + ei_data); + if (d == NULL || d != &tmpdata) + { + //__libelf_seterrno (ELF_E_INVALID_HANDLE); + return false; + } + } + + switch (type) + { +#define DO_TYPE(NAME, Name) \ + case ELF_T_##NAME: \ + if (addsub < 0) \ + tmpbuf.Name -= (GElf_##Name) value; \ + else \ + tmpbuf.Name += (GElf_##Name) value; \ + break; + TYPES; +#undef DO_TYPE + default: + //__libelf_seterrno (ELF_E_UNKNOWN_TYPE); + return false; + } + + /* Now finally put in the new value. */ + Elf_Data *s = gelf_xlatetof (elf, &rdata, + &tmpdata, + ei_data); + if (s == NULL || s != &rdata) + { + //__libelf_seterrno (ELF_E_INVALID_HANDLE); + return false; + } + + return true; +} + + +int dwelf_elf_remove_debug_relocations (Elf *elf) +{ + size_t shstrndx; + if (elf_getshdrstrndx (elf, &shstrndx) < 0) + return -1; + + GElf_Ehdr ehdr_mem, *ehdr = gelf_getehdr (elf, &ehdr_mem); + if (ehdr == NULL) + return -1; + + const unsigned int ei_data = ehdr->e_ident[EI_DATA]; + + Ebl *ebl = ebl_openbackend (elf); + if (ebl == NULL) + return -1; + + int res = -1; + Elf_Scn *scn = NULL; + while ((scn = elf_nextscn (elf, scn)) != NULL) + { + /* We need the actual section and header from the elf + not just the cached original in shdr_info because we + might want to change the size. */ + GElf_Shdr shdr_mem; + GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); + + if (shdr != NULL + && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)) + { + /* Make sure that this relocation section points to a + section to relocate with contents, that isn't + allocated and that is a debug section. */ + Elf_Scn *tscn = elf_getscn (elf, shdr->sh_info); + GElf_Shdr tshdr_mem; + GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem); + if (tshdr == NULL + || tshdr->sh_type == SHT_NOBITS + || tshdr->sh_size == 0 + || (tshdr->sh_flags & SHF_ALLOC) != 0) + continue; + + const char *tname = elf_strptr (elf, shstrndx, + tshdr->sh_name); + if (! tname || ! ebl_debugscn_p (ebl, tname)) + continue; + + /* OK, lets relocate all trivial cross debug section + relocations. */ + Elf_Data *reldata = elf_getdata (scn, NULL); + if (reldata == NULL || reldata->d_buf == NULL) + goto exit; + + /* Make sure we adjust the uncompressed debug data + (and recompress if necessary at the end). */ + GElf_Chdr tchdr; + int tcompress_type = 0; + bool is_gnu_compressed = false; + if (startswith (tname, ".zdebug")) + { + is_gnu_compressed = true; + if (elf_compress_gnu (tscn, 0, 0) != 1) + goto exit; + } + else + { + if (gelf_getchdr (tscn, &tchdr) != NULL) + { + tcompress_type = tchdr.ch_type; + if (elf_compress (tscn, 0, 0) != 1) + goto exit; + } + } + + Elf_Data *tdata = elf_getdata (tscn, NULL); + if (tdata == NULL || tdata->d_buf == NULL + || tdata->d_type != ELF_T_BYTE) + goto exit; + + /* Pick up the symbol table and shndx table to + resolve relocation symbol indexes. */ + Elf64_Word symt = shdr->sh_link; + Elf_Data *symdata, *xndxdata; + Elf_Scn * symscn = elf_getscn (elf, symt); + symdata = elf_getdata (symscn, NULL); + xndxdata = get_xndxdata (elf, symscn); + if (symdata == NULL) + goto exit; + + if (shdr->sh_entsize == 0) + goto exit; + + size_t nrels = shdr->sh_size / shdr->sh_entsize; + size_t next = 0; + const bool is_rela = (shdr->sh_type == SHT_RELA); + + for (size_t relidx = 0; relidx < nrels; ++relidx) + { + int rtype, symndx, offset, addend; + union { GElf_Rela rela; GElf_Rel rel; } mem; + void *rel_p; /* Pointer to either rela or rel above */ + + if (is_rela) + { + GElf_Rela *r = gelf_getrela (reldata, relidx, &mem.rela); + if (r == NULL) + goto exit; + offset = r->r_offset; + addend = r->r_addend; + rtype = GELF_R_TYPE (r->r_info); + symndx = GELF_R_SYM (r->r_info); + rel_p = r; + } + else + { + GElf_Rel *r = gelf_getrel (reldata, relidx, &mem.rel); + if (r == NULL) + goto exit; + offset = r->r_offset; + addend = 0; + rtype = GELF_R_TYPE (r->r_info); + symndx = GELF_R_SYM (r->r_info); + rel_p = r; + } + + /* R_*_NONE relocs can always just be removed. */ + if (rtype == 0) + continue; + + /* We only do simple absolute relocations. */ + int addsub = 0; + Elf_Type type = ebl_reloc_simple_type (ebl, rtype, &addsub); + if (type == ELF_T_NUM) + goto relocate_failed; + + /* And only for relocations against other debug sections. */ + GElf_Sym sym_mem; + Elf32_Word xndx; + GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, + symndx, &sym_mem, + &xndx); + if (sym == NULL) + goto exit; + Elf32_Word sec = (sym->st_shndx == SHN_XINDEX + ? xndx : sym->st_shndx); + + bool dbg_scn = ebl_debugscn_p (ebl, secndx_name (elf, sec)); + + if (!dbg_scn) + goto relocate_failed; + + if (! relocate (elf, offset, addend, tdata, ei_data, is_rela, + sym, addsub, type)) + goto relocate_failed; + + continue; /* Next */ + +relocate_failed: + if (relidx != next) + { + int updated; + if (is_rela) + updated = gelf_update_rela (reldata, next, rel_p); + else + updated = gelf_update_rel (reldata, next, rel_p); + if (updated == 0) + goto exit; + } + ++next; + } + + nrels = next; + shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize; + if (gelf_update_shdr (scn, shdr) == 0) + goto exit; + + if (is_gnu_compressed) + { + if (elf_compress_gnu (tscn, 1, ELF_CHF_FORCE) != 1) + goto exit; + } + else if (tcompress_type != 0) + { + if (elf_compress (tscn, tcompress_type, ELF_CHF_FORCE) != 1) + goto exit; + } + } + } + + res = 0; + +exit: + ebl_closebackend (ebl); + return res; +} diff --git a/libdwelf/libdwelf.h b/libdwelf/libdwelf.h index 263ca60e3..734aa9c3c 100644 --- a/libdwelf/libdwelf.h +++ b/libdwelf/libdwelf.h @@ -140,6 +140,10 @@ extern Elf *dwelf_elf_begin (int fd); value, or NULL if the given number isn't currently known. */ extern const char *dwelf_elf_e_machine_string (int machine); +/* Remove relocations for debug sections. Returns 0 on success. + Returns -1 on failure and sets elf_errno. */ +extern int dwelf_elf_remove_debug_relocations (Elf *elf); + #ifdef __cplusplus } #endif diff --git a/src/strip.c b/src/strip.c index 403e0f6fb..a431ebbec 100644 --- a/src/strip.c +++ b/src/strip.c @@ -367,46 +367,6 @@ parse_opt (int key, char *arg, struct argp_state *state) return 0; } -static const char * -secndx_name (Elf *elf, size_t ndx) -{ - size_t shstrndx; - GElf_Shdr mem; - Elf_Scn *sec = elf_getscn (elf, ndx); - GElf_Shdr *shdr = gelf_getshdr (sec, &mem); - if (shdr == NULL || elf_getshdrstrndx (elf, &shstrndx) < 0) - return "???"; - return elf_strptr (elf, shstrndx, shdr->sh_name) ?: "???"; -} - -/* Get the extended section index table data for a symbol table section. */ -static Elf_Data * -get_xndxdata (Elf *elf, Elf_Scn *symscn) -{ - Elf_Data *xndxdata = NULL; - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (symscn, &shdr_mem); - if (shdr != NULL && shdr->sh_type == SHT_SYMTAB) - { - size_t scnndx = elf_ndxscn (symscn); - Elf_Scn *xndxscn = NULL; - while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL) - { - GElf_Shdr xndxshdr_mem; - GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem); - - if (xndxshdr != NULL - && xndxshdr->sh_type == SHT_SYMTAB_SHNDX - && xndxshdr->sh_link == scnndx) - { - xndxdata = elf_getdata (xndxscn, NULL); - break; - } - } - } - - return xndxdata; -} /* Updates the shdrstrndx for the given Elf by updating the Ehdr and possibly the section zero extension field. Returns zero on success. */ @@ -440,306 +400,6 @@ update_shdrstrndx (Elf *elf, size_t shdrstrndx) } -/* Apply one relocation. Returns true when trivial - relocation actually done. */ -static bool -relocate (Elf *elf, GElf_Addr offset, const GElf_Sxword addend, - Elf_Data *tdata, unsigned int ei_data, const char *fname, - bool is_rela, GElf_Sym *sym, int addsub, Elf_Type type) -{ - /* These are the types we can relocate. */ -#define TYPES DO_TYPE (BYTE, Byte); DO_TYPE (HALF, Half); \ - DO_TYPE (WORD, Word); DO_TYPE (SWORD, Sword); \ - DO_TYPE (XWORD, Xword); DO_TYPE (SXWORD, Sxword) - - size_t size; - -#define DO_TYPE(NAME, Name) GElf_##Name Name; - union { TYPES; } tmpbuf; -#undef DO_TYPE - - switch (type) - { -#define DO_TYPE(NAME, Name) \ - case ELF_T_##NAME: \ - size = sizeof (GElf_##Name); \ - tmpbuf.Name = 0; \ - break; - TYPES; -#undef DO_TYPE - default: - return false; - } - - if (offset > tdata->d_size - || tdata->d_size - offset < size) - { - cleanup_debug (); - error_exit (0, _("bad relocation")); - } - - /* When the symbol value is zero then for SHT_REL - sections this is all that needs to be checked. - The addend is contained in the original data at - the offset already. So if the (section) symbol - address is zero and the given addend is zero - just remove the relocation, it isn't needed - anymore. */ - if (addend == 0 && sym->st_value == 0) - return true; - - Elf_Data tmpdata = - { - .d_type = type, - .d_buf = &tmpbuf, - .d_size = size, - .d_version = EV_CURRENT, - }; - Elf_Data rdata = - { - .d_type = type, - .d_buf = tdata->d_buf + offset, - .d_size = size, - .d_version = EV_CURRENT, - }; - - GElf_Addr value = sym->st_value; - if (is_rela) - { - /* For SHT_RELA sections we just take the - given addend and add it to the value. */ - value += addend; - /* For ADD/SUB relocations we need to fetch the - current section contents. */ - if (addsub != 0) - { - Elf_Data *d = gelf_xlatetom (elf, &tmpdata, - &rdata, - ei_data); - if (d == NULL) - INTERNAL_ERROR (fname); - assert (d == &tmpdata); - } - } - else - { - /* For SHT_REL sections we have to peek at - what is already in the section at the given - offset to get the addend. */ - Elf_Data *d = gelf_xlatetom (elf, &tmpdata, - &rdata, - ei_data); - if (d == NULL) - INTERNAL_ERROR (fname); - assert (d == &tmpdata); - } - - switch (type) - { -#define DO_TYPE(NAME, Name) \ - case ELF_T_##NAME: \ - if (addsub < 0) \ - tmpbuf.Name -= (GElf_##Name) value; \ - else \ - tmpbuf.Name += (GElf_##Name) value; \ - break; - TYPES; -#undef DO_TYPE - default: - abort (); - } - - /* Now finally put in the new value. */ - Elf_Data *s = gelf_xlatetof (elf, &rdata, - &tmpdata, - ei_data); - if (s == NULL) - INTERNAL_ERROR (fname); - assert (s == &rdata); - - return true; -} - -/* Remove any relocations between debug sections in ET_REL - for the debug file when requested. These relocations are always - zero based between the unallocated sections. */ -static void -remove_debug_relocations (Ebl *ebl, Elf *elf, GElf_Ehdr *ehdr, - const char *fname, size_t shstrndx) -{ - Elf_Scn *scn = NULL; - while ((scn = elf_nextscn (elf, scn)) != NULL) - { - /* We need the actual section and header from the elf - not just the cached original in shdr_info because we - might want to change the size. */ - GElf_Shdr shdr_mem; - GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); - if (shdr != NULL - && (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA)) - { - /* Make sure that this relocation section points to a - section to relocate with contents, that isn't - allocated and that is a debug section. */ - Elf_Scn *tscn = elf_getscn (elf, shdr->sh_info); - GElf_Shdr tshdr_mem; - GElf_Shdr *tshdr = gelf_getshdr (tscn, &tshdr_mem); - if (tshdr == NULL - || tshdr->sh_type == SHT_NOBITS - || tshdr->sh_size == 0 - || (tshdr->sh_flags & SHF_ALLOC) != 0) - continue; - - const char *tname = elf_strptr (elf, shstrndx, - tshdr->sh_name); - if (! tname || ! ebl_debugscn_p (ebl, tname)) - continue; - - /* OK, lets relocate all trivial cross debug section - relocations. */ - Elf_Data *reldata = elf_getdata (scn, NULL); - if (reldata == NULL || reldata->d_buf == NULL) - INTERNAL_ERROR (fname); - - /* Make sure we adjust the uncompressed debug data - (and recompress if necessary at the end). */ - GElf_Chdr tchdr; - int tcompress_type = 0; - bool is_gnu_compressed = false; - if (startswith (tname, ".zdebug")) - { - is_gnu_compressed = true; - if (elf_compress_gnu (tscn, 0, 0) != 1) - INTERNAL_ERROR (fname); - } - else - { - if (gelf_getchdr (tscn, &tchdr) != NULL) - { - tcompress_type = tchdr.ch_type; - if (elf_compress (tscn, 0, 0) != 1) - INTERNAL_ERROR (fname); - } - } - - Elf_Data *tdata = elf_getdata (tscn, NULL); - if (tdata == NULL || tdata->d_buf == NULL - || tdata->d_type != ELF_T_BYTE) - INTERNAL_ERROR (fname); - - /* Pick up the symbol table and shndx table to - resolve relocation symbol indexes. */ - Elf64_Word symt = shdr->sh_link; - Elf_Data *symdata, *xndxdata; - Elf_Scn * symscn = elf_getscn (elf, symt); - symdata = elf_getdata (symscn, NULL); - xndxdata = get_xndxdata (elf, symscn); - if (symdata == NULL) - INTERNAL_ERROR (fname); - - if (shdr->sh_entsize == 0) - INTERNAL_ERROR (fname); - - size_t nrels = shdr->sh_size / shdr->sh_entsize; - size_t next = 0; - const bool is_rela = (shdr->sh_type == SHT_RELA); - const unsigned int ei_data = ehdr->e_ident[EI_DATA]; - - for (size_t relidx = 0; relidx < nrels; ++relidx) - { - int rtype, symndx, offset, addend; - union { GElf_Rela rela; GElf_Rel rel; } mem; - void *rel_p; /* Pointer to either rela or rel above */ - - if (is_rela) - { - GElf_Rela *r = gelf_getrela (reldata, relidx, &mem.rela); - if (r == NULL) - INTERNAL_ERROR (fname); - offset = r->r_offset; - addend = r->r_addend; - rtype = GELF_R_TYPE (r->r_info); - symndx = GELF_R_SYM (r->r_info); - rel_p = r; - } - else - { - GElf_Rel *r = gelf_getrel (reldata, relidx, &mem.rel); - if (r == NULL) - INTERNAL_ERROR (fname); - offset = r->r_offset; - addend = 0; - rtype = GELF_R_TYPE (r->r_info); - symndx = GELF_R_SYM (r->r_info); - rel_p = r; - } - - /* R_*_NONE relocs can always just be removed. */ - if (rtype == 0) - continue; - - /* We only do simple absolute relocations. */ - int addsub = 0; - Elf_Type type = ebl_reloc_simple_type (ebl, rtype, &addsub); - if (type == ELF_T_NUM) - goto relocate_failed; - - /* And only for relocations against other debug sections. */ - GElf_Sym sym_mem; - Elf32_Word xndx; - GElf_Sym *sym = gelf_getsymshndx (symdata, xndxdata, - symndx, &sym_mem, - &xndx); - if (sym == NULL) - INTERNAL_ERROR (fname); - Elf32_Word sec = (sym->st_shndx == SHN_XINDEX - ? xndx : sym->st_shndx); - - bool dbg_scn = ebl_debugscn_p (ebl, secndx_name (elf, sec)); - - if (!dbg_scn) - goto relocate_failed; - - if (! relocate (elf, offset, addend, - tdata, ei_data, fname, is_rela, - sym, addsub, type)) - goto relocate_failed; - - continue; /* Next */ - -relocate_failed: - if (relidx != next) - { - int updated; - if (is_rela) - updated = gelf_update_rela (reldata, next, rel_p); - else - updated = gelf_update_rel (reldata, next, rel_p); - if (updated == 0) - INTERNAL_ERROR (fname); - } - ++next; - } - - nrels = next; - shdr->sh_size = reldata->d_size = nrels * shdr->sh_entsize; - if (gelf_update_shdr (scn, shdr) == 0) - INTERNAL_ERROR (fname); - - if (is_gnu_compressed) - { - if (elf_compress_gnu (tscn, 1, ELF_CHF_FORCE) != 1) - INTERNAL_ERROR (fname); - } - else if (tcompress_type != 0) - { - if (elf_compress (tscn, tcompress_type, ELF_CHF_FORCE) != 1) - INTERNAL_ERROR (fname); - } - } - } -} - static int process_file (const char *fname) { @@ -839,7 +499,7 @@ process_file (const char *fname) /* Processing for --reloc-debug-sections-only. */ static int -handle_debug_relocs (Elf *elf, Ebl *ebl, Elf *new_elf, +handle_debug_relocs (Elf *elf, Elf *new_elf, GElf_Ehdr *ehdr, const char *fname, size_t shstrndx, GElf_Off *last_offset, GElf_Xword *last_size) { @@ -910,8 +570,9 @@ handle_debug_relocs (Elf *elf, Ebl *ebl, Elf *new_elf, return 1; } - /* Adjust the relocation sections. */ - remove_debug_relocations (ebl, new_elf, ehdr, fname, shstrndx); + /* Remove debug section relocations. */ + if (dwelf_elf_remove_debug_relocations (new_elf) == -1) + INTERNAL_ERROR (fname); /* Adjust the offsets of the non-allocated sections, so they come after the allocated sections. */ @@ -1139,7 +800,7 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, if (reloc_debug_only) { - if (handle_debug_relocs (elf, ebl, newelf, ehdr, fname, shstrndx, + if (handle_debug_relocs (elf, newelf, ehdr, fname, shstrndx, &lastsec_offset, &lastsec_size) != 0) { result = 1; @@ -2457,7 +2118,10 @@ handle_elf (int fd, Elf *elf, const char *prefix, const char *fname, zero based between the unallocated sections. */ if (debug_fname != NULL && removing_sections && reloc_debug && ehdr->e_type == ET_REL) - remove_debug_relocations (ebl, debugelf, ehdr, fname, shstrndx); + { + if (dwelf_elf_remove_debug_relocations (debugelf) == -1) + INTERNAL_ERROR (fname); + } /* Now that we have done all adjustments to the data, we can actually write out the debug file. */ diff --git a/tests/Makefile.am b/tests/Makefile.am index ffccb0cd0..a3c4a745a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -59,7 +59,7 @@ check_PROGRAMS = arextract arsymtest newfile saridx scnnames sectiondump \ get-units-invalid get-units-split attr-integrate-skel \ all-dwarf-ranges unit-info next_cfi \ elfcopy addsections xlate_notes elfrdwrnop \ - dwelf_elf_e_machine_string \ + dwelf_elf_e_machine_string remove-relocs \ getphdrnum leb128 read_unaligned \ msg_tst system-elf-libelf-test system-elf-gelf-test \ nvidia_extended_linemap_libdw elf-print-reloc-syms \ @@ -204,6 +204,7 @@ TESTS = run-arextract.sh run-arsymtest.sh run-ar.sh newfile test-nlist \ run-strip-version.sh run-xlate-note.sh \ run-readelf-discr.sh \ run-dwelf_elf_e_machine_string.sh \ + run-remove-relocs.sh \ run-elfclassify.sh run-elfclassify-self.sh \ run-disasm-riscv64.sh \ run-pt_gnu_prop-tests.sh \ @@ -576,6 +577,7 @@ EXTRA_DIST = run-arextract.sh run-arsymtest.sh run-ar.sh \ run-readelf-discr.sh \ testfile-rng.debug.bz2 testfile-urng.debug.bz2 \ run-dwelf_elf_e_machine_string.sh \ + run-remove-relocs.sh testfile-remove-relocs.bz2 \ run-elfclassify.sh run-elfclassify-self.sh \ run-disasm-riscv64.sh \ testfile-riscv64-dis1.o.bz2 testfile-riscv64-dis1.expect.bz2 \ @@ -856,6 +858,7 @@ debuginfod_build_id_find_LDADD = $(libelf) $(libdw) xlate_notes_LDADD = $(libelf) elfrdwrnop_LDADD = $(libelf) dwelf_elf_e_machine_string_LDADD = $(libelf) $(libdw) +remove_relocs_LDADD = $(libelf) $(libdw) getphdrnum_LDADD = $(libelf) $(libdw) leb128_LDADD = $(libelf) $(libdw) read_unaligned_LDADD = $(libelf) $(libdw) diff --git a/tests/dwelf_reloc_dbg_scn b/tests/dwelf_reloc_dbg_scn new file mode 100755 index 0000000000000000000000000000000000000000..aeae29220197f5d410c20803f760aad81c1f521f GIT binary patch literal 19920 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 E10H0L3IG5A literal 0 Hc-jL100001 diff --git a/tests/remove-relocs.c b/tests/remove-relocs.c new file mode 100644 index 000000000..8870410b7 --- /dev/null +++ b/tests/remove-relocs.c @@ -0,0 +1,38 @@ +/* Test program for dwelf_elf_remove_debug_relocs + Copyright (C) 2024 Red Hat, Inc. + This file is part of elfutils. + + This file is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + elfutils is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include ELFUTILS_HEADER(dw) +#include ELFUTILS_HEADER(dwelf) + +int main(int /*argc*/, char **argv) { + Elf *elf; + elf_version (EV_CURRENT); + int fd = open (argv[1], O_RDWR); + + elf = elf_begin (fd, ELF_C_RDWR, NULL); + + dwelf_elf_remove_debug_relocations (elf); + elf_update (elf, ELF_C_WRITE); + + elf_end (elf); + return 0; +} diff --git a/tests/run-remove-relocs.sh b/tests/run-remove-relocs.sh new file mode 100755 index 000000000..3a49a5ab7 --- /dev/null +++ b/tests/run-remove-relocs.sh @@ -0,0 +1,54 @@ +#! /usr/bin/env bash +# Test dwelf_elf_remove_debug_relocations +# Copyright (C) 2024 Red Hat, Inc. +# This file is part of elfutils. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# elfutils is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +. $srcdir/test-subr.sh + +# = qq.c = +# int add (int a, int b) +# { +# return a+b; +# } +# +# int main() +# { +# return 0; +# } + +# Using gcc (GCC) 14.2.1 20240801 (Red Hat 14.2.1-1) +# gcc -g -c qq.c -o testfile-remove-relocs + +testfiles testfile-remove-relocs + +# Before debug relocations are removed some indices into string tables are +# set to a default value of 0. This causes incorrect file and function +# names to be displayed. +testrun_compare ${abs_builddir}/allfcts testfile-remove-relocs <<\EOF +qq.c/qq.c:6:GNU C17 14.2.1 20240801 (Red Hat 14.2.1-1) -mtune=generic -march=x86-64 -g +qq.c/qq.c:1:add +EOF + +# Remove debug relocations and write the changes to the testfile. +testrun ${abs_builddir}/remove-relocs testfile-remove-relocs + +# Correct file and function names should now be displayed. +testrun_compare ${abs_builddir}/allfcts testfile-remove-relocs <<\EOF +/home/dichen/elfutils/tests/qq.c:6:main +/home/dichen/elfutils/tests/qq.c:1:add +EOF + +exit 0 diff --git a/tests/testfile-remove-relocs.bz2 b/tests/testfile-remove-relocs.bz2 new file mode 100644 index 0000000000000000000000000000000000000000..2f7ca9b63adb5afc4e7b454c2531f328041de010 GIT binary patch literal 1088 zc-jF#1i$-2T4*^jL0KkKS)er3J^%yRfB*mg|Ni#(_5b_r(_;Vc|9gvQWE8{z0K`cE zL_h*SU`)^hde?`qYcFQ@bAv@sQ&Ue-r>W{R^)pjVs2MQ;dTIaw01W|=pa1{>01W^& zPfB_a%6e%(RMF`@G(Z3V02%-QXaE2J0ib9A05vd(6*r|D)X>l-(V*1Yn3`d!ko5*Y z1`q*>gu-ahVh=hgHKQZX`sOxGynhwMu5;^0B8n)00070Q8Xq%Ddieu z&@|8h^#e^b4F-ly02%;jXf$Zh8hU{B4^RmDELD`%=HnQFAq_A*eHhohwHb?H%Q?Jj5JcDh-nnrqZ0yV zp?K}+M)t}&0TYBpCP4s>M`X-{9QKu}rm8^2{8Ga9WfHFg1 z%;3?B4tGyKDx*gDKoITZ&l#gxiGagE0gi!*lLY8tvr_{Eh+xb?RW?#K>6!S>Hd-eY zQF{5F4VH#rf@T{fCstINWYCAw-%-swAQUOoJIk)esR% zCJPzH23@a#oMT86mdtp1}Z5NdFSDY9*X0TDQ5V3Fdkri;1Lt4OhI(Qj!q8Sh~h>FOk`gNj!!JRhh&3AoRYz1W~`%uZbp(j z`(a=?vs?t-7D9WEx*D>ns)3S3WRU}!zs#gkDn)mEW1m45Leqi7Mw0L+D9ZR7S?`rB ztJuvHH;@uvof^{^)#`vM17&EEEvlNnxSo7Nkuo7}BkPlc)_^q~`oeSqs;6gRG$Vkg z-0Y}YDo>PhS|Rb12AXW=fMvL^DBUv1JMdvp-3NODpvW%Fh(5%mW8x6j$@g_f4kR1I zM>t4XEXyu)<|I_1Udd|=Z1_|C;@CxF5}ksfWMDpRU%%`P2@9&F2mTMKM_fwv*4f+A454l literal 0 Hc-jL100001 -- 2.47.2