Sobre el diseño de memoria de los programas en Linux

Tengo algunas preguntas sobre el diseño de memoria de un programa en Linux. Sé por varias fuentes (estoy leyendo "Programación desde cero") que cada sección se carga en su propia región de memoria. La sección de texto se carga primero en la dirección virtual 0x8048000, la sección de datos se carga inmediatamente después de eso, luego está la sección bss, seguida del montón y la pila.

Para experimentar con el diseño, hice este programa en ensamblador. Primero imprime las direcciones de algunas etiquetas y calcula el punto de interrupción del sistema. Luego entra en un bucle infinito. El bucle incrementa un puntero y luego intenta acceder a la memoria en esa dirección, en algún momento una falla de segmentación saldrá del programa (lo hice intencionalmente).

Este es el 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:

Y estas son las partes relevantes de la salida (esto es 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

Mis preguntas son:

1) ¿Por qué mi programa comienza en la dirección 0x8048190 en lugar de 0x8048000? Con esto, supongo que la instrucción en la etiqueta "_start" no es lo primero que se carga, entonces, ¿qué hay entre las direcciones 0x8048000 y 0x8048190?

2) ¿Por qué hay una brecha entre el final de la sección de texto y el comienzo de la sección de datos?

3) Las direcciones de inicio y finalización de bss son las mismas. Supongo que los dos buffers están almacenados en otro lugar, ¿es esto correcto?

4) Si el punto de interrupción del sistema está en 0x83b4001, ¿por qué recibo la falla de segmentación antes en 0x804a000?

Respuestas a la pregunta(1)

Su respuesta a la pregunta