Skip to content

fix(build): link libc after the IREE archives so integration tests link - #1275

Merged
inureyes merged 1 commit into
mainfrom
fix/issue-1274-iree-link-args
Aug 21, 2026
Merged

fix(build): link libc after the IREE archives so integration tests link#1275
inureyes merged 1 commit into
mainfrom
fix/issue-1274-iree-link-args

Conversation

@inureyes

Copy link
Copy Markdown
Member

Summary

Repeat -lc after the IREE archives in the IREE_CUDA_HOME link recipe, so integration tests built with xla-iree link on aarch64.

Before this, any such test failed with:

/usr/bin/ld: libiree_runtime_unified.a(call.c.o): undefined reference to symbol '__stack_chk_guard@@GLIBC_2.17'
/usr/bin/ld: /lib/ld-linux-aarch64.so.1: error adding symbols: DSO missing from command line

Why the obvious reading of that error is wrong

The error names the dynamic linker, which invites the conclusion that the fix is to link it, or that libc is missing entirely. Neither holds:

  • libc.so.6 does not define __stack_chk_guard. It is UND there. The definition is in ld-linux-aarch64.so.1, reachable only through libc's DT_NEEDED.
  • libc was already on the link line. rustc-link-arg can only append, so the IREE archives land after rustc's own -lc. call.c.o is compiled with the stack protector, so its reference to __stack_chk_guard appears at a point where no libc follows it.

Repeating -lc after the archives puts libc where the pending reference can reach it, and ld then resolves through libc's DT_NEEDED as it does for any ordinary C program.

The fix is minimal because it was ablated, not guessed

The first attempt here added -Wl,--copy-dt-needed-entries, on the theory that the transitive resolution was what ld was refusing. That flag alone does not work: rustc appends our args after its -lc, so the flag lands at argument 80 while -lc is at argument 29, and it only governs inputs that follow it.

Ablating each half on the real link:

configuration result
trailing -lc only links
-Wl,--copy-dt-needed-entries only fails, same error
both links

So the policy flag is not shipped. It would have been a global relaxation that can hide a genuinely missing -l elsewhere in the same link, in exchange for nothing.

Verification

Four targets linked, covering both previously-failing and previously-passing cases:

target features before after
molmo2_xla_vision_parity cuda,xla-iree fails links
cli_help_consistency cuda,xla-iree fails links
molmo2_xla_vision_parity xla-diagnostics links links
chat_template_kwargs cuda,xla-iree links links

Plus cargo check --features cuda --all-targets clean, and cargo fmt --all -- --check.

The two previously-passing rows matter as much as the failing ones: the failure was never uniform across targets, so a fix verified only on a failing target could have regressed the rest.

Not changed

The IREE_DIST and macOS recipes are untouched. IREE_DIST emits a nearly identical group and is likely affected the same way, but this host has neither an IREE_DIST tree nor macOS, so neither can be confirmed by an actual link. Issue #1274 asks for those recipes to be either unchanged or verified, and unverified link changes do not belong in this file.

Also in this change

link_args.insert(5, ...) is gone. Adding a single entry ahead of it would have moved the vendored printf archive out of the --start-group block with no error, so the conditional entry is now pushed positionally. Each library in the group also carries the reason it is present, which #1274 asks for.

Left open

Why the failure was target-dependent and feature-dependent is not fully explained. chat_template_kwargs linked under cuda,xla-iree while molmo2_xla_vision_parity did not, and the same molmo2_xla_vision_parity linked under xla-diagnostics. Both working binaries carry ld-linux-aarch64.so.1 as an explicit DT_NEEDED and the failing links do not, so something in those link lines was already pulling it in. The surgery default feature was suspected and is neither confirmed nor ruled out. This fix removes the ordering dependency for every target, so the asymmetry stops mattering in practice, but it is recorded here rather than left as folklore.

Closes #1274

Any integration test built with `xla-iree` failed to link on aarch64:

    /usr/bin/ld: libiree_runtime_unified.a(call.c.o): undefined reference to
      symbol '__stack_chk_guard@@GLIBC_2.17'
    /usr/bin/ld: /lib/ld-linux-aarch64.so.1: error adding symbols:
      DSO missing from command line

`rustc-link-arg` can only append, so the IREE archives land after rustc's own
`-lc`. `call.c.o` is built with the stack protector and references
`__stack_chk_guard`, and that reference therefore appears with no libc left
after it. The symbol is not in libc itself (`libc.so.6` lists it as UND); the
definition is in `ld-linux-aarch64.so.1`, reachable through libc's DT_NEEDED,
which is why ld names the dynamic linker rather than libc. Repeating `-lc`
after the archives puts libc where the pending reference can reach it.

Established by ablation rather than assumed: the trailing `-lc` alone fixes the
link, and `-Wl,--copy-dt-needed-entries` alone does not, because that flag only
governs inputs following it and rustc appends our args after its `-lc`. The
policy flag is therefore not shipped; it would have been a global relaxation
that can hide a genuinely missing `-l` elsewhere in the same link, for no gain.

Verified by linking four targets: `molmo2_xla_vision_parity` and
`cli_help_consistency` under `cuda,xla-iree` (both previously failed),
`molmo2_xla_vision_parity` under `xla-diagnostics` and `chat_template_kwargs`
under `cuda,xla-iree` (both previously passed, still pass), plus
`cargo check --features cuda --all-targets` for the default build.

The `IREE_DIST` and macOS recipes are left alone. `IREE_DIST` emits a nearly
identical group and is likely affected the same way, but neither an
`IREE_DIST` tree nor a macOS host is available here to confirm it by an actual
link, and this file should not carry unverified link changes.

Also drops the `link_args.insert(5, ...)` magic index. Adding one entry ahead
of it would have moved the vendored printf archive out of the group silently,
so the conditional entry is now pushed positionally, and each library in the
group carries the reason it is there.
@inureyes inureyes added type:bug Bug fixes, error corrections, or issue resolutions priority:medium Medium priority area:core mlxcel-core: MLX FFI, primitives, KV cache, layers status:review Under review labels Aug 21, 2026
@inureyes inureyes added status:done Completed and removed status:review Under review labels Aug 21, 2026
@inureyes
inureyes merged commit 0aea101 into main Aug 21, 2026
8 checks passed
@inureyes
inureyes deleted the fix/issue-1274-iree-link-args branch August 21, 2026 14:00
inureyes added a commit that referenced this pull request Aug 21, 2026
`TECHNICAL_REPORTS/.keep-reports` opts this repository into tracking reports,
and the report is meant to land inside the squash merge rather than trail it.
These three merged without one, so they are added here after the fact.

The reports carry the parts that do not survive in a diff. #916's recorded
blocker was a TF32 artifact in the MLX oracle rather than an emitter defect,
and its gate had only ever run on a single-crop solid-color image. #1275's
first fix attempt shipped a redundant global linker flag until each half was
ablated separately. #1273 is trivial as a change and only interesting because a
compile error sat in `main` unseen, which is what #1270 tracks.

Both language versions are included, matching the existing convention.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:core mlxcel-core: MLX FFI, primitives, KV cache, layers priority:medium Medium priority status:done Completed type:bug Bug fixes, error corrections, or issue resolutions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: Integration tests cannot link with --features cuda,xla-iree

1 participant