Tiny C Compiler参考手册
1 简介TinyCC (简称TCC)是一个小且很快的C编译器。不像其他C编译器,他可以自依赖:你不需要扩展汇编器或连接器,因为TCC已经为你准备好了。 TCC编译速度奇快,以至于Makefile都不是那么必要了。 TCC不仅仅支持ANSI C,还支持ISO C99标准和很多GNU C扩展,包括内联汇编。 TCC还可以用于C脚本,例如一段C代码可以像Perl或Python脚本那样运行。编译速度很快,有如可执行文件一样。 TCC还会自动生成所有C指针操作的内存边界检查。TCC做这些是无需补丁库的。 使用libtcc,你可以用TCC作为动态代码生成的后端。 TCC主要支持Linux和Windows的i386目标。还有个alpha状态的ARM(arm-tcc)和TMS320C67xx(c67-tcc)目标。更多信息关注 http://lists./archive/html/tinycc-devel/2003-10/msg00044.html 。 关于在Windows上的使用,查看 tcc-win32.txt 。 2 命令行选项本手册基于Tiny C Compiler 0.9.25。 2.1 快速入门使用: tcc [options] [infile1 infile2 ...] [`-run' infile args ...] TCC选项非常像gcc的选项,主要的不同在于TCC可以直接执行结果程序,并赋予它运行时的参数。 如下是一些逻辑的理解: ``tcc -run a.c`` 编译 a.c 然后直接执行。 tcc -run a.c arg1 编译 a.c ,然后执行,并将arg1参数传递到 a.c 的 main() 函数。 tcc a.c -run b.c arg1 编译 a.c 和 b.c ,然后将他们连接到一起并执行。参数arg1作为结果程序 main() 的第一个参数。 tcc -o myprog a.c b.c 编译 a.c 和 b.c ,然后连接成可执行文件 myprog 。 tcc -o myprog a.o b.o 连接两个目标文件,生成输出文件 myprog 。 tcc -c a.c 编译 a.c ,生成目标文件 a.o 。 tcc -c asmfile.S 对 asmfile.S 使用C预处理器和汇编器,并生成目标文件 asmfile.o 。 tcc -c asmfile.s 直接汇编,不做预处理,生成 asmfile.o 。 tcc -r -o ab.o a.c b.c 编译 a.c 和 b.c ,并且把他们连接到一起,生成一个目标文件 ab.o 。 脚本: TCC可以从脚本调用,有如shell脚本一样。你只需要将 #! /usr/local/bin/tcc -run 加到你的C源码开头即可: #! /usr/local/bin/tcc -run #include <stdio.h> int main() { printf("Hello World!\n"); return 0; } TCC可以使用"-"选项,从标准输入读入C源码来替换输入文件,例如: echo 'main(){puts("hello");}' | tcc -run - 2.2 选项摘要常用选项: -v 显示TCC版本,增加显示信息。 -c 生成目标文件。(必须给定"-o"选项) -o outfile 输出目标文件、可执行文件、或者dll文件到outfile。 -Bdir 设置tcc内部库的搜索路径,缺省是 PREFIX/lib/tcc 。 -bench 输出编译统计。 -run source [args ...]
预处理选项: -Idir 指定附加的头文件路径。按照给出的顺序搜索。系统搜索路径总是排在最后。缺省系统头文件路径是: /usr/local/include 、 /usr/include 、 PREFIX/lib/tcc/include (PREFIX通常是 /usr 或者 /usr/local )。 -Dsym[=val] 定义预处理器符号 "sym" 到 val。如果val没有给出,则缺省值为1。这里也可以定义函数风格的宏,例如 -DF(a)=a+1 。 -Usym 删除预处理器定义的符号 sym 。 编译标识: 注意如下每个警告选项都可以换用 -fno- 前缀。 -funsigned-char 让char类型变成无符号的。 -fsigned-char 让char类型有符号。 -fno-common 不为未初始化数据生成普通符号。 -fleading-underscore 为每个C符号添加下划线前缀。 警告选项: -w 禁用所有警告。 如下每个警告选项都可以换用 -Wno- 前缀。 -Wimplicit-function-declaration 警告所有隐含(implicit)函数。 -Wunsupported 警告所有不支持的GCC的功能。 -Wwrite-string 让字符串常量类型使用 const char * 而不是 char * 。 -Werror 发生警告时中断编译。 -Wall 激活所有警告,除了 -Werror 、 -Wunsupported 、 -Wwrite-strings 。 链接选项: -Ldir 指定附加的静态库路径,用于 "-l" 链接选项。缺省库路径是 /usr/local/lib 、 /usr/lib 和 /lib 。 -lxxx 链接指定的动态库 libxxx.so 或静态库 libxxx.a 。搜索库路径是由 -L 选项提供的。 -shared 生成动态库,而不是可执行文件(必须使用"-o"选项)。(不确定) -static 生成静态链接可执行文件(缺省是动态链接)(必须用"-o"选项)。(不确定) -rdynamic 导出动态符号到动态连接器。这对于使用 dlopen() 打开的库访问可执行符号表很有用。 -r 生成对象文件包含所有的输入文件(必须用"-o"选项)。 -Wl,-Ttext,address 设置 .text 段的位置到address。 -Wl,--oformat,fmt
调试选项: -g 生成运行时调试信息,以便得到运行时错误信息。给出无效指针,而不仅仅是段错误。 -b 生成检查内存分配和数组/指针边界检查的附加代码。自动包含 "-g" 选项。注意生成代码会又大又慢。 -bt N 显示N个调用者的回溯。与"-g"和"-b"使用时有用。 注意GCC选项"-Ox"、"-fx"、"-mx"会被忽略。 3 C语言支持3.1 ANSI CTCC实现了ANSI C的所有标准,包括结构体中的位字段和浮点数(long double、double,和float的完整支持)。 3.2 ISOC99扩展TCC实现了新的C标准ISO C 99的很多特性。当前暂时不支持的包括:复数(complex)、虚数(imaginary)和变长数组。 当前实现的ISO C 99特性:
3.3 GNU C 扩展TCC实现了一部分GNU C扩展:
@waiting ... 3.4 TinyCC扩展
4 TinyCC汇编自从版本0.9.16,TinyCC开始集成自己的汇编器了。TinyCC的汇编器支持gas-like语法(GNU汇编器)。你可以禁用汇编器支持,来得到一个更小的TinyCC可执行文件(C编译器并不依赖汇编器)。 4.1 语法@waiting ... 4.2 表达式@waiting ... 4.3 标签@waiting ... 4.4 指令@waiting ... 4.5 X86汇编器@waiting ... 5 TinyCC连接器5.1 ELF文件生成器TCC可以在不需要其他连接器的情况下,直接输出重定位的ELF文件(目标文件),可执行ELF文件和动态ELF库。 动态ELF库可以直接输出,但是C编译器并不生成位置独立代码(PIC, Position independent Code)。这意味着TCC生成的动态库无法在进程间分解。 TCC连接器会清楚库里面的无引用对象。这在生成目标文件和库列表时直接跳过,所以指定目标文件和库文件的顺序很重要(对GNU ld也是一样的)。同样也不支持分组选项("--start-group"和"--end-group")。 5.2 ELF文件载入器TCC无法载入ELF目标文件、静态库(.a文件)和动态库(.so文件)。 5.3 PE-i386文件生成器TCC的Windows支持本地Win32可执行文件格式(PE-i386)。他可以生成EXE文件(控制台的和GUI的)和DLL文件。 了解在Windows上的使用,查看 tcc-win32.txt 。 5.4 GNU连接器脚本因为很多Linux系统的动态库使用GNU ld链接脚本,所以TCC连接器也支持GNU ld脚本的子集。 GROUP和FILE命令是支持的,OUTPUT_FORMAT和TARGET则被忽略。 例如 /usr/lib/libc.so /* GNU ld script */ GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a ) 6 TinyCC内存与边界检查该功能使用 "-b" 选项激活。 主意指针大小是不变的,而包含边界检查的代码生成与无检查的代码是全兼容的。当有来自无检查代码的指针时,会假设其有效。因此混用的代码之间也可以正常工作。 关于更多方法的信息请关注 http://www.doc./~phjk/BoundsChecking.html 。 这里是一些捕捉错误的例子: 标准字符串函数处理无效的范围: { char tab[10]; memset(tab,0,11); } 对本地和全局数组的越界错误: { int tab[10]; for(i=0;i<11;i++) { sum+=tab[i]; } } 由malloc生成的数据的越界错误: { int *tab; tab=malloc(20*sizeof(int)); for(i=0;i<21;i++) { sum+=tab4[i]; } free(tab); } 访问已经释放了的内存: { int *tab; tab=malloc(20*sizeof(int)); free(tab); for(i=0;i<20;i++) { sum+=tab4[i]; } } 二次释放: { int *tab; tab=malloc(20*sizeof(int)); free(tab); free(tab); } 7 libtcc库libtcc库允许你把TCC作为动态代码生成的后端。 阅读 libtcc.h 了解API。阅读 libtcc_test.c 看简单的例子。 这个主意在于你可以把C字符串直接通过libtcc编译。然后可以存取任意的全局符号。 7.1 libtcc APITCCState *tcc_new(void) 创建新的TCC编译上下文。 void tcc_delete(TCCState *s) 释放TCC编译上下文。 void tcc_enable_debug(TCCState *s) 在生成代码中添加调试信息。 void tcc_set_error_func(TCCState *s, void *error_opaque, void (*error_func)(void *opaque, const char *msg)) 设置发生错误/警告时的回调函数。 int tcc_set_warning(TCCState *s, const char *warning_name, int value) 设置和重设警告。 下面是预处理函数。 int tcc_add_include_path(TCCState *s, const char *pathname) 添加头文件搜索路径。 int tcc_add_sysinclude_path(TCCState *s, const char *pathname) 添加系统头文件搜索路径。 void tcc_define_symbol(TCCState *s, const char *sym, const char *value) 定义预处理器符号 "sym" ,可以添加可选的值。 void tcc_undefine_symbol(TCCState *s, const char *sym) 取消预处理器符号 "sym" 的定义。 如下是编译函数。 int tcc_add_file(TCCState *s, const char *filename) 添加一个文件,可以是C文件、dll、对象、库或者ld脚本。错误返回-1。 int tcc_compile_string(TCCState *s, const char *buf) 编译作为C源码的字符串,错误返回非0。 链接命令。 输出类型,必须在所有编译之前定义:
int tcc_set_output_type(TCCState *s, int output_type) 设置输出类型。 可执行文件格式:
int tcc_add_library_path(TCCState *s, const char *pathname) 等效于 "-Lpath" 选项。 int tcc_add_library(TCCState *s, const char *libraryname) 等同于用 "-lxxx" 选项添加链接库。 int tcc_add_symbol(TCCState *s, const char *name, void *val) 添加符号到编译过的程序。 int tcc_output_file(TCCState *s, const char *filename) 输出可执行文件、库或目标文件,在此之前别调用 tcc_relocate() 。 int tcc_run(TCCState *s, int argc, char **argv) 链接和运行 main() 函数,并返回值。在此之前别调用 tcc_relocate() 。 int tcc_relocate(TCCState *s1, void *ptr) 拷贝代码到内存传递给调用者,并做重定位(必须在 tcc_get_symbol() 之前调用)。如果出错返回-1且在ptr是NULL时需要大小。 void *tcc_get_symbol(TCCState *s, const char *name) 返回符号的值或找不到时返回NULL。 void tcc_set_lib_path(TCCState *s, const char *path) 设置运行时的CONFIG_TCCDIR。 7.2 libtcc的例子见 tests/libtcc_test.c #include <stdlib.h> #include <stdio.h> #include <string.h> #include "libtcc.h" /*这个函数将会被生成的代码所调用*/ int add(int a, int b) { return a+b; } char my_program[] = "int fib(int n) {\n" " if (n<=2)\n" " return 1;n" " else\n" " return fib(n-1)+fib(n-2);\n" "}\n" "int foo(int n) {\n" " printf(\"Hello World!\\n);\n" " return 0;\n" "}\n"; int main(int argc, char **argv) { TCCState *s; int (*func)(int); void *mem; int size; s=tcc_new(); if (!s) { fprintf(stderr, "Could not create tcc state\n"); exit(1); } /*如果tcclib.h和libtcc1.a尚未安装,寻找他们*/ if (argc==2 && !memcmp(argv[1],"lib_path=",9)) tcc_set_lib_path(s,argv[1]+9); /*必须在任何编译之前设置输出类型*/ tcc_set_output_type(s,TCC_OUTPUT_MEMORY); if (tcc_compile_string(s,my_program) == -1) return 1; /*添加add()函数允许动态生成代码调用,还可以用 tcc_add_dll() 来用。*/ tcc_add_symbol(s,"add",add); /*获取代码大小*/ size=tcc_relocate(s,NULL); if (size== -1) return 1; /*重定位内存并拷贝代码*/ mem=malloc(size); tcc_relocate(s,mem); /*获取入口符号*/ func=tcc_get_symbol(s,"foo"); if (!func) return 1; /*删除状态*/ tcc_delete(s); /*运行代码*/ func(32); free(mem); return 0; } 8 开发者指南本章给出一些介绍用以了解TCC的工作方式。如果不关心修改TCC的代码,可以直接跳过。 @waiting ... 8.1 文件读取@waiting ... 8.2 词法(lexer)@waiting ... 8.3 解析器(parser)@waiting ... 8.4 类型@waiting ... 8.5 符号@waiting ... 8.6 段@waiting ... 8.7 代码生成8.7.1 介绍TCC代码生成直接生成连接过的二进制代码。尽管不太常用(查看gcc生成文本汇编代码的例子),但是速度却很快且有些复杂。 TCC代码生成器是基于寄存器的。优化限于表达式级别。表达式的中间表示方式不保存,除非其值在 "value stack" 中。 在x86上,使用了3个临时寄存器。当需要更多寄存器时,一个寄存器被切分到栈上的临时变量中。 8.7.2 数值栈@waiting ... 8.7.3 操作数值栈@waiting ... 8.7.4 CPU相关的代码生成@waiting ... 8.8 优化@waiting ... 9 关于本文档@waiting ... |
|