如何在 x64 中创建 thunk? [英] How to create thunk in x64?
问题描述
我发现了很好的示例如何为闭包创建 thunk,但它是 32 位版本:
I've found nice example how to create thunk for closure, but it's 32-bit version:
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
struct env {
int x;
};
struct __attribute__((packed)) thunk {
unsigned char push;
struct env * env_addr;
unsigned char call;
signed long call_offset;
unsigned char add_esp[3];
unsigned char ret;
};
struct thunk default_thunk = {0x68, 0, 0xe8, 0, {0x83, 0xc4, 0x04}, 0xc3};
typedef void (* cfunc)();
struct thunk * make_thunk(struct env * env, void * code)
{
struct thunk * thunk = (struct thunk *)mmap(0,sizeof(struct thunk), PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
*thunk = default_thunk;
thunk->env_addr = env;
thunk->call_offset = code - (void *)&thunk->add_esp[0]; // Pretty!
mprotect(thunk,sizeof(struct thunk), PROT_EXEC);
return thunk;
}
void block(struct env * env) {
env->x += 1;
printf ("block: x is %d
", env->x);
}
cfunc foo (int x)
{
struct env * env = (struct env *)malloc(sizeof(struct env));
env->x = x;
printf ("x is %d
",env->x);
return (cfunc)make_thunk(env,(void *)&block);
}
int main() {
cfunc c = foo(5);
c();
c();
}
如何为 64 位版本重写它?
How can I rewrite it for 64-bit version?
我使用的是 Linux x86_64.我已经能够使用 gcc -m32
交叉编译它,效果很好.
I'm using Linux x86_64. I've been able to cross-compile it with gcc -m32
, which worked perfectly.
推荐答案
以下代码设计用于 Linux 上的 GCC,应支持 32 位和 64 位编译.
The code below is designed to be used with GCC on Linux and should support 32 and 64 bit compilation.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
struct env {
int x;
};
#if __x86_64__
struct __attribute__((packed)) thunk {
unsigned char mov[2];
struct env * env_addr;
unsigned char movrax[2];
void (*call_address)();
unsigned char jmp[2];
};
struct thunk default_thunk = {{0x48, 0xbf}, 0x0, {0x48, 0xb8}, 0x0, {0xff, 0xe0} };
#elif __i386__
struct __attribute__((packed)) thunk {
unsigned char push;
struct env * env_addr;
unsigned char call;
signed long call_offset;
unsigned char add_esp[3];
unsigned char ret;
};
struct thunk default_thunk = {0x68, 0, 0xe8, 0, {0x83, 0xc4, 0x04}, 0xc3};
#else
#error Architecture unsupported
#endif
typedef void (* cfunc)();
struct thunk * make_thunk(struct env * env, void * code)
{
struct thunk * thunk = (struct thunk *)mmap(0,sizeof(struct thunk),
PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
*thunk = default_thunk;
#if __x86_64__
thunk->env_addr = env;
thunk->call_address = code; /* Pretty! */
#else
thunk->env_addr = env;
thunk->call_offset = code - (void *)&thunk->add_esp[0]; /* Pretty! */
#endif
mprotect(thunk,sizeof(struct thunk), PROT_EXEC);
return thunk;
}
void block(struct env * env) {
env->x += 1;
printf ("block: x is %d
", env->x);
}
cfunc foo (int x)
{
struct env * env = (struct env *)malloc(sizeof(struct env));
env->x = x;
printf ("x is %d
",env->x);
return (cfunc)make_thunk(env,(void *)&block);
}
int main() {
cfunc c = foo(5);
c();
c();
return 0;
}
假设操作系统正在使用 系统V 64 位 ABI(Linux 使用的)调用约定然后将传递给函数的第一个参数将在寄存器 %rdi
中.然后我们只需要mov
环境地址(env_addr
)到%rdi
,然后做一个call
.该调用使用通过 %rax
间接跳转到绝对位置.所以指令序列看起来像(at&t 语法):
Assuming that the OS is using System V 64bit ABI (Which Linux uses) calling convention then the first parameter that will be passed to the function will be in register %rdi
. Then we just have to mov
the environment address (env_addr
) to %rdi
and then do a call
. The call uses an indirect jump to an absolute location through %rax
. So the instruction sequence looks like (at&t syntax):
mov $env_addr, %rdi
movabs $call_pointer, %rax
jmpq *%rax # Tail call instead of call/retq
这篇关于如何在 x64 中创建 thunk?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!