【发布时间】:2018-05-04 17:09:00
【问题描述】:
我正在尝试编写一个复制函数(并最终修改其程序集)并返回它的函数。这适用于一级间接,但在二级我得到一个段错误。
这是一个最小(不)工作的例子:
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#define BODY_SIZE 100
int f(void) { return 42; }
int (*G(void))(void) { return f; }
int (*(*H(void))(void))(void) { return G; }
int (*g(void))(void) {
void *r = mmap(0, BODY_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
memcpy(r, f, BODY_SIZE);
return r;
}
int (*(*h(void))(void))(void) {
void *r = mmap(0, BODY_SIZE, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
memcpy(r, g, BODY_SIZE);
return r;
}
int main() {
printf("%d\n", f());
printf("%d\n", G()());
printf("%d\n", g()());
printf("%d\n", H()()());
printf("%d\n", h()()()); // This one fails - why?
return 0;
}
我可以一次 memcpy 到一个 mmap'ed 区域,以创建一个可以调用的有效函数 (g()())。但是如果我尝试再次应用它(h()()()),它会出现段错误。我已经确认它正确地创建了g 的复制版本,但是当我执行该版本时,我得到了一个段错误。
我不能从另一个 mmap 区域执行一个 mmap 区域中的代码有什么原因吗?从带有x/i 的探索性gdb-ing 检查看来我可以成功调用,但是当我返回时,我来自的函数已被删除并替换为0。
我怎样才能让这种行为起作用?有没有可能?
大编辑:
很多人问我的理由,因为我显然在这里做一个 XY 问题。这是真实的和故意的。你看,不到一个月前this 问题发布在代码高尔夫堆栈交换上。它还为自己提供了一个不错的 bounty 用于 C/Assembly 解决方案。我对这个问题进行了一些闲散的思考,并意识到通过复制函数体,同时用一些唯一值存根地址,我可以在其内存中搜索该值并将其替换为有效地址,从而使我能够有效地创建 lambda 函数将单个指针作为参数。使用这个我可以让单一的柯里化工作,但我需要更一般的柯里化。因此,我目前的部分解决方案是链接here。这是展示我试图避免的段错误的完整代码。虽然这几乎是一个坏主意的定义,但我觉得它很有趣,并且想知道我的方法是否可行。我唯一缺少的是能够运行从函数创建的函数,但我无法让它工作。
【问题讨论】:
-
你确定g的body size小于100字节吗?
-
是的,反汇编显示它正好是 76 个字节。为了安全起见,我高估了。
-
因为使用更大的 BODY_SIZE 值不会产生段错误(但不是正确的值)。
-
我尝试转储内存地址,它们是相同的(可能是因为我的编译器)都有 76 字节的汇编,后跟 24 字节的 0。
标签: c linux assembly function-pointers currying