在代码段中使用数据
程序在运行前,操作系统需要为程序分配内存空间。分配的内存空间是安全的,可以任意改写其中的值。
程序在读写内存前,应该先确保该内存是安全的。若程序需要一定空间,必须在源程序中作出声明。
可以在汇编代码中使用关键字 dw 伪指令定义一系列字型数据(define word),数据之间以逗号分隔,例如:
定义完成后,这一系列字型数据会和代码一起被加载到内存中。
程序在运行时,可以通过寄存器 CS 和 IP 得到以上数据的段地址和偏移地址。它们在内存中与机器指令的相对位置和在源程序中与汇编指令的相对位置相同。
可执行文件( .exe
文件)的执行过程为:
- 由其它的程序将可执行文件中的程序加载入内存中
- 设置
CS:IP
指向程序中的第一条要执行的指令(即程序的入口),从而使程序开始执行 - 程序运行结束后,返回到加载它的程序
可以将这一系列字型数据与代码等价。为了区分数据和代码,通常将这一系列数据放到代码段的最前面,并跳过它们从有意义的代码开始执行。
有两种方法可以做到这一点:
- 为程序提供执行入口。程序的框架变为:
codesg segment
; ...data...
start:
; ...code...
codesg ends
end start
程序中在第一条指令前出现了一个标号 start
,这个标号在伪指令 end 后面也出现了。
end 除了告知编译器程序结束之外,还可以指明程序的入口在标号“ start
”处。
- 使用 JMP 指令修改 IP 寄存器,跳过数据内容:
codesg segment
JMP begin
; ...data...
begin:
; ...code...
codesg ends
end
由于标号本质上是标记代码所处的偏移地址,因此可以使用 JMP 指令直接修改寄存器 IP 为标号所处的偏移地址。
程序中的数据、代码与栈
可以在程序的代码段中通过定义一些无用的数据,来取得一部分空间,然后将这一段空间当做栈空间来使用。
一个段最大的容量只有64KB,将数据放入代码段中,会挤占代码的空间。更好的方法是将数据、代码和栈放入不同的段中。
定义多个段的方法是定义代码段方式的扩展,只是对于不同的段,要使用不同的段名:
datas segment
; ...data...
datas ends
stacks segment
; ...stack...
stacks ends
codes segment
start:
; ...code...
codes ends
end start
- 对段地址的引用
程序分为了多个段,段名就相当一个标号,标号代表了段地址。
例如,指令“ MOV AX, data
”就是将名称为“ data
”的段的段地址送入 AX 寄存器中。
- 数据段、栈段和代码段的安排
可以将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内的单元。
可以用一个段存放数据,段地址存放在 DS 段寄存器中,用 MOV 、ADD 等指令访问内存单元,将其定义为数据段。
可以用一个段当做栈使用,段地址存放在 SS 段寄存器中,将栈顶单元的偏移地址放在 SP 寄存器中,用 PUSH 、POP 等指令操作栈,将其定义为栈段。
可以用一个段存放代码,段地址存放在 CS 段寄存器中,将段中的第一条指令存放在 IP 寄存器中,CPU执行该段的指令,将其定义为代码段。
这些段完全是人为定义的,段的大小取决去程序安排的大小,段的位置取决于代码的位置,段的访问取决于相应寄存器的设置。
以下示例程序利用数据段保持一段数据,利用栈段和代码段将保存的数据逆序存放: