Exemplos mínimos executáveis do Linux com análise de desmontagem
Como esse é um detalhe da implementação não especificado pelos padrões, vamos apenas dar uma olhada no que o compilador está fazendo em uma implementação específica.
Nesta resposta, irei vincular a respostas específicas que fazem a análise ou fornecê-la diretamente aqui e resumir todos os resultados aqui.
Todos eles estão em várias versões do Ubuntu / GCC, e os resultados provavelmente são bastante estáveis entre as versões, mas se encontrarmos alguma variação, vamos especificar versões mais precisas.
Variável local dentro de uma função
Seja ele main
ou qualquer outra função:
void f(void) {
int my_local_var;
}
Como mostrado em: O que <valor otimizado> significa em gdb?
-O0
: pilha
-O3
: registra se não derramar, empilhar de outra forma
Para obter motivação sobre o motivo da pilha existir, consulte: Qual é a função das instruções push / pop usadas nos registradores na montagem x86?
Variáveis globais e static
variáveis de função
/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;
/* DATA */
int my_global_implicit_explicit_1 = 1;
void f(void) {
/* BSS */
static int my_static_local_var_implicit;
static int my_static_local_var_explicit_0 = 0;
/* DATA */
static int my_static_local_var_explicit_1 = 1;
}
char *
e char c[]
Como mostrado em: Onde as variáveis estáticas são armazenadas em C e C ++?
void f(void) {
/* RODATA / TEXT */
char *a = "abc";
/* Stack. */
char b[] = "abc";
char c[] = {'a', 'b', 'c', '\0'};
}
TODO literais de string muito grandes também serão colocados na pilha? Ou .data
? Ou a compilação falha?
Argumentos de função
void f(int i, int j);
Deve passar pela convenção de chamada relevante, por exemplo: https://en.wikipedia.org/wiki/X86_calling_conventions para X86, que especifica registros específicos ou locais de pilha para cada variável.
Então, como mostrado em O que <valor otimizado> significa em gdb? , -O0
coloca tudo na pilha, enquanto -O3
tenta usar os registros o máximo possível.
Se a função for incorporada, no entanto, elas serão tratadas como locais comuns.
const
Acredito que isso não faz diferença, porque você pode descartá-lo.
Por outro lado, se o compilador é capaz de determinar que alguns dados nunca são gravados, ele poderia, em teoria, colocá-lo .rodata
mesmo que não seja const.
Análise TODO.
Ponteiros
Eles são variáveis (que contêm endereços, que são números), iguais aos demais :-)
malloc
A questão não faz muito sentido malloc
, pois malloc
é uma função e em:
int *i = malloc(sizeof(int));
*i
é uma variável que contém um endereço e, portanto, se enquadra no caso acima.
Quanto ao funcionamento do malloc internamente, quando você o chama, o kernel do Linux marca determinados endereços como graváveis em suas estruturas de dados internas e, quando são tocados pelo programa inicialmente, ocorre uma falha e o kernel ativa as tabelas de páginas, o que permite o acesso. acontece sem segfaul: Como funciona a paginação x86?
Observe, no entanto, que isso é basicamente exatamente o que o exec
syscall faz quando você tenta executar um executável: ele marca as páginas nas quais deseja carregar e grava o programa, veja também: Como o kernel obtém um arquivo binário executável em execução linux? Exceto que exec
tem algumas limitações extras sobre onde carregar (por exemplo, se o código não é realocável ).
O syscall exato usado malloc
é mmap
nas implementações modernas de 2020 e no passado brk
foi usado: malloc () usa brk () ou mmap ()?
Bibliotecas dinâmicas
Basicamente, é mmap
editado na memória: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
variáveis ambientais e main
'sargv
Acima da pilha inicial: /unix/75939/where-is-the-environment-string-actual-stored TODO por que não em .data?