目标文件为二进制代码

假设我有一个没有外部依赖的C文件,只有const数据部分。 我想编译这个文件,然后得到一个二进制blob,我可以在另一个程序中加载,其中该函数将通过函数指针使用。

我们举一个例子,这是一个虚构的二进制模块,f1.c

static const unsigned char mylut[256] = { [0 ... 127] = 0, [128 ... 255] = 1, }; void f1(unsigned char * src, unsigned char * dst, int len) { while(len) { *dst++ = mylut[*src++]; len--; } } 

我想将它编译为f1.o,然后f1.bin,并在prog.c中使用它

 int somefunc() { unsigned char * codedata; f1_type_ptr f1_ptr; /* open f1.bin, and read it into codedata */ /* set function pointer to beginning of loaded data */ f1_ptr =(f1_type_ptr)codedata; /* call !*/ f1_ptr(src, dst, len); } 

我想从f1.c到f1.o涉及-fPIC以获得位置独立性。 我可以使用从f1.o到f1.bin的标志或链接描述文件是什么?

澄清:

我知道动态链接。 在这种情况下,动态链接是不可能的。 如果可能的话,链接步骤必须是指向加载数据的func指针

请假设没有OS支持。 如果可以的话,我会举例说明在与PC相关的assembly中assemblyf1。

首先,正如其他人所说,你应该考虑使用DLL或SO。

也就是说,如果你真的想这样做,你需要替换链接器脚本。 像这样的东西(测试不是很好,但我认为它有效):

 ENTRY(_dummy_start) SECTIONS { _dummy_start = 0; _GLOBAL_OFFSET_TABLE_ = 0; .all : { _all = .; LONG(f1 - _all); *( .text .text.* .data .data.* .rodata .rodata.* ) } } 

然后编译:

 $ gcc -c -fPIC test.c 

链接:

 $ ld -T script.ld test.o -o test.elf 

并提取二进制blob:

 $ objcopy -j .all -O binary test.elf test.bin 

可能会对脚本进行一些解释:

  • ENTRY(_dummy_start)这只是避免了关于没有入口点的程序的警告。
  • _dummy_start = 0; 这定义了前一行中使用的符号。 该值未使用。
  • _GLOBAL_OFFSET_TABLE_ = 0; 这可以防止另一个链接器错 我不认为你真的需要这个符号,所以它可以定义为0。
  • .all这是收集blob所有字节的部分的名称。 在此示例中,将所有.text.data.rodata部分放在一起。 如果您有复杂的function,可能还需要更多function,在这种情况下, objdump -x test.o是您的朋友。
  • LONG(f1 - _all)不是真的需要,但你想知道你的函数偏移到blob中,不是吗? 您不能假设它将在偏移0处。使用此行,blob中的前4个字节将是符号f1 (您的函数)的偏移量。 如果使用64位指针,则使用QUAD更改LONG

更新 :现在一个快速的测试(它的工作!):

 #include  #include  #include  typedef void (*f1_t)(char *a, char *b, int len); f1_t f1; int main() { char *blob = (char*)valloc(4096); FILE *f = fopen("test.bin", "rb"); fread(blob, 1, 4096, f); fclose(f); unsigned offs = *(unsigned*)blob; f1 = (f1_t)(blob + offs); mprotect(blob, 4096, PROT_READ | PROT_WRITE | PROT_EXEC); char txt[] = "¡hello world!"; char txt2[sizeof(txt)] = ""; f1(txt, txt2, sizeof(txt) - 1); printf("%s\n%s\n", txt, txt2); return 0; } 

您应该考虑构建一个共享库( .dll用于Windows,或.so用于Linux)。

像这样构建lib:

 gcc -c -fPIC test.c gcc -shared test.o -o libtest.so 

如果要从代码中动态加载库,请查看函数dlopen(3)dlsym(3)

或者,如果要在编译时链接库,请使用以下命令构建程序

 gcc -c main.c gcc main.o -o  -ltest 

编辑:

我真的不确定我会在这里说些什么,但这可以为你提供研究进展的线索……

如果您不想使用dlopendlsym ,可以尝试从.o文件中读取符号表以查找函数地址,然后使用读取和执行权限将内存中的目标文件映射到内存中。 然后,您应该能够在找到的地址执行加载的代码。 但请注意您在此代码中可能遇到的其他依赖项。

你可以查看man page elf(5)