Skip to content

tolerate undefined data symbols in emitted modules - #59

Open
svieujot wants to merge 5 commits into
WorldSEnder:mainfrom
svieujot:main
Open

tolerate undefined data symbols in emitted modules#59
svieujot wants to merge 5 commits into
WorldSEnder:mainfrom
svieujot:main

Conversation

@svieujot

Copy link
Copy Markdown

An input module can carry undefined data symbols: rustc incremental builds sometimes leave references to promoted anonymous globals (anon..N.llvm.) whose content-derived names changed between compilations, and wasm-ld links them under --allow-undefined. The emit phase treated any such symbol in a module's included set as fatal.

An undefined symbol has no definition to place or relocate, so skip it with a warning and keep its references exactly as the linker resolved them - the same treatment the zero-sized symbol case already gets.

We have this problem with cargo-leptos:

cargo-leptos runs this emit phase in-process for every watch --split rebuild, so without this fix a rustc incremental build that leaves undefined data symbols in the module (dangling references to renamed anon..llvm. promoted globals, accepted by wasm-ld under --allow-undefined) makes the whole front build fail with "Expected data symbol dep node to ref to defined data symbol" until the user wipes the target directory for a clean build.

An input module can carry undefined data symbols: rustc incremental
builds sometimes leave references to promoted anonymous globals
(anon.<hash>.N.llvm.<id>) whose content-derived names changed between
compilations, and wasm-ld links them under --allow-undefined. The emit
phase treated any such symbol in a module's included set as fatal.

An undefined symbol has no definition to place or relocate, so skip it
with a warning and keep its references exactly as the linker resolved
them - the same treatment the zero-sized symbol case already gets.
@WorldSEnder

WorldSEnder commented Aug 31, 2026

Copy link
Copy Markdown
Owner

I don't understand the full context here. Do you have a reproducing example - non-synthetic preferably - so a test case can catch this?
Specifically, just because the symbol exists doesn't imply the dependency pass picks it up to be included in an output module, for this it should be referenced by a dependency. Data dependency in this case would mean that something depends on the address of that (undefined) data symbol, which it the reason the code bails: we move around memory a bit to keep memory of each split module linear without holes. What's the result of ignoring that the undefined symbol might have changed address and does this require a fixup?

Builds a minimal splittable module whose symbol table carries an
undefined data symbol referenced by a R_WASM_MEMORY_ADDR_LEB relocation
from a function kept in the main module - the shape rustc incremental
builds produce via renamed promoted anonymous globals, linked by
wasm-ld under --allow-undefined.

Asserts the transform succeeds, that the defined data symbol next to it
is still relocated to the output segment base (proving the relocation
plumbing in the test attaches to the dependency pass), and that the
undefined reference keeps the linker-resolved address 0. Fails against
the previous emit code with "Expected data symbol dep node to ref to
defined data symbol".
@WorldSEnder

WorldSEnder commented Sep 1, 2026

Copy link
Copy Markdown
Owner

@svieujot does rust-lang/rust#81280 look relevant to you?

My current assumption is that you are more likely seeing the result of a compiler bug, and I would rather have that tracked upstream somewhere before masking it here and seeing more downstream issues as a result.

The producer of these symbols is tracked as rust-lang/rust#81280:
incremental builds - particularly interrupted ones - reuse objects that
reference promoted anonymous globals whose content-derived names changed.
Name the issue and the remediation (a clean rebuild of the defining
crate) in both the code comment and the warning, so the tolerance does
not hide the compiler bug from anyone who hits it.
@svieujot

svieujot commented Sep 1, 2026

Copy link
Copy Markdown
Author

I added an additional commit with a unit test to reproduce the problem (it needs to be cherry picked on a branch without the fix).

And yes, rust-lang/rust#81280 is exactly it.
I agree it's a compiler bug and shouldn't be hidden. The branch now cites the issue in the code comment and in the warning itself, which names the symbol, points at rust-lang/rust#81280, and states the remediation (a clean rebuild of the defining crate).

I believe we should still have this prevention in place as the issue has been open since 2021 and has no timeline.

@WorldSEnder

WorldSEnder commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Do you manually have to pass --allow-undefined in your workflow or is this passed by the compiler? I ask because I know that this changed recently and has led to another (unrelated) issue. Hence, what compiler version are we talking about?

I would prefer an test case that just links to an undefined extern "C" symbol and triggers the same behaviour instead of this very constructed "hand-built" wasm. Assuming the same error gets triggered in this case. It's probably futile to try to detect --allow-undefined from the wasm file. Hence, the tool should be able to handle this output anyway.

So, perhaps we can just fix the overall behaviour with undefined symbols and the fix then doesn't have to be special.

Sylvain Vieujot added 2 commits September 2, 2026 18:46
An undefined data symbol has no definition, so there is nothing to place
and no address to relocate; the linker already resolved its references
(to 0 under --allow-undefined) and reloc_value leaves such references
untouched. Dropping the symbol from the emit state is the general rule,
not a workaround for one producer, so the warning that pointed at the
rustc incremental-build issue becomes a trace and the comment states
the invariant. The hand-built module test goes away in favour of an
integration fixture that links a real undefined extern data symbol.
The crate references an extern data symbol that nothing defines and links
under --allow-undefined (re-added by its build script, since rustc no
longer passes the flag - rust-lang/rust#149868), so wasm-ld keeps the
symbol undefined in the symbol table, resolves the reference to address 0
and leaves a relocation against it. The split must accept the module and
the reference must keep the linker's address; the fixture also carries a
split point so the whole pipeline runs. This is the shape rustc
incremental builds leave behind under rust-lang/rust#81280.

Before the previous commit the split failed with 'Expected data symbol
dep node to ref to defined data symbol'.
@svieujot

svieujot commented Sep 2, 2026

Copy link
Copy Markdown
Author

Indeed, I did manually have to pass --allow-undefined.

I now removed it and will report back.

@WorldSEnder

Copy link
Copy Markdown
Owner

Oh wow, your test case is a fun way to trigger undefined behaviour without writing unsafe (note it is not needed for addr_of! and could be removed). Has to do with weak linkage still being unstable and the fact that the optimizer assumes that extern symbols are not null without that linkage attribute as far as I can tell.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants