Esempi minimizzabili di Linux con analisi di disassemblaggio
Dato che si tratta di un dettaglio dell'implementazione non specificato dagli standard, diamo un'occhiata a cosa sta facendo il compilatore in una particolare implementazione.
In questa risposta, collegherò a risposte specifiche che eseguono l'analisi, o fornirò l'analisi direttamente qui, e riassumerò tutti i risultati qui.
Tutti questi sono in varie versioni di Ubuntu / GCC e i risultati sono probabilmente abbastanza stabili tra le versioni, ma se troviamo delle variazioni specificiamo versioni più precise.
Variabile locale all'interno di una funzione
Sia esso maino qualsiasi altra funzione:
void f(void) {
int my_local_var;
}
Come mostrato in: Cosa significa <valore ottimizzato> in gdb?
-O0: stack
-O3: registra se non si rovesciano, impila diversamente
Per motivi sul perché esiste lo stack, vedere: Qual è la funzione delle istruzioni push / pop utilizzate sui registri nell'assembly x86?
Variabili globali e staticvariabili di funzione
/* 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[]
Come mostrato in: Dove sono archiviate le variabili statiche in C e C ++?
void f(void) {
/* RODATA / TEXT */
char *a = "abc";
/* Stack. */
char b[] = "abc";
char c[] = {'a', 'b', 'c', '\0'};
}
TODO verranno messi in pila anche letterali di stringhe molto grandi? Oppure .data? O la compilazione fallisce?
Argomenti di funzione
void f(int i, int j);
È necessario seguire la convenzione di chiamata pertinente, ad esempio: https://en.wikipedia.org/wiki/X86_calling_conventions per X86, che specifica registri specifici o posizioni di stack per ogni variabile.
Quindi, come mostrato in Cosa significa <valore ottimizzato> in gdb? , -O0quindi inserisce tutto nello stack, mentre -O3tenta di utilizzare i registri il più possibile.
Se la funzione viene tuttavia incorporata, vengono trattate come normali locali.
const
Credo che non faccia alcuna differenza perché puoi scriverlo via.
Al contrario, se il compilatore è in grado di determinare che alcuni dati non vengono mai scritti, in teoria potrebbe posizionarli .rodataanche se non cost.
Analisi TODO.
puntatori
Sono variabili (che contengono indirizzi, che sono numeri), così come tutto il resto :-)
malloc
La domanda non ha molto senso per malloc, poiché mallocè una funzione e in:
int *i = malloc(sizeof(int));
*i è una variabile che contiene un indirizzo, quindi rientra nel caso precedente.
Per quanto riguarda il funzionamento interno di malloc, quando lo chiami il kernel Linux segna alcuni indirizzi come scrivibili sulle sue strutture di dati interne e quando vengono toccati inizialmente dal programma, si verifica un errore e il kernel abilita le tabelle delle pagine, che consentono l'accesso succede senza segfaul: come funziona il paging x86?
Si noti tuttavia che questo è fondamentalmente esattamente ciò che fa il execsyscall quando si tenta di eseguire un eseguibile: contrassegna le pagine in cui si desidera caricare e scrive lì il programma, vedere anche: In che modo il kernel ottiene un file binario eseguibile in esecuzione sotto Linux? Tranne che execha alcune limitazioni extra su dove caricare (ad es. Il codice non è trasferibile ).
L'esatto syscall usato mallocè mmapnelle moderne implementazioni 2020 e in passato è brkstato usato: malloc () usa brk () o mmap ()?
Librerie dinamiche
Fondamentalmente mmapvai in memoria: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
variabili envinroment e main'sargv
Sopra lo stack iniziale: /unix/75939/where-is-the-environment-string-actual-stored TODO perché non in .data?