Приклади мінімального запуску для 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
екологічні змінні та mainsargv
Над початковим стеком: /unix/75939/where-is-the-environment-string-actual-stored TODO, чому б не в .data?