llvm / llvm/llvm-project

[LLD][COFF] /OPT:REF strips symbols referenced only via __imp_ local imports; IAT slots are written as ImageBase with no diagnostic

Open
#220,370 2 comments 0 reactions 0 assignees View on GitHub
lld:COFF
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

The below AI analysis is the first of two issues that are preventing DTLTO from working with our DLL setup and crashing. I'm currently using `/OPT:NOREF` and `/ignore:4217` as a workaround but the generated code is not optimal. I'll link the other issue in the comments.

---

When an undefined __imp_X is fulfilled by a locally defined X (local import), lld-link's /OPT:REF does not treat the local import as a reference to X. If X has no other live reference, its section is stripped. The local import pointer is still emitted and contains ImageBase + 0, with a base relocation. There is no diagnostic for the stripped definition; the "locally defined symbol imported" warning is emitted as usual, in both the broken and working configurations. Calling through the slot jumps to the image header.

MSVC link.exe keeps the definition for identical inputs.

Repro (no LTO):

```
// def.cpp
extern "C" __declspec(noinline) int target(int x) { volatile int v = x; return v * 7; }
extern "C" __declspec(noinline) int keeper(int x) { volatile int v = x; return v + 1; }
```

```
// user.cpp
extern "C" __declspec(dllimport) int target(int);
extern "C" int keeper(int);
extern "C" int entry() { return target(1) + keeper(2); }
```

`clang-cl -c -O2 def.cpp user.cpp`
`lld-link /entry:entry /subsystem:console /opt:ref /out:t.exe def.obj user.obj`

Result: target's body is absent from .text; entry calls through a slot containing 0x140000000 (ImageBase), base-relocated:

`llvm-objdump -d --section=.text t.exe` -> callq *0x…(%rip) # slot RVA
`llvm-objdump -s -j .rdata t.exe` -> slot bytes: 00000040 01000000

Comparisons on the same objects:
- lld-link /opt:noref (or /debug, which implies it): body present, slot correct.
- MSVC link /opt:ref /nodefaultlib: body present, slot correct, LNK4217 warning.

All variants warn locally defined symbol imported: target; only lld with /opt:ref strips the definition.

Analysis (current main):

- SymbolTable::resolveRemainingUndefines replaces `__imp_X` with a DefinedLocalImport wrapping the defined X and queues its LocalImportChunk. This runs before markLive (Driver.cpp).
- MarkLive.cpp addSym handles DefinedRegular, DefinedImportData, and DefinedImportThunk; there is no case for DefinedLocalImport, so a live section referencing `__imp_X` marks nothing.
- The Writer emits local import chunks unconditionally. LocalImportChunk::writeTo writes sym->getRVA() + imageBase; a discarded symbol's RVA is 0. getBaserels registers a relocation for the slot unconditionally.

Suggested fix: add a DefinedLocalImport case to addSym that marks the wrapped Defined live. The wrapped symbol is currently only stored in the private LocalImportChunk::sym, so an accessor is needed.

This pattern occurs at scale with distributed ThinLTO, where backends emit `__imp_` references for symbols that resolve within the link unit (). In one large mixed application, 26,910 of 72,641 import slots contained the image base; the link reported no errors.

Verified with lld 23.1.0; the cited code matches current main.

Contributor guide

Open the contributing guide

Research direction

Start with the no-LTO repro using clang-cl and lld-link, then inspect MarkLive.cpp's addSym handling and the LocalImportChunk path described in the issue; Driver.cpp identifies when resolution precedes markLive. Compare /opt:ref with /opt:noref and confirm the target body remains present and the import slot contains the target address rather than ImageBase.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.