Use my fancy new rust tool for checking DLL compatibility
Some checks failed
Build / build (push) Has been cancelled

This commit is contained in:
Will Toohey
2026-04-05 15:12:56 +10:00
parent 9f1a8f4a20
commit 6cef1aaa3b
8 changed files with 37 additions and 140 deletions

View File

@@ -12,6 +12,14 @@ RUN set -eux; \
# container run
RUN wine wineboot --init
RUN set -eux; \
curl -fsSL "https://api.github.com/repos/mon/windows-dll-compat-checker/releases/latest" \
| jq -r '.assets[] | select(.name | contains("linux-x86_64")) | .browser_download_url' \
| xargs curl -fsSL -o /tmp/dll_compat_checker.zip; \
unzip /tmp/dll_compat_checker.zip -d /usr/local/bin/; \
chmod +x /usr/local/bin/windows_dll_compat_checker; \
rm /tmp/dll_compat_checker.zip;
ENV CROSS_32=/opt/llvm-mingw/toolchain-files/meson/i686-mingw32-clang.ini
ENV CROSS_64=/opt/llvm-mingw/toolchain-files/meson/x86_64-mingw32-clang.ini

View File

@@ -1,122 +0,0 @@
import argparse
import functools
import os
from collections.abc import Callable
from glob import glob
from typing import TypeAlias
import pefile
from requests.utils import CaseInsensitiveDict
parser = argparse.ArgumentParser()
parser.add_argument("dll_path", nargs="+")
args = parser.parse_args()
Imports: TypeAlias = dict[str, list[tuple[bytes | None, int | None]]]
Exports: TypeAlias = list[tuple[bytes | None, int | None]]
class MissingDLL(Exception):
pass
class MissingFunction(Exception):
pass
def load_imports(paths: list[str]) -> CaseInsensitiveDict[Imports]:
dlls = CaseInsensitiveDict()
for path in paths:
print(f"Loading imports from {path}")
pe = pefile.PE(path)
imports: Imports = {}
total = 0
for entry in pe.DIRECTORY_ENTRY_IMPORT:
these = []
for imp in entry.imports:
these.append((imp.name, imp.ordinal))
imports[entry.dll.decode()] = these
total += len(these)
print(f" ...{total} imports from {len(imports)} DLLs")
dlls[os.path.basename(path)] = imports
return dlls
def load_exports(path: str) -> Callable[[], Exports]:
@functools.cache
def load():
print(f"Loading exports from {path}...")
pe = pefile.PE(path)
exports: Exports = []
for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:
exports.append((exp.name, exp.ordinal))
print(f" ...{len(exports)} exports")
return exports
return load
available_functions: CaseInsensitiveDict[Callable[[], Exports]] = CaseInsensitiveDict()
for dll in glob("./xp_dlls/*.dll"):
available_functions[os.path.basename(dll)] = load_exports(dll)
loaded_dlls = load_imports(args.dll_path)
# https://www.geoffchappell.com/studies/windows/win32/kernel32/api/index.htm
# my test .dll is 32-bit and these are only in 64
ignore_functions = [
b"RtlLookupFunctionEntry",
b"RtlUnwindEx",
b"RtlVirtualUnwind",
b"RtlRestoreContext",
b"__C_specific_handler",
]
exceptions = []
for path, needed_functions in loaded_dlls.items():
for dll_name, imports in needed_functions.items():
if (dll := available_functions.get(dll_name)) is None:
if dll_name not in loaded_dlls:
exceptions.append(
MissingDLL(
f"{path}: Need {dll_name} but it's not in the XP DLLs folder"
)
)
continue
dll = dll()
for name, ordinal in imports:
if name is not None:
if name in ignore_functions:
continue
if next((fn for fn in dll if fn[0] == name), None) is None:
exceptions.append(
MissingFunction(
f'{path}: Function "{name}" not present in XP {dll_name}'
)
)
continue
elif ordinal is not None:
if next((fn for fn in dll if fn[1] == ordinal), None) is None:
exceptions.append(
MissingFunction(
f"{path}: Function with ordinal {ordinal} not present in XP {dll_name}"
)
)
continue
else:
raise RuntimeError(f"{path}: Import with no name or ordinal???")
if exceptions:
raise ExceptionGroup("Failures", exceptions)
print("All functions present!")

View File

@@ -111,7 +111,12 @@ if host_machine.cpu_family() == 'x86'
)
endif
python = import('python').find_installation()
windows_dll_compat_checker = find_program('windows_dll_compat_checker', required: false)
if host_machine.cpu_family() == 'x86_64'
xp_check_ini = 'PREMADE/winxp_x86_64.ini'
else
xp_check_ini = 'PREMADE/winxp_x86_64_32bit_dlls.ini'
endif
foreach cfg : special_cfgs
folder_name = cfg[0]
@@ -139,14 +144,16 @@ foreach cfg : special_cfgs
install_dir: install_dir,
install: true,
)
test(lib_name + '_is_xp',
python,
args: [
files('ensure_xp_compatible.py'),
ifs_dll,
],
workdir: meson.current_source_dir(),
)
if windows_dll_compat_checker.found()
test(lib_name + '_is_xp',
windows_dll_compat_checker,
args: [
ifs_dll,
'-s', xp_check_ini,
],
workdir: meson.current_source_dir(),
)
endif
endforeach
injector_cfgs = [
@@ -183,14 +190,16 @@ foreach cfg : injector_cfgs
install: true,
)
test(dll_name + '_is_xp',
python,
args: [
files('ensure_xp_compatible.py'),
inject_dll,
],
workdir: meson.current_source_dir(),
)
if windows_dll_compat_checker.found()
test(dll_name + '_is_xp',
windows_dll_compat_checker,
args: [
inject_dll,
'-s', xp_check_ini,
],
workdir: meson.current_source_dir(),
)
endif
endforeach
if host_machine.cpu_family() == 'x86_64'

View File

@@ -1,3 +1,6 @@
#!/usr/bin/env bash
set -eu
meson test -C build32 --print-errorlogs "$@"
meson test -C build64 --print-errorlogs "$@"

View File

@@ -1 +0,0 @@
Taken from my Jubeat cab's XP image

Binary file not shown.

Binary file not shown.

Binary file not shown.