我正在使用Linux上的函数指针并尝试执行此C程序:
#include <stdio.h>
#include <string.h>
int myfun()
{
return 42;
}
int main()
{
char data[500];
memcpy(data, myfun, sizeof(data));
int (*fun_pointer)() = (void*)data;
printf("%d\n", fun_pointer());
return 0;
}
不幸的是,它会在fun_pointer()
电话上发生段错误。我怀疑它与一些内存标志有关,但我没有找到有关它的信息。
你能解释为什么这段代码会出现错误吗?不要看到固定的data
数组大小,没关系,无需调用函数即可成功。
UPD :最后我发现应使用mprotect系统调用PROT_EXEC
标记将内存段标记为可执行文件。此外,内存段应由mmap函数返回,如POSIX规范中所述。
有mmap
内存使用PROT_EXEC
标志分配的相同代码(并且有效):
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int myfun()
{
return 42;
}
int main()
{
size_t size = (char*)main - (char*)myfun;
char *data = mmap(NULL, size, PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
memcpy(data, myfun, size);
int (*fun_pointer)() = (void*)data;
printf("%d\n", fun_pointer());
munmap(data, size);
return 0;
}
此示例应符合-fPIC
gcc选项,以确保函数中的代码与位置无关。
答案 0 :(得分:5)
那里有几个问题:
data
数组位于data segment,而不是code segment。答案 1 :(得分:2)
除了Diask's answer之外,你可能还想使用一些JIT compilation技术(在内存中生成可执行代码),你应该确保包含代码的内存区域是可执行的(参见{{ 3}}和mprotect(2);出于安全原因,调用堆栈通常不可执行。您可以使用NX bit(快速发出慢速机器代码),GNU lightning,asmjit,libjit,LLVM(能够慢慢发出快速优化的机器代码)。您还可以在某个临时文件/tmp/emittedcode.c
中发出一些C代码,分叉编译命令gcc -Wall -O -fPIC -shared /tmp/emittedcode.c -o /tmp/emittedcode.so
然后GCCJIT共享对象/tmp/emittedcode.so
并使用dlopen(3)查找功能在那里用他们的名字指示。
另请参阅dlsym(3),this,this,this和this个答案。阅读that,trampoline code和closures&amp; continuations
当然,将代码从一个区域复制到另一个区域通常不起作用(必须CPS才能使其工作,或者您需要自己的position independent code机制,有点像{ {3}}确实如此。
答案 2 :(得分:0)
这是因为这条线错了:
memcpy(data, myfun, sizeof(data));
您正在复制函数的代码(已编译)而不是函数的地址。
myfun和&amp; myfun将拥有相同的地址,因此要进行memcpy操作,您必须使用函数指针,然后从其地址复制。
示例:
int (*p)();
p = myfun;
memcpy(data, &p, sizeof(data));