教材学习内容总结
本周的学习我在休学前也学习过。。以下时链接。
http://www.cnblogs.com/20145237fhn/p/5958605.html
http://www.cnblogs.com/20145237fhn/p/5967802.html
第三章 程序的机器级表示
第一节 历史观点
X86 寻址方式经历三代:
•1 DOS时代的平坦模式(不区分用户空间和内核空间,很不安全)
•2 8086的分段模式
•3 IA32的带保护模式的平坦模式
第二节 程序编码
gcc -S xxx.c -o xxx.s 获得汇编代码,也可以用objdump -d xxx 反汇编
64位机器上想要得到32代码:gcc -m32 -S xxx.c
机器级编程的两种抽象
(1)指令集结构ISA
是机器级程序的格式和行为,定义了处理器状态、指令的格式,以及每条指令对状态的影响。
(2)机器级程序使用的存储器地址是虚拟地址
处理器:
•程序计数器(CS:IP)
•整数寄存器(AX,BX,CX,DX)
•条件码寄存器(OF,SF,ZF,AF,PF,CF)
•浮点寄存器
二、代码示例
二进制文件可以用od 命令查看,也可以用gdb的x命令查看。 有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看。
在读取地址时要注意是不是小端法,小端法的正确读法是与自然方向相反。
第三节 数据格式
8 位:字节16位:字32位:双字64位:四字
数据传送指令的三个变种:
•movb 传送字节
•movw 传送字
•movl 传送双字
第四节 访问信息
操作数的三种类型
立即数
寄存器
存储器
寻址方式
(1)立即数寻址方式
格式:$后加用标准c表示法表示的整数,如$0xAFF
(2)寄存器寻址方式
(3)存储器寻址方式
•直接寻址方式
•寄存器间接寻址方式
•寄存器相对寻址方式
•基址变址寻址方式
•相对基址变址寻址方式
数据传送指令
mov指令
把一个字节(字)操作数从源SRC传送至目的地DST
IA32的限制:两个操作数都不能指向存储器。
此外:
•movb 传送字节
•movw 传送字
•movl 传送双字
•movs 符号位扩展
•movz 零扩展
push与pop(后进先出)
压栈push
指令格式——PUSH r16/m16/seg
如果压入的是双字,栈顶指针-4
出栈pop
指令格式——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分为直接跳转和间接跳转:
直接跳转:后面跟标号作为跳转目标间接跳转:*后面跟一个操作数指示符
循环
do-while循环
先执行循环体语句,再执行判断。
while循环
GCC的方法是,使用条件分支,表示省略循环体的第一次执行,归根究底,还是要把循环改成do-while的样子,然后用goto翻译。
for循环
for循环可以轻易的改成while循环,所以再依照上面的方法改成do-while再翻译。
第七节 过程
栈帧结构
栈用来传递参数、存储返回信息、保存寄存器,以及本地存储。
本质上栈帧还是栈
栈帧的两个指针:
寄存器%ebp-帧指针
寄存器%esp-栈指针
关于被调用者Q用栈的几个用处:
1.保存不能存放在寄存器中的局部变量。
2.存放它调用的其他过程的参数。
转移控制
call
call指令和转移指令相似,同样分直接和间接,直接调用的目标是标号,间接调用的目标是*后面跟一个操作数指示符,和JMP一样。
CALL指令的效果是将返回地址入栈,并跳转到被调用过程的起始处。返回地址是还在程序中紧跟在call后面的那条指令的地址。
ret
ret指从栈中弹出地址,并跳转到这个位置。
leave
这个指令可以使栈做好返回的准备,等价于:
movl %ebp,%esppopl %ebp
寄存器使用惯例
当要保存一个值以待以后运算可用的时候,有两种选择:
1.由调用者保存。在调用之前就压进栈。
2.由被调用者保存,在刚被调用的时候就压进栈,并在返回之前恢复。
查看函数调用栈信息的GDB命令
Ÿ backtrace/bt n
n是一个正整数,表示只打印栈顶上n层的栈信息。
-n表一个负整数,表示只打印栈底下n层的栈信息。
Ÿ frame n
n是一个从0开始的整数,是栈中的层编号。这个指令的意思是移动到n指定的栈帧中去,并打印选中的栈的信息。如果没有n,则打印当前帧的信息。
Ÿ up n
表示向栈的上面移动n层,可以不打n,表示向上移动一层。
Ÿ down n
表示向栈的下面移动n层,可以不打n,表示向下移动一层。
实验楼中练习的实现
使用 gcc –S –o main.s main.c -m32
编译
c语言代码如下
编译后
删除gcc产生代码中以"."开头的编译器指令
实验楼代码的反汇编分析
g:
pushl %ebp //保存现场,将父函数的栈底寄存器存入当前程序栈中
movl %esp, %ebp //构建当前函数堆栈
movl 8(%ebp), %eax //从父函数堆栈中取得参数,存入ax寄存器
addl $3, %eax //完成+3操作
popl %ebp //恢复原父函数堆栈
ret //g函数所占用的栈帧“消失”
f:
pushl %ebp //保存现场,将父函数的栈底寄存器存入当前程序栈中
movl %esp, %ebp //构建当前函数堆栈
pushl 8(%ebp) //压栈内存空间
call g //调用g
addl $4,%esp //数据压入堆栈,栈顶指针esp减少4字节
leave //清理局部变量空间
ret //返回,f函数所占用的栈帧“消失”
main:
pushl %ebp //保存现场,将父函数的栈底寄存器存入当前程序栈中
movl %esp, %ebp //构建当前函数堆栈
pushl $8 //压栈
call f //调用f
addl $4,%esp //数据压入堆栈,栈顶指针esp减少4字节
addl $1, %eax //完成+1操作`
leave //清理局部变量空间
ret //返回,main函数所占用的栈帧“消失”
对每条指令画出相应栈帧的情况
代码调试中的问题和解决过程
休学前学习时出现的问题在此次学习过程中我十分注意,没有出现。
代码托管
其他(感悟、思考等,可选)
休学前进行过的学习到本周就结束了,之前因为有过学习,虽然没有学的很好,但是还是有一些印象,所以学起来不是很困难,之后的学习没有了之前的基础,再加上我本身编程方面较为薄弱,需要投入更多的时间来学习,有不会的需要多请教学习好的同学才行。