不区分用户空间和内核空间,很不安全html
gcc -01 -o p p1.c
-01:表示使用第一级优化。一般提升优化级别会使最终程序运行得更快,可是编译时间可能会变长,用调试工具对代码进行调试会更困难。(实际中,第二级优化-02被认为是较好的选择)程序员
gcc命令调用一系列程序将源代码转换成可执行代码: - C预处理器 扩展源代码,插入#include命令指定的文件,扩展#define声明指定的宏。(.i) - 编译器 产生两个源代码的汇编代码。(.s) - 汇编器 将汇编代码转化成二进制目标代码(.o) - 链接器 将两个目标代码与实现库函数的代码合并,并产生最终的可执行代码文件。
参考资料3:体系结构-指令集结构编程
计算机系统使用了多种不一样形式的抽象,利用更简单的抽象模型来隐藏实现的细节。对于机器级编程来讲,两种抽象尤其重要:数组
机器级程序的格式和行为,定义为 ISA :指令集体系结构。安全
ISA,定义了处理器状态,指令格式,以及每条指令对状态的影响。 处理器的硬件并发的执行许多指令,可是能够采起措施保证总体行为与ISA指定的顺序整形彻底一致。 解决的问题: 指令的编码方式(即如何编码) 操做数和操做结构的存放位置 数据的类型和大小 支持哪些操做 下一条指令的地址
机器级程序使用的存储器地址是虚拟地址,提供的存储器模型看上去是一个很是大的字节数组。数据结构
一些一般对C语言程序员隐藏的机器代码在IA32中是可见的:并发
指示将要执行的下一条指令在存储器中的地址。less
包含8个命名的位置,分别存储32位的数值,这些寄存器能够存储地址(对应C语言的指针)或整数数据,有的寄存器被用来记录某些重要的程序状态,其余的寄存器用来保存临时数据,例如过程的局部变量和函数的返回值。函数
保存着最近执行的算术或逻辑指令的状态信息,他们用来实现控制或数据流中的条件变化。工具
一组浮点寄存器存放浮点数据
一条机器指令只执行一个很是基本的操做
参考资料4:函数调用过程栈帧变化详解
教材代码:
得到汇编代码:
gcc -S xxx.c -o xxx.s
反汇编:
objdump -d xxx
汇编代码(函数前两条和后两条汇编代码,全部函数都有,创建函数调用栈帧):
前两条: pushl %ebp 将寄存器%ebp的内容压入程序栈 movl %esp,%ebp 获得新栈低,将当前栈顶赋予栈低 后两条: popl %ebp 过程调用结束,恢复旧栈低 ret 子程序的返回指令
注意:
实验楼中:
二进制文件能够用od 命令查看,也能够用gdb的x命令查看。
有些输出内容过多,可使用 more或less命令结合管道查看,也可使用输出重定向来查看。
od code.o | more od code.o > code.txt
字(word) 16位 双字(double words) 32位 四字(quad words) 64位
一个IA32的中央处理器单元包含一组8个存储32位数值的寄存器。全部八个寄存器均可以做为16位(字)或32位(双字)来访问。
存储器,根据计算出来的地址(有效地址)访问某个存储器位置。
操做数格式:
有效地址的计算方式
Imm(Eb,Ei,s) = Imm + R[Eb] + R[Ei]*s
参考资料5:汇编(五)——数据传送指令一
参考资料6:汇编(八)——数据传送类指令三
符号 意义 r8 任意一个8位通用寄存器AH/AL/BH/BL/CH/CL/DH/DL r16 任意一个16通用寄存器AX/BX/CX/DX/SI/DI/BP/SP reg 表明r8或r16 seg 段寄存器CS/DS/ES/SS m8 一个8位存储器操做数单元(包括全部主存寻址方式) m16 一个16位存储器操做数单元(包括全部主存寻址方式) mem 表明m8或m16 i8 一个8位当即数 i16 一个16位当即数 imm 表明i8或i16 dest 目的操做数 src 源操做数
mov指令(指令的第一个是源操做数,第二个是目的操做数)
MOV reg/mem,imm 当即数送寄存器或是存储器 MOV reg/mem/seg,reg 寄存器送寄存器(包括段寄存器)或贮存 MOV reg/seg,mem 主存送寄存器(包括段寄存器) MOV reg/mem,seg 段寄存器送主存或寄存器
IA32的限制:两个操做数都不能指向存储器。
不能从内存地址直接MOV到另外一个内存地址,要用寄存器中转一下。
push与pop
栈向下增加,栈顶元素的地址是全部栈中元素地址中最低的。栈指针%esp保存栈顶元素的地址。
进栈指令PUSH: PUSH reg/mem/seg SP←SP-2,SS←reg/mem/seg - 进栈指令先使堆栈指令SP减2,而后把一个字操做数存入堆栈顶部。 - 堆栈操做的对象只能是字操做数。 - 进栈时,底字节存放于低地址,高字节存放在高地址,SP相应向低地址移动两个字节单元。 出栈指令POP: POP reg/seg/mem reg/seg/mem←SS:[SP],SP←SP+2 - 出栈指令把栈顶的一个字传送至指定的目的操做数,而后堆栈指针SP加2。 - 目的操做数应为字操做数。 - 字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
是movl指令的变形
一元操做
- INC 加1 - DEC 减1 - NEG 取负 - NOT 取补
二元操做
- ADD 加 - SUB 减 - IMUL 乘 - XOR 异或 - OR 或 - AND 与
注意操做的顺序:
第二个操做数 操做符 第一个操做数
先给出移位量,第二项给出要移位的数值。
- SAL 左移 - SHL 左移(等同于SAL) - SAR 算术右移 - SHR 逻辑右移
目的操做数:一个寄存器或是一个存储器位置。
乘积截断
imull 双操做数 - 从两个32位操做数产生一个32位的乘积。
乘积不截断
mull 无符号数乘法 imull 有符号数乘法 - 要求一个参数必须在寄存器%eax中,另外一个做为指令的源操做数给出。 - 乘积的高32位在%edx中,低32位在%eax中。
有符号除法
idivl 操做数 - 将DX:AX中的64位数做为被除数,操做数中为除数 - 结果:商在AX中,余数在DX中。
无符号除法
divl指令 - 一般会事先设定寄存器%edx为0.
经常使用条件码:
比较和测试指令:不修改任何寄存器的值,只设置条件码
比较指令cmp和减法指令sub有何不一样? - sub d,s 是d-s,结果送回d中,即送回目的操做数中。 - cmp d,s 也是d-s,但结果不送回目的操做数中,是利用减法进行两个数值的比较。
根据条件码的某个组合,将一个字节设置为0或1。
SET指令:执行比较指令,根据计算t=a-b的结果设置条件码
能够有条件的传送数据
间接跳转:跳转目标是从寄存器或存储器位置中读出的。
例: jmp *%eax 用寄存器%eax中的值做为跳转目标。 jmp *(%eax) 以%eax中的值做为读地址,从存储器中读出跳转目标。
一些底层的机器指令有多个名字,条件跳转只能是直接跳转。
通用形式模板
if(test-expr) then-statement else else-statement (注:test-expr 整数表达式[假/真])
汇编实现形式
t = test-expr; if (!t) goto false; then-statement goto done; false: else-statement done:
通用形式:
do body-statement while(test-expr);
翻译成以下条件和goto语句:
loop: body-statement t = test-expr; if(t) goto loop;
通用形式:
while (test-expr) body-statement
转换成 do-while 形式:
if(!test-expr) goto done; do body-statement while(test-expr); done:
翻译成 goto 形式:
t = test-expr; if(!t) goto done: loop: body-statement t = test-expr; if(t) goto loop; done:
通用形式
for(init-expr;test-expr;update-expr) body-statement
同 while:
init-expr; while(test-expr){ body-statement update-expr; }
对应 do-while 形式:
init-expr; if(!test-expr) goto done; do{ body-statement update-expr; }while(test-expr); done;
转换成 goto 代码:
init-expr t = test-expr; if(!t) goto done: loop: body-statement t = test-expr; if(t) goto loop; done:
栈帧: 为单个过程分配的那部分栈
最顶端的栈帧以两个指针界定:
- 寄存器%ebp为帧指针 - 寄存器%esp为栈指针
栈向低地址方向增加,栈指针%esp指向栈顶元素:
- 栈指针值适当减少能够分配没有指定初始值的数据的空间 - 相似的,能够经过增长栈指针来释放空间
支持过程调用和返回的指令:
效果:将返回地址入栈,并跳转到被调用过程的起始处。
使用这个指令时,栈指针要指向前面call指令存储返回地址的位置。
等价于:
movl %ebp,%esp popl %ebp
惯例:
- %eax,%edx,%ecx 划分为调用者保存寄存器 - %ebx,%esi,%edi 划分为被调用者保存寄存器 - %ebp,%esp 保持寄存器 - %eax 保存函数返回值
main函数保存%ebp,并设置新的帧指针。
pushl %ebp movl %esp,%ebp
分配4字节的栈空间
subl $4,%esp
设置 arg1=8
movl $8,(%esp)
将(%esp)中的8给 %eax,即存入栈中
movl %eax,(%esp)
将 %eax 与当即数 3 相加
add $3,%eax
在gh结束前弹栈
popl %ebp
main继续 %eax 加1的操做
addl $1,%eax
leave为返回准备栈,至关于%ebp出栈,最后ret结束。
参考资料1:深刻理解计算机系统(第二版)
参考资料2:教材导读与每周考试重点---不断更新
参考资料3:体系结构-指令集结构
参考资料4:函数调用过程栈帧变化详解
参考资料5:汇编(五)——数据传送指令一
参考资料6:汇编(八)——数据传送类指令三
参考资料7:20135202闫佳歆——信息安全系统设计基础第四周学习总结