Linux 中使用什么系统调用来加载库?

Linux 中使用什么系统调用来加载库?

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

答案1

dlopen不是系统调用,而是库函数libdl 库。仅系统调用出现在strace.

在 Linux 和许多其他平台上(特别是那些使用 ELF 格式作为可执行文件的平台),dlopen是通过使用 打开目标库open()并将其映射到内存来实现的mmap()mmap()这里确实是关键部分,它将库合并到进程的地址空间中,以便 CPU 可以执行其代码。但您必须open()先获得该文件才能mmap()执行此操作!

答案2

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

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

答案3

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

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

ltrace -S ./dlopen.out

显示:

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

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

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

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

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

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

用这个测试过GitHub 上带有构建样板的最小示例

答案4

strace报告系统调用(即由内核直接实现的函数)。动态库不是内核函数;而是内核函数。dlopen是 C 库的一部分,而不是内核。dlopen将调用(这是一个系统调用)的实现open来打开库文件以便可以读取它。

相关内容