Este é o resultado de olhar para a memória virtual de um processo em gdb; Tenho algumas dúvidas sobre isso:
Por que algumas partes da memória virtual são repetidas? Por exemplo, nosso programa (stack6) e biblioteca libc são repetidos 4 vezes; se eles os dividiram em partes diferentes, então por quê? Por que não apenas colocá-los todos juntos?
O caminho superior (/opt/pro...) é a seção de instruções (seção de texto) da nossa memória virtual e contém apenas as instruções?
Por que os tamanhos das 4 libc's são diferentes? Qual é o problema com o deslocamento, se já temos o tamanho e o endereço inicial, para que serve o deslocamento?
Onde estão as seções de dados, bss, kernel e heap e por que algumas partes da imagem acima não têm informações sobre elas? Existe alguma opção melhor no gdb que realmente mostre todas as partes?
Existe algum programa melhor que o gdb que mostre muito melhor a parte da memória virtual do nosso processo? Eu só quero ter um bom visual de uma memória virtual real, qual programa de depuração fornece o melhor resultado.
As seções que mencionei:
Há uma informação importante faltando na
gdb
saída de 's: as permissões das páginas. (Eles são mostrados no Solaris e no FreeBSD , mas não no Linux.) Você pode vê-los olhando para/proc/<pid>/maps
; os mapas para seu exemplo de Protostar mostram(O exemplo Protostar é executado em uma VM que é fácil de hackear, presumivelmente para tornar os exercícios tratáveis: não há proteção NX e nem ASLR.)
Você verá acima que o que parece ser mapeamentos repetidos na
gdb
verdade corresponde a mapeamentos diferentes com permissões diferentes. O segmento de texto é mapeado como somente leitura e executável; o segmento de dados é mapeado como somente leitura; O BSS e o heap são mapeados para leitura e gravação. Idealmente, o segmento de dados, BSS e heap não são executáveis, mas este exemplo não tem suporte a NX, então eles são executáveis. Cada biblioteca compartilhada obtém seu próprio mapeamento para seu segmento de texto, segmento de dados e BSS. O quarto mapeamento é um segmento não legível, não gravável e não executável normalmente usado para proteger contra estouros de buffer (embora, dada a idade do kernel e da biblioteca C usada aqui, isso possa ser algo diferente).O deslocamento, quando fornecido, indica o deslocamento dos dados dentro do arquivo, o que não necessariamente tem muito a ver com sua posição no espaço de endereço. Quando carregado, está sujeito a restrições de alinhamento; por exemplo,
libc-2.11.2.so
os cabeçalhos de programa de ' especificam dois cabeçalhos “LOAD”:(Use
readelf -l
para ver isso.)Isso pode resultar em vários mapeamentos no mesmo deslocamento, com endereços virtuais diferentes, se as seções mapeadas para os segmentos tiverem sinalizadores de proteção diferentes. No
stack6
caso de:(Isso também explica o tamanho pequeno mostrado por
proc info mappings
forstack6
: cada cabeçalho solicita menos de 4KiB, com um alinhamento de 4KiB, então ele obtém dois mapeamentos de 4KiB com o mesmo deslocamento em endereços diferentes.)Mapeamentos em branco correspondem a mapeamentos anônimos; veja
man 5 proc
para detalhes. Você precisaria entrarmmap
paragdb
determinar a que eles correspondem.Você não pode ver os mapeamentos do kernel (além do legado
vsyscall
em algumas arquiteturas) porque eles não importam da perspectiva do processo (eles são inacessíveis).Não conheço
gdb
opção melhor, sempre uso/proc/$$/maps
.Consulte Como os programas são executados: binários ELF para obter detalhes do formato ELF conforme lido pelo kernel e como ele é mapeado para alocações de memória; tem ponteiros para muito mais material de referência.