__asm__ volatile 之 C语言嵌入式汇编

时间:2022-01-25 15:07:42

在linux/include/unistd.h中定义的系统调用嵌入汇编宏函数_syscall0。

其实展开不难发现就是一个函数,只不过用宏定义了。其中关键的是怎么理解 __asm__ volatile这个语法。对于__asm__ volatile这个固定形式,我们暂且不理会它(可参考http://blog.csdn.net/robbie1314/archive/2011/04/17/6329880.aspx),关键看懂()里面的含义。

 

以下面这个为例子先简要介绍一下这个用法。

插入C语言的一个汇编语言代码片段可以分成4部分,以“:”号加以分隔,其一般形式为:

指令部:输出部:输入部:损坏部

 

static __inline__ void atomic_add(int i, atomic_t *v)

{

    __asm_volatile__(

               LOCK "addl %1, %0"

               : "=m" (v->counter)

               : "ir" (i), "m" (v->counter));

}

 

当汇编语言代码嵌入到C代码时,解决操作数和C代码中的变量结合是一个问题。我们无法确切知道gcc在嵌入点的前后会把哪个寄存器分配给哪个变量等,所以gcc采用一种方法:程序员只提供具体的指令,而对寄存器的使用则一般只提供一个“样板”和一些约束条件,而到底如何把变量和操作数结合的问题留给gcc和gas去处理。

 

指令部

数字加上前缀%,如%1、%0等,表示需要使用寄存器的样板操作数。可以使用的此类操作数的总数取决于具体CPU中通用寄存器的数量。

 

输出部

规定对输出变量,即目标操作数如何结合的约束条件。每个这样的条件称为一个“约束条件”。必要时输出部可以有多个约束条件,互相以逗号分隔。每个输出约束以“=”号开头,然后是一个字母表示对操作数类型的说明,然后是关于变量结合的约束。例子中:

: “=m”  (v->counter)

这里只有一个约束条件,“=m”表示相应的目标操作数(指令中的%0)是一个内存单元v->counter。

 

输入部

输入部约束的格式和输出约束相似,但不带“=”号。例子中:

第1个为"ir" (i),表示指令中的%1可以是一个在寄存器中的“直接操作数”(i表示immediate),并且该操作数来自于C代码中的变量名(这里是调用参数)i。

第2个约束条件“m” (v->counter)意义与输入约束相同。

 

损坏部

有些操作中,除了用于输入操作数和输出操作数的寄存器外,还要将若干个寄存器用于计算或者操作的中间结果。这样,这些寄存器原有的内容就损坏了,所以要在损坏部对操作的副作用加以说明,让gcc采取相应的措施。不过,有的时候就直接把这些说明放在输出部了,那也并无不可。

 

操作数的编号从输出部的第一个约束(序号为0)开始,顺序数下来,每个约束计数加1次。在指令部中引用这些操作数或分配用于这些操作数的寄存器时,就在序号前面加上%号。

表示约束条件的字母有很多,主要有:

"m"、"v"和"o"    ---- 表示内存单元

"r"                     ---- 表示任何寄存器

"q"                    ---- 表示寄存器eax,ebx,ecx,edx之一

"i"和"h"             ---- 表示直接操作数

"E"和"F"            ---- 表示浮点数

"g"                    ---- 表示任意

"a","b", "c" "d" ---- 分别表示要使用寄存器eax ebx ecx和edx

"S"和"D"            ---- 分别表示要使用寄存器esi和edi

"I"                     ---- 表示常数(0至31)

 

回到上面的例子,这段代码的作用是将参数i的值加到v->counter上。代码的关键字LOCK表示在执行addl指令时要把系统总线锁住,不让别的CPU打扰,以保证原子性操作。

 

下面就可以照着上面的说明把下面代码看懂吧。

代码:

/* 以下定义系统调用嵌入式汇编宏函数 */

/* 不带参数的系统调用宏函数。type name(void)。
 * %0 -eax(__res), %1 - eax(__NR_##name)。其中name是系统调用的名称
 * 与 __NR_ 组合形成上面的系统调用符号常用,从而用来对系统调用表中函数指针寻址
 * 调用系统中断0x80,返回值->eax(_res), 输入为系统中断调用号__NR_name
 * 返回: 如果返回值大于等于0,则返回该值,否则置出错号errno,并返回-1
 */
#define _syscall0(type, name)/
type name(void)/
{/
    long __res;/
    __asm__ volatile ("int $0x80" /
                      : "=a" (__res) /
                      : "" (__NR_##name));/
    if (__res >= 0)/
    {/
        return (type)__res;/
    }/
    errno = -__res;/
    return -1;/
}

/* 有1个参数的系统调用宏函数。type name(atype a)
 * %0 - eax(__res), %1 - eax(__NR_name), %2 - ebx(a)
 */
#define _syscall1(type, name, atype, a) /
type name(atype a)/
{/
    long __res;/
    __asm__ volatile ("int $0x80"/
                      : "=a" (_res)/
                      : "" (__NR_#name), "b" ((long)(a)));/
    if (__res >= 0)/
    {/
        return (type)__res;/
    }/
    errno = -__res;/
    return -1;/
}

/* 有2个参数的系统调用宏函数。type name(atype a, btype b)
 * %0 - eax(__res), %1 - eax(__NR_name), %2 - ebx(a), %3 - ecx(b)
 */
#define _syscall2(type, name, atype, a, btype, b)/
type name(atype a, btype b)/
{/
    long __res;/
    __asm__ volatile ("int $0x80"/
                      : "=a" (__res)/
                      : "" (__NR_##name), "b" ((long)(a)), "c" ((long)(b)));/
    if (__res >= 0)/
    {/
        return (type)__res;/
    }/
    errno = -__res;/
    return -1/
}

/* 有3个参数的系统调用宏函数。type name(atype a, btype b, ctype c)
 * %0 - eax(__res), %1 - eax(__NR_name), %2 - ebx(a), %3 - ecx(b), %4 - edx(c)
 */
#define _syscall3(type, name, atype, a, btype, b, ctype, c) /
type name(atype a, btype b, ctype c)/
{/
    long __res;/
    __asm__ volatile ("int $0x80"/
                      : "=a" (__res) /
                      : "" (__NR_##name), "b" ((long)(a)), /
                        "c" ((long)(b)), "d" ((long)(c)));/
    if (__res >= 0)/
    {/
        return (type)__res;/
    }/
    errno = -__res;/
    return -1;/
}