загрузчик изменяет информацию о перемещении при запуске программы? - PullRequest
0 голосов
/ 29 апреля 2018

Я всегда считал, что определение абсолютных адресов - это полностью работа линкера. То есть после того, как компоновщик объединит все объектные файлы в один исполняемый файл, он затем изменит все абсолютные адреса, чтобы отразить новое местоположение внутри исполняемого файла. Но после прочтения в здесь того, что загрузчик не должен размещать текст программ по адресу, указанному компоновщиком, я действительно запутался.

Возьмите этот код для примера

main.C

 void printMe();
int main(){
    printMe();

    return 0;

}

Foo.c

/* Lots of other functions*/
void printMe(){
     printf("Hello");
}

Скажите, что после связывания код для main размещается по адресу 0x00000010, а код для printMe - по адресу 0x00000020. Затем, когда программа запускается, загрузчик действительно загружает main и printMe по своим виртуальным адресам, указанным компоновщиком. Но если загрузчик не загружает программу таким образом, это не нарушит все ссылки на абсолютные адреса.

Ответы [ 2 ]

0 голосов
/ 30 апреля 2018

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

Если бы все эти скомпилированные использования имели фиксированные адреса, вероятно, при загрузке возникли бы конфликты. Если два связанных модуля использовали один и тот же адрес, приложение не может быть загружено.

В течение десятилетий перемещаемый код был решением этой проблемы. Модуль может быть загружен где угодно. Некоторые системы переходят на следующий шаг и случайным образом помещают модули в память для обеспечения безопасности.

В некоторых ситуациях код не может быть чисто перемещаемым.

Если у вас есть что-то вроде этого:

static int b, *a = &b ;

инициализация зависит от того, где модель находится в памяти (и где находится «b»). Линкеры обычно генерируют информацию для таких конструкций, чтобы загрузчик мог их исправить.

Таким образом, это не правильно:

Я всегда считал, что разрешение абсолютных адресов - это полностью работа линкера.

0 голосов
/ 29 апреля 2018

Насколько мне известно, здесь дело не в этом.

Если он связан статически, то адрес функции рассчитывается статически компоновщиком. Поскольку относительный адрес известен, поэтому выполняется относительный вызов функции, и все будет хорошо.

Если он связан динамически, то приходит ld.so и загружает библиотеку. Символ разрешается либо Перемещение общих библиотек во время загрузки или Независимый код позиции (PIC) в общих библиотеках (эти 2 статьи написаны не мной).

Проще говоря,

  1. Перемещение во время загрузки выполняется путем переписывания кода, чтобы дать им правильный адрес, который отключает wirte-protect и делится между различными процессами.

  2. PIC делается путем добавления 2 разделов, называемых GOT и PLT, все по определенному адресу, который может быть известен во время соединения. Вызов функции из динамической библиотеки сначала вызовет функцию ... @ plt (E.x. printf @ plt), а затем jump *GOT[offset]. При первом вызове это будет адрес следующей инструкции, которая вызовет динамический загрузчик для загрузки функции. При втором вызове это будет адрес функции. Как видите, это требует дополнительной памяти и времени по сравнению с обычным кодом.

...