x86下C调用风格

本文说的x86指的是32位下的x86,不过据说x86-64也是类似的。

每一种机器本来指令集就不一样,所以本身在不同机器上程序的调用风格就不完全一样。但即使在同一种机器上,如我们常用的x86,不同的高级语言它的调用风格也不一样。比如有些语言可能调用函数前,把状态寄存器压栈,或者把所有寄存器压栈,调用完之后恢复,但有些语言则不会这样做。因此,通常不同高级语言编译出来的目标文件(object file),是不能直接链接的。这种汇编级别的接口,我们有一个词,叫ABI(Application Binary Interface,应用程序二进制接口)。

本文根据《Programming Ground Up》,把C语言在x86下的调用风格作一个小结。

首先是调用者,调用者负责把函数的参数倒序压栈。比如调用者要调用函数func,它要求有N个参数,则调用者在调用函数前把参数按参数N,参数N-1…这样的顺序去压栈。这时程序的堆栈如图1所示。

Picture 1
Picture 1

然后调用者可以调用函数。被调用者则要做以下的工作,首先把%ebp寄存器压栈,然后把%esp拷贝到%ebp。这样以后,函数所有的参数和函数的局部变量,都可以通过%ebp的基址寻址方式去获得。这时程序的堆栈如图2和图3所示。

Picture 2
Picture 2
Picture 3
Picture 3

函数执行完之后,结束之前,也是很关键的一步,就是把%esp和%ebp恢复,同时把返回值放到%eax寄存器。最后调用者负责清理自己压栈的参数。

可见,C语言的调用风格是,%ebp肯定不会改变,但其他寄存器则不保证,所以如果调用函数前正在使用某个寄存器,应该先把它压栈,函数结束后才恢复该寄存器。

另外,Linux的shell在执行程序时,也是遵循同样的风格的。所以程序一开始的时候,0(%esp)就是C语言中的argc,4(%esp)就是C语言中的argv[0],以此类推。

谷歌中国牛年新春壁纸

今天在cnBeta看到报道,谷歌中国推出了牛年新春壁纸,于是正好把我Ubuntu的桌面换上新年的新装。要下载牛年新春系列的壁纸,只要访问谷歌中国的个性化主页即可。

这次一共推出了8款壁纸,而且还每一款都有名堂,确实是别具新意的。下面的截图是一款叫“金牛献宝”的壁纸,不过我自己选择使用的却是“新年到,鞭炮到”,呵呵,其实每一款都挺漂亮的。另外几款的名堂分别是“年年有余”、“团圆饭”、“大拜年”、“迎春接福”,“张灯结彩”以及“拜天公”。

Google China 09 Newyear
Google China 09 Newyear

在除夕之夜,周围不少住户在放鞭炮,寒冷的夜晚还是有不错的新年气氛,这一晚送旧迎新,首先要感谢女朋友半年以来无怨无悔的支持,同时小弟在此祝愿各位朋友在牛年事业顺利,身体健康。

汇编书籍PGUBook

《Programming Ground Up》(下面简称PGUBook)是一本使用GFDL协议发布的书。我是从TualatriX的博客上看到的关于这本书的介绍的。

PGUBook从汇编开始讲编程,但是绝不仅仅在于汇编,而是重于编程思想。书的前面大部分都是围绕汇编在讲,后面则有一部分讲C语言和其他。本来我以为这本书讲汇编一定不会勾起我的兴趣的,不料粗略浏览一看,发现PGUBook讲的比我大二的时候学的汇编要深刻很多。这本书只讨论x86和Linux,一方面使我对x86的理解深刻了不少,另外也使我有充分的环境去实现书上的例子。书的封面截图在下面,更详细的介绍可以看它的官方主页http://savannah.nongnu.org/projects/pgubook/

PGUBook
PGUBook

我看了PGUBook半个多月来,到今天整整看了五章了,感觉是越看越想看。主要原因是正如它第一章里所说的,没有把一些其他书回避的晦涩的地方一带而过,而是用比较好的章节结构使读者逐步去掌握一些x86的设计理念。而且看了PGUBook之后,对C语言的很多行为的理解有了很大提高,比如说调用函数时怎样压栈,Linux下gcc是倒序把参数压栈,所以函数中的相关参数怎样影响就可以很好理解了,比如f(a++, a++)之类的。当然其他编译器处理手段会一样,而且也不符合C语言规范,我只是想用来说明有助于理解。

接下来几周陆续写上读书笔记和小结,这次先把x86的简要架构写上。

32位的x86有6个通用寄存器和4个特殊寄存器,以前学的时候感受没这么深刻,现在倒是很容易就记下来。6个通用寄存器分别是:%eax(累加寄存器),%ebx(基本),%ecx(计数),%edx(数据),%esi(源索引),%edi(目的索引)。不过其实对于32位的x86来说,其实它们都是通用的,可以代替对方,那些括号里的含义在16位的时代才比较有意义。另外四个特殊寄存器是:%esp,栈寄存器,指向目前的栈顶;%ebp,基址寄存器,常和%edi组合使用;%eip,指向下一条指令的地址;%eflags,标志寄存器,对比较指令特有用。

寒假过一半前希望能把PGUBook看完一次。

ChmSee看不到部分图片

ChmSee是一个在Linux下看chm文件的软件,我以前在Linux下是使用一个FireFox的插件去看的,不过这次我想装ChmSee试试。

因为chm文件实际上只是一些网页的打包,所以处理的程序应该也不会很复杂。我用ChmSee看大部分chm文件都没有问题。直到最近,用ChmSee看一个chm文件,但是里面的图片总是显示不出来,就像下图所示。我一开始以为是打包的时候出了问题才会导致这样,但是当我换回到Windows的环境之下,图片又能看到了。这就让我感到很困惑了,为什么ChmSee看这么多文件没问题,偏偏这个文件却不行呢?最后,上网查到一篇博客文章写道,原来ChmSee看不到那些图片的原因是,那些文件有一些奇怪的后缀,比如abc.gif;65123之类的,所以ChmSee才无法解释。不过至于为什么有这种后缀,我不知道,我猜测可能是一些字符在URL中变成这样。

ChmSee cant see come pictures
ChmSee can't see come pictures

最近用Linux的一大改进就是发现原来Ubuntu下字体渲染可以选择“适合LCD”,这样我的笔记本上的画质马上得到质的飞跃。只可惜我的一本电子书没法用ChmSee看了。半年了,回到自己的博客。