Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .nanvix/z.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"dlfcn-global-c",
"dlfcn-needed-c",
"dlfcn-pie-c",
"dlfcn-weak-c",
"echo-c",
"echo-cpp",
"file-c",
Expand Down Expand Up @@ -98,6 +99,7 @@
STANDALONE_ONLY_SUITES = [
"dlfcn-c",
"dlfcn-pie-c",
"dlfcn-weak-c",
]

# Suites that require host networking (passed as -allow-host-networking to nanvixd).
Expand All @@ -110,6 +112,15 @@
SUITE_RAMFS_LIBS: dict[str, list[tuple[str, str]]] = {
"dlfcn-c": [("libmul.so", "lib/libmul.so")],
"dlfcn-pie-c": [("libmul-pie.so", "lib/libmul-pie.so")],
"dlfcn-weak-c": [
("libweak-func-resolved.so", "lib/libweak-func-resolved.so"),
("libweak-func-missing.so", "lib/libweak-func-missing.so"),
("libweak-data-resolved.so", "lib/libweak-data-resolved.so"),
("libweak-data-missing.so", "lib/libweak-data-missing.so"),
("libweak-plt-resolved.so", "lib/libweak-plt-resolved.so"),
("libweak-plt-missing.so", "lib/libweak-plt-missing.so"),
("libstrong-missing.so", "lib/libstrong-missing.so"),
],
}

# Docker image for cross-compilation.
Expand Down
4 changes: 2 additions & 2 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export LIBRARIES_DIR ?= $(BINARIES_DIR)
# Test Suites
#===============================================================================

SUITES := c-bindings dlfcn-c dlfcn-global-c dlfcn-needed-c dlfcn-pie-c echo-c echo-cpp file-c hello-c hello-cpp memory-c misc-c network-c noop-c noop-cpp thread-c
SUITES := c-bindings dlfcn-c dlfcn-global-c dlfcn-needed-c dlfcn-pie-c dlfcn-weak-c echo-c echo-cpp file-c hello-c hello-cpp memory-c misc-c network-c noop-c noop-cpp thread-c

#===============================================================================
# Build Rules
Expand All @@ -91,7 +91,7 @@ $(BINARIES_DIR):
#===============================================================================

# Test suites that can run via plain nanvixd invocation.
# dlfcn-c and dlfcn-pie-c need shared libraries inside the VM filesystem.
# dlfcn-c, dlfcn-pie-c, and dlfcn-weak-c need shared libraries inside the VM filesystem.
# file-c needs VFS access (tests open/read/write on files).
# network-c needs network infrastructure (sockets).
# misc-c needs environment variable setup (NANVIX_TEST).
Expand Down
109 changes: 109 additions & 0 deletions src/dlfcn-weak-c/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Copyright(c) The Maintainers of Nanvix.
# Licensed under the MIT License.

PROGRAM_NAME := dlfcn-weak-c

BINARIES_DIR ?= $(CURDIR)/../../build
LIBRARIES_DIR ?= $(BINARIES_DIR)
READELF ?= readelf

ifeq ($(OS),Windows_NT)
define REMOVE
if exist "$(1)" del /F /Q "$(1)"
endef
else
define REMOVE
rm -f $(1)
endef
endif

SOURCES := $(wildcard *.c)
OBJECTS := $(SOURCES:.c=.o)
BINARY := $(PROGRAM_NAME).elf

# Relocation+symbol assertions for the produced .so files.
#
# We assert both the relocation type and the symbol name (and where
# meaningful, that the symbol binding is WEAK / UND) so that fixtures cannot
# silently drift to exercising the wrong symbol.
#
# Weak function refs guarded by `if (&fn) fn();` compile to a GOT load +
# indirect call, so only R_386_GLOB_DAT is emitted. Weak data refs go through
# the GOT for the same reason. Strong undefined function calls go through
# the PLT and produce R_386_JUMP_SLOT. Weak undefined function calls without
# a NULL check (the weak-PLT fixtures below) also produce R_386_JUMP_SLOT,
# allowing us to exercise the loader's handling of weak `JUMP_SLOT`.

# Macro: assert that `$(1)` contains a `R_386_<type>` relocation against
# the symbol named `$(2)`. Uses `readelf -rW` so symbol names are not
# truncated, and `grep -E` so the pattern stays robust against extra columns.
define ASSERT_RELOC
@if ! $(READELF) -rW $(1) | grep -E -q '$(2)[[:space:]].*[[:space:]]$(3)$$'; then \
echo "Error: expected $(2) relocation against $(3) in $(1)"; \
$(READELF) -rW $(1); \
exit 1; \
fi
endef

# Macro: assert that `$(1)` declares `$(2)` as an undefined dynamic symbol
# with binding `$(3)` (WEAK or GLOBAL). We deliberately do not check the
# symbol type: the linker may emit `NOTYPE` for undefined function refs that
# carry no in-module definition, while OBJECT/FUNC are reserved for symbols
# with a known kind. The intended *use* is verified by the relocation-type
# assertion below; here we only prove the symbol is undefined and weak/global.
define ASSERT_DYNSYM
@if ! $(READELF) -WsD $(1) | grep -E -q '$(3)[[:space:]]+(DEFAULT|HIDDEN|PROTECTED|INTERNAL)[[:space:]]+UND[[:space:]]+$(2)$$'; then \
echo "Error: expected $(3) UND dynamic symbol $(2) in $(1)"; \
$(READELF) -WsD $(1); \
exit 1; \
fi
endef

all: $(OBJECTS) libs-all
$(CC) $(LDFLAGS) -pie -rdynamic -Wl,--no-dynamic-linker $(OBJECTS) $(LIBRARIES) -o $(BINARIES_DIR)/$(BINARY)

clean: libs-clean
$(call REMOVE,$(OBJECTS))
$(call REMOVE,$(BINARIES_DIR)/$(BINARY))

libs-all:
# Weak function ref with NULL-guard (R_386_GLOB_DAT) — resolved variant.
$(CC) $(CFLAGS) libs/weak_func.c -shared -fPIC -DCALLBACK_NAME=main_callback -o $(LIBRARIES_DIR)/libweak-func-resolved.so
$(call ASSERT_DYNSYM,$(LIBRARIES_DIR)/libweak-func-resolved.so,main_callback,WEAK)
$(call ASSERT_RELOC,$(LIBRARIES_DIR)/libweak-func-resolved.so,R_386_GLOB_DAT,main_callback)
# Weak function ref with NULL-guard (R_386_GLOB_DAT) — missing variant.
$(CC) $(CFLAGS) libs/weak_func.c -shared -fPIC -DCALLBACK_NAME=missing_callback -o $(LIBRARIES_DIR)/libweak-func-missing.so
$(call ASSERT_DYNSYM,$(LIBRARIES_DIR)/libweak-func-missing.so,missing_callback,WEAK)
$(call ASSERT_RELOC,$(LIBRARIES_DIR)/libweak-func-missing.so,R_386_GLOB_DAT,missing_callback)
# Weak data ref (R_386_GLOB_DAT) — resolved variant.
$(CC) $(CFLAGS) libs/weak_data.c -shared -fPIC -DWEAK_DATA_NAME=weak_data -o $(LIBRARIES_DIR)/libweak-data-resolved.so
$(call ASSERT_DYNSYM,$(LIBRARIES_DIR)/libweak-data-resolved.so,weak_data,WEAK)
$(call ASSERT_RELOC,$(LIBRARIES_DIR)/libweak-data-resolved.so,R_386_GLOB_DAT,weak_data)
# Weak data ref (R_386_GLOB_DAT) — missing variant.
$(CC) $(CFLAGS) libs/weak_data.c -shared -fPIC -DWEAK_DATA_NAME=missing_weak_data -o $(LIBRARIES_DIR)/libweak-data-missing.so
$(call ASSERT_DYNSYM,$(LIBRARIES_DIR)/libweak-data-missing.so,missing_weak_data,WEAK)
$(call ASSERT_RELOC,$(LIBRARIES_DIR)/libweak-data-missing.so,R_386_GLOB_DAT,missing_weak_data)
# Weak function ref WITHOUT NULL-guard (R_386_JUMP_SLOT) — resolved variant.
$(CC) $(CFLAGS) libs/weak_func_plt.c -shared -fPIC -DCALLBACK_NAME=main_callback -o $(LIBRARIES_DIR)/libweak-plt-resolved.so
$(call ASSERT_DYNSYM,$(LIBRARIES_DIR)/libweak-plt-resolved.so,main_callback,WEAK)
$(call ASSERT_RELOC,$(LIBRARIES_DIR)/libweak-plt-resolved.so,R_386_JUMP_SLOT,main_callback)
# Weak function ref WITHOUT NULL-guard (R_386_JUMP_SLOT) — missing variant.
$(CC) $(CFLAGS) libs/weak_func_plt.c -shared -fPIC -DCALLBACK_NAME=missing_plt_callback -o $(LIBRARIES_DIR)/libweak-plt-missing.so
$(call ASSERT_DYNSYM,$(LIBRARIES_DIR)/libweak-plt-missing.so,missing_plt_callback,WEAK)
$(call ASSERT_RELOC,$(LIBRARIES_DIR)/libweak-plt-missing.so,R_386_JUMP_SLOT,missing_plt_callback)
# Strong undefined function call (R_386_JUMP_SLOT) — regression guard.
$(CC) $(CFLAGS) libs/strong_missing.c -shared -fPIC -o $(LIBRARIES_DIR)/libstrong-missing.so
$(call ASSERT_DYNSYM,$(LIBRARIES_DIR)/libstrong-missing.so,strong_missing,GLOBAL)
$(call ASSERT_RELOC,$(LIBRARIES_DIR)/libstrong-missing.so,R_386_JUMP_SLOT,strong_missing)

libs-clean:
$(call REMOVE,$(LIBRARIES_DIR)/libweak-func-resolved.so)
$(call REMOVE,$(LIBRARIES_DIR)/libweak-func-missing.so)
$(call REMOVE,$(LIBRARIES_DIR)/libweak-data-resolved.so)
$(call REMOVE,$(LIBRARIES_DIR)/libweak-data-missing.so)
$(call REMOVE,$(LIBRARIES_DIR)/libweak-plt-resolved.so)
$(call REMOVE,$(LIBRARIES_DIR)/libweak-plt-missing.so)
$(call REMOVE,$(LIBRARIES_DIR)/libstrong-missing.so)

%.o: %.c
$(CC) $(CFLAGS) -fPIE $< -c -o $@
11 changes: 11 additions & 0 deletions src/dlfcn-weak-c/libs/strong_missing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright(c) The Maintainers of Nanvix.
* Licensed under the MIT License.
*/

extern int strong_missing(void);

int call_strong(void)
{
return (strong_missing());
}
21 changes: 21 additions & 0 deletions src/dlfcn-weak-c/libs/weak_data.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright(c) The Maintainers of Nanvix.
* Licensed under the MIT License.
*/

#include <stddef.h>

#ifndef WEAK_DATA_NAME
#define WEAK_DATA_NAME weak_data
#endif

extern int WEAK_DATA_NAME __attribute__((weak));

int read_weak_data(void)
{
if (&WEAK_DATA_NAME != NULL) {
return (WEAK_DATA_NAME);
}

return (-1);
}
21 changes: 21 additions & 0 deletions src/dlfcn-weak-c/libs/weak_func.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright(c) The Maintainers of Nanvix.
* Licensed under the MIT License.
*/

#include <stddef.h>

#ifndef CALLBACK_NAME
#define CALLBACK_NAME main_callback
#endif

extern int CALLBACK_NAME(int) __attribute__((weak));

int try_callback(void)
{
if (&CALLBACK_NAME != NULL) {
return (CALLBACK_NAME(7));
}

return (-1);
}
33 changes: 33 additions & 0 deletions src/dlfcn-weak-c/libs/weak_func_plt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright(c) The Maintainers of Nanvix.
* Licensed under the MIT License.
*/

/*
* Companion to libs/weak_func.c that exercises the *PLT* path for weak
* undefined function references.
*
* Calling the weak symbol without a preceding `&fn != NULL` check lets the
* compiler route the call through the PLT, producing a `R_386_JUMP_SLOT`
* relocation rather than the GOT-indirect `R_386_GLOB_DAT` emitted by
* libs/weak_func.c. Together the two helpers cover both relocation classes
* the dynamic loader must handle for weak undefined symbols.
*
* Safety note: the `missing` variant of this helper produces a `.so` whose
* weak symbol resolves to address zero at runtime. The main test driver
* MUST NOT invoke `call_weak_unchecked` against that variant — doing so
* would jump to NULL. Resolving `dlsym(handle, "call_weak_unchecked")` is
* safe; the goal of the missing variant is only to prove that the loader
* accepts the `.so` under RTLD_NOW despite the weak undefined PLT entry.
*/

#ifndef CALLBACK_NAME
#define CALLBACK_NAME main_callback
#endif

extern int CALLBACK_NAME(int) __attribute__((weak));

int call_weak_unchecked(int x)
{
return (CALLBACK_NAME(x));
}
Loading
Loading