Sobre o layout de memória dos programas no Linux
Eu tenho algumas perguntas sobre o layout de memória de um programa no Linux. Sei de várias fontes (estou lendo "Programação a partir do zero") que cada seção é carregada em sua própria região de memória. A seção de texto é carregada primeiro no endereço virtual 0x8048000, a seção de dados é carregada imediatamente depois, a seguir é a seção bss, seguida pela pilha e pela pilha.
Para experimentar o layout, eu fiz esse programa em montagem. Primeiro, imprime os endereços de algumas etiquetas e calcula o ponto de interrupção do sistema. Então entra em um loop infinito. O loop incrementa um ponteiro e, em seguida, tenta acessar a memória nesse endereço; em algum momento uma falha de segmentação sai do programa (eu fiz isso intencionalmente).
Este é o programa:
.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:
E estas são as partes relevantes do resultado (este é o Debian 32bit):
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
Minhas perguntas são:
1) Por que meu programa está iniciando no endereço 0x8048190 em vez de 0x8048000? Com isso, acho que a instrução no rótulo "_start" não é a primeira coisa a carregar, então o que há entre os endereços 0x8048000 e 0x8048190?
2) Por que existe uma lacuna entre o final da seção de texto e o início da seção de dados?
3) Os endereços inicial e final do bss são os mesmos. Presumo que os dois buffers estejam armazenados em outro lugar, isso está correto?
4) Se o ponto de interrupção do sistema é 0x83b4001, por que eu recebo a falha de segmentação anteriormente em 0x804a000?