diff options
author | Ian Moffett <ian@osmora.org> | 2024-03-07 17:28:00 -0500 |
---|---|---|
committer | Ian Moffett <ian@osmora.org> | 2024-03-07 17:28:32 -0500 |
commit | bd5969fc876a10b18613302db7087ef3c40f18e1 (patch) | |
tree | 7c2b8619afe902abf99570df2873fbdf40a4d1a1 /lib/mlibc/tests/rtdl | |
parent | a95b38b1b92b172e6cc4e8e56a88a30cc65907b0 (diff) |
lib: Add mlibc
Signed-off-by: Ian Moffett <ian@osmora.org>
Diffstat (limited to 'lib/mlibc/tests/rtdl')
51 files changed, 813 insertions, 0 deletions
diff --git a/lib/mlibc/tests/rtdl/dl_iterate_phdr/libbar.c b/lib/mlibc/tests/rtdl/dl_iterate_phdr/libbar.c new file mode 100644 index 0000000..41ccc56 --- /dev/null +++ b/lib/mlibc/tests/rtdl/dl_iterate_phdr/libbar.c @@ -0,0 +1,3 @@ +// Bar needs to have a relocation against foo in order to set DT_NEEDED. +int foo(void); +int bar() { return foo(); } diff --git a/lib/mlibc/tests/rtdl/dl_iterate_phdr/libfoo.c b/lib/mlibc/tests/rtdl/dl_iterate_phdr/libfoo.c new file mode 100644 index 0000000..9fe07f8 --- /dev/null +++ b/lib/mlibc/tests/rtdl/dl_iterate_phdr/libfoo.c @@ -0,0 +1 @@ +int foo() { return 0; } diff --git a/lib/mlibc/tests/rtdl/dl_iterate_phdr/meson.build b/lib/mlibc/tests/rtdl/dl_iterate_phdr/meson.build new file mode 100644 index 0000000..acb679e --- /dev/null +++ b/lib/mlibc/tests/rtdl/dl_iterate_phdr/meson.build @@ -0,0 +1,7 @@ +libfoo = shared_library('foo', 'libfoo.c') +libbar = shared_library('bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo) +test_depends = [libfoo, libbar] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +libbar_native = shared_library('native-bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo_native, native: true) +test_native_depends = [libfoo_native, libbar_native] diff --git a/lib/mlibc/tests/rtdl/dl_iterate_phdr/test.c b/lib/mlibc/tests/rtdl/dl_iterate_phdr/test.c new file mode 100644 index 0000000..5d48a41 --- /dev/null +++ b/lib/mlibc/tests/rtdl/dl_iterate_phdr/test.c @@ -0,0 +1,91 @@ +#include <assert.h> +#include <link.h> +#include <string.h> +#include <dlfcn.h> +#include <stdio.h> + +#ifdef USE_HOST_LIBC +#define LDSO_PATTERN "ld-linux-" +#define LIBFOO "libnative-foo.so" +#define LIBBAR "libnative-bar.so" +#else +#define LDSO_PATTERN "ld.so" +#define LIBFOO "libfoo.so" +#define LIBBAR "libbar.so" +#endif + +struct result { + int found_ldso; + int found_self; + int found_foo; + int found_bar; +}; + +static int ends_with(const char *suffix, const char *s) { + size_t suffix_len = strlen(suffix); + size_t s_len = strlen(s); + if (s_len < suffix_len) + return 0; + else { + return !strcmp(suffix, s + s_len - suffix_len); + } +} + +static int contains(const char *pattern, const char *s) { + return !!strstr(s, pattern); +} + +static int callback(struct dl_phdr_info *info, size_t size, void *data) { + assert(size == sizeof(struct dl_phdr_info)); + struct result *found = (struct result *) data; + + printf("%s\n", info->dlpi_name); + fflush(stdout); + + if (ends_with("foo.so", info->dlpi_name)) + found->found_foo++; + if (ends_with("bar.so", info->dlpi_name)) + found->found_bar++; + if (contains(LDSO_PATTERN, info->dlpi_name)) + found->found_ldso++; + + if (!strcmp("", info->dlpi_name)) + found->found_self++; + + assert(info->dlpi_phdr); + return 0; +} + +int main() { + struct result found = { 0 }; + assert(!dl_iterate_phdr(callback, &found)); + assert(found.found_ldso == 1); + assert(found.found_self == 1); + assert(found.found_foo == 0); + assert(found.found_bar == 0); + printf("---\n"); + + memset(&found, 0, sizeof(found)); + void *bar = dlopen(LIBBAR, RTLD_LOCAL | RTLD_NOW); + assert(bar); + assert(!dl_iterate_phdr(callback, &found)); + assert(found.found_ldso == 1); + assert(found.found_self == 1); + assert(found.found_bar == 1); + assert(found.found_foo == 1); // Since bar depends on foo. + printf("---\n"); + + memset(&found, 0, sizeof(found)); + void *foo = dlopen(LIBFOO, RTLD_GLOBAL | RTLD_NOW); + assert(foo); + assert(!dl_iterate_phdr(callback, &found)); + assert(found.found_ldso == 1); + assert(found.found_self == 1); + assert(found.found_foo == 1); + assert(found.found_bar == 1); + printf("---\n"); + + dlclose(bar); + dlclose(foo); + return 0; +} diff --git a/lib/mlibc/tests/rtdl/dladdr_local/libfoo.c b/lib/mlibc/tests/rtdl/dladdr_local/libfoo.c new file mode 100644 index 0000000..2903a3d --- /dev/null +++ b/lib/mlibc/tests/rtdl/dladdr_local/libfoo.c @@ -0,0 +1 @@ +char foo_global[] = ""; diff --git a/lib/mlibc/tests/rtdl/dladdr_local/meson.build b/lib/mlibc/tests/rtdl/dladdr_local/meson.build new file mode 100644 index 0000000..4ae6bb3 --- /dev/null +++ b/lib/mlibc/tests/rtdl/dladdr_local/meson.build @@ -0,0 +1,5 @@ +libfoo = shared_library('foo', 'libfoo.c') +test_depends = [libfoo] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +test_native_depends = [libfoo_native] diff --git a/lib/mlibc/tests/rtdl/dladdr_local/test.c b/lib/mlibc/tests/rtdl/dladdr_local/test.c new file mode 100644 index 0000000..c64d259 --- /dev/null +++ b/lib/mlibc/tests/rtdl/dladdr_local/test.c @@ -0,0 +1,21 @@ +#include <dlfcn.h> +#include <assert.h> + +#ifdef USE_HOST_LIBC +#define LIBFOO "libnative-foo.so" +#else +#define LIBFOO "libfoo.so" +#endif + +int main() { + void *foo_handle = dlopen(LIBFOO, RTLD_LOCAL | RTLD_NOW); + assert(foo_handle); + + char *foo_global = (char *)dlsym(foo_handle, "foo_global"); + assert(foo_global); + + Dl_info info; + assert(dladdr((const void *)foo_global, &info) != 0); + + assert(dlclose(foo_handle) == 0); +} diff --git a/lib/mlibc/tests/rtdl/ld_library_path/libfoo.c b/lib/mlibc/tests/rtdl/ld_library_path/libfoo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/lib/mlibc/tests/rtdl/ld_library_path/libfoo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/lib/mlibc/tests/rtdl/ld_library_path/meson.build b/lib/mlibc/tests/rtdl/ld_library_path/meson.build new file mode 100644 index 0000000..95629be --- /dev/null +++ b/lib/mlibc/tests/rtdl/ld_library_path/meson.build @@ -0,0 +1,19 @@ +# Remove the RPATH and set the LD_LIBRARY_PATH environment variable +# instead when running tests to make sure the library can be found +# in a directory specified by the user at runtime + +test_rpath = '$ORIGIN/' + +test_ld_path = meson.build_root() / 'tests' / 'rtdl' / test_name +test_env += ['LD_LIBRARY_PATH=' + test_ld_path] +test_native_env += ['LD_LIBRARY_PATH=' + test_ld_path] + +libfoo = shared_library('foo', 'libfoo.c', + dependencies: libc_dep, + link_args: test_additional_link_args, +) + +libfoo_native = shared_library('native-foo', 'libfoo.c', + link_args: ['-ldl'] + test_additional_link_args, + native: true +) diff --git a/lib/mlibc/tests/rtdl/ld_library_path/test.c b/lib/mlibc/tests/rtdl/ld_library_path/test.c new file mode 100644 index 0000000..15ca200 --- /dev/null +++ b/lib/mlibc/tests/rtdl/ld_library_path/test.c @@ -0,0 +1,16 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#ifdef USE_HOST_LIBC +#define LIBFOO "libnative-foo.so" +#else +#define LIBFOO "libfoo.so" +#endif + +int main() { + void *foo = dlopen(LIBFOO, RTLD_NOW); + assert(foo); + dlclose(foo); +} diff --git a/lib/mlibc/tests/rtdl/meson.build b/lib/mlibc/tests/rtdl/meson.build new file mode 100644 index 0000000..1331899 --- /dev/null +++ b/lib/mlibc/tests/rtdl/meson.build @@ -0,0 +1,56 @@ +rtdl_test_cases = [ + 'dl_iterate_phdr', + 'dladdr_local', + 'ld_library_path', + 'noload-promote', + 'rtld_next', + 'soname', + 'preinit', + 'scope1', + 'scope2', + 'scope3', + 'scope4', + 'scope5', + 'tls_align', +] + +foreach test_name : rtdl_test_cases + test_rpath = meson.build_root() / 'tests' / 'rtdl' / test_name / '' + test_rpath += ':$ORIGIN/' # Workaround old and buggy qemu-user on CI + + test_env = [] + test_link_with = [] + test_depends = [] + test_native_env = [] + test_native_link_with = [] + test_native_depends = [] + test_additional_link_args = [] + + # Build the needed DSOs for the test. This sets the variables above. + subdir(test_name) + + exec = executable('rtdl-' + test_name, [test_name / 'test.c', test_sources], + link_with: test_link_with, + dependencies: libc_dep, + build_rpath: test_rpath, + override_options: test_override_options, + c_args: test_c_args, + link_args: test_link_args + test_additional_link_args, + ) + test(test_name, exec, env: test_env, suite: 'rtdl', depends: test_depends) + + if build_tests_host_libc and not host_libc_excluded_test_cases.contains(test_name) + exec = executable('host-libc-' + test_name, test_name / 'test.c', + link_with: test_native_link_with, + dependencies: rtlib_deps, + build_rpath: test_rpath, + # Don't use ASan here, due to a bug that breaks dlopen() + DT_RUNPATH: + # https://bugzilla.redhat.com/show_bug.cgi?id=1449604 + override_options: 'b_sanitize=undefined', + c_args: ['-D_GNU_SOURCE', '-DUSE_HOST_LIBC'], + link_args: ['-ldl'] + test_additional_link_args, + native: true, + ) + test(test_name, exec, env: test_native_env, suite: ['host-libc', 'rtdl'], depends: test_native_depends) + endif +endforeach diff --git a/lib/mlibc/tests/rtdl/noload-promote/libfoo.c b/lib/mlibc/tests/rtdl/noload-promote/libfoo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/lib/mlibc/tests/rtdl/noload-promote/libfoo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/lib/mlibc/tests/rtdl/noload-promote/meson.build b/lib/mlibc/tests/rtdl/noload-promote/meson.build new file mode 100644 index 0000000..4ae6bb3 --- /dev/null +++ b/lib/mlibc/tests/rtdl/noload-promote/meson.build @@ -0,0 +1,5 @@ +libfoo = shared_library('foo', 'libfoo.c') +test_depends = [libfoo] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +test_native_depends = [libfoo_native] diff --git a/lib/mlibc/tests/rtdl/noload-promote/test.c b/lib/mlibc/tests/rtdl/noload-promote/test.c new file mode 100644 index 0000000..0a6c55c --- /dev/null +++ b/lib/mlibc/tests/rtdl/noload-promote/test.c @@ -0,0 +1,22 @@ +#include <dlfcn.h> +#include <assert.h> +#include <stddef.h> + +#ifdef USE_HOST_LIBC +#define LIBFOO "libnative-foo.so" +#else +#define LIBFOO "libfoo.so" +#endif + +int main() { + void *foo = dlopen(LIBFOO, RTLD_LOCAL | RTLD_NOW); + assert(foo); + + assert(dlsym(RTLD_DEFAULT, "foo") == NULL); + + // Opening a library with RTLD_NOLOAD | RTLD_GLOBAL should promote it to the global scope. + assert(dlopen(LIBFOO, RTLD_NOLOAD | RTLD_GLOBAL | RTLD_NOW) == foo); + assert(dlsym(RTLD_DEFAULT, "foo") != NULL); + + assert(dlopen("does-not-exist.so.1337", RTLD_NOLOAD | RTLD_GLOBAL | RTLD_NOW) == NULL); +} diff --git a/lib/mlibc/tests/rtdl/preinit/libfoo.c b/lib/mlibc/tests/rtdl/preinit/libfoo.c new file mode 100644 index 0000000..9c834ea --- /dev/null +++ b/lib/mlibc/tests/rtdl/preinit/libfoo.c @@ -0,0 +1,18 @@ +#include <stdio.h> +#include <assert.h> + +int fooDone = 0; + +// DSOs do not support pre-initialization functions. + +__attribute__((constructor)) +void fooInit() { + dprintf(1, "initialization function called in foo\n"); + + assert(fooDone == 0); + fooDone++; +} + +int isFooDone() { + return fooDone; +} diff --git a/lib/mlibc/tests/rtdl/preinit/meson.build b/lib/mlibc/tests/rtdl/preinit/meson.build new file mode 100644 index 0000000..1a7f398 --- /dev/null +++ b/lib/mlibc/tests/rtdl/preinit/meson.build @@ -0,0 +1,18 @@ +if host_machine.cpu_family() == 'riscv64' + # gp isn't initialized until after crt1.o runs, so to access + # globals in our pre-initializers we must disable it. + test_additional_link_args = ['-Wl,--no-relax'] +endif + +libfoo = shared_library('foo', 'libfoo.c', + dependencies: libc_dep, + override_options: 'b_sanitize=none', + link_args: test_additional_link_args, +) +test_link_with = [libfoo] + +libfoo_native = shared_library('native-foo', 'libfoo.c', + link_args: ['-ldl'] + test_additional_link_args, + native: true +) +test_native_link_with = [libfoo_native] diff --git a/lib/mlibc/tests/rtdl/preinit/test.c b/lib/mlibc/tests/rtdl/preinit/test.c new file mode 100644 index 0000000..5b5d5e8 --- /dev/null +++ b/lib/mlibc/tests/rtdl/preinit/test.c @@ -0,0 +1,44 @@ +#include <stdio.h> +#include <assert.h> + +int mainDone = 0; + +int isFooDone(); + +void preInit1() { + // Use dprintf because stdout might not be initialized yet. + dprintf(1, "pre-initialization function 1 called in main executable\n"); + + assert(isFooDone() == 0); + assert(mainDone == 0); + mainDone++; +} + +void preInit2() { + dprintf(1, "pre-initialization function 2 called in main executable\n"); + + assert(isFooDone() == 0); + assert(mainDone == 1); + mainDone++; +} + +__attribute__((constructor)) +void mainInit() { + dprintf(1, "initialization function called in main executable\n"); + + assert(isFooDone() == 1); + assert(mainDone == 2); + mainDone++; +} + +// Manually register the pre-initialization functions. +__attribute__((used, section(".preinit_array"))) +static void (*preinitFunctions[])(void) = { + &preInit1, + &preInit2, +}; + +int main() { + assert(isFooDone() == 1); + assert(mainDone == 3); +} diff --git a/lib/mlibc/tests/rtdl/rtld_next/libbar.c b/lib/mlibc/tests/rtdl/rtld_next/libbar.c new file mode 100644 index 0000000..c0950c5 --- /dev/null +++ b/lib/mlibc/tests/rtdl/rtld_next/libbar.c @@ -0,0 +1,16 @@ +#include <dlfcn.h> + +typedef char *charFn(void); + +__attribute__((weak)) +char *definedInBoth() { + return "bar"; +} + +charFn *barGetDefault() { + return (charFn *)dlsym(RTLD_DEFAULT, "definedInBoth"); +} + +charFn *barGetNext() { + return (charFn *)dlsym(RTLD_NEXT, "definedInBoth"); +} diff --git a/lib/mlibc/tests/rtdl/rtld_next/libfoo.c b/lib/mlibc/tests/rtdl/rtld_next/libfoo.c new file mode 100644 index 0000000..1d46b73 --- /dev/null +++ b/lib/mlibc/tests/rtdl/rtld_next/libfoo.c @@ -0,0 +1,17 @@ +#include <dlfcn.h> + +typedef char *charFn(void); + +__attribute__((weak)) +char *definedInBoth() { + return "foo"; +} + +charFn *fooGetDefault() { + return (charFn *)dlsym(RTLD_DEFAULT, "definedInBoth"); +} + +charFn *fooGetNext() { + return (charFn *)dlsym(RTLD_NEXT, "definedInBoth"); +} + diff --git a/lib/mlibc/tests/rtdl/rtld_next/meson.build b/lib/mlibc/tests/rtdl/rtld_next/meson.build new file mode 100644 index 0000000..d156c24 --- /dev/null +++ b/lib/mlibc/tests/rtdl/rtld_next/meson.build @@ -0,0 +1,25 @@ +# Prevent tail calls, as it breaks libc's 'calling object' detection. +no_tail_calls = '-fno-optimize-sibling-calls' + +libfoo = shared_library('foo', 'libfoo.c', + dependencies: libc_dep, + c_args: no_tail_calls, +) +libbar = shared_library('bar', 'libbar.c', + dependencies: libc_dep, + c_args: no_tail_calls, +) +test_link_with = [libfoo, libbar] # foo is linked before bar + +libfoo_native = shared_library('native-foo', 'libfoo.c', + c_args: [no_tail_calls, '-D_GNU_SOURCE'], + link_args: '-ldl', + native: true +) +libbar_native = shared_library('native-bar', 'libbar.c', + c_args: [no_tail_calls, '-D_GNU_SOURCE'], + link_args: '-ldl', + native: true +) +test_native_link_with = [libfoo_native, libbar_native] + diff --git a/lib/mlibc/tests/rtdl/rtld_next/test.c b/lib/mlibc/tests/rtdl/rtld_next/test.c new file mode 100644 index 0000000..3e90a63 --- /dev/null +++ b/lib/mlibc/tests/rtdl/rtld_next/test.c @@ -0,0 +1,26 @@ +#include <string.h> +#include <assert.h> + +typedef char *charFn(void); +charFn *fooGetDefault(void); +charFn *fooGetNext(void); +charFn *barGetDefault(void); +charFn *barGetNext(void); + +int main() { + charFn *ret; + + ret = fooGetDefault(); + assert(ret != NULL); + assert(!strcmp(ret(), "foo")); + + ret = fooGetNext(); + assert(ret != NULL); + assert(!strcmp(ret(), "bar")); + + ret = barGetDefault(); + assert(ret != NULL); + assert(!strcmp(ret(), "foo")); + + assert(barGetNext() == NULL); +} diff --git a/lib/mlibc/tests/rtdl/scope1/libbar.c b/lib/mlibc/tests/rtdl/scope1/libbar.c new file mode 100644 index 0000000..ecf043e --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope1/libbar.c @@ -0,0 +1,14 @@ +char *foo(void); +char *foo_global(void); + +char *bar() { + return "bar"; +} + +char *bar_calls_foo() { + return foo(); +} + +char *bar_calls_foo_global() { + return foo_global(); +} diff --git a/lib/mlibc/tests/rtdl/scope1/libfoo.c b/lib/mlibc/tests/rtdl/scope1/libfoo.c new file mode 100644 index 0000000..b4e1b8c --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope1/libfoo.c @@ -0,0 +1,9 @@ +char *foo() { + return "foo"; +} + +char global[] = "foo global"; + +char *foo_global() { + return global; +} diff --git a/lib/mlibc/tests/rtdl/scope1/meson.build b/lib/mlibc/tests/rtdl/scope1/meson.build new file mode 100644 index 0000000..acb679e --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope1/meson.build @@ -0,0 +1,7 @@ +libfoo = shared_library('foo', 'libfoo.c') +libbar = shared_library('bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo) +test_depends = [libfoo, libbar] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +libbar_native = shared_library('native-bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo_native, native: true) +test_native_depends = [libfoo_native, libbar_native] diff --git a/lib/mlibc/tests/rtdl/scope1/test.c b/lib/mlibc/tests/rtdl/scope1/test.c new file mode 100644 index 0000000..c19915d --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope1/test.c @@ -0,0 +1,90 @@ +#include <stddef.h> +#include <dlfcn.h> +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#ifdef USE_HOST_LIBC +#define LIBFOO "libnative-foo.so" +#define LIBBAR "libnative-bar.so" +#else +#define LIBFOO "libfoo.so" +#define LIBBAR "libbar.so" +#endif + +typedef char *strfn(void); + +int main() { + // We haven't dlopen'd these libs yet, so symbol resolution should fail. + assert(dlsym(RTLD_DEFAULT, "foo") == NULL); + assert(dlsym(RTLD_DEFAULT, "bar") == NULL); + + assert(!dlopen(LIBFOO, RTLD_NOLOAD)); + assert(!dlopen(LIBBAR, RTLD_NOLOAD)); + + void *foo_handle = dlopen(LIBFOO, RTLD_LOCAL | RTLD_NOW); + assert(foo_handle); + assert(dlopen(LIBFOO, RTLD_NOLOAD | RTLD_NOW)); + + strfn *foo_sym = dlsym(foo_handle, "foo"); + assert(foo_sym); + assert(foo_sym()); + assert(!strcmp(foo_sym(), "foo")); + + strfn *foo_global_sym = dlsym(foo_handle, "foo_global"); + assert(foo_global_sym); + assert(foo_global_sym()); + assert(!strcmp(foo_global_sym(), "foo global")); + + assert(dlsym(foo_handle, "doesnotexist") == NULL); + + // Nested opening should work + assert(dlopen(LIBFOO, RTLD_LOCAL | RTLD_NOW) == foo_handle); + assert(dlopen(LIBFOO, RTLD_NOLOAD | RTLD_NOW)); + + // Since we've loaded the same library twice, the addresses should be the same + assert(dlsym(foo_handle, "foo") == foo_sym); + assert(dlsym(foo_handle, "foo_global") == foo_global_sym); + + // libfoo was opened with RTLD_LOCAL, so we shouldn't be able to lookup + // its symbols in the global namespace. + assert(dlsym(RTLD_DEFAULT, "foo") == NULL); + + { + void *bar_handle = dlopen(LIBBAR, RTLD_GLOBAL | RTLD_NOW); + assert(bar_handle); + assert(dlopen(LIBBAR, RTLD_NOLOAD | RTLD_NOW)); + + strfn *bar_sym = dlsym(bar_handle, "bar"); + assert(bar_sym); + assert(bar_sym()); + assert(!strcmp(bar_sym(), "bar")); + + strfn *bar_calls_foo_sym = dlsym(bar_handle, "bar_calls_foo"); + assert(bar_calls_foo_sym); + assert(bar_calls_foo_sym()); + assert(!strcmp(bar_calls_foo_sym(), "foo")); + + strfn *bar_calls_foo_global_sym = dlsym(bar_handle, "bar_calls_foo_global"); + assert(bar_calls_foo_global_sym); + assert(bar_calls_foo_global_sym()); + assert(!strcmp(bar_calls_foo_global_sym(), "foo global")); + + // libbar was opened with RTLD_GLOBAL, so we can find symbols by + // searching in the global scope. + strfn *new_bar_sym = dlsym(RTLD_DEFAULT, "bar"); + assert(new_bar_sym); + assert(new_bar_sym == bar_sym); + + // Note that we loaded libbar with RTLD_GLOBAL, which should pull + // in libfoo's symbols globally too. + strfn *new_foo_sym = dlsym(RTLD_DEFAULT, "foo"); + assert(new_foo_sym); + assert(new_foo_sym == foo_sym); + + assert(dlclose(bar_handle) == 0); + } + + assert(dlclose(foo_handle) == 0); + assert(dlclose(foo_handle) == 0); +} diff --git a/lib/mlibc/tests/rtdl/scope2/libbar.c b/lib/mlibc/tests/rtdl/scope2/libbar.c new file mode 100644 index 0000000..4783c58 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope2/libbar.c @@ -0,0 +1,5 @@ +char *foo_baz_conflict(void); + +char *bar_calls_foo_baz_conflict() { + return foo_baz_conflict(); +} diff --git a/lib/mlibc/tests/rtdl/scope2/libbaz.c b/lib/mlibc/tests/rtdl/scope2/libbaz.c new file mode 100644 index 0000000..fc73adc --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope2/libbaz.c @@ -0,0 +1,3 @@ +char *foo_baz_conflict() { + return "resolved to baz"; +} diff --git a/lib/mlibc/tests/rtdl/scope2/libfoo.c b/lib/mlibc/tests/rtdl/scope2/libfoo.c new file mode 100644 index 0000000..9f7b881 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope2/libfoo.c @@ -0,0 +1,3 @@ +char *foo_baz_conflict() { + return "resolved to foo"; +} diff --git a/lib/mlibc/tests/rtdl/scope2/meson.build b/lib/mlibc/tests/rtdl/scope2/meson.build new file mode 100644 index 0000000..938272c --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope2/meson.build @@ -0,0 +1,9 @@ +libfoo = shared_library('foo', 'libfoo.c') +libbar = shared_library('bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo) +libbaz = shared_library('baz', 'libbaz.c') +test_depends = [libfoo, libbar, libbaz] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +libbar_native = shared_library('native-bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo_native, native: true) +libbaz_native = shared_library('native-baz', 'libbaz.c', native: true) +test_native_depends = [libfoo_native, libbar_native, libbaz_native] diff --git a/lib/mlibc/tests/rtdl/scope2/test.c b/lib/mlibc/tests/rtdl/scope2/test.c new file mode 100644 index 0000000..f4f42dc --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope2/test.c @@ -0,0 +1,36 @@ +#include <stddef.h> +#include <dlfcn.h> +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#ifdef USE_HOST_LIBC +#define LIBBAR "libnative-bar.so" +#define LIBBAZ "libnative-baz.so" +#else +#define LIBBAR "libbar.so" +#define LIBBAZ "libbaz.so" +#endif + +typedef char *strfn(void); + +int main() { + void *baz = dlopen(LIBBAZ, RTLD_LAZY | RTLD_GLOBAL); + assert(baz); + + // At this point, baz is loaded in the global scope. When we load bar locally, + // there is a relocation to `foo_baz_conflict` which is defined in both + // foo (which is a dependency of bar), and baz. In this case baz should win + // since we search the global scope first. + + void *bar = dlopen(LIBBAR, RTLD_LAZY | RTLD_LOCAL); + assert(bar); + + strfn *bfn = dlsym(bar, "bar_calls_foo_baz_conflict"); + assert(!strcmp(bfn(), "resolved to baz")); + + // TODO: Test RTLD_DEEPBIND and DT_SYMBOLIC once we implement it. + + dlclose(bar); + dlclose(baz); +} diff --git a/lib/mlibc/tests/rtdl/scope3/libbar.c b/lib/mlibc/tests/rtdl/scope3/libbar.c new file mode 100644 index 0000000..dc377b6 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope3/libbar.c @@ -0,0 +1,8 @@ +int g = 1; + +int call_foo(); + +int call_bar() { + return call_foo(); +} + diff --git a/lib/mlibc/tests/rtdl/scope3/libbaz.c b/lib/mlibc/tests/rtdl/scope3/libbaz.c new file mode 100644 index 0000000..32524cc --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope3/libbaz.c @@ -0,0 +1,7 @@ +int g = 2; + +int call_foo(); + +int call_baz() { + return call_foo(); +} diff --git a/lib/mlibc/tests/rtdl/scope3/libfoo.c b/lib/mlibc/tests/rtdl/scope3/libfoo.c new file mode 100644 index 0000000..bc86319 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope3/libfoo.c @@ -0,0 +1,5 @@ +int g = 0; + +int call_foo() { + return g; +} diff --git a/lib/mlibc/tests/rtdl/scope3/meson.build b/lib/mlibc/tests/rtdl/scope3/meson.build new file mode 100644 index 0000000..0c98583 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope3/meson.build @@ -0,0 +1,9 @@ +libfoo = shared_library('foo', 'libfoo.c') +libbar = shared_library('bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo) +libbaz = shared_library('baz', 'libbaz.c', build_rpath: test_rpath, link_with: libfoo) +test_depends = [libfoo, libbar, libbaz] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +libbar_native = shared_library('native-bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo_native, native: true) +libbaz_native = shared_library('native-baz', 'libbaz.c', build_rpath: test_rpath, link_with: libfoo_native, native: true) +test_native_depends = [libfoo_native, libbar_native, libbaz_native] diff --git a/lib/mlibc/tests/rtdl/scope3/test.c b/lib/mlibc/tests/rtdl/scope3/test.c new file mode 100644 index 0000000..30fc662 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope3/test.c @@ -0,0 +1,39 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <assert.h> + +#ifdef USE_HOST_LIBC +#define LIBBAR "libnative-bar.so" +#define LIBBAZ "libnative-baz.so" +#else +#define LIBBAR "libbar.so" +#define LIBBAZ "libbaz.so" +#endif + +int main() { + // In this test, we have bar -> foo and baz -> foo (where -> means 'depends on'). + // All three objects contain a definition of a symbol g. Bar calls into foo to retrieve + // what foo thinks g is, but since bar appears earlier in the scope than foo, bar's copy + // of g wins. + // + // Next, we load baz, which is identical to bar. When baz calls into foo to retrieve g, + // foo still sees bar's definition of g, so bar's copy of g wins. + // + // Swapping the load order of bar and baz should therefore change the value of g which + // foo sees. This behaviour is why dlmopen exists. If we ever implement that, we should + // write a similar test and assert that the calls return different results. + + void *libbar = dlopen(LIBBAR, RTLD_LAZY | RTLD_LOCAL); + int (*call_bar)(void) = dlsym(libbar, "call_bar"); + printf("call_bar: %d\n", call_bar()); + assert(call_bar() == 1); + + void *libbaz = dlopen(LIBBAZ, RTLD_LAZY | RTLD_LOCAL); + int (*call_baz)(void) = dlsym(libbaz, "call_baz"); + printf("call_baz: %d\n", call_baz()); + assert(call_baz() == 1); + + + dlclose(libbar); + dlclose(libbaz); +} diff --git a/lib/mlibc/tests/rtdl/scope4/libbar.c b/lib/mlibc/tests/rtdl/scope4/libbar.c new file mode 100644 index 0000000..514e456 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope4/libbar.c @@ -0,0 +1,3 @@ +// Bar needs to have a relocation against foo in order to set DT_NEEDED. +void foo(void); +void bar() { foo(); } diff --git a/lib/mlibc/tests/rtdl/scope4/libbaz.c b/lib/mlibc/tests/rtdl/scope4/libbaz.c new file mode 100644 index 0000000..256a0e3 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope4/libbaz.c @@ -0,0 +1 @@ +void baz() {} diff --git a/lib/mlibc/tests/rtdl/scope4/libfoo.c b/lib/mlibc/tests/rtdl/scope4/libfoo.c new file mode 100644 index 0000000..6710db7 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope4/libfoo.c @@ -0,0 +1,3 @@ +// Foo needs to have a relocation against baz in order to set DT_NEEDED. +void baz(void); +void foo() { baz(); } diff --git a/lib/mlibc/tests/rtdl/scope4/meson.build b/lib/mlibc/tests/rtdl/scope4/meson.build new file mode 100644 index 0000000..804a40c --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope4/meson.build @@ -0,0 +1,9 @@ +libbaz = shared_library('baz', 'libbaz.c') +libfoo = shared_library('foo', 'libfoo.c', build_rpath: test_rpath, link_with: libbaz) +libbar = shared_library('bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo) +test_depends = [libfoo, libbar, libbaz] + +libbaz_native = shared_library('native-baz', 'libbaz.c', native: true) +libfoo_native = shared_library('native-foo', 'libfoo.c', build_rpath: test_rpath, link_with: libbaz_native, native: true) +libbar_native = shared_library('native-bar', 'libbar.c', build_rpath: test_rpath, link_with: libfoo_native, native: true) +test_native_depends = [libfoo_native, libbar_native, libbaz_native] diff --git a/lib/mlibc/tests/rtdl/scope4/test.c b/lib/mlibc/tests/rtdl/scope4/test.c new file mode 100644 index 0000000..2365e26 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope4/test.c @@ -0,0 +1,36 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <assert.h> + +#ifdef USE_HOST_LIBC +#define LIBFOO "libnative-foo.so" +#define LIBBAR "libnative-bar.so" +#define LIBBAZ "libnative-baz.so" +#else +#define LIBFOO "libfoo.so" +#define LIBBAR "libbar.so" +#define LIBBAZ "libbaz.so" +#endif + +int main() { + // In this test, we have foo -> baz, bar -> foo (where '->' means 'depends on'). + // We first load foo with RTLD_LOCAL, and then load bar with RTLD_GLOBAL. + // This should bring foo and bar into the global scope. + + void *foo = dlopen(LIBFOO, RTLD_LOCAL | RTLD_NOW); + assert(foo); + assert(dlsym(foo, "foo")); + assert(dlsym(foo, "baz")); + assert(!dlsym(RTLD_DEFAULT, "foo")); + assert(!dlsym(RTLD_DEFAULT, "baz")); + + void *bar = dlopen(LIBBAR, RTLD_GLOBAL | RTLD_NOW); + assert(bar); + assert(dlsym(bar, "bar")); + assert(dlsym(RTLD_DEFAULT, "bar")); + assert(dlsym(RTLD_DEFAULT, "foo")); + assert(dlsym(RTLD_DEFAULT, "baz")); + + dlclose(foo); + dlclose(bar); +} diff --git a/lib/mlibc/tests/rtdl/scope5/libfoo.c b/lib/mlibc/tests/rtdl/scope5/libfoo.c new file mode 100644 index 0000000..85e6cd8 --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope5/libfoo.c @@ -0,0 +1 @@ +void foo() {} diff --git a/lib/mlibc/tests/rtdl/scope5/meson.build b/lib/mlibc/tests/rtdl/scope5/meson.build new file mode 100644 index 0000000..22ade5b --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope5/meson.build @@ -0,0 +1,5 @@ +libfoo = shared_library('foo', 'libfoo.c') +test_link_with = [libfoo] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +test_native_link_with = [libfoo_native] diff --git a/lib/mlibc/tests/rtdl/scope5/test.c b/lib/mlibc/tests/rtdl/scope5/test.c new file mode 100644 index 0000000..2c11c2e --- /dev/null +++ b/lib/mlibc/tests/rtdl/scope5/test.c @@ -0,0 +1,27 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <assert.h> + +#ifdef USE_HOST_LIBC +#define LIBFOO "libnative-foo.so" +#else +#define LIBFOO "libfoo.so" +#endif + +// We need to have a relocation against foo for DT_NEEDED. +void foo(); +void bar() { foo(); } + +int main() { + // In this test, we have exec -> foo (where '->' means 'depends on'). + // This means that foo is in the global scope due to DT_NEEDED. + // We then dlopen it again with RTLD_LOCAL, which should just return + // the already-loaded object, but used to crash in the mlibc linker instead. + + void *foo = dlopen(LIBFOO, RTLD_LOCAL | RTLD_NOW); + assert(foo); + assert(dlsym(foo, "foo")); + assert(dlsym(RTLD_DEFAULT, "foo")); + + dlclose(foo); +} diff --git a/lib/mlibc/tests/rtdl/soname/libbar.c b/lib/mlibc/tests/rtdl/soname/libbar.c new file mode 100644 index 0000000..7f9f475 --- /dev/null +++ b/lib/mlibc/tests/rtdl/soname/libbar.c @@ -0,0 +1 @@ +char *name() { return "bar"; } diff --git a/lib/mlibc/tests/rtdl/soname/libfoo.c b/lib/mlibc/tests/rtdl/soname/libfoo.c new file mode 100644 index 0000000..abe0999 --- /dev/null +++ b/lib/mlibc/tests/rtdl/soname/libfoo.c @@ -0,0 +1 @@ +char *name() { return "foo"; } diff --git a/lib/mlibc/tests/rtdl/soname/meson.build b/lib/mlibc/tests/rtdl/soname/meson.build new file mode 100644 index 0000000..1eb97a8 --- /dev/null +++ b/lib/mlibc/tests/rtdl/soname/meson.build @@ -0,0 +1,17 @@ +# Create two libraries with the same SONAME. +bar_soname = '-Wl,-soname=libbar.so' + +libfoo = shared_library('foo', 'libfoo.c', link_args: '-Wl,-soname=libbar.so') +libbar = shared_library('bar', 'libbar.c', link_args: '-Wl,-soname=libbar.so') +test_depends = [libfoo, libbar] + +libfoo_native = shared_library('native-foo', 'libfoo.c', + link_args: ['-ldl', '-Wl,-soname=libnative-bar.so'], + native: true +) +libbar_native = shared_library('native-bar', 'libbar.c', + link_args: ['-ldl', '-Wl,-soname=libnative-bar.so'], + native: true +) +test_native_depends = [libfoo_native, libbar_native] + diff --git a/lib/mlibc/tests/rtdl/soname/test.c b/lib/mlibc/tests/rtdl/soname/test.c new file mode 100644 index 0000000..23e5b7a --- /dev/null +++ b/lib/mlibc/tests/rtdl/soname/test.c @@ -0,0 +1,33 @@ +#include <dlfcn.h> +#include <stdio.h> +#include <assert.h> +#include <string.h> + +#ifdef USE_HOST_LIBC +#define LIBFOO "libnative-foo.so" +#define LIBBAR "libnative-bar.so" +#else +#define LIBFOO "libfoo.so" +#define LIBBAR "libbar.so" +#endif + +int main() { + void *foo = dlopen(LIBFOO, RTLD_NOW); + void *bar = dlopen(LIBBAR, RTLD_NOW); + assert(foo); + assert(bar); + + // Since these libraries have the same SONAME, they should return the same thing. + assert(foo == bar); + + char *(*fooSym)(void) = dlsym(foo, "name"); + char *(*barSym)(void) = dlsym(bar, "name"); + assert(fooSym && barSym); + assert(fooSym() && barSym()); + printf("foo: name() = \"%s\"\n", fooSym()); + printf("bar: name() = \"%s\"\n", barSym()); + assert(!strcmp(fooSym(), barSym())); + + dlclose(foo); + dlclose(bar); +} diff --git a/lib/mlibc/tests/rtdl/tls_align/libbar.c b/lib/mlibc/tests/rtdl/tls_align/libbar.c new file mode 100644 index 0000000..3f8d6a7 --- /dev/null +++ b/lib/mlibc/tests/rtdl/tls_align/libbar.c @@ -0,0 +1 @@ +_Thread_local __attribute__((aligned(8))) char bar_thread_local[8] = "Hello!"; diff --git a/lib/mlibc/tests/rtdl/tls_align/libfoo.c b/lib/mlibc/tests/rtdl/tls_align/libfoo.c new file mode 100644 index 0000000..8d98177 --- /dev/null +++ b/lib/mlibc/tests/rtdl/tls_align/libfoo.c @@ -0,0 +1 @@ +_Thread_local __attribute__((aligned(16))) char foo_thread_local[8] = "Hello!"; diff --git a/lib/mlibc/tests/rtdl/tls_align/meson.build b/lib/mlibc/tests/rtdl/tls_align/meson.build new file mode 100644 index 0000000..61d0fd9 --- /dev/null +++ b/lib/mlibc/tests/rtdl/tls_align/meson.build @@ -0,0 +1,7 @@ +libfoo = shared_library('foo', 'libfoo.c') +libbar = shared_library('bar', 'libbar.c') +test_link_with = [libfoo, libbar] + +libfoo_native = shared_library('native-foo', 'libfoo.c', native: true) +libbar_native = shared_library('native-bar', 'libbar.c', native: true) +test_native_link_with = [libfoo_native, libbar_native] diff --git a/lib/mlibc/tests/rtdl/tls_align/test.c b/lib/mlibc/tests/rtdl/tls_align/test.c new file mode 100644 index 0000000..4381831 --- /dev/null +++ b/lib/mlibc/tests/rtdl/tls_align/test.c @@ -0,0 +1,10 @@ +#include <assert.h> +#include <stdint.h> + +extern _Thread_local char foo_thread_local[]; +extern _Thread_local char bar_thread_local[]; + +int main() { + assert(!((uintptr_t)foo_thread_local & (16 - 1))); + assert(!((uintptr_t)bar_thread_local & (8 - 1))); +} |