Answers:
dlopen
不是系统调用,而是libdl库中的库函数。中仅显示系统调用strace
。
在Linux和许多其他平台(尤其是使用ELF格式的可执行文件的平台)上,dlopen
可以通过打开目标库open()
并将其映射到内存中来实现mmap()
。mmap()
这实际上是关键的部分,它是将库合并到进程的地址空间中的原因,因此CPU可以执行其代码。但是您必须先open()
归档文件mmap()
!
ld-linux
并由内核映射为execve
系统调用的一部分是很重要的。
当您想到共享库时,dlopen与它们无关。有两种加载共享库的方法:
main
函数之前立即调用动态的运行时链接程序(ld.so),并设置应用程序的进程空间,以便应用程序可以找到库的功能。这涉及open()
先润滑润滑脂,然后润滑mmap()
它,然后设置一些查找表。libdl
,然后您(使用第一种方法)可以从中调用dlopen()
和dlsym()
功能。使用dlopen可以获得库的句柄,然后可以将其与dlsym一起使用以接收指向特定函数的函数指针。对于程序员而言,此方法比第一种方法复杂得多(因为您必须手动进行设置,而不是让链接程序为您自动进行设置),并且它也更加脆弱(因为您没有获得编译的权限) -time检查您在第一种方法中获得的参数类型正确的函数),但优点是您可以决定在运行时加载哪个共享库(甚至根本不加载),从而这是用于插件类型功能的接口。最后,dlopen接口的可移植性也比其他方法差,因为它的机制取决于动态链接程序的确切实现(因此,libtool的接口libltdl
,它试图消除这些差异)。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代码,但是mmap
经过一番快速grep之后,我找不到了,我很懒。
通过在GitHub上构建样板的最小示例进行了测试。
strace
报告系统调用(即由内核直接实现的功能)。动态库不是内核功能。dlopen
是C库的一部分,而不是内核。dlopen
will调用的实现open
(这是系统调用),以打开库文件以便可以读取它。
ltrace
。
ltrace -S
非常适合分析此问题,因为它也显示系统调用:unix.stackexchange.com/a/462710/32558