Skip to content

Commit 20f4179

Browse files
Enrique SaurezCopilot
andcommitted
[build] E: Build libxslt.so and libexslt.so
Produce position-independent libxslt.so / libexslt.so alongside the existing static libxslt.a / libexslt.a, matching the shared-library transition already started for libxml2 in esaurez/libxml2 (`feat/build-shared-library`). Each shared object is self-contained: libxslt.so embeds the lower libxml2.a + libz.a via --whole-archive, and libexslt.so embeds libxslt.a on top of that. No DT_NEEDED entries are emitted, so the loader does not require sibling .so files to be present in the search path. libc/libm/libposix symbols remain unresolved and bind to the host executable's .dynsym at dlopen time, matching the CPython extension model already in use across the Nanvix port. Concretely: * `--enable-static --disable-shared` stays in configure (libtool's shared-library detection has no rules for i686-nanvix); `-fPIC` is added to CFLAGS so the same .o files are usable for both archives. * Two new `.so` targets link the shared libraries manually with `-shared -fPIC -nostdlib`, setting DT_SONAME=libxslt.so / DT_SONAME=libexslt.so so downstream consumers (lxml) emit the correct DT_NEEDED entry. * `make test` now sanity-checks each .so: presence, DT_SONAME, and that the public entry points (xsltParseStylesheetDoc, exsltRegisterAll) appear in .dynsym. * `.nanvix/z.py` `_BUILD_OUTPUTS` and `release()` ship both the static and shared variants. Runtime dependency: this shared-library build only becomes useful once esaurez/nanvix#27 ([syscall] E: Run dlopen ctors/dtors and DT_RUNPATH) ships, because libxml2's internal constructors must fire for libxslt to function correctly. The unit-test in this PR exercises only the static archives; the .so end-to-end test will live in the downstream lxml binding. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent ab86b80 commit 20f4179

2 files changed

Lines changed: 73 additions & 6 deletions

File tree

.nanvix/Makefile.nanvix

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,17 @@
66
# NANVIX_TOOLCHAIN=<toolchain> [target]
77
#
88
# Targets:
9-
# all Build libxslt.a, libexslt.a, and the test ELF
9+
# all Build libxslt.{a,so}, libexslt.{a,so}, and the test ELF
1010
# test Verify functional-test prerequisites (build artifacts).
1111
# The runtime execution is driven by z.py on the host.
1212
# clean Remove build artifacts
13+
#
14+
# Dependencies:
15+
# The shared-library build requires the Nanvix dynamic loader to honour
16+
# `.init_array` constructors, shipped in esaurez/nanvix#27. libxml2 is
17+
# embedded inside libxslt.so/libexslt.so via --whole-archive, so the
18+
# shared objects are self-contained and do not require libxml2.so to
19+
# be present at runtime.
1320

1421
# ===========================================================================
1522
# Global Variables
@@ -28,10 +35,12 @@ EXE = .elf
2835

2936
STATICLIB_XSLT := libxslt/.libs/libxslt.a
3037
STATICLIB_EXSLT := libexslt/.libs/libexslt.a
38+
SHAREDLIB_XSLT := libxslt/.libs/libxslt.so
39+
SHAREDLIB_EXSLT := libexslt/.libs/libexslt.so
3140
TEST_SRC := .nanvix/test/test_libxslt.c
3241
TEST_ELF := test_libxslt$(EXE)
3342

34-
_NANVIX_DOCKER_BUILD_GOALS := all $(STATICLIB_XSLT) $(STATICLIB_EXSLT) $(TEST_ELF)
43+
_NANVIX_DOCKER_BUILD_GOALS := all $(STATICLIB_XSLT) $(STATICLIB_EXSLT) $(SHAREDLIB_XSLT) $(SHAREDLIB_EXSLT) $(TEST_ELF)
3544
_NANVIX_GOALS := $(or $(MAKECMDGOALS),$(.DEFAULT_GOAL))
3645

3746
# Ensure required variables are defined.
@@ -66,8 +75,13 @@ endif
6675
# Build Targets
6776
# ===========================================================================
6877

69-
all: $(STATICLIB_XSLT) $(STATICLIB_EXSLT) $(TEST_ELF)
78+
all: $(STATICLIB_XSLT) $(STATICLIB_EXSLT) $(SHAREDLIB_XSLT) $(SHAREDLIB_EXSLT) $(TEST_ELF)
7079

80+
# Build the static archives with -fPIC so the same objects can be
81+
# linked into position-independent .so files below. autotools'
82+
# libtool shared-library detection does not know about i686-nanvix,
83+
# so we keep --disable-shared and link the .so files ourselves from
84+
# the .a archives.
7185
$(STATICLIB_XSLT) $(STATICLIB_EXSLT):
7286
sh -c '\
7387
export PATH="$(NANVIX_TOOLCHAIN)/bin:$$PATH" && \
@@ -77,15 +91,49 @@ $(STATICLIB_XSLT) $(STATICLIB_EXSLT):
7791
--enable-static --disable-shared --disable-maintainer-mode \
7892
--without-python --without-crypto \
7993
--without-debugger --without-plugins \
94+
--with-pic \
8095
--with-libxml-prefix="$(BUILDROOT_PATH)" \
8196
--with-libxml-include-prefix="$(BUILDROOT_PATH)/include/libxml2" \
8297
--with-libxml-libs-prefix="$(BUILDROOT_PATH)/lib" \
83-
CFLAGS="-I$(BUILDROOT_PATH)/include -I$(BUILDROOT_PATH)/include/libxml2" \
98+
CFLAGS="-I$(BUILDROOT_PATH)/include -I$(BUILDROOT_PATH)/include/libxml2 -fPIC" \
8499
LDFLAGS="-L$(BUILDROOT_PATH)/lib" \
85100
LIBS="-lxml2 -lz" && \
86101
make -C libxslt -j$(NPROC) && \
87102
make -C libexslt -j$(NPROC)'
88103

104+
# libxslt.so: self-contained; embeds libxml2.a and libz.a so the
105+
# shared object can be loaded standalone without requiring a separate
106+
# libxml2.so / libz.so in the loader search path. libc/libm/libposix
107+
# symbols are left unresolved and bound at dlopen time against the
108+
# host executable's .dynsym.
109+
$(SHAREDLIB_XSLT): $(STATICLIB_XSLT)
110+
sh -c '\
111+
export PATH="$(NANVIX_TOOLCHAIN)/bin:$$PATH" && \
112+
i686-nanvix-gcc -shared -fPIC -nostdlib \
113+
-Wl,-soname,libxslt.so -Wl,-z,noexecstack \
114+
-Wl,--whole-archive \
115+
$(STATICLIB_XSLT) \
116+
$(BUILDROOT_PATH)/lib/libxml2.a \
117+
$(BUILDROOT_PATH)/lib/libz.a \
118+
-Wl,--no-whole-archive \
119+
-o $(SHAREDLIB_XSLT)'
120+
121+
# libexslt.so: same shape — bundles libexslt.a + libxslt.a +
122+
# libxml2.a + libz.a. No DT_NEEDED entries; cpython-style loaders
123+
# resolve everything against the host executable.
124+
$(SHAREDLIB_EXSLT): $(STATICLIB_EXSLT) $(STATICLIB_XSLT)
125+
sh -c '\
126+
export PATH="$(NANVIX_TOOLCHAIN)/bin:$$PATH" && \
127+
i686-nanvix-gcc -shared -fPIC -nostdlib \
128+
-Wl,-soname,libexslt.so -Wl,-z,noexecstack \
129+
-Wl,--whole-archive \
130+
$(STATICLIB_EXSLT) \
131+
$(STATICLIB_XSLT) \
132+
$(BUILDROOT_PATH)/lib/libxml2.a \
133+
$(BUILDROOT_PATH)/lib/libz.a \
134+
-Wl,--no-whole-archive \
135+
-o $(SHAREDLIB_EXSLT)'
136+
89137
$(TEST_ELF): $(TEST_SRC)
90138
@test -f $(STATICLIB_XSLT) || { echo " FAIL: $(STATICLIB_XSLT) not found; run 'build' first"; exit 1; }
91139
@test -f $(STATICLIB_EXSLT) || { echo " FAIL: $(STATICLIB_EXSLT) not found; run 'build' first"; exit 1; }
@@ -113,7 +161,17 @@ test:
113161
@echo "=== libxslt functional tests ==="
114162
@test -f $(STATICLIB_XSLT) || { echo " FAIL: $(STATICLIB_XSLT) not found"; exit 1; }
115163
@test -f $(STATICLIB_EXSLT) || { echo " FAIL: $(STATICLIB_EXSLT) not found"; exit 1; }
164+
@test -f $(SHAREDLIB_XSLT) || { echo " FAIL: $(SHAREDLIB_XSLT) not found"; exit 1; }
165+
@test -f $(SHAREDLIB_EXSLT) || { echo " FAIL: $(SHAREDLIB_EXSLT) not found"; exit 1; }
116166
@test -s $(TEST_ELF) || { echo " FAIL: $(TEST_ELF) missing or empty"; exit 1; }
167+
@$(NANVIX_TOOLCHAIN)/bin/i686-nanvix-readelf -d $(SHAREDLIB_XSLT) | grep -q 'SONAME.*libxslt.so' \
168+
|| { echo " FAIL: SONAME=libxslt.so not set on $(SHAREDLIB_XSLT)"; exit 1; }
169+
@$(NANVIX_TOOLCHAIN)/bin/i686-nanvix-readelf -d $(SHAREDLIB_EXSLT) | grep -q 'SONAME.*libexslt.so' \
170+
|| { echo " FAIL: SONAME=libexslt.so not set on $(SHAREDLIB_EXSLT)"; exit 1; }
171+
@$(NANVIX_TOOLCHAIN)/bin/i686-nanvix-nm -D $(SHAREDLIB_XSLT) | grep -q 'T xsltParseStylesheetDoc' \
172+
|| { echo " FAIL: xsltParseStylesheetDoc missing from $(SHAREDLIB_XSLT) .dynsym"; exit 1; }
173+
@$(NANVIX_TOOLCHAIN)/bin/i686-nanvix-nm -D $(SHAREDLIB_EXSLT) | grep -q 'T exsltRegisterAll' \
174+
|| { echo " FAIL: exsltRegisterAll missing from $(SHAREDLIB_EXSLT) .dynsym"; exit 1; }
117175
@echo " OK: build artifacts present (runtime execution is driven by z.py on the host)"
118176
@echo "=== libxslt functional test prerequisites PASSED ==="
119177

.nanvix/z.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@
3434
_BUILD_OUTPUTS = [
3535
"test_libxslt.elf",
3636
"libxslt/.libs/libxslt.a",
37+
"libxslt/.libs/libxslt.so",
3738
"libexslt/.libs/libexslt.a",
39+
"libexslt/.libs/libexslt.so",
3840
]
3941

4042
IS_WINDOWS = sys.platform == "win32"
@@ -306,10 +308,12 @@ def release(self) -> None:
306308
xslt_inc.mkdir(parents=True)
307309
exslt_inc.mkdir(parents=True)
308310

309-
# Copy static libraries.
311+
# Copy static and shared libraries.
310312
for name, src_dir in [
311313
("libxslt.a", repo / "libxslt" / ".libs"),
314+
("libxslt.so", repo / "libxslt" / ".libs"),
312315
("libexslt.a", repo / "libexslt" / ".libs"),
316+
("libexslt.so", repo / "libexslt" / ".libs"),
313317
]:
314318
src = src_dir / name
315319
if not src.is_file():
@@ -341,7 +345,12 @@ def release(self) -> None:
341345
with tarfile.open(str(tarball), "r:gz") as tf:
342346
members = tf.getnames()
343347

344-
for expected in ("sysroot/lib/libxslt.a", "sysroot/lib/libexslt.a"):
348+
for expected in (
349+
"sysroot/lib/libxslt.a",
350+
"sysroot/lib/libxslt.so",
351+
"sysroot/lib/libexslt.a",
352+
"sysroot/lib/libexslt.so",
353+
):
345354
if expected not in members:
346355
raise ValueError(f"Package missing {expected}")
347356

0 commit comments

Comments
 (0)