基于Tiny C compiler的c脚本混合编程方法

章博耘
2023-12-01

基于Tiny C compiler的c脚本混合编程方法

  1831人阅读  评论(0)  收藏  举报
 
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) 生成DLL
gcc -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.c
2) 生成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语言毕竟是大多数人熟悉的语言,使用起来很方便。
 类似资料: