From d4a472c72943396b347436aee5ec73f61c7455e3 Mon Sep 17 00:00:00 2001 From: David Li Date: Wed, 12 Aug 2026 14:43:53 +0900 Subject: [PATCH 1/4] feat: check for unexpected dependencies on Linux Assisted-by: GPT-5.6 Sol --- adbc_drivers_dev/make_checks.py | 79 +++++++++++++++++++++++++++++++++ adbc_drivers_dev/make_config.py | 5 +++ tests/test_make_checks.py | 53 ++++++++++++++++++++++ 3 files changed, 137 insertions(+) diff --git a/adbc_drivers_dev/make_checks.py b/adbc_drivers_dev/make_checks.py index 96d0b14..7c3495b 100644 --- a/adbc_drivers_dev/make_checks.py +++ b/adbc_drivers_dev/make_checks.py @@ -22,6 +22,18 @@ from .make_config import MakeConfig, MakeEnv +_LINUX_RUNTIME_DEPENDENCIES = { + "linux-vdso.so.1", + "libgcc_s.so.1", + "libm.so.6", + "libpthread.so.0", + "libc.so.6", +} +_LINUX_LOADERS = { + "amd64": "/lib64/ld-linux-x86-64.so.2", + "arm64": "/lib/ld-linux-aarch64.so.1", +} + def _read_linux_symbols(binary: Path) -> list[str]: return ( @@ -33,6 +45,10 @@ def _read_linux_symbols(binary: Path) -> list[str]: ) +def _read_linux_dependencies(binary: Path) -> list[str]: + return subprocess.check_output(["ldd", str(binary)], text=True).splitlines() + + def _read_macos_symbols(binary: Path) -> list[str]: return ( subprocess.check_output(["nm", "-gU", str(binary)], text=True) @@ -83,6 +99,61 @@ def _read_linux_symbols_in_docker( ) +def _read_linux_dependencies_in_docker( + make_env: MakeEnv, make_config: MakeConfig, binary: Path +) -> list[str]: + rel_binary = binary.resolve().relative_to(make_env.repo_root.resolve()) + env = { + **os.environ, + "SOURCE_ROOT": str(make_env.repo_root), + "DOCKER_DEFAULT_PLATFORM": ( + f"{make_env.target_platform}/{make_env.target_architecture}" + ), + "MANYLINUX": make_config.manylinux, + } + return subprocess.check_output( + [ + "docker", + "compose", + "run", + "--rm", + "manylinux", + "ldd", + f"/source/{rel_binary.as_posix()}", + ], + cwd=Path(__file__).parent, + env=env, + text=True, + ).splitlines() + + +def _extract_linux_dependencies(output: list[str]) -> set[str]: + dependencies = set() + for raw_line in output: + line = raw_line.strip() + if not line: + continue + parts = line.split() + dependencies.add(parts[0]) + return dependencies + + +def check_linux_runtime_dependencies( + output: list[str], binary: Path, architecture: str, additional: list[str] +) -> None: + dependencies = _extract_linux_dependencies(output) + allowed = _LINUX_RUNTIME_DEPENDENCIES | set(additional) + try: + allowed.add(_LINUX_LOADERS[architecture]) + except KeyError as err: + raise ValueError(f"Unsupported Linux architecture: {architecture}") from err + + unexpected = {name for name in dependencies if name not in allowed} + if unexpected: + details = ", ".join(sorted(unexpected)) + raise RuntimeError(f"{binary} has unexpected runtime dependencies: {details}") + + def _extract_exported_linux_symbols(symbols: list[str]) -> list[str]: exported_symbols = [] for symbol in symbols: @@ -178,8 +249,10 @@ def check_linux_libc_requirement(symbols: list[str], manylinux: str) -> None: def _check_linux(make_env: MakeEnv, make_config: MakeConfig, binary: Path) -> None: if make_env.host_platform == "linux": symbols = _read_linux_symbols(binary) + dependencies = _read_linux_dependencies(binary) elif make_env.use_docker: symbols = _read_linux_symbols_in_docker(make_env, make_config, binary) + dependencies = _read_linux_dependencies_in_docker(make_env, make_config, binary) else: raise RuntimeError( "Cannot run Linux compatibility checks on non-Linux host without Docker" @@ -188,6 +261,12 @@ def _check_linux(make_env: MakeEnv, make_config: MakeConfig, binary: Path) -> No check_required_symbols(exported_symbols, binary, make_config.driver) check_disallowed_symbols(exported_symbols, binary, make_config.driver) check_linux_libc_requirement(symbols, make_config.manylinux) + check_linux_runtime_dependencies( + dependencies, + binary, + make_env.target_architecture, + make_config.additional_runtime_dependencies.get("linux", []), + ) def _check_macos_deployment_target(binary: Path) -> None: diff --git a/adbc_drivers_dev/make_config.py b/adbc_drivers_dev/make_config.py index 122a64a..56102dc 100644 --- a/adbc_drivers_dev/make_config.py +++ b/adbc_drivers_dev/make_config.py @@ -350,6 +350,11 @@ class MakeConfig(BaseModel): alias="additional-volumes", description="Additional Docker volume mounts, in HOST:CONTAINER format", ) + additional_runtime_dependencies: dict[typing.Literal["linux"], list[str]] = Field( + default_factory=dict, + alias="additional-runtime-dependencies", + description="Additional runtime dependencies allowed by platform", + ) def build_plan(self, config: MakeEnv) -> MakePlan: env_vars = default_build_env(config) diff --git a/tests/test_make_checks.py b/tests/test_make_checks.py index e79803d..86d369f 100644 --- a/tests/test_make_checks.py +++ b/tests/test_make_checks.py @@ -83,6 +83,59 @@ def test_extract_exported_linux_symbols() -> None: assert exported_symbols == ["AdbcDriverInit", "AdbcDriverDriverInit"] +def test_extract_linux_dependencies() -> None: + dependencies = make_checks._extract_linux_dependencies( + [ + "\tlinux-vdso.so.1 (0x00007fff)", + "\tlibm.so.6 => /lib64/libm.so.6 (0x00007fff)", + "\tlibmissing.so.1 => not found", + "\t/lib64/ld-linux-x86-64.so.2 (0x00007fff)", + ] + ) + assert dependencies == { + "linux-vdso.so.1", + "libm.so.6", + "libmissing.so.1", + "/lib64/ld-linux-x86-64.so.2", + } + + +def test_check_linux_runtime_dependencies() -> None: + output = [ + "linux-vdso.so.1 (0x00007fff)", + "libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007fff)", + "libm.so.6 => /lib64/libm.so.6 (0x00007fff)", + "libpthread.so.0 => /lib64/libpthread.so.0 (0x00007fff)", + "libc.so.6 => /lib64/libc.so.6 (0x00007fff)", + "/lib64/ld-linux-x86-64.so.2 (0x00007fff)", + ] + make_checks.check_linux_runtime_dependencies(output, Path("driver.so"), "amd64", []) + + output.append("libfoobar.so => /opt/libfoobar.so (0x00007fff)") + make_checks.check_linux_runtime_dependencies( + output, Path("driver.so"), "amd64", ["libfoobar.so"] + ) + make_checks.check_linux_runtime_dependencies( + ["/lib/ld-linux-aarch64.so.1 (0x00007fff)"], + Path("driver.so"), + "arm64", + [], + ) + + with pytest.raises(RuntimeError, match="libfoobar.so"): + make_checks.check_linux_runtime_dependencies( + output, Path("driver.so"), "amd64", [] + ) + + with pytest.raises(RuntimeError, match="libmissing.so.1"): + make_checks.check_linux_runtime_dependencies( + ["libmissing.so.1 => not found"], + Path("driver.so"), + "amd64", + [], + ) + + def test_extract_exported_macos_symbols() -> None: symbols = [ "000000 T _AdbcDriverMultiwordnameInit", From 4d5ee90b9821269eb35a81bbb175493ae8e28391 Mon Sep 17 00:00:00 2001 From: David Li Date: Thu, 13 Aug 2026 09:45:22 +0900 Subject: [PATCH 2/4] add more libraries --- adbc_drivers_dev/make_checks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/adbc_drivers_dev/make_checks.py b/adbc_drivers_dev/make_checks.py index 7c3495b..64a4703 100644 --- a/adbc_drivers_dev/make_checks.py +++ b/adbc_drivers_dev/make_checks.py @@ -23,11 +23,12 @@ from .make_config import MakeConfig, MakeEnv _LINUX_RUNTIME_DEPENDENCIES = { - "linux-vdso.so.1", + "libc.so.6", "libgcc_s.so.1", "libm.so.6", "libpthread.so.0", - "libc.so.6", + "libresolv.so.2", + "linux-vdso.so.1", } _LINUX_LOADERS = { "amd64": "/lib64/ld-linux-x86-64.so.2", From 94217dc7d4202419b10104a5766887d922940582 Mon Sep 17 00:00:00 2001 From: David Li Date: Thu, 13 Aug 2026 09:50:14 +0900 Subject: [PATCH 3/4] add libdl --- adbc_drivers_dev/make_checks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/adbc_drivers_dev/make_checks.py b/adbc_drivers_dev/make_checks.py index 64a4703..75f768a 100644 --- a/adbc_drivers_dev/make_checks.py +++ b/adbc_drivers_dev/make_checks.py @@ -24,6 +24,7 @@ _LINUX_RUNTIME_DEPENDENCIES = { "libc.so.6", + "libdl.so.2", "libgcc_s.so.1", "libm.so.6", "libpthread.so.0", From 400b3c724e1c2bbc8aaf36f0666cac2e196521ea Mon Sep 17 00:00:00 2001 From: David Li Date: Thu, 13 Aug 2026 10:03:56 +0900 Subject: [PATCH 4/4] add librt --- adbc_drivers_dev/make_checks.py | 1 + 1 file changed, 1 insertion(+) diff --git a/adbc_drivers_dev/make_checks.py b/adbc_drivers_dev/make_checks.py index 75f768a..c6d81c3 100644 --- a/adbc_drivers_dev/make_checks.py +++ b/adbc_drivers_dev/make_checks.py @@ -29,6 +29,7 @@ "libm.so.6", "libpthread.so.0", "libresolv.so.2", + "librt.so.1", "linux-vdso.so.1", } _LINUX_LOADERS = {