(目录)
一、基本内联汇编
asm格式
asm("assembly code");
要求:
- 汇编指令必须在双引号里。
- 指令超过一条必须使用换行符\n\t(或者“;”)——换行的每一条汇编指令都必须位于双引号中。
asm( "movl $1, %%eax\n\t"
"movl $0, %%ebx\n\t"
"int $0x80");
只有c中的全局变量才能在基本内联汇编中使用。
int a = 10;
int b = 20;
int main(){
asm( "movl a, %%eax\n\t"
"movl b, %%ebx\n\t");
}
不能在asm语句中使用局部变量。
基址变址寻址表达式 movl -4(, %edi, 4), %eax 或者 movl 4(%edi), %eax base_addr(offset_addr, index, size) 相当于 base_addr + offset_addr + index * size
基本汇编结构
// if-then
if:
<condition to evaluate>
jxx else
<code to implement the "then" statements>
jmp end
else:
<code to implement the "else" statements>
end:
// for
for:
<condition to evaluate for loop counter value>
jxx forcode
jmp end
forcode:
<for loop code to execute>
<increment for loop counter>
jmp for
end:
// loop
<code fefore the loop>
movl $10, %ecx
loop1:
<code to loop through>
loop loop1
<code after the loop>
二、使用volatile([ˈvɑːlətl])修饰符
把volatile修饰符放在asm语句中表示不希望编译器优化这个汇编代码段。
asm volatile ("assembly code");
如果使用ANSI C约定,需使用__asm__替换asm关键字。
__asm__ __volatile__ ("assembly code");
在基本内联汇编中应该不去改变任何寄存器的值。
三、扩展asm格式
asm("assembly code"
: output locations
: input operands
: changed registers
);
冒号分割的后三个部分不是都必须出现。“output locations”输出为空的部分要保留冒号分隔符,最后一个部分“changed registers”为空,相应的冒号分隔符可以省略。
“changed registers” 部分一般不使用(如果使用可能反而会报错——编译器假设输入和输出使用的寄存器会被改动)。这部分正确的使用场景是如果内联汇编没有在 “output locations” 和 “input operands” 部分初始地声明某寄存器,则需要在这里显式声明,以告知编译器不要再使用该寄存器。
1. 指定输入值和输出值
可以从寄存器和内存位置给输入和输出赋值。输入和输出值列表的格式是:
"constraint" (variable)
其中variable是c变量。扩展asm格式中c代码的局部和全局变量都可以使用。constraint定义把变量存放在寄存器中还是内存中。约束字符如下:
输出修饰符:
asm ("assembly code" : "=a"(result) : "d"(data1), "c"(data2));
把变量data1的值输入到寄存器EDX中,把变量data2的值输入到寄存器ECX中。内联汇编代码的结果(函数返回值)一般放在寄存器EAX中,把EAX中的值输出到变量result中。
2. 在汇编中使用寄存器
8个通用寄存器:EAX,EBX,ECX,EDX,EDI,ESI,EBP(指向栈底),ESP(指向栈顶元素) 指令修饰:
- l 32位
- w 16位
- b 8位
有输入有输出:
#include <stdio.h>
int main(){
int data1 = 3;
int data2 = 5;
int result = 0;
asm volatile (
"addl %%ecx, %%ebx;"
"movl %%ebx, %%eax"
:"=a"(result) // <-- output
:"c"(data1), "b"(data2) // <-- input
);
printf("sum: %d\n",result);
return 0;
}
有输入,没有输出
#include <stdio.h>
int main(){
char input[30]={"Hello world.\n"};
char output[30];
int len=14;
asm volatile (
"cld;"
"rep movsb"
:
:"S"(input), "D"(output), "c"(len)
);
printf("%s", output);
return 0;
}
这里volatile关键字不能省略,因为asm语句没有输出,会被编译器优化掉。
3. 位置序号占位符
在asm的汇编代码中可以通过%0,%1,%2,...等来对位置上的寄存器进行引用。
#include <stdio.h>
int main(){
int data1=10;
int data2=20;
int result;
asm(
"addl %1, %2;"
"movl %2, %0"
:"=r"(result) // output: result对应的寄存器 -> 占位符%0
:"r"(data1), "r"(data2) // input: data1对应的寄存器 -> 占位符%1, data2对应的寄存器 -> 占位符%2
);
printf("sum: %d\n", result);
return 0;
}
4. 引用位置序号占位符
#include <stdio.h>
int main(){
int data1=10;
int data2=20;
asm(
"addl %1, %0"
:"=r"(data2) // 占位符%0
:"r"(data1), "0"(data2) // 引用%0处的寄存器
);
printf("sum: %d\n", data2);
return 0;
}
5. 位置名称占位符
格式:
%[name]"constraint"(variable)
使用方法:
#include <stdio.h>
int main(){
int data1=10;
int data2=20;
asm(
"addl %[v1], %[v2]"
:[v2]"=r"(data2)
:[v1]"r"(data1), "0"(data2)
);
printf("sum: %d\n",data2);
return 0;
}
6. 内联汇编changed registers部分的使用场景
#include <stdio.h>
int main(){
int data1=10;
int result=20;
asm(
"movl %1, %%eax;"
"addl %%eax, %0"
:"=r"(result) // output locations
:"r"(data1), "0"(result) // input operands
:"%eax" // 注意只有一个%,寄存器EAX将不再被编译器使用
);
printf("sum: %d\n",result);
return 0;
}
如果内联汇编没有在 “output locations” 和 “input operands” 部分初始地声明某寄存器,则需要在这里显式声明,以告知编译器不要再使用该寄存器。
7. 使用内存位置
#include <stdio.h>
int main(){
int data1=10;
int data2=20;
int result;
asm(
"addl %1, %2;"
"movl %2, %0"
:"=m"(result) // 这里的m表示使用内存单元存储
:"b"(data1), "a"(data2)
);
printf("sum: %d\n",result);
return 0;
}
使用的c局部变量的内存单元实际是栈中的位置。