c - 加载原始二进制文件

c - loading raw binaries

是否可以执行存储在 char 数组中的原始二进制文件?我试过这样做:

#include "stdio.h"
int main(int argc, char **argv)
{
    FILE *f = fopen(argv[1],"r");
    if(!f)
        return 1;
    fseek(f,0,SEEK_END);
    long l=ftell(f);
    rewind(f);
    char *buf = malloc(l+1);
    fread(buf,1,l,f);
    fclose(f);
    void (*func)() = (void(*))buf;
    func();
}

但它只给出了我的段错误。我正在自己工作 OS(从头开始),所以我要摆脱它们。

抱歉,这不是一个确切的答案,但它太长了,不适合作为评论...

我假设使用原始二进制文件将文件读入缓冲区的目的是将代码字节放入 RAM,而您想执行这些字节。假设您已经修复了文件 I/O,那么现在您有一个包含代码字节的缓冲区。您仍然会出现段错误的原因有几个。

首先,您的 O/S 是否实现了具有页属性(如读、写和执行)的虚拟内存?大多数现代 O/S 不允许您在未标记为代码的页面上执行代码。 (以这种方式标记页面对于了解可以交换的内容以及防止恶意编码很重要。)

其次,你加载的二进制代码是完全可重定位的吗?换句话说,如果代码中有任何跳转,它们都是相对的吗?如果它们中有任何绝对 JUMP 操作,那么您需要 运行 通过修补它们以对齐缓冲区在内存中的位置。

三、二进制代码是100%自包含的吗?如果它调用任何外部函数,那么您也需要修补它们。

最后,二进制代码需要访问数据吗?如果是这样,所有数据是否也在二进制中以及相对寻址与绝对寻址。

您也许可以做到,但是:

  1. 您不能(通常)将可执行文件存储在堆中,就像您在此处使用 malloc 那样(出于同样的原因,也不能将其存储在堆栈中),因为如果您的硬件支持它,您的 OS 可能将这些区域标记为可读、可写但不可执行(或者至少应该这样做)。

  2. 你不能只是将编译后的程序代码提取到文件中并期望运行它,因为它通常需要重定位、导入动态库、设置另一个虚拟内存变量区域。

您可以使用一个简单的手工制作的程序来做到这一点,该程序对 exit(0) 进行系统调用并打印 "Hello World".

您也许可以使用编译后的代码。为此,您需要(至少):

  • 编译一个独立的程序(没有导入的动态库,link静态库并重新编译那些静态linked库);

  • 与位置无关的代码(-fpic of -fpie);

  • 没有任何搬迁(也许 -fvisibility=hidden 可能有帮助?)。

如果您设法做到这一点,您可能能够从 ELF 文件的 PT_LOAD 部分生成原始文件。它可能需要可执行、可读和可写(因为您将拥有代码和数据)。并且您可能必须预先添加一条指令以跳转到可能位于文件中间的入口点。

你可能会看看 ld.so 是如何编译的:它应该被加载到虚拟地址 space 中的任何地方,并且它自己的一个子集应该在重定位之前起作用(因为ld.so 按照我的理解重新定位。

但您可能应该尝试实现一个基本的 ELF 加载器(并正确处理重定位)。