<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/119479>119479</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[lld] Regression in LTO/gc-sections for certain symbols impacting AMDGPU ELFs
</td>
</tr>
<tr>
<th>Labels</th>
<td>
lld
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
benvanik
</td>
</tr>
</table>
<pre>
The caching added in https://github.com/llvm/llvm-project/commit/3733ed6f1c6b0eef1e13e175ac81ad309fc0b080 by @MaskRay seems to have broken LTO and `--gc-sections` for certain use cases. Specifically the change made in `lld/ELF/MarkLive.cpp` to use the cached `isExported` value instead of calling `includeInDynsym` seems to be causing additional sections to be dropped that should not be (or at least were not before the change): https://github.com/llvm/llvm-project/commit/3733ed6f1c6b0eef1e13e175ac81ad309fc0b080#diff-3c88c62d912008cc04f796b330a035ecda925645264eaef43185ad43991cb8e9L224)
The AMDGPU target inserts special kernel descriptor object symbols that must be preserved into the final ELF for the runtime to load. These match any exported kernel in name with a `.kd` suffix and are emitted by `AMDGPUTargetELFStreamer::EmitAmdhsaKernelDescriptor`. Prior to the referenced commit these symbols existed and after they don't.
By reverting the mentioned line in `MarkLive.cpp` the original behavior is restored. I'm not familiar with the codebase but I suspect `isExported` is not initialized or not safe to cache at that location.
The following repro shows the issue (`lld_lto_bug.c`):
```c
[[clang::amdgpu_kernel, gnu::visibility("protected")]] void some_kernel(int n) {
//
}
```
compiled using
```sh
$ clang \
-x c -std=c23 \
-target amdgcn-amd-amdhsa -march=gfx1100 \
-nogpulib \
-fgpu-rdc \
-fno-ident \
-fvisibility=hidden \
-O3 \
lld_lto_bug.c \
-c -emit-llvm -o lld_lto_bug.bc
```
or since bc files cannot be attached:
```
; ModuleID = 'lld_lto_bug.bc'
source_filename = "lld_lto_bug.c"
target datalayout = "e-p:64:64-p1:64:64-p2:32:32-p3:32:32-p4:64:64-p5:32:32-p6:32:32-p7:160:256:256:32-p8:128:128-p9:192:256:256:32-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-v2048:2048-n32:64-S32-A5-G1-ni:7:8:9"
target triple = "amdgcn-amd-amdhsa"
@__oclc_ABI_version = weak_odr hidden local_unnamed_addr addrspace(4) constant i32 500
; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none)
define protected amdgpu_kernel void @some_kernel(i32 noundef %n) local_unnamed_addr #0 {
entry:
ret void
}
attributes #0 = { mustprogress nofree norecurse nosync nounwind willreturn memory(none) "amdgpu-no-agpr" "amdgpu-no-completion-action" "amdgpu-no-default-queue" "amdgpu-no-dispatch-id" "amdgpu-no-dispatch-ptr" "amdgpu-no-heap-ptr" "amdgpu-no-hostcall-ptr" "amdgpu-no-implicitarg-ptr" "amdgpu-no-lds-kernel-id" "amdgpu-no-multigrid-sync-arg" "amdgpu-no-queue-ptr" "amdgpu-no-workgroup-id-x" "amdgpu-no-workgroup-id-y" "amdgpu-no-workgroup-id-z" "amdgpu-no-workitem-id-x" "amdgpu-no-workitem-id-y" "amdgpu-no-workitem-id-z" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx1100" "target-features"="+16-bit-insts,+atomic-fadd-rtn-insts,+ci-insts,+dl-insts,+dot10-insts,+dot12-insts,+dot5-insts,+dot7-insts,+dot8-insts,+dot9-insts,+dpp,+gfx10-3-insts,+gfx10-insts,+gfx11-insts,+gfx8-insts,+gfx9-insts,+wavefrontsize32" "uniform-work-group-size"="false" }
!llvm.module.flags = !{!0, !1, !2}
!0 = !{i32 1, !"amdhsa_code_object_version", i32 500}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 8, !"PIC Level", i32 2}
```
Linking with LTO and gc-sections:
```sh
lld \
-flavor gnu \
-m elf64_amdgpu \
-shared \
-plugin-opt=mcpu=gfx1100 \
-plugin-opt=O3 \
--lto-CGO3 \
--gc-sections \
--print-gc-sections \
--strip-debug \
--discard-all \
--discard-locals \
-o lld_lto_bug.so \
lld_lto_bug.bc
```
Before the commit this will print the expected output (no removal of the rodata):
```
removing unused section lld_lto_bug_patched.so.lto.o:(.text)
```
After the commit with the regression removing the rodata:
```
removing unused section lld_lto_bug.so.lto.o:(.text)
removing unused section lld_lto_bug.so.lto.o:(.rodata)
```
This can be verified with llvm-readelf as before:
```
Symbol table '.dynsym' contains 3 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001500 4 FUNC GLOBAL PROTECTED 7 some_kernel
2: 0000000000000480 64 OBJECT GLOBAL PROTECTED 6 some_kernel.kd
```
The `some_kernel.kd` `OBJECT` is what is required at runtime to use the ELF.
And after:
```
Symbol table '.dynsym' contains 2 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001500 4 FUNC GLOBAL PROTECTED 6 some_kernel
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJy8OFtv27jSv0Z5GdCQKF8f8uDESdHvc5timy5wngxKHNk8oUgtSTlxf_3BUJYtO-kudnFwgtY2Z4bDuXEuFN6rrUG8TSZ3yWR1I9qws-62QLMXRr3cFFYebp93CKUod8psQUiJEpSBXQiNT_Jlwh8T_rhVYdcWo9LWCX_Uet9_scbZf2MZEv5Y2rpW9COf5TnKaZWV0yJFrDLMcsxmE1HOMyHzdFGVaZHOUygOkIzTL8K__CYO4BFrD8HCTuwRCmdf0MD6-QmEkZBMU8a2JfNYBmWNT6YpVNZBiS4IZaD1pIJHP4LvDZaqUqXQ-gCBVNsJs0WohURSLJmmWsuEPz6sSbMvwr2s1R5HZdMQ12Ajs3C0CcazlX94a6wLKIlkL3RLrHxAIcFWQGeR8YjSlLqV-NmsDsYfaiI_KVYQy9YfzaxIEaGhV-lIIZ1tGpQQdiKA39lWSzA2ECrhc-tABNAofIBXdHhEVdbhQNeEL5J8-T9wYcJzqaqK5eV8Xk65XGQ8TedlmY6r2WJa5Hkq0nyCpRQLPpmOJ3w6RoHVOM_mEyHH-WKRlcUcF2vOxyR0ukzSJYXj8svq07cfEITbYiBTowsePLlWaHhBZ1CDRF861QTrwBakAvhDXVjtO-PVrY9maxx6dPsY1sFGM1WKLP-wfoxBRBDXmqBqJCdoK-QInnfoKWhCuQNhDoDHAOgPVwaMqBFeVdiBINePXmJ0-Laq1FuMWuEQsFaBtlGwT9NOr-eo1sP68XtwKGp05KR8-VCrsKzlzov_j2esTvoRd_jmFAnbaeCwQoemRAmd1wjq8WQBfFOejo1iVAGjlgeQ1iR8Fkadpe8O4HCPLlBIEtcaDcUiStDK9Lfl3Q3ZIVinttGGBe7EnuRSHhz6YB3KEXxO-KyOsVmJWmklXGenGKJWYiE8QtEG-Ay-Ja-G97dM-chAGRWU0OonSrAugryooqPi_aT7EN2tbSlI-NE5iiqrtX0l5Rw2ztJ1evVRCOV9Gy9Ulw02OthN0W5HZTJNu9tDXKZp96-kBSXQu1ILs-28JWq5bdpNFw4Jv4etaTvMXnlVKK3CgQ7gvHE2YElqcU7MJ6tksoK9VRK8rfHEYq5MAJPwBSSzuyRdQndx6fDZaihPki5LWzdKo4SYT4ZIv6MVH0OUFZLJPbEC9gYlMB9kkq9Knp_hxztG6pSGiVrS_50XwGrhyl2Sr7bVW5al6XmLsdum1ao4Q6pt0zInyx7CKmOZkmjCgGZgl3y1U1KiOWOfziJdOORMUQKjy8QoaQGzF2RFeWWfJF1aB16ZEqEooVIaPZTCHFOpCCEm9ytH0-_8Dr5Y2Wr8vIIkX0HCZ1cn8VmSLr1tXYkbYhzzQEfKL4OJ8yRdHg0sRRBaHGwbelpkTZIvp-P4wZpsuOBJvsy7D9bkw8V4SDYZYqbDxSzJl9k0TfIln0xPn4SZE4YfP1lDwZ4t-HtC1Z-yz6aRGdvzcYfa98fsx_MjzWJ6ZLg_MWP7E0e2n2QEnWSc7bM08qEvtudpZEFfzES20zH7nnO2nLBPGTMqyZekCxEtLgwanGr0yfDv4rejpX_jdLOxpS43y7vPmz06r6yJ215RvGysdHAMRkohetMacqjcCCkdFWrnG1FiwudUoqC0xgdhAqicwyQ9xhpFzWNrYiWHZQiO6m6sQI2zW4eeklnlkAq2w7J1nn75gynB2Na8KiPhVWntMLTOQI21dZQ9jDXYFUaJFaXkUy6Bi_zTZZNknF4llJxH_hIrSPgk5pYPlEx4nh5zDprgDt2tAHAYIuNzBkqXIgSnijagP24j88_u_nu69s5sWmYsE9vGJZxfAin5aSRbMxFN_o5CYiVaHdgfLbb4Hqt8Q4WdKflrXBPeH7xD0XyMsD5QF_ghUtWNVqWisP0Qr6Vnncs-EqhudVBbpyQjEzLhtu9IopYfsn617mXrbNswJdnbn6MPf47--SFaBax_zbvHfsy6x_acjWXBiaZRZstqEXZ0g_NVwnlwJyf6IMoXdrwF1rGirSp0zKufeCKfH2m7PMHKpj2hjqXskqBCEVqH_kSV8LtsygoVGDX5PuH3Cb8TwdaqZJWQkrlghqhSDVdSX6xsyNJrAL8CTK7Ws6v1_Gq9uFg3TfeDlEtZPsR1oCtAdgWYX60vuL-KPVbOmkAmzvnRcq1RlXV1dCPrQuTCBZXQvnNZnzcSnlHhHtWxto4qLbb-mLszSj08S6mJSniWHb_5cG86oKWs1hN1QbXzYkN95aabA_okH115f8rUHTueZb_m9VruhNv0qhz3jk87-dXO-Xnnt8_3sMY9pd3TRv6-b0vS5VqZF-pJY0fcD7jD2fayJ4n9nNZy0EhpsbeO-s0zrAbU1XS86W7YGe53wuFgb6PbrTLMNiHJVzVdjQ_6uwuic2PGmA6W3X8atGoXQ_kA2jhlwi9wngo3k1i0g-6U8m4pnGRC6w-gsWgNmFx2f972mL_qCe8Go3I_NikfyxFEmSMK35quxto2NNSvUXECh7XdC00Df5zALHV074eFJF1GSnJxa1qPsh_xh9JtYpFBOfJ2pIMd2Tinz0cB38JxEr6UfNkPcb3gp4HKYSy6dMDp4IGA_0S6X0v1DzafDfVOqWcyfikMteR7dKpSKDvF4suEQyFRVyD88Y3jvTLf47wLQRTUCvLZSHbvLnxGnVoQynjIgboahb7va-BrW1N_BgC_x7ec7-onwvOhQQLdUYMC8Lvy0P19lW_wVdTY7QWgphrSqz9I4evT87--PQCsn-6Xa4DVw-Pyx_oZAH58XUG_ObvenE3SNGLG8Pjj6z18Wj_dLdfw7ben54f754cVAMwu5sQjI_5OivE8MpqO4enu_x7unz9iNR2yGr3IK3PS1Ew555JmmhKwY3oczV9p5I4z_x-togwjwvANpX9Ce1g_HufxZf8Q8c98yP_Sh6e_9878u66MmF-58285E-AvnTA0xo28zeUiX4gbvM1meT6fzRaz2c3uVhSzCc-yopJFmU3HqSjSNJvx6Xy2SKtxMbtRtzzl44xTIh9Ps9kIsyrNJphmmMlpVfBknGItlB7FKmzd9ia-gdxm2WI8W9xoUaD28Zk4DrFUxSarG3cbL2LRbn0yTrXywZ8ZBBV0fFkm-skKfjsnIhWfbhP-OCwBwwfb_p1K1Q218GbbP_k9rB_9Tev07d9-vYz6-IQ_HlXa3_L_BAAA__9D-RAV">