3

我正在为 Linux 编写一个非常简单的进程加载器。我正在加载的可执行文件已经编译,并且我知道每个可执行文件在内存中的位置。我尝试的第一种方法是mmap()手动将每个代码或数据部分放在正确的位置,例如

mmap(addr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0)

除非我删除 MAP_FIXED 标志,否则会出现段错误,因为似乎一个块的地址与内存中已经存在的东西冲突,甚至可能与加载程序本身冲突;该地址0x401000似乎是有问题的地址。

我什至不确定从哪里开始。有朋友建议虚拟化内存访问操作;我不确定我会为此采取什么样的性能打击,我也不知道它是如何完成的,但它可能是一种选择。我真正想做的是创建一个“空”进程,就它而言,它会完全运行内存,因此在我想要它之前不会将任何内容加载到用户空间中。“空”过程的整个概念可能毫无意义,但它是描述我想要什么的最佳方式。我非常渴望一些可能对我有帮助的参考资料或示例。

4

1 回答 1

0

随着您的进程运行(可能在“sleep(1000);”中打盹),查看它的 /proc/pid/maps。这将告诉您 0x401000 的用途。

~$ sleep 1h &
[3] 2033
~$ cat /proc/2033/maps
00110000-002af000 r-xp 00000000 08:01 1313056    /lib/i386-linux-gnu/libc-2.15.so
...

在我的盒子上,/bin/sleep 不使用那个块,我的小单行程序也没有。

您可能正在链接某个想要登陆的图书馆?

因此,一种方法是尽早分配您需要的块(早在 main() 运行之前——在别处寻找该信息)。

另一种方法是将您的代码链接到您“知道”未使用的某个地址(大概是您自己生成 x86 操作码,或者以其他方式“链接”,所以这不应该是一个延伸)。

另一个更好的选择是使您的代码可重定位。您不想替换整个进程的地址空间(正是 exec 所做的)这一事实或多或少地表明您的代码应该就是这样。

所以找到一个可用的地址,在那里加载这些位,并根据需要执行重定位(所以你的磁盘文件格式,如果它不是 ELF,将需要包含 reloc 信息)。这就是大路,也是您接下来要从装载机那里得到的显而易见的东西。

当然,这几乎意味着自己重新实现 dlopen()。我假设您只是想了解它是如何工作的——如果没有,请 man dlopen。Stephane 的零规则:它已经存在 ;-)

不要忘记支持从您的代码链接其他库(不重复)、dlclose()、初始化程序、各种 RTLD_* 模式、尊重 MYCUSTOMLD_LIBRARY_PATH、GCC 的 __thread 说明符等;-)

于 2012-08-18T23:08:06.900 回答