Skip to content

Commit

Permalink
report: determine library search paths dynamically
Browse files Browse the repository at this point in the history
The library search order from `ld.so` differs from the hard-coded list:

```
$ ld.so --help
[...]
Shared library search path:
  (libraries located via /etc/ld.so.cache)
  /lib/x86_64-linux-gnu (system search path)
  /usr/lib/x86_64-linux-gnu (system search path)
  /lib (system search path)
  /usr/lib (system search path)
```

So determine the library search paths dynamically from `ld.so`.

Signed-off-by: Benjamin Drung <[email protected]>
  • Loading branch information
bdrung committed Jul 11, 2024
1 parent fa3e89c commit 7e9693b
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 8 deletions.
30 changes: 22 additions & 8 deletions apport/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,21 @@ def _get_gdb_path(gdb_sandbox: str | None, same_arch: bool) -> str:
return gdb_path


def _get_ld_search_paths(ld_so: str = "ld.so") -> list[str]:
"""Query the given ld.so binary for the shared library search paths."""
diagnostics = subprocess.run(
[ld_so, "--list-diagnostics"], check=True, stdout=subprocess.PIPE, text=True
)
# Example matching line: path.system_dirs[0x0]="/lib/x86_64-linux-gnu/"
matches = re.findall(
r'^path\.system_dirs\[(0x[0-9a-f]+)\]="([^"]+)"$',
diagnostics.stdout,
flags=re.MULTILINE,
)
ld_search_paths = {int(index, 0): path for (index, path) in matches}
return [path for (index, path) in sorted(ld_search_paths.items())]


def _run_hook(report, ui, hook):
if not os.path.exists(hook):
return False
Expand Down Expand Up @@ -1890,6 +1905,8 @@ def gdb_command(

if sandbox:
native_multiarch = "x86_64-linux-gnu"
host_library_paths = _get_ld_search_paths()
sandbox_library_paths = [f"{sandbox}{path}" for path in host_library_paths]
# N.B. set solib-absolute-prefix is an alias for set sysroot
command += [
"--ex",
Expand All @@ -1900,16 +1917,13 @@ def gdb_command(
"add-auto-load-safe-path " + sandbox,
# needed to fix /lib64/ld-linux-x86-64.so.2 broken symlink
"--ex",
f"set solib-search-path {sandbox}/lib/{native_multiarch}"
f":{sandbox}/usr/lib/{native_multiarch}",
f"set solib-search-path {':'.join(sandbox_library_paths)}",
]
if gdb_sandbox:
ld_lib_path = (
f"{gdb_sandbox}/lib"
f":{gdb_sandbox}/lib/{native_multiarch}"
f":{gdb_sandbox}/usr/lib/{native_multiarch}"
f":{gdb_sandbox}/usr/lib"
)
gdb_sandbox_library_paths = [
f"{gdb_sandbox}{path}" for path in host_library_paths
]
ld_lib_path = ":".join(gdb_sandbox_library_paths)
pyhome = f"{gdb_sandbox}/usr"
# env settings need to be modified for gdb in a sandbox
environ |= {
Expand Down
50 changes: 50 additions & 0 deletions tests/unit/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -1528,3 +1528,53 @@ def test_add_kernel_crash_info_fail(self, run_mock: MagicMock) -> None:
tmpfile = called_cmd[-1]
self.assertRegex(tmpfile, r"^/.*/apport_vmcore_\w{8}$")
self.assertFalse(pathlib.Path(tmpfile).exists())

@unittest.mock.patch("subprocess.run")
def test_get_ld_search_paths(self, run_mock: MagicMock) -> None:
run_mock.return_value = subprocess.CompletedProcess(
args=MagicMock(),
returncode=0,
stdout=textwrap.dedent(
"""\
path.prefix="/usr"
path.sysconfdir="/etc"
path.system_dirs[0x0]="/lib/x86_64-linux-gnu/"
path.system_dirs[0x1]="/usr/lib/x86_64-linux-gnu/"
path.system_dirs[0x2]="/lib/"
path.system_dirs[0x3]="/usr/lib/"
"""
),
)
ld_search_paths = apport.report._get_ld_search_paths()
self.assertEqual(
ld_search_paths,
[
"/lib/x86_64-linux-gnu/",
"/usr/lib/x86_64-linux-gnu/",
"/lib/",
"/usr/lib/",
],
)
run_mock.assert_called_once_with(
["ld.so", "--list-diagnostics"],
check=True,
stdout=subprocess.PIPE,
text=True,
)

@unittest.mock.patch("subprocess.run")
def test_get_ld_search_paths_sorting(self, run_mock: MagicMock) -> None:
run_mock.return_value = subprocess.CompletedProcess(
args=MagicMock(),
returncode=0,
stdout=textwrap.dedent(
"""\
path.system_dirs[0x2]="third"
path.system_dirs[0x1]="second"
path.system_dirs[0x0]="first"
"""
),
)
ld_search_paths = apport.report._get_ld_search_paths("ld.so")
self.assertEqual(ld_search_paths, ["first", "second", "third"])
run_mock.assert_called_once()

0 comments on commit 7e9693b

Please sign in to comment.