假设我有一个源文件dll.c
,该文件使用dlopen
和dlsym
函数在运行时加载名为F.so
的共享库。
dll.c
引用了some_function()
,而F.so
的定义是some_function()
。
假设下面的图片是可执行对象prog
,它是通过
linux> gcc -rdynamic -o prog dll.c -ldl
因此.text
部分包含some_function()
的引用,在执行程序加载F.so
并开始调用some_function()
我的问题是:
第1季度对我来说,动态链接程序需要修改RAM(可执行文件复制到内存)中的.text
部分(some_function()
所属的部分),因此{ {1}}可以解决,我的理解正确吗?
Q2-如果动态链接程序需要修改RAM中的some_function()
部分,该如何执行?据我了解,.text
部分是RAM中的只读段,如果将其称为只读段,该如何修改只读段?
答案 0 :(得分:1)
Q1-在我看来,动态链接程序需要修改RAM(可执行文件被复制到内存中)中的.text部分(some_function()所属的部分),以便可以解析some_function()的引用。 ,我的理解正确吗?
不必那样。有PLT(过程链接表)。基本上是这样的:
foo@PLT:
jmp <someTemporaryAddress>
main:
call foo@PLT
然后,在运行时,动态链接器仅修补此部分,因为它比在许多机器代码中查找调用更简单。
Q2-如果动态链接程序需要修改RAM中的.text部分,它如何执行?据我了解,.text段是RAM中的只读段,如果将其称为只读段,该如何修改只读段?
已将其设置为可执行/只读。
答案 1 :(得分:1)
Q1-在我看来,RAM(可执行文件被复制到内存中)中的.text部分(some_function()所属的部分)需要通过动态链接程序进行修改...
不。实际代码使用间接跳转:
.text
...
call *xsome_function
...
.data
xsome_function:
.long some_function
...因此,call
指令不会跳转到某个地址,而是会跳转到存储在.data
节中的某个地址。动态链接器必须替换行.long some_function
中的地址,而不是call
指令中的地址。
如果链接包含“直接” call
指令的代码,则链接器将执行JCWasmx86在其答案中写的内容:
.text
...
call some_function@PLT
...
some_function@PLT:
jmp *xsome_function
.data
xsome_function:
.long some_function
再一次,动态链接程序只需替换.data
部分中的地址。
dlopen
如果您使用dlopen
和dlsym
访问您的库,则动态链接程序甚至不会替换任何内容:
dlsym()
返回某个函数指针,该函数指针的处理方式与其他函数返回的任何其他指针一样(例如malloc()
返回的指针)。调用dlsym()
的程序负责以某种方式存储指针。
使用函数指针调用函数时,编译器将始终创建间接跳转指令(例如call %eax
)。
第二季度-如果动态链接程序需要修改RAM中的.text部分,则...
几年前,我为Linux编写了一些小型编译器/链接器。
由于我来自Windows开发,而Window的动态链接器实际上替换了.text
部分中的地址,因此我以与Windows下相同的方式编写了编译器/链接器:
结果:在运行使用我的编译器编译的程序时,动态链接器(ld-linux.so
)只是崩溃,导致“分段错误”,因为动态链接器无法在Linux下写入.text
段。 / p>
-编辑-
一个例子:
如果使用dlopen()
和dlsym()
,则C代码看起来不是这样:
extern double sin(double x);
...
sine = sin(angle);
相反,C代码如下所示:
static double (*psin)(double x);
...
sofile = dlopen("libm.so",FLAGS);
psin = dlsym(sofile,"sin");
...
sine = psin(angle);