| Issue |
160059
|
| Summary |
`lld` does not emit `PT_INTERP` when using `--dynamic-linker` with `-shared` (works with GNU ld/bfd)
|
| Labels |
lld
|
| Assignees |
|
| Reporter |
izissise
|
### 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
1. Minimal C file:
```c
int main() { return 42; }
```
2. Link with lld:
```bash
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
```
3. Inspect program headers:
```bash
readelf -l test.so | grep INTERP
```
→ no `INTERP` segment is present.
4. Run:
```bash
./test.so
```
→ segfault (jump to 0).
5. Run with loader explicitly:
```bash
/lib64/ld-linux-x86-64.so.2 ./test.so
```
→ works, returns exit code 42.
6. Now repeat with GNU ld (bfd):
```bash
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
```
→ `INTERP` is present, and `./test_bfd.so` runs fine.
### Analysis
Looking at lld source:
* **`needsInterpSection`** in `lld/ELF/SyntheticSections.cpp`:
```cpp
static bool needsInterpSection(Ctx &ctx) {
return !ctx.arg.relocatable && !ctx.arg.shared &&
!ctx.arg.dynamicLinker.empty() && ctx.script->needsInterpSection();
}
```
→ Explicitly excludes the `.interp` section when `-shared` is passed.
* **`Writer<ELFT>::createPhdrs`** in `lld/ELF/Writer.cpp`:
```cpp
if (OutputSection *cmd = findSection(ctx, ".interp", partNo))
addHdr(PT_INTERP, cmd->getPhdrFlags())->add(cmd);
```
→ Since `.interp` is never created in `-shared` mode, `PT_INTERP` never 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 `.interp` section
* Emit a `PT_INTERP` program header
so the shared object is executable directly via the kernel, matching GNU ld (bfd).
### Actual Behavior
* `.interp` and `PT_INTERP` are not emitted by lld.
* Running the `.so` directly segfaults, because the loader is never invoked.
### Impact
* Rust issue for reference: [rust-lang/rust#146780](https://github.com/rust-lang/rust/issues/146780)
### Environment
* Rust 1.90.0 stable (where `lld` became default for `x86_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.
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs