最近在读linux0.10内核,正好遇到了内联汇编。
gcc其中一个很强大的功能就是内联汇编。
但是苦于广大学生(包括我)都被intel语法毒害很深,结果没几个会用gcc内联汇编的了。。。
一开始我也看的云里雾里,但是一用gcc -S测试一下,马上就非常明白了。
所以还是实践为王。
这里就稍微从AT&T语法一直介绍到gcc内联汇编运用实例。
AT&T语法。
其实AT&T语法真的不难。。。看了20min大概就懂得差不多了。
推荐直接看gnu as的pdf,总共16页,而且涵盖了不少下面要将的内容。
这里只提个概要。
1.源目操作符顺序
同一条语句intel语法 MOV EAX,EBX 源在后,目在前(我自己其实很分不清源和目,只知道咋用),intel这句就是把EBX的值赋给EAX。
AT&T语法就是mov %ebx,%eax,顺序正好相反,其实个人觉得AT&T这个顺序更加直观。
2.寄存器,立即数
上面其实就已经有了寄存器的例子了,用寄存器的话要用%表示,否则会被解释成对应名称的内存地址
立即数同理,只不过必须用$前缀,不然表示地址。立即数方面,intel一般都是用h后缀表示16进制,而AT&T用更像C的0x前缀表示。
3.寻址
intel语法的变址+基址的时候
[基址寄存器+变址寄存器X比例因子+立即数],eg:[bx+si*4+1],很简单啊。。。
AT&T就比较难懂了
立即数(基址寄存器,变址寄存器,比例因子),eg :$1(%bx,%si,$4)再带上之前说的两个标准,看得确实蛋疼了。。。
4.后缀修饰
intel语法使用 word ptr 之类的命令来指明寻址内存时读取数据的长度。虽然vim C-N上下文补全很方便,但是还是蛮蛋疼的。
AT&T直接使用后缀表示,l=long=32bits,w=word=16bits,b=byte=8bits.比起intel语法方便多了
同样,有明确长度时(例如指明了寄存器),就可以省略后缀修饰。
其余变化应该不大
C函数传递。
这个直接关系到C与汇编的混合编程。
当然,这段写的最好的还是gnu as manual。
这里也主要是几个要点。
1.C函数的参数
C语言是通过堆栈传参的。按照参数表顺序依次将参数压入堆栈,然后call。
2.C函数的frame pointer
用gcc编译的时候,如果不用-fomit-frame-pointer选项的话,默认是会开启frame pointer功能。
此时在函数体一开始一般会有
pushl %ebp
mov %esp,%ebp;这两行等价于enter这一条命令
这两句,这就是建立了frame pointer。
此时esp,ebp都