I have the following C files:
Base.h:
void g();
void h();
Base.c:
#include <stdio.h>
#include <Base.h>
void g() {
printf("This is lib g\n");
h();
}
void h() {
printf("This is lib h\n");
}
A.c:
#include <stdio.h>
#include <Base.h>
void h() {
printf("This is A h\n");
}
void main() {
g();
h();
}
I compile and link as follows:
$ gcc -c -fPIC -o Base.o -I. Base.c
$ gcc -shared -o libBase.so Base.o
$ gcc -c -o A.o A.c
$ gcc -o A A.o -lBase -L.
Now I run the program
$ LD_LIBRARY_PATH=. ./A
and obtain:
This is lib g
This is A h
This is A h
That means, the call to h in libBase is resolved by h of A.o. This is not what I expected. I either expected the dynamic linker to resolve the call to h in libBase with h in libBase or an error message in the fourth gcc call.
If I rename h in A.c to h1
#include <stdio.h>
#include <Base.h>
void h1() {
printf("This is A h1\n");
}
void main() {
g();
h1();
}
I obtain
This is lib g
This is lib h
This is A h1
So, in this case h is resolved as I expected.
What do I have to do to either obtain an error message or to have the call in g resolved to h in libBase?
Your expectation is wrong. This is the way shared libraries on most UNIX systems work: the loader simply goes down the list of loaded libraries, and tries to find the given symbol. The first library to define the symbol “wins”.
This behavior has many advantages. One example: you can
LD_PRELOADlibtcmalloc.so, and suddenly allmallocandfreecalls resolve totcmalloc.On ELF systems, you can modify this behavior with the
-Bsymboliclinker flag (use-Wl,-Bsymbolicwhen passing it through GCC). Beware:-Bsymbolicgoes “against the system”, and you may get many unexpected and undesirable side-effects as a result.See also this answer.