首页 > 代码库 > linux主文件和动态库之间变量和函数访问

linux主文件和动态库之间变量和函数访问

通常我们需要从动态库里面直接调用可执行程序中的函数和变量,如果调用了-l选项,linux进程会自动把动态库的函数和变量加入到动态段中,所以直接访问是没有问题的。

我们这里要说的是非显示连接动态库,而是直接从c文件中通过dlopen函数打开动态库访问的方式,此时,gcc编译器不知道SO需要调用哪一个函数,所以不会讲函数放到动态段。故查找函数或者变量的时候,会出现找不到可执行程序中的符号的情况。

为什么会出现需要在c文件中直接dlopen动态库的情况呢? 这种情况一般出现在又不想重启进行,又需要有功能更新的情况。比如nginx服务器,在不重启web服务器的,需要增加一个filter模块,filter模块肯定是需要访问nginx内部函数和变量的,这种情况下,问题就引出来了。

如何共享变量呢?linux是通过什么机制来共享呢?我们通过一个例子来说明看看。


hello.c 用来编译成libhello.so

void hello()
{
        share_fun();
}

main.c主函数

#include <stdio.h>
#include <dlfcn.h>

void share_fun()
{
        printf("aa\n");
}

main()
{
        void *ptr = NULL;
        void (*hello)(void);

        ptr = dlopen("/home/mywork/libhello.so", RTLD_LAZY);
        if (ptr == NULL) {
                printf("error dlopen\n");
                return -1;
        }

        hello = dlsym(ptr, "hello");
        if (hello == NULL) {
                printf("error get fun\n");
                return -1;
        }

        hello();
        return 0;
}

两个文件中我们可以看到,共享库libhello.so中访问了share_fun函数,而这个函数是定义在主文件中的。


我们通过以下命令讲hello.c编译成libhello.so

gcc -fPIC -shared libhello.so hello.c


通过以下命令生成main可执行文件。

gcc main.c -o main -ldl

执行结果

./main: symbol lookup error: /home/mywork/libhello.so: undefined symbol: share_fun


通过以下命令生成main可执行文件。

gcc main.c -o main -ldl -L./ -lhello

执行结果

aa


为什么会出现这种情况呢?先看看linux中动态库是如何访问未定义符号的,以下是libhello.so中share_fun在elf文件中的位置:

Relocation section ‘.rel.plt‘ at offset 0x374 contains 3 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0000200c  00000207 R_386_JUMP_SLOT   00000000   share_fun
00002010  00000307 R_386_JUMP_SLOT   00000000   __cxa_finalize
00002014  00000407 R_386_JUMP_SLOT   00000000   __gmon_start__

The decoding of unwind sections for machine type Intel 80386 is not currently supported.

Symbol table ‘.dynsym‘ contains 13 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     2: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND share_fun
     3: 00000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.1.3 (2)
     4: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     5: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses

从上面可以看出share_fun因为在动态库中没有定义,从而被安置在了重定向段中,也就是说在编译时无法确定函数的准确地址,需要在运行加载时才能确定。

并且我们还可以看到share_fun被定为动态符号。


第一种情况下,main中动态符号并没有share_fun

[root@localhost mywork]# gcc main.c -o main -ldl
[root@localhost mywork]# ./main
./main: symbol lookup error: /home/mywork/libhello.so: undefined symbol: share_fun
[root@localhost mywork]# nm -D main
0804867c R _IO_stdin_used
         w _ITM_deregisterTMCloneTable
         w _ITM_registerTMCloneTable
         w _Jv_RegisterClasses
         w __gmon_start__
         U __libc_start_main
         U dlopen
         U dlsym
         U puts


第二种情况下,main中动态符号表里面含有share_fun

[root@localhost mywork]# gcc main.c -o main -ldl -L./ -lhello
[root@localhost mywork]# ./main
aa
[root@localhost mywork]# nm -D main
0804873c R _IO_stdin_used
         w _ITM_deregisterTMCloneTable
         w _ITM_registerTMCloneTable
         w _Jv_RegisterClasses
0804a024 B __bss_start
         w __gmon_start__
         U __libc_start_main
0804a024 D _edata
0804a028 B _end
08048724 T _fini
0804847c T _init
         U dlopen
         U dlsym
         U puts
08048610 T share_fun

从上可以看到动态库中访问主程序的函数时,符号的共享是通过动态段来实现的,当没有动态段时,是无法实现共享的,函数和变量都是一样。

从原理上讲,动态库或者主程序中没有定义的函数,都会在重定向表中,需要在程序中加载阶段进行动态映射,所以主程序和动态库共享和静态编译是相冲突的。


知道了原理,要解决办法就容易了,以下编译选项都能将符号导出到动态段,从而实现主程序和动态库符号访问。

-Bsymbolic
           When creating a shared library, bind references to global symbols to the definition within the shared library, if any.  Normally, it is possible for a
           program linked against a shared library to override the definition within the shared library.  This option is only meaningful on ELF platforms which
           support shared libraries.


       -Bsymbolic-functions
           When creating a shared library, bind references to global function symbols to the definition within the shared library, if any.  This option is only
           meaningful on ELF platforms which support shared libraries.


       --dynamic-list=dynamic-list-file
           Specify the name of a dynamic list file to the linker.  This is typically used when creating shared libraries to specify a list of global symbols whose
           references shouldn‘t be bound to the definition within the shared library, or creating dynamically linked executables to specify a list of symbols which
           should be added to the symbol table in the executable.  This option is only meaningful on ELF platforms which support shared libraries.


           The format of the dynamic list is the same as the version node without scope and node name.  See VERSION for more information.


       --dynamic-list-data
           Include all global data symbols to the dynamic list.


linux主文件和动态库之间变量和函数访问