关闭

Linux C内联汇编用法

4374人阅读 评论(0) 收藏 举报
分类:
  Linux内核中有很多c中使用汇编的情况,比如原子操作。内联汇编通常用下面的格式:

asm volatile("Instruction List" : Output : Input : Clobber/Modify);

        当然,或者写作如下格式(Output、Input、Clobber/Modify都是可选的),也就是三个冒号,4个部分:

asm volatile("Instruction List"

                                    : Output

                                    : Input

                                    : Clobber/Modify);

        为了理解方便,以屏蔽本地irq相关函数的代码为例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
static inline unsigned long native_save_fl(void)
{
    unsigned long flags;
  
    /*
     * "=rm" is safe here, because "pop" adjusts the stack before
     * it evaluates its effective address -- this is part of the
     * documented behavior of the "pop" instruction.
     */
    asm volatile("# __raw_save_flags\n\t"
             "pushf ; pop %0"
             : "=rm" (flags)
             : /* no input */
             : "memory");
  
    return flags;
}
  
static inline void native_restore_fl(unsigned long flags)
{
    asm volatile("push %0 ; popf"
             : /* no output */
             :"g" (flags)
             :"memory", "cc");
}
  
static inline void native_irq_disable(void)
{
    asm volatile("cli": : :"memory");
}
  
static inline void native_irq_enable(void)
{
    asm volatile("sti": : :"memory");
}

        本篇blog耗时2小时。

 

1、asm和volatile

        volatile是c的关键字,是个修饰符,用于修饰变量或者函数,意思是告诉编译器,该变量或者函数是“易变”的,编译器会在优化时做相关的考虑。驱动中常用于修饰急促器变量。Linux下c中的使用内联汇编,就是这样,需要asm()的格式,可以没有volitile,但不能没有asm。

        关于asm到底是什么?首先,它不是ANSI C关键字,c89/c99标准中没有asm关键字;asm属于“GNU C Language Extensions”,像typeof,inline等都是这种情况。

2、Instruction List

        指令序列,就是指要执行的指令汇编指令,多条指令之间使用 分隔符“;”、“\n\t”等进行联合。

        对应例子中,就是

  • "pushf;pop %0"
  • “push %0;popf”
  • "cli"
  • "sti"

        含义分别为:

  • 将EFLAGS寄存器的值压栈,再出栈给的1个变量(先这么叫吧)
  • 将第1个变量压栈,再出栈给EFLAGS寄存器
  • 禁止本地中断
  • 开启本地中断

3、Output

        指令序列执行的结果的输出,指指令执行的结果要输出到哪。Output和Input部分,通常使用下列格式:

“constraint”(variable)

        constraint为限制的意思,翻译好听点就是“修饰”,用于限制variable的。

        在例子中,只有navtive_save_fl函数有“=rm”(flags),这句还不好臆测,还得查资料(参考资料5):

  • =,表示只写,也是output必须具备的(看见=,就是output)
  • r表示寄存器
  • m表示内存
  • flags为变量名

        联合起来表示将flags变量以内存或者寄存器的方式进行操作,flags是输出。

        关于constraint:

  • a,b,c,d,S,D 分别代表 eax,ebx,ecx,edx,esi,edi 寄存器
  • r 上面的寄存器的任意一个(谁闲着就用谁)
  • m 内存
  • i 立即数(常量,只用于输入操作数)
  • g 寄存器、内存、立即数 都行(gcc你看着办)

4、Input

       Input和Output很像,就是没有“=”。Input为指令序列提供输入,在例子中,只有native_restore_fl函数有“g”(flags),g表示“Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.”,表示flags可以使用寄存器、内存、立即数等方式。

        还有个问题,就是Input和Output如何与Instruction List相关联的?Instruction List中的%0,前面叫他“第一个变量”,实际上是不准确的,实际上应该叫做input/output operand,输入/输出操作数,从Output开始,0表示第一个,1就表示第二个,以此类推。从gcc3.1开始,支持直接将%0写作%[input]、%[output]。

       input和Output可以有多个变量:中间用“,”隔开。

5、Clobber/Modify

       该部分表示哪些寄存器、内存被修改,但是又没有出现在Input/Output中。通常看到的就是memory,表示汇编语句可能修改了内存,如果有变量缓存在寄存器中,需要重新读取该变量。

 

参考资料:

1、Linux 中 x86 的内联汇编

2、__asm__ __volatile__内嵌汇编用法简述

3、第 19 章 汇编与C之间的关系

4、AT&T inline Assembly Constraint

5、Assembler Instructions with C Expression Operands

6、内联汇编

7、GCC-Inline-Assembly-HOWTO

Published in and tagged , on 2013年11月24日 by
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:70331次
    • 积分:1814
    • 等级:
    • 排名:千里之外
    • 原创:81篇
    • 转载:8篇
    • 译文:0篇
    • 评论:39条
    最新评论