vimer linux kernel 爱好者

动态加载和link再次回顾

2021-01-01

原来,自己在很多年前就总结了这块知识

今日,再一次进行更松散的总结。

linker在文件编译时,解决所有的外部符号;当执行时,loader(属于OS)将会重置所有的符号,并且放入合适的内存中。

linux 支持两类库: 静态库和动态库。根据程序编译阶段的不同,又可以分为动态链接库和动态加载库。

Shared library

共享库没啥特别的地方,也是一种ELF格式文件,和普通的C语言文件没有区别(编译之前),除了没有main()函数入口。当然,要告诉gcc-fPIC选项。

vimer@host:~/test/lib$ cat libfunction.c 
int double_me(int  a){
        return a + a;
}
// gcc -c -fPIC libfunction.c 
// gcc -shared -o libmylib.so libfunction.o  
vimer@host:~/test/lib$ ls
libfunction.c  libfunction.o  libmylib.so

当然,也可以综合起来:

gcc -shared -fPIC -o libmylib.so libfunction.c

那么, 在其他文件中,调用这个函数的方法是:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int double_me(int); // 最好放在 .h 文件内

int main(){
        int i;
        for(i = 0; i < 10; i++)
                printf("%d'' s duoble is %d\t", i, double_me(i));
}

我们的main函数在编译阶段会link这个lib。

gcc -o main main.c -L. -lmylib 
# -L: path
# -l : 自动加上lib及后缀so

但是,如果你执行试试:

vimer@host:~/test/lib$ ./main 
./main: error while loading shared libraries: libmylib.so: cannot open shared object file: No such file or directory

link是没问题的,但是load是无法的。也就是说,尽管我们我们在link阶段指明了so的位置,但是load并不会得知。以上,基本上就是前面文件的内容。

LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./main
# 必须在一条命令下才可以

So in order to make the loader find our library, we have to tell it where to look, either by adding the correct directory to the LD_LIBRARY_PATH environment variable, or by adding it to the ldconfig paths in either /etc/ld.so.conf inside the /etc/ld.so.conf.d/ directory. Let’s try it with the environment variable for now

动态加载共享库

如果只想在加载 load 时使用共享库的函数,还是有办法的。

// dynload.c
#include <stdio.h>
#include <dlfcn.h>
 
int main(void) {
    // handle for dynamic loading functions
    void *handle;
 
    // function pointer for the library's double_me() function
    int (*double_me)(int);
 
    // just a counter
    int i;
 
    // open our library ..hopefully
    if ( (handle = dlopen("./libmylib.so", RTLD_LAZY)) == NULL) {
        return 1;
    }
 
    // try to extract "double_me" symbol from the library
    double_me = dlsym(handle, "double_me");
    if (dlerror() != NULL) {
        dlclose(handle);
        return 2;
    }
 
    // use double_me() just like with a regularly linked library
    for (i = 1; i <= 10; i++) {
        printf("%d doubled is %d\n", i, double_me(i));
    }
 
    dlclose(handle);
    return 0;
}

这段代码需要借助libdl动态库, 其实不好的一点就是得猜。

gcc -o dynload dynload.c -ldl

linux lib 管理

linux中有两种lib,一种是静态的,一种是动态的(Dynamic Shared Object–DSO),由操作系统借助 ld.so或者ld-linux.so在运行时动态load.

  • ldconfig : Updates the necessary links for the run time link bindings.
  • ldd : Tells what libraries a given program needs to run.
  • ltrace : A library call tracer.
  • ld.so/ld-linux.so: Dynamic linker/loader.

重要的文件

  • /lib/ld-linux.so.* : Execution time linker/loader.
  • /etc/ld.so.conf : File containing a list of colon, space, tab, newline, or comma separated directories in which to search for libraries.
  • /etc/ld.so.cache : File containing an ordered list of libraries found in the directories specified in /*etc/ld.so.conf. This file is not in human readable format, and is not intended to be edited. This file is created by ldconfig command.

  • lib*.so.version : Shared libraries stores in /lib, /usr/lib, /usr/lib64, /lib64, /usr/local/lib directories.

ldconfig

在上面的 main 例子中, ldconfig作用在libmylib.so 的目录中并不起作用,不知道为什么。 但是,可以使用下面的命令让共享库生效:

sudo vim /etc/ld.so.conf.d/mylib.conf
vimer@host:~/test/lib$ cat /etc/ld.so.conf.d/mylib.conf 
/home/vimer/test/lib  # 把libmylib.so所在目录放入这个自己创建的文件,ldconfig一下就可以
vimer@host:~/test/lib$ sudo ldconfig 
vimer@host:~/test/lib$ ./main 
0'' s duoble is 0       1'' s duoble is 2       2'' s duoble is 4       3'' s duoble is 6       4'' s duoble is 8       5'' s duoble is 10   6'' s duoble is 12       7'' s duoble is 14      8'' s duoble is 16      9'' s duoble is 18 

查看ldconfig的内容使用-v选项。

 sudo ldconfig -v | grep -i mylib 
        libmylib.so -> libmylib.so

ldd

ldd可以查看一个可执行文件所依赖的动态库

ld.so or ld-linux.so

The ld.so or / ld-linux.so used as follows by Linux:

  • To load the shared libraries needed by a program.
  • To prepare the program to run, and then runs it.
# cd /lib64
vimer@host:/lib64$ ./ld-linux-x86-64.so.2 --list /home/vimer/test/lib/main
        linux-vdso.so.1 (0x00007fff48dfa000)
        libmylib.so => /home/vimer/test/lib/libmylib.so (0x00007f9654e92000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9654aa1000)

与ldd的作用一样

环境变量

The LD_LIBRARY_PATH can be used to set a library path for finding dynamic libraries using LD_LIBRARY_PATH, in the standard colon seperated format:

$ export LD_LIBRARY_PATH=/opt/simulator/lib:/usr/local/lib

The LD_PRELOAD allow an extra library not specified in the executable to be loaded:

$ export LD_PRELOAD=/home/vivek/dirhard/libdiehard.so

Please note that these variables are ignored when executing setuid/setgid programs.

补充

mit os

pkg-config

pkg-config的作用与ldconfig有些类似,但是pkg-config更侧重在编译、链接阶段;而ldconfig侧重链接、运行时阶段。


上一篇 shell根据ps kill

Comments

Content