Linker 共享库的静态加载是否像动态加载或静态链接一样链接?
根据, 动态加载是指在is启动后将可执行文件或库映射(或较少复制)到进程内存中动态链接是指在编译后解析符号-将其名称与地址或偏移量关联 因此,相应地:静态加载指的是在可执行文件或库启动之前将其映射到内存中,静态链接指的是在编译时解析符号 现在,当您对库执行静态加载和静态链接时,库的二进制代码将附加到二进制代码中,并且二进制代码对库的(函数和变量)引用将被修补(不确定这是否是正确的术语),以便它们指向正确的位置 这意味着在静态链接调用到函数之前Linker 共享库的静态加载是否像动态加载或静态链接一样链接?,linker,static-linking,dynamic-linking,Linker,Static Linking,Dynamic Linking,根据, 动态加载是指在is启动后将可执行文件或库映射(或较少复制)到进程内存中动态链接是指在编译后解析符号-将其名称与地址或偏移量关联 因此,相应地:静态加载指的是在可执行文件或库启动之前将其映射到内存中,静态链接指的是在编译时解析符号 现在,当您对库执行静态加载和静态链接时,库的二进制代码将附加到二进制代码中,并且二进制代码对库的(函数和变量)引用将被修补(不确定这是否是正确的术语),以便它们指向正确的位置 这意味着在静态链接调用到函数之前 foo() 将为您(在x86 ASM中)提供如下指
foo()
将为您(在x86 ASM中)提供如下指令:
call 0x00000000
在静态链接之后,您会得到如下结果:
call 0x00001043
其中0x00001043是链接器输出的二进制代码中函数foo的入口点
现在,当您执行动态加载和动态链接时,您将通过函数指针调用库函数:
typedef int(*fun_ptr)(无效)
<>这个机制也是C++虚拟方法如何工作的。要调用的方法的地址在运行时通过使函数指针指向实例的方法部分(存储在所谓的vtable中)来解析
我的问题是:
当您对共享库进行静态加载和动态链接时(对于上下文,假设在linux中是a.so),这种链接是否会像静态加载和链接场景中那样修补我的二进制文件的引用,或者它是通过函数指针的方式工作的,比如在<强>动态加载和链接<强>和C++虚拟方法>
在对共享库执行静态加载和动态链接时
您不会对共享库进行“静态加载”
即使在最终用户看来,例如libc.so.6
在进程启动时是“静态加载”的,但事实并非如此。相反,内核“静态加载”主二进制文件和ld linux.so
,然后ld linux
动态加载所有其他共享库
这种链接是像静态加载和链接场景中那样修补二进制文件的引用,还是像动态加载那样通过函数指针工作
视情况而定
通常,共享库是从位置无关的代码(PIC
)链接而来,并以函数指针的方式工作(指针存储在GOT
--全局偏移表中)
但有时共享库是从非
PIC
代码链接的,需要“文本重定位”,这与“静态链接”的工作原理类似。我已经能够使用libbfd(位于GNU binutils的ld链接器下面的库)生成一个运行时静态链接的示例:谢谢,知道这个非常有用!现在我终于明白了为什么在我的linux机器中编译共享库时必须添加-fPIC标志:)一些后续问题:您知道哪些系统使用文本重定位,哪些使用全局偏移表吗?是否可以编译一个linux内核来使用文本重定位而不是GOTs?还有,为什么喜欢哥特?更快的加载比更快的运行时性能更重要?我发现这很有用。这似乎意味着文本重定位只能由SELinux框中的内核执行(因为内存页在SELinux中既不能写也不能执行)。
library = dlopen("mylib.so");
fun_ptr foo = dlsym(library, "foo");
foo();