C++ Linux中加载时链接与运行时链接期间的符号地址
我试图了解Linux中动态库的加载时链接(使用C++ Linux中加载时链接与运行时链接期间的符号地址,c++,c,linux,gcc,dynamic-linking,C++,C,Linux,Gcc,Dynamic Linking,我试图了解Linux中动态库的加载时链接(使用gcc-l)与运行时链接(使用dlopen(),dlsym())的底层机制的区别,以及这些机制如何影响库的状态及其符号的地址 实验 我有三个简单的文件: libhello.c: int var; int func() { return 7; } libhello.h: extern int var; int func(); 主要条款c: #include <inttypes.h> #include <stdio.h>
gcc-l
)与运行时链接(使用dlopen(),dlsym()
)的底层机制的区别,以及这些机制如何影响库的状态及其符号的地址
实验
我有三个简单的文件:
libhello.c:
int var;
int func() {
return 7;
}
libhello.h:
extern int var;
int func();
主要条款c:
#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>
#include "libhello.h"
int main() {
void* h = dlopen("libhello.so", RTLD_NOW);
printf("Address Load-time linking Run-time linking\n");
printf("------- ----------------- ----------------\n");
printf("&var 0x%016" PRIxPTR " 0x%016" PRIxPTR "\n", (uintptr_t)&var , (uintptr_t)dlsym(h, "var" ));
printf("&func 0x%016" PRIxPTR " 0x%016" PRIxPTR "\n", (uintptr_t)&func, (uintptr_t)dlsym(h, "func"));
}
加载时链接地址保持不变,但每次运行时运行时链接地址都会更改
问题
dlopen()
。第二个加载不会复制第一个加载的状态。也就是说,如果在dlopen()
之前更改了var
的值,则该值不会反映在通过dlsym()
加载的var
版本中。有没有办法在第二次加载期间保持此状态在我看来,我要说:
- 当您使用可执行文件(静态链接)直接编译库时,您可以认为函数将直接注入源代码。如果检查可执行文件,您将看到每个部分(代码、数据等)都有一个固定的“虚拟内存”地址。如果我记得很清楚的话,每个Linux可执行文件将从默认地址0x100000开始,因此您将看到每个静态链接函数将有一个固定地址(0x100000+固定偏移量),并且这个地址永远不会改变。每次加载可执行文件时,每个特定函数都将加载到“虚拟内存”中的精确地址,这意味着操作系统将决定使用哪个物理地址,但您不会看到。在您的示例中,var变量的虚拟地址始终为0x00000000000601060,但您永远不知道它在物理内存中的位置
- 当您在运行时加载动态库时,操作系统已经将可执行文件加载到内存中,因此您将没有虚拟固定地址。相反,操作系统会在可执行地址空间中保留一系列从0x00007fxxxxxxxxxx开始的虚拟地址,并在其中加载和映射新加载的符号和函数。根据已经加载的内容和内存随机化算法,这些地址在每次运行中可能会有所不同
给出这个简短的解释后,很容易假设在第3点中比较的两个值是完全不同的变量(每个值加载在不同的内存位置),因此它们具有不同的值并且不会相互作用。您看到的取决于许多变量。这是我第一次尝试的Debian 64位
Address Load-time linking Run-time linking
------- ----------------- ----------------
&var 0x0000000000600d58 0x0000000000600d58
&func 0x00000000004006d0 0x00000000004006d0
这意味着,dlopen使用了已经链接的库,而您的系统似乎没有这样做。为了利用ASLR,您需要使用位置无关代码编译main.c
:gcc-fPIC main.c./libhello.so-ldl
Address Load-time linking Run-time linking
------- ----------------- ----------------
&var 0x00007f4e6cec6944 0x00007f4e6cec6944
&func 0x00007f4e6ccc6670 0x00007f4e6ccc6670
我希望这个提示能对你有所帮助
wutiejun@linux-00343520:~/Temp/sotest> LD_LIBRARY_PATH=./ ./test
Address Load-time linking Run-time linking
------- ----------------- ----------------
&var 0x000000000804a028 0x00000000f77a9014
&func 0x0000000008048568 0x00000000f77a744c
wutiejun@linux-00343520:~> cat /proc/7137/maps
08048000-08049000 r-xp 00000000 08:02 46924194 /home/wutiejun/Temp/sotest/test
08049000-0804a000 r--p 00000000 08:02 46924194 /home/wutiejun/Temp/sotest/test
0804a000-0804b000 rw-p 00001000 08:02 46924194 /home/wutiejun/Temp/sotest/test
0804b000-0806c000 rw-p 00000000 00:00 0 [heap]
f75d3000-f7736000 r-xp 00000000 08:02 68395411 /lib/libc-2.11.3.so
f7736000-f7738000 r--p 00162000 08:02 68395411 /lib/libc-2.11.3.so
f7738000-f7739000 rw-p 00164000 08:02 68395411 /lib/libc-2.11.3.so
f7739000-f773c000 rw-p 00000000 00:00 0
f773c000-f7740000 r-xp 00000000 08:02 68395554 /lib/libachk.so
f7740000-f7741000 r--p 00003000 08:02 68395554 /lib/libachk.so
f7741000-f7742000 rw-p 00004000 08:02 68395554 /lib/libachk.so
f777a000-f777c000 rw-p 00000000 00:00 0
f777c000-f7784000 r-xp 00000000 08:02 68395441 /lib/librt-2.11.3.so
f7784000-f7785000 r--p 00007000 08:02 68395441 /lib/librt-2.11.3.so
f7785000-f7786000 rw-p 00008000 08:02 68395441 /lib/librt-2.11.3.so
f7786000-f779d000 r-xp 00000000 08:02 68395437 /lib/libpthread-2.11.3.so
f779d000-f779e000 r--p 00016000 08:02 68395437 /lib/libpthread-2.11.3.so
f779e000-f779f000 rw-p 00017000 08:02 68395437 /lib/libpthread-2.11.3.so
f779f000-f77a2000 rw-p 00000000 00:00 0
f77a2000-f77a5000 r-xp 00000000 08:02 68395417 /lib/libdl-2.11.3.so
f77a5000-f77a6000 r--p 00002000 08:02 68395417 /lib/libdl-2.11.3.so
f77a6000-f77a7000 rw-p 00003000 08:02 68395417 /lib/libdl-2.11.3.so
f77a7000-f77a8000 r-xp 00000000 08:02 46924193 /home/wutiejun/Temp/sotest/libhello.so
f77a8000-f77a9000 r--p 00000000 08:02 46924193 /home/wutiejun/Temp/sotest/libhello.so
f77a9000-f77aa000 rw-p 00001000 08:02 46924193 /home/wutiejun/Temp/sotest/libhello.so
f77aa000-f77ab000 rw-p 00000000 00:00 0
f77ab000-f77ca000 r-xp 00000000 08:02 68395404 /lib/ld-2.11.3.so
f77ca000-f77cb000 r--p 0001e000 08:02 68395404 /lib/ld-2.11.3.so
f77cb000-f77cc000 rw-p 0001f000 08:02 68395404 /lib/ld-2.11.3.so
ffd99000-ffdba000 rw-p 00000000 00:00 0 [stack]
ffffe000-fffff000 r-xp 00000000 00:00 0 [vdso]
wutiejun@linux-00343520:~>
RTLD\u DEFAULT
。像这样加载同一动态库的两个实例通常不是一个好主意。如果某些系统知道已加载同一个库,并且动态链接器认为“同一个库”的内容可能会因库路径的不同而发生变化,则可以跳过在dlopen
中加载库。在这里,您的行为定义非常糟糕/薄弱,多年来,这些行为更多地是为了处理bug和问题,而不是通过精心设计RTLD_DEFAULT
将返回主可执行文件或第一个(加载时间)加载的动态库中符号的地址,而动态加载的库将被忽略
另外,另一件值得记住的事情是,如果在libhello中引用var
,它将始终解析库的加载时版本中的符号,即使在dlopen:ed版本中也是如此。我修改了func
以返回var
,并将此代码添加到示例代码中:
int (*fn)(void) = dlsym(h, "func");
int *vp;
var = 17;
printf("%d %d %d %p\n", var, func(), fn(), vp);
vp = dlsym(h, "var");
*vp = 4711;
printf("%d %d %d %p\n", var, func(), fn(), vp);
vp = dlsym(RTLD_DEFAULT, "var");
*vp = 42;
printf("%d %d %d %p\n", var, func(), fn(), vp);
并获得以下输出:
$ gcc main.c -L. -lhello -ldl && LD_LIBRARY_PATH=. ./a.out
17 17 17 0x7f2e11bec02c
17 17 17 0x7f2e11bec02c
42 42 42 0x601054
Address Load-time linking Run-time linking
------- ----------------- ----------------
&var 0x0000000000601054 0x0000000000601054
&func 0x0000000000400700 0x0000000000400700
请注意,OP在这里不执行任何静态链接,他以两种不同的方式链接共享库。是的,你当然是对的。将我的答案留在这里,因为它可能对其他人有帮助。相关,请参阅和。如果你想随机化数据段,那么我相信你需要设置
随机化空间=2
;注意:我使用此命令链接可执行文件:gcc-o main.c-L-lhello-ldl-Wl,-rpath$(pwd-L)
在AIX/PowerPC中也尝试过,这对地址('func'和'var')是相等的。链接命令是:gcc-omain.c-L-lhello-ldl-Wl,-brtl,-blibpath:$(pwd-L):/usr/lib
这比我预想的要微妙得多。
$ gcc main.c -L. -lhello -ldl && LD_LIBRARY_PATH=. ./a.out
17 17 17 0x7f2e11bec02c
17 17 17 0x7f2e11bec02c
42 42 42 0x601054
Address Load-time linking Run-time linking
------- ----------------- ----------------
&var 0x0000000000601054 0x0000000000601054
&func 0x0000000000400700 0x0000000000400700