汇编下的寻址方式

寻址方式

要想了解寻址方式,首先得对计算机存储体系有一定了解

寄存器

cache

主存

辅存

网络

从上至下,存储速度由快变慢,存储容量由小变大

寄存器寻址模式

类型 格式 操作数值 名称
立即数 $Imm Imm 立即数寻址
寄存器 ra R[ra] 寄存器寻址
存储器 Imm M[Imm] 绝对寻址
存储器 (ra) M[R[ra]] 间接寻址
存储器 Imm(rb) M[Imm+R[rb]] (基址 + 偏移量)寻址
存储器 (rb,ri) M[R[ra]+R[ri]] 变址寻址
存储器 Imm(rb,ri) M[lmm+R[rb]+R[ri]] 变址寻址
存储器 (,ri,s) M[R[ri]*s] 比例变址寻址
存储器 Imm(,ri,s) M[Imm+R[ri]*s] 比例变址寻址
存储器 (rb,ri,s) M[R[rb]+R[ri]*s] 比例变址寻址
存储器 Imm(rb,ri,s) M[Imm+R[rb]+R[ri]*s] 比例变址寻址

指令的类型可以是立即数(i),寄存器(R),内存(M)。

  • 立即数(immediate),在汇编中直接以常数的形式存在。

  • 寄存器(register),存在某个寄存器的内容。

  • 内存引用,他会根据计算出来的地址(通常称为有效地址)访问某个内存位置。

表中最后一种比例变址寻址是常见形式,比较适合引用数组和结构元素。
这样的引用有四个部分组成:一个立即数Imm,一个基址寄存器rb ,一个变址寄存器ri和一个比例因子s,s必须是1、2、4或者8。

例子

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "stdafx.h"
#include<iostream>
int add(int a, int b)
{
return (a+b);
}
int main()
{
int a =5;
int b = 10;
int c[5] = { 1,2,3,4,5 };
int *a1 = &a;
int *b1 = &b;
int *c1=c;
add(5,10);
add(a, b);
for (size_t i = 0; i < sizeof(c) / sizeof(c[0]); i++)
{
printf("%d", c[i]);
}
return 0;
}

加上断点,进入调试。截取其中一段反汇编代码

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
	int a = 5;
01021878 mov dword ptr [a],5
int b = 10;
0102187F mov dword ptr [b],0Ah
int c[5] = { 1,2,3,4,5 };
01021886 mov dword ptr [c],1
0102188D mov dword ptr [ebp-30h],2
01021894 mov dword ptr [ebp-2Ch],3
0102189B mov dword ptr [ebp-28h],4
010218A2 mov dword ptr [ebp-24h],5
int *a1 = &a;
010218A9 lea eax,[a]
010218AC mov dword ptr [a1],eax
int *b1 = &b;
010218AF lea eax,[b]
010218B2 mov dword ptr [b1],eax
int *c1=c;
010218B5 lea eax,[c]
010218B8 mov dword ptr [c1],eax
add(5,10);
010218BB push 0Ah
010218BD push 5
010218BF call add (0102127Bh)
010218C4 add esp,8
add(a, b);
010218C7 mov eax,dword ptr [b]
010218CA push eax
010218CB mov ecx,dword ptr [a]
010218CE push ecx
010218CF call add (0102127Bh)
010218D4 add esp,8
for (size_t i = 0; i < sizeof(c) / sizeof(c[0]); i++)
010218D7 mov dword ptr [ebp-64h],0
010218DE jmp main+99h (010218E9h)
010218E0 mov eax,dword ptr [ebp-64h]
010218E3 add eax,1
010218E6 mov dword ptr [ebp-64h],eax
010218E9 cmp dword ptr [ebp-64h],5
010218ED jae main+0B6h (01021906h)
{
printf("%d", c[i]);
010218EF mov eax,dword ptr [ebp-64h]
{
printf("%d", c[i]);
010218F2 mov ecx,dword ptr c[eax*4]
010218F6 push ecx
010218F7 push offset string "%d" (01026B30h)
010218FC call _printf (01021325h)
01021901 add esp,8
}
01021904 jmp main+90h (010218E0h)
return 0;
01021906 xor eax,eax
}

我们可以看到

1
2
3
4
int a = 5;
01021878 mov dword ptr [a],5
int b = 10;
0102187F mov dword ptr [b],0Ah

mov表示传送数据
dword 双字 就是四个字节
ptr pointer缩写 即指针
[]里的数据是一个地址值,这个地址指向一个双字型数据
5就是一个立即数,立即数引用
而a在这里可以理解成一个地址(变量名a可以理解成你给这块空间地址取的别名),就是直接引用

1
2
3
4
5
6
	int c[5] = { 1,2,3,4,5 };
01021886 mov dword ptr [c],1
0102188D mov dword ptr [ebp-30h],2
01021894 mov dword ptr [ebp-2Ch],3
0102189B mov dword ptr [ebp-28h],4
010218A2 mov dword ptr [ebp-24h],5

ebp是一个通用寄存器,一般保存被调用的数据

把4放到 [ebp-30h]就是变址寻址,Imm(rb,ri)的形式

我们知道,数组是一段连续的内存空间,这五条的偏移量的差是28h-24h,即为4;而一个int数据是4个字节,为什么这样就一目了然

1
2
3
4
5
6
printf("%d", c[i]);
010218F2 mov ecx,dword ptr c[eax*4]
010218F6 push ecx
010218F7 push offset string "%d" (01026B30h)
010218FC call _printf (01021325h)
01021901 add esp,8

mov ecx,dword ptr c[eax*4]

c[3]=4,c做首地址,寄存器间接寻址,ECX作为间址寄存器