我有关于Linux程序的内存布局的一些问题。我从各种来源了解到(我正在阅读《从零开始编程》),每个部分都加载到自己的内存区域中。文本部分首先在虚拟地址0x8048000处加载,其次是数据部分,接下来是bss部分,然后是堆和栈。
为了尝试这种布局,我用汇编语言编写了这个程序。首先,它打印一些标签的地址并计算系统断点。然后它进入一个无限循环。该循环递增指针,然后尝试访问该地址处的内存,某个时刻,一个分段错误将退出程序(我故意这样做)。
以下是程序:
以下是输出结果的相关部分(这是Debian 32位系统):
为了尝试这种布局,我用汇编语言编写了这个程序。首先,它打印一些标签的地址并计算系统断点。然后它进入一个无限循环。该循环递增指针,然后尝试访问该地址处的内存,某个时刻,一个分段错误将退出程序(我故意这样做)。
以下是程序:
.section .data
start_data:
str_mem_access:
.ascii "Accessing address: 0x%x\n\0"
str_data_start:
.ascii "Data section start at: 0x%x\n\0"
str_data_end:
.ascii "Data section ends at: 0x%x\n\0"
str_bss_start:
.ascii "bss section starts at: 0x%x\n\0"
str_bss_end:
.ascii "bss section ends at: 0x%x\n\0"
str_text_start:
.ascii "text section starts at: 0x%x\n\0"
str_text_end:
.ascii "text section ends at: 0x%x\n\0"
str_break:
.ascii "break at: 0x%x\n\0"
end_data:
.section .bss
start_bss:
.lcomm buffer, 500
.lcomm buffer2, 250
end_bss:
.section .text
start_text:
.globl _start
_start:
# print address of start_text label
pushl $start_text
pushl $str_text_start
call printf
addl $8, %esp
# print address of end_text label
pushl $end_text
pushl $str_text_end
call printf
addl $8, %esp
# print address of start_data label
pushl $start_data
pushl $str_data_start
call printf
addl $8, %esp
# print address of end_data label
pushl $end_data
pushl $str_data_end
call printf
addl $8, %esp
# print address of start_bss label
pushl $start_bss
pushl $str_bss_start
call printf
addl $8, %esp
# print address of end_bss label
pushl $end_bss
pushl $str_bss_end
call printf
addl $8, %esp
# get last usable virtual memory address
movl $45, %eax
movl $0, %ebx
int $0x80
incl %eax # system break address
# print system break
pushl %eax
pushl $str_break
call printf
addl $4, %esp
movl $start_text, %ebx
loop:
# print address
pushl %ebx
pushl $str_mem_access
call printf
addl $8, %esp
# access address
# segmentation fault here
movb (%ebx), %dl
incl %ebx
jmp loop
end_loop:
movl $1, %eax
movl $0, %ebx
int $0x80
end_text:
以下是输出结果的相关部分(这是Debian 32位系统):
text section starts at: 0x8048190
text section ends at: 0x804823b
Data section start at: 0x80492ec
Data section ends at: 0x80493c0
bss section starts at: 0x80493c0
bss section ends at: 0x80493c0
break at: 0x83b4001
Accessing address: 0x8048190
Accessing address: 0x8048191
Accessing address: 0x8048192
[...]
Accessing address: 0x8049fff
Accessing address: 0x804a000
Violación de segmento
我的问题是:
1) 为什么我的程序从地址0x8048190开始,而不是0x8048000?我猜测"_start"标签处的指令不是加载的第一件事情,那么在地址0x8048000和0x8048190之间有什么东西吗?
2) 为什么文本段结束和数据段开始之间有一个间隔?
3) bss的起始地址和结束地址相同。我假设这两个缓冲区存储在其他地方,这正确吗?
4) 如果系统断点在0x83b4001处,为什么我会在0x804a000处得到分段错误?
.text
部分(链接后)是文本段中唯一的内容。链接器将.rodata
等节合并到.text
中。此外,“堆”并不是真正存在的东西,更多的是一个概念(使用mmap(MAP_ANONYMOUS)进行的分配与brk
不连续)。我不确定人们是否认为BSS和静态数据是堆的一部分。同样不确定Linux是否将初始的brk
放在BSS之后。 - Peter Cordes