#c #linux #64-bit
#c #linux #64-разрядная
Вопрос:
#include <stdio.h>
void func() {}
int main() {
printf("%p", amp;func);
return 0;
}
Эта программа выводила 0x55c4cda9464a
Предположим, что это func
будет сохранено в .text
разделе, и, согласно этому рисунку, из CS:APP
:
Я полагаю, что адрес func
будет где-то рядом с начальным адресом .text
раздела, но этот адрес находится где-то посередине. Почему это так? Локальные переменные, хранящиеся в стеке, имеют адреса около 2 ^ 48 — 1, но я пытался разобрать разные коды C, и инструкции всегда располагались где-то около этого 0x55...
адреса.
Комментарии:
1. @Jabberwocky: Прекрати это. Предполагая, что люди не должны заботиться об адресах своих функций, говорят, что люди должны ожидать запуска только в идеализированной среде C и никогда не заботиться о деталях реализации. Это чушь собачья. Через некоторое время после изучения некоторых основ программирования студенты посещают курсы по операционным системам и узнают о макетах памяти. Кто-то должен поддерживать операционные системы. Кто-то должен обновить загрузчики программ. Кто-то должен диагностировать ошибки, связанные с ними. Учащимся необходимо изучить эти вещи и поэкспериментировать с ними. Не подавляйте любопытство.
2. @Jabberwocky: Я не писал, что вы написали, что ему должно быть все равно, я написал, что предлагать людям не заботиться — это плохо. При постановке вопроса явно подразумевается, что у человека должна быть причина для желания приобрести знания. Это плохое и вредное отношение. Остановите это.
3. @TortelliniTeusday Я полагаю, у вас нет точно такой же системы (какой, кстати?), как та, которую они использовали в книге. Если вы попробуете использовать GDB Online , результат будет ближе к тому, что вы ожидаете. OTOH на Ideone результат ближе к тому, что вы получаете в своей системе.
4. @TortelliniTeusday также взгляните на это , вы можете видеть, что в зависимости от размера,
malloc
память выделяется из совершенно разных мест.5. @TortelliniTeusday также попробуйте скомпилировать свой код с помощью
-m32
переключателя, который сгенерирует 32-битный двоичный файл, выходные данные которого снова будут совершенно другими.
Ответ №1:
gcc
при настройке на --enable-default-pie
1 (который используется по умолчанию) создает независимые от позиции исполняемые файлы (PIE). Это означает, что адрес загрузки не совпадает с тем, который компоновщик указал во время компиляции (0x400000 для x86_64). Это механизм безопасности, позволяющий включить рандомизацию рандомизации адресного пространства (ASLR) 2. То есть gcc компилируется с -pie
опцией по умолчанию.
Если вы скомпилируете с -no-pie
опцией ( gcc -no-pie file.c
), то вы увидите, что адрес func
ближе к 0x400000.
В моей системе я получаю:
$ gcc -no-pie t.c
$ ./a.out
0x401132
Вы также можете проверить адрес загрузки с помощью readelf
:
$ readelf -Wl a.out | grep LOAD
LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x000478 0x000478 R 0x1000
LOAD 0x001000 0x0000000000401000 0x0000000000401000 0x0001f5 0x0001f5 R E 0x1000
LOAD 0x002000 0x0000000000402000 0x0000000000402000 0x000158 0x000158 R 0x1000
LOAD 0x002e10 0x0000000000403e10 0x0000000000403e10 0x000228 0x000230 RW 0x1000
1 вы можете проверить это с помощью gcc --verbose
.
2 Вы также можете заметить, что адрес, печатаемый вашей программой, отличается при каждом запуске. Это из-за ASLR. Вы можете отключить это с помощью:
$ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
ASLR включен в Linux по умолчанию.
Комментарии:
1. Интересно… но если бы это было так, то почему
0x55...
адрес не был бы рандомизирован?2. @jackw11111 Я еще не полностью прочитал ответ. Я забыл упомянуть, что происходит рандомизация, но она все еще вокруг этого адреса. Я думаю, что это только рандомизирует начало сегмента в диапазоне нескольких мегабайт, если это то, о чем вы спрашиваете.
3. @TortelliniTeusday Я могу воспроизвести это поведение и в своей системе. Повторяется повсюду
0x55
, иногда0x56
.4. @jackw11111 На x86, ELF_ET_DYN_BASE вычисляется примерно таким значением (если я компилирую с отключенным ASLR, начальный адрес в x86_64, который я вижу, является
0x555555555145
). Я полагаю, что ASLR calc применяется поверх этого, но ограничен определенным количеством битов.