libelf: fix OOB read in elf_getarsym 32-bit archive index count
read_number_entries () reads the number of entries of an archive symbol
index. On the mmap path, taken for images created with elf_memory ()
(and for mmap-backed files), it copied sizeof (union u) == 8 bytes
instead of the width-appropriate 4 bytes for a 32-bit ("symdef") index.
For a minimal in-memory archive whose index member holds only the 4-byte
count field, this read 4 bytes past the caller's buffer. Use w, matching
the pread path and the 64-bit case, so both paths read exactly the bytes
they need.
* libelf/elf_getarsym.c (read_number_entries): Use w, not sizeof u,
for the mmap memcpy.
* tests/ar-getarsym-mem.c: New file.
* tests/Makefile.am (check_PROGRAMS): Add ar-getarsym-mem.
(TESTS): Likewise.
(ar_getarsym_mem_LDADD): New variable.
* tests/.gitignore: Add ar-getarsym-mem.
Mark Wielaard [Fri, 3 Jul 2026 14:01:32 +0000 (16:01 +0200)]
libelf: Correct the size of the in memory datastructure (off-by-one)
Fix the u64 and u32 variable length array bounds. We state they are
[n], but there is actually room for [n+1] symbols. We don't actually
use the [n+1] entry, but we the undefined sanitizer warns about this.
Found by a new testcase by Kane Wang <wangqiang1@kylinos.cn>
* libelf/elf_getarsym.c (elf_getarsym): Mark (*u64) and (*u32)
as [n+1] arrays.
Sayed Kaif [Sun, 28 Jun 2026 14:59:01 +0000 (20:29 +0530)]
libdw: Fix out-of-bounds read in dwarf_next_cfi CIE augmentation parsing
dwarf_next_cfi parses CFI entries directly from the (untrusted)
.eh_frame or .debug_frame section data. When a CIE uses a sized ('z')
augmentation, the loop over the augmentation string characters consumes
an encoding byte for the 'L' and 'P' directives with
encoding = *bytes++;
without first checking that bytes is still within the entry. The only
bounds check (bytes > augmentation_data + augmentation_data_size) is done
after the loop, so the read happens before it is validated.
A crafted CIE with augmentation "zL" (or "zP") and an empty augmentation
data area leaves bytes pointing exactly at the end of the section, so
*bytes++ reads one byte past the section buffer. When the malformed CIE
is the last entry in the section this is a read past the end of the
mapped data.
Add the same "bytes >= limit" check that the surrounding code already
uses for the other reads in this function before consuming the encoding
byte.
The problem was found with AddressSanitizer. A 16-byte .eh_frame
containing a single CIE
With the fix dwarf_next_cfi returns DWARF_E_INVALID_DWARF for that input,
and parsing of the .eh_frame data of normal binaries is unchanged
(libc.so, gcc and libelf.so all iterate without errors).
Mark Wielaard [Sun, 28 Jun 2026 16:41:55 +0000 (18:41 +0200)]
configure: Remove rawmemchr decl check
Commit b35e1ec43e93 ("libelf: Fix out-of-bounds read in elf_getarsym
for unterminated symbol table") removed the last usage of rawmemchr
from the codebase so we no longer need to check for it.
Sayed Kaif [Sun, 28 Jun 2026 14:58:32 +0000 (20:28 +0530)]
libelf: Fix out-of-bounds read in elf_getarsym for unterminated symbol table
elf_getarsym parses the archive symbol table (the "/" or "/SYM64/"
member). After validating only the size of the index, it walks the
declared number of symbols and, for each one, computes a hash with
_dl_elf_hash and advances to the next name with rawmemchr (or an
open-coded loop on platforms without rawmemchr). Both of these scan
for a terminating NUL byte without any bound.
Nothing guarantees that the string area following the offset table
actually contains that many NUL-terminated names, or any NUL at all.
A crafted archive whose symbol-table string area is not NUL terminated
makes the name scan run past the end of the heap buffer holding the
string data (or past the mapped file) -- an out-of-bounds read.
Remember the end of the string area and use a bounded memchr to locate
each name terminator. If a name is not terminated within the string
area the archive is malformed, so fail with ELF_E_INVALID_ARCHIVE
instead of reading out of bounds.
Reproduced under AddressSanitizer with a crafted archive whose string
area contains no NUL byte:
==ERROR: AddressSanitizer: heap-buffer-overflow ... READ of size 1
#0 _dl_elf_hash dl-hash.h:45
#1 elf_getarsym elf_getarsym.c:294
0 bytes after 116-byte region ... allocated by elf_getarsym.c:215
The function is reachable from eu-nm -s and eu-readelf, which call
elf_getarsym on untrusted .a files.
Frank Ch. Eigler [Wed, 24 Jun 2026 18:31:17 +0000 (14:31 -0400)]
PR34309: debuginfod: support relative source paths for reproducible builds
Relaxed constraint checks in debuginfod-client and debuginfod-find to
allow relative source filenames (e.g. those starting with '.' or
normal filenames), supporting binaries built with options like
-fdebug-prefix-map as one way to produce reproducible builds. Note
that such paths will not generally be resolvable by debuginfod within
packages, nor if debuginfod's startup PWD is different from the build
tree. This is a niche function only.
Added tests/testfile-debug-map{.c,.bz2} compiled with -fdebug-prefix-map
and tests/run-debuginfod-debug-map.sh to verify.
Signed-off-By: Frank Ch. Eigler <fche@redhat.com> Reviewed-By: Aaron Merey <amerey@redhat.com>
Naveed Khan [Mon, 22 Jun 2026 14:27:22 +0000 (19:57 +0530)]
x86: Fix out-of-bounds read of register table in sample_perf_regs_mapping
x86_sample_perf_regs_mapping selects dwarf_to_perf from one of two
tables, regs_i386[] (9 entries) or regs_x86_64[] (17 entries), depending
on the sample ABI. The final mapping loop was bounded by
ebl->frame_nregs and indexed dwarf_to_perf[i]. For the x86_64 backend
frame_nregs is 17, so when a sample carries PERF_SAMPLE_REGS_ABI_32
(is_abi32 true) the loop reads regs_i386[9..16], eight ints past the end
of the 9-element array. The resulting out-of-bounds values are then used
to index perf_to_regs[].
The abi value originates from a perf sample and is passed unvalidated
through the public dwflst_perf_sample_getframes() API; it is never
checked against the backend ELF class. Commit a532f8d hardened the
perf_to_regs[] write side but left this read overflow in place.
Bound the loop by the minimum of frame_nregs and the selected table
length. Neither bound dominates: an i386 backend (frame_nregs 9)
handling an x86_64-ABI sample has frame_nregs < dwarf_to_perf_len, the
reverse of the overflow above, so MIN() is required rather than the table
length alone. Computing the limit once also keeps the loop bound
invariant.
* backends/x86_initreg_sample.c: Include system.h for MIN.
(x86_sample_perf_regs_mapping): Compute dwarf_to_perf_len for the
selected table and bound the mapping loop by
MIN (ebl->frame_nregs, dwarf_to_perf_len).
print_cu_index_section validated the .debug_cu_index/.debug_tu_index size
table against dataend using only a single row:
lengths_end = lengths + section_count * 4;
but the size-table print loop iterates one row per used unit, with
prow = lengths + (row - 1) * section_count * 4 for row up to unit_count,
reading up to lengths + unit_count * section_count * 4. For unit_count >= 2
this reads (unit_count - 1) * section_count * 4 bytes past the validated end
of the section -- a heap-buffer-overflow read reachable from a crafted
.dwp/ELF via 'eu-readelf --debug-dump=cu_index FILE' (or 'eu-readelf -w').
The offset table is not affected: its extent
(unit_count * section_count * off_bytes, which equals lengths) is already
validated against dataend. Size lengths_end over all unit_count rows to
match, and guard the multiplication against overflow.
* src/readelf.c (print_cu_index_section): Size the size table over
all unit_count rows; add an overflow guard.
__libdw_findabbrev tries to prevent a time-of-check to time-of-use race
condition by double-checking whether the target abbrev entry has been read
into cu->abbrev_hash. This double-check is conditional on all abbrev
entries having already been read (indicated by cu->last_abbrev_offset == -1).
This conditional double-check causes a race condition. This can happen
when the current thread's first Dwarf_Abbrev_Hash_find call returns NULL
and, before the current thread can acquire the abbrev_lock mutex, another
thread reads the target entry into cu->abbrev_hash but does not end up
reading all entries.
In this case, the current thread skips double-checking cu->abbrev_hash
because cu->last_abbrev_offset != -1. It then attempts to read any
remaining entries but fails to find the target entry because
cu->last_abbrev_offset has already advanced past it. __libdw_findabbrev
then returns DWARF_END_ABBREV despite the presence of a valid entry.
Fix this by unconditionally double-checking cu->abbrev_hash after
acquiring abbrev_lock.
Matej Smycka [Fri, 19 Jun 2026 22:06:50 +0000 (00:06 +0200)]
libdwfl: fix OOB read in elf_from_remote_memory phdr check
phoff (e_phoff) comes straight from the untrusted file header, and the
sum is computed in unsigned 64-bit with no overflow check.
* libdwfl/elf-from-memory.c (elf_from_remote_memory): Use an
overflow-safe bound when checking whether the phdrs are already
in the initial read.
* tests/elf-from-memory.c: New file.
* tests/Makefile.am (check_PROGRAMS): Add elf-from-memory.
(TESTS): Likewise.
(elf_from_memory_LDADD): New variable.
Signed-off-by: Matej Smycka <matejsmycka@gmail.com>
A crafted .debug_macro section with opcode 0 makes (opcode - 1) wrap
to 0xFFFFFFFF in the unsigned array index, writing a 16-byte struct
past the 255-element op_protos array. Reject opcode == 0 and add the
missing readp >= endp checks before reading the count and opcode
bytes.
* libdw/dwarf_getmacros.c (get_macinfo_table): Reject opcode 0,
add bounds checks.
* tests/testfile-macros-opcode0.bz2: New test input.
* tests/run-dwarf-getmacros.sh: Test it.
* tests/Makefile.am (EXTRA_DIST): Add new test file.
Sayed Kaif [Wed, 17 Jun 2026 10:31:45 +0000 (16:01 +0530)]
libdwfl: Skip segment that starts past contents_size in elf-from-memory
When rebuilding an ELF from process memory, segments_end used the last
PT_LOAD segment's end rather than the largest, so a later smaller
segment could shrink contents_size below an earlier segment's start,
underflowing end - start into a huge length and overflowing the heap
buffer. Skip any segment that starts past contents_size, matching the
guard already used in dwfl_segment_report_module.
Sayed Kaif [Fri, 19 Jun 2026 08:49:45 +0000 (14:19 +0530)]
x86: Fix out-of-bounds heap write in sample_perf_regs_mapping
x86_sample_perf_regs_mapping initialized perf_to_regs[] only up to the
highest set bit of perf_regs_mask, but indexed it with dwarf_to_perf[i]
values up to PERF_REG_X86_64_MAX-1. For a sample carrying only the base
GP registers the upper slots stay uninitialized, and the resulting index
j was used unchecked to write cached_regs_mapping[j], a count-element
calloc buffer. Initialize the whole perf_to_regs[] array to -1, bounds-
check j before the write, and check calloc for failure.
With -o elfcompress opens the output file with O_WRONLY and O_CREAT.
If the output file already existed then without O_TRUNC the file is
written from the start, but keeps all existing data. That means the
file might contain extra data if the (compressed) ELF file is shorter
than the existing file. Without -o the input file is the output file
and we create a temp file to create the output which should then be
atomically replaced using rename. This might fail when the input file
was a symlink (to a file in a different directory.)
An additional complication is that we were using full path based calls
for open, rename and unlink (in case we had to remove the file again).
This can go wrong if between the open and the unlink a path element is
changed by a different process. To prevent renaming or unlinking the
wrong file we have to first open the target directory and only then
create a file inside it with openat. And use the directory fd with
renameat or unlinkat when we have to rename or delete the file.
So to solve all these issues we first try to pin the directory of the
output file. Then try to open the output file with O_EXCL. Which makes
sure we created the file so we can write directly to it. If that fails
then the output file already exists. Then we first resolve the full
file path (in case the output file is a symlink) and then open the
(resolved) directory where the output should go. We then make sure to
create a temporary file inside this directory using a new system.h
helper function xmkstempat. Finally, after the output file is fully
created (and flushed out to disk) we use renameat to atomically
replace the output file with our temp file. Since we still have the
directory open this works even if the file path is changed while
creating the temp file data. On failure we use unlinkat with the
directory fd to remove the output file.
Add a new run-compress-test-out.sh testcase for input and/or output
file being symlinks and/or pointing to the same file.
* configure.ac: Check for getentropy, sys/random.h and
getrandom.
* lib/system.h (read_retry): New static inline function.
(xrandom64): Likewise.
(xmkstempat): Likewise.
* src/elfcompress.c (process_file): Declare new variables to
keep copies of the (ouput) directory, dirfd, base file and
temporary file name. Track whether we need to unlink_fnew. Fix
trailing \n in error fmt. Call open with O_EXCL for output
file. Use realpath and xmkstempat if output file exists. fsync
temp file before caling renameat. Use unlinkat in
cleanup. close and free new temporaries.
* tests/run-compress-test-out.sh: New test file.
* tests/Makefile.am (TESTS): Add new test file.
(EXTRA_DIST): Likewise.
Aaron Merey [Mon, 15 Jun 2026 23:58:55 +0000 (19:58 -0400)]
version.h: Add _ELFUTILS_THREAD_SAFE
Currently there is no API for applications to detect whether elfutils
libraries are built with thread safety support enabled (via the
--enable-thread-safety configure option).
Add macro _ELFUTILS_THREAD_SAFE to version.h. This macro is set to 1
if thread safety support is enabled. If thread safety support is not
enabled then this macro is left undefined.
Aaron Merey [Mon, 15 Jun 2026 02:43:55 +0000 (22:43 -0400)]
debuginfod-client.c: Set CURLOPT_CONNECTTIMEOUT_MS
If the $DEBUGINFOD_TIMEOUT environment variable is set then it is applied
to CURLOPT_LOW_SPEED_TIME/_LIMIT in init_handle(). This causes a server
to be skipped if it does not transfer 100K of data within the given timeout.
CURLOPT_LOW_SPEED_TIME/_LIMIT begins tracking this timeout only after the
connection to a server has been established. This results in
$DEBUGINFOD_TIMEOUT failing to be enforced if a server is unresponsive
when attempting to establish connection.
Fix this by also setting CURLOPT_CONNECTTIMEOUT_MS with the
$DEBUGINFOD_TIMEOUT as well.
CURLOPT_CONNECTTIMEOUT_MS uses type long. If converting the timeout to
milliseconds will exceed LONG_MAX, then set CURLOPT_CONNECTTIMEOUT_MS to
LONG_MAX.
Aaron Merey [Mon, 1 Jun 2026 01:54:53 +0000 (21:54 -0400)]
riscv_disasm.c: Fix out-of-bounds reads
The riscv_disasm function reads instruction mnemonics from static
arrays based on the Control and Status Register (CSR) number encoded
in an instruction. Two separate bounds checks performed before reading
from these arrays had incorrect upper bounds and allowed out-of-bounds
reads.
Fix two CSR bounds checks to prevent this. The affected CSR numbers that
are no longer included in the modified checks are now properly handled
in a generic CSR handler in the riscv_disasm function.
Mark Wielaard [Tue, 26 May 2026 16:18:25 +0000 (18:18 +0200)]
elfcompress: Handle shstrtab/symtab names more consistently
We track the shstrtab and symtab section names in case they need to be
renamed. These names are sometimes assigned a static string (from the
shstrtab string pool), sometimes created as a copy of an existing
string name and sometimes created by constructing a new string. This
means they might leak because they are never freed.
Fix this leak by defining the variables early (as NULL) before looping
over all sections, making sure to always assign a allocated (copy) of
the (new) name and explicitly free them at cleanup (run both on
success and failure).
This makes it easier to reason about allocation status and string
"ownership" making it easy to see there are no memory issues even in
the rare case of a .[z]debug section being used as a symtab or
shstrtab table (which is why there is no testcase, because it is
questionable whether such a file is actually valid).
* src/elfcompress.c (process_file): Define shstrtab_name,
shstrtab_newname, symtab_name and symtab_newname
early. xstrdup shstrtab_name and symtab_name if they were
assigned to a static string. Free all four strings at
cleanup.
Mark Wielaard [Mon, 18 May 2026 16:47:21 +0000 (18:47 +0200)]
libelf: Get orig_addralign the same way for 32/64 bit ELF in elf_compress
Depending on whether the ELF was 32 or 64 bit elf_compress would get
the original sh_addralign, as embedded in the Chdr, differenly. For
64bit ELF we would simply get the sh_addralign from the Shdr. For
32bit ELF we would take the (max) alignment of the underlying
Elf_Data, provided by __libelf_compress. Use the second method for
both since the Shdr might not have been setup yet, so what counts is
the Elf_Data alignment.
* libelf/elf_compress.c (elf_compress): Remove sh_addralign.
Set chdr.ch_addralign to orig_addralign in 64bit case.
Mark Wielaard [Mon, 18 May 2026 17:33:26 +0000 (19:33 +0200)]
elfcompress: Don't over allocate sections bitmap
In process_file we allocate a bitmap sections to track which sections
we want to (de)compress. This allocates shnum / 8 + 1 unsigned
ints. But an unsigned int can contain more bits than just 8. In all
other places we use WORD_BITS defined as 8U * sizeof (unsigned int).
Also use WORD_BITS to calculate how many unsigned ints we need for the
sections bitmap.
* src/elfcompress.c (process_file): Use shnum / WORD_BITS + 1
to allocate sections.
Aaron Merey [Thu, 14 May 2026 21:12:59 +0000 (17:12 -0400)]
dwarf_getsrclines.c: Restore initialization of debug_str_offset
Prior to commit d4b0848b ("libdw: dwarf_getsrcfiles should not imply
dwarf_getsrclines") debug_str_offset was initialized with a default
value of 0. This default initialization was removed as part of the
refactor introduced in d4b0848b.
Restore debug_str_offset's default initialization to 0. For a
well-formed binary, it wasn't possible to use debug_str_offset
uninitialized. The value is only relevant for line headers using
NVIDIA's CUBIN extension, in which case the real offset was always
stored in debug_str_offset before use. However it's possible for a
malformed binary to cause uninitialized use and static analyzers
may complain about this.
Mark Wielaard [Wed, 13 May 2026 15:56:18 +0000 (17:56 +0200)]
debuginfod: Don't trust x-debuginfod-size in debuginfod_validate_imasig
Double check file size the server sent against the file size we
actually got in debuginfod_validate_imasig. So we check the signature
over the whole file size as we received it. Otherwise we might be
creating a hash over a shorter (possibly zero sized) data. This makes
sure the server sents a signature that should match the full file (and
not just an arbitrary shorter prefix).
* debuginfod/debuginfod-client.c (debuginfod_validate_imasig):
Call fstat on fd and check x-debuginfod-size equals received
file size.
Mark Wielaard [Wed, 13 May 2026 13:57:57 +0000 (15:57 +0200)]
debuginfod-client: Use CURLOPT_MAXREDIRS
Guard against bad servers. Older libcurl allowed unlimited
redirects. Newer libcurl default to max 30. Explicitly set
CURLOPT_MAXREDIRS to 6 (twice the number of redirects that seems
"reasonable"). This guards against badly setup servers that keep
redirecting. https://curl.se/libcurl/c/CURLOPT_MAXREDIRS.html
Aaron Merey [Fri, 1 May 2026 17:11:47 +0000 (13:11 -0400)]
tests/Makefile.am: Add --track-destroy=yes to valgrind_cmd
Add --track-destroy=yes to valgrind_cmd when --tool=helgrind. This
reports missing pthread_{mutex,rwlock}_destroy calls when a lock is
initialized at the address of a live rwlock or mutex.
Aaron Merey [Sun, 10 May 2026 20:13:02 +0000 (16:13 -0400)]
src/strip.c: Unconditionally re-iterate when preserving sh_link/sh_info
handle_elf may perform multiple iterations over section headers to
ensure that if a section is included in the debug file, then its
sh_link and sh_info sections are also included in the debug file.
handle_elf contains an optimization where an additional iteration may
be avoided if the sh_link or sh_info of a section marked for inclusion
in the main elf file have yet to be investigated by the current iteration.
The additional iteration is scheduled only if the loop index 'cnt' has
already passed the sh_link or sh_info.
This optimization can cause a bug where the sh_link or sh_info of
a section in the debug file may not be present in the debug file.
sh_link/sh_info cross references between .symtab and .rel.* sections
might be ordered such that .symtab is marked for stripping while iterating
over all the .rel.debug_* sections that reference .symtab in their sh_link,
resulting in no additional debug-preservation handling for .symtab.
However, it's possible later in the loop for a .rel.* section to refer
to an allocated section via sh_info and .symtab via sh_link. In this case,
.symtab gets marked for preservation in the main elf file. If no other
.rel.debug_* sections are encountered for the rest of the iteration, then
debug-preservation isn't triggered for .symtab. This results in the debug
file containing a NOBITS .symtab despite .rel.debug_* sections referencing
.symtab in their sh_link. If --reloc-debug-sections is given, a segfault
or error may occur when attempting to resolve these relocations.
Fix this by unconditionally scheduling another iteration across section
headers when an sh_link or sh_info gets flagged for inclusion in the main
elf file.
For the above case, the additional iteration will trigger debug-preservation
for .symtab upon detection that a .rel.debug_* sh_link is being kept in the
main elf file. .symtab will then be present in both the main elf file and
debug file.
Mark Wielaard [Mon, 4 May 2026 15:26:14 +0000 (17:26 +0200)]
libdwfl: Fix out of memory handling in link_map.c report_r_debug
In report_r_debug we don't fully handle an out of memory condition
when trying to allocate memory for the r_debug_info_module struct.
Deallocated allocated buffers and return -1 immediately instead of
trying to continue.
* libdwfl/link_map.c (report_r_debug): Call release_buffer
with -1 and return on malloc failure.
"gelf_*.3*" is the glob intended to include all gelf man pages in %files.
Man page gelf.3 was recently added and does not match the glob. Fix
this by also including "gelf.3*" in %files.
Serhei Makarov [Mon, 23 Mar 2026 18:53:56 +0000 (14:53 -0400)]
src/stacktrace.c: Enable ARM support
Since we plan to replace stacktrace with stackprof (& the associated
test case) in a subsequent patchset, this is a minimal patch to
illustrate where the existing code using libdwfl_stacktrace needs to
generalize.
* configure.ac: Expand ac_cv_has_asm_x86_perf_regs_h
to ac_cv_has_compat_asm_perf_regs_h also allowing arm/arm64.
* src/stacktrace.c: Enable for ARM architectures.
(expected_frame_nregs): New function.
(sp_reg_index): New function.
(sysprof_find_dwfl): Replace hardcoded SP index with new functions.
(sysprof_unwind_frame_cb): Likewise.
(main): Open default_ebl backend appropriate to the current arch.
Serhei Makarov [Fri, 6 Mar 2026 20:49:45 +0000 (15:49 -0500)]
backends/: add 32-bit ARM sample_regs support
After developing the common pieces in prior patches, adding register
sample support for another architecture is fairly predictable. Here we
add support for 32-bit ARM.
* backends/Makefile.am (arm_SRCS): Add arm_initreg_sample.c.
* backends/arm_init.c (arm_init): Add hooks for
sample_sp_pc, perf_frame_regs_mask.
* backends/arm_initreg_sample.c: New file. Use default
arm_set_initial_registers_sample implementation.
* backends/libebl_PERF_FLAGS.h (PERF_FRAME_REGISTERS_ARM): New
constant describing registers needed for 32-bit arm unwinding. Also
define it on 64-bit arm to allow profiling programs running in 32-bit
compatibility mode.
Serhei Makarov [Fri, 6 Mar 2026 20:25:16 +0000 (15:25 -0500)]
backends/: should use size_t n_regs_mapping for *_sample_sp_pc
These should have been size_t all along, matching the public
libdwfl_stacktrace dwflst_sample_getframes interface. Fix.
* backends/aarch64_initreg_sample.c (aarch64_sample_sp_pc): Fixup
argument type for n_regs_mapping.
* backends/i386_initreg_sample.c (i386_sample_sp_pc): Fixup argument
type for n_regs_mapping.
* backends/libebl_PERF_FLAGS.h (generic_sample_sp_pc): Fixup argument
type for n_regs_mapping, adjust loop index var accordingly.
* backends/x86_64_initreg_sample.c (x86_64_sample_sp_pc): Fixup
argument type for n_regs_mapping.
* libebl/ebl-hooks.h (sample_sp_pc): Fixup argument type for
n_regs_mapping.
Serhei Makarov [Fri, 6 Mar 2026 17:21:47 +0000 (12:21 -0500)]
libebl/eblinitreg_sample.c: fix a potential off-by-one error
I believe the way things were setup on x86 meant this never triggered
(since n_regs == n_regs_mapping, the for loop would stop at the
correct moment), but the logic here looks clearly incorrect for the
general case. Fix.
* libebl/eblinitreg_sample.c (ebl_set_initial_registers_sample):
Be sure to break when i == n_regs_mapping, which is out of range.
Serhei Makarov [Fri, 6 Mar 2026 17:33:49 +0000 (12:33 -0500)]
backends/: fixup x86 initreg_sample to share code with aarch64
A bunch of x86 sample_regs code should be common across
architectures. In particular, sample_sp_pc() implementation looks to
be universally applicable apart from the register positions.
* backends/x86_initreg_sample.c (x86_sample_sp_pc): Remove in favour
of generic_sample_sp_pc (added to libebl_PERF_FLAGS.h in prev patch).
(x86_sample_perf_regs_mapping): This should have been an inline
function. Fix.
* backends/i386_init.c (i386_init): Use
__libebl_init_cached_regs_mapping.
* backends/x86_64_init.c (x86_64_init): Use
__libebl_init_cached_regs_mapping.
* backends/i386_initreg_sample.c (i386_sample_sp_pc): Use
generic_sample_sp_pc.
* backends/x86_64_init.c (x86_64_initreg_sample): Use
generic_sample_sp_pc.
Serhei Makarov [Fri, 6 Mar 2026 17:10:29 +0000 (12:10 -0500)]
backends/: add aarch64 sample_regs support
Expanding the libdwflst support for handling perf sample_regs register
files to other architectures. First, add the pieces for aarch64.
* backends/Makefile.am (aarch64_SRCS): Add aarch64_initreg_sample.c.
* backends/aarch64_init.c (aarch64_init): Add hooks for
set_initial_registers_sample, sample_sp_pc, perf_frame_regs_mask.
* backends/aarch64_initreg_sample.c: New file. Implement
aarch64_set_initial_registers_sample mirroring the ptrace->dwarf_regs
logic in aarch64_initreg.c.
* backends/libebl_PERF_FLAGS.h (PERF_FRAME_REGISTERS_AARCH64): New
constant describing registers needed for aarch64 unwinding.
(generic_sample_sp_pc): New inline function generalizing existing
x86_sample_sp_pc code to any arch given sp_index and pc_index.
* libebl/eblinitreg_sample.c (ebl_sample_perf_regs_mapping): Update
with a default implementation for arches where perf_regs and
dwarf_regs order coincides (we just need the mapping to account for
present/absent registers in perf_regs mask).
* libebl/eblopenbackend.c (__libebl_init_cached_regs_mapping): New
function generalizing a bit of ARCH_init regs_mapping boilerplate.
* libebl/eblopenbackend.c (__libebl_init_cached_regs_mapping):
Implement new function.
Mark Wielaard [Mon, 13 Apr 2026 21:30:45 +0000 (23:30 +0200)]
libdw: Recognize .debug_dwp section in scn_dwarf_type as TYPE_DWO
dwarf_begin_elf tries to guess what type of Dwarf we have. When it
sees a .debug_dwp section it assumed it was TYPE_PLAIN instead of
TYPE_DWO and would try to find more "plain" .debug sections (ignoring
other .dwo sections). This would cause it to not detect any debug
sections in a .dwp file containing a .debug_dwp section.
* libdw/dwarf_begin_elf.c (scn_dwarf_type): Recognize
.debug_dwp or .zdebug_dwp sections.
Mark Wielaard [Mon, 13 Apr 2026 12:29:27 +0000 (14:29 +0200)]
elf_newdata: Make sure to set d_type to ELF_T_BYTE and d_align to one
If d_align isn't explicitly set in elf_newdata it will default to
zero. This confuses elf_update when more than one Elf_Data has been
added to a (new) Elf_Section. Only the last will appear to be added.
ELF_T_BYTE happens to be zero and so d_type did appear to be setup
correctly. Also set it explicitly to show the defaults.
The elf_newdata test happened to pass because it set d_align to 1
explicitly. But would fail if it wouldn't set it. Add some extra
checks.
Also update the elf_newdata man page with the new defaults.
* libelf/elf_newdata.c (elf_newdata): Explicitly set d_type
and d_align.
* tests/newdata.c (add_section_data): Check d_buf, d_size,
d_type, d_align and d_version default values.
* doc/elf_newdata.3: Document d_align defaults to one.
Mark Wielaard [Thu, 9 Apr 2026 12:40:44 +0000 (14:40 +0200)]
elflint: Recognize debug dwo sections
.debug dwo sections can include the SHF_EXCLUDE flag to indicate that
the linker shouldn't process them. Also a .debug_str.dwo section is
like a normal .debug_str section. There is no .debug_line_str.dwo
section, so that doesn't need special casing.
Add some dwo files to run-elflint-test.sh to make sure debug files
with .dwo sections aren't flagged by eu-elflint with --gnu --debug.
* src/elflint.c (special_sections): Add .debug_str.dwo.
(IS_DEBUG_DWO): New macro.
(check_sections): For IS_DEBUG_DWO files allow SHF_EXCLUDE.
* tests/run-elflint-test.sh: Add .dwo testfiles.
Mark Wielaard [Mon, 23 Mar 2026 18:01:53 +0000 (19:01 +0100)]
readelf: print .debug_cu_index and .debug_tu_index sections
DWARF Package files (.dwp) might contain a .debug_cu_index and/or
.debug_tu_index section. Print the contents of these sections. Handles
version 2 (DWARF4 + GNU Debugfission extension), version 5
(standardized) and version 6 (prelimenary, adds 64bit offsets). Use
the existing dwp4 and dwp5 testfiles.
* src/readelf.c (options): Add cu_index to debug-dump docs.
(section_e): Add section_cu_index.
(section_all): Likewise.
(parse_opt): handle "cu_index" arg for debug-dump.
(dwarf_section_short_string): New static function.
(print_cu_index_section): Likewise.
(print_debug): Move up implicit_info and explicit_info. Only
search for skeleton file if .debug_info is needed. Handle both
.debug_cu_index and .debug_tu_index through
print_cu_index_section.
* tests/run-readelf-cu-index.sh: New test.
* tests/Makefile.am (TESTS): Add run-readelf-cu-index.sh.
(EXTRA_DIST): Likewise.
Josef Cejka [Wed, 18 Mar 2026 11:02:13 +0000 (12:02 +0100)]
debuginfod: add metadata query by buildid
This patch extends current metadata by search by buildid.
It allows clients to query for all packages (with exact versions)
and files associated with a specific buildid.
I'd like to use the exact package versions during core dump
analysis to match them with already known issues/bugs,
to check that involved packages are reasonable up-to-date
and to identify other invalid setup, such as mixing packages
from different Linux distros.
Search by buildid prepares alternative db query, handling
of results incl. gathering results from federated servers
remains the same.
It does not search for source files or source file archives.
Mark Wielaard [Thu, 26 Mar 2026 12:32:18 +0000 (13:32 +0100)]
debuginfod: Make sure PATH_MAX exists in debuginfod-client.c
Some systems (Hurd) don't define PATH_MAX (which is optional according
to POSIX). Make sure it is defined and no special linux specific
headers are used in debuginfod-client.c.
* debuginfod/debuginfod-client.c: Include limits.h instead of
linux/limits. If PATH_MAX isn't define it to 4K (the default
on GNU/Linux).
Mark Wielaard [Sat, 21 Mar 2026 22:56:58 +0000 (23:56 +0100)]
readelf: Print split DWARF CU version, unit type and id
When printing the split units we reused the version, unit type and id
found in the skeleton unit. The version and id really should match,
but the unit type shouldn't. We could hard code the expected values,
but it is better to look them up explicitly. One testcase needed
updating to show the correct split_compile unit type.
* src/readelf.c (print_debug_units): Call dwarf_cu_die and
dwarf_cu_info to explicitly fetch split_version,
split_unit_type and split_unit_id.
* tests/run-readelf-loc.sh: Adjust Unit type.
Fabian Rast [Fri, 20 Mar 2026 13:50:35 +0000 (14:50 +0100)]
elflint: add additional lints to LOAD, INTERP and PHDR segments
* src/elflint.c (check_program_header):
- PT_PHDR may not occur more than once
- PT_INTERP must precede any loadable segment entry
- PT_LOAD entries must be sorted on the p_vaddr member
debuginfod_default_progressfn was added without a corresponding dummy
variant, causing an error when building with --disable-debuginfod and
--enable-libdebuginfod=dummy.
Commit d7cb72f7 changed debuginfod-find to always set a progress
callback function in order to handle interrupts during downloading.
Previously debuginfod-find set a progressfn only when the -v command
line option was given. If the DEBUGINFOD_PROGRESS environment variable
is set, libdebuginfod automatically sets a default progressfn as long as
no other progressfn has been set using debuginfod_set_progressfn.
Commit d7cb72f7 therefore resulted in DEBUGINFOD_PROGRESS no longer
affecting debuginfod-find when invoked without -v.
This patch restores the old behaviour: when DEBUGINFOD_PROGRESS is set
and -v is not given, the default progressfn will trigger during
downloading.
Aaron Merey [Wed, 11 Mar 2026 14:01:38 +0000 (10:01 -0400)]
libdebuginfod: Add the default progress callback function to the public API
Rename default_progressfn to debuginfod_default_progressfn and expose it
in libdebuginfod's public API. Previously this function could only be
indirectly invoked by clients by setting the DEBUGINFOD_PROGRESS
environment variable and not explicitly setting a progress callback
function with debuginfod_set_progressfn.
The purpose of this change is to make debuginfod_default_progressfn
directly callable so that custom progress callback functions can
delegate progress reporting to it.
Aaron Merey [Fri, 13 Mar 2026 21:46:48 +0000 (17:46 -0400)]
gelf_update_move.c: Handle ELFCLASS32 and ELFCLASS64 separately
Similar to commit c460e2088, gelf_update_move does not distinguish
between ELFCLASS32 and ELFCLASS64 binaries.
The code assumes that Elf32_Move and Elf64_Move structs are the same
size but they are not. The m_info and m_poffset fields have type
uint32_t for Elf32_Move but uint64_t for Elf64_Move.
Fix this by handling ELFCLASS32 and ELFCLASS64 cases separately.
Mark Wielaard [Sat, 7 Mar 2026 14:55:48 +0000 (15:55 +0100)]
libelf: elf_update should make sure section zero exists
elf_update might crash for 65280+ sections when section zero headers
aren't loaded. Make sure section zero is loaded before trying to write
to it.
Add testcase to tests/test-manyfuncs.c but use ELF_C_READ_MMAP_PRIVATE
to workaround https://sourceware.org/bugzilla/show_bug.cgi?id=33968
* libelf/elf32_updatenull.c (updatenull_wrlock): Call
getshdr_wrlock to load shdr.
* tests/test-manyfuncs.c (check_elf): Use elf_begin with
ELF_C_READ_MMAP_PRIVATE if use_mmap. Call elf_update early.
Mark Wielaard [Mon, 2 Mar 2026 14:51:48 +0000 (15:51 +0100)]
libdwfl: Work around ET_REL files with sh_addr fields set to non-zero
libdwfl tries to model the loading of modules (executables, shared
libraries, the linux kernel and/or kernel modules) in
memory. Depending on the (offline) load address it then also applies
(simple) relocations for ET_REL (object files or kernel modules). Load
addresses are normally represented through phdr segments for ET_EXEC
or ET_DYN, but for ET_REL files (which don't have phdrs) the Elf
section sh_addr fields are used.
The sh_addr fields of the ET_REL Elf images are updated in two places
__libdwfl_elf_address_range (through __libdwfl_report_elf) and
__libdwfl_relocate (through dwfl_module_getelf and
dwfl_module_getdwarf). Both rely on sh_addr being zero if no load
address has been set yet, so the address layout for each (SHF_ALLOC)
section is done only once.
Recent linux kernels use a linker script that does set the sh_addr
fields to (random, linker assigned) non-zero addresses. See commit 1ba9f8979426 ("vmlinux.lds: Unify TEXT_MAIN, DATA_MAIN, and related
macros").
The sh_addr values seems unnecessary, but because they aren't zero
anymore our layout/relocation code doesn't know it still has to figure
out a "real" load value for these sections.
Introduce __libdwfl_reset_sh_addr which resets all sh_addr fields to
zero for SHF_ALLOC sections in ET_REL files.
We don't call __libdwfl_reset_sh_addr on aux_sym files (from
.gnu_debugdata) and when constructing an Elf from a core file. In all
other cases we know (or assume) that the Elf file is being opened
through libdw_open_elf (called indirectly through __libdw_open_elf,
__libdwfl_report_offline, dwfl_module_getelf and dwfl_module_getdwarf)
This technically changes the Elf that goes through libdwfl, but we
would already update the sh_addr fields for ET_REL Elf sections in
memory anyway to represent the load address as libdwfl would see
them. So this isn't really a change in behavior (it just might update
the sh_addr field twice).
Mark Wielaard [Tue, 24 Feb 2026 21:19:48 +0000 (22:19 +0100)]
aarch64: Recognize SHT_AARCH64_ATTRIBUTES
Recognize SHT_AARCH64_ATTRIBUTES. This is enough to stop elflint
complaining about unknown section types. But doesn't implement parsing
the attributes.
* backends/aarch64_symbol.c (aarch64_section_type_name):
New function.
* backends/aarch64_init.c (aarch64_init): Hook
section_type_name.
Aaron Merey [Tue, 24 Feb 2026 21:08:07 +0000 (16:08 -0500)]
dwarf_begin_elf.c: Fix lock resource leak
dwarf_begin_elf might free memory for a struct Dwarf after its rwlock and
mutex fields have been initialized without calling the corresponding
destroy functions to release the lock resources.
Fix this by moving lock inits to the end of the valid_p function, after
the struct Dwarf being intialized has passed all error checking.
Samuel Dainard [Tue, 17 Feb 2026 22:07:06 +0000 (23:07 +0100)]
readelf: Fix NULL pointer dereference in process_symtab with -D flag
When using eu-readelf with the -D (use_dynamic_segment) flag on malformed
ELF files, process_symtab can receive a NULL symstr_data pointer if
elf_getdata_rawchunk fails or the dynamic segment is malformed.
The function then dereferences symstr_data->d_buf without checking,
causing a segmentation fault.
Add NULL check before accessing symstr_data fields.
Fixes regression introduced in 0.194 where process_symtab was refactored
to handle dynamic segment symbol tables.
Signed-off-by: Samuel Dainard <sdainard@amazon.com>
Mark Wielaard [Tue, 10 Feb 2026 17:25:42 +0000 (18:25 +0100)]
tests: Skip tests on non-ELF bitcode object files
clang with -flto produces object files that contain bitcode. This
breaks various (self) testcases that expect those .o files are ELF
files. eu-readelf, eu-nm, eu-elfline and eu-elfcompress won't work on
such files.
Add a helper function is_obj_bitcode in test-subr.sh that tests can
use to possibly skip a check on an .o file.
* tests/test-subr.sh (is_obj_bitcode): New function.
(testrun_on_self): Use is_obj_bitcode to possibly skip a file.
(testrun_on_self_obj): Likewise.
(testrun_on_self_compressed): Likewise.
(testrun_on_self_quiet): Likewise.
* tests/run-elfclassify-self.sh: Check if the object files are
actually ELF files.
* tests/run-nm-self.sh: Use is_obj_bitcode to possibly skip file.
* tests/run-reverse-sections-self.sh: Likewise.
* tests/run-strip-reloc-self.sh: Likewise.
* tests/run-strip-strmerge.sh: Likewise.
* tests/strip-reloc-subr.sh: Likewise.
Mark Wielaard [Thu, 5 Feb 2026 18:58:05 +0000 (19:58 +0100)]
readelf: Handle CUs that share an addr_base or str_offsets_base
readelf assumed each .debug_addr and .debug_str_offsets was associated
with precisely one CU DIE. But CU DIEs can have the same
DW_AT_addr_base or DW_AT_str_offsets_base. Change the code to find the
first CU associated with the offset.
* src/readelf.c (print_debug_addr_section): Skip CUs that
have an addr_base already handled.
(print_debug_str_offsets_section): Likewise for
str_offsets_base.
Mark Wielaard [Mon, 9 Feb 2026 22:18:40 +0000 (23:18 +0100)]
elflint, readelf, support SHT_LLVM_LTO and SHT_LLVM_ADDRSIG
An SHT_LLVM_LTO section contains LLVM bitcode. An SHT_LLVM_ADDRSIG
section contains ULEB128 integers referring to the symbol table
indexes of address-significant symbols. This patch just recognizes the
SHT constants but doesn't handle the section contents.
* libebl/eblsectiontypename.c (ebl_section_type_name):
Recognize SHT_LLVM_ADDRSIG and SHT_LLVM_LTO.
Mark Wielaard [Mon, 9 Feb 2026 22:17:19 +0000 (23:17 +0100)]
elflint: Accept R_X86_64_DTPOFF64 in ET_REL files
Normally R_X86_64_DTPOFF32, which takes a 32bit TLS offset, is used,
but R_X86_64_DTPOFF64, which takes a 64bit TLS offset may also be used
in ET_REL files.
Aaron Merey [Tue, 3 Feb 2026 21:31:00 +0000 (16:31 -0500)]
src/elfclassify.c: Fix memory leak in check_ar_members
If current_path needs to be reallocated, full_path is assigned a newly
malloced buffer and then full_path is assigned to current_path. This
leaks the previous value of full_path.
Mark Wielaard [Sat, 7 Feb 2026 17:25:09 +0000 (18:25 +0100)]
configure: Add -Wdeprecated-non-prototype and -Wfree-labels if available
Calling a function with one or more arguments declared with an empty
parameter list is an error in C23. In C23 labels applied to
non-statements are allowed, but not in earlier standards (they are as
GNU extension in GCC). Add -Wdeprecated-non-prototype and
-Wfree-labels (available since GCC 15) to catch this.
* configure.ac: Add HAVE_DEPRECATED_NON_PROTOTYPE_WARNING
and HAVE_FREE_LABELS_WARNING check.
* config/eu.am: Set FREE_LABELS_WARNING and
FREE_LABELS_WARNING.
(AM_CFLAGS): Add DEPRECATED_NON_PROTOTYPE_WARNING and
FREE_LABELS_WARNING.
Signed-off-by: Mark Wielaard <mark@klomp.org> Reviewed-by: Sam James <sam@gentoo.org>
Mark Wielaard [Sat, 7 Feb 2026 00:48:26 +0000 (01:48 +0100)]
configure: Add -Wmissing-parameter-name if available
Before C23 omitting parameter names in function definitions isn't
supported. Add -Wmissing-parameter-name (available since GCC 15) to
catch this.
* configure.ac: Add HAVE_MISSING_PARAMETER_NAME_WARNING check.
* config/eu.am: Set MISSING_PARAMETER_NAME_WARNING based on
HAVE_MISSING_PARAMETER_NAME_WARNING.
(AM_CFLAGS): Add MISSING_PARAMETER_NAME_WARNING.
Signed-off-by: Mark Wielaard <mark@klomp.org> Reviewed-by: Sam James <sam@gentoo.org>