Опция 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
изменить: в ответ на комментировать.
Если ваш код скомпилирован с помощью-fPIC, он подходит для включения в библиотеку - библиотека должна иметь возможность перемещаться из своего предпочтительного местоположения в памяти на другой адрес, может быть другая уже загруженная библиотека по адресу, который предпочитает ваша библиотека.
Я попытаюсь объяснить то, что уже было сказано, более простым способом.
всякий раз, когда загружается общая библиотека, загрузчик (код в ОС, который загружает любую запущенную программу) изменяет некоторые адреса в коде в зависимости от того, куда был загружен объект.
в приведенном выше примере "111"в коде без PIC записывается загрузчиком при первой загрузке.
для не общих объектов, вы можете захотеть, чтобы это было так, потому что компилятор может сделать некоторые оптимизации на этом коде.
для общего объекта, если другой процесс захочет " связать "с этим кодом, он должен прочитать его на те же виртуальные адреса, или" 111 " не будет иметь смысла. но это виртуальное пространство может уже использоваться во втором процессе.
код, встроенный в общие библиотеки, обычно должен быть независимым от позиции кодом, так что общая библиотека может быть легко загружена по (более или менее) любому адресу в памяти. Элемент
-fPIC
опция гарантирует, что GCC производит такой код.
дополнительно...
каждый процесс имеет одинаковое виртуальное адресное пространство (если рандомизация виртуального адреса остановлена с помощью флага в ОС linux) (Для более подробной информации отключить и повторно включить рандомизация адресного пространства только для себя)
поэтому, если его один exe без общей ссылки (гипотетический сценарий), то мы всегда можем дать тот же виртуальный адрес той же инструкции asm без какого-либо вреда.
но когда мы хотим связать общие объект exe, то мы не уверены в начальном адресе, назначенном общему объекту, поскольку он будет зависеть от порядка, в котором были связаны общие объекты.При этом инструкция asm inside .so всегда будет иметь разный виртуальный адрес в зависимости от процесса его привязки.
таким образом, один процесс может дать начальный адрес К. Так как 0x45678910 в своем собственном виртуальном пространстве и другой процесс в то же время может дать начальный адрес 0x12131415 и если они не используют относительный обращаясь, так вообще не получится.
поэтому они всегда должны использовать режим относительной адресации и, следовательно, опцию fpic.
ссылка на функцию в динамической библиотеке разрешается при загрузке библиотеки или во время выполнения. Таким образом, как исполняемый файл, так и динамическая библиотека загружаются в память при запуске программы. Адрес памяти, по которому загружается динамическая библиотека, не может быть определен в заранее, потому что фиксированный адрес может конфликтовать с другой динамической библиотекой, требующей того же адреса.
есть два широко используемых методов для борьбы с этим проблема:
1.Переселение. Все указатели и адреса в коде при необходимости изменяются в соответствии с фактическим адресом загрузки. Перемещение выполняется компоновщиком и загрузчиком.
2.Позиционно-независимый код. Все адреса в коде относятся к текущей позиции. Общие объекты в Unix-подобных системах по умолчанию используют позиционно-независимый код. Это менее эффективно, чем перемещение, если программа выполняется в течение длительного времени, особенно в 32-разрядной версии режим.
на имя "позиционно-независимый код" на самом деле подразумевает следующее:
раздел кода не содержит абсолютных адресов, которые нуждаются в перемещении, но только относительно себя адреса. Таким образом, раздел кода может быть загружен по произвольному адресу памяти и совместно использоваться несколькими процессами.
раздел данных не является общим для нескольких процессов, поскольку он часто содержит записываемый данные. Поэтому раздел данных может содержать указатели или адреса, которые нужен переезд.
все публичные функции и публичные данные могут быть переопределены в Linux. Если функция в основном исполняемый файл имеет то же имя, что и функция в общем объекте, то версия в main будет иметь приоритет не только при вызове из main, но и когда вызывается из общего объекта. Аналогично, когда глобальная переменная в main имеет то же самое имя как глобальная переменная в общей папке объект, то экземпляр в main будет используется, даже при доступе из общего объекта.
эта так называемая интерпозиция символов предназначена для имитации поведения статических библиотек.
общий объект имеет таблицу указателей на его функции, называемую таблицей связей процедур (PLT) и таблицей указателей на его переменные, называемые global offset table (GOT), чтобы реализовать эту функцию "переопределения". Все доступы к функциям и общественности переменные проходят через эту таблицу.
p.s. там, где динамической компоновки нельзя избежать, существуют различные способы избежать временных функций независимого от позиции кода.
вы можете прочитать больше из этой статьи:http://www.agner.org/optimize/optimizing_cpp.pdf
незначительное дополнение к уже опубликованным ответам: объектные файлы, не скомпилированные для независимости от позиции, перемещаются; они содержат записи таблицы перемещения.
эти записи позволяют загрузчику (тот бит кода, который загружает программу в память) переписать абсолютные адреса для настройки на фактический адрес загрузки в виртуальном адресном пространстве.
операционная система будет пытаться совместно использовать одну копию" библиотеки общих объектов", загруженную в память со всеми программы, связанные с той же библиотекой общих объектов.
поскольку адресное пространство кода (в отличие от разделов пространства данных) не обязательно должно быть непрерывным, и поскольку большинство программ, которые ссылаются на определенную библиотеку, имеют довольно фиксированное дерево зависимостей библиотеки, это происходит в большинстве случаев. В тех редких случаях, когда есть несоответствие, да, может потребоваться иметь две или более копий библиотеки общих объектов в памяти.
очевидно, любая попытка рандомизации адрес загрузки библиотеки между программами и / или экземплярами программ (чтобы уменьшить возможность создания эксплуатируемого шаблона) сделает такие случаи распространенными, а не редкими, поэтому, когда система включила эту возможность, следует сделать все попытки скомпилировать все библиотеки общих объектов, чтобы быть независимыми от позиции.
поскольку вызовы в эти библиотеки из тела основной программы также будут сделаны перемещаемыми, это делает гораздо менее вероятным, что общая библиотека будет должны быть скопированы.