转载自:Arm汇编学习笔记(三)——GCC内联汇编

首先,C语言中为什么要内联汇编以及其带来的好处这个我就不说了。C语言中使用汇编要通过函数asm(),即__asm__()的别名,两者是一样的。

常见的内联汇编有下面两种形式:

1
2
3
4
5
6
asm(
"mov r0, r0\n\t"
"mov r0, r0\n\t"
"mov r0, r0\n\t"
"mov r0, r0"
);

例1

1
2
3
4
5
6
asm(
"mov %[result], %[value], ror #1"
: [result] "=r" (y)
: [value] "r" (x)
:
);

例2

第一种和一般汇编文件中的汇编程序是一的,这里不多说了。
着重看第二种,为什么要有第二种形式呢?这就和为什么内联汇编有很大关系了,一般都是在一个C的函数中会使用内联汇编,汇代码一般会与C函数的代码有数据交换,也就是说通过汇编代码来操作C代码中的一些变量数据,而C代码中的数据存放在内存还是寄存器,或者存放在哪个寄存器我们在写C代码的时候并不知道,所以不可能使用第一种内联汇编形式,那就必须要用第二种形式了。

第二种汇编格式定义:

1
2
3
4
5
6
asm(
code /*汇编指令*/
: output operand list /*输出操作数列表*/
: input operand list /*输入操作数列表*/
: clobber list /*被改变资源列表*/
);

可以内联汇编通过冒号将内容分成了四个部分,内联汇编和C操作数之前的关联性体现在上面的input和out操作数上。下面我们针对例2进行分析:

  • 汇编指令
    mov %[result], %[value], ror #1

  • 输出操作数列表,可选,每个输出数的符号名用方括号包围,后面跟一个约束串,然后再加上一个括号包围的C表达式,这个括号里的符号就是C语言代码中的变量。

[result] "=r" (y) /*result:符号名 "=r":约束串* (y):C表达式*/

  • 输入操作数列表,可选,语法上与输出操作数列表一样。括号中的x是C语言代码中的变量。
    [value] "r" (x)

  • 被改变资源列表,这里是空的,它主要是 告诉编译器哪些资源发生了改变需要去更新

那么上面的”r”和”=r”是什么意思呢?

operand

操作符 含义
r 通用寄存器 r0-r15
m 有效的内存地址
I 数据处理中的立即数
X 只能 用于输出操作数列表
修饰符 说明
操作符只读
= 操作符只写
+ 操作符 可读可写
& 只能用于 修饰输出操作数列表的操作符

Demo

如果你曾经读过一些别人写的内联汇编代码就会发现与我们上面写的略有不同,是下面这种形式的:

1
2
3
4
5
6
asm(
"mov %0, %1, ror #1"
: "=r" (result)
: "r" (value)
:
);

例3

实际上是同一种形式,只不过例2的那种形式是从GCC3.1版本开始才支持的,而再次之前一直是例3的形式。我之前对”%0” “%1”一直很迷惑,操作数用一个带百分号的数字来表示,上述0%和1%分别表示第一个、第二个操作数。GCC的最新版本仍然支持上述语法,但明显,上述语法更容易出错,且难以维护:假设你写一个较长的内联汇编,然后需要在某个位置插入一个新的输出操作数,此时,之后的操作数都需要重新编号。

另外还有一点就是,你会经常看见asm volatile()这样的内联汇编,在asm后面会跟上一个volatile关键字,这是因为编译器会去优化你的代码,不同的C编译器优化的方式还不一样,而这些优化有时候会适得其反,或者运行的过程和你希望的不一样,甚至将你的汇编代码给优化掉。针对这一问题的解决方法是增加volatile属性,这一属性告诉编译器不要对本代码段进行优化。

Demo2

https://github.com/Fuzion24/AndroidHostileEnvironmentDetection/blob/master/app/jni/emudetect.c

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
36
37
#ifdef __amd64__
__asm__ __volatile__ ( "mov %0, %%rbx;"
"movl $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);" "add $1, (%%rbx);"
"add $1, (%%rbx);"
:
:"c"(&global_value)
);
#endif
#ifdef __arm__
__asm__ __volatile__ ("mov r0, %[global];"
"mov r1, #1;"
"add r1, r1, #1;" "str r1, [r0];"
"add r1, r1, #1;" "str r1, [r0];"
"add r1, r1, #1;" "str r1, [r0];"
:
:[global] "r" (&global_value)
);
#endif
#ifdef __i386__
__asm__ __volatile__ (
"movl %0, %%ebx;"
"movl $1, (%%ebx);"
"add $1, (%%ebx);"
"add $1, (%%ebx);"
:
:"c"(&global_value)
);
#endif