课程:汇编语言程序设计
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-79HPajlT-1680186661285)(null)]
C源程序
#include
int func(int a, int b, int length)
{
int i = 0;
while (i < length)
{
a++;
b = a * 2 - a / 4 + i + 1;
b--;
if (a > b)
a = b;
else
a = b * 2;
i++;
}
return a;
}
int main(int argc, char *argv[])
{
int a = 10;
int b = 20;
int length = 5;
a = func(a, b, length);
__asm__ __volatile__(
"shl %%eax \n\t"
"addl %%ebx,%%eax \n\t"
"and %%eax,%%ebx \n\t"
"shl %%eax"
:"=a"(a)
:"a"(a),"b"(b));
printf("a = %d",a);
}
运行结果:
在VS Code之中进行反汇编,查看汇编代码如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4zatxneC-1680186660874)(null)]
main()
函数对应的汇编代码:
-exec disassemble /m main
Dump of assembler code for function main:
24 {
0x00000000004015e3 <+0>: push rbp
0x00000000004015e4 <+1>: push rbx ; 保存现场
0x00000000004015e5 <+2>: sub rsp,0x38 ; 开辟栈空间
0x00000000004015e9 <+6>: lea rbp,[rsp+0x80] ; 将rsp+80h的位置付给rbp
0x00000000004015f1 <+14>: mov DWORD PTR [rbp-0x30],ecx
0x00000000004015f4 <+17>: mov QWORD PTR [rbp-0x28],rdx ; 将参数保存到堆栈
0x00000000004015f8 <+21>: call 0x401710 <__main>
25 int a = 10;
=> 0x00000000004015fd <+26>: mov DWORD PTR [rbp-0x54],0xa ; 声明变量
26 int b = 20;
0x0000000000401604 <+33>: mov DWORD PTR [rbp-0x58],0x14
27 int length = 5;
0x000000000040160b <+40>: mov DWORD PTR [rbp-0x5c],0x5
28 a = func(a, b, length);
0x0000000000401612 <+47>: mov ecx,DWORD PTR [rbp-0x5c]
0x0000000000401615 <+50>: mov edx,DWORD PTR [rbp-0x58]
0x0000000000401618 <+53>: mov eax,DWORD PTR [rbp-0x54] ; 通过寄存器传递参数
0x000000000040161b <+56>: mov r8d,ecx
0x000000000040161e <+59>: mov ecx,eax ; 可能是eax需要保存返回值以及在子函数之中需要使用,
; 提前将eax之中的内容保存到ecx
0x0000000000401620 <+61>: call 0x401550 ; 调用函数
0x0000000000401625 <+66>: mov DWORD PTR [rbp-0x54],eax
29 __asm__ __volatile__(
0x0000000000401628 <+69>: mov eax,DWORD PTR [rbp-0x54]
0x000000000040162b <+72>: mov edx,DWORD PTR [rbp-0x58] ; 将a和b保存到eax与ebx之中,以方便内联汇编之中使用
0x000000000040162e <+75>: mov ebx,edx ; 将b移动到ebx
0x0000000000401630 <+77>: shl eax,1
0x0000000000401632 <+79>: add eax,ebx
0x0000000000401634 <+81>: and ebx,eax
0x0000000000401636 <+83>: shl eax,1 ; 以上四行是嵌入的汇编代码
0x0000000000401638 <+85>: mov DWORD PTR [rbp-0x54],eax ; 保存a,对应'=a'部分
30 "shl %%eax \n\t"
31 "addl %%ebx,%%eax \n\t"
32 "and %%eax,%%ebx \n\t"
33 "shl %%eax"
34 :"=a"(a)
35 :"a"(a),"b"(b));
36 printf("a = %d",a);
0x000000000040163b <+88>: mov eax,DWORD PTR [rbp-0x54] ; 将a保存到eax之中
0x000000000040163e <+91>: mov edx,eax
0x0000000000401640 <+93>: lea rcx,[rip+0x29b9] # 0x404000 ; 理论上来讲,应该是取输出字符串的偏移地址.之后传给printf
0x0000000000401647 <+100>: call 0x402b40 ; 调用printf来进行输出
0x000000000040164c <+105>: mov eax,0x0
37 }
0x0000000000401651 <+110>: add rsp,0x38
0x0000000000401655 <+114>: pop rbx
0x0000000000401656 <+115>: pop rbp ; 函数结束,还原现场
0x0000000000401657 <+116>: ret
End of assembler dump.
func()
部分的汇编代码:
Dump of assembler code for function func:
4 {
0x0000000000401550 <+0>: push rbp ; 将rbp压栈,保存现场
0x0000000000401551 <+1>: mov rbp,rsp ;
0x0000000000401554 <+4>: sub rsp,0x10 ; 栈顶指针上移,开辟栈空间
0x0000000000401558 <+8>: mov DWORD PTR [rbp+0x10],ecx ; ecx,edx与r8d致中保存了传进来的参数.将参数保存到栈空间
0x000000000040155b <+11>: mov DWORD PTR [rbp+0x18],edx
0x000000000040155e <+14>: mov DWORD PTR [rbp+0x20],r8d ; r8是x64架构的64位寄存器,r8d是其低三十二位字节
5 int i = 0;
0x0000000000401562 <+18>: mov DWORD PTR [rbp-0x4],0x0 ; 声明变量
6 while (i < length) ; 跳到比较大小部分
0x0000000000401569 <+25>: jmp 0x4015d2
0x00000000004015d2 <+130>: mov eax,DWORD PTR [rbp-0x4] ; 注意这两行的ip为130和133.也就是说,如果不满足while的条
; 件,会跳过循环体,直接向后边执行.这样设计的另一个好处是,
; 执行完循环部分内容以后,可以自动将ip指向条件判断部分
0x00000000004015d5 <+133>: cmp eax,DWORD PTR [rbp+0x20] ; 判断i是否小于length
0x00000000004015d8 <+136>: jl 0x40156b ; 如果小于,接着向下执行
7 {
8 a++;
0x000000000040156b <+27>: add DWORD PTR [rbp+0x10],0x1 ; 自增
9 b = a * 2 - a / 4 + i + 1;
0x000000000040156f <+31>: mov eax,DWORD PTR [rbp+0x10] ; 将参数a保存到寄存器之中
0x0000000000401572 <+34>: lea ecx,[rax+rax*1] ; 实现a*2.lea为load effictive address的含义,会取得
; rax之中的内容.
0x0000000000401575 <+37>: mov eax,DWORD PTR [rbp+0x10] ; 将a保存到寄存器之中
0x0000000000401578 <+40>: lea edx,[rax+0x3] ; 从rax的第三个字节开始取内容,相当于a左移两位,也就是除以4
0x000000000040157b <+43>: test eax,eax ; 判断正负
0x000000000040157d <+45>: cmovs eax,edx ; 如果eax最高位为1,也就是负数,将其移动到edx之中
0x0000000000401580 <+48>: sar eax,0x2 ; 这几步实现了a/4
0x0000000000401583 <+51>: neg eax ; 取反
0x0000000000401585 <+53>: lea edx,[rcx+rax*1] ; 将a*2-a/4保存到edx之中
0x0000000000401588 <+56>: mov eax,DWORD PTR [rbp-0x4] ; 将i保存到eax
0x000000000040158b <+59>: add eax,edx ; 加上i
0x000000000040158d <+61>: add eax,0x1 ; 加上立即数,1
0x0000000000401590 <+64>: mov DWORD PTR [rbp+0x18],eax
10 b--;
0x0000000000401593 <+67>: sub DWORD PTR [rbp+0x18],0x1
11 b-=1;
0x0000000000401597 <+71>: sub DWORD PTR [rbp+0x18],0x1 ; 自减.这两种写法在底层是同样的.
12 a = a+i%2;
0x000000000040159b <+75>: mov edx,DWORD PTR [rbp-0x4] ; 将i保存到edx之中
0x000000000040159e <+78>: mov eax,edx ; 将i保存到eax之中
0x00000000004015a0 <+80>: sar eax,0x1f ; 算数右移31位,取得符号位
0x00000000004015a3 <+83>: shr eax,0x1f ; 先算数右移,再逻辑右移
0x00000000004015a6 <+86>: add edx,eax ; 将
0x00000000004015a8 <+88>: and edx,0x1 ;
0x00000000004015ab <+91>: sub edx,eax
0x00000000004015ad <+93>: mov eax,edx
0x00000000004015af <+95>: add DWORD PTR [rbp+0x10],eax
13 a = a^4;
0x00000000004015b2 <+98>: xor DWORD PTR [rbp+0x10],0x4 ` ; 与4进行异或
14 if (a > b)
0x00000000004015b6 <+102>: mov eax,DWORD PTR [rbp+0x10] ; 取得a与b
0x00000000004015b9 <+105>: cmp eax,DWORD PTR [rbp+0x18]
0x00000000004015bc <+108>: jle 0x4015c6 ; 如果不满足判断条件,那么跳转到else部分
15 a = b;
0x00000000004015be <+110>: mov eax,DWORD PTR [rbp+0x18]
0x00000000004015c1 <+113>: mov DWORD PTR [rbp+0x10],eax ; 将b的数值赋给a
0x00000000004015c4 <+116>: jmp 0x4015ce ; 跳过else
16 else
17 a = b * 2;
0x00000000004015c6 <+118>: mov eax,DWORD PTR [rbp+0x18] ; 取得b
0x00000000004015c9 <+121>: add eax,eax ; *2
0x00000000004015cb <+123>: mov DWORD PTR [rbp+0x10],eax ; 将b的数值赋给a
18 i++;
0x00000000004015ce <+126>: add DWORD PTR [rbp-0x4],0x1 ; 自增
19 }
20 return a;
0x00000000004015da <+138>: mov eax,DWORD PTR [rbp+0x10] ; 将返回值保存到eax之中.汇编通过eax来返回参数
21 }
0x00000000004015dd <+141>: add rsp,0x10
0x00000000004015e1 <+145>: pop rbp ; 还原现场
0x00000000004015e2 <+146>: ret
End of assembler dump.
注意到在main()
函数刚刚开始的执行的时候调用的__main
函数,查看__main
函数的汇编代码如下:
Dump of assembler code for function __main:
0x00000000004016d0 <+0>: mov eax,DWORD PTR [rip+0x595a] # 0x407030
0x00000000004016d6 <+6>: test eax,eax
0x00000000004016d8 <+8>: je 0x4016e0 <__main+16>
0x00000000004016da <+10>: ret
0x00000000004016db <+11>: nop DWORD PTR [rax+rax*1+0x0]
0x00000000004016e0 <+16>: mov DWORD PTR [rip+0x5946],0x1 # 0x407030
0x00000000004016ea <+26>: jmp 0x401660 <__do_global_ctors>
0x00000000004016ef <+31>: nop
End of assembler dump.