CISC(复杂指令集):Intel 芯片
RISC(精简指令集):ARM 芯片、MAC m1、m2 芯片
disas /r
:查看汇编的时候,显示机器码
汇编中,函数的返回值一般在 eax 中。ecx 寄存器一般存储循环次数
寄存器中以 e 开头的,就是 32 位寄存器。如果以 r 开头,就是 64 位寄存器。
通用寄存器:
- eax:32 位。函数返回值
- ebx:32 位
- ecx:32 位。循环次数
- edx:32 位
- ebp:32 位 栈底寄存器
- esp:32 位 栈顶寄存器
- esi:source
- edi:dst
- eip:程序计数器,无法直接操作,需要特定指令间接操作
一般在操作寄存器时,清零一下:xor eax, eax
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
| // 开辟新的栈帧,一个函数一个栈帧 push ebp mov ebp, esp sub esp, 0CCH // 保存现场,通过栈保存常用的寄存器 push ebx push esi push edi // 初始化栈内存(可做可不做) lea edi, [ebp+xxxh] mov ecx, 33h mov eax, 0CCCCCCCCh rep stos dword ptr es:[edi] // 业务逻辑 mov dword ptr [ebp-8], 0Ah mov eax, dword ptr [ebp-8] // 恢复现场 pop edi pop esi pop ebx // 恢复栈帧,下面两行可以用 leave 来替换 mov esp, ebp pop ebp // 返回 retx
|
栈指令:
1 2 3 4 5 6
| push pop call ret jmp 无条件跳转 jcc 有条件跳转(eflags)
|
内联汇编:__asm__ volatile ("sgdt gdt_ptr;");
1 2 3 4 5 6
| add: push ebp mov ebp, esp ... leave ret
|
汇编函数最好写一个 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
| VC的调用约定(微软) __cdecl 1. 虽然没有写调用约定,但是默认的调用约定时 __cdecl 2. 调用时,参数的压栈顺序是从右向左(所有的调用约定都是从右向左),依靠栈传参 3. 传递的参数是在那个栈里?_tmain 还是 print。 传参是在调用者栈中,在 _tmain,而不是在被调用者 print 中 4. print 函数中怎么取到参数?借助 ebp 寄存器;第一个参数的计算公式:ebp+8 32 位机器,栈的 slot 是 4 字节,计算公式:ebp + 4*2 64 位机器,栈的 slot 是 8 字节,计算公式:rbp + 8*2 5. 因为传参破坏了栈平衡,如何平栈(堆栈平衡) 内平栈:被调用函数自己来平衡栈 ret 4*N 外平栈:调用者来平衡栈 __cdecl 使用外平栈(一般 gcc 使用这个方式) __stdcall 使用内平栈,windows API 使用的
__fastcall 在 x86 和 x64 机器上有不同。64 位只有这种调用约定 他会借助寄存器传参,就是寄存器+栈的方式传参 x86 下 参数小于等于 2,纯寄存器传参。 用了两个寄存器,传参顺序自右向左,不需要平栈 大于 2,寄存器+栈 传参 用了两个寄存器,其他参数用栈,还是自右向左,内平栈 x64 下,参数小于等于 6,大于 6,寄存器+栈 传参
__asm__ volatile(""); asm volatile(""); __asm__("");
|