See the comment at https://bugs.winehq.org/show_bug.cgi?id=50257#c20 for context. % cat weak_sym_override_test.c #include <assert.h> #include <dlfcn.h> #include <stdlib.h> #include <stdio.h> #if SHLIB void* calloc(size_t number, size_t size) { printf("libc called\n"); exit(1); } #else int main() { assert(dlopen("override.so", RTLD_NOW) != NULL); setenv("WHATEVER", "1", 0); // uses calloc internally return 0; } #endif % cc -shared -fPIC -DSHLIB weak_sym_override_test.c -o override.so && cc weak_sym_override_test.c -Wl,-rpath,. -o test % ./test libc called Doesn't happen with LD_BIND_NOW=1. No idea how that works on Linux, glibc consistently avoids calling weak symbols it exports, preferring internal versions prefixed with with __ (two underscores) instead, thus there is no obvious way to test it.
Yes this is really a libc problem, not rtld. We do not use internal namespace consistently. OTOH for malloc(3) related functions, I suspect the decision was deliberate to make it possible to interpose system implementation with any other user provided with LD_PRELOAD.
> OTOH for malloc(3) related functions, I suspect the decision was deliberate to make it possible to interpose system implementation with any other user provided with LD_PRELOAD. Well, I tested (before filling the bug) whether this works in general and, as expected, it doesn't: % cat weak_sym_override_test2.c #include <assert.h> #include <dlfcn.h> #include <stdlib.h> #include <stdio.h> #if defined(ORIG) void __attribute__((weak)) test() { printf("original\n"); } void wrapper() { test(); } #elif defined(OVERRIDE) void test() { printf("override\n"); } #else extern void wrapper(); int main() { assert(dlopen("override.so", RTLD_NOW) != NULL); wrapper(); return 0; } #endif % cc -shared -fPIC -DORIG weak_sym_override_test2.c -o orig.so % cc -shared -fPIC -DOVERRIDE weak_sym_override_test2.c -o override.so % cc weak_sym_override_test2.c orig.so -Wl,-rpath,. -o test % ./test original Did I miss some compilation flags or something? How do I deliberately get this effect?
You need to LD_PRELOAD your interposer, I noted it in my comment #1. solo% LD_PRELOAD=./override.so ./test override In your example, dlopened object would be added at the end of the global list and in fact its symbols are interposed by the objects loaded at startup, i.e. the effect is reverse. (And your example misses RTLD_GLOBAL, but does not matter much).