何时/如何将Linux加载到地址空间?

时间:2011-02-27 00:47:12

标签: c linux compiler-construction linker shared-libraries

我的问题如下:

程序中指定的共享对象的地址是什么时候?链接期间?数据加载中?如果我想在我的程序中找到systemlibc命令的内存地址,我可以在gdb中轻松找到它,但是如果我不想带来编程到调试器?

此地址是否可以从运行更改为运行?是否还有其他静态分析工具可以查看运行时将库或函数加载到该程序的内存空间的位置?

编辑:我想在程序之外使用此信息(即使用objdump之类的实用程序来收集信息)

5 个答案:

答案 0 :(得分:62)

库由ld.so加载(动态链接器或运行时链接器,即rtld,ld-linux.so.2ld-linux.so.*,以防Linux; glibc的一部分)。它被声明为所有动态链接的ELF二进制文件的“解释器”(INTERP; .interp部分)。因此,当您启动程序时,Linux将启动ld.so(加载到内存并跳转到其入口点),然后ld.so将程序加载到内存中,准备然后运行它。您也可以使用

启动动态程序
 /lib/ld-linux.so.2 ./your_program your_prog_params

ld.so执行所有需要的ELF文件的实际openmmap,程序的ELF文件和所有必需库的ELF文件。此外,它填充GOT和PLT表并进行重定位解析(它将函数的地址从库写入调用站点,在许多情况下使用间接调用)。

使用ldd实用程序可以获得的某些库的典型加载地址。它实际上是一个bash脚本,它设置一个ld.so的调试环境变量(在glibc的rtld情况下实际为LD_TRACE_LOADED_OBJECTS=1)并启动一个程序。您甚至可以在不需要脚本的情况下自己完成,例如使用bash轻松更改单个运行的环境变量:

 LD_TRACE_LOADED_OBJECTS=1 /bin/echo

ld.so将看到此变量并将解析所有需要的库并打印它们的加载地址。但是使用此变量集,ld.so实际上不会启动程序(不确定程序或库的静态构造函数)。如果禁用ASLR feature,则加载地址大多数时间都相同。现代Linux通常启用了ASLR,因此要禁用它,请使用echo 0 | sudo tee /proc/sys/kernel/randomize_va_space

您可以在binutils中使用system实用程序找到libc.sonm函数的偏移量。我想,你应该使用nm -D /lib/libc.soobjdump -T /lib/libc.so和grep输出。

答案 1 :(得分:12)

“走到源头,问马......”

Drepper - How To Write Shared Libraries

Linux库编写者的必读文档。详细解释加载的机制。

答案 2 :(得分:8)

nm上使用的libc.so命令会显示systemlibc.so符号的位置。但是,如果启用了ASLR,则会加载地址libc.so,因此每次运行程序时system的最终地址都会随机变化。即使没有ASLR,您也需要确定加载地址libc.so并将system的地址抵消该数量。

答案 3 :(得分:6)

如果您只想要一个函数的地址而不是硬编码名称,那么您可以dlopen()主程序:

void *self = dlopen(NULL, RTLD_NOW);
dlsym(self, "system"); // returns the pointer to the system() function

如果您只想在编译时知道名称的函数的地址,只需使用void *addr = &system;

答案 4 :(得分:0)