Skip to content

[nanvix] E: Link libnvx_crt0.a into python.elf#684

Open
esaurez wants to merge 1 commit into
feat/makefile-nanvix-so-link-flagsfrom
feat/cpython-link-libnvx-crt0
Open

[nanvix] E: Link libnvx_crt0.a into python.elf#684
esaurez wants to merge 1 commit into
feat/makefile-nanvix-so-link-flagsfrom
feat/cpython-link-libnvx-crt0

Conversation

@esaurez
Copy link
Copy Markdown

@esaurez esaurez commented May 30, 2026

Summary

Stacks on top of nanvix/cpython#682. Adds libnvx_crt0.a -- the executable-startup archive introduced by the Nanvix nvx-crt0 crate split -- to python.elf's --whole-archive LIBS segment.

This makes python.elf forward-compatible with the planned Nanvix upstream change that removes _start / _do_start / c_trampoline from libposix.a: once libposix drops the duplicates, libnvx_crt0.a becomes the sole provider of those symbols. Today both archives ship them; with the existing -Wl,--allow-multiple-definition LDFLAG and the new ordering (libnvx_crt0 first), the linker picks the crt0 copy, so python.elf uses the same _start source across both the current and the future Nanvix state.

What changed in Makefile.nanvix

Section Change
Variable definitions New LIBNVX_CRT0 := .../lib/libnvx_crt0.a in both the docker and host-build branches.
Existence guard Parse-time $(error ...) if $(LIBNVX_CRT0) is missing, gated on MAKECMDGOALS so clean and distclean still work against older sysroots.
LIBS line Prepend $(LIBNVX_CRT0) to the --whole-archive group: -Wl,--whole-archive $(LIBNVX_CRT0) $(LIBPOSIX) $(LIBC) $(LIBM) $(LIBSTDCXX) $(LIBGCC) -Wl,--no-whole-archive ....
Comment block Extended to document the ordering rationale and the future-state migration to a stripped-down libposix.a.

.nanvix/config.py::configure_env() (the dead-code helper kept in sync per the prior PR's docstring) is also updated to mirror the new LIBS line.

Ordering rationale (the subtle bit)

libnvx_crt0 is listed first inside --whole-archive so that under -Wl,--allow-multiple-definition, the linker picks the crt0-side copy of _start / _do_start / c_trampoline over the libposix-side copy. This is an intentional behaviour change:

  • Today (libposix still ships startup symbols): _start comes from libnvx_crt0.a. Functionally identical to the old libposix-provided _start, but the source archive is different.
  • Tomorrow (after the planned Nanvix upstream change removes startup from libposix): _start comes from libnvx_crt0.a -- now the sole provider.

Listing crt0 first means python.elf's startup behaviour does not silently change when the upstream cutover happens. The alternative (list crt0 last to preserve today's exact provider) would mean a subtle behaviour shift the day libposix changes.

Existence guard

The new guard fails the make-parse step with an actionable message if the sysroot lacks libnvx_crt0.a:

$(error libnvx_crt0.a not found at $(LIBNVX_CRT0). Update the Nanvix sysroot to one that ships libnvx_crt0.a ...)

Without the guard, a stale sysroot would lead to a confusing linker error ("file not found: libnvx_crt0.a") inside a long python configure log. With it, the user gets the message the moment they invoke make build.

The guard is gated:

ifdef CONFIG_NANVIX
ifneq ($(filter clean distclean,$(MAKECMDGOALS)),$(MAKECMDGOALS))
ifeq ($(wildcard $(LIBNVX_CRT0)),)
$(error ...)
endif
endif
endif

so it only fires when CPython is actually being built; make clean against an older sysroot still works.

Validation

  • ./z build succeeds; python.elf grows by ~500 bytes (the small handful of crt0 objects added under --whole-archive; most of nvx-crt0's content was already in python.elf via libposix transitively).
  • i686-nanvix-nm python.elf shows T _start and T _do_start resolved.
  • numpy 1.26.4 import + np.arange + np.dot + reshape + broadcasting all work; the test harness prints NUMPY_TEST_OK on the Nanvix microvm.

Compatibility

  • Requires a Nanvix sysroot that ships libnvx_crt0.a -- this is enforced by the existence guard.
  • No change to CPython source code. Only the Nanvix-specific build harness (Makefile.nanvix + .nanvix/config.py).
  • Multi-process / single-process / standalone modes unaffected (the linker changes are not mode-conditional).

Follow-ups

A separate Nanvix-side PR (next in the stack) will remove the startup symbols from libposix.a itself, after which libnvx_crt0.a becomes the only _start provider. The link line in this PR is already correct for that future state -- no further cpython change is needed.

Stacked on top of the prior `[nanvix] E: Embed C/C++ runtime in
python.elf for .so dlopen support` PR.

Adds `libnvx_crt0.a` -- the executable-startup archive introduced by
the Nanvix `nvx-crt0` crate split -- to python.elf's `--whole-archive`
LIBS segment.  This makes python.elf forward-compatible with the
planned Nanvix upstream change that removes `_start` / `_do_start` /
`c_trampoline` from `libposix.a`: once libposix drops the duplicate,
`libnvx_crt0.a` becomes the sole provider of those symbols.

What changed in `Makefile.nanvix`:

  - New `LIBNVX_CRT0` variable (defined in both the docker and
    host-build branches): absolute path to the sysroot copy of
    `libnvx_crt0.a`.

  - Existence check: parse-time error if `$(LIBNVX_CRT0)` is missing
    from the sysroot, with a clear "update your sysroot" hint.  Gated
    on `MAKECMDGOALS` so `clean` and `distclean` still work against
    older sysroots.

  - `LIBS` line: prepend `$(LIBNVX_CRT0)` to the `--whole-archive`
    group.  Listed FIRST so that under the existing
    `-Wl,--allow-multiple-definition` LDFLAG, the linker picks
    `libnvx_crt0`'s `_start` over the duplicate copy `libposix.a`
    currently still ships.  This is an intentional behaviour change:
    python.elf today and python.elf against a future stripped-down
    libposix.a both use the same `_start` source (the standalone crt0
    crate), avoiding subtle differences in startup behaviour across
    the migration window.

  - Comment block extended to document the ordering rationale and the
    expected future state where libposix no longer carries startup
    symbols.

`.nanvix/config.py::configure_env()` (the dead-code helper kept in
sync as documented in PR-10) is updated to mirror the new LIBS line.

Validated end-to-end:

  - `./z build` succeeds; python.elf grows by ~500 bytes (the crt0
    objects added under `--whole-archive`).
  - `nm python.elf` shows `T _start` and `T _do_start` resolved.
  - numpy 1.26.4 import + arithmetic + broadcasting test produces
    `NUMPY_TEST_OK` on the Nanvix microvm.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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