偶尔想看一下memcpy的源码,发现在linux里它是用内联汇编写的,所以就学了下,以前看linux源码时也曾碰到过很多内联汇编,但一直没有总结它的语法和使用,现在分析下,如有什么问题,欢迎留言讨论:-)
它的基本格式是: asm("assembly code");
1、汇编指令必须在括号的引号里
2、如果有多条汇编指令,则需要用换号符来分离,当然为了在生成汇编的格式,习惯上加上制表符
而基本的内联汇编可以使用C语言中的全局变量,记住:全局
下面举个简单的例子:
下面是它的扩展形式,基本形式有很多局限性,比如我们只能通过全局变量来使用,因为局部变量的通过栈空间来分配的,而访问这些局部变量需要用到%ebp寄存器来间接寻址,不能直接因为它的符号,全局变量则是放在数据段,所以可以通过因为它的地址(标号表示)直接访问。
它的扩展形式: asm("assembly code" : output location : input location : changed register);
在扩展格式中,可以通过寄存器和内存位置为输入值和输出值赋值,它的赋值形式是:
“constraint”(variable)
其中variable是C语言中定义的变量,这里既可以是局部变量也可以是全局变量,constraint则定义了变量的输入输出的定位,即把变量放在哪里(输入值),它表示从外部变量movl到寄存器 的操作;从哪里得到变量(输出值),它表示从寄存器movl到变量 的操作。
这里有一些约束是定义把变量放在寄存器中还是内存中:
约束 描述
a 使用%eax,或%ax
b 使用%ebx,或%bx
c 使用 %ecx,或%cx
d 使用 %edx或%dx
S 使用%esi 或 %si
D 使用%edi 或%di
r 使用任意可用的通用寄存器
f 使用浮点寄存器
m 使用变量的内存地址
o 使用偏移的内存地址
i 使用立即整数值
g 使用任何可用的寄存器或内存地址
另外对与输出值,也有一些约束:
+ 可用读取或写入操作数
= 只能写入操作数
& 在内联函数完成之前,可以删除或重新使用操作数
下面是那个例子:
使用占位符:
在内联汇编中,如果输入值很多,则就需要很多寄存器来存放,这有些繁琐,所以引入了占位符的概念,它是用来引用输入值和输出值,占位符前面加上百分号的数字,这也是为什么在上面的内联汇编中要使用两个百分号。
例如:
下面分析一下memcpy的内联汇编形式,当然下次我们分析一下在BSD中的源码
首先输出操作数分别对应于%0-%2,输入操作数则分别对应%3-%6
然后第一条指令是rep,表示下一条指令要重复执行,且执行的次数由%ecx决定,即n/4,然后测试%4低位字节(用b表示)第二位是否为1,如果为1,则表示至少还有两个字节的数据,进行一个字的mov,然后测试最后一位是否为1,如果为1,则就剩下最后一个字节。