fix(runfiles): resolve real exe path when argv[0] is relative (fixes bazel run)#44
Closed
gregmagolan wants to merge 2 commits into
Closed
Conversation
…ILES_DIR) Add a regression test that invokes the launcher via a RELATIVE argv[0] from a different working directory with no RUNFILES_DIR / RUNFILES_MANIFEST_FILE — the `bazel run` / deployed-binary scenario. The launcher must discover `<argv[0]>.runfiles` and resolve an absolute program path for execve, so the exec succeeds regardless of the caller's cwd. The existing `fallback_runfiles_dir` test only invokes the stub via an absolute argv[0], which doesn't exercise this path. Downstream this surfaced as "execve failed with errno 2" when running an aspect_rules_py py_binary via `bazel run` (aspect-build/rules_py#1113) with hermetic_launcher 0.0.9; it passes on current main, so this locks in the behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
That's on me. Let me fix the rbe credential handling for forks. |
…] is relative When the launcher is exec'd with a relative argv[0] from a cwd unrelated to the binary (notably `bazel run`, which sets cwd inside `<x>.runfiles/_main`, passes argv[0]="bazel-bin/<name>", and does NOT set RUNFILES_DIR), `<argv[0]>.runfiles` resolved against cwd points nowhere. Discovery then fails, the embedded program rlocation is left unresolved, and execve aborts with errno 2 — observed downstream as aspect-build/rules_py#1113 ("execve failed with errno 2") when running a py_binary via `bazel run`. Fix: when argv[0] is not absolute, resolve the real executable path and use it for `<exe>.runfiles` discovery: - macOS: _NSGetExecutablePath - Linux (x86_64/aarch64): readlinkat(/proc/self/exe) - other (s390x/Windows): unchanged (return None -> argv[0] fallback) RUNFILES_DIR / RUNFILES_MANIFEST_FILE still take precedence when set. Adds an integration case `relative_argv0_discovery` reproducing `bazel run`'s geometry exactly (relative argv[0] via arg0 override + cwd inside runfiles + no runfiles env), plus `symlinked_program_relative_argv0` for the venv-shaped relative-symlink program target. Both fail before this change and pass after. NB: the Linux readlinkat paths could not be run locally (macOS host); the macOS path is verified end-to-end. s390x readlinkat is intentionally left unwired. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
|
@gregmagolan please rebase onto the latest HEAD of main (ad4500d). It includes #46. |
Collaborator
|
Superseded by #48 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A launcher binary aborts with
ERROR: execve failed with errno 2when exec'd with a relativeargv[0]from a working directory unrelated to the binary, with noRUNFILES_DIRset. This is exactly whatbazel rundoes: it sets cwd to<x>.runfiles/_main, passesargv[0] = "bazel-bin/<name>", and leavesRUNFILES_DIRunset.In that case the launcher computes
<argv[0]>.runfilesrelative to cwd →<x>.runfiles/_main/bazel-bin/<name>.runfiles, which doesn't exist → runfiles discovery returnsNone→ the embedded program rlocation is left unresolved →execveof a bad path → errno 2.Reported downstream as aspect-build/rules_py#1113: an
aspect_rules_pypy_binaryfails underbazel run. (Confirmed it reproduces on this repo'smainwith the new test, and that pinning rules_py to the latest released launcher did not fix it — the bug is in the launcher, not the version.)Fix
When
argv[0]is not absolute, resolve the real executable path and use it for<exe>.runfilesdiscovery instead of joining a relativeargv[0]against cwd:_NSGetExecutablePathreadlinkat(/proc/self/exe)(addsSYS_READLINKAT+ asyscall4macro for x86_64)None, falling back to the existingargv[0]behavior (Windows derives argv[0] from the command line viaCreateProcessW, so it doesn't hit this path; s390xreadlinkatis left unwired)RUNFILES_DIR/RUNFILES_MANIFEST_FILEstill take precedence when set.Tests
Adds two integration cases:
relative_argv0_discovery— reproducesbazel run's geometry exactly (relativeargv[0]via anarg0override, cwd inside the runfiles tree, no runfiles env vars).symlinked_program_relative_argv0— same, with the embedded program reached through a relative symlink (the venv shape rules_py uses).Both fail before this change and pass after.
Test plan
bazel test //integration-tests:integration_test— 11/11 pass on macOS with the fix;relative_argv0_discoveryfails without it.readlinkatpaths were not run locally (macOS host) — please confirm on CI. The macOS path is verified end-to-end.🤖 Generated with Claude Code