簡體   English   中英

動態鏈接 - Linux 與。 Windows

[英]Dynamic linking - Linux Vs. Windows

在 Windows 下,當我在 MSVC 的 DLL 項目中編譯 C/C++ 代碼時,我得到 2 個文件:

  1. MyDll.dll
  2. MyDll.lib

據我了解, MyDll.lib包含某種指針表,指示 dll 中的函數位置。 當使用這個 dll 時,比如說在一個 exe 文件中, MyDll.lib在鏈接期間嵌入到 exe 文件中,因此在運行時它“知道”函數位於MyDll.dll中的位置並可以使用它們。

但是,如果我在 Linux 下編譯相同的代碼,我只會得到一個沒有MySo.a MySo.so相當於 Linux 中的lib文件),那么 Linux 下的可執行文件如何知道函數在MySo.so中的位置,如果鏈接期間沒有嵌入任何內容?

MSVC linker 可以將 object 文件 (.obj) 和 object 庫 (.lib) 鏈接在一起,以生成一個.EXE 或 a16AD70384E402759

To link with a DLL, the process in MSVC is to use a so-called import library (.LIB) that acts as a glue between the C function names and the DLL's export table (in a DLL a function can be exported by name or 按序數- 后者通常用於未記錄的 API)。

However, in most cases the DLL export table has all the function names and thus the import library (.LIB) contains largely redundant information (" import function ABC -> exported function ABC ", etc).
甚至可以從現有的.DLL生成.LIB。

其他平台的鏈接器沒有這個“特性”,可以直接鏈接動態庫。

在 Linux 上,linker(不是動態鏈接器)搜索鏈接時指定的共享庫,並在可執行文件中創建對它們的引用。 當動態 linker 加載這些可執行文件時,它會將所需的共享庫加載到 memory 並解析符號,從而允許運行二進制文件。

MySo.a如果創建,實際上將包含要直接鏈接到二進制文件中的符號,而不是 Windows 上使用的“符號查找表”。

rustyx 的回答比我更徹底地解釋了 Windows 上的過程; 我已經很久沒有使用 Windows 了。

The difference you are seeing is more of an implementation detail - under the hood both Linux and Windows work similarly - you code calls a stub function which is statically linked in your executable and this stub then loads DLL/shlib if necessary (in case of delayed loading ,否則在程序啟動時加載庫)和(在第一次調用時)通過GetProcAddress / dlsym解析符號。

唯一的區別是,在 Linux 上,這些存根函數(稱為 PLT 存根)是在您將應用程序與動態庫鏈接時動態生成的(庫包含足夠的信息來生成它們),而在 Windows 上,它們是在 Z58540E40D711AD 本身為在單獨的.lib文件中創建。

這兩種方法非常相似,實際上可以在 Linux 上模仿 Windows 導入庫(參見Implib.so項目)。

在 Linux 上,您將MySo.so傳遞給 linker,它能夠僅提取鏈接階段所需的內容,並提供運行時需要MySo.so的引用。

.dll.so是共享庫(在運行時鏈接),而.a.lib是 static 庫(在編譯時鏈接)。 這在 Windows 和 Linux 之間沒有區別。

不同之處在於,它們是如何處理的。 注意:區別僅在於海關,它們是如何使用的。 使 Linux 建立在 Windows 方式上並不會太難,反之亦然,但實際上沒有人這樣做。

如果我們使用 dll,或者甚至從我們自己的二進制文件中調用 function,則有一種簡單明了的方法。 例如,在 C 中,我們看到:

int example(int x) {
  ...do_something...
}

int ret = example(42);

但是,在 asm 級別上,可能存在許多差異。 比如在x86上,執行了一個call操作碼,棧上給出了42 或在某些寄存器中。 或任何地方。 沒有人知道在寫 dll 之前,它將如何使用。 或者項目將如何使用它,可能是用現在甚至不存在的編譯器(或語言)編寫的(或者對於 dll 的開發人員來說是未知的)。

例如,默認情況下,C 和 Pascal 都會從堆棧中放入 arguments (並獲取返回值) -但它們的執行順序不同 您還可以通過一些 - 編譯器相關 - 優化在寄存器中的函數之間交換 arguments。

如您所見,Windows 自定義是構建 dll,我們還使用它創建了一個最小的.a / .lib 這個最小的 static 庫只是一個包裝器,通過它可以到達 dll 的符號(函數)。 這會進行所需的 asm 級調用轉換。

它的優點是兼容性。 它的缺點是,如果你只有一個.dll,你可能很難弄清楚它的函數是如何被調用的。 如果 dll 的開發人員沒有給您.a ,這會使 dll 的使用成為一項黑客任務。 因此,它主要服務於封閉性目的,例如,更容易為 SDK 獲得額外的現金。

它的另一個缺點是即使您使用動態庫,也需要靜態編譯這個小包裝器。

在 Linux 中,dll 的二進制接口是標准的,遵循 C 約定。 因此,不需要.a並且共享庫之間存在二進制兼容性,作為交換,我們沒有 microsoft 自定義的優勢。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM