Skip to content

[build] F: Drop -Wl,--allow-multiple-definition (no longer needed)#75

Closed
esaurez wants to merge 1 commit into
feat/link-libnvx_crt0-for-PR11b-compat-upstreamfrom
feat/drop-allow-multiple-definition-upstream
Closed

[build] F: Drop -Wl,--allow-multiple-definition (no longer needed)#75
esaurez wants to merge 1 commit into
feat/link-libnvx_crt0-for-PR11b-compat-upstreamfrom
feat/drop-allow-multiple-definition-upstream

Conversation

@esaurez

@esaurez esaurez commented Jun 9, 2026

Copy link
Copy Markdown

Important

DO NOT MERGE until ALL of the following have landed and propagated:

  1. nanvix/nanvix#2487sys-ffi crate split that removes 36 of 37 duplicate strong symbols.
  2. nanvix/newlib#17 — weakens _do_start in newlib's crt0.S so libnvx_crt0's strong override wins (the 37th duplicate).
  3. nanvix/toolchain-gcc#14 — pins the new newlib commit and republishes the toolchain image (DRAFT until [ci] E: Update nanvix workflow refs to v1.15.0 #17 merges).
  4. The Docker image at ghcr.io/nanvix/toolchain-gcc has actually been republished from [ci] E: Pin nanvix to v0.12.538 #14 with the new newlib bundled in.
  5. The parent E: PR that this PR is stacked on top of (the one that added the libnvx_crt0.a wildcard link entry with the safety flag).

If merged early, the link step for the test ELF will fail with multiple definition of __kcall_* errors against the unpatched toolchain image.

Summary

Drops -Wl,--allow-multiple-definition from the test ELF link line. The flag was added by the parent E: PR as a temporary safety guard while 37 duplicate strong symbols were still present in both libnvx_crt0.a and libposix.a. Once those are resolved structurally, the flag is no longer needed and the SSE-aligned _do_start from libnvx_crt0.a finally wins at the entry point.

Why the flag was needed

libnvx_crt0.a and libposix.a both contained byte-identical duplicate strong definitions of:

  • 34 sys-crate __kcall_* FFI wrappers
  • _do_exit_thread
  • _do_start_thread
  • _do_start (which also collides with newlib's prebuilt crt0.o)

-Wl,--allow-multiple-definition told ld to silently take whichever copy it saw first. Since the copies were byte-identical (same Rust source, same --release profile, same compiler), the outcome was semantically equivalent to deduplication -- but the flag also hides legitimate symbol collisions, which is why it was always intended to be temporary.

How the duplicates have been resolved

  • 36 of 37 -- nanvix/nanvix#2487 introduces a sys-ffi crate that owns all #[no_mangle] re-exports of sys::kcall::*. Only libposix.a links sys-ffi, so libnvx_crt0.a no longer duplicates the __kcall_* / _do_exit_thread / _do_start_thread symbols.
  • The 37th (_do_start colliding between libnvx_crt0.a and newlib's prebuilt crt0.o) -- nanvix/newlib#17 declares newlib's stub .weak so libnvx_crt0's strong SSE-aligned _do_start wins cleanly without the flag.
  • Toolchain image republish -- nanvix/toolchain-gcc#14 bumps the pinned newlib commit and triggers a republish of ghcr.io/nanvix/toolchain-gcc so all consumers get the new newlib.

Until ALL three changes land AND the toolchain image is republished, this PR's link step will fail. That is why this is a deliberately-separate follow-up PR rather than being squashed into the parent E: PR -- it gives reviewers the option to merge the E: compat fix immediately (preserving backward compatibility with both pre-cutover and post-cutover sysroots, with the safety flag) and circle back to drop the flag in a single small PR once the toolchain catches up.

Side benefit: SSE-aligned _do_start at the entry point

With the flag in place, ld silently takes newlib's 9-byte unaligned _do_start (crt files come first in the gcc spec). Dropping the flag lets libnvx_crt0's 18-byte SSE-aligned variant -- which aligns %esp to a 16-byte boundary before the CALL instruction, as required by the i386 SysV ABI for SSE-using callees (movaps / movdqa) -- finally win. This fixes a latent SSE-alignment bug present in shipping binaries today.

Validation

Built end-to-end against a locally-rebuilt toolchain image carrying the nanvix/newlib#17 change. Test ELF links cleanly without --allow-multiple-definition. Disassembling the resulting binary confirms _do_start at the entry point is the SSE-aligned variant:

<_do_start>:
    and    $0xfffffff0, %esp      ; 16-byte align (was missing)
    mov    %esp, %ebp
    sub    $0x8, %esp              ; alignment padding (was missing)
    push   %ecx
    push   %edx
    call   _start

… `-Wl,--allow-multiple-definition` flag was added in the previouscommit to silently coalesce 37 duplicate strong symbols that appearedin BOTH `libnvx_crt0.a` and `libposix.a`: * 34 `__kcall_*` FFI wrappers (`__kcall_lock_mutex`, `__kcall_signal_cond`, `__kcall_send`, ...) * `_do_exit_thread` (thread-exit handler) * `_do_start_thread` (asm thread-bootstrap stub) * `_do_start` (process-entry stub)These have now been resolved structurally upstream: * 36 of 37 by the `sys-ffi` crate split that moves the `#[no_mangle]` FFI exports out of `sys` into a dedicated crate that only `libposix.a` links: nanvix/nanvix#2487.  * The remaining `_do_start` by declaring newlib's stub `.weak` so    libnvx_crt0's strong SSE-aligned override wins cleanly:    nanvix/newlib#17, plus the toolchain image republish in    nanvix/toolchain-gcc#14.Once all three of the above have merged and the toolchain image isrepublished with the new newlib commit, the link line no longerneeds `--allow-multiple-definition`.MERGE ORDER: do NOT merge this PR until ALL of the following havelanded:  1. nanvix/nanvix#2487 (sys-ffi crate split) -- already filed.  2. nanvix/newlib#17   (.weak _do_start)    -- already filed.  3. nanvix/toolchain-gcc#14 (republish image with new newlib)     -- already filed; draft until newlib#17 merges.  4. The toolchain image at ghcr.io/nanvix/toolchain-gcc has been     republished from #14 with the new newlib commit.  5. The parent E: PR adding the libnvx_crt0.a wildcard link     entry, which this PR is stacked on top of.If merged early, the .so / test ELF link step will fail with"multiple definition of __kcall_*" errors.Validated end-to-end against a locally-rebuilt toolchain imagecarrying the newlib #17 change: the resulting ELF's `_do_start`entry point is libnvx_crt0's SSE-aligned variant (`and $0xfffffff0,%esp ; sub $0x8, %esp ; push ; push ; call _start`) -- the latentSSE-alignment bug present in shipping binaries today is fixed.Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@esaurez esaurez force-pushed the feat/link-libnvx_crt0-for-PR11b-compat-upstream branch from a2ad98f to 6117813 Compare June 9, 2026 21:03
@esaurez esaurez force-pushed the feat/drop-allow-multiple-definition-upstream branch from a42c8b8 to 45cc9a7 Compare June 9, 2026 21:03
@esaurez

esaurez commented Jun 9, 2026

Copy link
Copy Markdown
Author

Folded into the simplified main PR #74. The original two-PR design (add the flag in E:, drop it in F:) assumed a transitional period where the link line needed to work both before and after the toolchain image was republished. We're now landing this whole work only after Wave 1 (nanvix/nanvix#2487) + Wave 2 (nanvix/newlib#17 + nanvix/toolchain-gcc#14) have merged and the toolchain image has been republished, so the safety flag was never needed in the first place. The main PR has been rewritten as a single straightforward change that adds libnvx_crt0.a to the test ELF link line without --allow-multiple-definition.

@esaurez esaurez closed this Jun 9, 2026
@ppenna ppenna deleted the feat/drop-allow-multiple-definition-upstream branch June 11, 2026 08:21
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.

1 participant