如何将LLVM的.ll文件转换为可嵌入的C字节数组形式机器码
解决方案
下面是一套直接实现你需求的流程,不用手动解析目标文件,完全基于LLVM和常用工具链完成:
1. 将.ll文件编译为目标平台汇编代码
用LLVM的llc工具把LLVM IR(.ll文件)转成对应平台的汇编代码:
llc -filetype=asm -mtriple=<目标平台> input.ll -o output.s
- 替换
<目标平台>为你的目标架构,比如x86_64-linux-gnu、i386-windows-gnu等,确保和主程序的编译平台一致 - 如果需要强制指定CDECL调用约定,可添加
-call-conv=cdecl参数
2. 将汇编代码转为纯机器码二进制文件
用GNU汇编器as编译汇编文件,再用objcopy提取.text段的纯机器码:
# 编译汇编为目标文件 as output.s -o output.o # 提取.text段的纯二进制机器码 objcopy -O binary --only-section=.text output.o output.bin
这一步会得到只包含函数机器码的二进制文件,没有任何目标文件的元数据。
3. 将二进制文件转为C字节数组
用xxd工具把二进制文件直接转成可嵌入的C头文件:
xxd -i output.bin > output.h
生成的output.h会包含类似这样的内容:
unsigned char output_bin[] = { 0x55, 0x48, 0x89, 0xe5, 0x48, 0x83, 0xec, 0x10, 0x89, 0x7d, 0xfc, 0x89, 0x75, 0xf8, 0xb8, 0x0a, 0x00, 0x00, 0x00, 0xc9, 0xc3 }; unsigned int output_bin_len = 20;
4. 在主程序中调用机器码数组
- 包含生成的头文件:
#include "output.h" - 调整内存权限为可执行(以Linux为例):
#include <sys/mman.h> #include <stdint.h> // 计算机器码数组所在的内存页起始地址 uintptr_t page_start = (uintptr_t)output_bin & ~(sysconf(_SC_PAGE_SIZE) - 1); // 设置内存页为可读可写可执行 mprotect((void*)page_start, sysconf(_SC_PAGE_SIZE), PROT_READ | PROT_WRITE | PROT_EXEC);
- 转为函数指针并调用(遵循CDECL约定):
// 定义匹配函数签名的函数指针类型 typedef int (*MyFunc)(int, int); MyFunc func = (MyFunc)output_bin; // 调用函数 int result = func(10, 20);
注意事项
- 确保.ll文件中的函数实现严格遵循CDECL调用约定:参数从右往左入栈,调用者负责清理栈,返回值存到指定寄存器(x86是EAX,x86_64是RAX)
- 目标平台的工具链要和LLVM的
llc目标三元组匹配,避免架构不兼容 - Windows平台下设置内存权限要用
VirtualProtect函数,逻辑类似但API不同
内容的提问来源于stack exchange,提问作者Jerome Haltom
相关产品推荐
相关产品推荐

