Skip to content

[nanvix] E: Build _ssl/_hashlib/_ctypes as .so#738

Closed
esaurez wants to merge 10 commits into
feat/wave5-pr-a-stdlib-sofrom
feat/wave5-pr-b-unbundle-externals
Closed

[nanvix] E: Build _ssl/_hashlib/_ctypes as .so#738
esaurez wants to merge 10 commits into
feat/wave5-pr-a-stdlib-sofrom
feat/wave5-pr-b-unbundle-externals

Conversation

@esaurez

@esaurez esaurez commented Jun 16, 2026

Copy link
Copy Markdown

Summary

Converts the three stdlib extensions whose external dependencies are Nanvix-ported libraries that already ship as .so (libssl/libcrypto from openssl, libffi) from *static* (linked into python.elf) to *shared* (lib/python3.12/lib-dynload/<name>.cpython-312.so), each emitting DT_NEEDED for the corresponding sysroot library.

This is exactly how upstream CPython builds these modules on a Linux distro with --with-system-* detection enabled (the default).

DT_NEEDED chains

  • _ctypes.cpython-312.soDT_NEEDED libffi.so
  • _ssl.cpython-312.soDT_NEEDED libssl.soDT_NEEDED libcrypto.so
  • _hashlib.cpython-312.soDT_NEEDED libcrypto.so

The Nanvix dynamic loader walks the chain at dlopen() time (the openssl chain forms a diamond at libcrypto.so), and UND symbols in each .so bind to python.elf .dynsym. Same flow Linux distros use.

_ssl and _hashlib previously embedded their own copies of libcrypto (~5.5 MB each); routing both through the single libcrypto.so ends the OpenSSL init-order bug where the two copies maintained separate provider state and the first openssl_sha256() call failed.

Scope

python.elf LIBS drops -lssl -lcrypto -lffi. _bz2 / _lzma / zlib / _sqlite3 are intentionally not moved here — the Nanvix port repos for libbz2 / liblzma / libz / libsqlite3 do not yet ship .so builds, so those four extensions stay statically built into python.elf (CPython upstream default) until those port repos ship .so builds and a follow-up PR switches the four extensions to DT_NEEDED.

lxml is not part of this PR. lxml is a third-party package, not a CPython stdlib module, and the way it is currently integrated on Nanvix (a flat-name built-in shim) diverges from how upstream CPython loads third-party extensions. That divergence is addressed separately — this PR stays focused on the stdlib modules that match upstream's system-library .so flow.

Mechanics

File Change
.nanvix/runtime_sos.py (new) REQUIRED_RUNTIME_SOS single source of truth (libffi.so, libcrypto.so, libssl.so) + stage_runtime_sos(), consumed by .nanvix/test.py and .nanvix/package.py to copy the .so files from the buildroot into the test / release sysroot.
.nanvix/setup_local.py _ssl / _hashlib / _ctypes shared entries.
.nanvix/test.py CPYTHON_TEST_EXTERNAL_DEPS smoke checks (import + trivial attribute access).
.nanvix/z.py _DEP_EXPECTED_LIBS validates libffi.so + libssl.so / libcrypto.so installed into the buildroot at ./z setup time.
Makefile.nanvix LIBS drops -lssl -lcrypto -lffi.

Dependencies

Base branch: feat/wave5-pr-a-stdlib-so (#732).

Port-repo PRs that produce the required .so files (already filed upstream):

Required .so Produced by
libffi.so nanvix/libffi#235
libcrypto.so, libssl.so nanvix/openssl#252

Runtime dependencies (already merged upstream):

Enrique Saurez and others added 10 commits May 28, 2026 18:24
Updates `Makefile.nanvix` so that `python.elf` correctly serves as the
"main module" against which extension `.so`s (numpy, ssl, lxml, future
pip-installed wheels, ...) resolve their C and C++ runtime symbols at
dlopen() time. This is the consumer-side companion to the Nanvix
loader's STB_WEAK support (esaurez/nanvix#22) and is gated on the new
libposix `pathconf` / `fpathconf` stubs (esaurez/nanvix#23) for the
configure conftest to even produce an executable.

Three coordinated link-flag changes to the `CONFIGURE_ENV` block:

  1. `LIBS` segment 1 -- new `--whole-archive ... --no-whole-archive`
     block ahead of the existing `--start-group`. Forces every object
     from libposix, libc, libm, libstdc++, and libgcc into python.elf
     so the runtime symbols extension `.so`s depend on are embedded
     (and re-exported via `-Wl,--export-dynamic`, already present).
     Without this, the static linker drops unreferenced objects
     (e.g. `fscanf`, `longjmp`, `strtold_l` for numpy; `operator
     new/delete[]`, `__cxa_*`, `_Unwind_*`, `std::type_info` vtables
     for any C++ extension) and subsequent dlopen() of those `.so`s
     fails with "symbol not found".

  2. `LIBS` segment 2 -- the existing `--start-group` is trimmed to
     just the external add-on libraries (sqlite3, ssl, crypto, z, bz2,
     lzma, ffi). It no longer re-lists libposix / libc / libm: those
     archives are already fully included by segment 1, so the external
     libs can resolve their references against the already-embedded
     objects.

  3. Two new top-level Makefile vars `LIBSTDCXX := -lstdc++` and
     `LIBGCC := -lgcc`. The GCC driver resolves them against its built-
     in search paths (libgcc lives under a versioned `lib/gcc/i686-
     nanvix/<gcc-version>/` directory, which would be fragile to
     hardcode). Defined once at top level because the `-l` form is
     identical between the docker and host build paths.

`LDFLAGS` is unchanged. The existing `-Wl,--allow-multiple-definition`
flag is kept and the surrounding comment is expanded to honestly
enumerate the duplicate-symbol categories the flag is masking (newlib
long-double math helpers, libposix/libc env+isatty overlaps, libc/libm
math helper overlaps, libgcc internal `__x86.get_pc_thunk.*`
duplicates, etc.) -- the set is large and toolchain-build-version-
dependent, and is the only practical workaround until the contributing
upstreams are fixed.

`.nanvix/config.py::configure_env()` -- an unused helper that mirrors
`Makefile.nanvix`'s `CONFIGURE_ENV` -- is kept in sync (same
`--whole-archive` LIBS, same LDFLAGS) and gains a docstring calling
out the dead-code status. A separate small cleanup PR can delete the
helper entirely.

Validated end-to-end on the Nanvix microvm: CPython 3.12 + numpy 1.26.4
runs `import numpy`, `np.arange`, `np.dot`, `reshape`, `flatten`,
broadcasting, all producing `NUMPY_TEST_OK`. Hello.py and the existing
single-process / multi-process / standalone modes are unaffected by
the change because the linker flags are not mode-conditional.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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>
Adds `ninja-build`, `python3-pip`, and `Cython<3` to the
toolchain-python docker image so that meson- and Cython-based Python
extension cross-builds (numpy, scipy, ...) work out-of-the-box,
without an apt/pip preamble on every `docker run` invocation.

What changed in `.nanvix/docker/Dockerfile`:

  - Added `python3-pip` and `ninja-build` to the apt install list.
  - Added `pip3 install --break-system-packages 'Cython<3'` (pinned for
    numpy 1.26.x compatibility; lift the pin when bumping numpy).
  - Added `rm -rf /usr/include/python3.12` after the install. The
    `python3-pip`/`ninja-build` apt packages transitively pull in
    `libpython3.12-dev`, whose headers under `/usr/include/python3.12`
    would otherwise be picked up by meson's regen step ahead of the
    Nanvix cross sysroot headers and silently corrupt the cross-build.
  - Comment block explaining the rationale for each addition and the
    `/usr/include/python3.12` purge.

Why this matters:

The numpy `.so` cross-build (validated end-to-end on 2026-05-27 with
the STB_WEAK loader fix landed) requires two tools that were not
present in the image as shipped:

  - `ninja` — meson's default backend; missing it makes every
    meson-based Python extension build fail immediately.
  - `Cython` — used by `numpy/_build_utils/tempita.py` to template
    `.pyx.in` files; without it the `numpy.random` codegen step fails.

Before this change, the workaround was to inject:

```bash
apt-get update -qq
apt-get install -qq -y --no-install-recommends ninja-build python3-pip
pip3 install --quiet --break-system-packages 'Cython<3'
rm -rf /usr/include/python3.12
```

into every numpy build invocation, which (a) was fragile, (b) required
the docker container to have outbound network access on every build
(non-hermetic), and (c) re-paid the apt install cost in CI every run.

Validated locally:

  - `docker build -f .nanvix/docker/Dockerfile -t toolchain-python:pr13
    .nanvix/docker/` succeeds.
  - `docker run --rm <image> bash -c 'ninja --version'` → `1.11.1`.
  - `docker run --rm <image> bash -c 'python3 -c "import Cython;
    print(Cython.__version__)"'` → `0.29.37`.
  - `docker run --rm <image> bash -c 'ls /usr/include/python3.12'` →
    exits non-zero / "No such file or directory".

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… and 'origin/feat/toolchain-python-bake-ninja-cython' into phase0-array
Adds a small bash wrapper that sits in front of the real `i686-nanvix-gcc` and `i686-nanvix-g++` driver binaries in the toolchain-python docker image. The wrapper detects whether the invocation is producing an executable or a shared library and applies the correct linker flags for each case.

This mirrors the well-established `emcc` pattern used by emscripten and Pyodide, where the compiler-driver wrapper is what knows the difference between main-module and side-module builds, so consumers (cpython's Makefile.nanvix, future numpy / scipy / lxml meson builds, etc.) can use a single `LDFLAGS` env var without needing to encode the exe-vs-shared distinction themselves.

## Why this exists

`Makefile.nanvix` sets a single `LDFLAGS` on `./configure` that contains executable-specific linker flags:

- `-T <sysroot>/lib/user.ld` — executable layout script.
- `-no-pie` — disable PIE for executables.
- `-Wl,--no-dynamic-linker` — executable-only marker.
- `-Wl,--export-dynamic` — populate the main executable's `.dynsym`.

cpython propagates that same `LDFLAGS` to BOTH the main `python.elf` link AND every extension-module `.so` link via `PY_LDFLAGS`. For `.so` outputs these exe-only flags are wrong:

- `-T user.ld` tells `ld` to use an executable layout. When applied to a `-shared` link, `ld` treats the output as an exe and rejects undefined symbols, even those that should resolve at `dlopen()` time against the main exe's `.dynsym` (the Python C API symbols every extension references).
- `-no-pie` is incompatible with `-shared` (shared libraries must be PIC).
- `-Wl,--no-dynamic-linker` is meaningless for `.so`.
- `-Wl,--export-dynamic` is exe-only.

There is no clean place in cpython 3.12's `Modules/Setup` system to express "use this LDFLAGS for the exe link and that LDFLAGS for shared modules". cpython expects a single `LDFLAGS` shared between both.

The cleanest fix is to install a compiler-driver wrapper that does the split transparently: forwards exe builds unchanged, and strips exe-only flags + adds `-fPIC` for shared builds. That way `LDFLAGS` stays the same for everyone and the wrapper does the right thing per-invocation.

## What changed in `.nanvix/docker/`

- **New file `cc-wrapper.sh`** (~110 lines bash). Detects compile-only mode (`-c` / `-S` / `-E`), exe-link mode (no `-shared`), or shared-link mode (`-shared` present). Compile-only and exe modes forward to the real binary unchanged via `exec "$real_bin" "$@"`. Shared mode iterates the args and:
  - Strips `-T <script>` (both `-T <script>` and `-T<script>` forms), `-no-pie`, `-Wl,--no-dynamic-linker`, `-Wl,--export-dynamic`, `-Wl,-T,<script>`, and bare `*.ld` argument files.
  - Adds `-fPIC` if not already present.
  - Uses a bash `filtered=()` array (not a string) so argv boundaries and quoting are preserved across the rewrite — `exec "$real_bin" "${filtered[@]}"` is argv-preserving for arguments containing spaces, empty strings, or glob metacharacters.
- **Dockerfile updated** to install the wrapper. The script is copied to `/opt/nanvix/bin/i686-nanvix-cc-wrapper.sh`, made executable, and the real `i686-nanvix-gcc` / `i686-nanvix-g++` binaries are renamed to `<name>.real`. Symlinks `<name>` -> `i686-nanvix-cc-wrapper.sh` are then installed. The wrapper picks the right `.real` binary based on `argv[0]`.

The install pattern is defensive: if the upstream base image already has a wrapper symlinked (some local-build flavours of `toolchain-python` carry an earlier wrapper), the symlink is removed and replaced — but only after asserting that the matching `<name>.real` file already exists, so a missing `.real` aborts the install rather than stranding the toolchain. If the real binary has not yet been renamed, it is moved to `<name>.real` in the same step.

## Validation

Built the image locally with the additions and smoke-tested all three wrapper modes against a trivial `int main(){return 0;}`:

- **Test 1 (exe link, no `-shared`):** wrapper transparent; real gcc handles normally.
- **Test 2 (simple `-shared`):** wrapper detects shared mode, adds `-fPIC`, link succeeds.
- **Test 3 (`-shared` plus the toxic exe-only flags `-T <script>` `-no-pie` `-Wl,--no-dynamic-linker`):** wrapper strips the toxic flags and the link succeeds with no spurious "cannot create executables" error.

Build-side end-to-end smoke test with cpython: applied a Phase-0-style change to `Modules/Setup.local` adding `*shared*\narray arraymodule.c`. With the wrapper in place, `array.cpython-312-i686-nanvix.so` (~190 KB) is produced and installed at the canonical `lib/python3.12/lib-dynload/` location; `PyInit_array` no longer appears in `python.elf`. Full `./z build` succeeds.

## What this unblocks

This wrapper is the foundational prerequisite for the broader `.a` -> `.so` migration documented in `nanvix-todo/cpython-static-to-shared-migration.md`. Every subsequent phase of that plan (peeling stdlib extension modules off `python.elf` and shipping them as `.so` files loaded via dlopen, exactly as upstream CPython works) depends on this wrapper being in place. Without it, every per-module conversion would need a per-call LDFLAGS hack.

## Backward compatibility

This change is purely additive at the image level — it adds the wrapper script and rewrites the gcc/g++ entry points to point at it. Existing build paths that don't pass `-shared` see no behavioural change (the wrapper falls through to the real binary unchanged). Existing `.so` build paths that DID work before (e.g., when LDFLAGS happened not to include `-T user.ld`) continue to work because the wrapper's strip-then-add-fPIC produces a strict superset of what bare `gcc -shared` would have done.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
First step of the .a -> .so migration plan documented in
`nanvix-todo/cpython-static-to-shared-migration.md`.  Converts `array`
from a static built-in module into a shared `.so` loaded via dlopen
at runtime, matching upstream CPython's layout for stdlib extension
modules.

## Changes

* `.nanvix/docker.py` + `.nanvix/lxml.py` — `Setup.local` generation:
  - Group the existing static modules under an explicit `*static*`
    directive (was the implicit `#` comment that makesetup tolerated).
  - Add a `*shared*` group with `array arraymodule.c` so cpython's
    `setup.py build_ext` produces
    `array.cpython-312-i686-nanvix.so` at
    `lib/python3.12/lib-dynload/`.

* `.nanvix/test.py` — extend the `test_hello.py` smoke test to
  `import array`, assert it is NOT in `sys.builtin_module_names`, and
  exercise a tiny array operation.  This proves the dlopen path
  end-to-end on every test run; if the `.so` fails to load (loader
  bug, missing PIE relocation, symbol-resolution issue, ...) the
  smoke test fails immediately rather than silently regressing.

## Validation

`./z build` and `./z test`:
- `array.cpython-312.so` is produced at
  `<sysroot>/lib/python3.12/lib-dynload/array.cpython-312.so`.
- Hello smoke prints
  `CPYTHON_TEST_ARRAY_SO: array loaded via dlopen from
   /lib/python3.12/lib-dynload/array.cpython-312.so`.
- lxml + HTTP server smoke continue to PASS.

## Dependencies

This PR depends on the following prereq PRs landing first:
- #682 — `--whole-archive` + `--export-dynamic` in
  python.elf LIBS.
- #683 — bake `ninja` + `Cython` into the toolchain
  image (needed by future meson-based extensions; harmless for
  `array`).
- #684 — link `libnvx_crt0.a` into python.elf (will
  become unnecessary once nanvix/nanvix#2453 lands but harmless
  meanwhile).
- #687 — cc-wrapper for `-shared` vs exe link mode
  (the `.so` link uses it).
- nanvix/nanvix#2450 — loader honours `STB_WEAK` undefined symbols.
- nanvix/nanvix#2453 — `libposix.a` is the sole owner of `sysalloc`
  state (Fix 2 v3); without this, the first `dlopen` collides with
  the heap.

Reviewers: please wait for those PRs (and the matching
`ghcr.io/nanvix/toolchain-python:latest` rebuild) before testing
this one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Builds 39 CPython stdlib extension modules as runtime-loaded .so files
under lib/python3.12/lib-dynload/<name>.cpython-312.so instead of
statically linking them into python.elf. Generalises the *shared*
Setup.local flow first proven by feat/phase0-array-so.

Modules built as .so
--------------------
- Data primitives (no external deps): _bisect, _heapq, _struct,
  _random, _opcode, _queue, _csv, binascii, _json, _pickle, _zoneinfo.
- Math and memory (libm symbols resolved against python.elf): math,
  cmath, _statistics, mmap, _contextvars.
- Text codecs (no external deps): unicodedata, _multibytecodec,
  _codecs_cn/hk/iso2022/jp/kr/tw.
- With bundled-in-cpython C deps (each .so bundles its own .a, same
  as upstream cpython's default ./configure run): _asyncio,
  _datetime, _decimal, pyexpat, _elementtree, _md5, _sha1, _sha2,
  _sha3, _blake2, select, _socket, _posixsubprocess, fcntl, termios.

For the modules with bundled-in-cpython C deps (_decimal, pyexpat,
_sha2), each .so bundles its own copy of the vendored .a via
cpython's normal MODULE_*_LDFLAGS machinery -- exactly the behavior
of cpython's default ./configure run on Linux without
--with-system-libmpdec / --with-system-expat. mpdec / expat /
libHacl_Hash_SHA2 are stateless C APIs with at most one consumer
each (pyexpat exposes a PyCapsule that _elementtree calls through,
so libexpat lives in pyexpat.so only), so duplication into
python.elf would buy nothing.

--with-libm= is cleared so libm symbols stay in python.elf -- math /
cmath resolve them via the existing --whole-archive of libm.a in
LIBNVX_CRT0.

Infrastructure
--------------
- .nanvix/setup_local.py (new): SETUP_LOCAL_ENTRIES data table +
  render_setup_local() -- single source of truth for
  Modules/Setup.local, consumed by both the host (.nanvix/lxml.py)
  and Docker (.nanvix/docker.py) build paths.

- .nanvix/test.py: _SO_MODULE_SANITY_CHECKS table +
  _render_so_sanity_snippets() emit smoke-test snippets that
  exercise every migrated module via import + a trivial method call
  and assert each module is no longer in sys.builtin_module_names.

- Makefile.nanvix: --with-libm= cleared (Phase 1B math/cmath modules
  resolve libm symbols against python.elf .dynsym at dlopen time
  via the existing --whole-archive of libm.a in LIBNVX_CRT0).

Runtime dependencies (already merged upstream)
----------------------------------------------
- nanvix/nanvix#2472 -- libm visibility fix.
- nanvix/nanvix#2473 -- dlfcn init-array + DT_RUNPATH support.

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

When --with-system-libmpdec / --with-system-expat is *not* given,
the bundled-library code paths in configure.ac (lines 3831, 3915)
hardcoded a literal `-lm` in:

  LIBEXPAT_LDFLAGS="-lm $(LIBEXPAT_A)"
  LIBMPDEC_LDFLAGS="-lm $(LIBMPDEC_A)"

On Linux this happens to work because LIBM defaults to `-lm`, so the
result is a no-op. On Nanvix we pass `--with-libm=` (empty) to keep
libm.a out of every Setup.local `.so` -- libm is whole-archived into
python.elf instead. The literal `-lm` defeats that and bundles a full
copy of libm.a into both `_decimal.so` and `pyexpat.so` -- about
~400 KB of redundant code per .so, plus a symbol-collision risk that
forces --allow-multiple-definition at every other .so link.

Switching the literal to `$(LIBM)` is equivalent on Linux (LIBM=-lm
by default) and correctly drops the duplicate libm when LIBM is
empty.

Both `configure.ac` and the generated `configure` are patched in
lockstep so no autoreconf is required.

Bug surfaced by the cpython-on-Nanvix .so management audit; tracked
for a follow-up upstream contribution to python/cpython once the
Nanvix port stabilizes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Converts the three stdlib extensions whose external dependencies are
Nanvix-ported libraries that already ship as .so (libssl/libcrypto
from openssl, libffi) from *static* (linked into python.elf) to
*shared* (lib/python3.12/lib-dynload/<name>.cpython-312.so), each
emitting DT_NEEDED for the corresponding sysroot library:

  _ctypes.cpython-312.so   -> libffi.so
  _ssl.cpython-312.so      -> libssl.so -> libcrypto.so
  _hashlib.cpython-312.so  -> libcrypto.so  (shared with _ssl, deduped)

This is exactly how upstream cpython builds these modules on a Linux
distro with --with-system-* detection enabled (the default). The
Nanvix dynamic loader walks the DT_NEEDED chain at dlopen time (the
openssl chain forms a diamond at libcrypto.so) and UND symbols in
each .so bind to python.elf .dynsym.

_ssl and _hashlib previously embedded their own copies of libcrypto
(~5.5 MB each); routing both through the single libcrypto.so ends the
OpenSSL init-order bug where the two copies maintained separate
provider state and the first openssl_sha256() call failed.

python.elf LIBS drops -lssl -lcrypto -lffi. _bz2 / _lzma / zlib /
_sqlite3 are intentionally NOT moved here: the Nanvix port repos for
libbz2 / liblzma / libz / libsqlite3 do not yet ship .so builds, so
those four extensions stay statically built into python.elf (cpython
upstream default) until the follow-up PR that lands alongside the
Wave 6 port-repo .so PRs.

Infrastructure
--------------
- .nanvix/runtime_sos.py (new): REQUIRED_RUNTIME_SOS single source of
  truth (libffi.so, libcrypto.so, libssl.so) + stage_runtime_sos(),
  consumed by .nanvix/test.py and .nanvix/package.py to copy the .so
  files from the buildroot into the test / release sysroot.
- .nanvix/setup_local.py: _ssl / _hashlib / _ctypes shared entries.
- .nanvix/test.py: CPYTHON_TEST_EXTERNAL_DEPS smoke checks.
- .nanvix/z.py: _DEP_EXPECTED_LIBS validates libffi.so + libssl.so /
  libcrypto.so installed into the buildroot at `./z setup` time.
- Makefile.nanvix: LIBS drops -lssl -lcrypto -lffi.

Runtime dependencies (already merged upstream)
----------------------------------------------
- nanvix/nanvix#2473 -- dlfcn init-array + DT_RUNPATH support.
- nanvix/nanvix#2478 -- diamond DT_NEEDED handling (openssl chain).
- nanvix/nanvix#2481 -- pthread_once (critical: OpenSSL routes every
  internal one-time init through pthread_once).

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

ppenna commented Jul 16, 2026

Copy link
Copy Markdown

Closing the pre-SDK implementation rather than carrying its GCC/Newlib assumptions forward. The product goal remains valid, but the current OpenSSL/libffi SDK packages ship only static archives, so correct _ssl/_hashlib/_ctypes DT_NEEDED chains cannot yet be produced. Follow-up is tracked in #799.

@ppenna ppenna closed this Jul 16, 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