Динамично свързване на Windows и Linux
Тази статия обсъжда концепцията за споделени библиотеки както в Windows, така и в Linux, като предоставя общ преглед на различните структури от данни, за да обясни как работи динамичното свързване на тези системи.
Превод Александър Тимаков
Тази статия обсъжда концепцията за споделени библиотеки както в Windows, така и в Linux, като предоставя общ преглед на различните структури от данни, за да обясни как работи динамичното свързване на тези системи. Този документ ще бъде полезен за разработчици с интерес към сигурността, които изискват високоскоростно динамично свързване и предполагат известни предварителни познания за процеса на динамично свързване.
Първата част представя основите на това как работи процедурата на Windows и Linux, но фокусът ще бъде изместен към Linux. Следващият път, във втората част на статията, ще обсъдим как работи процедурата за обвързване в Windows и след това ще преминем към сравняване на двете системи.
Статични и динамични библиотеки.
Като първо приближение библиотеките могат да бъдат разделени на статични и динамични.
Статичните библиотеки са колекция от обектни файлове и традиционно имат разширение ".a" на UNIX-подобни операционни системи и ".lib" на Windows. Когато програма се свърже към статична библиотека, собственият код от обектните файлове за всяка библиотечна функция, използвана от програмата, се копира от библиотеката в крайния изпълним файл.
Независим от адреса код (Win32 DLL и ".so")
Динамично свързване на Linux.
Структури от данни в ELF
Глобална офсетна таблица.
Таблица за свързване на процедури.
В секцията .dynamic типовете, които са важни за насса:
DT_NEEDED:Елементът съдържа отместването в таблицата на низовете, завършващи на NULL, низовете съдържат имената на необходимите библиотеки. Това отместване е индекс за записите в таблицата DT_STRTAB.
DT_HASH:Елементът съдържа отместването за символната хеш таблица, към която сочи DT_SYMTAB.
Хеш - таблица със символи
Както Buckets, така и Chain масивите съдържат индекси на символна таблица. Изчислява се хеш функция за символа, който трябва да бъде намерен, и хешът %nBuckets се използва като индекс в масива bucket[]. Елементът от bucket[] съдържа индекса symindx както за масива chain[], така и за таблицата със символи. Ако записът в таблицата със символи не съвпада, следващият елемент от таблицата със символи със същата хеш стойност се извлича с помощта на индекса, получен от chain[symindex].
Как работи
Имаме два метода за указване на обекти, които да бъдат предварително заредени: чрез променливата на средата LD_PRELOAD или чрез файла /etc/ld.so.preload. Последното може да се използва, когато от съображения за сигурност не използвате променливи на средата. Товарачът добавя DT_NEEDED към списъка за предварително зареждане на библиотеката, който посочихме.
Редакторът на връзки съхранява в паметта списък с вече свързани („свързани“) библиотеки за всеки файл (с типа на запис в списъка struct link_map, описан от параметъра dl_loaded в struct rtld_global). Редакторът използва хеш таблицата (DT_HASH), предоставена във файла ELF, за да ускори процедурата за търсене на знаци.
След изграждане на списъка от свързани библиотеки с всички зависимости, товарачът осъществява достъп до всяка библиотека и обработва нейния списък с отмествания, като попълва глобалната таблица на отместванията за библиотеката(GOT) и извършване на необходимите компенсации.
Преглед на процедурата за обвързване при поискване.
Снимка 1 . Изходният код е разглобен от gdb.
Нека започнем проследяването с инструкцията за повикване, показана на Фигура 1.
Адресът, посочен в инструкцията, е запис в таблицата за връзка на процедурата (PLT). Първите 4 записа в PLT (от които последните 2 са запазени) са еднакви за всички извиквания на процедури. Останалите са групирани в блокове от 3 записа, по един блок за всяка процедура. Това е показано на фигура 2.
Фигура 2. Няколко записа в началото на таблицата за връзка на процедурата (PLT).
Фигура 3. Глобална офсетна таблица (GOT), прочетена от диска.
Фигура 4. Обект в офсетната таблица (дължина 8 байта).
Фигура 5. Точка на прекъсване (точка на прекъсване).
Нека го разгледаме по-подробно:
Нека да разгледаме ELF _ MACHINE _ RUNTIME _ TRAMPOLINE , дефиниран в dl-machine. ч. Този код запазва регистрите и извиква функцията fixup():
На свой ред функцията за коригиране е дефинирана в dl-runtime.c. Масивът l_info, даден в структурата struct link_map, съдържа индексирани указатели към динамичния раздел.
От reloc->r_info вече получаваме индекс за таблицата със символи, за да го използваме за получаване на информация от таблицата (reloc->r_offset+1->l_addr).
Функцията fixup() извиква _dl_lookup_symbol() за всеки запис в масива на библиотеката. Масивът съдържа елементи от тип r_scope_elem за библиотеки и е част от цялостното поле за търсене. Тази структура се попълва по време на зареждане.
do_lookup е дефиниран в FCT във файла do-loopup.h. Нека да разгледаме тази процедура, сякаш е написана на обикновен английски:
Да се върнем към fixup():
Обратно към dl-машината. ч, възстановява запазените регистри:
Обобщавайки първата част
В тази част обсъдихме използването на динамично свързване за Linux и Windows, като се фокусирахме основно върху Linux. В част 2 ще разгледаме по-подробно този процес в Windows в същия дух, ще споменем „мързеливо свързване“ и Delay Load Helper и след това ще се опитаме да ускорим процеса на свързване и на двете системи. Поддържате връзка.
- Линкери и зареждащи устройства от Джон Левин.
- Изходният код на Glibc-2.3.3.
- Предварителна връзка от Якуб Йелинек, RedHat Inc.
- Вътре в Windows Задълбочен поглед във формата на преносим изпълним файл, част2
- Библиотеката на MSDN
- UNIX Man страници
Реджи Томас е инженер-разработчик от екипа на Symnatec Corp. Сред интересите му са математически науки, компютърна сигурност, компилатори, създаване на финансови модели. Bhasker Reddy също участва в разработването на софтуер за Symantec Corp.; Интересите му включват компилатори, системно програмиране и теория на операционните системи.