嵌入汇编示例讲解

嵌入汇编介绍

参考:《Linux内核完全注释:基于0.11内核》
该书下载地址:http://www.oldlinux.org/download/clk011c-3.0.pdf

底层的C语言程序经常用到嵌入汇编(也称内联汇编)语句。经常写高级语言的同学应该很少接触到,一旦碰到两眼一抹黑。这里列举一个例子简单介绍下。如果有精力推荐Linux内核完全注释系统地学习。

具有输入和输出的嵌入汇编语句基本格式如下:

asm("汇编语句"
    :输出寄存器
    :输入寄存器
    :会被修改的寄存器);

除了第一行,后面带冒号的行不适用就可以忽略(“:”需要保留,否则无法区分输出寄存器、输入寄存器还是会被修改的寄存器)。

“汇编语句”是一系列汇编指令,用于实现某个功能。“输出寄存器”指定哪些寄存器用于存放输出数据。“输入寄存器”指定那些寄存器的数据作为汇编语句的输入。“会被修改的寄存器”包括汇编语句中改动的寄存器,gcc编译器不能依赖这些寄存器里的值。如果非要用,那么gcc编译器只能重新加载。下面用一个例子来讲解:

#define get_seg_type(seg, addr)    \
({                                 \
register char __res;               \  // 定义了一个寄存器变量__res
__asm__("push %%fs;                \  // 首先保存fs寄存器原值(段选择符)。push是入栈,需要的时候再弹出来,所以是保存
         mov %%ax,%%fs;           \  // 然后用seg设置fs
         movb %%fs:%2,%%al;        \  // 取seg:addr处1字节内容到al寄存器中(movb、movw、movl、movq分别表示1、2、4、8字节的复制)
         pop %%fs"                 \  // 恢复fs寄存器原内容
         :"=a"(__res)              \  // 输出寄存器列表
         :"0"(seg),"m"(*addr)));   \  // 输入寄存器列表
__res;})

这段10行代码定义了一个嵌入汇编语言宏函数。用圆括号括住的组合语句(花括号中的语句):“({})”可以作为表达式使用,其中最后一行上的变量__res(第10行)就是该表达式的输出值。

第3行定义了一个寄存器变量__res。该变量被保存在一个寄存器中,方便快速访问和操作。如果想指定寄存器(例如eax),那么可以把该句写成“register char __res asm (“ax”);”。

第4行上的“__asm__”表示嵌入汇编语句的开始。第4行到第7行的4条语句是AT&T格式的汇编语句(还有一种流行的是因特尔的,二元操作数位置正好相反)。

为了让gcc编译产生的汇编语言程序中寄存器名称前有一个百分号“%”,在嵌入汇编语句寄存器名称前就必须有两个百分号“%%”。

第8行即是输出寄存器,这句话的含义是在这段代码运行结束后将eax所代表的寄存器的值放入__res变量中,作为本函数的输出值,“=a”中的“a”称为加载代码,“=”表示这是输出寄存器,并且其中的值将被输出值替代。

第9行表示在这段代码运行开始时将seg放到eax寄存器中,“0”表示使用与上面同个位置的输出相同的寄存器。而(*(addr))表示是一个内存偏移地址值。为了使上面汇编语句中使用该地址值,嵌入汇编程序规定把输出和输入寄存器统一按顺序编号,顺序是从输出寄存器序列从左到右、从上到下,以“%0”开始,分别记为%0、%1、%2…%9。因此输出寄存器的编号是%0(这里只有一个输出寄存器),输入寄存器前一部分(“0”(seg))的编号是%1,而后部分的编号是%2。上面第6行的**%2**即代表(*(addr))这个内存偏移量。

此外第8、9行出现的“a”表示寄存器。“m”表示内存,从该内存地址取处的值作为第6行%2的值。
嵌入汇编示例讲解_第1张图片
在这里插入图片描述
现在研究下4-7行代码的作用。
“push %%fs;”——将fs段寄存器的值入栈;
“mov %%ax,%%fs";”——将eax中的段值赋给fs段寄存器;
“movb %%fs:%2,%%al;”——把fs:(*(addr))所指定的字节放入al寄存器中。这涉及到寻址方式的知识
“pop %%fs" ——恢复fs段寄存器的值

看到这里就应该明白这段代码的作用:从“%fs:(*addr)”这个地址取处1字节的值并返回

(结果不重要,重要的是过程)


其他细节:
1、“__asm__”也可以替换成“asm” ;
2、很多代码中“__asm__”旁边还有“__volatile__”或者“volatile”。这里的volatile告诉gcc编译器不要进行指令优化。指令优化(指令重排序)可以提高执行效率,但是可能会导致程序执行不正确。了解这个有利于高级语言中正确使用volatile;
3、注意:反斜杠’'不是汇编必须的,这里只是因为宏语句必须定义在一行。在C语言中内嵌汇编并不需要用它。

你可能感兴趣的:(汇编)