Я читав про Параметри GCC для конвенцій генерації коду , але не міг зрозуміти, що робить "Створення коду, незалежного від позиції" (PIC). Наведіть приклад, щоб пояснити мені, що це означає.
Я читав про Параметри GCC для конвенцій генерації коду , але не міг зрозуміти, що робить "Створення коду, незалежного від позиції" (PIC). Наведіть приклад, щоб пояснити мені, що це означає.
Відповіді:
Незалежний код позиції означає, що створений машинний код не залежить від того, щоб він знаходився за певною адресою для роботи.
Скажімо, скачки будуть генеруватися як відносні, а не абсолютні.
Псевдоскладання:
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, він підходить для включення до бібліотеки - бібліотека повинна бути в змозі переїхати зі свого бажаного місця в пам'яті на іншу адресу, може бути ще одна завантажена бібліотека за адресою, яку ваша бібліотека віддає перевагу.
-fPIC
під час компіляції програми або статичної бібліотеки, оскільки в процесі існуватиме лише одна основна програма, тому перенесення часу виконання не потрібен. У деяких системах програми все ще залишаються незалежними для підвищення безпеки.
Спробую пояснити те, що вже було сказано, більш простим способом.
Щоразу, коли завантажується спільна ліб, завантажувач (код в ОС, який завантажує будь-яку програму, яку ви запускаєте) змінює деякі адреси в коді залежно від того, куди був завантажений об'єкт.
У наведеному вище прикладі "111" в не-PIC-коді записується навантажувачем під час першого завантаження.
Для спільних об'єктів, можливо, ви хочете, щоб це було так, оскільки компілятор може зробити деякі оптимізації цього коду.
Для спільного об'єкта, якщо інший процес захоче "посилання" на цей код, він повинен прочитати його на ті самі віртуальні адреси, або "111" не матиме сенсу. але цей віртуальний простір вже може бути використаний у другому процесі.
Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.
Я думаю, що це неправильно, якщо їх компілюють із -fpic та причиною, чому -fpic існує, тобто з міркувань продуктивності або тому, що у вас є завантажувач, який не в змозі переїхати, або тому, що вам потрібно кілька копій в різних місцях або з багатьох інших причин.
Код, вбудований у спільні бібліотеки, як правило, повинен бути незалежним від позиції кодом, так що спільна бібліотека може легко завантажуватися за (більш-менш) будь-якою адресою в пам'яті. -fPIC
Варіант гарантує , що GCC виробляє такий код.
-fPIC
прапора? це не пов'язано з програмою? коли програма працює, операційна система завантажує її в пам'ять. Я щось пропускаю?
-fPIC
прапор, щоб забезпечити завантаження цієї вкладки на будь-яку віртуальну адресу в процесі, що пов'язує її? вибачте за подвійні коментарі минуло 5 хвилин не може редагувати попередній.
libwotnot.so
) та посилання на неї ( -lwotnot
). Посилаючись, вам не потрібно метушитися -fPIC
. Раніше було так, що при складанні спільної бібліотеки вам потрібно було переконатися, що -fPIC
використовувались усі об'єктивні файли, які потрібно вбудувати у спільну бібліотеку. Правила можуть бути змінені, оскільки компілятори складаються за PIC-кодом за замовчуванням в ці дні. Отже, те, що було критичним 20 років тому, а може бути важливим ще 7 років тому, є менш важливим у ці дні, я вважаю. Адреси поза ядром o / s - це "завжди" віртуальні адреси.
-fPIC
. Не передаючи цей прапор, згенерований код під час створення .so потрібно завантажувати на конкретні віртуальні адреси, які можуть бути використані?
Додавання далі ...
У кожному процесі є однаковий віртуальний адресний простір (Якщо рандомізацію віртуальної адреси зупинено за допомогою прапора в ОС Linux) (Детальніше Вимкніть та повторно увімкніть рандомізацію макета адресного простору лише для себе )
Отже, якщо його один exe без спільного посилання (Гіпотетичний сценарій), то ми завжди можемо надати ту саму віртуальну адресу тій же інструкції Asm без будь-якої шкоди.
Але коли ми хочемо пов’язати спільний об'єкт із exe, тоді ми не впевнені у стартовій адресі, призначеній спільному об'єкту, оскільки це буде залежати від порядку, яким були об'єднані спільні об'єкти. різні віртуальні адреси залежно від процесу, на який він пов'язується.
Таким чином, один процес може дати початкову адресу.
Тому вони завжди повинні використовувати режим відносної адресації і, отже, опцію fpic.
Посилання на функцію в динамічній бібліотеці вирішується під час завантаження бібліотеки або під час виконання. Тому і виконуваний файл, і динамічна бібліотека завантажуються в пам'ять при запуску програми. Адреса пам'яті, на яку завантажується динамічна бібліотека, не може бути визначена заздалегідь, оскільки фіксована адреса може стикатися з іншою динамічною бібліотекою, яка вимагає тієї самої адреси.
Існує два найпоширеніших методи вирішення цієї проблеми:
1.Релокація. Усі вказівники та адреси в коді змінюються, якщо необхідно, відповідно до фактичної адреси завантаження. Переселення здійснюють линкер і навантажувач.
2. Незалежний від положення код. Усі адреси в коді відносно поточного положення. Спільні об'єкти в системах, подібних Unix, за замовчуванням використовують незалежний від позиції код. Це менш ефективно, ніж переїзд, якщо програма триває тривалий час, особливо в 32-бітному режимі.
Назва " незалежний від позиції код " насправді означає:
Розділ коду не містить абсолютних адрес, які потребують переїзду, а лише власні відносні адреси. Тому розділ коду можна завантажувати за довільною адресою пам'яті та ділити між декількома процесами.
Розділ даних не поділяється між декількома процесами, оскільки він часто містить дані, що записуються. Тому розділ даних може містити покажчики або адреси, які потребують переїзду.
Усі публічні функції та загальнодоступні дані можуть бути замінені в Linux. Якщо функція в головному виконуваному файлі має те саме ім'я, що і функція в спільному об'єкті, то версія в магістралі матиме перевагу не тільки при виклику з головного, але і при виклику з спільного об'єкта. Так само, коли глобальна змінна в основному має те саме ім'я, що і глобальна змінна у спільному об'єкті, тоді головний екземпляр буде використаний, навіть коли звертатися до цього спільного об'єкта.
Ця так звана символьна інтерпозиція покликана імітувати поведінку статичних бібліотек.
Об'єкт, що поділяється, має таблицю покажчиків на його функції, звану таблицею зв’язків процедур (PLT) та таблицю покажчиків на його змінні, що називаються глобальною таблицею зміщення (GOT), щоб реалізувати цю функцію "переосмислення". Усі звернення до функцій та загальнодоступних змінних проходять через ці таблиці.
ps Якщо динамічного зв’язку неможливо уникнути, існують різні способи уникнути функцій, що споживають час, незалежного від позиції коду.
Ви можете прочитати більше з цієї статті: http://www.agner.org/optimize/optimizing_cpp.pdf
Незначне доповнення до вже опублікованих відповідей: об'єктні файли, не складені для незалежної позиції, переміщуються; вони містять записи таблиці переміщення.
Ці записи дозволяють завантажувачу (той біт коду, який завантажує програму в пам'ять) перезаписати абсолютні адреси, щоб налаштувати фактичну адресу завантаження у віртуальний адресний простір.
Операційна система спробує поділитися єдиною копією "спільної бібліотеки об'єктів", завантаженою в пам'ять, з усіма програмами, які пов'язані з тією самою спільною бібліотекою об'єктів.
Оскільки кодовий простір адрес (на відміну від розділів простору даних) не повинен бути суміжним і оскільки більшість програм, що посилаються на певну бібліотеку, мають досить фіксоване дерево залежності бібліотеки, це досягає успіху більшу частину часу. У тих рідкісних випадках, коли є невідповідність, так, може знадобитися дві або більше копій спільної бібліотеки об'єктів у пам'яті.
Очевидно, будь-яка спроба рандомізувати адресу завантаження бібліотеки між програмами та / або програмними екземплярами (щоб зменшити можливість створення експлуатованого шаблону) зробить такі випадки поширеними, а не рідкісними, тому, коли система включила цю можливість, слід робити всі спроби компілювати всі бібліотеки спільних об'єктів, щоб вони були незалежними.
Оскільки виклики в ці бібліотеки з основної програми також будуть перенесені, це значно зменшує ймовірність копіювання спільної бібліотеки.