关于汇编的那些事
$ 和 $$
地址只是数字,描述各种符号在源程序中的位置,他是源代码文件中各符号偏移文件开头的距离
编译器给程序中各符号(变量名或者函数名等)分配的地址,就是各符号相对于文件开头的偏移量
**$ 和 $$**是编译器NASM预留的关键字,用来表示当前行和本section的地址,起到了标号的作用,他是NASM提供的,并不是CPU原生支持的,相当于伪指令一样,对CPU来说是假的,默认情况下,它们的值相对于本文件开头的偏移量
伪指令是相对于CPU可识别的指令来说的,它只是编译器定义的,CPU中并不存在这个指令,它是编译器为了开发人员写代码而提供的一些符号,这些符号在编译时,会由编译器转换成CPU可识别的东西,如指令或者地址等
属 于 “ 隐 式 地 “ 藏 在 本 行 代 码 前 的 标 号 , 也 就 是 编 译 器 给 当 前 行 安 排 的 地 址 , 只 有 ” 显 示 地 “ 用 了 属于“隐式地“藏在本行代码前的标号,也就是编译器给当前行安排的地址,只有”显示地“用了 属于“隐式地“藏在本行代码前的标号,也就是编译器给当前行安排的地址,只有”显示地“用了的地方,nasm编译器才会将此行的地址公布出来。
**$$**指代本section的起始地址,此地址同样是编译器给安排的
vstart关键字可以影响编译器安排地址的行为,如果该section用了vstart=XXXX修饰,$ 的 值 则 是 此 s e c t i o n 的 虚 拟 起 始 地 址 X X X X , 的值则是此section的虚拟起始地址XXXX, 的值则是此section的虚拟起始地址XXXX,的值是以XXXX为起始地址的顺延。如果使用了vstart关键字,想要获得本section在文件中的真实偏移,可以使用
section.节名.start
vstart的作用是为section内的数据指定一个虚拟的起始地址.
使用vstart的时机:预先知道程序将来加载到某地址处
section也称为节、段,顾名思义,是程序中的一小块,Section并没有对程序中的地址产生任何影响。
NASM的简单用法
nasm-f <format><filename>[-o <output>]
nasm -o 指定输出可执行文件的名称
CPU的实模式
实模式是指8086CPU的寻址方式、寄存器大小、指令用法等,是用来反应CPU在该环境下如何工作的概念。
CPU的工作原理
CPU的唯一任务就是就是执行指令(取指执行)
CPU的工作原理:
取指令:控制单元(CPU的控制中心 指令寄存器IR、指令译码器ID、操作控制器OC)要取下一条待运行的指令,该指令的地址在程序计数器PC中(在X86CPU上,程序计数器就是CS:IP),于是读取PC计数器后,将此地址送上地址总线,CPU根据此地址便得到了指令
指令译码:CPU将取得的指令存入到指令寄存器IR中,这时候就轮到指令译码器上场了,他根据指令格式检查指令寄存器中的指令,先确定操作码是什莫,在检查操作数类型(是在内存中呢,还是在地址中呢)
执行指令:如果操作数是在内存中,就将相应的操作数从内存中取回放入自己的存储单元(CPU的存储单元是指CPU内部的L1,L2缓存及寄存器),如果操作数是在寄存器中就直接用了,免了取操作数这一过程。操作码有了,操作数也有了,操作控制器就给运算单元下令,开工,于是运算单元便开始真正的执行指令了。
修改PC计数器的大小:PC计数器的值被加上当前指令的大小,于是PC又指向了下一条指令的地址
寄存器
寄存器是一种物理存储元件,只不过它比一般的存储介质要快,能够跟上CPU的步伐,所以在CPU内部有好多这样的寄存器用来给CPU存储数据。
寄存器是给CPU处理数据的场所。
CPU寄存器中的分类:
一类是其内部使用的,对程序员不可见(程序员不能使用),但其中的一部分需要初始化
另一类是对程序员可见的寄存器。
段寄存器
段寄存器的作用就是指定一片内存的起始地址,故也称为段基址寄存器。段内偏移地址,顾名思义,仅仅相对于此起始地址的偏移量(16位宽)
实模式下内存分段由来
“实模式”的“实”体现在:程序中用到的地址都是真实的物理地址,“段基址:段内偏移”产生的逻辑地址就是物理地址,也就是程序员看到的完全是真实的内存。
实模式被保护模式淘汰的原因
安全隐患
在实模式下,用户程序和操作系统可以说是同一特权的程序,因为实模式下没有特权级,它处处和操作系统平起平坐,所以可以执行一些具有破坏性的指令。
程序可以随意修改自己的段基址,这样便在1MB的内存空间内不受阻拦,可以随意访问任意物理内存,包括操作系统所在的内存数据。
由于完全没有保护性可言,用户程序甚至可以覆盖操作系统在内存中的映像