Minimalne uruchamialne przykłady w systemie Linux z analizą dezasemblacji
Ponieważ jest to szczegół implementacji, który nie jest określony przez standardy, przyjrzyjmy się tylko, co kompilator robi w określonej implementacji.
W tej odpowiedzi albo podam link do konkretnych odpowiedzi, które wykonują analizę, albo przedstawię analizę bezpośrednio tutaj i podsumuję wszystkie wyniki tutaj.
Wszystkie są w różnych wersjach Ubuntu / GCC, a wyniki są prawdopodobnie dość stabilne we wszystkich wersjach, ale jeśli znajdziemy jakieś różnice, określmy bardziej precyzyjne wersje.
Zmienna lokalna wewnątrz funkcji
Czy to main
czy jakakolwiek inna funkcja:
void f(void) {
int my_local_var;
}
Jak pokazano na: Co oznacza <wartość zoptymalizowana na zewnątrz> w gdb?
-O0
: stos
-O3
: rejestruje, jeśli się nie rozlewają, stosuj inaczej
Aby dowiedzieć się, dlaczego stos istnieje, zobacz: Jaka jest funkcja instrukcji push / pop używanych w rejestrach w asemblerze x86?
Zmienne globalne i static
zmienne funkcyjne
/* 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 *
i char c[]
Jak pokazano na: Gdzie są przechowywane zmienne statyczne w C i C ++?
void f(void) {
/* RODATA / TEXT */
char *a = "abc";
/* Stack. */
char b[] = "abc";
char c[] = {'a', 'b', 'c', '\0'};
}
DO ZROBIENIA, czy na stosie zostaną również umieszczone bardzo duże literały ciągów? Albo .data
? A może kompilacja się nie udaje?
Argumenty funkcji
void f(int i, int j);
Musi przejść przez odpowiednią konwencję telefoniczną, np .: https://en.wikipedia.org/wiki/X86_calling_conventions dla X86, która określa konkretne rejestry lub lokalizacje stosu dla każdej zmiennej.
Następnie, jak pokazano w Co oznacza <wartość zoptymalizowana na zewnątrz> w gdb? , -O0
a następnie wysysa wszystko do stosu, -O3
próbując jednocześnie używać rejestrów tak często, jak to możliwe.
Jeśli jednak funkcja zostanie wstawiona, są traktowani tak, jak zwykli lokalni.
const
Uważam, że nie ma to znaczenia, ponieważ można to odrzucić.
I odwrotnie, jeśli kompilator jest w stanie określić, że niektóre dane nigdy nie są zapisywane, teoretycznie mógłby umieścić je w, .rodata
nawet jeśli nie jest to const.
Analiza TODO.
Wskaźniki
Są to zmienne (zawierające adresy, które są liczbami), tak samo jak cała reszta :-)
malloc
Pytanie nie ma większego sensu malloc
, ponieważ malloc
jest funkcją, a w:
int *i = malloc(sizeof(int));
*i
jest zmienną, która zawiera adres, więc przypada na powyższy przypadek.
Jeśli chodzi o to, jak malloc działa wewnętrznie, kiedy to nazywasz, jądro Linuksa oznacza określone adresy jako zapisywalne w swoich wewnętrznych strukturach danych, a kiedy program dotyka ich początkowo, pojawia się błąd i jądro włącza tabele stron, co umożliwia dostęp dzieje się bez segfaul: Jak działa stronicowanie x86?
Zauważ jednak, że jest to w zasadzie dokładnie to, co exec
wywołanie systemowe robi pod maską, kiedy próbujesz uruchomić plik wykonywalny: zaznacza strony, na które chce załadować, i zapisuje tam program, zobacz także: Jak jądro pobiera wykonywalny plik binarny działający pod linux? Z wyjątkiem tego, że exec
ma pewne dodatkowe ograniczenia dotyczące miejsca, do którego należy załadować (np. Czy kodu nie można przenieść ).
Dokładne wywołanie systemowe używane malloc
jest mmap
we współczesnych implementacjach 2020, a w przeszłości brk
było używane: Czy malloc () używa brk () lub mmap ()?
Biblioteki dynamiczne
Zasadniczo pobierz się mmap
do pamięci: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
envinroment zmienne i main
sargv
Powyższy stos początkowy: /unix/75939/where-is-the-environment-string-actual-stored TODO, czemu nie w .data?