Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I have a question about C library compilation

Tags:

c

linux

I have some C source file func1.c、func2.c、main.c and a C header file func1.h、func2.h as follows:

// func1.h
#ifndef _FUNC1_H
#define _FUNC1_H
void show(int* nums, int n);


#endif
// func1.c
#include "func1.h"
#include <stdio.h>

void show(int* nums, int n)
{
    int i;
    for(i = 0; i < n; i++)
    {
        printf("%d ", nums[i]);
    }
    printf("\n");
}
// func2.h
#ifndef _FUNC2_H
#define _FUNC2_H
void show2(void* nums, int len);


#endif
// func2.c
#include "func1.h"

void show2(void* nums, int len)
{
    int* array = (int*)nums;
    show(array, len);
}
//main.c
#include <stdio.h>
#include <dlfcn.h>
typedef void (*SHOW2_FUNC)(void* nums, int len);
static void * handler = NULL;

void* get_func(const char* libname)
{
    handler = dlopen(libname, RTLD_LAZY);
    if (handler == NULL)
    {
        printf("open lib: %s error! \n", libname);
        return NULL;
    }

    SHOW2_FUNC show_func = dlsym(handler, "show2");
    if (show_func == NULL)
    {
        printf("can't find function: %s ! \n", "show2");
        return NULL;        
    }

    printf("find funcion successful, address is %p \n", show_func);
    return show_func;
}

int main()
{
    int nums[5] = {1, 2, 3, 4, 5};
    
    SHOW2_FUNC pf_show = get_func("./libfunc2.so");
    pf_show(nums, 5);
    dlclose(handler);
    return 0;
}

I use the following gcc command to compile func1.c,and I got static link library libfunc1.a

gcc -c func1.c -o func1.o
ar crv libfunc1.a func1.o

And then, I use the following gcc command to compile func2.c, and I got dynamic link library libfunc2.so

gcc -c func2.c -o func2.o
gcc func2.o -o libfunc2.so -shared -fPIC libfunc1.a

Finally, I use the following gcc command to compile main.c, and I got executable file main

gcc -o main main.c -ldl

Execute main, the results are as follows:

find funcion successful, address is 0x7fdadcde069a 
1 2 3 4 5 

The result of running the ldd libfunc2.so command is as follows:

linux-vdso.so.1 (0x00007fffd8e91000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd687be0000)
/lib64/ld-linux-x86-64.so.2 (0x00007fd688200000)

But, if I use the following gcc command to compile func2.c. No errors were reported during compilation.

gcc -o libfunc2.so -shared -fPIC libfunc1.a func2.o  #  Place the file func2.o at the end

Execute main again, the results are as follows, and I got a run error.

find funcion successful, address is 0x7f6b577e05ba 
./main: symbol lookup error: ./libfunc2.so: undefined symbol: show

The result of running the ldd libfunc2.so command is as follows:

statically linked

Bisides, I compile func1.c into a dynamic link library libfunc1.so, and use it to compile libfunc2.so. Compile commands are as follows:

gcc func1.c -o libfunc1.so -shared -fPIC
gcc -o libfunc2.so -shared -fPIC -L. -lfunc1 -Wl,-rpath=. func2.o

Execute main again, the results are as follows, and I got a run error.

find funcion successful, address is 0x7f6b577e05ba 
./main: symbol lookup error: ./libfunc2.so: undefined symbol: show

Why would a different order lead to the above result? Why is there no error at compile time? Thank you for reading.

like image 709
Da Wang Avatar asked Dec 05 '25 13:12

Da Wang


1 Answers

I created the files and used the following makefile with commands from your post:

all:
    gcc -c func1.c -o func1.o
    ar crv libfunc1.a func1.o
    gcc -c func2.c -o func2.o
    gcc func2.o -o libfunc2.so -shared -fPIC libfunc1.a
    gcc -o main main.c -ldl
    ./main || :
    ldd libfunc2.so

    gcc -o libfunc2.so -shared -fPIC libfunc1.a func2.o
    ./main || :

    ldd libfunc2.so
    gcc func1.c -o libfunc1.so -shared -fPIC
    gcc -o libfunc2.so -shared -fPIC -L. -lfunc1 -Wl,-rpath=. func2.o
    ./main || :

it results in the following output:

gcc -c func1.c -o func1.o
ar crv libfunc1.a func1.o
r - func1.o
gcc -c func2.c -o func2.o
gcc func2.o -o libfunc2.so -shared -fPIC libfunc1.a
gcc -o main main.c -ldl
./main || :
find funcion successful, address is 0x7fb8d951c129 
1 2 3 4 5 
ldd libfunc2.so
        linux-vdso.so.1 (0x00007ffe1a7e7000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f24dab42000)
        /usr/lib64/ld-linux-x86-64.so.2 (0x00007f24dad72000)
gcc -o libfunc2.so -shared -fPIC libfunc1.a func2.o
./main || :
find funcion successful, address is 0x7f9a49030109 
./main: symbol lookup error: ./libfunc2.so: undefined symbol: show
ldd libfunc2.so
        linux-vdso.so.1 (0x00007ffc92996000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f245acd8000)
        /usr/lib64/ld-linux-x86-64.so.2 (0x00007f245af08000)
gcc func1.c -o libfunc1.so -shared -fPIC
gcc -o libfunc2.so -shared -fPIC -L. -lfunc1 -Wl,-rpath=. func2.o
./main || :
find funcion successful, address is 0x7fab12301109 
1 2 3 4 5 

I am running on ArchLinux gcc13.2.1 ld.so2.38.


gcc -o libfunc2.so -shared -fPIC libfunc1.a func2.o  #  Place the file func2.o at the end

Execute main again, the results are as follows, and I got a run error.

find funcion successful, address is 0x7f6b577e05ba 
./main: symbol lookup error: ./libfunc2.so: undefined symbol: show

Yes. If you take a look at libfunc2.so, you will see that:

$ nm ./libfunc2.so 
.....
                 U show
0000000000001109 T show2
0000000000004010 d __TMC_END__

Symbol show is undefined. Why is that? Because from https://man7.org/linux/man-pages/man1/ld.1.html :

       The linker will search an archive only once, at the location
       where it is specified on the command line.  If the archive
       defines a symbol which was undefined in some object which
       appeared before the archive on the command line, the linker
       will include the appropriate file(s) from the archive.
       However, an undefined symbol in an object appearing later on
       the command line will not cause the linker to search the
       archive again.

In short, only used symbols from archive are used at the time they are encountered. So func2.o has to be before libfunc1.a on the command line. Or, use --whole-archive option. See also https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html .


The result of running the ldd libfunc2.so command is as follows:

statically linked

I believe you made an error here, I am not able to reproduce. For me, the output at this point is:

ldd libfunc2.so
        linux-vdso.so.1 (0x00007ffdc39f3000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007f6613201000)
        /usr/lib64/ld-linux-x86-64.so.2 (0x00007f6613431000)

gcc func1.c -o libfunc1.so -shared -fPIC
gcc -o libfunc2.so -shared -fPIC -L. -lfunc1 -Wl,-rpath=. func2.o

Execute main again, the results are as follows, and I got a run error.

find funcion successful, address is 0x7f6b577e05ba 
./main: symbol lookup error: ./libfunc2.so: undefined symbol: show

Once again, I am not able to reproduce. For me, at this stage, the executable links using RPATH=. with libfunc1.so, like the following:

$ LD_DEBUG=all ./main 2>&1 | grep show
     63682:     symbol=show2;  lookup in file=./libfunc2.so [0]
     63682:     binding file ./libfunc2.so [0] to ./libfunc2.so [0]: normal symbol `show2'
     63682:     symbol=show;  lookup in file=./main [0]
     63682:     symbol=show;  lookup in file=/usr/lib/libc.so.6 [0]
     63682:     symbol=show;  lookup in file=/lib64/ld-linux-x86-64.so.2 [0]
     63682:     symbol=show;  lookup in file=./libfunc2.so [0]
     63682:     symbol=show;  lookup in file=./libfunc1.so [0]
     63682:     binding file ./libfunc2.so [0] to ./libfunc1.so [0]: normal symbol `show'

Why would a different order lead to the above result?

Because linker searcher for symbols in linked archives in order of the command line parameters.

Why is there no error at compile time?

The magic of shared libraries is that you they are resolved at runtime, not compile time. So no compile time errors. You can ignore unresolved symbols - it's just a sanity check.

like image 121
KamilCuk Avatar answered Dec 08 '25 05:12

KamilCuk



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!