什么系统调用用于在 Linux 中加载库?

Mel*_*lab 29 dynamic-linking strace dynamic-loading shared-library

strace输出中,可执行文件调用的库的路径在调用中open()。这是动态链接的可执行文件使用的系统调用吗?怎么样dlopen()open()不是我猜想的调用会在程序执行中发挥作用。

Cel*_*ada 38

dlopen不是系统调用,而是libdl 库中的库函数。中只显示系统调用strace

在 Linux 和许多其他平台上(尤其是那些使用 ELF 格式的可执行文件),dlopen是通过打开目标库open()并使用mmap(). mmap()在这里确实是关键部分,它将库合并到进程的地址空间中,因此 CPU 可以执行其代码。但是,您必须先访问open()该文件,然后才能使用mmap()它!

  • “mmap() 确实是关键部分”:然后动态链接器必须进行重定位、初始化等(但这在系统调用级别看不到)。 (2认同)

Wou*_*lst 6

dlopen 与您认为的共享库无关。加载共享对象有两种方法:

  1. 您告诉编译时链接器(ld,但通常通过编译器调用它)您想要使用来自特定共享库的函数。使用这种方法,您必须知道在编译时链接器运行时库的名称是什么,但是您可以调用库的函数,就好像它们被静态链接到您的程序中一样。当应用程序运行时,动态的运行时链接器(ld.so)将在调用main函数之前被调用,并设置应用程序的进程空间,以便应用程序找到库的函数。这涉及到open()ing lubrary,然后mmap()ing 它,然后设置一些查找表。
  2. 你告诉编译时链接器你想链接libdl,然后你(使用第一种方法)可以调用dlopen()dlsym()职能。使用 dlopen,您可以获得库的句柄,然后您可以将其与 dlsym 一起使用以接收指向特定函数的函数指针。对于程序员来说,这种方法比第一种方法复杂得多(因为您必须手动进行设置,而不是让链接器自动为您完成),而且它也更脆弱(因为您没有得到编译-time 检查您在第一个方法中获得的参数类型是否正确调用函数),但优点是您可以决定在运行时加载哪个共享对象(甚至是否加载它),使得这是一个用于插件类型功能的接口。最后,dlopen 接口的可移植性也低于其他方式,因为它的机制取决于动态链接器的确切实现(因此 libtool 的libltdl,它试图抽象出这些差异)。


Cir*_*郝海东 5

ltrace -S一个最小示例的分析表明它mmap在 glibc 2.23 中使用

在 glibc 2.23、Ubuntu 16.04 中,运行latrace -S在使用以下命令的最小程序上dlopen

ltrace -S ./dlopen.out
Run Code Online (Sandbox Code Playgroud)

显示:

dlopen("libcirosantilli_ab.so", 1 <unfinished ...>
SYS_open("./x86_64/libcirosantilli_ab.so", 524288, 06267650550)      = -2
SYS_open("./libcirosantilli_ab.so", 524288, 06267650550)             = 3
SYS_read(3, "\177ELF\002\001\001", 832)                              = 832
SYS_brk(0)                                                           = 0x244c000
SYS_brk(0x246d000)                                                   = 0x246d000
SYS_fstat(3, 0x7fff42f9ce30)                                         = 0
SYS_getcwd("/home/ciro/bak/git/cpp-cheat"..., 128)                   = 54
SYS_mmap(0, 0x201028, 5, 2050)                                       = 0x7f1c323fe000
SYS_mprotect(0x7f1c323ff000, 2093056, 0)                             = 0
SYS_mmap(0x7f1c325fe000, 8192, 3, 2066)                              = 0x7f1c325fe000
SYS_close(3)                                                         = 0
SYS_mprotect(0x7f1c325fe000, 4096, 1)                                = 0
Run Code Online (Sandbox Code Playgroud)

所以我们立即看到dlopen调用open+ mmap

这个很棒的ltrace工具可以跟踪库调用和系统调用,因此非常适合检查这种情况下发生的情况。

仔细分析表明,它open返回文件描述符3(stdin、out 和 err 之后的下一个空闲描述符)。

read然后使用该文件描述符,但是TODO 为什么mmap参数被限制为四个,并且我们看不到那里使用了哪个 fd,因为这是第五个参数strace正如所料,证实了这3一点,宇宙的秩序得以恢复。

勇敢的灵魂也可以冒险进入 glibc 代码,但我在快速 grep 后找不到,mmap而且我很懒。

使用 GitHub 上的构建样板对这个最小示例进行了测试。