Simple scenario
- Application uses
write
function fromlibc
and links to shared library. - Shared library defines
write
function. - Original
write
function fromlibc
will be overloaded by its version from the shared library.
Nested scenario
- Application uses
write
function fromlibc
. - Shared library 1 does not defines its own
write
but depends on shared library 2. - Shared library 2 defines
write
function. - No.
write
function will NOT be replaced with a version from the second shared library.
I do want to understand Why so ? How to make it work with nested shared library dependencies.
Here is exact code examples:
main.c
#include <unistd.h>int main() { write(1,"Hello\n",6); return 0;}
shared-lib-1.c
#include <unistd.h>__attribute__((constructor))void shared_lib_1_constructor(void) { char str[] = "shared-lib-1-constructor\n"; write(1, str, sizeof(str)); }
shared-lib-2.c
#include <stdlib.h>#include <unistd.h>ssize_t write(int fd, const void *buf, size_t count) { exit(1); return 0;}__attribute__((constructor))void shared_lib_2_constructor(void) { char str[] = "shared-lib-2-constructor\n"; write(1, str, sizeof(str)); }
build.sh
#!/usr/bin/env shset -vset -egcc -g -fPIC -shared shared-lib-2.c -o libshared-lib-2.sogcc -g -fPIC -shared -Wl,-rpath . -L`pwd` -lshared-lib-2 shared-lib-1.c -o libshared-lib-1.sogcc -g -L`pwd` -Wl,-rpath . -lshared-lib-1 main.c
Execution of the a.out
gives:
[smirnov@localhost tmp]$ ./a.out shared-lib-2-constructorshared-lib-1-constructorHello
write
was not overwritten by shared-lib-2. It was completely ignored even from shared-lib-2 code.
How does it work with nested libraries ? Moving write
definition to shared-lib-1 does work, it overloads glibc
version and exits from application.
Running application with LD_DEBUG=all
shows order of resolving:
36441: Initial object scopes 36441: object=./a.out [0] 36441: scope 0: ./a.out ./libshared-lib-1.so /lib64/libc.so.6 ./libshared-lib-2.so /lib64/ld-linux-x86-64.so.2... 36441: calling init: ./libshared-lib-2.so 36441: 36441: symbol=write; lookup in file=./a.out [0] 36441: symbol=write; lookup in file=./libshared-lib-1.so [0] 36441: symbol=write; lookup in file=/lib64/libc.so.6 [0] 36441: binding file ./libshared-lib-2.so [0] to /lib64/libc.so.6 [0]: normal symbol `write'
Why libc
placed in between shared-lib-1 and shared-lib-2 ?It seems that ld
resolves in the following order:
- all dependencies of a.out, but not recursivelly, only first level
- all subdependencies, but not recursivelly
- so on...
The only solution I've found is to use
LD_DYNAMIC_WEAK=1 ./a.out
Is there a way to fix behavior in advance ?