Skip to content

Commit d866160

Browse files
committed
bootstrap.py: guard against GC in NixOS patching support.
1 parent c2dbebd commit d866160

File tree

1 file changed

+42
-31
lines changed

1 file changed

+42
-31
lines changed

src/bootstrap/bootstrap.py

+42-31
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ def __init__(self):
349349
self.use_vendored_sources = ''
350350
self.verbose = False
351351
self.git_version = None
352+
self.nix_deps_dir = None
352353

353354
def download_stage0(self):
354355
"""Fetch the build system for Rust, written in Rust
@@ -440,8 +441,7 @@ def _download_stage0_helper(self, filename, pattern, tarball_suffix, date=None):
440441
get("{}/{}".format(url, filename), tarball, verbose=self.verbose)
441442
unpack(tarball, tarball_suffix, self.bin_root(), match=pattern, verbose=self.verbose)
442443

443-
@staticmethod
444-
def fix_executable(fname):
444+
def fix_executable(self, fname):
445445
"""Modifies the interpreter section of 'fname' to fix the dynamic linker
446446
447447
This method is only required on NixOS and uses the PatchELF utility to
@@ -472,38 +472,49 @@ def fix_executable(fname):
472472
nix_os_msg = "info: you seem to be running NixOS. Attempting to patch"
473473
print(nix_os_msg, fname)
474474

475-
try:
476-
interpreter = subprocess.check_output(
477-
["patchelf", "--print-interpreter", fname])
478-
interpreter = interpreter.strip().decode(default_encoding)
479-
except subprocess.CalledProcessError as reason:
480-
print("warning: failed to call patchelf:", reason)
481-
return
482-
483-
loader = interpreter.split("/")[-1]
484-
485-
try:
486-
ldd_output = subprocess.check_output(
487-
['ldd', '/run/current-system/sw/bin/sh'])
488-
ldd_output = ldd_output.strip().decode(default_encoding)
489-
except subprocess.CalledProcessError as reason:
490-
print("warning: unable to call ldd:", reason)
491-
return
492-
493-
for line in ldd_output.splitlines():
494-
libname = line.split()[0]
495-
if libname.endswith(loader):
496-
loader_path = libname[:len(libname) - len(loader)]
497-
break
498-
else:
499-
print("warning: unable to find the path to the dynamic linker")
500-
return
501-
502-
correct_interpreter = loader_path + loader
475+
# Only build `stage0/.nix-deps` once.
476+
nix_deps_dir = self.nix_deps_dir
477+
if not nix_deps_dir:
478+
nix_deps_dir = "{}/.nix-deps".format(self.bin_root())
479+
if not os.path.exists(nix_deps_dir):
480+
os.makedirs(nix_deps_dir)
481+
482+
nix_deps = [
483+
# Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
484+
"stdenv.cc.bintools",
485+
486+
# Needed for patching ELF binaries (see doc comment above).
487+
"patchelf",
488+
]
489+
490+
# Run `nix-build` to "build" each dependency (which will likely reuse
491+
# the existing `/nix/store` copy, or at most download a pre-built copy).
492+
# Importantly, we don't rely on `nix-build` printing the `/nix/store`
493+
# path on stdout, but use `-o` to symlink it into `stage0/.nix-deps/$dep`,
494+
# ensuring garbage collection will never remove the `/nix/store` path
495+
# (which would break our patched binaries that hardcode those paths).
496+
for dep in nix_deps:
497+
try:
498+
subprocess.check_output([
499+
"nix-build", "<nixpkgs>",
500+
"-A", dep,
501+
"-o", "{}/{}".format(nix_deps_dir, dep),
502+
])
503+
except subprocess.CalledProcessError as reason:
504+
print("warning: failed to call nix-build:", reason)
505+
return
506+
507+
self.nix_deps_dir = nix_deps_dir
508+
509+
patchelf = "{}/patchelf/bin/patchelf".format(nix_deps_dir)
510+
bintools_dir = "{}/stdenv.cc.bintools".format(nix_deps_dir)
511+
512+
with open("{}/nix-support/dynamic-linker".format(bintools_dir)) as dynamic_linker:
513+
interpreter = dynamic_linker.read().rstrip()
503514

504515
try:
505516
subprocess.check_output(
506-
["patchelf", "--set-interpreter", correct_interpreter, fname])
517+
[patchelf, "--set-interpreter", interpreter, fname])
507518
except subprocess.CalledProcessError as reason:
508519
print("warning: failed to call patchelf:", reason)
509520
return

0 commit comments

Comments
 (0)