x86汇编语言之段空间大小的对齐

段所占空间大小和特点

以8086为例,假如声明一个段,不论是数据段,栈段还是代码段, 一旦段里面有内容,那么会从一个新的段地址开始开辟空间,如果代码进行了分段处理,那么就会形成16字节对齐现象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assume ds:data
;场景一
data segment
;如果没有内容,则不开辟空间, 被编译器忽略
data ends

;场景二
data segment
dw 55FFH,55FFH ;后面的数据用0补足16个字节
data ends

;场景三
data segment
dw 55FFH,55FFH
db 'hello' ;以上占用连续的内存空间 55FFH 55FFH hello
data ends

;场景四
data segment
dw 55FFH,55FFH,55FFH,55FFH,55FFH,55FFH,55FFH,55FFH,55FFH,55FFH,55FFH,55FFH;超过16个字节,那么开辟16的倍数也就是32个字节的空间, 以此类推
data ends
段空间占用计算公式:

如果段中的数据占用N个字节,则程序加载后,该段实际占用空间为:

1
(N/16+1)*16

为什么说是现象呢

本质并不是因为段固定占用n16字节,而是因为段必须从一个新的地址段开始开辟空间,这就导致了我们认为*段一次最少拉升16字节的内存空间,必须为16的倍数**, 原因看如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

data segment
db 'hello'
data ends

start:
call print
mov ax,2000H

print:
mov bx,3000H
mov cx,4000H
ret


end start

内存分布如下:

我们发现data段并没有独占16个字节空间,而是让数据从一个新的16字节地址开始存入,段的作用是让数据在内存中的排列按照一定的布局进行排列,方便我们进行计算读取, 但是使用段的话明显会占用更多的内存空间

各个段之间内存排列分布

数据段,栈段,代码段 他们在内存中开辟的空间是根据代码由上到下依次分布的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
assume ds:data,cs:code,ss:stack

data segment
dw 66FF
data ends
;假如数据段的段地址是2000H 那么栈段的段地址为2001H, 代码段的段地址为:2002H 依次排列

stack segment
dw 77FF
stack ends

code segment
mov ax,data
mov ds,ax

mov ax,stack
mov ss,ax

code ends

;数据段--->栈段--->代码段

假如数据段的段地址是2000H 那么栈段的段地址为2001H, 代码段的段地址为:2002H 依次排列

如果我将各个段的代码位置调整一下,那么所在内存的位置也会跟着发生改变:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
assume ds:data,cs:code,ss:stack

;假如栈段的段地址是2000H 那么代码段的段地址为2001H, 数据段的段地址为:2002H 依次排列

stack segment
dw 77FF
stack ends

code segment
mov ax,data
mov ds,ax

mov ax,stack
mov ss,ax

code ends


data segment
dw 66FF
data ends

;栈段--->代码段--->数据段

也就是说各个段的内存分布不是固定的, 和代码的编写有关系

如果不加start标记的话,汇编代码默认由上往下执行,cs+ip从上往下,所过之处,全部被当做代码处理, 因此即便你在数据段中存放指令,甚至打入代码起始标签也没有问题:

1
2
3
4
5
6
7
8
9
10
assume cs:code,ds:data

data segment
mov ax,2000H ;第一步 执行
data ends
;由于一个段所占空间为16的倍数,后面空位补0,被当做为指令对待,因此当在数据段中执行16个字节代码后,由于段与段之间内存是连续分布的,如果ip的值刚好指向了代码段,那么紧接着执行代码段中的内容,如果ip没有指向代码段,则不执行, 这个情况是不可控的

code segment
mov ax,3000H ;ip偏移16个字节后 执行
code ends

如果在数据段中加上代码起始标记:

1
2
3
4
5
6
7
8
9
10
11
12
13
assume cs:code,ds:data

data segment
dw 55FFH
start:
mov ax,2000H ;第一步 先执行 如果没有标记,则从dw开始当做代码执行
data ends


code segment
mov ax,3000H ;ip偏移16个字节后 执行
code ends
end start

段和段地址之间的关系

每个段会独占一个栈地址

1
2
3
4
5
6
7
assume cs:code,ds:data

mov ax,3000H ;占用三个字节空间

data segment
age dw 'hello'
data ends

内存分布如下:

咱们会发现数据段,并不是从0100:0003开始开辟16个字节的空间,而是新起一个段地址从0101:0000开始开辟, 也就是说一个段占用独立的一个栈地址

假如把段去掉:

1
2
3
4
5
assume cs:code,ds:data

mov ax,3000H ;占用三个字节空间

age dw 'hello'

则内存分布如下:

那么代码会依次连续进行累加填充

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

0%