GCC -FPIC опция - PullRequest
       35

GCC -FPIC опция

380 голосов
/ 15 марта 2011

Я прочитал о опциях GCC для соглашений по генерации кода , но не смог понять, что делает "Генерация независимого от позиции кода (PIC)". Пожалуйста, приведите пример, чтобы объяснить мне, что это значит.

Ответы [ 6 ]

455 голосов
/ 15 марта 2011

Независимый от позиции код означает, что сгенерированный машинный код не зависит от того, находится ли он по определенному адресу для работы.

например. скачки будут генерироваться как относительные, а не абсолютные.

Псевдо-сборки:

PIC: это сработало бы, если бы код был по адресу 100 или 1000

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

Non-PIC: это будет работать, только если код находится по адресу 100

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

РЕДАКТИРОВАТЬ: в ответ на комментарий.

Если ваш код скомпилирован с -fPIC, он подходит для включения в библиотеку - библиотека должна быть в состоянии перемещаться из своего предпочтительного места в памяти на другой адрес, может быть другая библиотека уже загружена по адресу вашей библиотеки предпочитает.

52 голосов
/ 29 мая 2011

Я постараюсь объяснить то, что уже было сказано, более простым способом.

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

В вышеприведенном примере «111» в коде, отличном от PIC, записывается загрузчиком при первой загрузке.

Для не общих объектов вы можете захотеть, чтобы это было так, потому что компилятор может оптимизировать этот код.

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

37 голосов
/ 15 марта 2011

Код, встроенный в разделяемые библиотеки, обычно должен быть независимым от позиции кодом, чтобы разделяемая библиотека могла быть легко загружена по (более или менее) любому адресу в памяти.Опция -fPIC гарантирует, что GCC создает такой код.

18 голосов
/ 02 декабря 2014

Добавление дальше ...

Каждый процесс имеет одно и то же виртуальное адресное пространство (если рандомизация виртуального адреса остановлена ​​с помощью флага в ОС Linux) (Подробнее Отключить и снова включить рандомизацию адресного пространства только для себя )

Так что, если это один exe-файл без разделяемой ссылки (гипотетический сценарий), то мы всегда можем дать один и тот же виртуальный адрес одной и той же инструкции asm без какого-либо вреда.

Но когда мы хотим связать общий объект с exe, то мы не уверены в начальном адресе, назначенном для общего объекта, поскольку это будет зависеть от порядка, в котором были связаны общие объекты. При этом инструкция asm внутри .so всегда будет иметь другой виртуальный адрес в зависимости от процесса, на который он ссылается.

Таким образом, один процесс может дать начальный адрес .so как 0x45678910 в своем собственном виртуальном пространстве, а другой процесс может одновременно дать начальный адрес 0x12131415, и если они не используют относительную адресацию, .so не будет работать вообще.

Таким образом, они всегда должны использовать режим относительной адресации и, следовательно, опцию fpic.

13 голосов
/ 23 ноября 2016

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


Существует два обычно используемых метода решения этой проблемы:

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

2. Независимый от позиции код.Все адреса в коде относятся к текущей позиции.Общие объекты в Unix-подобных системах по умолчанию используют независимый от позиции код.Это менее эффективно, чем перемещение, если программа выполняется долго, особенно в 32-битном режиме.


Имя " позиционно-независимый код " фактически подразумевает следующее:

  • Раздел кода не содержит абсолютных адресов, которые необходимо переместить, а только собственные относительные адреса.Поэтому раздел кода может быть загружен по произвольному адресу памяти и использоваться несколькими процессами.

  • Раздел данных не используется несколькими процессами, поскольку он часто содержит данные для записи.Поэтому раздел данных может содержать указатели или адреса, которые необходимо переместить.

  • Все общедоступные функции и общедоступные данные могут быть переопределены в Linux.Если функция в главном исполняемом файле имеет то же имя, что и функция в общем объекте, тогда версия в главном будет иметь приоритет не только при вызове из основного, но и при вызове из общего объекта.Аналогично, когда глобальная переменная в main имеет то же имя, что и глобальная переменная в общем объекте, тогда будет использоваться экземпляр в main, даже если доступ к нему осуществляется из общего объекта.


Это так называемое взаимное расположение символов предназначено для имитации поведения статических библиотек.

Общий объект имеет таблицу указателей на свои функции, называемую таблицей связывания процедур (PLT), и таблицу указателей на его переменные, называемую глобальной таблицей смещений (GOT), для реализации этой функции «переопределения».Все обращения к функциям и общедоступным переменным проходят через эти таблицы.

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

Вы можетечитать больше из этой статьи: http://www.agner.org/optimize/optimizing_cpp.pdf

6 голосов
/ 16 июля 2016

Незначительное дополнение к уже опубликованным ответам: объектные файлы, не скомпилированные, чтобы быть независимыми от позиции, можно перемещать;они содержат записи таблицы перемещения.

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

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

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

Очевидно, что любая попытка рандомизировать адрес загрузки библиотеки между программамии / или экземпляры программы (чтобы уменьшить вероятность создания эксплуатируемого шаблона) сделают такие случаи общими, а не редкими, поэтому, когда система включила эту возможность, следует делать каждую попытку компилировать все библиотеки общих объектов, чтобы они были позиционированынезависимый.

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

...