C语言中嵌入汇编程序

背景

阅读linux0.11源码的kernel/traps.c文件里包含了几个语句是内嵌的汇编程序,虽知道整体的意思,但是细化后就不明所以然,这里记录下。

C中嵌入汇编程序

格式

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

带冒号的行可以省略,“输出寄存器”表示汇编执行完后,存放输出数据的寄存器,“输入寄存器”开始执行代码的时候,指定指定寄存器存放值。

实例代码

示例1

1
2
3
4
5
6
7
int a=10,b;
asm("movl %1, %%eax;
movl %%eax, %0;"
:"=r"(b)
:"r"(a)
:"%eax"
);

表示C语言里的“b=a;”。
“r”表示使用任意寄存器,%0、%1表示使用两个寄存器,一般只能%0~%9共十个操作数,按输入输出寄存器出现顺序进行映射。
寄存器用两个百分号,是因为使用了%0%1这些数字使百分号有了特殊意义,所以在操作数出现的寄存器必须用双百分表示。
会被修改的寄存器里边的%eax表示eax寄存器在汇编代码块执行过程中会被改写,在执行前要保护好,这是提交给编译器决定的。

示例2

1
2
3
4
5
6
7
8
9
#define get_seg_byte(seg,addr) ({ \
register char __res; \
__asm__("push %%fs; \ // 将fs寄存器值压栈
mov %%ax,%%fs; \ // 将eax中的段值赋值给fs寄存器
movb %%fs:%2,%%al; \ // 将fs:(*(addr)) --> al
pop %%fs" \ // 弹出fs
:"=a" (__res) \ // 将eax值 --> __res
:"0" (seg),"m" (*(addr))); \
__res;})

表示取段seg中地址addr处的一个字符。
上面是kernel/traps.c中的一段代码,其中:

参数:seg - 段选择符;addr - 段内指定地址。
输出:%0 - eax(__res);输入:%1 - eax(seg);%2 - 内存地址(*(addr))。
“=a”中的’a’称为加载代码,’=’表示这是输出寄存器,执行完后,eax的值将赋给_res,’”0” (seg)’表示在这段代码开始运行时将seg放到eax寄存器中,”0”(0可以省略)表示这里使用的寄存器和上面输出寄存器一样。

编译器规定:标号从输出寄存器的最左边开始,被编号为0,往右和往下将被依次编号。所以这里的”0”表示eax,”1”表示seg,”2”表示*(addr)。

%%fs:%2是使用编号为2的变量(*(addr)),这里加上%%fs的意思??

示例3

1
2
3
4
5
6
7
8
9
10
11
12
13
// 取段seg中地址addr处的一个字长(4字节)。
// 参数:seg - 段选择符;addr - 段内指定地址。
// 输出:%0 - eax(__res);输入:%1 - eax(seg);%2 - 内存地址(*(addr))。

#define get_seg_long(seg,addr) ({ \
register unsigned long __res; \
__asm__("push %%fs; \
mov %%ax,%%fs; \
movl %%fs:%2,%%eax; \
pop %%fs" \
:"=a" (__res) \
:"0" (seg),"m" (*(addr))); \
__res;})

示例4

1
2
3
4
5
6
7
8
9
10
// 取fs段寄存器的值(选择符)。
// 输出:%0 - eax(__res)

#define _fs() ({ \
register unsigned short __res; \
__asm__("mov %%fs,%%ax" \
:"=a" (__res) \
: // 没有输入寄存器
); \
__res;})
Brick wechat
扫一扫,用手机看更方便(^ ◕ᴥ◕ ^)