TCC
簡要介紹:TCC 最有趣的特性是可以用 UNIX 系統上常見的 #!/usr/bin/tcc 的方式來執行 ANSI C 語言寫就的來源程式,省略掉了在命令列上進行編譯和連結的步驟,而可以直接運行 C 語言寫就的來源程式。這樣就能做到像任何一種其它的指令碼語言比如 Perl 或者是 Python 一樣,顯著的加快開發步調。可以像編寫 Shell 指令碼一樣的使用 C 語言,隨便想一想都覺得是一件奇妙的事情。但是 TCC 還有一些其它的特性呢!
- TCC 的體積非常小,全部原始碼打包壓縮以後不到 200 K 位元組大小,編譯後的 tcc 可執行程式不過 80 K 位元組大小。這意味著我們幾乎可以在任何場合使用 TCC 提供給我們的編寫 C 語言指令碼的能力。這其中當然包括硬碟空間十分緊張的環境,比如嵌入式系統和啟動磁碟片等等。
- 在給 TCC 的來源程式中可以使用任何在給 GCC 的來源程式中可以使用的動態連結程式庫。TCC 不僅支援標準的 ANSI C 語言,而且也支援 ISO C99 標準和一部分來自於 GCC 的對 C 語言所做的擴充。
- TCC 直接產生經過部分最佳化的 X86 機器代碼。並不需要產生任何虛擬機器的二進位代碼。據 TCC 作者提供的資料,TCC 的編譯速度比 GNU C 編譯器在不做任何代碼最佳化工作(gcc -O0)的時候都要快。當然啦,要是讓 GCC 做代碼最佳化的話,那麼編譯速度就更加比不上 TCC 嘍。
- Libtcc庫能夠讓使用者將TCC作為作為動態代碼產生的後端,在libtcc.h中有API的說明,libtcc_test.c是使用libtcc的簡單例子。Libtcc包含了這樣一種思想,使用者可以將程式包含在字串中,然後直接編譯,接下來就可以存取全域符號(函數和變數)。
產生LIBTCC:
由於libtcc是基於linux的,為了能夠產生window下的動態串連庫,我們先要使用mingw/msys編譯,然後用下面的語句產生dll。為了方便msvc使用,還需要再產生lib庫用於連結。整個步驟如下:1) 產生DLLgcc -O2 -shared -Wall -Wl,--export-all-symbols -mpreferred-stack-boundary=2 -march=i386 -falign-functions=0 -fno-strict-aliasing -DTCC_TARGET_PE -DLIBTCC -o libtcc.dll tcc.c2) 產生def
pexports libtcc.dll | sed "s/^_//" > libtcc.def 3) 從def產生lib
lib /machine:i386 /def:libtcc.def
由於c語言編程需要標頭檔和庫,我們也需要在發布程式的時候帶上include目錄和lib目錄,然後在主程式中指定該路徑。
TCC
和主程式的配合:
在我們的程式中,主要把固定的邏輯放在主程式中,而將變化的東西放在tcc指令碼中。該程式的基本流程如下: 按照使用者的輸入參數開啟截包,按照指定的類型將每一個資料報合適的數組片斷傳給使用者寫的tcc程式,由tcc程式將處理後的資料傳給主函數,由主函數進一步處理,比如寫入檔案中。注意到我們需要處理的可能是rtp包,也可能是沒有rtp頭的包,在輸出的時候,有些碼流也需要加入額外的處理,比如h261和h263需要調用主程式中的函數進行sbit,ebit的拼接;比如h263+的簡單處理,比如h264的流頭的添加,比如音訊直接輸出。所以我們在主程式和tcc程式間不光要傳遞變數,還需要傳遞函數。下面是大概的代碼架構和解釋。
主程式:TCCState *s;建立新的tcc狀態 s = tcc_new(); if (!s) { fprintf(stderr, "Could not create tcc state/n"); exit(1); } 添加路徑: tcc_add_include_path(s,fullinc); tcc_add_library_path(s,fulllib); 設定輸出方式為記憶體: tcc_set_output_type(s, TCC_OUTPUT_MEMORY); 編譯一個檔案到tcc中(其實是讀檔案到緩衝區,然後調用了tcc_compile_string) tcc_compile_file(s,fullpath); 將變數和函數符號加入到tcc環境中去,這樣在tcc中就可以直接使用這些符號 tcc_add_symbol(s, "encodeflag", (unsigned long)&encodeflag); tcc_add_symbol(s,"f",(unsigned long )&f); tcc_add_symbol(s,"Enter_H263Decode",(unsigned long)&Enter_H263Decode); tcc_add_symbol(s,"writeFile",(unsigned long)&writeFile); 做所有的重定位工作,在tcc_get_symbol之前必須調用 tcc_relocate(s); 擷取tcc中的函數和變數符號 tcc_get_symbol(s, &val, "Process"); Process = (pf_Process)val; tcc_get_symbol(s,&val,"withRtp"); withRtp=(int)(*(int *)val); tcc_get_symbol(s,&val,"offset"); offset=(int)(*(int *)val); 迴圈處理每一個資料包 while((res = pcap_next_ex( fp, &header, &pkt_data)) >= 0){ 。。。調用tcc中的處理函數 Process(&pkt_data[offset],totallen);} 清理tcc環境tcc_delete(s);
TCC
程式#include <stdio.h>#include <stdlib.h> int withRtp=1;int offset=54;unsigned char zero[2]={0,0};unsigned char h264head[4]={0,0,0,1}; extern FILE *f;extern unsigned char encodeflag;extern int writeFile(unsigned char *buf,int len); int Process(unsigned char *pt,int len){ if(encodeflag==0 ||encodeflag==1) {/*h261或者h263*/ if((pt[0])&0x7==0) { Enter_H263Decode(len,pt,1); } else { Enter_H263Decode(len,pt,0); } } else if(encodeflag==2) {/*h263+*/ if((pt[0]==0x04) && (pt[1]==0x00)) { writeFile(zero,2); writeFile(&pt[2],len-2); } else if((pt[0]==0x00) && (pt[1]==0x00)) { writeFile(&pt[2],len-2); } else { printf("not correct 263+ format/n"); } } else if(encodeflag==3) {/*raw data*/ fwrite(pt,1,len,f); } else if(encodeflag==4) {/*mp3*/ if(pt[0]==0xff && pt[1]==0xfb) { writeFile(&pt[4],len-4); } else { printf("not a mp3 header,%02X %02X/n",pt[0],pt[1]); } } else if(encodeflag==5) {/*h264*/ writeFile(h264head,4); writeFile(pt,len); } else { ; } return 0;} 從上面的例子可以看出,使用tcc,可以很容易的處理數組,畢竟,採用其他指令碼語言的話,比如lua,perl等,要想處理數組,要想容易的進行位元運算,還是很麻煩的,而且,c語言畢竟是大多數人熟悉的語言,使用起來很方便。