This repository was archived by the owner on Jun 24, 2026. It is now read-only.
[dlfcn] E: Add dlfcn-weak-c tests for STB_WEAK loader semantics#176
Open
esaurez wants to merge 1 commit into
Open
[dlfcn] E: Add dlfcn-weak-c tests for STB_WEAK loader semantics#176esaurez wants to merge 1 commit into
esaurez wants to merge 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new dlfcn-weak-c regression suite that validates ELF STB_WEAK undefined-symbol semantics end-to-end via the public dlopen/dlsym interface, and wires it into the build/test runner so the required .so fixtures are bundled into the ramfs for standalone runs.
Changes:
- Introduces
src/dlfcn-weak-c/with a standalone driver (main.c) plus helper shared-library fixtures covering bothR_386_GLOB_DATandR_386_JUMP_SLOTweak-undefined cases. - Adds a suite Makefile that builds fixtures and asserts their relocation/symbol-table properties via
readelf. - Wires the new suite into
src/Makefileand.nanvix/z.py(suite lists + ramfs library bundling).
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/Makefile |
Adds dlfcn-weak-c to the suite list and updates standalone-only suite comment. |
src/dlfcn-weak-c/Makefile |
Builds the test binary and 7 .so fixtures; adds readelf-based assertions for relocations and dynsym bindings. |
src/dlfcn-weak-c/main.c |
Implements the 7-case weak/strong undefined-symbol behavior checks through dlopen/dlsym. |
src/dlfcn-weak-c/libs/weak_func.c |
Fixture for weak undefined function via GOT (R_386_GLOB_DAT) with NULL-guard. |
src/dlfcn-weak-c/libs/weak_func_plt.c |
Fixture for weak undefined function via PLT (R_386_JUMP_SLOT) without NULL-guard. |
src/dlfcn-weak-c/libs/weak_data.c |
Fixture for weak undefined data via GOT (R_386_GLOB_DAT) with NULL-guard. |
src/dlfcn-weak-c/libs/strong_missing.c |
Fixture for strong undefined function via PLT (R_386_JUMP_SLOT) to ensure dlopen(RTLD_NOW) fails. |
.nanvix/z.py |
Registers the suite and bundles its .so fixtures into the ramfs for standalone test runs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds a new src/dlfcn-weak-c/ regression suite covering STB_WEAK
undefined-symbol semantics through the public POSIX dlfcn interface
(dlopen / dlsym).
The System V gABI ("Symbol Table" chapter) specifies that an
unresolved STB_WEAK undefined symbol takes value zero at dynamic
link time, with the program responsible for null-checking before
use. Every mainstream ELF loader (glibc elf/dl-lookup.c, musl
ldso/dynlink.c, FreeBSD rtld-elf/rtld.c, Android Bionic
linker/linker_relocate.cpp) implements this rule. The Nanvix loader
honours the contract; this suite pins the behaviour in place so
future loader refactors cannot silently re-break it.
Test matrix -- 7 cases driven from src/dlfcn-weak-c/main.c. Magic
string "ok" is written to stdout on full success; assert() aborts
on failure (the convention shared by dlfcn-c, dlfcn-pie-c,
dlfcn-global-c, dlfcn-needed-c).
1. libweak-func-resolved.so weak func ref + GOT-indirect
resolved at exe scope -> calls succeed
2. libweak-func-missing.so weak func ref + GOT-indirect
unresolved -> &fn == NULL
3. libweak-data-resolved.so weak data ref + GOT-indirect
resolved at exe scope
4. libweak-data-missing.so weak data ref + GOT-indirect
unresolved -> &data == NULL
5. libstrong-missing.so strong undefined symbol;
dlopen(RTLD_NOW) MUST fail
(regression guard, runs first)
6. libweak-plt-resolved.so weak func via PLT resolved
7. libweak-plt-missing.so weak func via PLT unresolved;
dlopen(RTLD_NOW) succeeds
(the case deliberately resolves but
does not call the NULL PLT entry)
Case 5 runs first so a regression in the strong-undefined path is
reported independently of the weak cases. It is also the only
case that passes against a loader without STB_WEAK support.
Relocation-class coverage: the weak NULL-guarded pattern
`if (&fn) fn();` in libs/weak_func.c and libs/weak_data.c forces a
GOT-indirect call/load and exercises R_386_GLOB_DAT.
libs/weak_func_plt.c (unguarded call) exercises R_386_JUMP_SLOT.
The strong-undefined regression guard (libs/strong_missing.c) is
unguarded by construction and also emits R_386_JUMP_SLOT. Together
the helpers cover both relocation classes the loader must handle
for undefined symbols in i686 ELF.
The suite Makefile asserts via `readelf -rW` and `readelf -WsD`
that each produced .so contains both:
1. the intended relocation type AND symbol name, proving the
fixture is exercising the symbol we think it is and not some
accidental CRT-injected weak symbol;
2. the intended dynamic-symbol binding (WEAK / GLOBAL) and that
the symbol is UND.
Main executable link flags: `-pie -rdynamic -Wl,--no-dynamic-linker`.
-pie + -rdynamic export main_callback and weak_data in the main
exe's .dynsym so the resolved cases can look them up (matches the
existing dlfcn-pie-c pattern). --no-dynamic-linker because Nanvix
uses an in-process loader via syscalls, not a separate ld.so.
The dlfcn-weak-c suite has also been wired into .nanvix/z.py as
both an ALL_SUITES and STANDALONE_ONLY_SUITES entry, with a
SUITE_RAMFS_LIBS mapping that copies the 7 fixture .so files into
the ramfs under lib/ where main.c expects them.
Validation: all 7 cases pass end-to-end on a Nanvix microvm guest;
the full posix-tests suite (17 ELFs, 15 integration runs) passes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
c1fa45e to
5d6ff41
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new
src/dlfcn-weak-c/regression suite covering STB_WEAK undefined-symbol semantics through the public POSIXdlfcninterface (dlopen/dlsym). This is the canonical end-to-end test that pins the Nanvix loader's STB_WEAK handling in place so future loader refactors cannot silently re-break the contract.Why this matters
The System V gABI ("Symbol Table" chapter) specifies that an unresolved STB_WEAK undefined symbol takes value zero at dynamic link time, and the program is responsible for null-checking before use. Every mainstream ELF loader (glibc
elf/dl-lookup.c, muslldso/dynlink.c, FreeBSDrtld-elf/rtld.c, Android Bioniclinker/linker_relocate.cpp) implements this rule. The Nanvix loader honours the contract; until this PR there was no hermetic C-level test driven through the public POSIXdlfcnAPI to anchor that behaviour.Test matrix
7 cases, all in
main.c. Magic string"ok"is written on full success;assert()aborts on failure (matches the convention of the siblingdlfcn-c,dlfcn-pie-c,dlfcn-global-c, anddlfcn-needed-csuites).libstrong-missing.soR_386_JUMP_SLOTstrong_missingdlopen(RTLD_NOW)failslibweak-func-resolved.soR_386_GLOB_DATmain_callbacktry_callback()==70libweak-func-missing.soR_386_GLOB_DATmissing_callback&fn == NULL;try_callback() == -1libweak-data-resolved.soR_386_GLOB_DATweak_dataread_weak_data()==99libweak-data-missing.soR_386_GLOB_DATmissing_weak_data&data == NULL;read_weak_data() == -1libweak-plt-resolved.soR_386_JUMP_SLOTmain_callbacklibweak-plt-missing.soR_386_JUMP_SLOTmissing_plt_callbackdlopen(RTLD_NOW)succeedsCase 5 runs first so that a regression in the strong-undefined path is reported independently of the weak cases. It is also the only case that passes against a loader without STB_WEAK support.
Case 7 deliberately does not call the function pointer it resolves. The JMP_SLOT was zeroed by the loader (weak undefined missing → 0); invoking it would jump to NULL. The case only validates that
dlopen(..., RTLD_NOW)accepts the.sodespite the weak undefined PLT entry.Relocation-class coverage
The weak NULL-guarded pattern
if (&fn) fn();inlibs/weak_func.candlibs/weak_data.cforces a GOT-indirect call/load and only exercisesR_386_GLOB_DAT. To also exercise weak references via the PLT,libs/weak_func_plt.c(an unguarded call) emitsR_386_JUMP_SLOT. Together, the two helpers cover both relocation classes the loader must handle for weak undefined symbols in i686 ELF. The strong-undefined regression guardlibs/strong_missing.cis unguarded by construction and also emitsR_386_JUMP_SLOT.Build-time fixture verification
The suite Makefile asserts via
readelf -rWandreadelf -WsDthat each produced.socontains both: (1) the intended relocation type AND symbol name (proves the fixture is exercising the symbol we think it is, not some accidental CRT-injected weak symbol); and (2) the intended dynamic-symbol binding (WEAK/GLOBAL) and that the symbol isUND. Helper.sofiles are compiled with the suite-wideCFLAGS, keeping the test representative of the project's normal-O2+ warning environment.Linker flags
Main executable:
-pie -rdynamic -Wl,--no-dynamic-linker.-pie+-rdynamicexportmain_callbackandweak_datain the main exe's.dynsymso the resolved cases can look them up (matches the existingdlfcn-pie-cpattern).--no-dynamic-linkerbecause Nanvix uses an in-process loader via syscalls, not a separateld.so.Suite wiring
The new suite is wired into
src/Makefile(SUITESlist and the standalone-only comment) and into.nanvix/z.pyas anALL_SUITESentry, aSTANDALONE_ONLY_SUITESentry, and aSUITE_RAMFS_LIBSmapping that bundles all 7 fixture.sofiles into the ramfs underlib/wheremain.copens them.Validation
End-to-end on a Nanvix microvm guest with the current
0.16.17release (which already contains the STB_WEAK loader support): all 7 cases pass. The full posix-tests suite (17 binaries, 15 integration runs) also passes. Localz lint(pyright) andz format --check(black) report clean.