Bug 31635 - debuginfod cannot correctly fetch shared libraries without soname from server due to inconsistency
Summary: debuginfod cannot correctly fetch shared libraries without soname from server...
Status: UNCONFIRMED
Alias: None
Product: gdb
Classification: Unclassified
Component: corefiles (show other bugs)
Version: HEAD
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2024-04-12 11:48 UTC by Pablo Galindo Salgado
Modified: 2024-04-19 19:48 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Pablo Galindo Salgado 2024-04-12 11:48:50 UTC
When gdb uses debuginfod to fetch missing sharedlibraries from a given debuginfod server it fails for most libraries that don't have a soname due to an inconsistency in how the buildids are registered and how they are queried.

In corelow.c in the build_file_mappings we can see how the build ids are registered:

	/* If this is a bfd of a shared library, record its soname
	   and build id.  */
	if (build_id != nullptr)
	  {
	    gdb::unique_xmalloc_ptr<char> soname
	      = gdb_bfd_read_elf_soname (bfd->filename);
	    if (soname != nullptr)
	      set_cbfd_soname_build_id (current_program_space->cbfd,
					soname.get (), build_id);
	  }
      });


Here, if a library was downloaded by a debuginfod server bfd->filename it's the file that is in the debuginfod cache, (for example /home/pablogsal/.cache/debuginfod_client/4a834042b43eec1f2556ef4979828ea3b0813adc/executable). Notice that here there are two problems:

1) Libraries that do not have soname will never be registered in the map by set_cbfd_soname_build_id. This is very unfortunate because most shared libraries for dynamic languages such as Python do not have sonames set.
2) The registering into the map from library to buildid in set_cbfd_soname_build_id it's made by SONAME (from the dynamic table).


But later, in solib.c in the get_cbfd_soname_build_id() function we can see the following:

  soname_build_id_map *mapptr
    = cbfd_soname_build_id_data_key.get (abfd.get ());

  if (mapptr == nullptr)
    return {};

  auto it = mapptr->find (lbasename (soname));
  if (it == mapptr->end ())
    return {};


Here the query is made by the basename of the *soname* argument (lbasename (soname)) but this function is NOT called with the soname but rather the full path. Indeed, in solib_map_sections() we can observe:

  gdb::unique_xmalloc_ptr<char> build_id_hexstr
    = get_cbfd_soname_build_id (current_program_space->cbfd,
				so.so_name.c_str ());

but so.so_name.c_str() is a full path from the linker map. Notice the following:

1) This full path may be different from the one in the previous section because one comes from the core and the other comes from the linker map. One it's an absolute path and the other can be a symlink.
2) The query is made by full path but the map it's populate from DT_SONAME, which is inconsistent.


All of this together means that gdb will never properly load executables that are downloaded from the server for shared libraries that do not have sonames set. This is very unfortunate because many extension modules for dynamic languages such as Python don't have sonames and will never work for debuginfod:

$ python
Python 3.11.7 (main, Dec 24 2023, 14:12:08) [GCC 13.2.1 20230801] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import _ssl
>>> _ssl
<module '_ssl' from '/home/pablogsal/.pyenv/versions/3.11.7/lib/python3.11/lib-

$ readelf -d /home/pablogsal/.pyenv/versions/3.11.7/lib/python3.11/lib-dynload/_ssl.cpython-311-x86_64-linux-gnu.so

Dynamic section at offset 0x2ad28 contains 27 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libssl.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [libcrypto.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [/home/pablogsal/.pyenv/versions/3.11.7/lib]
 0x000000000000000c (INIT)               0x12000
 0x000000000000000d (FINI)               0x1c23c
 0x0000000000000019 (INIT_ARRAY)         0x2bc10
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x2bc18
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x000000006ffffef5 (GNU_HASH)           0x300
 0x0000000000000005 (STRTAB)             0x22c0
 0x0000000000000006 (SYMTAB)             0x328
 0x000000000000000a (STRSZ)              6467 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000003 (PLTGOT)             0x2bfe8
 0x0000000000000002 (PLTRELSZ)           7488 (bytes)
 0x0000000000000014 (PLTREL)             RELA
 0x0000000000000017 (JMPREL)             0xffd8
 0x0000000000000007 (RELA)               0x3f18
 0x0000000000000008 (RELASZ)             49344 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000006ffffffe (VERNEED)            0x3ea8
 0x000000006fffffff (VERNEEDNUM)         3
 0x000000006ffffff0 (VERSYM)             0x3c04
 0x000000006ffffff9 (RELACOUNT)          2033
 0x0000000000000000 (NULL)               0x0
Comment 1 Pablo Galindo Salgado 2024-04-12 11:49:09 UTC
Also related: https://sourceware.org/bugzilla/show_bug.cgi?id=31634
Comment 2 Aaron Merey 2024-04-19 19:48:14 UTC
(In reply to Pablo Galindo Salgado from comment #0) 
> 1) Libraries that do not have soname will never be registered in the map by
> set_cbfd_soname_build_id. This is very unfortunate because most shared
> libraries for dynamic languages such as Python do not have sonames set.
> 2) The registering into the map from library to buildid in
> set_cbfd_soname_build_id it's made by SONAME (from the dynamic table).

One challenge for getting debuginfod to work with corefiles is figuring out 
which library buildid in the corefile corresponds to a particular library
that gdb is unable to open in solib_map_sections.  Sonames are used because
we can create a soname to buildid mapping from the corefile and then use 
this mapping to get the right buildid for the debuginfod query when a shared
library can't otherwise be found by gdb in solib_map_sections.

> [...]
> Here the query is made by the basename of the *soname* argument (lbasename
> (soname)) but this function is NOT called with the soname but rather the 
> full path. Indeed, in solib_map_sections() we can observe:
> 
>   gdb::unique_xmalloc_ptr<char> build_id_hexstr
>     = get_cbfd_soname_build_id (current_program_space->cbfd,
>                               so.so_name.c_str ());
> 
> but so.so_name.c_str() is a full path from the linker map. Notice the 
> following:
> 
> 1) This full path may be different from the one in the previous section
> because one comes from the core and the other comes from the linker map. One 
> it's an absolute path and the other can be a symlink.
> 2) The query is made by full path but the map it's populate from DT_SONAME,
> which is inconsistent.

AFAIK when the library has a soname, the basename in get_cbfd_soname_build_id
should generally match a soname in the map.  If this wasn't the case then gdb 
would fail to query debuginfod for libraries associated with corefiles much 
more often.

I'll look into what paths gdb uses in solib_map_sections for libraries with
no soname.  Maybe it's possible to create a path-to-buildid map from the corefile
that we could use instead of the the soname-to-buildid map when a soname isn't
available.