<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/59639>59639</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            lld-link.exe does not provide unwind information on Windows x64 for tailMergeX64 code when using /delayload
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            new issue
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          yjugl
      </td>
    </tr>
</table>

<pre>
    Hello,

As described below, `lld-link.exe` adds no unwind information for [the `tailMergeX64` code](https://github.com/llvm/llvm-project/blob/7c26641d9dcea70a75ca48d2e3a5bf6ca7a925bb/lld/COFF/DLL.cpp#L198) when using `lld-link.exe` with `/delayload`, even though this code includes a `call` instruction. As stated [in Microsoft documentation](https://learn.microsoft.com/en-us/cpp/build/exception-handling-x64):

> Table-based exception handling requires a table entry for all functions that allocate stack space or call another function (for example, nonleaf functions). [...] Failure to do so will result in unreliable exception handling and debugging of processes.

This missing unwind information currently has the following real-world impact in the Firefox web browser for Windows x64, when an exception is raised with a return address pointing to `tailMergeX64` stored on the stack:
- this can prevent the crash reporter from being reached, because Microsoft's internal code in `ntdll` that unwinds structured exception handlers may stop at `tailMergeX64`, resulting in no crash dump being created (see [bug 1801322 comment 1](https://bugzilla.mozilla.org/show_bug.cgi?id=1801322#c1));
- when we somehow succeed to get a crash dump, our crash analyzer generates garbage once it reaches `tailMergeX64`, resulting in hard-to-triage crashes (see [bug 1740094](https://bugzilla.mozilla.org/show_bug.cgi?id=1740094)).

Now I will provide more details with a simple example that reproduces the issue. When using the latest LLVM Windows x64 public release:

```powershell
>"C:\Program Files\LLVM\bin\clang.exe" --version
clang version 15.0.6
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Program Files\LLVM\bin

>"C:\Program Files\LLVM\bin\lld-link.exe" --version
LLD 15.0.6
```

To compile the following program into an object file:

```c++
#include <windows.h>

int main(void) {
  MessageBoxW(NULL, L"Hello", L"Hello", MB_OK);
  return 0;
}
```

Then link it with `lld-link.exe` using `/delayload`:

```powershell
> "C:\Program Files\LLVM\bin\clang.exe" -c main.cpp
> "C:\Program Files\LLVM\bin\lld-link.exe" main.o /defaultlib:libcmt.lib /defaultlib:user32.lib /defaultlib:delayimp.lib /delayload:user32.dll
```

The `tailMergeX64` code is added at the end of the `.text` section to handle delay-loading, but there is not associated unwind information for this function in the `.pdata` (`RUNTIME_FUNCTION`) and `.xdata` (`UNWIND_INFO`) sections.

This can be verified with a script that I provide [here](https://github.com/yjugl/unwindchecker/):

```powershell
> python show_missing_unwind_info.py main.exe
Loading 'main.exe', please wait...

Section .text: unwind information covers 94.00% of 51712 bytes.

Looking for call instructions in the parts not covered by unwind information...

0x14000d8af:    e8903dffff      call 0x140001644
0x14000d8b4:    660f6f0424      movdqa  xmm0, xmmword ptr [rsp]
0x14000d8b9:    660f6f4c2410    movdqa  xmm1, xmmword ptr [rsp + 0x10]
0x14000d8bf:    660f6f542420    movdqa  xmm2, xmmword ptr [rsp + 0x20]

Found 1 call instructions without unwind information in section .text.
Note that some of those may be false positives.
```

Or with a debug session, here in WinDbg:

```powershell
# This is the entry in the import address table for MessageBoxW
0:000> dq main+179f0 L1
00007ff6`afde79f0  00007ff6`afddd878
# This is the delay-load jumper for MessageBoxW
0:000> u main+d878 L0x18
main+0xd878:
00007ff6`afddd878 488d0571a10000  lea rax,[main+0x179f0 (00007ff6`afde79f0)]
00007ff6`afddd87f e900000000 jmp     main+0xd884 (00007ff6`afddd884)
# This is the tailMergeX64 function for delay-loaded user32.dll
00007ff6`afddd884 51 push    rcx
00007ff6`afddd885 52              push rdx
00007ff6`afddd886 4150            push    r8
00007ff6`afddd888 4151 push    r9
00007ff6`afddd88a 4883ec48        sub rsp,48h
00007ff6`afddd88e 660f7f0424      movdqa  xmmword ptr [rsp],xmm0
00007ff6`afddd893 660f7f4c2410    movdqa  xmmword ptr [rsp+10h],xmm1
00007ff6`afddd899 660f7f542420    movdqa  xmmword ptr [rsp+20h],xmm2
00007ff6`afddd89f 660f7f5c2430    movdqa  xmmword ptr [rsp+30h],xmm3
00007ff6`afddd8a5 488bd0          mov rdx,rax
00007ff6`afddd8a8 488d0d817c0000  lea     rcx,[main+0x15530 (00007ff6`afde5530)]
# This calls __delayLoadHelper2
00007ff6`afddd8af e8903dffff      call    main+0x1644 (00007ff6`afdd1644)
00007ff6`afddd8b4 660f6f0424      movdqa  xmm0,xmmword ptr [rsp]
00007ff6`afddd8b9 660f6f4c2410    movdqa  xmm1,xmmword ptr [rsp+10h]
00007ff6`afddd8bf 660f6f542420    movdqa  xmm2,xmmword ptr [rsp+20h]
00007ff6`afddd8c5 660f6f5c2430    movdqa  xmm3,xmmword ptr [rsp+30h]
00007ff6`afddd8cb 4883c448        add     rsp,48h
00007ff6`afddd8cf 4159            pop r9
00007ff6`afddd8d1 4158            pop     r8
00007ff6`afddd8d3 5a pop     rdx
00007ff6`afddd8d4 59              pop rcx
00007ff6`afddd8d5 ffe0            jmp     rax
# Displaying full contents of the .pdata section (which is sorted)
0:000> dt ntdll!_IMAGE_RUNTIME_FUNCTION_ENTRY[0x128] main+0x19000
[...]
   +0x000 BeginAddress     : 0xd81b
   +0x004 EndAddress       : 0xd834
   +0x008 UnwindInfoAddress : 0x15ff8
   +0x008 UnwindData       : 0x15ff8

 +0x000 BeginAddress     : 0xd834
   +0x004 EndAddress       : 0xd84c
 +0x008 UnwindInfoAddress : 0x15ff8
   +0x008 UnwindData       : 0x15ff8

   +0x000 BeginAddress     : 0xd84c
   +0x004 EndAddress : 0xd878
   +0x008 UnwindInfoAddress : 0x15ff8
   +0x008 UnwindData : 0x15ff8

# There should be an entry here for the tailMergeX64 function (BeginAddress: 0xd884),
# but the unwind information ends here
   +0x000 BeginAddress     : 0
   +0x004 EndAddress       : 0
 +0x008 UnwindInfoAddress : 0
   +0x008 UnwindData       : 0
```

The expected result is the one obtained when linking with MSVC's `link.exe` instead:

```
> link.exe .\main.o libcmt.lib user32.lib delayimp.lib /DELAYLOAD:user32.dll
Microsoft (R) Incremental Linker Version 14.33.31630.0
Copyright (C) Microsoft Corporation.  All rights reserved.
```

Or using `clang-cl.exe` to do compile and link at the same time, although I'm not sure what this does under the hood:

```powershell
"C:\Program Files\LLVM\bin\clang-cl.exe" main.cpp user32.lib delayimp.lib /link/DELAYLOAD:user32.dll
```

In both cases, unwind information is present for the `tailMergeX64` code; note that this code also differs slightly from the one added by `lld-link.exe`.

This can be seen with the script:

```powershell
> python show_missing_unwind_info.py main.exe
Loading 'main.exe', please wait...

Section .text: unwind information covers 94.23% of 51712 bytes.

Looking for call instructions in the parts not covered by unwind information...

Found no call instructions without unwind information in section .text.
```

Or, in a debug session:

```powershell
# This is the entry in the import address table for MessageBoxW
0:000> dq main+0x18a10
00007ff7`2fcd8a10  00007ff7`2fcc1030
# This is the delay-load jumper for MessageBoxW
0:000> u main+0x1030 L0x1C
main+0x1030:
00007ff7`2fcc1030 488d05d9790100  lea rax,[main+0x18a10 (00007ff7`2fcd8a10)]
00007ff7`2fcc1037 e900000000 jmp     main+0x103c (00007ff7`2fcc103c)
# This is the tailMergeX64 function for delay-loaded user32.dll
00007ff7`2fcc103c 48894c2408      mov qword ptr [rsp+8],rcx
00007ff7`2fcc1041 4889542410      mov     qword ptr [rsp+10h],rdx
00007ff7`2fcc1046 4c89442418      mov     qword ptr [rsp+18h],r8
00007ff7`2fcc104b 4c894c2420      mov     qword ptr [rsp+20h],r9
00007ff7`2fcc1050 4883ec68        sub rsp,68h
00007ff7`2fcc1054 660f7f442420    movdqa  xmmword ptr [rsp+20h],xmm0
00007ff7`2fcc105a 660f7f4c2430    movdqa  xmmword ptr [rsp+30h],xmm1
00007ff7`2fcc1060 660f7f542440    movdqa  xmmword ptr [rsp+40h],xmm2
00007ff7`2fcc1066 660f7f5c2450    movdqa  xmmword ptr [rsp+50h],xmm3
00007ff7`2fcc106c 488bd0          mov rdx,rax
00007ff7`2fcc106f 488d0df2570100  lea     rcx,[main+0x16868 (00007ff7`2fcd6868)]
# This calls __delayLoadHelper2
00007ff7`2fcc1076 e8e1030000      call    main+0x145c (00007ff7`2fcc145c)
00007ff7`2fcc107b 660f6f442420    movdqa  xmm0,xmmword ptr [rsp+20h]
00007ff7`2fcc1081 660f6f4c2430    movdqa  xmm1,xmmword ptr [rsp+30h]
00007ff7`2fcc1087 660f6f542440    movdqa  xmm2,xmmword ptr [rsp+40h]
00007ff7`2fcc108d 660f6f5c2450    movdqa  xmm3,xmmword ptr [rsp+50h]
00007ff7`2fcc1093 488b4c2470      mov     rcx,qword ptr [rsp+70h]
00007ff7`2fcc1098 488b542478      mov     rdx,qword ptr [rsp+78h]
00007ff7`2fcc109d 4c8b842480000000 mov     r8,qword ptr [rsp+80h]
00007ff7`2fcc10a5 4c8b8c2488000000 mov     r9,qword ptr [rsp+88h]
00007ff7`2fcc10ad 4883c468 add     rsp,68h
00007ff7`2fcc10b1 eb00            jmp main+0x10b3 (00007ff7`2fcc10b3)
00007ff7`2fcc10b3 ffe0            jmp rax
00007ff7`2fcc10b5 cc              int     3
00007ff7`2fcc10b6 cc int     3
00007ff7`2fcc10b7 cc              int     3
# Displaying contents of the .pdata section (which is sorted)
0:000> dt ntdll!_IMAGE_RUNTIME_FUNCTION_ENTRY[4] main+0x1a000
[4]    +0x000 BeginAddress     : 0x1000
   +0x004 EndAddress       : 0x102a
   +0x008 UnwindInfoAddress : 0x15cd0
   +0x008 UnwindData       : 0x15cd0

-> +0x000 BeginAddress     : 0x103c
   +0x004 EndAddress       : 0x10b3
 +0x008 UnwindInfoAddress : 0x15cd8
   +0x008 UnwindData       : 0x15cd8 <-

   +0x000 BeginAddress     : 0x10b8
   +0x004 EndAddress       : 0x1162
   +0x008 UnwindInfoAddress : 0x15d18
   +0x008 UnwindData : 0x15d18

   +0x000 BeginAddress     : 0x1164
   +0x004 EndAddress : 0x1202
   +0x008 UnwindInfoAddress : 0x15ce0
   +0x008 UnwindData       : 0x15ce0

   [...]
# This is the unwind information for the tailMergeX64 function,
# stored in the .xdata section
0:000> db main+0x15cd8 L8
00007ff7`2fcd5cd8  01 18 01 00 18 c2 00 00
```
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzUWktz4ziS_jX0JcMKEnyIOvhgy6Udx7qqNma6undODhAAJXRBBBsALXl-_UaCpERJpGz3zuxD4ZBtEviQSOTzI6m1cl0JcRekD0H6eEMbt9Hm7u33Zq1uCs3f7v4ilNIBWQbhYxDet9_3FriwzMhCcCiE0ruALCHIQqX4rZLVz5nYiyALgXJuodLQVDtZcZBVqc2WOqkrKLWBIH1wG4ETHZXqqzBr8Z9ZghOZ5iJIHwOSb5yrbRDfB2QVkNVauk1TzJjeBmSl1Gv_67Y2-nfBXEBWhdJFQFZzRrIsifiCM0HnIZ2njCY5JyKmaVFmjM7pgqRF4QF4QFbL7ytc4fH5ecbqOiDxc7TIA7KA3UZU0FhZrce2uJNug9cDsuJC0TelKff_LkG8igrcRjfrDbiNtH5XICumGi4sUJzGqFIIIyvrTMNQNTO4t2AddYKjhmQFXyUz2urSAdes2YrKeR2OKUgJaqrZtp_QaUpUt40NyMpvbFU00u9Y7JmoEeh2QyuuZLW-3WdJQBaINjjuIP4Cv9BCiduCWsHhMA_6eWDEH400flMOR4KonHnzh0yVgrKp_NYsuA11eEkz6gTukv0EW1MmQBtAZQCttNsIc5gDAckRR-zptlYCFVvpSglaHmEDspihrmazWZA-wopK1RgBTgPXYDXspFJghG2UA1lBUxmhZCvn5V5oxYGLolmv8T9dQm00E9YKOxtq5Rc80a203jBGDJw1xojKqTfYUNy3gFIrpXetuqi63WmjOMhtTZmXCoespBGl3sNOFFAYvbOoCG3gN1lxvbPgz2fZmiStBtJLC4ZKPB1vkBSMcI2p0AONsBZqLSuHSzs95m_WaSM46FYKfywHI7jtjJdWUBu0aecHMUPtBoyotXEopdFbKES3O7YRHAUtBKONFUcLDsjcgqycMBVVvUOgRJXjrSd4C2n1iW6ATtGYS6sTxsKWvqHkNVA3silcvz1zFEpWGIhaoXmzrTtZmRGto5HcCoE2VDRriPIwigkBprfobRCNuVrRrP8hlaKzrW5_a7MOyMpu9O6laNYztpZBvJI8iB87vIDELEL_Qhd76LXrD3MnwOqt2Ogd2IYxITie1Fo4oAOhcUu6Md0VWlH19g9hYC0qYagTFtbUFHQtQFdMgHTdUdiPaGdDDb91-tYZiQh-CZx5qph5EoaL5L-rjg7Fa-LEqb7pHTy17lob_Sq5gK02ArhA8W1v3FZiKOhDQmsyRtRG84aJ1tmktY2YwW_H4I1XFWrJwfPzr1-HPgV1UyjJwAglqBXn8S8L259a74SxG6HUITAGhCxxeLr8D6PXhm5hJZWwQbrENYJ0WcgqSJdM0WrtUwYhcHv7KozFAO5R_D3oLkGUzsJZ1oUYatbCBfE97PPsJUtua3a7a6W-3dpX1gciIyiHreZC4dhaW7lvbz1V1lGlBH-UBm-9L-pp2P_g7k5y4uUGn58fT7Z10OdJONXobbX0xzmMlXW3sqycxpinC0z0UEo1eU4sIA_4014lcZdyIYiXnfpmG9zdYK6sHGyprAKSv2rJMe8H8w4B4Kuwlq7Fg97_FpD824_nZ3Sf54CQrjwiY_9_fXj5_u9DX4c-KoeHS8H88ZpS0HpRs-jLfaVxXoEcSpPzEuTjVgx_yoyZ15ivlj4LdG4xHkiD30FJG-WULIL4XsmCbd1MyeLiVmOFicnoLa8Eua2PN3ulHKbxw-Yn1D5VlGKipZwLjjkHDVVUHGuEro6dObF3PqGKtnhxuktX4KW4RTFktfa5sfEIxmNW2gG1VjPp09FEwewT8aEw6moGXLbm1FFcNyB5kIV__fHtl6evX15WP74tf3n6_s0bx8JXNjh6fzr6x7ffnr49vjx9W33vBnbSjxQ8WAUUAqOVLOWx2sBmoHZtIH46hO4gfcD9vVvI-3YjIKt212wj2E9h_LiLUvSaEddvbqMr8BmnK8xeWsgXVOSsfmvtDG2uDUztYUBA5ocbZI5nU_s8ADsqHdaUAwn-1h1se9Lx_WjppzH-wSKZhWFAUrSPNJpHBIo3d1ZFPmv9E0Uo-_J30AjY_oRralxrIh4Zm663kXXPJA33URKGIc9piXICgMgXYczLsizBf_yC3bAoS5KzeUXSzcuysMzKMCFJO2-rX_kfFGC_3fo6Yr_d7rThUDvf0xlb45GfgS1OwBJGkig8A4vGwSAgDyhmOIJanqCmCUnIOSq5ikqOqO33SjcVh2jkNNDWdePGTlxWB4_3hjHryxnXVSdY3LVxQlvhC9dCQEmVFT5fO_l6NIyxkPTd9K7mWxOwwvoES5bQhpAK65nHYv1hhyExeI-Wtotj2LB1Bie3WNUfmoe2pUMTHSbC9iSC-D4MQ3Q__keXQR-i-aIM4TnqhoRhOC_LLMhCWnLh78HpRc7zeT4u1zFswu_Ntu76oWtyNL0YCArP4T7qoLvL4d6v1utpRBJI8pyH6TyiEd4FUIKCofuALIP04QDTbjMg-cgOMXQdrPVihRLEIuw-8Pu2br3qKF6eXMJyvIywo2oa5qpjhkBVHRWIeeUs_Y0sAWkEdWM3KJJh-6lxKaQETj5-kuGTMzJIojS8mIHL5FNzcpwzkGcxNZDimcWCJXkPbpsCMBKRZZJvpqYJHzjm47FtJKaRpY9542iLuEMbDW7naOQhCjcHzFFfQcxFhzka2i4xyQCTTGGWPSYjSfw-ZjzAjCcwaYoHUPDB-W71qzcHskTXmZjW-RrPozk7-lpve2f-lqbxmL_h5aG_HXwDQ7iFlxfvApjs_yJULcyUXmg5niGHvolpcsQ3ffYkU9ZZJO-l0GsZ9AJt8V4OvWZtE5jlexn0mrWNY7K0xxwzs3gKM76KWXg_Z8nRzynnrb284-usxFiyOIk_up4OKTzC8fn5-KvxiseQ0uOwyVjIEziVpJNlMtzyFMpSnATPPm0cnAvN_lHaGpsfrCgbpYDpyonK2b5FaRuFQ60SkHy3kWyDWcRq4wQ_GvEgsTto2TkSvTx9vf-3Ly_n_cXLl2-__PXvQYoOQvIgfRw4DCa6TsCeoO27YfAj0OsfxFpW9125gR8s6zATRsX54AS-VHw49Dg4Ts4H5_DDF2tPVan7Oe3oKC3LfGr4IyppiD0Y3c15X_RLaa6InrBT3H-d4B_R-kGacdH7YfNJOf6M2BMCt9Eca1y70Y3iWDjTqqtX_fW2M56qgQKSD_fZy97WUofnWiTu-_Gx8l5U3Pq1PqrDj578Bw_9o8f9Dqch9rVgTvDDo5C2dtSVAF04Kits6HvKCWOIbzm-_u3Xpaftgywc8k7YGwlPqow2HMe-vJ8EsyBddkzPgNkZMDnnzM3jl-f7vz9_v38cYW6OD8UCkv81IAt4qpgR_umYgmdZ_RQGfu1Z1WQWx7M4yuJw1km21PWbkeuNn7_E-UfEpTa1Nm1TDXCvFPiRFhUnzKvg77RqB0rOE2a3TPU6a59I9UQnrXjL7nVckqVbAU5u_UMuqrpHh08BmW99928bI2C38aOlBa6FhabiojX_jdaTh3HZ_X2c7-vF72k6VtfXzgw39M7RjartqYJCuw0waoVFBYy12RZqPILKHZx-6uFx_IAq67rv49NXqqwGLstSGAtW4amqt_bhVe8KLb1XvI1wrZN8mBWiar3Fn6Nnw_6fU1ck_t-jrloOptL_FBJmwklRV7K6IFT-D9An4T7KaXTaZc6DLCQl43jjwJ50F1kUxuE_nT0J9wjr-ZPlGX_i1zvjT4aydPwJX8wXYXSFP_GbOTZTwz2O8CeDFeZX-ZMojNklLM5j_yL-ZLgEbn6BrVmYH_vgPy77nLztqc9r_iNUEnko7Mii8AiFnxG4A5lw3nUMADNIWL5IEDB_HzDvAfMpvKLFY33LeBXvwEycdVxHvDTsSJxshMTJzhq7wbSkp10-T5GMOhli0gGV80mKJJrAzMIBlZO8j5lMUjkDzGxA5aTvY6aTVM4Ak32CyhlMKzsqpyTp_Oj2ME7lZHmWj7g-Xv7zVM5RmHkGIhcYizynBKNUTpKOhokkZedUzgC46MmXMWOboHJGiZIjZh4NCJ0LY5skdEaIkgHmfEDoXBjbJKGTXMXkA0LnwtgmCZ30GuYi9saGO5-fxZDWakYiyfwqoGcUC9z2_CzItRY8BphfA-QY5Yo8IUnep5wDYj4OmF-TkKYtICNJnp8DLiYAr0lIeUeKZfkZGzYdNIsIRBFeEEqDHFrEozm0iKedo4hHWarpmFGkwBicfGTl_O-p6FRkOOXdUfP3gM_Ysv9poiw5JcnokCTz9z7A00SHSR-gmKKQ0M-wNYx_mHUYjm6_b9tXQd4TP75OM52KX8QfZ8gY_wxDxngOQby8_SRNFoXFxSrT8kcZ-Yz6efQxsuww7uNiR9l1YrIbRsJPCczEp-xFhOdin5LD5_X55AsxE6X7CbPXvd3adWft6y-9a186cTHwS28bz6MFMPf3IIwgyvE7DPEPRvCP8JyJu-F3MV_EC3oj7qJsHmVxGCXhzeYuJDFJFgnJo4iRPOZpXCZiMWcRi2mal-WNvCMhIREhURQnhISzmC8IyficJRktRESDJBRbKtVMqdftTJv1jX_18S5dZPHiRtFCKOtf7yekErv2vciAkCB9vDF3_t35olnbIAmVtM4eUZx0StwNCZCWcMJevn_BZ-RUdHXyYqU_pOEBeRZm-Er94P2sm8aou0-_8O93ZAOy8jv-rwAAAP__Tj9Hzw">