学习计时:共11小时
读书:5小时
代码:2小时
作业:3小时
博客:1小时
86 寻址方式经历三代:
1 DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
2 8086的分段模式
3 IA32的带保护模式的平坦模式
程序编码
命令gcc指的是GCC C编译器
-01 使用第一级优化(提高优化级别会使最终程序运行更快,但编译时间可能会变长)
第二级优化被认为是较好的选择,-o 表示将p1.c编译后的可执行文件命名为p。
执行汇编命令:gcc –s xxx.c –o xxx.s
解释:预处理器——扩展源代码-生成.i文件编译器——产生两个源代码的汇编代码-——生成.s文件汇编器——将汇编代码转化成二进制目标代码——生成.o文件
机器级程序使用的存储器地址是虚拟地址,存储器系统的实际实现是将多个硬件存储器和操作系统软件组合起来。
可见的处理器状态:程序计数器(PC),整数寄存器文件,条件码寄存器,浮点寄存器
gcc -S xxx.c -o xxx.s 获得汇编代码,也可以用objdump -d xxx 反汇编; 注意函数前两条和后两条汇编代码,所有函数都有
注意:
64位机器上想要得到32代码:gcc -m32 -S xxx.c
MAC OS中没有objdump, 有个基本等价的命令otool
Ubuntu中 gcc -S code.c (不带-O1)产生的代码更接近教材中代码(删除"."开头的语句)
二进制文件可以用od 命令查看,也可以用gdb的x命令查看。
有些输出内容过多,我们可以使用 more或less命令结合管道查看,也可以使用输出重定向来查看
od code.o | more
od code.o > code.txt
gcc -S 产生的汇编中可以把以"."开始的语句都删除了再阅读
Linux和Windows的汇编格式的区别
数据格式:
esi edi可以用来操纵数组,esp ebp用来操纵栈帧。
对于寄存器,特别是通用寄存器中的eax,ebx,ecx,edx,大家要理解32位的eax,16位的ax,8位的ah,al都是独立的。
例题:假定当前是32位x86机器,eax寄存器的值为0x8226,执行完addw $0x8266, %ax指令后eax的值是多少?
解析:0x8226+0x826=0x1044c, ax是16位寄存器,出现溢出,最高位的1会丢掉,剩下0x44c,不要以为eax是32位的不会发生溢出.此答案是本次学习的参照
esiedi可以用来操纵数组,espebp用来操纵栈帧。多数情况下,前6个寄存器为通用寄存器,最后两个寄存器保存着指向程序栈中重要位置的指针。另外,字节操作指令可以独立的读或者写前4个寄存器的2个低位字节。
结合表,深入理解各种寻址方式;理解操作数的三种类型:立即数、寄存器、存储器;掌握有效地址的计算方式 Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
MOV相当于C语言的赋值"=",注意ATT格式中的方向,另外注意不能从内存地址直接MOV到另一个内存地址,要用寄存器中转一下。
MOVS和MOVZ指令类都是将一个较小的源数据复制到一个较大的数据位置,高位用符号位扩展(MOVS)或者零扩展(MOVZ)进行填充。
注意栈顶元素的地址是所有栈中元素地址中最低的
指针就是地址;局部变量保存在寄存器中
C语言中所谓的指针就是地址。间接引用指针是将该指针放在寄存器中然后在存储器引用中使用这个寄存器。局部变量通常保存在寄存器中。寄存器访问要比存储器快很多。
一元操作:只有一个操作数,既是源又是目的,可以是一个寄存器,或者存储器位置。
二元操作:第一个操作数可以是立即数、寄存器或者存储器位置第二个操作数可以是寄存器或者存储器位置但是不能同时是存储器位置。
移位:第一个是移位量,用单个字节编码(只允许0-31位的移位),可以是立即数或者放在单字节寄存器%cl中,算术右移SAR,填上符号位;逻辑右移SHR,填上0。目的操作数可以是一个寄存器或存储器。
有条件跳转(实现if,switch,while,for)无条件跳转jmp(实现goto)
汇编中用条件测试和跳转组合实现循环的效果。大多数汇编器根据do-while形式来产生循环代码,其他的循环会首先转换成do-while形式,然后再编译成机器代码。
Switch:根据一个整数索引值进行多重分支。通过使用跳转表这种数据结构实现更加高效。跳转表是一个数组,表项i是一个代码段的地址,这个代码段实现当开关索引值为i时程序该做的。
这里的跳转可以用到goto/jmp
for循环可以轻易的改成while循环,所以再依照上面的方法改成do-while再翻译即可。
条件码的改变:
数据传送指令
MOV 不影响标志位
PUSH POP 不影响标志位
XCHG 交换指令不影响标志位
XLAT 换码指令不影响标志位
LEA 有效地址送寄存器指令不影响标志位
PUSHF 标志进栈指令不影响标志位
POPF 标志出栈指令标志位由装入值决定