[转]将tcc 载入工程 编译脚本
来源:互联网 发布:php 统计图 编辑:程序博客网 时间:2024/05/24 05:43
原文地址:http://blog.csdn.net/nondeep/article/details/8498430
tiny cc 编译器
tcc是一个支持windows和linux环境的C语言编译器。它也可以将代码编译之后直接嵌入到进程中,这就具有脚本的相关特性了,但它却不是解释执行的。
tcc将C源码编译称机器码,然后分配一块内存,将机器码写在这块内存中,宿主进程可以调用源码中所定义的函数,源码也可以调用宿主进程提供的函数,交互还是相当灵活的。
我们可以将tcc源码编译称DLL(就直接用TCC自己编译自己,网上有具体的方法),这里只简单介绍一下如何将tcc.dll加入到我们的工程中,以此为我们的工程增加脚本功能,步骤如下:
包含头文件,导入库文件,这就不多说了
1.创建编译环境(包括状态、符号表等等的初始化)
TCCState *s=::tcc_new();
if(s==0)
{
printf("can not create compile environment\n"); return 0;
}
2.设置错误处理回调函数
::tcc_set_error_func(s,NULL,call_back_func_addr);
3.设置头文件和库文件目录
::tcc_add_sysinclude_path(s,"tcc/include");
::tcc_add_sysinclude_path(s,"tcc/include/winapi");
::tcc_add_sysinclude_path(s,"tcc/include/sys");
::tcc_add_library_path(s,"tcc/lib");
4.添加支持库(最好不要给脚本太多的API支持,如果用户要干坏事的话,太多的API支持让挂接的工作都省了。这也是C语言不适合做脚本的原因,因为他离操作系统太近了。起码也得在宿主进程和脚本之间隔一层虚拟机之类的东西,才能算得上有些安全。)
::tcc_add_library(hTcc,"user32");
::tcc_add_library(hTcc,"gdi32");
//::tcc_add_library(hTcc,"kernel32");
5.添加函数给脚本调用(也可以添加变量)
int arg_extern_test=123456;
int (*api_malloc)(int); api_malloc=malloc;
::tcc_add_symbol(s,"api_malloc",api_malloc);
::tcc_add_symbol(s,"arg_extern_test",(void*)&arg_extern_test);
5.设置机器码的存储类型(是生成一个硬盘EXE文件还是直接放到进程空间)
::tcc_set_output_type(s,TCC_OUTPUT_MEMORY);
6.编译(编译生成的机器码暂时还存放在语法树中,所以这时候只能确定指令的大小而不能确定指令起始地址)
if(::tcc_compile_string(s,g_source_string)==-1)
{
printf("compile failed!\n"); return 0;
}
7.获取机器码的总字节数
int size=::tcc_relocate(s,NULL);
if(size==-1)
{
printf("can not get opcode size\n"); return 0;
}
8.分配内存,存储机器码,存储结束后才能从符号表中搜索符号对应的地址
//从链表中取出机器码存放在opcode指向的内存块中(此过程中还会修改jmp,call这类指令的偏移以确保正确)
void *opcode=::malloc(size); ::tcc_relocate(s,opcode);
9.从符号表中查找脚本所定义的函数地址(这是让宿主进程能够实时地调用脚本中定义的函数)
void (*msg_init)();
void (*msg_exit)();
msg_init=(void(*)())::tcc_get_symbol(s,"msg_init");
msg_exit=(void(*)())::tcc_get_symbol(s,"msg_exit");
10.删除编译环境(删除包括编译过程中产生的临时数据,例如符号表、类型表、语法树等等)
::tcc_delete(s);
说明: 其实7、8两步完全可以由tcc内部处理的,可能是为了灵活性,最终还是采用了这种有些别扭的方式
主进程可以把任何事件交给脚本处理,比如点下某个按钮的时候,主进程就在脚本中找int msg_button_down,找到了就调用,脚本处理完后返回一个结果给主进程,本人习惯把这一过程称之为消息响应。当然,主进程必须提供足够多的函数,本人也习惯把这种功能函数称作接口。
以前用它写过某贴吧发贴机,效果如图:
#define __stdcall __attribute__((__stdcall__))
{
int d0, d1, d2;
__asm__ __volatile__(
"rep ; movsl\n\t"
"testb $2,%b4\n\t"
"je 1f\n\t"
"movsw\n"
"1:\ttestb $1,%b4\n\t"
"je 2f\n\t"
"movsb\n"
"2:"
: "=&c" (d0), "=&D" (d1), "=&S" (d2)
:"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
: "memory");
return (to);
}
- [转]将tcc 载入工程 编译脚本
- 将 tcc 载入工程 编译脚本
- 脚本载入
- TCC
- 通过ant脚本,编译打包android工程
- 批处理脚本编译VS2005工程的命令
- 通过ant脚本,编译打包android工程
- 通过ant脚本编译打包android工程
- 通过ant脚本编译打包android工程
- 通过ant脚本编译打包android工程
- 通过ant脚本,编译打包android工程
- 通过ant脚本,编译打包android工程
- 通过ant脚本,编译打包android工程
- 如何用shell脚本编译java工程
- 通过ant脚本,编译打包android工程
- 使用bat脚本编译VS工程
- 使用脚本(命令行)编译KEIL工程
- [转]用makefile脚本方式调用vs 2010 的nmake来编译工程的实例
- Oracle数据库RMAN备份与灾难恢复在寒区旱区特色数据库的应用
- 如何提高Java并行程序性能
- PHP中this关键字和self的却别
- git如何删除一个仓库
- 使用C++自定义Binder
- [转]将tcc 载入工程 编译脚本
- 给数据库建索引的规则
- ORACLE10G AWR使用和分析
- Apache 2.2和升级版2.4的httpd.conf配置差别总结
- Hibernate—Query
- gcc 编译选项
- 如何把oracle服务加到linux启动项
- 协议森林12 天下为公 (TCP堵塞控制)
- Triangle