Skip to content

[build] E: Link libnvx_crt0.a into test ELF for PR-11b compatibility#2

Closed
esaurez wants to merge 8 commits into
nanvix/v2.12.9from
feat/link-libnvx_crt0-for-PR11b-compat
Closed

[build] E: Link libnvx_crt0.a into test ELF for PR-11b compatibility#2
esaurez wants to merge 8 commits into
nanvix/v2.12.9from
feat/link-libnvx_crt0-for-PR11b-compat

Conversation

@esaurez

@esaurez esaurez commented Jun 8, 2026

Copy link
Copy Markdown
Owner

Summary

PR-11b (nanvix/nanvix#2453) moves the _start entry point out of libposix.a into a new libnvx_crt0.a. Consumers that link only libposix / libc fall back to newlib's weak default _start and hang at startup with no diagnostic output.

This PR adds libnvx_crt0.a to the test ELF link line in .nanvix/Makefile.nanvix. Uses $(wildcard ...) so the path is only included when the file exists, keeping the Makefile compatible with both:

  • Pre-PR-11b releaseslibnvx_crt0.a absent; _start still in libposix.a. Wildcard returns empty, link line unchanged.
  • Post-PR-11b releaseslibnvx_crt0.a present; libposix.a no longer carries _start. Wildcard expands, _start is provided.

Why this matters now

Without this change, the next nanvix release that includes PR-11b will silently break this repo's functional test — it would still build but hang at startup with no diagnostic output.

What's in this PR

Two commits on .nanvix/Makefile.nanvix (3-line net change):

  1. [build] E: Link libnvx_crt0.a into test ELF for PR-11b compatibility — adds $(wildcard $(SYSROOT_PATH)/lib/libnvx_crt0.a) to the TEST_ELF link rule, inside the existing --start-group, ahead of $(NANVIX_LIBS). Initially landed with -Wl,--allow-multiple-definition as a safety flag to coalesce duplicate strong symbols (__kcall_*, _do_exit_thread, _do_start_thread, _do_start) that appeared in both libnvx_crt0.a and libposix.a once PR-11b forced them into the same link line.

  2. [build] F: Drop -Wl,--allow-multiple-definition (no longer needed) — the safety flag is no longer required after the upstream cleanup chain (sys-ffi crate split for libposix.a + .weak _do_start for newlib's crt0.o) eliminates all 37 duplicate strong symbols at the source. The cleanup chain is a companion to this PR series:

    • sys-ffi crate split (resolves 36 of 37 duplicates: __kcall_* ×34, _do_exit_thread, _do_start_thread)
    • newlib _do_start weakening (resolves the 37th)
    • toolchain image republish (picks up the new newlib)

How to merge this PR independently of the cleanup chain

If you want to merge this PR before the cleanup chain lands and the toolchain image is republished, revert the F: fixup commit and keep -Wl,--allow-multiple-definition in the link line. The flag is purely defensive (silently coalesces byte-identical duplicate strong symbols, which is safe but hides legitimate collisions). Functional behavior of the test ELF is unchanged either way:

  • With the F: fixup commit + the new toolchain image → cleanest link line, no flag.
  • Without the F: commit (flag retained) → no toolchain dependency, merges immediately.

Validation

End-to-end validated against a locally-built nanvix sysroot that includes PR-11b:

  • nm confirms the resulting test ELF resolves _start from libnvx_crt0.a (not newlib's weak fallback).
  • The test ELF boots through nanvixd.exe to test_libxml2: PASS.
  • With the F: fixup commit applied (against a sysroot built from the cleanup chain), the link line drops --allow-multiple-definition and the resulting binary uses libnvx_crt0's SSE-aligned _do_start at the entry point (verified by disassembly: and $0xfffffff0, %esp; sub $0x8, %esp; ...) — fixing a latent SSE-alignment bug that was previously masked when newlib's 9-byte unaligned _do_start won the link.

Related work (informational)

The same one-line fix is being filed against every port-library repo that builds a test ELF on a Nanvix sysroot (nanvix/posix-tests, nanvix/libxml2, nanvix/libxslt, nanvix/lxml, nanvix/libffi, nanvix/openssl). Each PR is functionally independent — they can be reviewed and merged in any order.

@esaurez

esaurez commented Jun 8, 2026

Copy link
Copy Markdown
Owner Author

Validation update — squashed in fixup commit

End-to-end validation against a locally-built nanvix-dev sysroot with PR-11b surfaced a hard linker error in the original --whole-archive approach: libnvx_crt0.a and libposix.a both bundle the sys crate (Rust monomorphizes the FFI __kcall_* symbols into both archives), so --whole-archive on libnvx_crt0 produces "multiple definition of _kcall*" errors against libposix.

Pushed fixup commit that switches to:

-Wl,--allow-multiple-definition
-Wl,--start-group
  $(wildcard $(SYSROOT_PATH)/lib/libnvx_crt0.a)
  libposix.a libc.a [libm.a]
-Wl,--end-group

With libnvx_crt0.a inside the start-group ahead of libposix.a, the linker resolves the entry-point _start from libnvx_crt0 (strong T symbol) over newlib's weak _start fallback. The remaining __kcall_* duplicates from the two sys monomorphizations are byte-identical (same Rust source, same release profile, same compiler options) and silently coalesced by --allow-multiple-definition.

The $(wildcard ...) trick still preserves backward compatibility: on pre-PR-11b sysroots libnvx_crt0.a is absent, the wildcard expands to empty, and the link line collapses to the original behavior.

Validated by building + running the test ELF via nanvixd.exe against a locally-built nanvix-dev sysroot that includes PR-11b. All 6 repos (posix-tests, libxml2, libxslt, lxml, libffi, openssl) now build cleanly and their test binaries boot to PASS.

Follow-up upstream issue (not addressed here): the duplicate __kcall_* symbols in libnvx_crt0 are a libnvx_crt0 crate issue — that crate shouldn't be re-exporting sys-crate FFI symbols. Proper fix is to teach nvx-crt0 to import the sys crate without re-exporting its #[no_mangle] extern "C" symbols (e.g. hidden visibility, link section trick, or splitting sys into FFI/non-FFI halves). Once that lands, the --allow-multiple-definition flag can be removed.

PR-11b (nanvix/nanvix#2453) moves the `_start` entry point out of
`libposix.a` into a new `libnvx_crt0.a`.  Consumers that link only
libposix + libc fall back to newlib's weak default `_start` and hang
at startup with no diagnostic output.

Add `libnvx_crt0.a` to the test ELF link line, placed inside the
existing `--start-group` ahead of `libposix.a` so the linker resolves
the entry point from libnvx_crt0's strong `_start` (T symbol) over
newlib's weak fallback.

The path is wrapped in `$(wildcard ...)` so the same Makefile works
on both pre-PR-11b and post-PR-11b sysroots:

  - Pre-PR-11b: libnvx_crt0.a does not exist; wildcard expands to
    empty; `_start` continues to come from libposix.a as before.
  - Post-PR-11b: libnvx_crt0.a is present; wildcard expands; the
    linker pulls `_start` and the rest of the crt0 trampoline.

`-Wl,--allow-multiple-definition` is required because libnvx_crt0
and libposix both bundle the `sys` Rust crate, and Rust monomorphizes
its `#[no_mangle] extern "C" __kcall_*` FFI symbols into BOTH static
archives as strong `T` definitions.  The two copies are byte-identical
(same Rust source, same `--release` profile, same compiler), so it is
safe to let the linker silently take whichever it sees first.

The duplicate-symbol issue is a libnvx_crt0 crate bug; once that is
fixed (`libnvx_crt0` should not re-export sys-crate FFI symbols), the
`--allow-multiple-definition` flag can be removed.

Validated by building the test ELF and booting it through `nanvixd`
against a locally-built nanvix-dev sysroot that contains PR-11b.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The `-Wl,--allow-multiple-definition` flag was added in the previous
commit to silently coalesce 37 duplicate strong symbols that appeared
in 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.  See nanvix/nanvix#TBD (esaurez/nanvix#30).
  * The remaining `_do_start` by declaring newlib's stub `.weak`
    so libnvx_crt0's strong SSE-aligned override wins cleanly.
    See nanvix/newlib#TBD (esaurez/newlib#5).

With those upstream and a toolchain image republished from
nanvix/toolchain-gcc#TBD (esaurez/toolchain-gcc#1) that bundles the
new newlib commit, the link line no longer needs the flag.

Validated end-to-end with the patched toolchain
(`local-nanvix/toolchain-gcc:do_start-weak`):

  * posix-tests: 18 binaries built, 12 integration suites pass
  * libxml2 functional test: PASS
  * `_do_start` at the entry point is libnvx_crt0's SSE-aligned
    variant (`and $0xfffffff0, %esp ; sub $0x8, %esp ; push ; push ;
    call _start`), not newlib's 9-byte unaligned stub.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@esaurez

esaurez commented Jun 9, 2026

Copy link
Copy Markdown
Owner Author

Superseded by upstream PR nanvix#74, which carries the same compat fix -- rebased onto the corresponding upstream branch (port libs stack on top of the freshly-filed Wave 3 .so PR; posix-tests stacks on nanvix/posix-tests#177), with only the E: commit retained (the F: cleanup that drops --allow-multiple-definition will be filed as a follow-up once nanvix/newlib#17 + nanvix/toolchain-gcc#14 land and the toolchain image is republished). Commit message rewritten to cite nanvix/* PR numbers (nanvix/nanvix#2453, #2487, nanvix/newlib#17, nanvix/toolchain-gcc#14) instead of esaurez/* placeholders. Closing this fork PR; tracking continues upstream.

@esaurez esaurez closed this Jun 9, 2026
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