Skip to content

[build] E: Build libxml2.so alongside libxml2.a#73

Open
esaurez wants to merge 1 commit into
nanvix:nanvix/v2.12.9from
esaurez:feat/build-shared-library-upstream
Open

[build] E: Build libxml2.so alongside libxml2.a#73
esaurez wants to merge 1 commit into
nanvix:nanvix/v2.12.9from
esaurez:feat/build-shared-library-upstream

Conversation

@esaurez

@esaurez esaurez commented Jun 9, 2026

Copy link
Copy Markdown

Summary

Produce a position-independent libxml2.so in addition to the existing static libxml2.a. The shared library embeds zlib via --whole-archive so it is self-contained, and leaves libposix / libc / libm symbols unresolved so they bind to the host executable's .dynsym at dlopen time -- matching the extension-module model used elsewhere on Nanvix.

What changes

File Change
.nanvix/Makefile.nanvix Adds a $(SHAREDLIB) target (.libs/libxml2.so) that links manually from libxml2.a + libz.a via -shared -fPIC -nostdlib, sets DT_SONAME=libxml2.so so downstream consumers emit the right DT_NEEDED entry. test-functional now sanity-checks the .so: presence, minimum size, DT_SONAME, and that the public libxml2 entry points (xmlInitParser, xmlParseMemory) appear in .dynsym. package / verify-package ship both libxml2.a and libxml2.so.
.nanvix/z.py Adds .libs/libxml2.so to _BUILD_OUTPUTS (so the Docker wrapper copies the .so back to the host) and to _staged_output_files() (so release packages it).

Why bare SONAME (libxml2.so, not libxml2.so.2)

Upstream libxml2 ships SONAME libxml2.so.2. The Nanvix port uses the bare libxml2.so because Nanvix is currently single-vendor: every consumer of this library is also built in this monorepo against this exact version, and Nanvix has no LD_LIBRARY_PATH-style runtime version search. If a third-party consumer ever needs the versioned name, the package target can install a libxml2.so.2 -> libxml2.so symlink as a follow-up; that change is non-load-bearing for current consumers and intentionally out of scope here.

Configure invariants kept

--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.

Runtime dependency

The .so only becomes useful once the loader changes in nanvix/nanvix#2473 ([syscall] E: Run dlopen ctors/dtors and DT_RUNPATH) ship, because most C/C++ libraries -- including libxml2's downstream consumers libxslt and lxml -- rely on .init_array constructors that the current loader silently skips. The libxml2 unit-test in this PR does not exercise that path; downstream .so consumers will.

Validation

./z build produces .libs/libxml2.a (~5.7 MB) and .libs/libxml2.so (~1.5 MB). test-functional passes the structural checks (SONAME present, public entry points exported).

Produce a position-independent libxml2.so in addition to the
existing static libxml2.a.  The .so embeds zlib via --whole-archive
so it is self-contained, and leaves libposix/libc/libm symbols
unresolved so they bind to the host executable's .dynsym at dlopen
time (matching the extension-module model already used by Nanvix
guest binaries that dlopen .so plugins).

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.
* A new `.libs/libxml2.so` target links the .so manually from
  libxml2.a + libz.a via `-shared -fPIC -nostdlib`, setting
  DT_SONAME=libxml2.so so downstream consumers emit the correct
  DT_NEEDED entry.
* `test-functional` now sanity-checks the .so: presence, minimum
  size, DT_SONAME, and that the public libxml2 entry points
  (xmlInitParser, xmlParseMemory) appear in .dynsym.
* `package` / `verify-package` ship both libxml2.a and libxml2.so.

Runtime dependency: this shared-library build only becomes
useful once the loader changes in nanvix/nanvix#2473 ([syscall] E:
Run dlopen ctors/dtors and DT_RUNPATH) ship, because most C/C++
libraries -- including libxml2's transitive consumers like libxslt
and Python bindings -- rely on `.init_array` constructors that the
current loader silently skips.  The libxml2 unit-test in this PR
does not exercise that path; downstream `.so` consumers will.

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

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Nanvix port build to produce a shared libxml2.so alongside the existing static libxml2.a, and to include the .so in build outputs and release staging.

Changes:

  • Add a libxml2.so build target that links from libxml2.a plus libz.a with PIC and a fixed SONAME.
  • Extend functional tests to sanity-check the produced .so (SONAME and exported symbols).
  • Update the Nanvix z.py build script to treat libxml2.so as a build/release artifact.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
.nanvix/z.py Adds libxml2.so to Docker-copied build outputs and to the staged outputs list for release.
.nanvix/Makefile.nanvix Adds a shared-library link step and .so checks in tests; adds package/verify targets; changes build/staging flow.
Comments suppressed due to low confidence (1)

.nanvix/z.py:79

  • Libxml2Build.docker_config() will always try to copy back the install-staged outputs returned by _staged_output_files() (e.g., lib_out()/libxml2.a, bin_out()/xml2-config, etc.). However, the updated Makefile no longer has an install/staging step in the all target, so these paths will not be created during ./z build and the Docker output-copy step is likely to fail (or ./z release will miss artifacts). Either restore a staging step in .nanvix/Makefile.nanvix (preferred, since the comments here assume it), or update this script to copy/package artifacts from their new locations (e.g., the package target’s tarball).
    def _staged_output_files(self) -> list[str]:
        """Return install-staged artifact paths (relative to repo_root())
        so Windows tar-copy mode also copies them back to the host.
        """
        root = repo_root()
        return [
            str((lib_out() / "libxml2.a").relative_to(root)),
            str((lib_out() / "libxml2.so").relative_to(root)),
            str(
                (include_out() / "libxml2" / "libxml" / "xmlversion.h").relative_to(
                    root
                )
            ),
            str((bin_out() / "xml2-config").relative_to(root)),
            str((test_out() / "test_libxml2.elf").relative_to(root)),

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .nanvix/Makefile.nanvix
Comment thread .nanvix/Makefile.nanvix
Comment thread .nanvix/Makefile.nanvix
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