| Issue |
209244
|
| Summary |
[ARM] PIC codegen emits R_ARM_REL32 against preemptible weak symbols, breaking shared-library links (regression from #208372)
|
| Labels |
new issue
|
| Assignees |
|
| Reporter |
alexreinking
|
### Summary
On 32-bit ARM ELF in PIC mode, references to **weak** symbols that are preemptible in a shared object are now emitted as a direct PC-relative reference (`R_ARM_REL32`) instead of going through the GOT. GNU `ld` rejects an `R_ARM_REL32` relocation against an external/preemptible symbol when producing a shared object, so any `-shared` link that references such a weak symbol fails with:
```
relocation R_ARM_REL32 against external or undefined symbol `<sym>' can not be used when making a shared object; recompile with -fPIC
```
This is a regression from #208372 ("[ARM] Use .reloc for weak symbols in PIC mode instead of GOT indirection"), which replaced the GOT indirection added in #198577 (the fix for #183916). In other words, #208372 attempted to optimize away the GOT load introduced by #198577, but the `.reloc`/`R_ARM_REL32` approach is invalid for weak symbols that are preemptible in a shared object — reintroducing a breakage in this area.
### Impact
- Breaks the compiler-rt runtime shared libraries (e.g. `libclang_rt.ubsan_standalone.so`) on armv7 — the objects reference weak symbols such as the glibc weak import `__libc_stack_end` and the sanitizer-internal weak `__sanitizer::internal_pthread_create`.
- This blocks downstream builds that package recent LLVM for 32-bit ARM (e.g. Halide's manylinux armv7l wheels). It is the same platform/pipeline as my earlier arm-32 report #197920.
### First bad commit
Bisected between `7f449d38cd9d` (2026-07-04, good) and `d6d0ccce19d1` (2026-07-11, bad):
- **`290279b6b69138f4162cace4865713efa12ec262`** — #208372 "[ARM] Use .reloc for weak symbols in PIC mode instead of GOT indirection".
### Root cause
The commit removed the "weak symbols go through the GOT in PIC" rule. In `ARMSubtarget::isGVInGOT` and `ARMTargetLowering::LowerGlobalAddressELF`:
```diff
- bool UseGOT = !GV->isDSOLocal() || GV->isWeakForLinker();
- SDValue G = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0, UseGOT ? ARMII::MO_GOT : 0);
+ SDValue G = DAG.getTargetGlobalAddress(GV, dl, PtrVT, 0, GV->isDSOLocal() ? 0 : ARMII::MO_GOT);
```
```diff
- return isTargetELF() && TM.isPositionIndependent() &&
- (!GV->isDSOLocal() || GV->isWeakForLinker());
+ return isTargetELF() && TM.isPositionIndependent() && !GV->isDSOLocal();
```
The GOT is now used only when `!isDSOLocal()`. But a weak symbol that LLVM has inferred as `dso_local` can still be **preempted/undefined at link time** in a shared object; the previous `isWeakForLinker()` GOT path existed precisely to handle that. Emitting a direct `R_ARM_REL32` for such a symbol produces a relocation GNU `ld` refuses in a `.so`. The two symbols observed in the failing link are both weak: the glibc weak import `__libc_stack_end@@GLIBC_2.4` and the sanitizer weak `__sanitizer::internal_pthread_create`.
### Reproduction (verified: compiler-rt shared lib link)
Building compiler-rt for armv7 in PIC mode and linking the sanitizer runtime shared library fails. The link command (abridged) and errors:
```
[NNNN/NNNN] Linking CXX shared library .../libclang_rt.ubsan_standalone.so
FAILED: .../libclang_rt.ubsan_standalone.so
: && clang++ --target=armv8l-unknown-linux-gnueabihf -fPIC ... -shared -Wl,-z,defs \
-march=armv7-a -mfloat-abi=hard -nostdlib++ ... -o libclang_rt.ubsan_standalone.so \
.../sanitizer_common_libcdep.cpp.o .../sanitizer_linux.cpp.o ... && :
/usr/bin/ld: sanitizer_common_libcdep.cpp.o: relocation R_ARM_REL32 against external or undefined symbol
`_ZN11__sanitizer23internal_pthread_createEPvS0_PFS0_S0_ES0_' can not be used when making a shared object;
recompile with -fPIC
sanitizer_common_libcdep.cpp:(.text._ZN11__sanitizer25MaybeStartBackgroudThreadEv+0x98): dangerous relocation: unsupported relocation
/usr/bin/ld: sanitizer_linux.cpp.o: relocation R_ARM_REL32 against external or undefined symbol
`__libc_stack_end@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: sanitizer_linux.cpp.o(.text._ZN11__sanitizer7GetArgvEv+0x1f0): unresolvable R_ARM_REL32 relocation
against symbol `__libc_stack_end@@GLIBC_2.4'
/usr/bin/ld: final link failed
clang++: error: linker command failed with exit code 1
```
(The interleaved `DWARF error: invalid or unhandled FORM value: 0x25` line is an unrelated benign `ld` warning.)
Full downstream log / workflow run: https://github.com/halide/halide-llvm/actions/runs/29173588330 (job "Build / arm-32-linux").
### Suggested minimal reproduction
A weak symbol that LLVM treats as `dso_local` under PIC should now round-trip to `R_ARM_REL32` rather than a GOT reference. Something like:
```llvm
; llc -mtriple=armv7-unknown-linux-gnueabihf -relocation-model=pic -filetype=obj t.ll -o t.o
; llvm-readobj -r t.o # expect an R_ARM_REL32 against `w` where a GOT-relative reloc is required
@w = weak dso_local global i32 0
define dso_local ptr @get() {
ret ptr @w
}
```
(I bisected via the commit diff above against a downstream build; I have not run this exact snippet — please treat the compiler-rt link failure as the authoritative repro.)
### Expected behavior
References to weak symbols that may be preempted at link time should continue to use GOT indirection in ARM PIC mode (as they did before #208372), so that shared-object links succeed. If the goal of #208372 (allowing linker override of a weak definition without a GOT load) is to be preserved, the direct-`.reloc` path needs to be restricted to weak symbols that are genuinely non-preemptible in the final link.
### Environment
- LLVM: `main`, bad at `d6d0ccce19d1`, last good at `7f449d38cd9d`; regression commit `290279b6b691` (#208372)
- Target: `armv8l-unknown-linux-gnueabihf`, `-march=armv7-a -mfloat-abi=hard`, PIC
- Host/image: `quay.io/pypa/manylinux_2_31_armv7l` (GNU ld / binutils from that image)
- Downstream: Halide wheel build pipeline (https://github.com/halide/halide-llvm)
### Related
- #208372 — the regressing PR
- #198577 — the GOT-for-weak fix it replaced, which fixed #183916 (original ARM PIC weak-symbol override bug)
- #197920 — earlier arm-32 codegen regression caught in the same Halide wheel pipeline
cc @dongjianqiang2 (author of #208372 / #198577)
_______________________________________________
llvm-bugs mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs