mirror of
https://github.com/systemd/systemd.git
synced 2026-11-06 05:17:24 +00:00
Programs written in Rust are declared just like the C ones, in the same executables list. The crate root comes first and that's how the loop tells them apart. 'conditions' and 'type' work the same way. Names starting with 'test-' become unit tests and are installed next to the C ones with -Dinstall-tests. Installed programs take part in the --help and --version checks of the dist suite and go into the mkosi images like the C ones. There are no .standalone variants as Rust programs always link libsystemd-shared dynamically. They take part in test-link-abi like every other program. Nothing from the crates is exported and the programs link with --exclude-libs ALL. The linker exports a symbol of the program when a DSO in the link defines it too and a stub of the crates would then preempt the real one for every library the program loads later. meson keeps rustc's own library directory in the runpath of Rust programs that link a shared library. It's only needed for Rust dylibs which we don't use. meson can't grow a runpath at install time so with a long pkglibdir such as Debian's multiarch one the install fails. So give the build-time runpath a padding entry of the right length so the install always succeeds and drop the leftover entry afterwards with tools/fix-rust-rpath.py which rewrites the runpath string in place. Both become no-ops once meson stops adding the directory. The fix is proposed upstream (cf. [1]). A rustfmt check over every program source runs in the dist suite and ninja clippy covers everything. [1]: https://github.com/mesonbuild/meson/pull/16287 Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
102 lines
3.2 KiB
Python
102 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
"""Drop rustc's own library directory from the runpath of installed programs written in Rust.
|
|
|
|
meson adds rustc's own library directory to the runpath of every Rust program that links a shared
|
|
library (a rustup toolchain needs that to find a dynamically linked libstd) and keeps it when installing.
|
|
Our programs do not link libstd at all. The given directory, the one libsystemd-shared lives in, comes
|
|
first, other entries stay, like the runtime directory of clang's sanitizers that the C programs get too.
|
|
meson 1.12.2 will have the fix (https://github.com/mesonbuild/meson/pull/16287), this is a no-op then.
|
|
TODO: drop once the meson baseline is >= 1.12.2.
|
|
|
|
Usage: fix-rust-rpath.py RPATH FILE...
|
|
|
|
FILE is the installed path, $DESTDIR is prepended.
|
|
"""
|
|
|
|
import os
|
|
import struct
|
|
import sys
|
|
|
|
DT_NULL = 0
|
|
DT_RPATH = 15
|
|
DT_RUNPATH = 29
|
|
SHT_DYNAMIC = 6
|
|
|
|
|
|
def read_cstring(f, offset: int) -> bytes:
|
|
f.seek(offset)
|
|
s = b''
|
|
while True:
|
|
c = f.read(1)
|
|
if c in (b'', b'\0'):
|
|
return s
|
|
s += c
|
|
|
|
|
|
def fix_rpath(path: str, rpath: bytes) -> None:
|
|
with open(path, 'r+b') as f:
|
|
ident = f.read(16)
|
|
if ident[:4] != b'\x7fELF':
|
|
sys.exit(f'{path}: not an ELF file')
|
|
|
|
is64 = ident[4] == 2
|
|
end = '<' if ident[5] == 1 else '>'
|
|
|
|
f.seek(0)
|
|
if is64:
|
|
ehdr = struct.unpack(end + '16sHHIQQQIHHHHHH', f.read(64))
|
|
else:
|
|
ehdr = struct.unpack(end + '16sHHIIIIIHHHHHH', f.read(52))
|
|
shoff, shentsize, shnum = ehdr[6], ehdr[11], ehdr[12]
|
|
|
|
sections = []
|
|
for i in range(shnum):
|
|
f.seek(shoff + i * shentsize)
|
|
if is64:
|
|
sections.append(struct.unpack(end + 'IIQQQQIIQQ', f.read(64)))
|
|
else:
|
|
sections.append(struct.unpack(end + 'IIIIIIIIII', f.read(40)))
|
|
|
|
dynamic = next((s for s in sections if s[1] == SHT_DYNAMIC), None)
|
|
if dynamic is None:
|
|
return
|
|
|
|
dynstr = sections[dynamic[6]]
|
|
entsize = 16 if is64 else 8
|
|
entfmt = end + ('qQ' if is64 else 'iI')
|
|
|
|
for off in range(dynamic[4], dynamic[4] + dynamic[5], entsize):
|
|
f.seek(off)
|
|
tag, val = struct.unpack(entfmt, f.read(entsize))
|
|
if tag == DT_NULL:
|
|
break
|
|
if tag not in (DT_RPATH, DT_RUNPATH):
|
|
continue
|
|
|
|
str_off = dynstr[4] + val
|
|
old = read_cstring(f, str_off)
|
|
keep = [e for e in old.split(b':') if e and e != rpath and b'/lib/rustlib/' not in e]
|
|
new = b':'.join([rpath] + keep)
|
|
if old == new:
|
|
continue
|
|
if len(new) > len(old):
|
|
sys.exit(f'{path}: runpath {new!r} does not fit into {old!r}')
|
|
|
|
# Overwrite in place, the string table cannot grow.
|
|
f.seek(str_off)
|
|
f.write(new + b'\0' * (len(old) - len(new) + 1))
|
|
print(f'Setting runpath of {path} to {new.decode()}')
|
|
|
|
|
|
def main() -> None:
|
|
rpath = sys.argv[1].encode()
|
|
destdir = os.environ.get('DESTDIR', '')
|
|
|
|
for path in sys.argv[2:]:
|
|
fix_rpath(destdir + path, rpath)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|