[英]Misunderstanding concerning g++, dynamic and static linking on linux vs windows
[英]Dynamic linking - Linux Vs. Windows
在 Windows 下,當我在 MSVC 的 DLL 項目中編譯 C/C++ 代碼時,我得到 2 個文件:
MyDll.dll
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.