user: implement mlibc as the libc, finally.
It's finally done.. Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
# only the test executable is required
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
__attribute__((destructor)) void destroy() {
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
int main() {
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# only the test executable is required
|
||||
@@ -0,0 +1,43 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
// Make sure files are flushed on process exit.
|
||||
|
||||
int main() {
|
||||
int pipefd[2];
|
||||
if (pipe(pipefd) < 0) {
|
||||
perror("pipe");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pid_t child = fork();
|
||||
if (child < 0) {
|
||||
perror("fork");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This should sit in libc's buffer and be flushed on exit.
|
||||
const char *message = "hello world";
|
||||
|
||||
if (child == 0) {
|
||||
// Child
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
printf("%s", message);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Parent
|
||||
wait(NULL);
|
||||
|
||||
char buffer[64];
|
||||
ssize_t len = read(pipefd[0], buffer, sizeof(buffer));
|
||||
assert(len > 0);
|
||||
buffer[len] = 0;
|
||||
|
||||
return strcmp(buffer, message) != 0;
|
||||
}
|
||||
|
||||
@@ -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(); }
|
||||
@@ -0,0 +1 @@
|
||||
int foo() { return 0; }
|
||||
@@ -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]
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
char foo_global[] = "";
|
||||
@@ -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]
|
||||
@@ -0,0 +1,23 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
env = os.environ.copy()
|
||||
|
||||
# parse environment variable assignments
|
||||
i = 0
|
||||
while i < len(args) and '=' in args[i]:
|
||||
var, val = args[i].split('=', 1)
|
||||
env[var] = val
|
||||
i += 1
|
||||
|
||||
if i < len(args):
|
||||
exe_wrapper = os.environ.get('MESON_EXE_WRAPPER')
|
||||
if exe_wrapper:
|
||||
cmd = [exe_wrapper] + args[i:]
|
||||
else:
|
||||
cmd = args[i:]
|
||||
os.execvpe(cmd[0], cmd, env)
|
||||
else:
|
||||
print("No command provided to execute.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,11 @@
|
||||
#include <stdint.h>
|
||||
|
||||
static uint64_t foo_impl(void) {
|
||||
return 69;
|
||||
}
|
||||
|
||||
static uint64_t (*foo_resolver(void))(void) {
|
||||
return foo_impl;
|
||||
}
|
||||
|
||||
uint64_t foo(void) __attribute__((ifunc("foo_resolver")));
|
||||
@@ -0,0 +1,5 @@
|
||||
libifunc_test = shared_library('ifunc-test-dlopen', 'lib.c', dependencies: libc_dep, install: true)
|
||||
test_depends = [libifunc_test]
|
||||
|
||||
libifunc_test_native = shared_library('native-ifunc-test-dlopen', 'lib.c', native: true)
|
||||
test_native_depends = [libifunc_test_native]
|
||||
@@ -0,0 +1,23 @@
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
int foo(void);
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define LIB "libnative-ifunc-test-dlopen.so"
|
||||
#else
|
||||
#define LIB "libifunc-test-dlopen.so"
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
void *handle = dlopen(LIB, RTLD_NOW | RTLD_LOCAL);
|
||||
assert(handle);
|
||||
|
||||
int (*func)() = (int (*)()) dlsym(handle, "foo");
|
||||
assert(func);
|
||||
|
||||
int ret = func();
|
||||
assert(ret == 69);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#include <stdint.h>
|
||||
|
||||
static uint64_t foo_impl(void) {
|
||||
return 420;
|
||||
}
|
||||
|
||||
#if defined(__riscv) && !defined(USE_HOST_LIBC)
|
||||
#include <sys/hwprobe.h>
|
||||
#elif defined(__riscv)
|
||||
int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count, size_t cpusetsize, cpu_set_t *cpus,
|
||||
unsigned int flags) __attribute__((access(read_write, 1, 2)));
|
||||
|
||||
typedef int (*__riscv_hwprobe_t)(struct riscv_hwprobe *pairs, size_t pair_count,
|
||||
size_t cpusetsize, cpu_set_t *cpus, unsigned int flags) __attribute__((access(read_write, 1, 2)));
|
||||
#endif
|
||||
|
||||
#if defined(__riscv)
|
||||
#include <stdlib.h>
|
||||
|
||||
static uint64_t (*foo_resolver(long long, __riscv_hwprobe_t hwprobe, void *))(void) {
|
||||
if (!hwprobe)
|
||||
return NULL;
|
||||
struct riscv_hwprobe pairs[1] = {
|
||||
{ .key = 4, .value = 0 }
|
||||
};
|
||||
hwprobe(pairs, 1, 0, 0, 0);
|
||||
return foo_impl;
|
||||
}
|
||||
#else
|
||||
static uint64_t (*foo_resolver(void))(void) {
|
||||
return foo_impl;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t foo(void) __attribute__((ifunc("foo_resolver")));
|
||||
@@ -0,0 +1,5 @@
|
||||
libifunc_test = shared_library('ifunc-test', 'lib.c', dependencies: libc_dep)
|
||||
test_link_with = [libifunc_test]
|
||||
|
||||
libifunc_test_native = shared_library('native-ifunc-test', 'lib.c', native: true)
|
||||
test_native_link_with = [libifunc_test_native]
|
||||
@@ -0,0 +1,10 @@
|
||||
#include <assert.h>
|
||||
|
||||
int foo(void);
|
||||
|
||||
int main(void) {
|
||||
int res = foo();
|
||||
assert(res == 420);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void foo() {}
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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.global_build_root() / 'tests' / 'rtld' / 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,
|
||||
)
|
||||
|
||||
test_depends = [libfoo]
|
||||
|
||||
libfoo_native = shared_library('native-foo', 'libfoo.c',
|
||||
link_args: ['-ldl'] + test_additional_link_args,
|
||||
native: true
|
||||
)
|
||||
test_native_depends = [libfoo_native]
|
||||
@@ -0,0 +1,18 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
rtld_test_cases = [
|
||||
'dl_iterate_phdr',
|
||||
'dladdr_local',
|
||||
'ld_library_path',
|
||||
'noload-promote',
|
||||
'rtld_next',
|
||||
'soname',
|
||||
'preinit',
|
||||
'destroy1',
|
||||
'destroy2',
|
||||
'scope1',
|
||||
'scope2',
|
||||
'scope3',
|
||||
'scope4',
|
||||
'scope5',
|
||||
'search-order',
|
||||
'tls_align',
|
||||
'relr',
|
||||
'symver',
|
||||
]
|
||||
|
||||
if host_machine.cpu_family() != 'm68k'
|
||||
rtld_test_cases += [
|
||||
'ifunc',
|
||||
'ifunc-dlopen',
|
||||
]
|
||||
endif
|
||||
|
||||
host_libc_rtld_nosan_test_cases = [
|
||||
'preinit',
|
||||
]
|
||||
|
||||
py = import('python').find_installation('python3')
|
||||
|
||||
foreach test_name : rtld_test_cases
|
||||
test_rpath = meson.global_build_root() / 'tests' / 'rtld' / 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 = []
|
||||
test_skipped = false
|
||||
|
||||
# Build the needed DSOs for the test. This sets the variables above.
|
||||
subdir(test_name)
|
||||
|
||||
if test_skipped
|
||||
exec = executable('rtld-' + test_name, ['skipped.c', test_sources],
|
||||
dependencies: libc_dep,
|
||||
override_options: test_override_options,
|
||||
c_args: test_c_args,
|
||||
link_args: test_link_args,
|
||||
)
|
||||
test(test_name, exec, suite: 'rtld')
|
||||
if build_tests_host_libc and not host_libc_excluded_test_cases.contains('rtld' / test_name)
|
||||
test(test_name, exec, suite: ['host-libc', 'rtld'])
|
||||
endif
|
||||
continue
|
||||
endif
|
||||
|
||||
exec = executable('rtld-' + 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,
|
||||
)
|
||||
args = [
|
||||
meson.global_source_root() + '/tests/rtld/env.py',
|
||||
test_env,
|
||||
exec,
|
||||
]
|
||||
# wrap the test so we can control the environment variables without meson interfering
|
||||
test(test_name, py, args: args, suite: 'rtld', depends: test_depends)
|
||||
|
||||
if build_tests_host_libc and not host_libc_excluded_test_cases.contains('rtld' / test_name)
|
||||
if test_name in host_libc_rtld_nosan_test_cases
|
||||
host_libc_rtld_sanitize_options = 'b_sanitize=none'
|
||||
else
|
||||
# Don't use ASan here, due to a bug that breaks dlopen() + DT_RUNPATH:
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=1449604
|
||||
host_libc_rtld_sanitize_options = 'b_sanitize=undefined'
|
||||
endif
|
||||
|
||||
exec = executable('host-libc-' + test_name, test_name / 'test.c',
|
||||
link_with: test_native_link_with,
|
||||
dependencies: rtlib_deps,
|
||||
build_rpath: test_rpath,
|
||||
override_options: host_libc_rtld_sanitize_options,
|
||||
c_args: ['-D_GNU_SOURCE', '-DUSE_HOST_LIBC'],
|
||||
link_args: ['-ldl'] + test_additional_link_args,
|
||||
native: true,
|
||||
)
|
||||
args = [
|
||||
meson.global_source_root() + '/tests/rtld/env.py',
|
||||
test_native_env,
|
||||
exec,
|
||||
]
|
||||
test(test_name, py, args: args, suite: ['host-libc', 'rtld'], depends: test_native_depends)
|
||||
endif
|
||||
endforeach
|
||||
@@ -0,0 +1 @@
|
||||
void foo() {}
|
||||
@@ -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]
|
||||
@@ -0,0 +1,24 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
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,
|
||||
override_options: 'b_sanitize=none',
|
||||
native: true
|
||||
)
|
||||
test_native_link_with = [libfoo_native]
|
||||
@@ -0,0 +1,46 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
test_additional_link_args += ['-Wl,-z,pack-relative-relocs']
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
|
||||
const char* strings[] = {
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
"", "", "", "", "", "", "", "",
|
||||
};
|
||||
|
||||
int main() {
|
||||
for(size_t i = 0; i < sizeof(strings) / sizeof(*strings); ++i) {
|
||||
assert(strings[i][0] == 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# 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',
|
||||
override_options: 'b_sanitize=none',
|
||||
native: true
|
||||
)
|
||||
libbar_native = shared_library('native-bar', 'libbar.c',
|
||||
c_args: [no_tail_calls, '-D_GNU_SOURCE'],
|
||||
link_args: '-ldl',
|
||||
override_options: 'b_sanitize=none',
|
||||
native: true
|
||||
)
|
||||
test_native_link_with = [libfoo_native, libbar_native]
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
char *foo(void);
|
||||
char *foo_global(void);
|
||||
|
||||
char *bar(void) {
|
||||
return "bar";
|
||||
}
|
||||
|
||||
char *bar_calls_foo(void) {
|
||||
return foo();
|
||||
}
|
||||
|
||||
char *bar_calls_foo_global(void) {
|
||||
return foo_global();
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
char *foo(void) {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
char global[] = "foo global";
|
||||
|
||||
char *foo_global(void) {
|
||||
return global;
|
||||
}
|
||||
@@ -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]
|
||||
@@ -0,0 +1,92 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
char *foo_baz_conflict(void);
|
||||
|
||||
char *bar_calls_foo_baz_conflict(void) {
|
||||
return foo_baz_conflict();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
char *foo_baz_conflict() {
|
||||
return "resolved to baz";
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
char *foo_baz_conflict() {
|
||||
return "resolved to foo";
|
||||
}
|
||||
@@ -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]
|
||||
@@ -0,0 +1,38 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
int g = 1;
|
||||
|
||||
int call_foo(void);
|
||||
|
||||
int call_bar(void) {
|
||||
return call_foo();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
int g = 2;
|
||||
|
||||
int call_foo(void);
|
||||
|
||||
int call_baz(void) {
|
||||
return call_foo();
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
int g = 0;
|
||||
|
||||
int call_foo(void) {
|
||||
return g;
|
||||
}
|
||||
@@ -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]
|
||||
@@ -0,0 +1,41 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Bar needs to have a relocation against foo in order to set DT_NEEDED.
|
||||
void foo(void);
|
||||
void bar() { foo(); }
|
||||
@@ -0,0 +1 @@
|
||||
void baz() {}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Foo needs to have a relocation against baz in order to set DT_NEEDED.
|
||||
void baz(void);
|
||||
void foo() { baz(); }
|
||||
@@ -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]
|
||||
@@ -0,0 +1,38 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
void foo() {}
|
||||
@@ -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]
|
||||
@@ -0,0 +1,29 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define LIBFOO "libnative-foo.so"
|
||||
#else
|
||||
#define LIBFOO "libfoo.so"
|
||||
#endif
|
||||
|
||||
int foo(void);
|
||||
|
||||
int bar(void) {
|
||||
return foo();
|
||||
}
|
||||
|
||||
[[gnu::constructor]] void init(void) {
|
||||
void *libfoo = dlopen(LIBFOO, RTLD_LOCAL | RTLD_NOW);
|
||||
assert(libfoo);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
int foo(void) {
|
||||
return 0xCAFEBABE;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# meson does not have native custom_target, so when cross compiling it gets
|
||||
# unhappy that we're trying to link a "host" library (which is actually built
|
||||
# for the build machine) to a build executable. This means we can't patchelf
|
||||
# native libraries if we want cross compilation to work.
|
||||
|
||||
patchelf = find_program('patchelf', required: false)
|
||||
|
||||
if patchelf.found()
|
||||
# Overwrite LD_LIBRARY_PATH to make meson not fix our shenanigans
|
||||
# Otherwise, meson's LD_LIBRARY_PATH makes this test useless because libfoo
|
||||
# will be found through it instead of failing when libfoo is not found
|
||||
test_env += ['LD_LIBRARY_PATH=']
|
||||
test_native_env += ['LD_LIBRARY_PATH=']
|
||||
|
||||
libfoo = shared_library('foo', 'libfoo.c', link_args: test_additional_link_args)
|
||||
libbar_unpatched = shared_library('bar-unpatched', 'libbar.c',
|
||||
link_args: test_additional_link_args,
|
||||
override_options: 'b_sanitize=none',
|
||||
dependencies: libc_dep,
|
||||
link_with: libfoo)
|
||||
libbar = custom_target('patch-libbar',
|
||||
command: [patchelf,
|
||||
'--remove-rpath',
|
||||
'--set-soname', 'libbar.so',
|
||||
libbar_unpatched,
|
||||
'--output', '@OUTPUT0@'],
|
||||
output: ['libbar.so'],
|
||||
)
|
||||
test_link_with = [libbar, libfoo]
|
||||
else
|
||||
test_skipped = true
|
||||
endif
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define LIBBAR "libnative-bar.so"
|
||||
#else
|
||||
#define LIBBAR "libbar.so"
|
||||
#endif
|
||||
|
||||
int foo();
|
||||
int bar();
|
||||
|
||||
int main() {
|
||||
void *libbar = dlopen(LIBBAR, RTLD_LOCAL | RTLD_NOW);
|
||||
assert(libbar);
|
||||
|
||||
assert(foo() == (int) 0xCAFEBABE);
|
||||
assert(bar() == (int) 0xCAFEBABE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
int main() {
|
||||
return 77;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
char *name() { return "bar"; }
|
||||
@@ -0,0 +1 @@
|
||||
char *name(void) { return "foo"; }
|
||||
@@ -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]
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
int foo_v1(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int foo_v2(void) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
int foo_v3(void) {
|
||||
return 3;
|
||||
}
|
||||
|
||||
asm(".symver foo_v1, foo@FOO_1");
|
||||
asm(".symver foo_v2, foo@FOO_2");
|
||||
asm(".symver foo_v3, foo@@FOO_3"); // default version (due to @@ vs @)
|
||||
@@ -0,0 +1,14 @@
|
||||
FOO_1 {
|
||||
global: foo;
|
||||
local: *;
|
||||
};
|
||||
|
||||
FOO_2 {
|
||||
global: foo;
|
||||
local: *;
|
||||
};
|
||||
|
||||
FOO_3 {
|
||||
global: foo;
|
||||
local: *;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
version_script = meson.current_source_dir() / 'libfoo.ver'
|
||||
|
||||
libfoo = shared_library('foo', 'libfoo.c',
|
||||
link_args : ['-Wl,--version-script', version_script])
|
||||
test_depends = [libfoo]
|
||||
test_link_with = [libfoo]
|
||||
|
||||
libfoo_native = shared_library('native-foo', 'libfoo.c', native: true,
|
||||
link_args : ['-Wl,--version-script', version_script])
|
||||
test_native_depends = [libfoo_native]
|
||||
test_native_link_with = [libfoo_native]
|
||||
@@ -0,0 +1,39 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifdef USE_HOST_LIBC
|
||||
#define LIBFOO "libnative-foo.so"
|
||||
#else
|
||||
#define LIBFOO "libfoo.so"
|
||||
#endif
|
||||
|
||||
int foo(void);
|
||||
|
||||
int main() {
|
||||
int ver = foo();
|
||||
fprintf(stderr, "called foo version %d\n", ver);
|
||||
assert(ver == 3); // version 3 is the default for libfoo.so
|
||||
|
||||
void *libfoo_h = dlopen(LIBFOO, RTLD_GLOBAL | RTLD_NOW);
|
||||
assert(libfoo_h);
|
||||
|
||||
int (*foo1)(void) = dlvsym(libfoo_h, "foo", "FOO_1");
|
||||
assert(foo1);
|
||||
|
||||
int (*foo2)(void) = dlvsym(libfoo_h, "foo", "FOO_2");
|
||||
assert(foo2);
|
||||
|
||||
int (*foo3)(void) = dlvsym(libfoo_h, "foo", "FOO_3");
|
||||
assert(foo3);
|
||||
|
||||
int (*foo_def)(void) = dlsym(libfoo_h, "foo");
|
||||
assert(foo_def);
|
||||
|
||||
assert(foo1() == 1);
|
||||
assert(foo2() == 2);
|
||||
assert(foo3() == 3);
|
||||
assert(foo3 == foo_def);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
_Thread_local __attribute__((aligned(8))) char bar_thread_local[8] = "Hello!";
|
||||
@@ -0,0 +1 @@
|
||||
_Thread_local __attribute__((aligned(16))) char foo_thread_local[8] = "Hello!";
|
||||
@@ -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]
|
||||
@@ -0,0 +1,12 @@
|
||||
#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)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user