[PATCH] bfd/ELF: Handle multiple build-ids in corefile

Tom de Vries tdevries@suse.de
Wed Nov 26 15:58:33 GMT 2025


Consider the following test-case:
...
$ cat abort.c
int main (void) { __builtin_abort (); }
$ gcc abort.c
$ ./a.out
Aborted (core dumped)
$
...

If we use gdb to load both the exec and corefile:
...
$ gdb -q -batch a.out core
[New LWP 2568650]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./a.out'.
Program terminated with signal SIGABRT, Aborted.
44  return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0;
$
...
then bfd_elf64_core_file_matches_executable_p is called, which returns true
because core_bfd->build_id->data == exec_bfd->build_id.

Likewise if we rename a.out to b.out:
...
$ mv a.out b.out
$ gdb -q -batch b.out core

warning: Can't open file /data/vries/gdb/a.out during file-backed mapping \
  note processing
[New LWP 2568650]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./a.out'.
Program terminated with signal SIGABRT, Aborted.
44  return INTERNAL_SYSCALL_ERROR_P (ret) ? INTERNAL_SYSCALL_ERRNO (ret) : 0;
...

The only difference is that we get a warning from gdb about not being able to
find a.out.

Sofar, this is on openSUSE Leap 16.0 x86_64.  Let's try the same again in an
openSUSE Leap 15.6 podman container on the same system.

After recompiling and generating the core file, we have:
...
$ gdb -q -batch a.out core
[New LWP 3176707]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./a.out'.
Program terminated with signal SIGABRT, Aborted.
 #0  $hex in __pthread_kill_implementation () from /lib64/libc.so.6
$
...

Again, bfd_elf64_core_file_matches_executable_p is called, but
core_bfd->build_id->data != exec_bfd->build_id, so it falls back to the
filename comparison, which matches so still the function returns true.

But after renaming a.out:
...
$ mv a.out b.out
$ gdb -q -batch b.out core

warning: core file may not match specified executable file.
[New LWP 3176707]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./a.out'.
Program terminated with signal SIGABRT, Aborted.
 #0  $hex in __pthread_kill_implementation () from /lib64/libc.so.6
$
...
bfd_elf64_core_file_matches_executable_p returns false, and gdb issues the
"may not match" warning.

So in the latter setup, why do we have
core_bfd->build_id->data != exec_bfd->build_id?

The sizes are the same:
...
(gdb) p core_bfd->build_id.size
$1 = 20
(gdb) p exec_bfd->build_id.size
$2 = 20
...

But the data doesn't match:
...
(gdb) p /x *(char[20] *)&core_bfd->build_id.data
$3 = {0x23, 0x4d, 0x18, 0x74, 0xdf, 0x7c, 0x0, 0x12, 0xfc, 0x40, 0xa8, 0x8b, \
      0x6f, 0xde, 0xc0, 0xb2, 0x1e, 0xa0, 0x58, 0xe2}
(gdb) p /x *(char[20] *)&exec_bfd->build_id.data
$4 = {0x69, 0x3f, 0xcf, 0x4f, 0xb, 0x41, 0x36, 0x4c, 0xe2, 0x55, 0xc4, 0x6e, \
      0xc9, 0xd8, 0x41, 0x81, 0x71, 0xab, 0xd3, 0xb4}
...

The exec_bfd->build_id.data is correct:
...
$ file b.out
b.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically \
  linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 4.3.0, \
  BuildID[sha1]=693fcf4f0b41364ce255c46ec9d8418171abd3b4, with debug_info, \
  not stripped
...

But the core_bfd->build_id.data matches one of the shared libs:
...
$ ldd b.out
	linux-vdso.so.1 (0x00007f99578c3000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f99576a1000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f99578c5000)
$ file /lib64/ld-linux-x86-64.so.2
/lib64/ld-linux-x86-64.so.2: ELF 64-bit LSB shared object, x86-64, version 1 \
  (GNU/Linux), dynamically linked, \
  BuildID[sha1]=234d1874df7c0012fc40a88b6fdec0b21ea058e2, not stripped
...

The problem is the logic used while reading the core file, which considers that
the build id of a core file (meaning, the build id of the executable from which
the corefile was generated) is the first one encountered.

Usually, that is the case, but in this case not.

Fix this by:
- changing the type struct bfd_build_id to include a next field, turning it
  into a list,
- while reading the core, storing all build ids encountered in that list
- in elf_core_file_matches_executable_p, checking the list of build ids.

This gets rid of the "may not match" warning:
...
$ gdb -q -batch b.out core
[New LWP 3176707]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `./a.out'.
Program terminated with signal SIGABRT, Aborted.
 #0  $hex in __pthread_kill_implementation () from /lib64/libc.so.6
...

This fixes all FAILs in the container setup for test-case
gdb.debuginfod/corefile-mapped-file.exp.

Tested gdb, binutils, gas and ld on x86_64-linux with -enable-targets=all.

PR corefiles/33666
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33666
---
 bfd/bfd-in2.h |  1 +
 bfd/bfd.c     |  1 +
 bfd/elf.c     | 11 +++++++++--
 bfd/elfcore.h | 20 +++++++++++---------
 4 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index 6c05c21a0da..94d5de1878e 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1917,6 +1917,7 @@ enum bfd_plugin_format
 
 struct bfd_build_id
   {
+    const struct bfd_build_id *next;
     bfd_size_type size;
     bfd_byte data[1];
   };
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 11ce75669d3..eebd4656c1d 100644
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -71,6 +71,7 @@ EXTERNAL
 .
 .struct bfd_build_id
 .  {
+.    const struct bfd_build_id *next;
 .    bfd_size_type size;
 .    bfd_byte data[1];
 .  };
diff --git a/bfd/elf.c b/bfd/elf.c
index 33c2d269a9c..925e9db458a 100644
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -3471,7 +3471,7 @@ bfd_section_from_phdr (bfd *abfd, Elf_Internal_Phdr *hdr, int hdr_index)
     case PT_LOAD:
       if (! _bfd_elf_make_section_from_phdr (abfd, hdr, hdr_index, "load"))
 	return false;
-      if (bfd_get_format (abfd) == bfd_core && abfd->build_id == NULL)
+      if (bfd_get_format (abfd) == bfd_core)
 	_bfd_elf_core_find_build_id (abfd, hdr->p_offset);
       return true;
 
@@ -11294,9 +11294,16 @@ elfobj_grok_gnu_build_id (bfd *abfd, Elf_Internal_Note *note)
   if (build_id == NULL)
     return false;
 
+  build_id->next = NULL;
   build_id->size = note->descsz;
   memcpy (build_id->data, note->descdata, note->descsz);
-  abfd->build_id = build_id;
+  const struct bfd_build_id **next = &abfd->build_id;
+  while (*next != NULL)
+    {
+      const struct bfd_build_id *tmp = *next;
+      next = (const struct bfd_build_id **)&tmp->next;
+    }
+  *next = build_id;
 
   return true;
 }
diff --git a/bfd/elfcore.h b/bfd/elfcore.h
index 6eb354bdd1d..1cd918f2785 100644
--- a/bfd/elfcore.h
+++ b/bfd/elfcore.h
@@ -50,12 +50,14 @@ elf_core_file_matches_executable_p (bfd *core_bfd, bfd *exec_bfd)
     }
 
   /* If both BFDs have identical build-ids, then they match.  */
-  if (core_bfd->build_id != NULL
-      && exec_bfd->build_id != NULL
-      && core_bfd->build_id->size == exec_bfd->build_id->size
-      && memcmp (core_bfd->build_id->data, exec_bfd->build_id->data,
-		 core_bfd->build_id->size) == 0)
-    return true;
+  if (core_bfd->build_id != NULL)
+    {
+      const struct bfd_build_id *idx;
+      for (idx = core_bfd->build_id; idx != NULL; idx = idx->next)
+	if (idx->size == exec_bfd->build_id->size
+	    && memcmp (idx->data, exec_bfd->build_id->data, idx->size) == 0)
+	  return true;
+    }
 
   /* See if the name in the corefile matches the executable name.  */
   corename = elf_tdata (core_bfd)->core->program;
@@ -389,12 +391,12 @@ NAME(_bfd_elf, core_find_build_id)
 			offset + i_ehdr.e_phoff + (i + 1) * sizeof (x_phdr),
 			SEEK_SET) != 0)
 	    goto fail;
-
-	  if (abfd->build_id != NULL)
-	    return true;
 	}
     }
 
+  if (abfd->build_id != NULL)
+    return true;
+
   /* Having gotten this far, we have a valid ELF section, but no
      build-id was found.  */
   goto fail;

base-commit: 39b6fee4842875e29803f2ef92f52de185e84f36
-- 
2.51.0



More information about the Binutils mailing list