Search code examples
clinuxshared-librariesstatic-linkingposition-independent-code

When static library needs position independent code


There are 3 files a.c, b.c, main.c:

// a.c
int a() { return 123; }

// b.c
extern int a();
int b() { return a(); }

// main.c
#include <stdio.h>
extern int b();
int main() {
    printf("%d\n", b());
    return 0;
}

a.c is build as a static library liba.a.
b.c is build as a shared library libb.so.
liba.a is then linked with (i.e. included into) libb.so.
main.c is built as an executable, and dynamically linked to libb.so.

The Makefile looks like:

all:
    gcc -c -fPIC -o a.o a.c
    ar rcs liba.a a.o

    gcc -c -fPIC -o b.o b.c
    gcc -shared -o libb.so b.o liba.a

    gcc -o main -L. -lb

b.o is compiled with the position-independent code flag, because it becomes a shared library. Is it necessary to also build a.o as position-independent code, because it will be included into a shared library? At least on the system I'm testing it on, it seems to work fine when the first -fPIC is removed.

Also, if main.c would be changed to also declare extern int a() and call it (assuming libb.so makes it visible), would it then be needed to compile a.o as position-independent code? (On my system this also works without the -fPIC for a.o).


Solution

  • Yes, all code that goes into shared library has to be compiled with -fPIC regardless of whether it's in an object file or static library.

    It works without -fPIC in your case because modern distros enable -fPIC by default for security reasons:

    $ echo 'int x; int foo() { return x; }' | gcc -x c - -S -o- | grep rip
            movl    x(%rip), %eax
    

    It's better to keep it explicit in your build scripts for clarity though.