3

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?

Luca
  • 9,259
  • 5
  • 46
  • 59
florenz
  • 83
  • 1
  • 4

1 Answers1

2

This is not what I expected.

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_PRELOAD libtcmalloc.so, and suddenly all malloc and free calls resolve to tcmalloc.

On ELF systems, you can modify this behavior with the -Bsymbolic linker flag (use -Wl,-Bsymbolic when passing it through GCC). Beware: -Bsymbolic goes "against the system", and you may get many unexpected and undesirable side-effects as a result.

See also this answer.

Community
  • 1
  • 1
Employed Russian
  • 199,314
  • 34
  • 295
  • 362