程序的机器级表示(包含大量IA32-Intel格式汇编代码!!!)

程序的机器级表示

寻址方式

  • 立即寻址

    立即寻址

  • 直接寻址

    直接寻址

  • 寄存器寻址

    寄存器寻址

  • 寄存器间接寻址

    寄存器间接寻址

  • 基址寻址

    img

  • 变址寻址

    img

  • ……

IA32汇编指令

通用寄存器

img

前四个:a -> add, b -> base, c -> count, d -> 'data'

si: s -> source -> 源地址, di -> destination -> 目的地址

bp -> base pointer, sp -> stack pointer

ATT & Intel

img

寻址方式

img

常用指令

数据传输指令

  • mov指令

数据传递方向

ATT ->

Intel <-

img

  • lea:load effective address,加载有效地址,将一个内存地址加载到目的寄存器;
  • push:将数据压入栈,同时 esp 减去数据长度;
  • pop:将栈顶数据弹出栈,同时 esp 加上数据长度;

算术和逻辑运算指令

img

除法指令,它们都只有一个操作数,表示除数,被除数则放在 edx : eax 中;得到的结果商放在 eax 中,余数放在 edx 中:

  • div:无符号除
  • idiv:有符号除

转移指令

img

  • 重置条件码

img

e.g

选择结构机器级表示

C程序

1
2
3
4
5
6
7
8
9
10
int a = 23;
int b = 31;
if (a > b)
{
a++;
}
else {
a--;
}
printf(" a = %d\n", a);

使用goto

1
2
3
4
5
6
7
8
    if (a <= b)
goto L1;
a++;
goto L2;
L1:
a--;
L2:
printf(" a = %d\n", a);

汇编

img

使用vs2022 x86反汇编

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    int a = 23;
00151896 mov dword ptr [ebp-8],17h
int b = 31;
0015189D mov dword ptr [ebp-14h],1Fh
if (a > b)
001518A4 mov eax,dword ptr [ebp-8]
001518A7 cmp eax,dword ptr [ebp-14h]
001518AA jle 001518B7
{
a++;
001518AC mov eax,dword ptr [ebp-8]
001518AF add eax,1
001518B2 mov dword ptr [ebp-8],eax
}
001518B5 jmp 001518C0
else {
a--;
001518B7 mov eax,dword ptr [ebp-8]
001518BA sub eax,1
001518BD mov dword ptr [ebp-8],eax
}

循环结构机器级表示

do - while循环

C程序

1
2
3
4
5
int a = 0;
do {
a++;
} while (a < 5);
printf(" a = %d \n ", a);

使用goto

1
2
3
4
5
6
7
    int a = 0;
L1:
a++;

if (a < 5)
goto L1;
printf(" a = %d \n ", a);

汇编

img

1
2
3
4
5
6
7
8
9
10
	int a = 0;
007A1896 mov dword ptr [ebp-8],0
do {
a++;
007A189D mov eax,dword ptr [ebp-8]
007A18A0 add eax,1
007A18A3 mov dword ptr [ebp-8],eax
} while (a < 5);
007A18A6 cmp dword ptr [ebp-8],5
007A18AA jl 007A189D
while循环
1
2
3
4
5
int a = 0;
while (a < 5) {
a++;
}
printf(" a = %d \n ", a);
1
2
3
4
5
6
7
8
    int a = 0;
L1:
if (a >= 5)
goto L2;
a++;
goto L1;
L2:
printf(" a = %d \n ", a);

img

1
2
3
4
5
6
7
8
9
10
11
	int a = 0;
007A18BD mov dword ptr [ebp-8],0
while (a < 5) {
007A18C4 cmp dword ptr [ebp-8],5
007A18C8 jge 007A18D5
a++;
007A18CA mov eax,dword ptr [ebp-8]
007A18CD add eax,1
007A18D0 mov dword ptr [ebp-8],eax
}
007A18D3 jmp 007A18C4
for循环
1
2
3
4
5
6
int a = 10;
for (int i = 0; i < 5; i++)
{
a--;
}
printf(" a = %d \n ", a);
1
2
for( 初始化循环变量; 判断循环条件; 更新循环变量)
循环体
1
2
3
4
5
6
7
int a = 10;
int i = 0;
while (i < 5) {
a--;
i++;
}
printf(" a = %d \n ", a);
1
2
3
4
5
6
7
8
9
10
    int a = 10;
int i = 0;
L1:
if (i >= 5)
goto L2;
a--;
i++;
goto L1;
L2:
printf(" a = %d \n ", a);

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
	int a = 10;
007A18E6 mov dword ptr [ebp-8],0Ah
for (int i = 0; i < 5; i++)
007A18ED mov dword ptr [ebp-14h],0
007A18F4 jmp 007A18FF
007A18F6 mov eax,dword ptr [ebp-14h]
007A18F9 add eax,1
007A18FC mov dword ptr [ebp-14h],eax
007A18FF cmp dword ptr [ebp-14h],5
007A1903 jge 007A1910
{
a--;
007A1905 mov eax,dword ptr [ebp-8]
007A1908 sub eax,1
007A190B mov dword ptr [ebp-8],eax
}
007A190E jmp 007A18F6
printf(" a = %d \n ", a);
007A1910 ...

过程调用的机器级表示

使用栈实现

img

流程

img

  • 程序代码和数据:主要包括 只读代码段读写段(.data 和 .bss)。对所有进程来说,代码都是从固定地址开始,紧接着就是 C 语言中的全局和静态数据。其中 .data 中是已初始化的全局和静态 C 变量,而 .bss 中是未初始化的全局和静态变量。
  • 堆(Heap):用于运行时的动态内存分配,向上(高地址)生长。代码和数据区,在进程开始运行时就被指定了大小;而通过调用 malloc 和 free 这样的 C 标准库函数,可以让堆区动态地扩展和收缩。
  • 共享库的内存映射区:用户区的中间部分是一块内存映射区域,用来存放像 C 标准库这样的共享库。(printf)
  • 用户栈(Stack):位于虚拟地址空间用户区顶部,向下(低地址)生长。一般用来存储局部变量和函数参数,结合堆栈指针可以方便地实现函数的调用和返回。

img

  1. 从可执行文件加载

    image-20240622154330031
  2. 执行P指令

    1. P函数局部变量入栈

      image-20240622154512855
    2. 调用Q函数

      问题:

      1. 参数能够传递给Q函数
      2. 执行完Q函数能够返回到跳转语句的下一句
      3. P函数中使用的寄存器的值应该被保存

      依次由如下解决方案:

      1. 在P函数的栈帧中压入参数构造区和调用指令的下一条指令

        image-20240622195348350

        image-20240622195404685

        eip寄存器传入跳转的指令的位置

      2. 在Q函数的栈帧中P寄存器的值入栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int add(int x, int y);

int main() {
int a = 23;
int b = 31;

int sum = add(a, b);

printf(" sum = %d\n", sum);
}

int add(int x, int y) {
int sum = x + y;
return sum;
}

main栈帧

1
2
3
4
5
# P的局部变量
int a = 23;
005018F6 mov dword ptr [ebp-8],17h
int b = 31;
005018FD mov dword ptr [ebp-14h],1Fh

EAX = 0050C008 EBX = 02636000 ECX = 0050C008 EDX = 00000001 ESI = 02636000

EDI = 028FF6BC EIP = 00501904 ESP = 028FF5CC EBP = 028FF6BC EFL = 00000246

1
2
3
4
5
6
# 参数构造区
int sum = add(a, b);
00501904 mov eax,dword ptr [ebp-14h] # 参数b移到eax寄存器
00501907 push eax # eax压栈
00501908 mov ecx,dword ptr [ebp-8] # 参数a移到ecx寄存器
0050190B push ecx # ecx压栈

EAX = 0000001F EBX = 02636000 ECX = 00000017 EDX = 00000001 ESI = 02636000

EDI = 028FF6BC EIP = 0050190C ESP = 028FF5C4 EBP = 028FF6BC EFL = 00000246

栈顶指针对应的内存地址

image-20240622201110421

1
2
3
4
5
6
7
# 调用Q函数(`add`)
# 跳转的同时,该地址的下一行地址作为返回地址入栈
0050190C call 00501023

---
0050190C call 00501023
00501911 add esp,8

ESP = 028FF5C0

image-20240622201732128
  • call位置

    1
    2
    3
    4
    5
    6
    00501023  jmp         00501870

    ---
    # 跳转到add函数起始位置
    int add(int x, int y) {
    00501870 push ebp

1
2
# 将P的ebp栈基指针入栈
00501870 push ebp

ESP = 028FF5BC EBP = 028FF6BC

image-20240622202224089

Q的栈帧开始

1
00501871  mov         ebp,esp  # P的栈顶作为Q的栈基址

初态: ESP = 028FF5BC EBP = 028FF6BC

末态: ESP = 028FF5BC EBP = 028FF5BC

1
00501873  sub         esp,0CCh  # 加上一个较大的偏移量开辟栈空间

ESP = 028FF4F0

1
2
3
4
# 保存P的寄存器的值(ebx, esi, edi)入栈
00501879 push ebx
0050187A push esi
0050187B push edi

P寄存器值

EBX = 02636000 ESI = 02636000 EDI = 028FF6BC

image-20240622203334719

此时:ESP = 028FF4E4

1
2
3
4
5
# Q的局部变量 && Q局部变量执行Q语句
int sum = x + y;
00501896 mov eax,dword ptr [ebp+8] # Q的栈基址向上寻址,对应参数a放入eax
00501899 add eax,dword ptr [ebp+0Ch] # 参数a和eax的值作为alu的两个输入
0050189C mov dword ptr [ebp-8],eax
image-20240622204158738
image-20240622204922635
image-20240622205049461
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 调用Q结果返回
return sum;
0050189F mov eax,dword ptr [ebp-8] # 将结果移动到eax寄存器
}
# 保存的P寄存器出栈
005018A2 pop edi
005018A3 pop esi
005018A4 pop ebx
# 回收Q的栈空间地址
005018A5 add esp,0CCh
# 维持P的栈顶esp
005018AB cmp ebp,esp
005018AD call 00501253
005018B2 mov esp,ebp
# 保存的P的栈基址出栈
005018B4 pop ebp
# 返回地址出栈
005018B5 ret
# 到此P的各寄存器值均恢复
1
2
3
4
00501911  add         esp,8  
00501914 mov dword ptr [ebp-20h],eax
printf(" sum = %d\n", sum);
...

实践(归并排序的机器级表示)

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <stdio.h>
#include <stdlib.h>

void merge(int arr[], int left, int mid, int right) {
int n1 = mid - left + 1;
int n2 = right - mid;

int* leftArray = (int*)malloc(n1 * sizeof(int));
int* rightArray = (int*)malloc(n2 * sizeof(int));

for (int i = 0; i < n1; i++) {
leftArray[i] = arr[left + i];
}
for (int j = 0; j < n2; j++) {
rightArray[j] = arr[mid + 1 + j];
}

int i = 0, j = 0, k = left;
while (i < n1 && j < n2) {
if (leftArray[i] <= rightArray[j]) {
arr[k] = leftArray[i];
i++;
}
else {
arr[k] = rightArray[j];
j++;
}
k++;
}

while (i < n1) {
arr[k] = leftArray[i];
i++;
k++;
}
while (j < n2) {
arr[k] = rightArray[j];
j++;
k++;
}

free(leftArray);
free(rightArray);
}

void mergeSort(int arr[], int left, int right) {
if (left < right) {
int mid = left + ((right - left) >> 1);

mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);

merge(arr, left, mid, right);
}
}

void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}

int main() {
int arr[] = { 9, 5, 6, 2, 1, 4, 3, 7, 8, 10 };
int arr_size = sizeof(arr) / sizeof(arr[0]);

printf("排序前的数组: \n");
printArray(arr, arr_size);

mergeSort(arr, 0, arr_size - 1);

printf("排序后的数组: \n");
printArray(arr, arr_size);

return 0;
}

main
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
31
32
33
34
35
36
37
38
39
40
41
42
43
int main() {
00D418A0 push ebp
00D418A1 mov ebp,esp
00D418A3 sub esp,100h
00D418A9 push ebx
00D418AA push esi
00D418AB push edi
00D418AC lea edi,[ebp-40h]
00D418AF mov ecx,10h
00D418B4 mov eax,0CCCCCCCCh
00D418B9 rep stos dword ptr es:[edi]
00D418BB mov eax,dword ptr ds:[00D4A000h]
00D418C0 xor eax,ebp
00D418C2 mov dword ptr [ebp-4],eax
00D418C5 mov ecx,0D4C00Eh
00D418CA call 00D41343
00D418CF nop
int arr[] = { 9, 5, 6, 2, 1, 4, 3, 7, 8, 10 };
00D418D0 mov dword ptr [ebp-30h],9
00D418D7 mov dword ptr [ebp-2Ch],5
00D418DE mov dword ptr [ebp-28h],6
00D418E5 mov dword ptr [ebp-24h],2
00D418EC mov dword ptr [ebp-20h],1
00D418F3 mov dword ptr [ebp-1Ch],4
00D418FA mov dword ptr [ebp-18h],3
00D41901 mov dword ptr [ebp-14h],7
00D41908 mov dword ptr [ebp-10h],8
00D4190F mov dword ptr [ebp-0Ch],0Ah
int arr_size = sizeof(arr) / sizeof(arr[0]);
00D41916 mov dword ptr [ebp-3Ch],0Ah

printf("排序前的数组: \n");
00D4191D push 0D47B38h
00D41922 call 00D410D7
00D41927 add esp,4
printArray(arr, arr_size);
# 参数构造区
00D4192A mov eax,dword ptr [ebp-3Ch]
00D4192D push eax
00D4192E lea ecx,[ebp-30h] # 加载数组的有效地址
00D41931 push ecx
# 调用printArray
00D41932 call 00D411EF

ECX = 0x00AFF75C

image-20240622215803198
printArray
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
31
32
33
void printArray(int arr[], int size) {
# main的栈底入栈
00D41D50 push ebp
# main的栈顶作为printArray的栈底
00D41D51 mov ebp,esp
# 开辟保存main寄存器值的空间
00D41D53 sub esp,0CCh
# 归档main寄存器
00D41D59 push ebx
00D41D5A push esi
00D41D5B push edi
...
for (int i = 0; i < size; i++) {
00D41D76 mov dword ptr [ebp-8],0 # i=0
00D41D7D jmp 00D41D88
# L1(i++)
00D41D7F mov eax,dword ptr [ebp-8]
00D41D82 add eax,1
00D41D85 mov dword ptr [ebp-8],eax
# L2(终止条件,>=跳转到程序外的地址)
00D41D88 mov eax,dword ptr [ebp-8]
00D41D8B cmp eax,dword ptr [ebp+0Ch]
00D41D8E jge 00D41DA9

printf("%d ", arr[i]);
00D41D90 mov eax,dword ptr [ebp-8]
00D41D93 mov ecx,dword ptr [ebp+8]
00D41D96 mov edx,dword ptr [ecx+eax*4]
00D41D99 push edx
00D41D9A push 0D47B30h
00D41D9F call 00D410D7
00D41DA4 add esp,8
}

打印第一个数:

  • i传递给eax

    mov eax,dword ptr [ebp-8]

image-20240622221525302
  • arr的有效地址传递给ecx

    mov ecx,dword ptr [ebp+8]

image-20240622221923369
image-20240622221944873
  • 取出数组的第一个数赋给edx寄存器(使用比例变址寻址)

    mov edx,dword ptr [ecx+eax*4]

1
00D41DA7  jmp         00D41D7F  # 跳转到L1
main
1
2
3
4
5
6
7
8
9
10
11
    mergeSort(arr, 0, arr_size - 1);
# arr_size-1
00D4193A mov eax,dword ptr [ebp-3Ch]
00D4193D sub eax,1
00D41940 push eax
00D41941 push 0
# arr有效地址
00D41943 lea ecx,[ebp-30h]
00D41946 push ecx

00D41947 call 00D41280
mergeSort
1
2
3
4
5
6
7
void mergeSort(int arr[], int left, int right) {
00D41C90 push ebp
00D41C91 mov ebp,esp
00D41C93 sub esp,0CCh
00D41C99 push ebx
00D41C9A push esi
00D41C9B push edi
1
2
3
4
5
    if (left < right) {
# 终止条件
00D41CB6 mov eax,dword ptr [ebp+0Ch]
00D41CB9 cmp eax,dword ptr [ebp+10h]
00D41CBC jge 00D41D0F
image-20240622223748067
1
2
3
4
5
6
7
8
9
        int mid = left + ((right - left) >> 1);
# (right - left) >> 1
00D41CBE mov eax,dword ptr [ebp+10h]
00D41CC1 sub eax,dword ptr [ebp+0Ch]
00D41CC4 sar eax,1
# left + ()
00D41CC6 add eax,dword ptr [ebp+0Ch]
# 返回值从栈基址开始写入,此处只用了4个字节
00D41CC9 mov dword ptr [ebp-8],eax
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 和main调用mergeSort一样递归调用
mergeSort(arr, left, mid);
00D41CCC mov eax,dword ptr [ebp-8]
00D41CCF push eax
00D41CD0 mov ecx,dword ptr [ebp+0Ch]
00D41CD3 push ecx
00D41CD4 mov edx,dword ptr [ebp+8]
00D41CD7 push edx
00D41CD8 call 00D41280
00D41CDD add esp,0Ch
mergeSort(arr, mid + 1, right);
00D41CE0 mov eax,dword ptr [ebp+10h]
00D41CE3 push eax
00D41CE4 mov ecx,dword ptr [ebp-8]
00D41CE7 add ecx,1
00D41CEA push ecx
00D41CEB mov edx,dword ptr [ebp+8]
00D41CEE push edx
00D41CEF call 00D41280
00D41CF4 add esp,0Ch
merge
1
2
3
4
5
6
7
void merge(int arr[], int left, int mid, int right) {
00D41A00 push ebp
00D41A01 mov ebp,esp
00D41A03 sub esp,12Ch
00D41A09 push ebx
00D41A0A push esi
00D41A0B push edi
1
2
3
4
5
6
7
8
9
10
11
    int n1 = mid - left + 1;
# 计算左侧数组的长度放到[ebp-8]的位置
00D41A26 mov eax,dword ptr [ebp+10h]
00D41A29 sub eax,dword ptr [ebp+0Ch]
00D41A2C add eax,1
00D41A2F mov dword ptr [ebp-8],eax
int n2 = right - mid;
# 计算右侧数组的长度放到[ebp-20]的位置
00D41A32 mov eax,dword ptr [ebp+14h]
00D41A35 sub eax,dword ptr [ebp+10h]
00D41A38 mov dword ptr [ebp-14h],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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
    // 创建临时数组
int* leftArray = (int*)malloc(n1 * sizeof(int));
# 数组的字节数 长度*4
00D41A3B mov eax,dword ptr [ebp-8]
00D41A3E mov ecx,4
00D41A43 mul eax,ecx
## 了解:防止乘法溢出
00D41A45 mov edx,0FFFFFFFFh
00D41A4A cmovb eax,edx # 条件传送指令之一,该指令成立条件(~CF)
# 保存当前栈顶指针
00D41A4D mov esi,esp
# 调用malloc
00D41A4F push eax
00D41A50 call dword ptr ds:[00D4B170h]
00D41A56 add esp,4
00D41A59 cmp esi,esp
00D41A5B call 00D41262
00D41A60 mov dword ptr [ebp-20h],eax

int* rightArray = (int*)malloc(n2 * sizeof(int));
00D41A63 mov eax,dword ptr [ebp-14h]
00D41A66 mov ecx,4
00D41A6B mul eax,ecx
00D41A6D mov edx,0FFFFFFFFh
00D41A72 cmovb eax,edx
00D41A75 mov esi,esp
00D41A77 push eax
00D41A78 call dword ptr ds:[00D4B170h]
00D41A7E add esp,4
00D41A81 cmp esi,esp
00D41A83 call 00D41262
00D41A88 mov dword ptr [ebp-2Ch],eax

for (int i = 0; i < n1; i++) {
00D41A8B mov dword ptr [ebp-38h],0
00D41A92 jmp 00D41A9D
# L1 (i++)
00D41A94 mov eax,dword ptr [ebp-38h]
00D41A97 add eax,1
00D41A9A mov dword ptr [ebp-38h],eax
# L2 (>=n1跳出)
00D41A9D mov eax,dword ptr [ebp-38h]
00D41AA0 cmp eax,dword ptr [ebp-8]
00D41AA3 jge 00D41ABC
leftArray[i] = arr[left + i];
00D41AA5 mov eax,dword ptr [ebp+0Ch] # left
00D41AA8 add eax,dword ptr [ebp-38h] # left+i
00D41AAB mov ecx,dword ptr [ebp-38h]
00D41AAE mov edx,dword ptr [ebp-20h] # leftArray有效地址
00D41AB1 mov esi,dword ptr [ebp+8] # arr有效地址
# 比例变址寻址,比例因子4为字节,eax,ecx寄存器中保存的是对应index
00D41AB4 mov eax,dword ptr [esi+eax*4] # eax <-arr[left + 1]
00D41AB7 mov dword ptr [edx+ecx*4],eax # leftArray[i] <- eax
}
00D41ABA jmp 00D41A94 # 跳转L1

# 同上
for (int j = 0; j < n2; j++) {
00D41ABC mov dword ptr [ebp-44h],0
00D41AC3 jmp 00D41ACE
00D41AC5 mov eax,dword ptr [ebp-44h]
00D41AC8 add eax,1
00D41ACB mov dword ptr [ebp-44h],eax
00D41ACE mov eax,dword ptr [ebp-44h]
00D41AD1 cmp eax,dword ptr [ebp-14h]
00D41AD4 jge 00D41AF1
rightArray[j] = arr[mid + 1 + j];
00D41AD6 mov eax,dword ptr [ebp-44h]
00D41AD9 mov ecx,dword ptr [ebp+10h]
00D41ADC lea edx,[ecx+eax+1]
00D41AE0 mov eax,dword ptr [ebp-44h]
00D41AE3 mov ecx,dword ptr [ebp-2Ch]
00D41AE6 mov esi,dword ptr [ebp+8]
00D41AE9 mov edx,dword ptr [esi+edx*4]
00D41AEC mov dword ptr [ecx+eax*4],edx
}
00D41AEF jmp 00D41AC5
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*合并数组
回忆归并排序:(由于递归调用,所以每次合并左右数组均认为有序)
1. 左右数组均还有元素,一次比较对应指针索引的值,取小填入,填入的数组对应的索引+1
2. 一侧数组无元素,直接在尾部合并 */
# 情景一
int i = 0, j = 0, k = left;
00D41AF1 mov dword ptr [ebp-50h],0
00D41AF8 mov dword ptr [ebp-5Ch],0
00D41AFF mov eax,dword ptr [ebp+0Ch] # left
00D41B02 mov dword ptr [ebp-68h],eax
while (i < n1 && j < n2) {
# i >= n1跳出
00D41B05 mov eax,dword ptr [ebp-50h]
00D41B08 cmp eax,dword ptr [ebp-8]
00D41B0B jge 00D41B6C
# i >= n2跳出
00D41B0D mov eax,dword ptr [ebp-5Ch]
00D41B10 cmp eax,dword ptr [ebp-14h]
00D41B13 jge 00D41B6C

# branch
if (leftArray[i] <= rightArray[j]) {
00D41B15 mov eax,dword ptr [ebp-50h] # i
00D41B18 mov ecx,dword ptr [ebp-20h] # leftArray的有效地址
00D41B1B mov edx,dword ptr [ebp-5Ch] # j
00D41B1E mov esi,dword ptr [ebp-2Ch] # rightArray的有效地址
00D41B21 mov eax,dword ptr [ecx+eax*4]
00D41B24 cmp eax,dword ptr [esi+edx*4]
00D41B27 jg 00D41B46 # >跳出
arr[k] = leftArray[i];
00D41B29 mov eax,dword ptr [ebp-68h] # left/k
00D41B2C mov ecx,dword ptr [ebp+8] # arr的有效地址
00D41B2F mov edx,dword ptr [ebp-50h] # i
00D41B32 mov esi,dword ptr [ebp-20h] # leftArray的有效地址
00D41B35 mov edx,dword ptr [esi+edx*4]
00D41B38 mov dword ptr [ecx+eax*4],edx
i++;
00D41B3B mov eax,dword ptr [ebp-50h]
00D41B3E add eax,1
00D41B41 mov dword ptr [ebp-50h],eax
}
00D41B44 jmp 00D41B61 # 跳转到k++
else {
arr[k] = rightArray[j];
00D41B46 mov eax,dword ptr [ebp-68h]
00D41B49 mov ecx,dword ptr [ebp+8]
00D41B4C mov edx,dword ptr [ebp-5Ch]
00D41B4F mov esi,dword ptr [ebp-2Ch]
00D41B52 mov edx,dword ptr [esi+edx*4]
00D41B55 mov dword ptr [ecx+eax*4],edx
j++;
00D41B58 mov eax,dword ptr [ebp-5Ch]
00D41B5B add eax,1
00D41B5E mov dword ptr [ebp-5Ch],eax
}

# 修改arr的指针右移
k++;
00D41B61 mov eax,dword ptr [ebp-68h]
00D41B64 add eax,1
00D41B67 mov dword ptr [ebp-68h],eax
}
00D41B6A jmp 00D41B05 # 跳转while循环

# 情景二:复制剩余元素(基本同上)
while (i < n1) {
00D41B6C mov eax,dword ptr [ebp-50h]
00D41B6F cmp eax,dword ptr [ebp-8]
00D41B72 jge 00D41B9A
arr[k] = leftArray[i];
00D41B74 mov eax,dword ptr [ebp-68h]
00D41B77 mov ecx,dword ptr [ebp+8]
00D41B7A mov edx,dword ptr [ebp-50h]
00D41B7D mov esi,dword ptr [ebp-20h]
00D41B80 mov edx,dword ptr [esi+edx*4]
00D41B83 mov dword ptr [ecx+eax*4],edx
i++;
00D41B86 mov eax,dword ptr [ebp-50h]
00D41B89 add eax,1
00D41B8C mov dword ptr [ebp-50h],eax
k++;
00D41B8F mov eax,dword ptr [ebp-68h]
00D41B92 add eax,1
00D41B95 mov dword ptr [ebp-68h],eax
}
00D41B98 jmp 00D41B6C
while (j < n2) {
00D41B9A mov eax,dword ptr [ebp-5Ch]
00D41B9D cmp eax,dword ptr [ebp-14h]
00D41BA0 jge 00D41BC8
arr[k] = rightArray[j];
00D41BA2 mov eax,dword ptr [ebp-68h]
00D41BA5 mov ecx,dword ptr [ebp+8]
00D41BA8 mov edx,dword ptr [ebp-5Ch]
00D41BAB mov esi,dword ptr [ebp-2Ch]
00D41BAE mov edx,dword ptr [esi+edx*4]
00D41BB1 mov dword ptr [ecx+eax*4],edx
j++;
00D41BB4 mov eax,dword ptr [ebp-5Ch]
00D41BB7 add eax,1
00D41BBA mov dword ptr [ebp-5Ch],eax
k++;
00D41BBD mov eax,dword ptr [ebp-68h]
00D41BC0 add eax,1
00D41BC3 mov dword ptr [ebp-68h],eax
}
00D41BC6 jmp 00D41B9A
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 释放临时数组空间(free函数调用,和其他函数调用的流程一样)
free(leftArray);
00D41BC8 mov esi,esp
00D41BCA mov eax,dword ptr [ebp-20h]
00D41BCD push eax
00D41BCE call dword ptr ds:[00D4B174h]
00D41BD4 add esp,4
00D41BD7 cmp esi,esp
00D41BD9 call 00D41262
00D41BDE nop
free(rightArray);
00D41BDF mov esi,esp
00D41BE1 mov eax,dword ptr [ebp-2Ch]
00D41BE4 push eax
00D41BE5 call dword ptr ds:[00D4B174h]
00D41BEB add esp,4
00D41BEE cmp esi,esp
00D41BF0 call 00D41262
00D41BF5 nop
}
1
2
3
4
5
6
7
8
9
10
# 执行完函数后出栈返回
00D41BF6 pop edi
00D41BF7 pop esi
00D41BF8 pop ebx
00D41BF9 add esp,12Ch
00D41BFF cmp ebp,esp
00D41C01 call 00D41262
00D41C06 mov esp,ebp
00D41C08 pop ebp
00D41C09 ret

后记

写了一整个晚上,虽然还有很多没有复习,马上的考试确实很慌,但是这篇博客真的是越写越激动!

回过头来,其实不难发现,整个汇编语言就是一双手能数的过来的常用指令反复的cv,同时也能看出这世上也没有什么尽善尽美,一来编写汇编不是一个优雅的过程,很多反复出现的代码编写;二来,即使是经过这么多年发展的编译器编译出来的汇编指令仍然有许多冗余,也不够优雅。

害!


程序的机器级表示(包含大量IA32-Intel格式汇编代码!!!)
https://blog.potential.icu/2024/06/23/2024-6-23-程序的机器级表示/
Author
Xt-Zhu
Posted on
June 23, 2024
Licensed under