X86 寻址方式经历三代:
是机器级程序的格式和行为,定义了处理器状态、指令的格式,以及每条指令对状态的影响。
二进制文件可以用od 命令查看,也可以用gdb的x命令查看。 有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看。
在读取地址时要注意是不是小端法,小端法的正确读法是与自然方向相反。
8 位:字节16位:字32位:双字64位:四字
立即数
寄存器
存储器
格式:$后加用标准c表示法表示的整数,如$0xAFF
把一个字节(字)操作数从源SRC传送至目的地DST
指令格式——PUSH r16/m16/seg
如果压入的是双字,栈顶指针-4
指令格式——POP r16/m16/seg
加载有效地址指令——leal
指令形式:从存储器读取数据到寄存器。即将有效地址写入到目的操作数,而目的操作数必须是寄存器;并不真实引用存储器。
只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。
源操作数 目的操作数
第一个操作数可以是立即数、寄存器或者存储器位置第二个操作数可以是寄存器或者存储器位置但是不能同时是存储器位置。
SAL 算术左移SAR 算术右移
SHL 逻辑左移SHR 逻辑右移
有符号数进行算数移位,无符号数逻辑移位
imull,有符号数乘法
双操作数,从两个32位操作数产生一个32位的乘积。
mull,无符号数乘法
idivl有符号除法
divl无符号除法
CF:进位标志ZF:零标志SF:符号标志OF:溢出标志
书上图3-7中的指令除改变寄存器的值外,也会设置条件码;而TEXT指令与CMP指令都只设置条件码
SET指令,通过set与不同的条件码的组合,达到不同的跳转条件。SET指令根据t=a-b的结果设置条件码
跳转指令JUMP 及其编码
jump分为直接跳转和间接跳转:
直接跳转:后面跟标号作为跳转目标间接跳转:*后面跟一个操作数指示符
先执行循环体语句,再执行判断。
GCC的方法是,使用条件分支,表示省略循环体的第一次执行,归根究底,还是要把循环改成do-while的样子,然后用goto翻译。
for循环可以轻易的改成while循环,所以再依照上面的方法改成do-while再翻译。
栈用来传递参数、存储返回信息、保存寄存器,以及本地存储。
本质上栈帧还是栈
关于被调用者Q用栈的几个用处:
1.保存不能存放在寄存器中的局部变量。
2.存放它调用的其他过程的参数。
call
call指令和转移指令相似,同样分直接和间接,直接调用的目标是标号,间接调用的目标是*后面跟一个操作数指示符,和JMP一样。
CALL指令的效果是将返回地址入栈,并跳转到被调用过程的起始处。返回地址是还在程序中紧跟在call后面的那条指令的地址。
ret指从栈中弹出地址,并跳转到这个位置。
这个指令可以使栈做好返回的准备,等价于:
movl %ebp,%esppopl %ebp
当要保存一个值以待以后运算可用的时候,有两种选择:
1.由调用者保存。在调用之前就压进栈。
2.由被调用者保存,在刚被调用的时候就压进栈,并在返回之前恢复。
n是一个正整数,表示只打印栈顶上n层的栈信息。
-n表一个负整数,表示只打印栈底下n层的栈信息。
n是一个从0开始的整数,是栈中的层编号。这个指令的意思是移动到n指定的栈帧中去,并打印选中的栈的信息。如果没有n,则打印当前帧的信息。
表示向栈的上面移动n层,可以不打n,表示向上移动一层。
表示向栈的下面移动n层,可以不打n,表示向下移动一层。
在最开始时,我完全看不懂题目,后来我查看了参考答案,对这道题有了深刻的理解,在图3-3中完全标出了寻址方式。对于寻址方式可以方便记忆,对比图3-3与题3.1其中E表示寄存器、R表示地址栏以及寄存器的值、M[]对应地址栏对应的值。
在此题中,我还是不能很好的区分8位、16位、32位寄存器的表示符号,以及movb\movw\movl的对应字节,这在后来阅读GCC代码有很大的帮助,后来我重复记忆,多看汇编代码,就可以很好的理解了。
在最开始看图3-5时,我完全不能理解popl的操作以及%esp 在此操作中的行为。后来,在看图3-21时,我已充分了解了指针%ebp的作用,然后再回来看这个图就很好理解了。
学完此章节,我觉得3.1-3.6对我理解3.7有很大的帮助,也是重新复习了一下汇编,其中的一些指令有些细微差距,最后的栈帧结构更加深了我对栈操作的理解。