- 
                Notifications
    You must be signed in to change notification settings 
- Fork 15k
Description
Description
When linking with lld and passing both -shared and --dynamic-linker=/lib64/ld-linux-x86-64.so.2, the resulting ELF shared object does not contain a PT_INTERP program header.
This differs from GNU ld (bfd), which does emit a PT_INTERP in the same situation.
This causes executables built as cdylib+-shared+--dynamic-linker to segfault when run directly, because the runtime loader is never invoked.
If run explicitly with the loader (/lib64/ld-linux-x86-64.so.2 ./libmylib.so), the binary executes correctly.
Steps to Reproduce
- 
Minimal C file: int main() { return 42; } 
- 
Link with lld: clang -fuse-ld=lld -shared test.c \ /usr/lib/x86_64-linux-gnu/Scrt1.o \ -Wl,--dynamic-linker=/lib64/ld-linux-x86-64.so.2 \ -o test.so 
- 
Inspect program headers: readelf -l test.so | grep INTERP→ no INTERPsegment is present.
- 
Run: ./test.so → segfault (jump to 0). 
- 
Run with loader explicitly: /lib64/ld-linux-x86-64.so.2 ./test.so → works, returns exit code 42. 
- 
Now repeat with GNU ld (bfd): clang -fuse-ld=bfd -shared test.c \ /usr/lib/x86_64-linux-gnu/Scrt1.o \ -Wl,-pie \ -Wl,--dynamic-linker=/lib64/ld-linux-x86-64.so.2 \ -o test_bfd.so readelf -l test_bfd.so | grep INTERP→ INTERPis present, and./test_bfd.soruns fine.
Analysis
Looking at lld source:
- 
needsInterpSectioninlld/ELF/SyntheticSections.cpp:static bool needsInterpSection(Ctx &ctx) { return !ctx.arg.relocatable && !ctx.arg.shared && !ctx.arg.dynamicLinker.empty() && ctx.script->needsInterpSection(); } → Explicitly excludes the .interpsection when-sharedis passed.
- 
Writer<ELFT>::createPhdrsinlld/ELF/Writer.cpp:if (OutputSection *cmd = findSection(ctx, ".interp", partNo)) addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd); → Since .interpis never created in-sharedmode,PT_INTERPnever gets emitted.
This is the direct cause: lld refuses to emit PT_INTERP for -shared binaries, even if --dynamic-linker is given.
Expected Behavior
When passing -shared and --dynamic-linker=..., lld should:
- Emit .interpsection
- Emit a PT_INTERPprogram header
 so the shared object is executable directly via the kernel, matching GNU ld (bfd).
Actual Behavior
- .interpand- PT_INTERPare not emitted by lld.
- Running the .sodirectly segfaults, because the loader is never invoked.
Impact
- Rust issue for reference: rust-lang/rust#146780
Environment
- Rust 1.90.0 stable (where lldbecame default forx86_64-unknown-linux-gnu)
- LLVM/lld master (current behavior still the same)
- Reproduced on Ubuntu 22.04 (glibc-based)
Suggested Fix
Loosen the condition in needsInterpSection() so that .interp can be emitted if --dynamic-linker is provided, even under -shared. This matches bfd behavior and would fix the segfault.