【汇编实战】多重循环程序实验(一文学会)
C语言编写多重循环程序(大于3重),查看其反汇编代码,分析各条语句功能(分析情况需要写入实验报告),并采用汇编语言重写相同功能程序。
首先,我们使用C++编写四重循环代码,每重循环大小为10,在循环最内层对ans加一,预期答案为10000。
编写C++代码如下所示:
#include
using namespace std;
int main(){
int ans = 0;
for(int i =0; i < 10; i++){
for(int j = 0 ;j < 10; j++){
for(int k =0 ; k< 10 ; k++){
for(int h = 0; h < 10; h++){
ans += 1;
}
}
}
}
cout << ans << endl;
return ans;
}
使用g++查看其汇编代码以及反汇编代码,分别运行如下命令行:
g++ -S .\triloop.cpp
g++ -g -c .\triloop.cpp
objdump -S triloop.o >triloop_dump.s
在文件夹下分别生成汇编文件triloop.s以及反汇编文件triloop_dump.s。本实验中我们通过查看反汇编代码triloop_dump.s分析每一行汇编代码的功能。
上一小节中生成的汇编代码核心部分如下所示:
#include
using namespace std;
int main(){
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 40 sub $0x40,%rsp
8: e8 00 00 00 00 callq d <main+0xd>
int ans = 0;
d: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
for(int i =0; i < 10; i++){
14: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
1b: 83 7d f8 09 cmpl $0x9,-0x8(%rbp)
1f: 7f 43 jg 64 <main+0x64>
for(int j = 0 ;j < 10; j++){
21: c7 45 f4 00 00 00 00 movl $0x0,-0xc(%rbp)
28: 83 7d f4 09 cmpl $0x9,-0xc(%rbp)
2c: 7f 30 jg 5e <main+0x5e>
for(int k =0 ; k< 10 ; k++){
2e: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%rbp)
35: 83 7d f0 09 cmpl $0x9,-0x10(%rbp)
39: 7f 1d jg 58 <main+0x58>
for(int h = 0; h < 10; h++){
3b: c7 45 ec 00 00 00 00 movl $0x0,-0x14(%rbp)
42: 83 7d ec 09 cmpl $0x9,-0x14(%rbp)
46: 7f 0a jg 52 <main+0x52>
ans += 1;
48: 83 45 fc 01 addl $0x1,-0x4(%rbp)
for(int h = 0; h < 10; h++){
4c: 83 45 ec 01 addl $0x1,-0x14(%rbp)
50: eb f0 jmp 42 <main+0x42>
for(int k =0 ; k< 10 ; k++){
52: 83 45 f0 01 addl $0x1,-0x10(%rbp)
56: eb dd jmp 35 <main+0x35>
for(int j = 0 ;j < 10; j++){
58: 83 45 f4 01 addl $0x1,-0xc(%rbp)
5c: eb ca jmp 28 <main+0x28>
for(int i =0; i < 10; i++){
5e: 83 45 f8 01 addl $0x1,-0x8(%rbp)
62: eb b7 jmp 1b <main+0x1b>
}
}
}
}
cout << ans << endl;
64: 8b 45 fc mov -0x4(%rbp),%eax
67: 89 c2 mov %eax,%edx
69: 48 8b 0d 00 00 00 00 mov 0x0(%rip),%rcx
# 70
70: e8 00 00 00 00 callq 75 <main+0x75>
75: 48 8b 15 00 00 00 00 mov 0x0(%rip),%rdx
# 7c
7c: 48 89 c1 mov %rax,%rcx
7f: e8 00 00 00 00 callq 84 <main+0x84>
return ans;
84: 8b 45 fc mov -0x4(%rbp),%eax
87: 48 83 c4 40 add $0x40,%rsp
8b: 5d pop %rbp
8c: c3 retq
【分析】
Push %rbp
Mov %rsp,%rbp
Rbp是指向当前栈帧底部的基指针,rsp是指向当前栈顶部的栈帧顶部的指针
关于rbp和rsp的更多资料可以查看这篇博客https://blog.csdn.net/ABo_Zhang/article/details/89668046
Sub $0x40,%rsp
rsp的值减去了64,看似只是简单地修改了rsp寄存器的值,其实质确实给main函数的局部变量和临时变量预留了64字节的空间
callq d <main+0xd>
将下一条指令的CS和IP入栈,操作与call指令对应的jump指令
movl $0x0,-0x4(%rbp)
rbp向下移动四个字节的位置存放ans,赋值为0
movl $0x0,-0x8(%rbp)
rbp向下移动八个字节的位置存放i,赋值为0
cmpl $0x9,-0x8(%rbp)
jg 64 <main+0x64>
比较i和9的大小,如果i > 9 跳到64,64执行cout<
movl $0x0,-0xc(%rbp)
cmpl $0x9,-0xc(%rbp)
jg 5e <main+0x5e>
rbp向下移动12字节处存放j,赋值为0
比较j和9的大小,如果 j > 9 跳到5e
movl $0x0,-0x10rbp)
cmpl $0x9,-0x10rbp)
jg 58 <main+0x58>
rbp向下移动16字节处存放k,赋值为0
比较k和9的大小,如果 k > 9 跳到58 。
h同理,省略。
addl $0x1,-0x4(%rbp)
前面提到rbp向下移动四个字节的位置存放ans,这里执行ans+1
addl $0x1,-0x14(%rbp)
jmp 42 <main+0x42>
如果不跳到52证明h < 9, h++
跳到42处继续比较h和9的大小
addl $0x1,-0x10(%rbp)
jmp 35 <main+0x35>
K++,之后跳到35处继续比较k和9的大小。
后面j和i的处理过程类似,在此省略。
mov -0x4(%rbp),%eax
add $0x40,%rsp
pop %rbp
retq
将ans的值赋值给eax,rsp指向rbp的位置,将rbp推出栈帧。
编写四重循环汇编程序如下:
.386
.model flat, stdcall
option casemap : none
include msvcrt.inc
includelib msvcrt.lib
endl equ <0DH, 0AH>
.data
szFormat db "%d ", endl, 0
.code
main PROC
; mov ecx, 10; 循环最多10次
xor esi, esi
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
loop1: ; 第一层循环
mov eax, 0
L4: cmp eax, 9
jg endloop1
loop2:; 第二层循环
mov ebx, 0
L3: cmp ebx, 9;
jg endloop2
loop3:; 第三层循环
mov ecx, 0
L2: cmp ecx, 9;
jg endloop3
loop4: ; 第四层循环
mov edx, 0
L1: cmp edx, 9;
jg endloop4
inc esi
inc edx
jmp L1
endloop4:
inc ecx
jmp L2
endloop3:
inc ebx
jmp L3
endloop2:
inc eax
jmp L4
endloop1:
invoke crt_printf , addr szFormat, esi;输出ans
ret
main endp
END main
运行上述程序得到如下结果,实验结果与预期相同。