2

长话短说,我有一个整数数组,它表示具有一个函数的 ELF 二进制文件的 .text 部分。我想执行这个功能。在尝试执行命令之前,我已经运行了这个命令:

mprotect(function, sHeader.sh_size, PROT_EXEC | PROT_READ | PROT_WRITE);

认为它可能会解决权限问题,但是当我尝试运行它时它仍然会出现段错误:

int (*fp)(int, int) = (int (*)(int, int))getFunc("t.o");
int a = 2;
int b = 3;
cout << fp(a, b) << "\n";

但是当我尝试运行它时它仍然会出现段错误:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000603010 in ?? ()

有什么我错过的吗?

我正在尝试执行的函数的 objdump:

0000000000000000 <mult>:
mult():
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   89 7d fc                mov    %edi,-0x4(%rbp)
   7:   89 75 f8                mov    %esi,-0x8(%rbp)
   a:   8b 45 fc                mov    -0x4(%rbp),%eax
   d:   0f af 45 f8             imul   -0x8(%rbp),%eax
  11:   5d                      pop    %rbp
  12:   c3                      retq   
4

2 回答 2

3

ELF对象文件包含重定位信息,并且很可能它的部分包含要重定位的.text代码,因此代码不会按原样运行。使用objdumpreadelf命令来探索它。如果您真的想按您的方式加载它,您应该处理重定位信息,这是复杂的、特定于处理器的且乏味的。如果您真的想花几周时间来做这件事,请研究 x86-64 ABI。但是使用dlopenthen.sodlsym简单得多(因为在-ed 段 fromdlopen之后进行重定位 ),见下文。mmapt.so

x86-64 ABI 曾经位于http://x86-64.org/documentation/abi.pdf但该站点今天无法使用

是什么getFunc?你如何在你的内部进行搬迁t.o?为什么你不能有一个t.so共享对象(例如用 编译gcc -Wall -fPIC -O -shared t.c -o t.so)然后使用dlopen(3)加载它,dlsym(3)例如

typedef int functionsig_t (int, int);
void* dlh = dlopen("./t.so", RTLD_NOW);
if (!dlh) {
   fprintf(stderr, "dlopen t.so failed with %s\n", dlerror());
   exit(EXIT_FAILURE);
};
functionsig_t* fp = (functionsig_t*) dlsym(dlh, "myfunc");
if (!fp) {
   fprintf(stderr, "dlsym myfunc failed with %s\n", dlerror());
   exit(EXIT_FAILURE),
}
// now you can call fp
int res = (*fp) (1,2);

一旦fp返回并且不存在调用堆栈帧,使用t.so您可以使用的任何函数dlclose(dlh);munmap来自t.so. 您可以避免调用dlclose(这通常会使进程地址空间泄漏微不足道;请参阅/proc/1234/mapspid 1234 进程的文件),尤其是在您没有dlopen大量共享对象的情况下。

如果t.so插件从您的主程序调用函数,您希望该主程序与-rdynamic选项链接到ldgcc

如果t.so是从一些 C++ 源代码编译的,它应该声明

 extern "C" int myfunc(int,int);

因为名称修改g++

我的manydl.cdlopen程序表明,您可以在 Linux 进程中执行数十万个-s。它的工作原理是生成“随机”C 代码,将其编译成 some .so,然后dlopen-ing 该.so文件,然后重复多次。

如果您不想将代码编译成.c插件的负担,您可以考虑使用LLVMasmjitlibjitGNU 闪电等在内存中即时生成代码....cc.so

于 2013-01-12T08:05:01.593 回答
2

您缺少搬迁。二进制不包含绝对地址,而是偏移量。

加载二进制文件时,操作系统通过将分配的段地址添加到二进制文件中的偏移量来重新定位函数指针(和所有其他符号)。

于 2013-01-12T08:00:13.980 回答