第三章 程序的机器级表示
1.P104\P105
X86 寻址方式经历三代:
-DOS时代的平坦模式,不区分用户空间和内核空间,很不安全
-8086的分段模式
-IA32的带保护模式的平坦模式
2.P106
ISA:机器级程序的格式和行为,定义为指令集体系机构,它定义了处理器状态指令的格式,以及每条指令对状态的影响。
机器级程序使用的存储器地址是虚拟地址,提供的存储器模型看上去是一个非常大的字符数组
PC:程序计数器。在IA32中,用%eip表示,指示将要执行的下一条指令在存储器中的地址。
程序存储器:包含程序的可执行机器代码,操作系统需要的一些信息,用来管理过程调用和返回的运行时栈,以及用户分配的存储器块。
3.P107
64位机上想得到32位代码:gcc -m32 -S xxx.c
编译并产生汇编目标文件xxx.o:gcc -O1 -c xxx.c
获得汇编代码:gcc -S xxx.c -o xxx.s
Ubuntu中获得汇编代码:gcc -S xxx.c更接近教材
教材中获得汇编代码:gcc -O1 -S xxx.c(编译器使用的事第一级优化)
4.P108
二进制文件可用od命令查看,也可以用gdb的x查看:(gdb)x/17xb sum表示检查17个十六进制的字节;
显示代码过多或过少可用more、less结合管道查看,也可以用输出重定向:
od xxx.o | more
od xxx.o > xxx.txt
5.P109
gcc -S 产生的汇编中可以把 以”.“开始的语句都删除了再阅读
6.P110
Linux和windows的汇编格式的区别:
-Intel代码省略了指示大小的后缀,即'l'
-Intel代码省略了寄存器名字前面的‘%’符号,用的是esp,而不是%esp
-Intel代码用不同的方式来描述存储器中位置
-在带有多个操作数的指令情况下,列出操作数的顺序相反
7.P111
-db char 1
-dw short int 2
-dd int 或float 4
-dq long int 或 double 8
8.P112
-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位的不会发生溢出。
多数情况下,前6个寄存器为通用寄存器,最后两个寄存器保存着指向程序栈中重要位置的指针。
另外,字节操作指令可以独立的读或者写前4个寄存器的2个低位字节。
9.P113
操作数的三种类型:立即数(常数值)、寄存器(某个寄存器的内容)、存储器(根据计算出来的地址访问某个存储器位置)
-有效地址的计算方式 Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
10.P114
-MOV:相当于C语言的赋值“=”,将源操作数的值复制到目的操作数中;
MOVS:将一个较小的源数据复制到一个较大的数据位置,高位用位扩展;
MOVZ:将一个较小的源数据复制到一个较大的数据位置,高位用零扩展。
-push:把数据压入栈中;
pop:删除数据。
11.P115\P116
-栈顶元素的地址是所有栈中元素地址中最低的
-栈指针%esp保存栈顶元素的地址。
12.P117
-指针就是地址
-局部变量保存在寄存器中
13.P119
按目的操作数分类:
第一类:加载有效地址。实际是将有效地址写入目的操作数,目的操作数必须是寄存器。
第二类:一元操作。操作数既是源又是目的。可以是寄存器也可以是存储器。
第三类:二元操作。第二个操作数既是源又是目的。但两个操作数不能同时是存储器。
第四类:移位操作。位移量是一个立即数或放在单字节寄存器%cl中。移位操作的目的操作数可以是一个寄存器或是一个存储器位置。
14.P123\P124
有条件跳转(实现if,switch,while,for)
无条件跳转jmp(实现goto)
15.P125
SET指令根据t=a-b的结果设置条件码
16.P127
跳转指令(导致执行切换到程序中一个全新的位置,跳转的目的地通常用一个标号指明)
无条件跳转:JMP 可以是直接跳转也可以是间接跳转(写法是*后面加操作数指示符)
有条件跳转:根据条件码的某个组合,或者跳转或者继续执行下一条指令
17.P130-P145
-条件分支——if-else结构:在两个分支语句中选择执行一个,汇编实现通过goto,就是汇编器为两个分支产生各自的代码块,它会插入条件和无条件分支,以保证能执行正确的代码块。
-循环结构——do-while、while、for:用条件测试和跳转组合实现循环的效果。大多数汇编器根据do-while形式来产生循环代码,其他的循环会首先转换成do-while形式,然后再编译成机器代码。
-switch语句:根据一个整数索引值进行多重分支。通过使用跳转表这种数据结构实现更加高效。跳转表是一个数组,表项i是一个代码段的地址,这个代码段实现当开关索引值为i时程序该做的。此时跳转可以用goto/jmp
18.P149
IA32通过程序栈来实现过程调用
19.P150\P151
call指令\ret指令
函数返回值存在%eax中
20.P174
关于栈帧的gdb命令bt/frame/up/down
命令 |
描述 |
---|---|
backtrace(或bt) |
查看各级函数调用及参数 |
finish |
连续运行到当前函数返回为止,然后停下来等待命令 |
frame(或f) 帧编号 |
选择栈帧 |
info(或i) locals |
查看当前栈帧局部变量的值 |
list(或l) |
列出源代码,接着上次的位置往下列,每次列10行 |
list 行号 |
列出从第几行开始的源代码 |
list 函数名 |
列出某个函数的源代码 |
next(或n) |
执行下一行语句 |
print(或p) |
打印表达式的值,通过表达式可以修改变量的值或者调用函数 |
quit(或q) |
退出 |
set var |
修改变量的值 |
start |
开始执行程序,停在 |
step(或s) |
执行下一行语句,如果有函数调用则进入到函数中 |
break(或b) 行号 |
在某一行设置断点 |
break 函数名 |
在某个函数开头设置断点 |
break ... if ... |
设置条件断点 |
continue(或c) |
从当前位置开始连续运行程序 |
delete breakpoints |
删除断点 |
display 变量名 |
跟踪查看某个变量,每次停下来都显示它的值 |
disable breakpoints |
禁用断点 |
enable 断点号 |
启用断点 |
info(或i)breakpoints |
查看当前设置了哪些断点 |
run(或r) |
从头开始连续运行程序 |
undisplay 跟踪显示号 |
取消跟踪显示 |
watch |
设置观察点 |
info(或i) watchpoints |
查看当前设置了哪些观察点 |
x |
从某个位置开始打印存储单元的内容,全部当成字节来看,而不区分哪个字节属于哪个变量 |
遇到的问题:
-当时学c语言时对if-else\do-while\while\for\switch掌握得就不好,现在还加上了汇编结构,以及有条件跳转和无条件跳转的使用
-CMP和SUB用在什么地方