Search code examples
cgccshared-librariesstatic-librariesdead-code

Remove dead code when linking static library into dynamic library


Suppose I have the following files:

libmy_static_lib.c:

#include <stdio.h>
void func1(void){
    printf("func1() called from a static library\n");
}
void unused_func1(void){
    printf("printing from the unused function1\n");
}
void unused_func2(void){
    printf("printing from unused function2\n");
}

libmy_static_lib.h:

void func(void);
void unused_func1(void);
void unused_func2(void);

my_prog.c:

#include "libmy_static_lib.h"
#include <stdio.h>
void func_in_my_prog()
{
    printf("in my prog\n");
    func1();

}

And here is how I link the library:

# build the static library libmy_static_lib.a
gcc -fPIC -c -fdata-sections --function-sections -c libmy_static_lib.c -o libmy_static_lib.o
ar rcs libmy_static_lib.a libmy_static_lib.o

# build libmy_static_lib.a into a new shared library
gcc -fPIC -c ./my_prog.c -o ./my_prog.o
gcc -Wl,--gc-sections -shared -m64 -o libmy_shared_lib.so ./my_prog.o -L. -l:libmy_static_lib.a

There are 2 functions in libmy_static_lib.c that are not used, and from this post, I think

gcc fdata-sections --function-sections

should create a symbol for each function, and

gcc -Wl,--gc-sections

should remove the unused symbols when linking

however when I run

nm libmy_shared_lib.so

It is showing that these 2 unused functions are also being linked into the shared library.

Any suggestions on how to have gcc remove the unused functions automatically?

Edit: I am able to use the above options for gcc to remove the unused functions if I am linking a static library directly to executable. But it doesn't remove the unused functions if I link the static library to a shared library.


Solution

  • You can use a version script to mark the entry points in combination with -ffunction-sections and --gc-sections.

    For example, consider this C file (example.c):

    int
    foo (void)
    {
      return 17;
    }
    
    int
    bar (void)
    {
      return 251;
    }
    

    And this version script, called version.script:

    {
      global: foo;
      local: *;
    };
    

    Compile and link the sources like this:

    gcc -Wl,--gc-sections -shared -ffunction-sections -Wl,--version-script=version.script example.c
    

    If you look at the output of objdump -d --reloc a.out, you will notice that only foo is included in the shared object, but not bar.

    When removing functions in this way, the linker will take indirect dependencies into account. For example, if you turn foo into this:

    void *
    foo (void)
    {
      extern int bar (void);
      return bar;
    }
    

    the linker will put both foo and bar into the shared object because both are needed, even though only bar is exported.

    (Obviously, this will not work on all platforms, but ELF supports this.)