Приклади мінімального запуску для Linux з аналізом демонтажу
Оскільки це детальна інформація про реалізацію, не визначена стандартами, давайте просто подивимось, що компілятор робить для певної реалізації.
У цій відповіді я або посилаюсь на конкретні відповіді, які роблять аналіз, або надам аналіз тут, і підсумую тут усі результати.
Усі вони є у різних версіях Ubuntu / GCC, і результати, ймовірно, досить стабільні у різних версіях, але якщо ми знайдемо будь-які варіанти, давайте уточнимо більш точні версії.
Локальна змінна всередині функції
Будь то main
чи будь-яка інша функція:
void f(void) {
int my_local_var;
}
Як показано на: Що означає <значення, оптимізоване> в gdb?
-O0
: стек
-O3
: реєструється, якщо вони не розтікаються, складіть інакше
Для мотивації того, чому такий стек існує, див.: Яка функція інструкцій push / pop, що використовуються в регістрах у складі x86?
Глобальні змінні та static
функціональні змінні
/* 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;
}
- якщо ініціалізовано до
0
або не ініціалізовано (і тому неявно ініціалізовано в 0
): .bss
розділ, див. також: Для чого потрібен сегмент .bss?
- інакше:
.data
розділ
char *
і char c[]
Як показано на: Де зберігаються статичні змінні в C і C ++?
void f(void) {
/* RODATA / TEXT */
char *a = "abc";
/* Stack. */
char b[] = "abc";
char c[] = {'a', 'b', 'c', '\0'};
}
Чи дуже великі літеральні рядки TODO також будуть поставлені на стек? Або .data
? Або компіляція провалюється?
Аргументи функції
void f(int i, int j);
Потрібно пройти відповідну конвенцію про дзвінки, наприклад: https://en.wikipedia.org/wiki/X86_calling_conventions для X86, яка визначає конкретні регістри або місця стеку для кожної змінної.
Тоді як показано в розділі Що означає <значення оптимізованого> в gdb? , -O0
потім кладе все в стек, -O3
намагаючись максимально використовувати регістри.
Якщо функція стає вбудованою, до них звертаються так само, як до звичайних місцевих жителів.
const
Я вважаю, що це не має ніякого значення, тому що ви можете набрати це.
І навпаки, якщо компілятор може визначити, що деякі дані ніколи не записуються, він теоретично міг би розмістити їх, .rodata
навіть якщо не const.
Аналіз TODO.
Покажчики
Вони є змінними (містять адреси, які є цифрами), так само, як і всі інші :-)
малок
Питання не має великого сенсу malloc
, оскільки malloc
це функція і в:
int *i = malloc(sizeof(int));
*i
є змінною, яка містить адресу, тому вона потрапляє у вищезазначений випадок.
Що стосується того, як malloc працює всередині нього, коли ви називаєте його, ядро Linux позначає певні адреси як записувані у своїх внутрішніх структурах даних, і коли їх програма торкається спочатку, відбувається помилка, і ядро дозволяє сторінки таблиці, що дозволяє отримати доступ відбувається без segfaul: Як працює підказка x86?
Однак зауважте, що це в основному саме те, що exec
робить syscall під кришкою, коли ви намагаєтеся виконати виконуваний файл: він позначає сторінки, на які хоче завантажити, і записує туди програму, дивіться також: Як ядро отримує виконуваний бінарний файл під управлінням linux? За винятком того, що exec
є деякі додаткові обмеження щодо того, куди слід завантажувати (наприклад, код не переміщується ).
Точний системний виклик використовується для malloc
це mmap
в сучасних реалізаціях 2020 року, а в минулому brk
був використаний: Чи має Танос () використання битий () або ММАП ()?
Динамічні бібліотеки
В основному, mmap
редагуйте пам'ять: /unix/226524/what-system-call-is-used-to-load-libraries-in-linux/462710#462710
екологічні змінні та main
sargv
Над початковим стеком: /unix/75939/where-is-the-environment-string-actual-stored TODO, чому б не в .data?