https://github.com/rrbutani/rtld-shim
https://github.com/rrbutani/rtld-shim
bazel bazelcon25 hermetic-builds rtld
Last synced: 26 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/rrbutani/rtld-shim
- Owner: rrbutani
- License: mit
- Created: 2025-11-05T02:02:10.000Z (8 months ago)
- Default Branch: main
- Last Pushed: 2025-11-11T16:08:16.000Z (8 months ago)
- Last Synced: 2025-11-22T08:02:36.901Z (8 months ago)
- Topics: bazel, bazelcon25, hermetic-builds, rtld
- Language: Rust
- Homepage: https://youtu.be/RCcE5sxOn2o
- Size: 186 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Runtime Loader Shim
## what
## why
## how
#### normal execution
```mermaid
flowchart TB
exec["`exec path/to/binary`"]
bin_load@{ shape: procs, label: "mmap from binary"}
interp@{ shape: hex, label: "has
PT_INTERP?" }
interp_load@{ shape: procs, label: "mmap from ld.so"}
bin_entry@{ shape: stadium, label: "➡️ binary's entrypoint" }
interp_entry@{ shape: stadium, label: "➡️ ld.so's entrypoint" }
interp_run@{ shape: procs, label: "resolve dylibs
apply relocations" }
%% edges:
exec --> bin_load --> interp
interp -- "`no`" --> bin_entry
interp -- "`yes
/lib64/ld-linux-x86-64.so.2`" --> interp_load
interp_load --> interp_entry --> interp_run --> bin_entry
style interp fill:#444
```
#### explicit `ld.so` invocation
```mermaid
flowchart TB
exec["`exec ld.so path/to/binary`"]
bin_load@{ shape: procs, label: "mmap from binary"}
interp_load@{ shape: procs, label: "mmap from ld.so"}
bin_entry@{ shape: stadium, label: "➡️ binary's entrypoint" }
interp_entry@{ shape: stadium, label: "➡️ ld.so's entrypoint" }
interp_run@{ shape: procs, label: "resolve dylibs
apply relocations" }
%% edges:
exec --> interp_load --> interp_entry --> bin_load --> interp_run --> bin_entry
```
#### with loader shim
```mermaid
flowchart TB
exec["exec path/to/binary
(patched w/loader shim)"]
bin_load@{ shape: procs, label: "mmap from binary"}
interp@{ shape: hex, label: "has
PT_INTERP?" }
bin_entry@{ shape: stadium, label: "➡️ binary's entrypoint
(loader shim)" }
under_rtld@{ shape: hex, label: "under ld.so?" }
%% edges:
exec --> bin_load --> interp
interp -- "`no`" --> bin_entry --> under_rtld
under_rtld -- "no" --> runfiles_lookup --> rtld_exec
under_rtld -- "yes" --> rtld_bin_entry
style interp fill:#444
style under_rtld fill:#444
subgraph rtld_exec_sub ["."]
direction TB
rtld_exec["`exec ld.so path/to/binary`"]
rtld_bin_load@{ shape: procs, label: "mmap from binary"}
rtld_load@{ shape: procs, label: "mmap from ld.so"}
rtld_bin_entry@{ shape: stadium, label: "➡️ binary's entrypoint
(original)" }
rtld_entry@{ shape: stadium, label: "➡️ ld.so's entrypoint" }
rtld_run@{ shape: procs, label: "resolve dylibs
apply relocations" }
runfiles_lookup@{ shape: event, label: "runfiles lookup
for ld.so" }
under_rtld@{ shape: hex, label: "under ld.so?" }
%% edges:
rtld_exec --> rtld_load --> rtld_entry --> rtld_bin_load --> rtld_run --> bin_entry
end
```
## demo?
> [!CAUTION]
> Hacky, coupled to Bazel 8, x86-64 only for now.
> [!IMPORTANT]
> As of this writing, Bazel's experimental hermetic linux sandbox does not set up procfs appropriately; this causes `ld.so` to fail to canonicalize program paths which then causes shared object resolution (i.e. for `DT_NEEDED` entries with `$ORIGIN` and such) to fail.
1. Set up an RBE service of your choosing to use an [empty container image](bzl/Dockerfile) for action execution
- the [bundled example workspace's `--config=rbe`](bzl/.bazelrc) is set up for [buildbuddy](https://www.buildbuddy.io/) w/the [`rrbutani/empty`](https://hub.docker.com/repository/docker/rrbutani/empty/general) container image
+ be sure to create an account, get an API key, and place it in `bzl/.buildbuddy-api-key.bazelrc`
2. Run `bazel build //:out --config=rbe` in [`bzl`](./bzl/)
- if you're using [`nix`](flake.nix) + [`direnv`](bzl/.envrc), Bazel 8.4.2 will be fetched for you; if not you'll need to [grab it yourself](https://github.com/bazelbuild/bazelisk)