如何动态生成并运行本机代码?
- 2024-11-13 08:36:00
- admin 原创
- 22
问题描述:
我想为一个我编写的玩具语言处理器(纯学术用途)编写一个非常小的概念验证 JIT 编译器,但我在设计的中期遇到了一些麻烦。从概念上讲,我熟悉 JIT 的工作原理 - 将字节码编译成(机器或汇编?)代码来运行。然而,在具体细节方面,我不太明白你实际上是如何做到这一点的。
由于我根本不知道从哪里开始,所以我的(非常“新手”)下意识反应是尝试以下操作:
mmap() 一块内存,设置对 PROT_EXEC 的访问
将本机代码写入块中
将当前寄存器(堆栈指针等)存储在某个舒适的地方
修改当前寄存器以指向映射区域中的本机代码块
本机代码现在将由机器执行
恢复先前的寄存器
这是否接近/正确的算法?我尝试仔细研究我知道有 JIT 编译器可供研究的不同项目(例如V8),但这些代码库由于其大小而难以使用,而且我不知道从哪里开始寻找。
解决方案 1:
不确定是否适用于 Linux,但这适用于 x86/windows。
更新:http ://codepad.org/sQoF6kR8
#include <stdio.h>
#include <windows.h>
typedef unsigned char byte;
int arg1;
int arg2;
int res1;
typedef void (*pfunc)(void);
union funcptr {
pfunc x;
byte* y;
};
int main( void ) {
byte* buf = (byte*)VirtualAllocEx( GetCurrentProcess(), 0, 1<<16, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
if( buf==0 ) return 0;
byte* p = buf;
*p++ = 0x50; // push eax
*p++ = 0x52; // push edx
*p++ = 0xA1; // mov eax, [arg2]
(int*&)p[0] = &arg2; p+=sizeof(int*);
*p++ = 0x92; // xchg edx,eax
*p++ = 0xA1; // mov eax, [arg1]
(int*&)p[0] = &arg1; p+=sizeof(int*);
*p++ = 0xF7; *p++ = 0xEA; // imul edx
*p++ = 0xA3; // mov [res1],eax
(int*&)p[0] = &res1; p+=sizeof(int*);
*p++ = 0x5A; // pop edx
*p++ = 0x58; // pop eax
*p++ = 0xC3; // ret
funcptr func;
func.y = buf;
arg1 = 123; arg2 = 321; res1 = 0;
func.x(); // call generated code
printf( "arg1=%i arg2=%i arg1*arg2=%i func(arg1,arg2)=%i
", arg1,arg2,arg1*arg2,res1 );
}
解决方案 2:
您可能想看看libjit,它提供了您正在寻找的基础设施:
libjit 库实现了即时编译功能。与其他 JIT 不同,该库被设计为独立于任何特定的虚拟机字节码格式或语言。
http://freshmeat.net/projects/libjit
解决方案 3:
如何 JIT - 介绍是一篇新文章(从今天开始!),它解决了其中一些问题并描述了更大的前景。
解决方案 4:
Android Dalvik JIT 编译器可能也值得一看。它应该相当小巧精简(不确定这是否有助于理解它或使事情变得更加复杂)。它也针对 Linux。
如果事情变得更加严重,那么看看 LLVM 也许也是一个不错的选择。
Jeremiah 建议的函数指针方法听起来不错。您可能无论如何都想使用调用者的堆栈,并且可能只剩下几个寄存器(在 x86 上)需要保留或不触碰。在这种情况下,如果您的编译代码(或入口存根)在继续之前将它们保存在堆栈上,这可能是最简单的。最后,一切都归结为编写一个汇编函数并从 C 与其交互。
解决方案 5:
答案取决于您的编译器以及代码的放置位置。请参阅http://encode.ru/threads/1273-Just-In-Time-Compilation-Improvement-For-ZPAQ?p=24902&posted=1#post24902
在 32 位 Vista 中测试,无论代码是放在堆栈、堆还是静态内存中,Visual C++ 都会出现 DEP(数据执行保护)错误。有时可以使 g++、Borland 和 Mars 正常工作。JIT 代码访问的数据需要声明为 volatile。
解决方案 6:
除了目前建议的技术之外,研究线程创建函数可能也是值得的。如果您创建一个新线程,并将起始地址设置为生成的代码,那么您肯定知道没有需要保存或恢复的旧寄存器,并且操作系统会为您处理相关寄存器的设置。也就是说,您可以省去列表中的步骤 3、4 和 6。
解决方案 7:
Linux x86mmap
最小示例
只是为了提供一个 Linux 版本mmap
。在运行时,我将一个相当于以下内容的函数注入内存:
int ing(int i) {
return i + 1;
}
主程序
#define _XOPEN_SOURCE 700
#include <assert.h>
#include <stddef.h> /* NULL */
#include <sys/mman.h> /* mmap, munmap */
union funcptr {
int (*f)(int);
unsigned char *bytes;
};
int main(void) {
unsigned char *buf = (unsigned char *)mmap(NULL, 4, PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
assert(buf != MAP_FAILED);
unsigned char *p = buf;
// return i + 1;
// lea 0x1(%rdi),%eax
*p++ = 0x8d;
*p++ = 0x47;
*p++ = 0x01;
// ret
*p++ = 0xC3;
assert(((union funcptr){ .bytes = buf }).f(1) == 2);
// Just to check if we can modify the code witout any explicit icache flushing.
// return i + 2;
// lea 0x1(%rdi),%eax
buf[2] = 0x02;
assert(((union funcptr){ .bytes = buf }).f(1) == 3);
int ret = munmap(buf, 4);
assert(!ret);
}
编译并运行:
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main main.c
./main
union
我在https://stackoverflow.com/a/4912662/895245中使用as ,因为 C 标准显然禁止将非函数指针转换为函数指针:ISO C Void * 和函数指针这有点家长式作风,如果你忽略警告,GCC 也可以直接使用以下命令进行转换:
assert(((int (*)(int))(buf))(1) == 2);
通过编译测试文件获取shell代码:
不是main.c
int inc(int i) {
return i + 1;
}
并-O3
拆卸它:
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -c -o notmain.o notmain.c
objdump -d notmain.o
输出内容如下:
0000000000000000 <inc>:
0: f3 0f 1e fa endbr64
4: 8d 47 01 lea 0x1(%rdi),%eax
7: c3 ret
这endbr64
是一个 NOP/安全功能,因此我们可以(“不”安全地)忽略它:endbr64 指令实际上做什么?
有关的:
在 C++ 中,是否可以在运行时动态创建一个函数?
如何直接从内存编译并执行?
在 Ubuntu 23.10 amd64 上测试。
解决方案 8:
您可能对为什么幸运的僵尸使用Potion编程语言感兴趣。它是一种小型、不完整的语言,具有即时编译功能。Potion 的体积小,更容易理解。存储库包含该语言内部的描述(JIT 内容从标题“~jit ~ ”开始)。
由于它在Potion 的 VM上下文中运行,因此实现起来很复杂。不过,不要让这吓到你。你很快就能看出来他在做什么。基本上,使用一小组 VM 操作码可以将一些操作建模为优化的汇编。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件