[dlfcn] E: Add tests for diamond DT_NEEDED graphs#3
Conversation
Add a new test suite (`dlfcn-init-runpath-c`) that exercises the
three System V ABI capabilities the Nanvix dynamic loader now
implements (esaurez/nanvix PR `feat/dlfcn-init-array-and-runpath`):
1. `libctor.so` defines `.init_array` and `.fini_array` entries via
`__attribute__((constructor))` / `((destructor))`. The
constructor writes a sentinel into a library-local global; the
destructor writes a different sentinel into the test program's
exported `g_dtor_ran` global so the witness survives the
library being unloaded. The test asserts both sentinels appear
in the expected order.
2. `libparent.so` is linked against `libchild.so` (creating a
`DT_NEEDED` edge) and built with `-Wl,--enable-new-dtags,
-rpath,lib/subdir`, which the linker emits as `DT_RUNPATH
lib/subdir`. At runtime `libchild.so` is staged into
`lib/subdir/` only — never `lib/` — so the only way `dlopen`
can succeed is by honouring `libparent.so`'s `DT_RUNPATH`.
Both libraries are built flat in `build/` and re-staged into the
correct ramfs paths via the existing `SUITE_RAMFS_LIBS` mechanism
in `.nanvix/z.py`, matching the pattern already used by
`dlfcn-c`. The suite is registered in `STANDALONE_ONLY_SUITES`
because it requires ramfs-bundled `.so` files, and in
`ALL_SUITES`, the host `Makefile`, and the container
`src/Makefile`.
Test output on a standalone Nanvix VM running the updated loader:
=== dlfcn init_array + DT_RUNPATH tests ===
PASS: init_array fires on dlopen
PASS: fini_array fires on dlclose
PASS: DT_RUNPATH dependency search
3 passed, 0 failed
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a new test suite (`dlfcn-diamond-c`) that exercises diamond
DT_NEEDED graphs, the canonical multi-edge shape that production
shared-library chains take:
libdiamond.so -> libleft.so -> libbase.so
-> libright.so -> libbase.so
Three assertions:
1. depth-2 control (`dlopen(libright.so)`) -- a single linear edge
from libright.so to libbase.so. This currently passes on stock
Nanvix and proves the test rig works.
2. diamond load (`dlopen(libdiamond.so)`) -- the diamond proper.
On stock Nanvix this HANGS in the loader (deadlock on ancestor
mutex), then after the deadlock is fixed PANICS in `dlclose`
with an over-strict refcount assertion. Both bugs are addressed
by the companion esaurez/nanvix#28.
3. handle stability (`dlopen` twice returns the same handle) --
the standard idempotence guarantee of `dlopen` against an
already-loaded library.
To detect the "libbase.so loaded twice" failure mode (where a buggy
loader gives libleft and libright each their own private copy of
libbase.so's `unique_counter` global instead of sharing one), the
diamond test calls `left_bump()` then `right_bump()` and asserts
the counter reaches 2 on the second call. Without the fix, both
arms see their own counter, both go to 1, and the test fails with
a precise reason string.
The suite is registered in `STANDALONE_ONLY_SUITES` because it
needs `SUITE_RAMFS_LIBS` staging.
End-to-end run on a standalone VM with the loader fix in place:
=== dlfcn diamond DT_NEEDED tests ===
PASS: dlopen(libright.so) [depth-2 chain]
PASS: dlopen(libdiamond.so)
PASS: re-dlopen(libdiamond.so) returns same handle
3 passed, 0 failed
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
04b2a38 to
ee7a87a
Compare
|
Superseded by upstream PR nanvix/posix-tests#177, which carries the same test contribution -- stacked on top of nanvix/posix-tests#174 (the init-array test branch), deep-reviewed, validated end-to-end against a local Nanvix build with the nanvix/nanvix#2478 diamond loader fix applied (all 3 cases pass; full posix-tests integration suite passes). One MUST FIX applied: stripped non-ASCII box-drawing characters and em-dashes from main.c, libs/base.c, and the suite Makefile. Two SHOULD FIXes applied: (a) added build-time |
Summary
Add a new test suite
dlfcn-diamond-cthat exercises diamondDT_NEEDEDgraphs — the canonical multi-edge shape that production shared-library chains take:The existing
dlfcn-needed-csuite covers a single linear edge; this suite extends coverage to the diamond case which surfaces in every real-world.sochain.Three assertions
dlopen("lib/libright.so")— a single linear edge fromlibright.sotolibbase.so. Passes on stock Nanvix; proves the test rig works.dlopen("lib/libdiamond.so")— the diamond proper. On stock Nanvix this hangs in the loader (deadlock on ancestor mutex), then after the deadlock is fixed panics indlclose. Both bugs are addressed by the companion esaurez/nanvix#28.dlopentwice returns the same handle.Detecting duplicate libbase.so
To catch the "loader silently loaded libbase.so twice" failure mode (where
libleftandlibrighteach get their own private copy oflibbase.so'sunique_counterglobal), the diamond test callsleft_bump()thenright_bump()and asserts the counter reaches 2 on the second call. A buggy loader gives each arm its own counter, both reach 1, and the test fails with a precise reason:Hooks
src/dlfcn-diamond-c/{Makefile,main.c,libs/*.c}Makefile,src/Makefiledlfcn-diamond-cinSUITES.nanvix/z.pyALL_SUITES,STANDALONE_ONLY_SUITES, andSUITE_RAMFS_LIBS(stages all 4.sofiles intolib/)Validation
End-to-end on a standalone Nanvix VM with esaurez/nanvix#28 applied:
All 18 testable suites pass (the new diamond suite + 17 pre-existing).
ELF metadata sanity check
Companion PR
esaurez/nanvix
feat/dlfcn-diamond-needed— the loader fix. This test suite verifies that fix and would HANG without it.Stacking
Stacked on top of esaurez/posix-tests#2 (the
.init_array/DT_RUNPATHtest suite). Merge order:posix-tests#2first, then this PR.