【发布时间】:2012-10-17 06:01:47
【问题描述】:
我正在开发一个项目,以在 Linux 平台上加载和运行自定义二进制格式的可执行文件(在我的例子中是 PE)。到目前为止,我已经非常成功地完成了这项工作,首先加载可执行文件,然后加载一个小型 ELF 共享库,该库调用可执行文件的起始地址,然后安全退出。
不过,出于几个原因,我真的不想自己加载 ELF。首先,我使用的共享库是用汇编编写的(我不能使用其他任何东西,因为我没有链接到libc 等),这将是非常特定于平台的,我想离开从那里开始并使用C,所以我可以为任何平台编译。此外,使用 Linux 的原生 ELF 加载器而不是我自己的简化版本会更容易、更安全。
我想知道是否有办法使用我的 binfmt 处理程序(一个已安装的内核模块)来加载我的可执行文件,然后然后要求 Linux 将我的共享库(及其依赖项)加载到相同的地址空间而不会覆盖我的可执行代码。我首先认为uselib 系统调用可能有用,但手册页上的描述不清楚这是否符合我的目的:
从 libc 4.4.4 开始,仅加载库“/lib/ld.so”,因此 这个动态库可以加载所需的剩余库(再次 使用此调用)。这也是libc5的现状。
glibc2 不使用这个调用。
我也从未见过使用它的示例,而且我总是对使用我不理解的系统调用保持警惕。
有没有实现我所描述的好方法?我是否可以使用 Linux 的现有功能将共享库(用 C 编写)加载到已经包含可执行代码的地址空间中,如果可以,我如何在不知道加载位置的情况下使用该库?
【问题讨论】:
-
好吧,你可以完全解决这个问题。与其向内核添加 PE 支持,不如提供在用户空间运行的 PE 加载程序(即 ELF 可执行文件)。您可能会遇到一些页面保护错误(尝试写入可执行页面)的问题,但在这种情况下,您可以制作一个内核模块,允许您的应用程序在您的应用程序中创建一个新的可执行页面。基本上,创建一个可以加载 PE 可执行文件(如 PE 共享库)的内核模块。
-
@Linuxios 我一开始尝试过,但是我遇到了 PE 代码有一个固定地址的问题,这恰好是默认的 Linux 加载地址;如果我强制使用
mmap,那么整个事情都会崩溃,因为我正在覆盖内存中的执行代码。当用户空间不再是一个选项时,我切换到使用内核模块进行加载;它基本上是 ELF 加载器的模拟,我想要运行的 ELF 代码(来自共享库)是 ELF 解释器的模拟。 -
我明白了。现在,有一个想法:仍然加载一个 elf 二进制文件,但告诉 GCC 创建与位置无关的代码。然后,必须有某种标头来告诉 Linux 在哪个地址加载您的精灵代码,让 PE 地址空闲。
-
@Linuxios 这就是我现在正在做的事情,除了它是一个共享库而不是 PIC(对于我的目的来说基本上是一样的)。问题是 如何 告诉 linux 首先加载 ELF 代码。从内核模块调用
execve肯定会加载它,但在它自己的地址空间中;我想把它加载到 PE 中。
标签: linux memory-management process shared-libraries elf