Skip to content

[build] E: Build libffi.so alongside libffi.a#1

Closed
esaurez wants to merge 1 commit into
nanvix/v3.4.6from
feat/build-shared-library
Closed

[build] E: Build libffi.so alongside libffi.a#1
esaurez wants to merge 1 commit into
nanvix/v3.4.6from
feat/build-shared-library

Conversation

@esaurez

@esaurez esaurez commented Jun 5, 2026

Copy link
Copy Markdown
Owner

Summary

Produce a position-independent libffi.so in addition to the existing static libffi.a, mirroring the pattern already established by esaurez/libxml2#1. The .so leaves libposix/libc/libm symbols unresolved so they bind to the host executable's .dynsym at dlopen time — the same model used by all CPython extension modules on Nanvix.

Why

This unblocks the Group B unbundling of _ctypes in CPython (see tracking note). Today _ctypes.cpython-312.so statically bundles a copy of libffi (~700 KB of duplication). With libffi.so available, _ctypes.so can drop to a thin module + DT_NEEDED libffi.so (~80 KB), and any other consumers that want to use libffi at runtime can share the same .so.

Beyond the size win, this also fixes a latent correctness issue: libffi.a as previously built was unusable under ld --whole-archive, which made it impossible to pull libffi into python.elf's .dynsym for the Group A unbundling strategy. The root cause and fix are documented below.

Implementation

Fix 1 — libffi.a nested-archive bundling

Change Detail
NANVIX_LIBS now uses -L/-l form Was: explicit /sysroot/lib/libposix.a /toolchain/lib/libc.a /toolchain/lib/libm.a passed via configure-time LDFLAGS. Libtool's static-link path treats explicit .a paths as "convenience archives" and bundles their members into the resulting libffi.a via ar cr. The result before this fix:

$ ar t libffi.a
libposix.a
libc.a
libm.a
lt1-libposix.a
lt2-libc.a
lt3-libm.a
prep_cif.o
...

Consumers that try ld --whole-archive libffi.a then fail because ld cannot expand a nested archive. Switching to -L/-l form lets autoconf's AC_PROG_CC conftest still link a hello-world (so configure passes) while libtool records the libs as system deplibs in libffi.la and does NOT bundle them into libffi.a.

After the fix:

$ ar t libffi.a
prep_cif.o
types.o
raw_api.o
java_raw_api.o
closures.o
tramp.o
ffi.o
sysv.o

Fix 2 — libffi.so build target

Change Detail
-fPIC in CFLAGS Same .o files reusable for .a and .so
Keep --disable-shared libtool's shared-lib detection does not know about i686-nanvix; the .so is linked manually
New $(SHAREDLIB) target i686-nanvix-gcc -shared -fPIC -nostdlib -Wl,--whole-archive libffi.a -Wl,--no-whole-archive -Wl,-soname,libffi.so -Wl,-z,noexecstack -o libffi.so
test-functional extended Verifies DT_SONAME=libffi.so, ffi_call / ffi_prep_cif exported in .dynsym, and that libffi.a is free of nested-archive members
package / verify-package Ship both .a and .so
.nanvix/z.py _OUTPUT_FILES Copy libffi.so back from the Docker build dir

Validation

$ readelf -d i686-pc-nanvix/.libs/libffi.so
 0x0000000e (SONAME)  Library soname: [libffi.so]

$ nm -D i686-pc-nanvix/.libs/libffi.so | grep -E '^[0-9a-f]+ T ffi_' | head
00001796 T ffi_call
000017c2 T ffi_call_go
00001167 T ffi_closure_alloc
000011a8 T ffi_closure_free
00000c1e T ffi_get_struct_offsets
00000b09 T ffi_prep_cif
00000b3a T ffi_prep_cif_var
00001ab0 T ffi_prep_closure_loc
00001ce0 T ffi_raw_call

$ ar t i686-pc-nanvix/.libs/libffi.a
prep_cif.o
types.o
raw_api.o
java_raw_api.o
closures.o
tramp.o
ffi.o
sysv.o

End-to-end runtime resolution (CPython dlopen_ctypes.soDT_NEEDED libffi.so → loader walks chain → resolves libposix/libc/libm UND against python.elf's .dynsym) will be validated in the follow-up CPython integration PR.

Runtime dependencies

  • esaurez/nanvix#27.init_array invocation and DT_NEEDED chain walking in the user-space loader. (libffi has no global constructors of its own, but DT_RUNPATH is needed by the consumer chain.)

libffi.so does not introduce a diamond, so esaurez/nanvix#28 is not required for this PR specifically; consumers that combine libffi with other shared libs may still rely on nanvix#28 indirectly.

Downstream consumer

  • esaurez/cpythonesaurez/cpython#14 will rework _ctypes.cpython-312.so to DT_NEEDED libffi.so so the ~700 KB libffi copy is shared instead of bundled per-extension.

Produce a position-independent libffi.so in addition to the existing static libffi.a. The .so leaves libposix/libc/libm symbols unresolved so they bind to the host executable's .dynsym at dlopen time -- the same model already used by libxml2.so / liblxml_etree.so on Nanvix.

Two changes wired together:

1. NANVIX_LIBS now uses -L/-l form instead of explicit .a paths. When explicit archive paths are passed via configure-time LDFLAGS, libtool treats them as convenience archives and bundles their members into libffi.a (libposix.a, libc.a, libm.a as nested ar members, plus lt1-/lt2-/lt3- renamed copies). Consumers that try \--whole-archive libffi.a\ then fail because ld cannot expand a nested archive. Switching to -L/-l form lets autoconf's AC_PROG_CC conftest still link a hello-world while libtool records the libs as system deplibs (in libffi.la) and does NOT bundle them. Result: libffi.a contains only its own 8 .o files.

2. Added a SHAREDLIB target that links libffi.so from the (now PIC) libffi.a via -Wl,--whole-archive, sets DT_SONAME=libffi.so, leaves libposix/libc/libm UND. Updated z.py output_files, the package / verify-package targets, and added structural checks in test-functional (SONAME, ffi_call exported, no nested archives).

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#235, which carries the same .so build addition -- rebased onto current upstream version branch, scope-trimmed (CI workflow downgrade, nanvix.toml version downgrade, .gitignore tweaks, .zutils-version downgrade, z.ps1/z.sh additions all dropped as unrelated env drift), and esaurez/* / python.elf / nanvix-todo references cleaned from commit message and PR body. Closing this fork PR; tracking continues upstream.

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