参考资料:https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html
Asm Syntax
linux上我一般都在用GNU C 编译器 GCC,支持AT&T和intel格式的内联汇编,gcc默认是att,我个人是习惯了intel格式的汇编,但是大部分操作系统源码都是att格式的内联汇编,cao
+------------------------------+------------------------------------+ | Intel Code | AT&T Code | +------------------------------+------------------------------------+ | mov eax,1 | movl $1,%eax | | mov ebx,0ffh | movl $0xff,%ebx | | int 80h | int $0x80 | | mov ebx, eax | movl %eax, %ebx | | mov eax,[ecx] | movl (%ecx),%eax | | mov eax,[ebx+3] | movl 3(%ebx),%eax | | mov eax,[ebx+20h] | movl 0x20(%ebx),%eax | | add eax,[ebx+ecx*2h] | addl (%ebx,%ecx,0x2),%eax | | lea eax,[ebx+ecx] | leal (%ebx,%ecx),%eax | | sub eax,[ebx+ecx*4h-20h] | subl -0x20(%ebx,%ecx,0x4),%eax | +------------------------------+------------------------------------+ c语言变量寻址 intel mov eax,[_value] at&t mov _value,%eax at&t 这样把eax的值写入0x26fa8地址处:mov %eax,0x26fa8
|
- 源操作数和目的操作数顺序相反
- att寄存器%前缀
- att立即数前有$,16进制0x而不是后加h
- att mov 操作大小’b’、’w’、’l’ 分别指明了字节(8位)、字(16位)、长型(32位)替代intel”byte ptr”、 “word ptr” 和 “dword ptr” 前缀
- intel:section:[base + index*scale + disp]—>att:section:disp(base, index, scale),这里立即数不用$
Basic Inline
两种语法都可以
asm("assembly code") __asm__("assembly code")
|
asm("assembly code1;" "assembly code2;") asm("assembly code1\n\t" "assembly code2\n\t")
|
#include <stdio.h> #include <unistd.h> int main() { asm("mov rcx,0x666;" "mov rax, rcx;"); asm("mov rcx,0x666\n\t;" "mov rax, rcx;\n\t"); }
|
#include <stdio.h> #include <unistd.h> int main() { __asm__("mov %rax, %rbx\n\t" "mov $56, %rsi\n\t"); }
|
这样插入汇编很简单但是gcc并不知道我们改变了那些寄存器的值,gcc可能进行某些代码优化时,会用到这些寄存器但是它里面的值已经被我们插入汇编修改掉了,就会导致一些error
Extended Asm
为了解决上述问题并且和c有个更好的交互,就有了拓展汇编
asm ( assembler template : output operands : input operands : list of clobbered registers );
|
operands
总操作数的数目限制在 10 个,或者机器描述中的任何指令格式中的最大操作数数目,以较大者为准。
在汇编程序模板中,每个操作数用数字引用。编号方式从0开始,从上往下
- r表示我们让gcc自动帮我们去选择一个输入寄存器一个输出寄存器
asm ("leal (%1,%1,4), %0" : "=r" (five_times_x) : "r" (x) );
|
asm ("leal (%0,%0,4), %0" : "=r" (five_times_x) : "0" (x) );
|
- 指定ecx寄存器,为了和操作数进行区分,寄存器我们采用两个%
asm ("leal (%%ecx,%%ecx,4), %%ecx" : "=c" (x) : "c" (x) );
|
我们没有往list of clobbered registers去添加寄存器,因为目前情况gcc知道我们破坏了哪个,因为它们被显式地指定为约束了
Clobber List
如果指令隐式或显式地使用了任何其他寄存器,(并且寄存器没有出现在输出或者输出约束列表里),那么就需要在Clobber List中指定这些寄存器。
如果我们的指令可以修改条件码寄存器(cc),我们必须将 “cc” 添加进修饰寄存器列表。
如果我们的指令以不可预测的方式修改了内存,那么需要将 “memory” 添加进修饰寄存器列表。
Volatile修饰符
gcc会在source–>code的过程中会进行一些优化,例如删除一些代码改变一些代码位置等
为了不让gcc去优化我们的inline asm将关键词volatile 或者 __volatile__ 放置在 asm 后面、()的前面。
constraints
寄存器约束
“reg constraints”(variable),输出操作数还要有“=”的约束在”=reg constraints”(variable)
+---+--------------------+ | r | Register(s) | +---+--------------------+ | a | %eax, %ax, %al | | b | %ebx, %bx, %bl | | c | %ecx, %cx, %cl | | d | %edx, %dx, %dl | | S | %esi, %si | | D | %edi, %di | +---+--------------------+ q eax, ebx, ecx, edx
|
内存操作数约束
“m”(variable),使用memory而不是reg作为temp来做运算
匹配(数字)约束
在某些情况下,一个变量可能既充当输入操作数,也充当输出操作数。可以通过使用匹配约束在 “asm” 中指定这种情况。
asm (“incl %0” :”=a”(var):”0”(var));
+
“+”号用于指定一个操作数既可以用作输入,也可以用作输出,即输入/输出操作数。
int a = 10, b = 20; asm("addl %[a], %[b]" : [b] "+r" (b) : [a] "r" (a));
|
&
“&” : 意味着这个操作数为一个早期改动的操作数,其在该指令完成前通过使用输入操作数被修改了。因此,这个操作数不可以位于一个被用作输出操作数或任何内存地址部分的寄存器。如果在旧值被写入之前它仅用作输入而已,一个输入操作数可以为一个早期改动操作数。
#include <stdio.h> #include <unistd.h> int main() { int count = 10, fill_value = 0x6; char dest[10] = {0}; asm("cld\n\t" "rep\n\t" "stosb\n\t" : : "c" (count), "a" (fill_value), "D" (dest) : ); for (size_t i = 0; i < 10; i++) { printf("%d\n", dest[i]); } }
|
0x0000000000001169 <+0>: endbr64 0x000000000000116d <+4>: push rbp 0x000000000000116e <+5>: mov rbp,rsp 0x0000000000001171 <+8>: sub rsp,0x30 0x0000000000001175 <+12>: mov rax,QWORD PTR fs:0x28 0x000000000000117e <+21>: mov QWORD PTR [rbp-0x8],rax 0x0000000000001182 <+25>: xor eax,eax 0x0000000000001184 <+27>: mov DWORD PTR [rbp-0x28],0xa 0x000000000000118b <+34>: mov DWORD PTR [rbp-0x24],0x6 0x0000000000001192 <+41>: mov QWORD PTR [rbp-0x12],0x0 0x000000000000119a <+49>: mov WORD PTR [rbp-0xa],0x0 0x00000000000011a0 <+55>: mov edx,DWORD PTR [rbp-0x28] 0x00000000000011a3 <+58>: mov eax,DWORD PTR [rbp-0x24] 0x00000000000011a6 <+61>: lea rsi,[rbp-0x12] 0x00000000000011aa <+65>: mov ecx,edx 0x00000000000011ac <+67>: mov rdi,rsi 0x00000000000011af <+70>: cld 0x00000000000011b0 <+71>: rep stos BYTE PTR es:[rdi],al 0x00000000000011b2 <+73>: mov QWORD PTR [rbp-0x20],0x0 0x00000000000011ba <+81>: jmp 0x11e8 <main+127> 0x00000000000011bc <+83>: lea rdx,[rbp-0x12] 0x00000000000011c0 <+87>: mov rax,QWORD PTR [rbp-0x20] 0x00000000000011c4 <+91>: add rax,rdx 0x00000000000011c7 <+94>: movzx eax,BYTE PTR [rax] 0x00000000000011ca <+97>: movsx eax,al 0x00000000000011cd <+100>: mov esi,eax 0x00000000000011cf <+102>: lea rax,[rip+0xe2e] # 0x2004 0x00000000000011d6 <+109>: mov rdi,rax 0x00000000000011d9 <+112>: mov eax,0x0 0x00000000000011de <+117>: call 0x1070 <printf@plt> 0x00000000000011e3 <+122>: add QWORD PTR [rbp-0x20],0x1 0x00000000000011e8 <+127>: cmp QWORD PTR [rbp-0x20],0x9 0x00000000000011ed <+132>: jbe 0x11bc <main+83> 0x00000000000011ef <+134>: mov eax,0x0 0x00000000000011f4 <+139>: mov rdx,QWORD PTR [rbp-0x8] 0x00000000000011f8 <+143>: sub rdx,QWORD PTR fs:0x28 0x0000000000001201 <+152>: je 0x1208 <main+159> 0x0000000000001203 <+154>: call 0x1060 <__stack_chk_fail@plt> 0x0000000000001208 <+159>: leave 0x0000000000001209 <+160>: ret
|
一直到+67处做好准备工作,去提升堆栈,初始化局部变量
#include <stdio.h> #include <unistd.h> int main() {
int a=10, b=0; asm ("mov %1, %%eax;" "mov %%eax, %0;" :"=r"(b) :"r"(a) :"%eax" ); printf("%d,%d", a, b);
}
|
0x0000000000001149 <+0>: endbr64 0x000000000000114d <+4>: push rbp 0x000000000000114e <+5>: mov rbp,rsp 0x0000000000001151 <+8>: sub rsp,0x10 0x0000000000001155 <+12>: mov DWORD PTR [rbp-0x8],0xa 0x000000000000115c <+19>: mov DWORD PTR [rbp-0x4],0x0 0x0000000000001163 <+26>: mov eax,DWORD PTR [rbp-0x8] 0x0000000000001166 <+29>: mov eax,eax 0x0000000000001168 <+31>: mov eax,eax 0x000000000000116a <+33>: mov DWORD PTR [rbp-0x4],eax 0x000000000000116d <+36>: mov edx,DWORD PTR [rbp-0x4] 0x0000000000001170 <+39>: mov eax,DWORD PTR [rbp-0x8] 0x0000000000001173 <+42>: mov esi,eax 0x0000000000001175 <+44>: lea rax,[rip+0xe88] # 0x2004 0x000000000000117c <+51>: mov rdi,rax 0x000000000000117f <+54>: mov eax,0x0 0x0000000000001184 <+59>: call 0x1050 <printf@plt> 0x0000000000001189 <+64>: mov eax,0x0 0x000000000000118e <+69>: leave 0x000000000000118f <+70>: ret
|