[PATCH] debugedit: Support files with more than 65280 ELF sections
Mark Wielaard
mark@klomp.org
Mon Mar 9 17:38:41 GMT 2026
debugedit was using e_shnum, e_phnum and e_shstrndx directly from the
Elf header. Unfortunately these are too small to contain large
values. Use elf_getshdrnum and elf_getshdrstrndx and store results in
DSO. Also use gelf_getsymshndx instead of gelf_getsym to get the
proper section index for relocation symbols.
This also works around a crash in libelf when trying to update the Elf
when section zero hasn't been loaded yet.
Include a wrapper with an alternative implemention for elf_scnshndx,
which was broken before elfutils 0.193.
Add testcase that generates approx 65548 sections plus debuginfo and
check debugedit can rewrite the file paths.
* configure.ac: Add AC_PROG_CXX.
* tools/debugedit.c (DSO): Add shnum and shstrndx.
(scnshndx): New static function wrapper for elf_scnshndx.
(setup_relbuf): Get xndxdata if shnum >= SHN_LORESERVE.
Call gelf_getsymshndx.
(edit_dwarf2): Use DSO shnum and shstrndx.
(fdopen_dso): Set shnum and shstrndx.
(handle_build_id): Use DSO phnum.
(main): Use DSO shnum and shstrndx.
* tests/data/SOURCES/large.c: New test source file.
* tests/Makefile.am (EXTRA_DIST): Add large.c.
* tests/atlocal.in: Set CXX and CXXFLAGS.
* tests/debugedit.at (DEBUGEDIT_SETUP_LARGE): New m4 define.
Add debugedit large file test.
https://sourceware.org/bugzilla/show_bug.cgi?id=33819
Signed-off-by: Mark Wielaard <mark@klomp.org>
---
configure.ac | 1 +
tests/Makefile.am | 3 +-
tests/atlocal.in | 2 +
tests/data/SOURCES/large.c | 29 ++++++++++
tests/debugedit.at | 37 ++++++++++++
tools/debugedit.c | 116 ++++++++++++++++++++++++++++++-------
6 files changed, 167 insertions(+), 21 deletions(-)
create mode 100644 tests/data/SOURCES/large.c
diff --git a/configure.ac b/configure.ac
index a0cbb5445803..7ab3d3c9c558 100644
--- a/configure.ac
+++ b/configure.ac
@@ -42,6 +42,7 @@ AC_USE_SYSTEM_EXTENSIONS
AC_PROG_AWK
AC_PROG_SED
m4_version_prereq([2.70], [AC_PROG_CC], [AC_PROG_CC_C99])
+AC_PROG_CXX
AC_PROG_LN_S
AC_CHECK_TOOL([LD], [ld])
AC_CHECK_TOOL([READELF], [readelf])
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e17678dcc40d..331864ffae21 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -41,7 +41,8 @@ EXTRA_DIST += data/SOURCES/foo.c \
data/SOURCES/bar.c \
data/SOURCES/baz.c \
data/SOURCES/dupe.c \
- data/SOURCES/foobar.h
+ data/SOURCES/foobar.h \
+ data/SOURCES/large.c
TESTSUITE = $(srcdir)/testsuite
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 5b1b367c1c23..8e8c3df74b05 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -6,7 +6,9 @@ PATH=@abs_builddir@:@abs_top_builddir@:$top_srcdir:$srcdir:$PATH
# We do use the CC and LD found by configure, but explicitly keep
# CFLAGS and LDFLAGS empty because the tests use specific flags.
CC="@CC@"
+CXX="@CXX@"
CFLAGS=""
+CXXFLAGS=""
LD="@LD@"
AR="@AR@"
LDFLAGS=""
diff --git a/tests/data/SOURCES/large.c b/tests/data/SOURCES/large.c
new file mode 100644
index 000000000000..9314d452391c
--- /dev/null
+++ b/tests/data/SOURCES/large.c
@@ -0,0 +1,29 @@
+// Generates approx 65548 sections ( 32768 comdat groups + 32770 .text )
+// when build as an object file
+// https://sourceware.org/bugzilla/show_bug.cgi?id=33819
+// Testcase suggested by James Abbatiello <abbeyj@gmail.com>>
+
+template <int I> void foo() { }
+
+#define F0 foo<__COUNTER__>();
+#define F1 F0 F0
+#define F2 F1 F1
+#define F3 F2 F2
+#define F4 F3 F3
+#define F5 F4 F4
+#define F6 F5 F5
+#define F7 F6 F6
+#define F8 F7 F7
+#define F9 F8 F8
+#define F10 F9 F9
+#define F11 F10 F10
+#define F12 F11 F11
+#define F13 F12 F12
+#define F14 F13 F13
+#define F15 F14 F14
+
+int main()
+{
+ F15;
+ return 0;
+}
diff --git a/tests/debugedit.at b/tests/debugedit.at
index b1a52ef9cb5a..fd0f8bb3d892 100644
--- a/tests/debugedit.at
+++ b/tests/debugedit.at
@@ -63,6 +63,15 @@ $LD $LDFLAGS -r -o foobarbaz.part.o foo.o subdir_bar/bar.o baz.o
$CC $CFLAGS -g3 $GZ_FLAG $1 -o foobarbaz.exe foo.o subdir_bar/bar.o baz.o
]])
+m4_define([DEBUGEDIT_SETUP_LARGE],[[
+# Create some a large test binaries (65280+ sections).
+
+export HOME=${PWD}
+cp "${abs_srcdir}"/data/SOURCES/large.c .
+
+$CXX $CXXFLAGS -g -c large.c
+]])
+
# ===
# Check debugedit --help doesn't crash and burn.
# ===
@@ -938,3 +947,31 @@ AT_CHECK([[debugedit -p -b $(pwd) -d /foo/bar/baz ./main]])
AT_CHECK([[stat -c "%x %y" main]], [0], [expout], [])
AT_CLEANUP
+
+# debugedit should handle Elf files with > 65280 sections
+AT_SETUP([debugedit large file])
+AT_KEYWORDS([debugedit] [large])
+DEBUGEDIT_SETUP_LARGE
+
+# The debuginfo contains the current working directory
+AT_CHECK([[
+$READELF --debug-dump=info ./large.o | grep $(pwd)
+]],[0],[ignore],[ignore])
+
+AT_CHECK([[debugedit -b $(pwd) -d /foo ./large.o]])
+
+# Now it should not contain $(pwd)
+AT_CHECK([[
+$READELF --debug-dump=info ./large.o | grep $(pwd)
+]],[1],[ignore],[ignore])
+
+# Make sure it does contain /foo instead.
+AT_DATA([expout],
+[/foo
+])
+AT_CHECK([[
+$READELF --debug-dump=info ./large.o | grep -E 'DW_AT_(name|comp_dir)' \
+ | rev | cut -d: -f1 | rev | cut -c2- | grep ^/foo | sort -u
+]],[0],[expout],[ignore])
+
+AT_CLEANUP
\ No newline at end of file
diff --git a/tools/debugedit.c b/tools/debugedit.c
index b3f637230100..03788ff92a70 100644
--- a/tools/debugedit.c
+++ b/tools/debugedit.c
@@ -232,6 +232,8 @@ typedef struct
const char *filename;
int lastscn;
size_t phnum;
+ size_t shnum;
+ size_t shstrndx;
struct strings debug_str, debug_line_str;
struct debug_lines lines;
/* List of CUs that keeps track of version, ptr_size,
@@ -574,6 +576,48 @@ rel_cmp (const void *a, const void *b)
return 0;
}
+/* Wrapper for elf_scnshndx which is broken before elfutils 0.193. */
+static int
+scnshndx (Elf *elf, Elf_Scn *scn)
+{
+#if !_ELFUTILS_PREREQ (0, 193)
+ size_t scnndx = elf_ndxscn (scn);
+ /* By convention the SHT_SYMTAB_SHNDX section is right after the
+ SHT_SYMTAB section, so start there. */
+ Elf_Scn *nscn = scn;
+ while ((nscn = elf_nextscn (elf, nscn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (nscn, &shdr_mem);
+ if (shdr == NULL)
+ return -1;
+
+ if (shdr->sh_type == SHT_SYMTAB_SHNDX && shdr->sh_link == scnndx)
+ return elf_ndxscn (nscn);
+ }
+
+ /* OK, not found, start from the top. */
+ nscn = NULL;
+ while ((nscn = elf_nextscn (elf, nscn)) != NULL
+ && elf_ndxscn (nscn) != scnndx)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (nscn, &shdr_mem);
+ shdr = gelf_getshdr (nscn, &shdr_mem);
+ if (shdr == NULL)
+ return -1;
+
+ if (shdr->sh_type == SHT_SYMTAB_SHNDX && shdr->sh_link == scnndx)
+ return elf_ndxscn (nscn);
+ }
+
+ /* No shndx found, but no errors. */
+ return 0;
+#else
+ return elf_scnshndx (scn);
+#endif
+}
+
/* Returns a malloced REL array, or NULL when there are no relocations
for this section. When there are relocations, will setup relend,
as the last REL, and reltype, as SHT_REL or SHT_RELA. */
@@ -585,7 +629,11 @@ setup_relbuf (DSO *dso, debug_section *sec)
GElf_Rela rela;
GElf_Sym sym;
GElf_Addr base = dso->shdr[sec->sec].sh_addr;
+ Elf_Scn *symscn;
Elf_Data *symdata = NULL;
+ Elf_Data *xndxdata = NULL;
+ int xndxscnidx;
+ Elf32_Word shndx;
int rtype;
REL *relbuf;
REL *relend;
@@ -615,12 +663,23 @@ setup_relbuf (DSO *dso, debug_section *sec)
if (relbuf == NULL)
error (1, errno, "%s: Could not allocate memory", dso->filename);
- symdata = elf_getdata (dso->scn[dso->shdr[i].sh_link], NULL);
+ symscn = dso->scn[dso->shdr[i].sh_link];
+ symdata = elf_getdata (symscn, NULL);
assert (symdata != NULL && symdata->d_buf != NULL);
assert (elf_getdata (dso->scn[dso->shdr[i].sh_link], symdata) == NULL);
assert (symdata->d_off == 0);
assert (symdata->d_size == dso->shdr[dso->shdr[i].sh_link].sh_size);
+ /* Get extended section index table if there are 64k+ sections. */
+ xndxscnidx = dso->shnum >= SHN_LORESERVE ? scnshndx (dso->elf, symscn) : 0;
+ if (xndxscnidx > 0)
+ {
+ xndxdata = elf_getdata (elf_getscn (dso->elf, xndxscnidx), NULL);
+ if (xndxdata == NULL)
+ error (1, 0, "%s: Could not get extended section index table: %s",
+ dso->filename, elf_errmsg (-1));
+ }
+
for (ndx = 0, relend = relbuf; ndx < maxndx; ++ndx)
{
if (dso->shdr[i].sh_type == SHT_REL)
@@ -632,20 +691,23 @@ setup_relbuf (DSO *dso, debug_section *sec)
}
else
gelf_getrela (data, ndx, &rela);
- gelf_getsym (symdata, ELF64_R_SYM (rela.r_info), &sym);
+ gelf_getsymshndx (symdata, xndxdata, ELF64_R_SYM (rela.r_info),
+ &sym, &shndx);
+ if (sym.st_shndx != SHN_XINDEX)
+ shndx = sym.st_shndx;
/* Relocations against section symbols are uninteresting in REL. */
if (dso->shdr[i].sh_type == SHT_REL && sym.st_value == 0)
continue;
/* Only consider relocations against .debug_str,
.debug_str_offsets, .debug_line, .debug_line_str,
.debug_macro and .debug_abbrev. */
- if (sym.st_shndx == 0 ||
- (sym.st_shndx != debug_sections[DEBUG_STR].sec
- && sym.st_shndx != debug_sections[DEBUG_STR_OFFSETS].sec
- && sym.st_shndx != debug_sections[DEBUG_LINE].sec
- && sym.st_shndx != debug_sections[DEBUG_LINE_STR].sec
- && sym.st_shndx != debug_sections[DEBUG_MACRO].sec
- && sym.st_shndx != debug_sections[DEBUG_ABBREV].sec))
+ if (shndx == 0 ||
+ (shndx != debug_sections[DEBUG_STR].sec
+ && shndx != debug_sections[DEBUG_STR_OFFSETS].sec
+ && shndx != debug_sections[DEBUG_LINE].sec
+ && shndx != debug_sections[DEBUG_LINE_STR].sec
+ && shndx != debug_sections[DEBUG_MACRO].sec
+ && shndx != debug_sections[DEBUG_ABBREV].sec))
continue;
rtype = GELF_R_TYPE (rela.r_info);
@@ -2963,11 +3025,11 @@ edit_dwarf2 (DSO *dso)
debug_sections[i].relsec = 0;
}
- for (i = 1; i < dso->ehdr.e_shnum; ++i)
+ for (i = 1; i < dso->shnum; ++i)
if (! (dso->shdr[i].sh_flags & (SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR))
&& dso->shdr[i].sh_size)
{
- const char *name = strptr (dso, dso->ehdr.e_shstrndx,
+ const char *name = strptr (dso, dso->shstrndx,
dso->shdr[i].sh_name);
if (name != NULL
@@ -3513,7 +3575,7 @@ fdopen_dso (int fd, const char *name)
GElf_Ehdr ehdr;
int i;
DSO *dso = NULL;
- size_t phnum;
+ size_t phnum, shnum, shstrndx;
if (dest_dir == NULL && (!do_build_id || no_recompute_build_id))
elf = elf_begin (fd, ELF_C_READ, NULL);
@@ -3544,11 +3606,23 @@ fdopen_dso (int fd, const char *name)
goto error_out;
}
+ if (elf_getshdrnum (elf, &shnum) != 0)
+ {
+ error (0, 0, "Couldn't get number of shdrs: %s", elf_errmsg (-1));
+ goto error_out;
+ }
+
+ if (elf_getshdrstrndx (elf, &shstrndx) != 0)
+ {
+ error (0, 0, "Couldn't get section string index: %s", elf_errmsg (-1));
+ goto error_out;
+ }
+
/* Allocate DSO structure. Leave place for additional 20 new section
headers. */
dso = (DSO *)
- malloc (sizeof(DSO) + (ehdr.e_shnum + 20) * sizeof(GElf_Shdr)
- + (ehdr.e_shnum + 20) * sizeof(Elf_Scn *));
+ malloc (sizeof(DSO) + (shnum + 20) * sizeof(GElf_Shdr)
+ + (shnum + 20) * sizeof(Elf_Scn *));
if (!dso)
{
error (0, ENOMEM, "Could not open DSO");
@@ -3569,10 +3643,12 @@ fdopen_dso (int fd, const char *name)
memset (dso, 0, sizeof(DSO));
dso->elf = elf;
dso->phnum = phnum;
+ dso->shnum = shnum;
+ dso->shstrndx = shstrndx;
dso->ehdr = ehdr;
- dso->scn = (Elf_Scn **) &dso->shdr[ehdr.e_shnum + 20];
+ dso->scn = (Elf_Scn **) &dso->shdr[shnum + 20];
- for (i = 0; i < ehdr.e_shnum; ++i)
+ for (i = 0; i < shnum; ++i)
{
dso->scn[i] = elf_getscn (elf, i);
gelf_getshdr (dso->scn[i], dso->shdr + i);
@@ -3668,7 +3744,7 @@ handle_build_id (DSO *dso, Elf_Data *build_id,
x.d_type = ELF_T_PHDR;
x.d_size = sizeof u.phdr;
- for (i = 0; i < dso->ehdr.e_phnum; ++i)
+ for (i = 0; i < dso->phnum; ++i)
{
if (gelf_getphdr (dso->elf, i, &u.phdr) == NULL)
goto bad;
@@ -3680,7 +3756,7 @@ handle_build_id (DSO *dso, Elf_Data *build_id,
x.d_type = ELF_T_SHDR;
x.d_size = sizeof u.shdr;
- for (i = 0; i < dso->ehdr.e_shnum; ++i)
+ for (i = 0; i < dso->shnum; ++i)
if (dso->scn[i] != NULL)
{
u.shdr = dso->shdr[i];
@@ -3859,7 +3935,7 @@ main (int argc, char *argv[])
if (dso == NULL)
exit (1);
- for (i = 1; i < dso->ehdr.e_shnum; i++)
+ for (i = 1; i < dso->shnum; i++)
{
const char *name;
@@ -3876,7 +3952,7 @@ main (int argc, char *argv[])
}
/*@fallthrough@*/
case SHT_PROGBITS:
- name = strptr (dso, dso->ehdr.e_shstrndx, dso->shdr[i].sh_name);
+ name = strptr (dso, dso->shstrndx, dso->shdr[i].sh_name);
/* TODO: Handle stabs */
if (name != NULL && strcmp (name, ".stab") == 0)
{
--
2.53.0
More information about the Debugedit
mailing list