恶斗TCC(Tiny C Compiler)

 

    TCC,全称Tiny C Compiler(http://bellard.org/tcc/),是一个颇具特色的C编译器,你能把它当作一个C语言解释器来用,也可以嵌入你自己的应用程序作一个动态代码生成器。是的,我们就是这么干的。在我们的项目中,粒子系统的运动规则用C语言来描述,然后由TCC动态生成native code运行。这么做既不失效率又保持了较高的动态能力。


    但是,既然是使用第三方库,那就要准备好享受成果的同时吞下bug。这一次,我们吃到的可是一只非常揪心的虫子。


    众所周知,X86 CPU的浮点计算单元(FPU)共有8个浮点数寄存器,它们是按照栈的形式组织的。如果load浮点数进了一个寄存器,那么它就属于被占用的,需要用类似pop的操作把它释放掉后才能重新使用。


    对于TCC来说,一个函数如果使用了浮点运算,那么它生成的代码在函数返回的时候会在FPU栈上留下一个垃圾(为什么?

这是后话,也是本文的主旨。),这样8个寄存器就只剩下7个可以用了。如果你的程序全部用TCC编译这没什么问题,但是和gcc或者msvc混合使用的话就有问题了。因为这些编译器一直认为在刚刚进入任何一个函数的时候都会有8个浮点寄存器可用,而如果打开了优化开关的话,它们就有可能生成一些很牛B的代码,一下子把8个寄存器全都用满。这就糟糕了,只有7个茅坑(另外一个TCC占着不拉屎),一下子要蹲8个,于是就触发了FPU内部的“茅坑使用异常”(学名叫FPU invalid operation

exception:#IE)。关键是这个鸟异常一般情况下是被FPU罩(mask)着的,我们根本不知道,以为天下太平,但是从浮点寄存器

上取回的值那就错得象一坨屎一样了。


    这个bug折磨了我们好几天,同事云风已经说过这事情了。接下来我要带大家掘地三尺,找准位置,痛下杀脚,踩死这只臭虫(看丫还蹦达)。


    我们的分析标本是tcc-0.9.25,也是目前最新的官方发行版,源码这里下:http://download.savannah.nongnu.org/releases/tinycc/tcc-0.9.25.tar.bz2。


    TCC通过`fstp %st(1)'指令在FPU栈上留垃圾。st是FPU stack的简写,st(n)指的是浮点栈的第n号寄存器,栈顶到栈底依次按0、1、2...进行编号。这句指令的意思是不管st(1)有没有被占用,都把st(0)的内容拷贝到st(1),然后释放st(0)即栈顶,原来的st(1)成为新的栈顶。该指令结束后,FPU栈顶一定是被占用的。


    有两个地方会生成`fstp %st(1)'指令(二进制编码是0xd9dd):tccgen.c的689行(vpop函数内);tccgen.c的210行(save_reg函数内)。我们首先把它们都改为生成`fstp %st(0)'指令(二进制编码是0xd8dd)。`fstp %st(0)'的意思其实就是弹出FPU栈顶寄存器的内容,使st(0)成为未占用状态,不做任何多余的事。本来vpop和save_reg这两个函数就是为了释放寄存器才生成相关指令的,这么一改就合乎函数的原本意图了。


    那是不是这样就万事大吉了呢?显然是不够的,如果真这么简单我用得着写这篇文章吗?让我们试着用修改过的TCC编译下面的函数:


void foo()

{

  double var = 2.7;

  var++;

}


它会生成这样的机器码:


.text:08000000                 public foo

.text:08000000 foo             proc near

.text:08000000

  • 6
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

soloist

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值