Как получается, что основная функция всегда загружается по одному и тому же адресу, тогда как переменные большую часть времени имеют разные адреса? - PullRequest
4 голосов
/ 13 сентября 2010

Я написал эту небольшую программу сегодня, и результаты меня поразили. Вот программа


int main(int argc, char **argv)
{
 int a;
 printf("\n\tMain is located at: %p and the variable a is located at address: %p",main,&a);
 return 0;
}

на моем компьютере главная функция всегда загружается по адресу "0x80483d4", а адрес переменной постоянно меняется. Как это происходит? Я читал в операционных системах, что как часть схемы виртуализации ОС продолжает перемещать адрес инструкций. Так почему же каждый раз, когда я запускаю эту программу, main загружается по одному и тому же адресу?

заранее спасибо, ребята.

Ответы [ 2 ]

7 голосов
/ 13 сентября 2010

В системах ELF, таких как Linux, адреса, по которым загружаются сегменты обычных исполняемых файлов (тип ELF ET_EXEC), фиксируются во время компиляции.Общие объекты (тип ELF ET_DYN), такие как библиотеки, построены так, чтобы быть независимыми от позиции, а их сегменты загружаются в любом месте адресного пространства (возможно, с некоторыми ограничениями для некоторых архитектур).Можно построить исполняемые файлы так, чтобы они на самом деле были ET_DYN - они известны как "независимые от позиции исполняемые файлы" (PIE), но это не распространенная техника.

То, что вы видите, является фактомчто ваша main() функция находится в текстовом сегменте с фиксированным адресом вашего скомпилированного исполняемого файла.Попробуйте также распечатать адрес библиотечной функции, такой как printf(), после ее обнаружения с помощью dlsym() - если ваша система поддерживает и включила рандомизацию размещения адресного пространства (ASLR), то вы должны увидеть, что адрес этой функции меняется сзапустить для запуска вашей программы.(Если вы просто распечатываете адрес библиотечной функции, помещая ссылку непосредственно в ваш код, то на самом деле вы можете получить адрес батута таблицы поиска процедур (PLT), который статически компилируется по фиксированному адресу в вашем исполняемом файле..)

Переменная, которую вы видите, меняет адрес с запуска на запуск, потому что это автоматическая переменная, созданная в стеке, а не в статически выделенной памяти.В зависимости от ОС и версии, адрес базы стека может меняться от запуска к запуску даже без ASLR.Если вы переместите объявление переменной как глобальное за пределами вашей функции, вы увидите, что оно ведет себя так же, как ваша main() функция.

Вот полный пример - скомпилируйте что-то вроде gcc -o example example.c -dl:

#include <stdio.h>
#include <dlfcn.h>

int a = 0;

int main(int argc, char **argv)
{
    int b = 0;
    void *handle = dlopen(NULL, RTLD_LAZY);
    printf("&main: %p; &a: %p\n", &main, &a);
    printf("&printf: %p; &b: %p\n", dlsym(handle, "printf"), &b);
    return 0;
}
0 голосов
/ 13 сентября 2010

main(...) - это код запуска библиотеки времени выполнения, в котором операционная система загружается и выполняется каждый раз. Взгляните на CRT (C Runtime Library), которая будет содержать код для этого в зависимости от вашего компилятора.

Еще один момент, который стоит иметь в виду, этот адрес - я не стал бы беспокоиться об этом слишком долго, пока работает код C. Это случайность, в зависимости от ряда факторов, таких как загрузка ОС, используемые драйверы, аппаратное обеспечение, антивирусное программное обеспечение и т. Д. *

Кроме того, в отношении кода, если вы добавите статические переменные, функции, указатели, которые будут изменять расположение двоичного кода, и еще , что важно , адреса те символы, которые загружаются во время выполнения, будут другими.

...