Як працює бібліотека імпорту? Деталі?


88

Я знаю, що для вундеркіндів це може здатися цілком базовим. Але я хочу зробити це кристально чистим.

Коли я хочу використовувати DLL Win32, зазвичай я просто викликаю API, такі як LoadLibrary () та GetProcAdderss (). Але нещодавно я розробляю DirectX9, і мені потрібно додати файли d3d9.lib , d3dx9.lib тощо.

Я досить чув, що LIB призначений для статичного зв’язування, а DLL - для динамічного зв’язування.

Тож моє поточне розуміння полягає в тому, що LIB містить реалізацію методів і статично пов’язаний під час зв’язку як частина остаточного файлу EXE. Хоча DLL динамічно завантажується під час виконання і не є частиною остаточного файлу EXE.

Але іноді разом із файлами DLL є деякі файли LIB , тому:

  • Для чого потрібні ці файли LIB?
  • Як вони досягають того, для чого призначені?
  • Чи є інструменти, які дозволяють мені перевіряти внутрішні частини цих файлів LIB?

Оновлення 1

Перевіривши wikipedia, я пам’ятаю, що ці файли LIB називаються бібліотекою імпорту . Але мені цікаво, як це працює з моєю основною програмою та бібліотеками DLL для динамічного завантаження.

Оновлення 2

Подібно до того, як сказав RBerteig, у файлах LIB є деякі коди заглушки, народжені разом з DLL. Тож послідовність виклику повинна бути такою:

Моя основна програма -> заглушка в LIB -> реальна цільова DLL

То яка інформація повинна міститися в цих LIB? Я міг подумати про таке:

  • Файл LIB повинен містити повний шлях до відповідної DLL; Отже, DLL може бути завантажений під час виконання.
  • Відносна адреса (або зміщення файлу?) Точки входу кожного методу експорту DLL повинна бути закодована в заглушці; Тож можна зробити правильні стрибки / виклики методів.

Я правий у цьому? Чи є щось більше?

BTW: Чи існує якийсь інструмент, який може перевірити бібліотеку імпорту? Якщо я зможу це побачити, сумнівів більше не буде.


4
Я бачу, що ніхто не звертався до останньої частини Вашого запитання, що стосується інструментів, які можуть перевірити бібліотеку імпорту. З Visual C ++ існує принаймні два способи зробити це: lib /list xxx.libі link /dump /linkermember xxx.lib. Див. Це запитання щодо переповнення стека .
Алан

Також, dumpbin -headers xxx.libнадає деяку більш детальну інформацію, порівняно з утилітами libта link.
m_katsifarakis

Відповіді:


102

Посилання на файл DLL може відбуватися неявно під час компіляції або явно під час запуску. У будь-якому випадку DLL завантажується в простір пам'яті процесів, і всі експортовані точки входу доступні додатку.

Якщо явно використовується під час виконання, ви використовуєте LoadLibrary()і GetProcAddress()для ручного завантаження DLL і отримання покажчиків на функції, які вам потрібно викликати.

Якщо неявно пов’язані під час побудови програми, то заглушки для кожного експорту DLL, що використовується програмою, зв’язуються з програмою з бібліотеки імпорту, і ці заглушки оновлюються, коли EXE та DLL завантажуються під час запуску процесу. (Так, я тут трохи більше спростив ...)

Ці заглушки повинні надходити звідкись, а в ланцюжку інструментів Microsoft вони надходять із спеціальної форми файлу .LIB, яка називається бібліотекою імпорту . Необхідний .LIB зазвичай створюється одночасно з DLL і містить заглушку для кожної функції, експортованої з DLL.

Заплутано, але статична версія тієї ж бібліотеки також буде надіслана як файл .LIB. Немає тривіального способу їх відокремити, за винятком того, що LIB, які є бібліотеками імпорту для DLL, зазвичай будуть меншими (часто набагато меншими), ніж відповідні статичні LIB.

До речі, якщо ви використовуєте ланцюжок інструментів GCC, вам насправді не потрібні бібліотеки імпорту, щоб відповідати вашим бібліотекам DLL. Версія Gnu linker, перенесена на Windows, розуміє DLL-файли безпосередньо і може на льоту синтезувати більшість необхідних заглушок.

Оновлення

Якщо ви просто не можете встояти, знаючи, де насправді знаходяться всі гайки і болти, і що насправді відбувається, у MSDN завжди є що допомогти. Стаття Метта П'єтрека Поглиблений огляд портативного виконуваного файлу Win32 - це дуже повний огляд формату файлу EXE та способу його завантаження та запуску. Її навіть було оновлено, щоб охопити .NET і більше, оскільки вона спочатку з’явилася в журналі MSDN ca. 2002 рік.

Крім того, може бути корисно знати, як точно дізнатись, які бібліотеки DLL використовуються програмою. Інструментом для цього є Dependency Walker, він же залежний.exe. Його версія входить до складу Visual Studio, але остання версія доступна у її автора за адресою http://www.dependencywalker.com/ . Він може ідентифікувати всі бібліотеки DLL, які були вказані під час зв’язку (як раннє завантаження, так і затримка навантаження), а також може запустити програму та спостерігати за будь-якими додатковими бібліотеками DLL, які вона завантажує під час роботи.

Оновлення 2

Я переформулював деякі попередні тексти, щоб пояснити їх при повторному читанні та використати терміни мистецтва, що є неявними та явними посиланнями для узгодження з MSDN.

Отже, у нас є три способи, якими функції бібліотеки можуть бути доступними для використання програмою. Тоді очевидним подальшим запитанням є: "Як вибрати, який шлях?"

Статичне зв’язування - це те, як пов’язана основна частина програми. Всі ваші файли об’єктів перераховані та пов’язані разом у файл EXE. Попутно компонувальник піклується про дрібні клопоти, такі як виправлення посилань на глобальні символи, щоб ваші модулі могли викликати функції один одного. Бібліотеки також можуть бути статично пов’язані. Об'єктні файли, що складають бібліотеку, збираються бібліотекарем у файл .LIB, який компонувальник шукає модулі, що містять необхідні символи. Одним із ефектів статичного зв’язування є те, що до нього прив’язані лише ті модулі з бібліотеки, які використовуються програмою; інші модулі ігноруються. Наприклад, традиційна математична бібліотека C включає багато функцій тригонометрії. Але якщо ви посилаєтеся проти нього і використовуєтеcos(), ви не отримаєте копію коду для sin()або tan()якщо ви також не викликали ці функції. Для великих бібліотек з багатим набором функцій це вибіркове включення модулів є важливим. На багатьох платформах, таких як вбудовані системи, загальний розмір коду, доступного для використання в бібліотеці, може бути великим порівняно з простором, доступним для зберігання виконуваного файлу в пристрої. Без вибіркового включення було б важче керувати деталями побудови програм для цих платформ.

Однак наявність копії тієї самої бібліотеки в кожній запущеній програмі створює тягар для системи, яка зазвичай запускає багато процесів. За правильного типу системи віртуальної пам'яті сторінки пам’яті, що мають однаковий вміст, повинні існувати лише один раз у системі, але можуть використовуватися багатьма процесами. Це створює перевагу для збільшення шансів на те, що сторінки, що містять код, можуть бути ідентичними деякій сторінці в якомога більшій кількості запущених процесів. Але якщо програми статично посилаються на бібліотеку середовища виконання, то кожна з них має різне поєднання функцій, кожна з яких викладена в картці пам'яті в різних місцях, і не так багато спільних сторінок коду, якщо це не програма, яка сама по собі є запустити більше, ніж процес. Тож ідея DLL отримала ще одну, головну, перевагу.

Бібліотека DLL для бібліотеки містить усі її функції, готові до використання будь-якою клієнтською програмою. Якщо багато програм завантажують цю DLL, усі вони можуть спільно використовувати її кодові сторінки. Усі виграють. (Ну, поки ви не оновите DLL новою версією, але це не є частиною цієї історії. Пекло Google DLL для тієї сторони казки.)

Отже, перший великий вибір, який потрібно зробити при плануванні нового проекту, - це між динамічним та статичним зв’язком. За допомогою статичного зв’язку у вас менше файлів для встановлення, і ви не застраховані від стороннього оновлення використовуваної вами DLL. Однак ваша програма є більшою, і вона не є настільки хорошим громадянином екосистеми Windows. За допомогою динамічного зв’язування вам потрібно встановити більше файлів, можливо, у вас виникнуть проблеми із оновленням використовуваної вами DLL третьою стороною, але, як правило, ви ставитесь до інших процесів у системі.

Великою перевагою DLL є те, що її можна завантажувати та використовувати без перекомпіляції та навіть перевстановлення основної програми. Це може дозволити сторонньому постачальнику бібліотек (наприклад, Microsoft і середовище виконання C) виправити помилку у своїй бібліотеці та розповсюдити її. Після того, як кінцевий користувач встановить оновлену бібліотеку DLL, вони негайно отримують переваги виправлення помилки у всіх програмах, що використовують цю бібліотеку DLL. (Якщо це не зламає речі. Див. DLL Hell.)

Інша перевага полягає в різниці між неявним та явним завантаженням. Якщо ви докладете максимум зусиль для явного завантаження, то DLL, можливо, навіть не існувало, коли програма була написана та опублікована. Це дозволяє використовувати механізми розширення, які можуть виявляти та завантажувати плагіни, наприклад.


3
Видалення мого допису та підтримка цього, тому що ви пояснюєте речі набагато краще, ніж я;) Гарна відповідь.
там

2
@RBerteig: Дякую за чудову відповідь. Просто трохи корекції, згідно тут ( msdn.microsoft.com/en-us/library/9yd93633.aspx ), там вже 2 типу динамічного зв'язування з DLL, час завантаження неявні сполучною і час виконання явного зв'язуванням . Немає посилання під час компіляції . Зараз мені цікаво, в чому різниця між традиційним статичним зв'язуванням (посилання на файл * .lib, що містить повну реалізацію) і динамічним зв'язком під час завантаження до DLL (за допомогою бібліотеки імпорту)?
smwikipedia

1
Продовжити: Які плюси та мінуси статичного зв’язування та динамічного зв’язування під час завантаження ? Здається, ці два підходи завантажують усі необхідні файли в адресний простір на початку процесу. Навіщо нам 2 з них? Дякую.
smwikipedia

1
Ви можете скористатися інструментом, таким як "objdump", щоб зазирнути всередину файлу .lib і з'ясувати, чи це бібліотека імпорту чи справжня статична бібліотека. в Linux при перехресній компіляції до цілі Windows можна запустити 'ar' або 'nm' у файлах .a (версія mingw .lib-файлів) і зауважити, що бібліотеки імпорту мають загальні імена файлів .o і не мають коду (просто інструкція "jmp"), тоді як статичні бібліотеки мають багато функцій і коду всередині.
дон яскравий

1
Невелике виправлення: Ви також можете неявно посилатися під час виконання. Підтримка Linker для DLL із затримкою пояснює це детально. Це корисно, якщо ви хочете динамічно змінювати шлях пошуку DLL або витончено вирішувати проблеми з роздільною здатністю імпорту (для підтримки нових функцій ОС, але, як і раніше, на старих версіях).
IIНевидимий

5

Ці файли бібліотеки імпорту .LIB використовуються в наступній властивості проекту Linker->Input->Additional Dependenciesпри створенні групи DLL- файлів , які потребують додаткової інформації під час зв’язку, яка надається файлами бібліотеки імпорту .LIB. У наведеному нижче прикладі, щоб не отримувати помилки компонувальника, мені потрібно посилатися на DLL A, B, C та D через їх файли lib. (зауважте, що компонувальник знаходить ці файли, можливо, вам доведеться включити шлях до їх розгортання, Linker->General->Additional Library Directoriesінакше ви отримаєте помилку збірки про те, що не вдалося знайти жоден із наданих файлів lib.)

Посилання-> Введення-> Додаткові залежності

Якщо ваше рішення створює всі динамічні бібліотеки, можливо, вам вдалося уникнути цієї явної специфікації залежностей, натомість покладаючись на посилання на прапорці, виставлені в Common Properties->Framework and Referencesдіалоговому вікні. Здається, ці прапорці автоматично здійснюють зв’язування від вашого імені за допомогою файлів * .lib. Основи та посилання

Однак, як сказано в загальних властивостях, що не залежить від конфігурації та платформи. Якщо вам потрібно підтримати змішаний сценарій збірки, як у нашому додатку, у нас була конфігурація збірки для відображення статичної збірки та спеціальна конфігурація, яка побудувала обмежену збірку підмножини збірок, які були розгорнуті як динамічні бібліотеки. Я використовував Use Library Dependency Inputs і Link Library Dependenciesпрапори, встановлені в true в різних випадках, щоб змусити речі будувати, а пізніше зрозумівши, щоб спростити речі, але, вводячи свій код у статичні збірки, я ввів масу попереджень компонувальника, і збірка була неймовірно повільною для статичних збірок. Я закінчив, представляючи купу подібних попереджень ...

warning LNK4006: "bool __cdecl XXX::YYY() already defined in CoreLibrary.lib(JSource.obj); second definition ignored  D.lib(JSource.obj)

І я закінчив використовувати ручну специфікацію, Additional Dependenciesщоб задовольнити компоновщик для динамічних збірок, зберігаючи при цьому статичні конструктори щасливими, не використовуючи загальну властивість, яка їх сповільнювала. Коли я розгортаю динамічну побудову підмножини, я розгортаю лише файли dll, оскільки ці файли lib використовуються лише під час посилання, а не під час виконання.


3

Існує три типи бібліотек: статична, спільна та динамічно завантажена бібліотеки.

Статичні бібліотеки пов'язані з кодом на етапі зв'язування, тому вони фактично знаходяться у виконуваному файлі, на відміну від спільної бібліотеки, яка має лише заглушки (символи) для пошуку у файлі спільної бібліотеки, який завантажується під час виконання до викликається основна функція.

Динамічно завантажувані схожі на спільні бібліотеки, за винятком того, що вони завантажуються тоді, коли і якщо потреба виникає в коді, який ви написали.


@ Дякую zacsek. Але я не впевнений у Вашому твердженні щодо спільної бібліотеки.
smwikipedia

@smwikipedia: У Linux їх є, я їх використовую, тож вони точно існують. Також читайте: en.wikipedia.org/wiki/Library_(computing)
Zoltán Szőcs

3
Це тонка різниця. Спільна та динамічна бібліотеки - це файли DLL. Різниця полягає в тому, коли вони завантажені. Спільні бібліотеки завантажуються ОС разом із EXE. Динамічні бібліотеки завантажуються за допомогою виклику коду LoadLibrary()та відповідних API.
RBerteig

З [1] я прочитав, що DLL - це реалізація корпорацією Майкрософт концепції спільної бібліотеки. [1]: en.wikipedia.org/wiki/Dynamic-link_library#Import_libraries
smwikipedia

Я не згоден з тим, що це незначна різниця, з точки зору програмування величезна різниця, чи спільна бібліотека завантажена динамічно чи ні (якщо вона динамічно завантажена, то для доступу до функцій потрібно додати шаблонний код).
Zoltán Szőcs

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.