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 mainou 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 staticvariá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? , -O0coloca tudo na pilha, enquanto -O3tenta 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 .rodatamesmo 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 execsyscall 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 exectem algumas limitações extras sobre onde carregar (por exemplo, se o código não é realocável ).
O syscall exato usado mallocé mmapnas implementações modernas de 2020 e no passado brkfoi usado: malloc () usa brk () ou mmap ()?
Bibliotecas dinâmicas
Basicamente, é mmapeditado 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?