Динамично свързване на 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), прочетена от диска.

windows

Фигура 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 и след това ще се опитаме да ускорим процеса на свързване и на двете системи. Поддържате връзка.

  1. Линкери и зареждащи устройства от Джон Левин.
  2. Изходният код на Glibc-2.3.3.
  3. Предварителна връзка от Якуб Йелинек, RedHat Inc.
  4. Вътре в Windows Задълбочен поглед във формата на преносим изпълним файл, част2
  5. Библиотеката на MSDN
  6. UNIX Man страници

Реджи Томас е инженер-разработчик от екипа на Symnatec Corp. Сред интересите му са математически науки, компютърна сигурност, компилатори, създаване на финансови модели. Bhasker Reddy също участва в разработването на софтуер за Symantec Corp.; Интересите му включват компилатори, системно програмиране и теория на операционните системи.