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?

questionAnswers(1)

yourAnswerToTheQuestion