Paging и PIC исполняемые файлы - PullRequest
2 голосов
/ 19 мая 2019

Мне трудно понять необходимость исполняемых файлов PIC при использовании виртуальной памяти.Из того, что я собрал, каждой программе присваивается запись в таблице страниц, и поэтому у нее есть иллюзия, что в ее распоряжении есть вся память, в то время как механизм подкачки позаботится о возможных перемещениях, ошибках страниц и т. Д. Так что, если какая-либо программа имеет иллюзиювладеть всеми возможными адресами памяти, зачем использовать PIC?

Ответы [ 2 ]

3 голосов
/ 19 мая 2019

Две основные причины:

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

  2. Security. Наличие определенного кода, присутствующего в предсказуемых местах в памяти, представляет собой угрозу безопасности, поскольку позволяет использовать эксплойтов, которые переходят к коротким кодовым «гаджетам» в памяти , которые можно объединить для выполнения произвольных операций. Случайное перемещение кода в запуск приложения помогает победить эти атаки.

2 голосов
/ 19 мая 2019

Нам не нужно , и до последнего года или двух все исполняемые файлы Linux зависели от позиции (не PIC). См. 32-разрядные абсолютные адреса, более не разрешенные в x86-64 Linux? .

Вы по-прежнему можете создавать не-PIE исполняемый файл с gcc -fno-pie -no-pie, а статические исполняемые ELF-файлы всегда не-PIE с их адресом загрузки, выбранным во время соединения. Обычно по умолчанию устанавливается начало текстового сегмента на 401000.

Позиционно-независимые исполняемые файлы ELF начинались как хак: общий объект ELF с точкой входа. Но в наши дни это широко используется и по умолчанию gcc в большинстве дистрибутивов Linux. Адрес загрузки может быть рандомизирован во время выполнения.


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

Общие объекты ELF в Linux, например, могут содержать перемещения для 64-битных абсолютных адресов, поэтому вы можете иметь традиционные таблицы переходов (массив указателей кода) или статически инициализированные массивы указателей (на данные или функции) в коде, скомпилированном с gcc -fPIC для x86 и x86-64.


Обратите внимание, что gcc -fPIC также включает поддержку вставки символов, поэтому функции не могут напрямую обращаться к глобальным переменным; они должны загрузить адрес из GOT, если символ не имеет «скрытой» видимости ELF. (Или, конечно, если вы сделаете его static вместо глобального).

См. https://www.macieira.org/blog/2012/01/sorry-state-of-dynamic-libraries-on-linux/

(Некоторые идеи, предложенные в этом блоге, были реализованы, например, GCC поддерживает -fno-plt.)

Фактическая стоимость просто независимости от позиции с -fpie довольно мала. Но все же ненулевое значение в ОС, где гарантированно загружаются зависящие от позиции исполняемые файлы в низком 32-битном виртуальном адресном пространстве (например, в Linux), поэтому вы можете использовать 32-битные абсолютные адреса для 5-байтового mov r32, imm32 вместо 7-байтовый REA-относительный LEA для помещения статического адреса в регистр или [array + reg] для индексации статического массива с его адресом в смещении disp32 как части режима адресации.

...