【汇编语言与计算机系统结构笔记06】地址计算指令,lea / leal,x86-32与x86-64下的swap对比,汇编的格式对比(Intel/Microsoft Differs from GAS)
2021/6/22 17:29:30
本文主要是介绍【汇编语言与计算机系统结构笔记06】地址计算指令,lea / leal,x86-32与x86-64下的swap对比,汇编的格式对比(Intel/Microsoft Differs from GAS),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
本次笔记内容:
07.寻址模式与数据传输指令等-2
文章目录
- 变址寻址
- 寻址模式实例
- 总结mov指令
- 地址计算指令 lea
- 整数计算指令
- 将leal指令用于计算
- 实例1
- 实例2
- x86-32与x86-64的数据类型宽度
- x86-64的通用寄存器
- x86-32与x86-64下的swap对比
- 小结:x86指令的特点
- 扩展:x86汇编的格式
- Intel/Microsoft Format
- AT&T Format
- Intel/Microsoft Differs from GAS
- 练习题
- 答疑:movl与leal中地址表达式
变址寻址
承接上次笔记内容,对变址寻址进行讨论:
D(Rb, Ri, S) : Mem[Reg[Rb] + S * Reg[Ri]]
- D为常量(地址偏移量);
- Rb为基址寄存器:8个通用寄存器其之一;
- Ri为索引寄存器:%esp不作为索引寄存器,一般%ebp也不做这个用途;
- S为比较因子1, 2, 3或8。
其他变形还有:
(Rb, Ri) : Mem[Reg[Rb] + Reg[Ri]] D(Rb, Ri) : Mem[Reg[Rb] + Reg[Ri] + D] (Rb, Ri, S) : Mem[Reg[Rb] + S * Reg[Ri]]
寻址模式实例
首先,已知%edx = 0xf000,%ecx = 0x100.
Expression | Computation | Address |
---|---|---|
0x8(%edx) | 0xf000 + 0x8 | 0xf008 |
(%edx, %ecx) | 0xf000 + 0x100 | 0xf100 |
(%edx, %ecx, 4) | 0xf000 + 4*0x100 | 0xf400 |
0x80 (, %edx, 2) | 2*0xf000 + 0x80) | 0x1e080) |
总结mov指令
MOV S, D 表示将S移动到D(在AT&T汇编格式中):
- movb:传送字节;
- movw:传送字;
- movl:传送双字。
MOVS S, D 表示将符号扩展的S移动到D(在AT&T汇编格式中):
- movsbw:将做了符号扩展的字节传送到字;
- movsbl:将做了符号扩展的字节传送到双字;
- movswl:将做了符号扩展的字传送到双字。
MOVZ S, D 表示将零扩展的S移动到D(在AT&T汇编格式中):
- movzbw:将做了零扩展的字节传送到字;
- movzbl:将做了零扩展的字节传送到双字;
- movzwl:将做了零扩展的字传送到双字。
pushl S 表示将双字压栈;popl D 表示将双字出栈。之后的课程会具体讨论。
地址计算指令 lea
leal Src, Dest
- Src是地址计算表达式;
- 计算出来的地址赋给Dest。
使用实例:
- 地址计算(无需访存)如:translation of p = %x[i];
- 进行x + k * y这一类型的整数运算:k = 1, 2, 4 or 8。
整数计算指令
双操作数指令:
Format | Computation |
---|---|
addl Src, Dest | Dest = Dest + Src |
subl Src, Dest | Dest = Dest - Src |
imull Src, Dest | Dest = Dest * Src |
sall Src, Dest | Dest = Dest << Src 与shll等价 |
sarl Src, Dest | Dest = Dest >> Src 算数右移 |
shrl Src, Dest | Dest = Dest >> Src 逻辑右移 |
xorl Src, Dest | Dest = Dest ^ Src 异或 |
andl Src, Dest | Dest = Dest & Src |
orl Src, Dest | Dest = Dest |
单操作数指令:
Format | Computation |
---|---|
incl Dest | Dest = Dest + 1 |
decl Dest | Dest = Dest - 1 |
negl Dest | Dest = - Dest |
notl Dest | Dest = ~ Dest |
将leal指令用于计算
实例1
int arith(int x, int y, int z) { int t1 = x + y; int t2 = z + t1; int t3 = x + 4; int t4 = y * 48; int t5 = t3 + t4; int rval = t2 * t5; return rval; }
arith: pushl %ebp movl %esp, %ebp # codes above are for Set up movl 8(%ebp), %eax movl 12(%ebp), %edx leal (%edx, %eax), %ecx leal (%edx, %edx, 2), %edx sall $4, %edx addl 16(%ebp), %ecx leal 4(%edx, %eax), %eax imull %ecx, %eax # codes above are for Body movl %ebp, %esp popl %ebp ret # Finish
在进入Body之前,获得如下结构:
其中,ebp表示基址。栈中,返回地址为ebp + 4,x、y、z分别也被压入栈中(符合函数调用传参规范)。
1 movl 8(%ebp), %eax # eax = x 2 movl 12(%ebp), %edx # edx = y 3 leal (%edx, %eax), %ecx # ecx = x + y 4 leal (%edx, %edx, 2), %edx # edx = 3 * y 5 sall $4, %edx # edx = 48 * y (t4) 6 addl 16(%ebp), %ecx # ecx = z + t1 (t2) 7 leal 4(%edx, %eax), %eax # eax = 4 + t4 + x (t5) 8 imull %ecx, %eax # eax = t5 * t2 (rval)
对于第3条指令,用lea指令将x+y值放在ecx中。相当于c语言过程的局不变量。如果寄存器够用,将局部变量放在寄存器中,否者放在栈中。
对于第4、5条指令,首先乘3,已达到最终乘以48的效果:
- 如何乘3?即将edx加edx乘以2,放在edx中,以实现edx乘以3。
- 之后,左移4位,即乘16。
注意到,第3条指令计算t1,第4、5条t4,第6条t2,第7条t5,与c中的顺序并不一致。
实例2
int logical(int x, int y) { int t1 = x^y; int t2 = t1 >> 17; int mask = (1<<13) - 7; int t4 = y * 48; int rval = t2 & mask; return rval; }
logical: pushl %ebp movl %esp, %ebp # codes above are for Set up movl 8(%ebp), %eax xorl 12(%ebp), %eax sarl $17, %eax andl $8185, %eax # codes above are for Body movl %ebp, %esp popl %ebp ret # Finish
只看Body,先不讨论栈的分配与回收:
1 movl 8(%ebp), %eax eax = x 2 xorl 12(%ebp), %eax eax = x ^ y (t1) 3 sarl $17, %eax eax = t1 >> 17 (t2) 4 andl $8185, %eax eax = t2 & 8185
- 第2行,进行异或运算;
- 第3行,t1是一个带符号数,因此右移使用sarl进行算数右移,而非逻辑右移;
- 2 13 − 7 = 8185 2^{13} - 7 = 8185 213−7=8185,编译器将常数mask计算了出来,而非交给机器去做。
c中,return一个32位整型,放在eax中;如果32位中return一个long long类型(64位),放在edx + eax中。如果在64位机器中,则直接放在rax中。
x86-32与x86-64的数据类型宽度
Size of C Objects (in Bytes)
C Data Type | Typical 32-bit | Intel IA32 | x86-64 |
---|---|---|---|
unsigned | 4 | 4 | 4 |
int | 4 | 4 | 4 |
long int | 4 | 4 | 8 |
char | 1 | 1 | 1 |
short | 2 | 2 | 2 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
long double | 8 | 10/12 | 16 |
char * | 4 | 4 | 8 |
如果返回数在32位及以下,那么一个eax就够了。
x86-64的通用寄存器
64位进行了扩展:
- 一方面,扩展现有的,增加了8个新的寄存器;
- 另一方面,寄存器宽度扩展了,但eax等名称还可以继续使用;
- %ebp/%rbp不再是专用寄存器。
x86-32与x86-64下的swap对比
void swap (int *xp, int *yp) { int t0 = *xp; int t1 = *yp; *xp = t1; *yp = t0; }
32位下:
pushl %ebp movl %esp, %ebp pushl %ebx // 以上为Set Up movl 12(%ebp), %ecx movl 8(%ebp), %edx movl (%ecx), %eax movl (%edx), %ebx movl %eax, (%edx) movl %ebx, (%ecx) // 以上为Body movl -4(%ebp), %ebx movl %ebp, %esp popl %ebp ret // Finsih
将两个地址取出,放在ecx与edx中,再将ecx与edx的地址的数取出,放在eax和ebx中,之后在eax和ebx中的数取出,放在edx与ecx中,即可返回。
而64位下:
movl (%rdi), %edx movl (%rsi), %eax movl %eax, (%rdi) movl %edx, (%rsi) retq
注意到64位中该功能非常简洁,可以注意到64位情况下传参是用过寄存器来传递的,而32位中寄存器不够用,参数用栈Stack来传递。
64位中,减少访问内存,使用寄存器来传参数。
但最多传6个参数,超过6个部分,依此从右往左压入栈中。
注意到上例中,被操作数仍然是32位,因此使用寄存器%eax和%edx,以及movl指令。
而x86-64下long int类型的swap如下。
void swap_l (long int *xp, long int *yp) { long int t0 = *xp; long int t1 = *yp; *xp = t1; *yp = t0; }
汇编代码如下:
swap_: movq (%rdi), %rdx movq (%rsi), %rax movq %rax, (%rdi) movq %rdx, (%rsi) retq
被操作数是64位:
- 所以使用寄存器%rax与%rdx;
- 以及movq指令,q表示4字。
小结:x86指令的特点
- 支持多种类型的指令操作数,包括立即数、寄存器和内存数据;
- 算逻辑指令可以以内存数据作为操作数;
- 支持多种内存地址计算模式,包括Rb + S * Ri + D,也可用于整数计算如leal指令;
- 变成指令from 1 to 15 bytes。
扩展:x86汇编的格式
Intel/Microsoft Format
(-masm = intel, Intel 语法)
lea eax, [ecx + ecx * 2] sub esp, 8 cmp dword ptr [ebp-8], 0 mov eax, dword ptr [eax * 4 + 100h]
AT&T Format
leal (%ecx + %ecx * 2), %eax subl $8, %esp cmpl $0, -8(%ebp) movl $0x100 (, %eax, 4), %eax
Intel/Microsoft Differs from GAS
- Operands listed in opposite order
-
- mov Dest, Src
-
- movl Src, Dest
- Constants not preceded by ‘$’, Denote hex with ‘h’ at end
-
- 100h
-
- $0x100
- Operand size indicated by operands rather than operator suffix
-
- sub
-
- subl
- Addressing format shows effective address computation
-
- [eax * 4 + 100]
-
- $0x100 (, %eax, 4)
练习题
一个函数的原型为:
int decode2(int x, int y, int z);
汇编代码为:
movl 16(%ebp), %edx subl 12(%ebp), %edx movl %edx, %eax sall $15, %eax sarl $15, %eax xorl 8(%ebp), %edx imull %edx, %eax
另外,有:
x at %ebp + 8, y at %ebp + 12, z at %ebp + 16
参数x、y和z存放在存储器中相对于寄存器%ebp中地址偏移量为8、12和16的地方,代码将返回值放在寄存器%eax中,写出等价于汇编代码的decode2的c代码。
答案为:
int decode2(int x, int y, int z) { int t1 = z - y; int t2 = (t1 << 15) >> 15; int t3 = x ^t1; int t4 = t2 * t1; return t4;
答疑:movl与leal中地址表达式
movl中出现地址表达式是用要去访问相应内存的,而leal中是用于算数的。
这篇关于【汇编语言与计算机系统结构笔记06】地址计算指令,lea / leal,x86-32与x86-64下的swap对比,汇编的格式对比(Intel/Microsoft Differs from GAS)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享