Вариант GCC-fPIC

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

6 ответов


Position Independent Code означает, что сгенерированный машинный код не зависит от расположения по определенному адресу для работы.

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

псевдо-сборка:

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

EDIT: в ответ на комментировать.

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


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

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

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

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

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


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


дополнительно...

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

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

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

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

поэтому они всегда должны использовать относительный режим адресации и, следовательно, возможность спос.


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


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

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

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


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

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

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

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


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

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

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

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


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

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

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

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

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

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