Run and Verify a Relocatable Object with llvm-rtdyld 20
You will compile a small self-contained C function, load it with llvm-rtdyld-20, pass one argument to it, and verify the resulting memory image without running the function. Allow about fifteen minutes. You need the installed llvm-20 package and, for this example, clang-20. The installed tool here is LLVM 20.1.8, from package version 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 1. Check the installed interface
- 2. Build a self-contained object
- 3. Execute an explicitly named entry point
- 4. Verify without executing
- 5. Inspect functions without loading them first
- 6. Measure phases when a test is slow
- 7. Diagnose the common unresolved-symbol failure
- 8. Remove the temporary files when finished
Checkpoint
This tool loads object files for RuntimeDyld tests. It is not a general replacement for the system linker or a way to run an ordinary executable. The examples write only under a temporary directory and need no elevated privileges.
1. Check the installed interface
Start with read-only version and help queries. They establish which spelling of each option your installed build accepts:
$ llvm-rtdyld-20 --version
Ubuntu LLVM version 20.1.8
Optimized build.
$ llvm-rtdyld-20 --help
The useful shape is llvm-rtdyld-20 [options] <input files> --args <program arguments>.... Actions such as --execute, --verify and --printobjline are separate modes. Put --args after the input file so the boundary is obvious.
2. Build a self-contained object
Make a disposable working directory and compile a function with no library calls. That avoids an unresolved reference to a symbol such as printf while you learn the loader's basic workflow:
$ work=$(mktemp -d /tmp/llvm-rtdyld.XXXXXX)
$ printf '%s\n' 'int main(int argc, char **argv) { return argc == 2 ? 0 : 7; }' > "$work/sample.c"
$ clang-20 -c -O0 -o "$work/sample.o" "$work/sample.c"
$ file "$work/sample.o"
/tmp/llvm-rtdyld.XXXXXX/sample.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
The exact temporary directory name varies. The important part of the file result is that the input is a relocatable object, not an already linked executable. Keep the object and source together until the test is complete so you can reproduce a failure.
3. Execute an explicitly named entry point
Run the object and pass one argument after --args:
$ llvm-rtdyld-20 --execute --entry=main "$work/sample.o" --args demo
loaded 'main' at: 0x7be361b5d000
$ printf 'exit status: %s\n' "$?"
exit status: 0
The address is expected to vary. The explicit --entry=main matters on this Linux build: without it, the tool may look for the platform-specific entry symbol _main and fail even though the object contains main. The test function returns zero only when it receives exactly one argument, so a zero status checks both entry selection and argument passing.
Execution is still arbitrary code execution. Do not feed this command an object from an untrusted source. Loading an object can execute code, allocate memory and resolve references. No service restart or system-wide configuration change is part of this example.
4. Verify without executing
Use --verify when you want RuntimeDyld to load, link and verify the memory image but not call the entry point. This mode needs a target triple:
$ llvm-rtdyld-20 --verify --triple=x86_64 "$work/sample.o"
$ printf 'verify status: %s\n' "$?"
verify status: 0
There is no success banner in the normal output. The zero exit status is the result to check. Choose a triple matching the object you are testing; x86_64 matches the object produced above on this machine. If you omit --triple, the installed tool reports -triple required when running in -verify mode.
5. Inspect functions without loading them first
--printobjline prints line information from the object without loading it first. With an object compiled without debug information, the installed output still identifies the function and its size:
$ llvm-rtdyld-20 --printobjline "$work/sample.o"
Function: main, Size = 36, Addr = 0
Instruction size can change with compiler version and options, so treat the number as diagnostic output rather than a fixed expected value. For source line information, compile with the debug options appropriate to your test and compare the output on your own object.
6. Measure phases when a test is slow
Add --show-times to print timings for loading, linking and the overall operation:
$ llvm-rtdyld-20 --execute --entry=main --show-times "$work/sample.o" --args demo
loaded 'main' at: 0x7be361b5d000
===-------------------------------------------------------------------------===
timers for llvm-rtdyld phases
===-------------------------------------------------------------------------===
The address and timing values vary between runs. Use this output to separate time spent adding object files from time spent linking or executing. It is not a correctness check by itself.
7. Diagnose the common unresolved-symbol failure
If the object calls a function that is not present in the loaded objects or added libraries, execution stops during linking. For example, an object that calls printf can produce:
llvm-rtdyld-20: Could not find definition for "printf"
This is a missing definition, not a request for sudo. Check the object with llvm-nm-20, then decide whether the test should include another object or a library via the documented --dylib=<path> option. Do not load an arbitrary shared library merely to silence the diagnostic. Confirm its origin, architecture and intended symbols first.
Other useful controls are --mcpu=<cpu-name> for a specific CPU, --triple=<string> for disassembly targeting, and --preallocate=<ulong> for an upfront allocation size. Use them only when the test has a concrete target or memory requirement. The default CPU and allocation strategy are not stated by this manpage, so do not treat them as portable compatibility guarantees.
8. Remove the temporary files when finished
Once you have saved any diagnostic output you need, inspect the directory and then remove only this disposable directory. This is the one destructive command in the workflow:
$ find "$work" -maxdepth 1 -type f -printf '%f\n'
sample.c
sample.o
$ rm -r -- "$work"
Do not substitute a broad path for "$work". If you need to rerun a test, keep the directory instead; there is nothing to undo after removal.
Done means
- The installed version and option syntax were checked with
--versionand--help. - A self-contained relocatable object executed through
mainand returned status 0 with one argument. - The same object passed
--verify --triple=x86_64without executing its entry point. - You know that unresolved external symbols need deliberate object or library inputs, not elevated privileges.