Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ env:
kde_deps: procps pyqt6-dev-tools python3-pyqt6
unit_deps: locales python3-systemd python3-zstandard
integration_deps: >
binutils dpkg-dev gcc gdb kmod libc6-dev libglib2.0-dev libxml2-utils
binutils gcc gdb kmod libc6-dev libglib2.0-dev libxml2-utils
polkitd python3-systemd valgrind xterm
system_deps: >
chaos-marmosets dbus dbus-x11 dirmngr dpkg-dev gcc gdb gvfs-daemons psmisc
Expand Down
15 changes: 4 additions & 11 deletions apport/package_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,23 +148,16 @@ def get_system_architecture() -> str:
"this method must be implemented by a concrete subclass"
)

@staticmethod
def get_native_multiarch_triplet() -> str:
"""Return the GNU multiarch triplet for of the system architecture, if
applicable, raises NotImplementedError otherwise.
"""
raise NotImplementedError(
"this method must be implemented by a concrete subclass if applicable"
)
def get_library_paths(self, architecture: str | None = None) -> str:
"""Return a list of default library search paths for the architecture.

def get_library_paths(self) -> str:
"""Return a list of default library search paths.
If architecture is not specified, the system archictecture is used.

The entries should be separated with a colon ':', like for
$LD_LIBRARY_PATH. This needs to take any multiarch directories into
account.
"""
# simple default implementation, pylint: disable=no-self-use
# simple default implementation, pylint: disable=no-self-use,unused-argument
return "/lib:/usr/lib"

def set_mirror(self, url: str) -> None:
Expand Down
35 changes: 21 additions & 14 deletions apport/packaging_impl/apt_dpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@
import apport.logging
from apport.package_info import PackageInfo

_ARCH_MULTIARCH_TRIPLET_MAPPING = {
"amd64": "x86_64-linux-gnu",
"arm64": "aarch64-linux-gnu",
"armhf": "arm-linux-gnueabihf",
"i386": "i386-linux-gnu",
"ppc64el": "powerpc64le-linux-gnu",
"riscv64": "riscv64-linux-gnu",
"s390x": "s390x-linux-gnu",
}
# The Contents-*.gz files are huge. Loading all data into memory would result in a
# dictionary with millions of entries consuming several gigabytes of memory.
# Therefore exclude unneeded paths with 100k entries or more.
Expand Down Expand Up @@ -869,26 +878,24 @@ def get_system_architecture() -> str:
assert arch
return arch

@staticmethod
@functools.cache
def get_native_multiarch_triplet() -> str:
"""Return the multiarch triplet for the system architecture"""
dpkg = subprocess.run(
["dpkg-architecture", "-qDEB_HOST_MULTIARCH"],
check=True,
text=True,
stdout=subprocess.PIPE,
)
return dpkg.stdout.strip()
def get_library_paths(self, architecture: str | None = None) -> str:
"""Return a list of default library search paths for the architecture.

def get_library_paths(self) -> str:
"""Return a list of default library search paths.
If architecture is not specified, the system archictecture is used.

The entries should be separated with a colon ':', like for
$LD_LIBRARY_PATH. This needs to take any multiarch directories into
account.
"""
return f"/lib/{self.get_native_multiarch_triplet()}:/lib"
if architecture is None:
architecture = self.get_system_architecture()
multiarch_triplet = _ARCH_MULTIARCH_TRIPLET_MAPPING.get(architecture)
if multiarch_triplet is None:
raise NotImplementedError(
f"Missing multi-arch triplet information"
f" for architecture {architecture}"
)
return f"/lib/{multiarch_triplet}:/usr/lib/{multiarch_triplet}:/lib:/usr/lib"

def set_mirror(self, url: str) -> None:
"""Explicitly set a distribution mirror URL for operations that need to
Expand Down
29 changes: 9 additions & 20 deletions apport/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,21 +341,6 @@ 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 @@ -2009,10 +1994,13 @@ def gdb_command(
environ: dict[str, str] = {}

if sandbox:
host_library_paths = _get_ld_search_paths(
"/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2"
)
sandbox_library_paths = [f"{sandbox}{path}" for path in host_library_paths]
arch = self.get("PackageArchitecture")
if not arch or arch == "all":
arch = self.get("Architecture")
sandbox_library_paths = [
f"{sandbox}{path}"
for path in packaging.get_library_paths(arch).split(":")
]
# N.B. set solib-absolute-prefix is an alias for set sysroot
command += [
"--ex",
Expand All @@ -2027,7 +2015,8 @@ def gdb_command(
]
if gdb_sandbox:
gdb_sandbox_library_paths = [
f"{gdb_sandbox}{path}" for path in host_library_paths
f"{gdb_sandbox}{path}"
for path in packaging.get_library_paths().split(":")
]
ld_lib_path = ":".join(gdb_sandbox_library_paths)
pyhome = f"{gdb_sandbox}/usr"
Expand Down
1 change: 0 additions & 1 deletion tests/integration/test_packaging_apt_dpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,6 @@ def test_get_system_architecture(self) -> None:
self.assertNotEqual(arch, "")
self.assertNotIn("\n", arch)

@skip_if_command_is_missing("dpkg-architecture")
def test_get_library_paths(self) -> None:
"""get_library_paths()."""
paths = impl.get_library_paths()
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/test_packaging_apt_dpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ def test_is_distro_package_system_image(
getitem_mock.assert_called_once_with("apport")
exists_mock.assert_called_once_with("/etc/system-image/channel.ini")

def test_get_library_paths_unknown_arch(self) -> None:
"""get_library_paths() for unknown architecture."""
with self.assertRaisesRegex(NotImplementedError, "architecture osprey64"):
impl.get_library_paths("osprey64")

def test_map_mirror_to_arch_ports_to_primary(self) -> None:
"""Test _map_mirror_to_arch() to map ports to primary."""
self.assertEqual(
Expand Down
46 changes: 0 additions & 46 deletions tests/unit/test_report.py
Original file line number Diff line number Diff line change
Expand Up @@ -1644,49 +1644,3 @@ 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()
Loading