<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/202148>202148</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[RISC-V][lld] ld.lld emit relocs relax keeps stale callplt relocs (before LLVM 18)
</td>
</tr>
<tr>
<th>Labels</th>
<td>
lld
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
XiaobingHou1219
</td>
</tr>
</table>
<pre>
I ran into this while reducing a RISC-V linker testcase. The reproducer is small, and I have been seeing the same result consistently across three reruns.
### Summary
ld.lld 15.0.7/16.0.6/17.0.6 link successfully but keep stale R_RISCV_CALL_PLT relocations on the first call and on f, while losing the second call/'s final R_RISCV_JAL and the terminal R_RISCV_ALIGN. ld.lld 18.1.8 and 22.1.0 emit the correct final relocation set: two R_RISCV_JAL plus one R_RISCV_ALIGN.
### Expected behavior
Link a tiny RV64 executable at a fixed address with --emit-relocs so two adjacent call sites relax to jal while the aligned ret keeps a final R_RISCV_ALIGN. This is a meaningful final-output relocation rewrite check.
### Environment
- linker route: llvm-mc 22 plus ld.lld version replay
- march: rv64i
- mabi: lp64
- first failing stage: link
- local stability check: True
### Reduced testcase
These are the reduced input files from the minimized reproducer I used locally:
#### `case.s`
```asm
# REQUIRES: riscv
## Test that we can handle --emit-relocs while relaxing.
# RUN: rm -rf %t && mkdir %t && cd %t
# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o -riscv-align-rvc=0
# RUN: ld.lld -Ttext=0x10000 --emit-relocs 32.o -o 32
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 32 | FileCheck %s
# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o -riscv-align-rvc=0
# RUN: ld.lld -Ttext=0x10000 --emit-relocs 64.o -o 64
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64 | FileCheck %s
## -r should keep original relocations.
# RUN: ld.lld -r 64.o -o 64.r
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.r | FileCheck %s --check-prefix=CHECKR
## --no-relax should keep original relocations.
# RUN: ld.lld --emit-relocs --no-relax 64.o -o 64.norelax
# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s --check-prefix=CHECKNORELAX
# CHECK: <_start>:
# CHECK-NEXT: jal ra, 0x10008 <f>
# CHECK-NEXT: R_RISCV_JAL f
# CHECK-NEXT: R_RISCV_RELAX *ABS*
# CHECK-NEXT: jal ra, 0x10008 <f>
# CHECK-NEXT: R_RISCV_JAL f
# CHECK-NEXT: R_RISCV_RELAX *ABS*
# CHECK-EMPTY:
# CHECK-NEXT: <f>:
# CHECK-NEXT: jalr zero, 0x0(ra)
# CHECK-NEXT: R_RISCV_ALIGN *ABS*+0x4
# CHECKR: <_start>:
# CHECKR-NEXT: auipc ra, 0x0
# CHECKR-NEXT: R_RISCV_CALL_PLT f
# CHECKR-NEXT: R_RISCV_RELAX *ABS*
# CHECKR-NEXT: jalr ra, 0x0(ra)
# CHECKR-NEXT: auipc ra, 0x0
# CHECKR-NEXT: R_RISCV_CALL_PLT f
# CHECKR-NEXT: R_RISCV_RELAX *ABS*
# CHECKR-NEXT: jalr ra, 0x0(ra)
# CHECKR-NEXT: addi zero, zero, 0x0
# CHECKR-NEXT: R_RISCV_ALIGN *ABS*+0x4
# CHECKR-EMPTY:
# CHECKR-NEXT: <f>:
# CHECKR-NEXT: jalr zero, 0x0(ra)
# CHECKNORELAX: <_start>:
# CHECKNORELAX-NEXT: auipc ra, 0x0
# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f
# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS*
# CHECKNORELAX-NEXT: jalr ra, 0x10(ra)
# CHECKNORELAX-NEXT: auipc ra, 0x0
# CHECKNORELAX-NEXT: R_RISCV_CALL_PLT f
# CHECKNORELAX-NEXT: R_RISCV_RELAX *ABS*
# CHECKNORELAX-NEXT: jalr ra, 0x8(ra)
# CHECKNORELAX-EMPTY:
# CHECKNORELAX-NEXT: <f>:
# CHECKNORELAX-NEXT: jalr zero, 0x0(ra)
# CHECKNORELAX-NEXT: R_RISCV_ALIGN *ABS*+0x4
.global _start
_start:
call f
call f
.balign 8
f:
ret
```
### Reproduction notes
- This packaged root does not have a single canonical `run.ps1` wrapper.
- Use the reduced inputs under `case/` and follow the commands documented in `case/README.md`.
- Stable witness outputs, when present, are preserved under `verify/run1..run3/`.
### What I checked
- Reduced inputs are preserved under case/.
- Stable witness outputs are preserved under verify/run1..run3/.
- The strict recheck says stable normalized run signatures across three runs: True.
- Tracker guidance link: https://llvm.org/docs/HowToSubmitABug.html
- evidence summary: 3 clean reproductions under hunt/verify/lld_emit_relocs_relax_keeps_stale_callplt_relocs_before_18/run1..run3 show stable assembly success for 64.o, stable 15/16/17 final outputs whose objdump files keep R_RISCV_CALL_PLT on 0x10000 and 0x10008 after relaxation, and stable 18/22 final outputs whose objdump files show R_RISCV_JAL on both relaxed calls and R_RISCV_ALIGN on f.
### Notes
upstream///llvm-project/lld/test/ELF/riscv-relax-emit-relocs.s
Root key: `lld.riscv.emit_relocs_relax_keeps_stale_callplt_relocs_before_18`
Case id: `20260527-lld-emit-relocs-stale-callplt`
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJy0WF1T4zrS_jXipssux05MuOAiQHiHd5nZs4GZnb1KKVY71iBLLklOyPn1W5LsfE0CzDmzFEVhqz-fbsl6mhrDlxLxmoxuyOjugra2Uvr6O6dqweXyk2oH6eDqYqHY5voBNJXApVVgK25gXXGBoJG1BZdLoDB7eLqNvoHg8gU1WDS2oAZjeK6cWKMVawvUwA2YmgpB0lugksEDVHSFsECUYBCdLVshGFo7NdMKC4WShhuL0ooN0EIrY8BWGp2AbqWJSXJHkkn3N83CLzy1dU31JrwWLBaCwWAUJ_ElSe8HeZzEufvn0v3jwwbTFgUaU7ZCbGDRWnhBbMBYKhBmc5fgt_nt5PFx_sfjM2gUqqCWK2lASR90ybWxUFAhfGpKQunSDFAJZbbJYaEk84IkvSfppYGSSyq2Tv5_8ugtOGGLuj5YnDw-_N-XGPqMxvEgHnvpNI0HcQJYc-s1C6U1FrazvYsXDFqSTcCu1YHHRrQuFTzydBrd6WuDhUUGC6zoiisdBB4djhQslxuYfcuHgK9YtJYuBAK1QKHkr8iAMqbRGFhzW0EUuZAjH6ABo3xglP2gBcoOTsMtGpcCfQWr4AcVHaouTypcFzPQGCpmvJsTkD27xuVuuUYquVyWrQiSkWpt09p9kDSuNbcIRYXFyzkQ5IprJWuUNixFff9r1Vp0IAuxqqO6gDQN-HZlW6E2wUsj6KZXrqkuKqelV_mQ794uuDfV5MP-XWi1knLhmspYugzeuHzZRqIKKtzSggtuNyERJ_SsWzydz8xtZ2Tb3RvWnys0CFQHtHUnw6UDrOQCDZRa1X6x5pLX_E9fjO2Wf4DWIAvxiA3JJiedO_8kT_yhYUiedMt5En6pqbcKMJv-6-vDbPrkoeKmWO3bgmc0bgNQC2uEgkqoqGQCj_qsP8AEfeVyeVxgmH394q3XEOkSSDqyQNKcpDnUL4zrgzcF849nTPQdEDms7KZBkt2pxQ-IrOaNcE8-hSyFqKbWapLdkfQm9DpJRwYiBVkaK4i8XOTbPdKrgmR3yc_eQn9FzxZfrZN4HSRJkhwlH-w5u6fDVYsfrK0biJiGKJIqMpVaR5quIy6NhOgzSOUCoQadMSCXt3DPBd66HvNR_z0w8uEbYOTD3wtGsOfs_gYw8uH7YDgHkQZTqVaw8JFRmi-Pjuntd-3nfPRezLH-LVHH-kTcEEX-1IgajSV_Jdnd7afp7T9mp_JxLkKd_npeB2XZs7iXrVT-1e_JuTP28cy__HM2fZx8P25uv-aiINnt3FiqLcmmu4Oul4i-TL8_OzH3475hmrr7QejKsVMund4Zpf1PdfmWZffTC_twgaSTyc0TSc8G9MvBHPt5M6gPBjP9_Mfzf95ArQ_pXVw1_IlahWwSko5dZlcfzcTfFfaCTG-S1-HJgs-26m-XfXbgi7a8KbZgJ2_K7se1vXgewzz7RZxnP8O1i-Y0WDuV_2H0v9a6fysLXwfG-LZP9vvlPXTfapCd1plmnr3fzScyO9_Oh6r98fTuSdQJ_kJjHmu8W9hTLj5e3lPaB0UenKvyseavpfXhvv296R2kNn4nszOtdWzzbIOdxfb9U_NcF7x7aMZLoRZUQNeT_l3fn50IBKrXAX34APHC3_RgHF6Ue1oa7RFXOEduAiPx7E4qi6ZnSp4XNrR4oUvHXJSywBQaJxSmExQcdxeeTSjJHa8ieaJbGTdmQPIE1po2Deq4t_jVnCBLBlrJUPcsx1H_PPHMvVRCqHVH2uuaSmaAqaJ1xNJr7-nMppO7z9O4ZiRPtv6eAsNecysdrQ5s1oThA0poNBrHUdNbT-P8o14h2wW0Qs3LDUnvdSsHcaxbmYX4zjDffzt69RA4JbI-jNlhuqd8dVm8E_lJ1dMxxrsqIhireeFovA8MDN0Yz4AFglS6piJw01aC4UtJbavRHI2UWml6krwzrWnhWP2y5YzKAgPPziZQWds4cT_GuXd30FjpJUnvmSoMSe8_qfWzemoXNbeTm3YZV7YWvVFccYbOmOkGVdkEMigEUrllz2G8FNKvWlfB-y0KQrC5uy_Pw3157q-ycz8AmfuZ1dxtoUZsBRZYKo3zwfgAQnddX_cgUWOwXohNPwqDUgWu4VqnkxmM_PzMz866OUtftXWlDEJ_Cw-zAU8DfjpOlYSejLkN0N86aWlRB1LuuUI_Jexdu9DT9ANefVL7l1MlYaFsFWxjGL4Zb_vwDFMSyjM9_2V3ZrSNsRppHerelT5qtPqBhQ21Iem9ReMepo9OIvBV736f5sQH5HDmzp4X9L1A8kQIFnu9-C8Wuj8Lb6lB4KwzmyZpnozSy0gIth9L5K1FnbWt8gW7zthVdkUv8HpwOU7GWZ7nVxfVdZ6OB1lSDMthkSejq-wqKcbJkI1xnA-zPB1f8GvvKk8uB-lokFzFdDAuk-xyjHTIRsOyJMMEa8pF3G-dC25Mi05tMBxfCLpAYfx4Ok09pikZ3V3oaw_3ol0aMkwEN9bsDFhuhZ9oh4E0Gd2R0Y3THd31PNNPSDuWGQhgGBuGSW-Xfi9A0nHAEx4fv332LXh10Wpxfbj3l9xW7SIuVN11w89N4VNzp0KX3eo6_W8AAAD__xXK9x0">