1. 程式人生 > >MSVC vs. MinGW 之 (lib,dll,def,obj,exe) vs (a,dll,def,o,exe) 玩轉攻略手記

MSVC vs. MinGW 之 (lib,dll,def,obj,exe) vs (a,dll,def,o,exe) 玩轉攻略手記

轉自:http://hi.baidu.com/kaienfr/item/0d12b1f34cb3eeda6225d2b1

一份粗糙的研究記錄,有待補完和整理。

MinGW:
c -> o           gcc -c a.c
c -> exe         gcc a.c libs.o -o a.exe (從主程式a.c,附加libs,生成a.exe)
o -> exe         gcc a.o b.o ... -o main.exe
c -> dll,def,a   gcc a.c -shared -o a.dll -Wl,--output-def,a.def,--out-implib,liba.a
a -> dll           a2dll liba.a 
dll -> a:          dlltool --dllname a.dll --def a.def --output-lib liba.a (需要def檔案)
a -> def:        dumpbin /exports lib.a > lib.def (在windows上呼叫,def需要修改)
dll -> def :      pexports a.dll -o > a.def (這裡的-o是指給函式標序號)
lib -> def :      reimp -d a.lib
lib -> a:          (for __cdecl functions in most case) reimp a.lib; (for __stdcall functions)

MSVC:
c -> lib     cl /LD a.c (注意已經定義了export列表)
c -> dll     cl /LD a.c
c -> obj     cl /c a.c
c -> exe     cl a.c /out:a.exe
dll ->lib   lib /machine:ix86 /def:a.def /out:a.lib (需要def檔案)
obj ->lib   lib a.obj b.obj... /out:mylib.lib
dll ->def   DUMPBIN a.dll /EXPORTS /OUT:a.def (生成的def需要做修正)
lib ->def   reimp -d a.lib (這個要在MSYS+MinGW下用)

關於這些工具的適用範圍可以很容易的理解和記憶。

dll和exe都是PE檔案,所以可以使用pexports.

lib和a是靜態庫檔案,都是歸檔型別,不是PE格式。所以不能使用pexports.

dll可以使用dlltool.

lib可以使用lib, 和reimp(lib->a工具)

所有的bin檔案,包括dll,exe,lib,a都可以使用dumpbin.

參考:

本篇測試用程式碼:

1. main.cpp

#include <iostream>
#include <stdlib.h>
#include "mylib.h"

using namespace std;

int main()
{
char str[]="Hello world!";
printhello(str);

return 0;
}

2. mylib.cpp

#include <iostream>
#include <stdlib.h>
#include "mylib.h"

using namespace std;

void EXPORT printhello(char *str)
{
cout << str << endl;
}

3. mylib.h


#define EXPORT __declspec(dllexport)

extern "C"
{
void EXPORT printhello(char *str);
}

關於DLL的定義和使用:
1. 需要外部呼叫的函式,定義的時候要在函式宣告前加入
__declspec(dllexport)
方便起見,可以定義成巨集
#define EXPORT __declspec(dllexport)
然後在定義函式宣告的時候使用,例如:
void EXPORT printhello(char *str);

只有註明EXPORT的函式才能出現在dll的輸出表中,外部函式才能呼叫。
關於函式呼叫約定__cdecl 還是 __stdcall。我們可以在輸出函式名前新增,也可以用cl編譯的時候指明
/Gd 使用 __cdecl 呼叫約定 (C declaration,是C和C++預設格式),手動堆疊平衡(支援可變引數)
/Gz 使用 __stdcall 呼叫約定 (是pascal, fortran等的呼叫約定), 自動堆疊平衡
此外還有其他的呼叫約定,如_fastcall,把前兩個引數通過暫存器傳遞,呼叫速度快。

要使用dll,我們可以動態呼叫它,也可以轉換成lib庫靜態呼叫。
動態呼叫就是說,先用LoadLibrary載入到記憶體中。再用GetProcAddress得到函式地址就可以使用了。比較麻煩。
靜態呼叫就是說,先為需要呼叫的函式生成def檔案,然後製作靜態庫lib檔案。再用這個lib呼叫dll裡的函式。

__cdecl 和 __stdcall

VC上編譯C和C++程式時,預設使用__cdecl函式呼叫約定。如果想生成__stdcall的函式,我們可以使用/GZ編譯.例如:

cl /Gz /LD mylib.cpp

這樣生成的dll和lib就是使用的__stdcall約定

通過下面的命令

dumpbin /exports mylib.dll 或mylib.lib 我們可以看到

    ordinal hint RVA      name

          1    0 0000107E 

下面是__cdecl的函式名書寫規格

    ordinal hint RVA      name

          1    0 0000107E printhello

可見__stdcall裡多了字首和字尾。

DEF檔案格式:
LIBRARY DLLNAME.DLL
EXPORTS
fonctionname1   @1
fonctionname2    @2
................

(注意__cdecl和__stdcall呼叫約定的DEF檔案書寫區別。)


從DLL製作def檔案:

MinGW上的實現方法:

pexports mydll.dll -o > mydll.def

MSVC上的實現方法:

1. 製作一份dll的匯出函式表,使用VC的dumpbin命令
dumpbin mydll.dll /exports > mylib.def
2. 開啟def檔案修改之
i) 新增 LIBRARY mydll.dll
    EXPORTS
ii) 在EXPORTS後面加入改好的需要匯出到lib中函式名列表

(注意: 因為函式呼叫約定的不同,所以匯出的函式名會有字首或字尾,這些都儘量不要修改。否則可能無法正常呼叫!具體操作,後面會舉例說明)
(非常值得注意的是: 這個EXPORTS的函式名列表,有些情況下,你可能根本無法知道這些函式名的書寫規則。不要總認為dumpbin中得到的名字就可以通用了。也不要認為pexports得到的def檔案就不用修改了。事實上,如果我們宣告函式的時候沒有用extern "C"{}. 那麼你在VC中呼叫一個MinGW的dll時你就會發現,問題變得很棘手。def的函式名書寫規則和你要呼叫dll的函式約定有關,和dll裡的前後綴無關。切記!)
另外, 在使用dll裡的函式的時候,需要一份.h檔案,對呼叫的函式進行宣告。這裡的函式名裡沒有那些附加的前後綴的,這點要注意。


在VC中生成dll和lib(呼叫dll庫)

cl /LD mylib.cpp    (得到mylib.dll和mylib.lib)

在MinGW中生成dll, def和a(呼叫dll庫)
g++ mylib.cpp -shared -o mylib_linux.dll -Wl,--output-def,mydll.def,--out-implib,mylib.a

在VC中生成靜態庫lib(不使用dll)

(注意: 靜態庫(lib和a)實際上就是一種歸檔檔案)

cl -c mylib.cpp    編譯cpp得到obj

lib mylib.obj /out:mylib.lib (生成歸檔檔案mylib.lib)

在MinGW中生成靜態庫a(不使用dll)
g++ -c mylib.cpp   得到o檔案

ar r mylib.a mylib.o 生成歸檔檔案mylib.a

VC中呼叫VC的dll (lib方式)

分為有lib檔案和無lib檔案兩種情況。第二種情況需要先生成lib檔案再呼叫。

我們感興趣的是第二種情況。具體操作實戰如下:
我們只有一個mylib.dll, 需要呼叫一個輸出函式printhello
1. 製作def
dumpbin mylib.dll /exports >mylib.def
得到一份完整的輸出函式列表。
在開頭新增 LIBRARY mylib.dll
再把下面的輸出函式資訊
    ordinal hint RVA      name

          1    0 0000107E printhello
    修改成
    EXPORTS
          printhello       @1
其他的資訊都刪除。

也可以在MinGW上直接呼叫 pexports mylib.dll > mylib.def (這就是MinGW的方便之處了)

2. 生成lib
需要mylib.dll和mylib.def
lib /machine:ix86 /def:mylib.def
這樣就會生成mylib.lib和mylib.exp兩個檔案。(mylib.exp可以刪除)

3. 通過lib呼叫dll
在程式main.cpp中加入#include "mylib.h"
這樣就可以呼叫這個函數了

cl main.cpp mylib.lib   編譯生成main.exe檔案。
(注意:這個程式的執行需要dll的參與!編譯後lib檔案可以刪除,但mylib.dll不能刪除,切記!)

MinGW呼叫MinGW的dll (直接連線 和 a連線)

MinGW的dll可以像靜態庫.a那樣直接連線使用。

g++ main.cpp mylib.dll

也可以使用

g++ main.cpp -lmylib

直接呼叫dll編譯生成main.exe檔案。非常的簡單方面。

當然,從研究的角度來說,我們討論一下如何通過庫檔案a來實現連線。其方法和VC的情況雷同。

考慮無a檔案的情況。
1. 製作def
直接呼叫 pexports mylib.dll > mylib.def

2. 生成a
需要mylib.dll和mylib.def
dlltool --dllname mylib.dll --def mylib.def --output-lib libmylib.a

這樣就會生成庫檔案libmylib.a檔案。

3. 通過a呼叫dll

在程式main.cpp中加入#include "mydll.h"
這樣就可以呼叫dll裡的函數了

g++ main.cpp libmylib.a -o main.exe 編譯生成main.exe檔案。

VC中呼叫MinGW的dll
現在如何在VC中呼叫MinGW生成的mylib_linux.dll呢?(注意:VC無法使用MinGW的a檔案,也無法像MinGW那樣直接呼叫dll)
我們可以使用def檔案生成VC可用的lib,通過lib呼叫

如果你沒有def檔案,那就用前面說過的方法(dumpbin(手動),或pexports(自動)) 生成一個。

如果你已經有了def檔案。內容如下:
LIBRARY mylib_linux.dll
EXPORTS
printhello              @1

下面的命令可以根據def生成lib

lib /machine:ix86 /def:mydll.def      生成mydll.lib。
通過lib呼叫dll

cl main.cpp mydll.lib 生成main.exe呼叫dll

MinGW中呼叫VC的dll

如過dll是__cdecl約定,那麼可以像靜態庫那樣直接使用。如果

如果使用的是__stdcall呼叫約定。這時,我們無法像__cdecl那樣直接使用了。我們有兩種思路,一種是生成VC上的lib,然後直接呼叫。另一種是製作def和a檔案,通過他們呼叫dll。

(注意,在這種情況下,不能用reimp從lib得到a。即使這個lib可以直接使用。生成的a也不能用。想得到可用的a檔案需要按以下步驟操作)

1. 製作def
呼叫 pexports mylib.dll | sed "s/_//" > mylib.def (sed部分去除函式名前的_)

未完待續。。。

2. 生成a
需要mylib.dll和mylib.def
dlltool -U -D mylib.dll -d mylib.def -l libmylib.a (注意,這個-U絕對不能少)

這樣就會生成庫檔案libmylib.a檔案。

3. 通過a呼叫dll

在程式main.cpp中加入#include "mydll.h"
這樣就可以呼叫dll裡的函數了

g++ main.cpp libmylib.a -o main.exe 編譯生成main.exe檔案。

注意,呼叫__stdcall的函式也必須宣告為__stdcall如下:

extern "C"
{
void __stdcall printhello(char *str);
}

在VC中則不需要修改程式碼,cl編譯的時候使用/Gz即可。

寫到這裡,基本上已經討論的差不多了。至於靜態庫.lib和.a之間的轉換。據說這是同一種類型的歸檔檔案,不同的只是歸檔裡包含的內容。lib裡包含的是.obj檔案,a裡包含的是.o檔案。然而,這兩種檔案的格式據說也是相同的,然而我們發現lib和a無法通用!(請注意,這裡的靜態庫lib不是呼叫dll時的那種靜態庫lib。那種lib只是起到索引和連線dll的功能,而這裡所說的靜態庫是脫離dll工作的庫,函式過程都包含在庫裡了)。以前我曾經寫過一篇文章,討論過cygwin上的庫和mingw通用的方法。其實現在我們可以更清晰的明白,他們之所以通用是因為兩者都是用gcc編譯的。同一種編譯器出來的結果當然可以相容。那篇文章的價值在於。cygwin雖然工具包很多,但是要獨立執行還需要dll支援。而mingw則可以生成不依賴於dll獨立執行的程式。

那麼lib和a不能通用的原因到底在哪裡呢?起初覺得有可能在編譯器上。因為vc的編譯器cl和gcc的編譯器編譯出來的obj和o檔案雖然格式相同卻不能通用。實驗表明,拿o檔案給cl用,或拿obj給gcc用都通不過。然而,我對這點還不表示懷疑!因為我覺得最可能的原因並不在這裡。行不通的原因很可能是兩個編譯器呼叫了不同的庫函式。cl呼叫了MSVC提供的庫函式,而gcc呼叫了他自己的庫函式。所以我們回發現,通用靜態庫失敗時顯示的都是庫函式沒定義之類的錯誤。其實是函式名的符號不能識別。如果知道所需的庫函式的具體檔案,並加入到專案的編譯中,相信很可能就會解決問題。不論如何,目前靜態庫的通用方法還有待進一步的探討。

相關推薦

MSVC vs. MinGW (lib,dll,def,obj,exe) vs (a,dll,def,o,exe) 手記

轉自:http://hi.baidu.com/kaienfr/item/0d12b1f34cb3eeda6225d2b1 一份粗糙的研究記錄,有待補完和整理。 MinGW: c -> o           gcc -c a.c c -> exe         gcc a.c li

TFAutoMLAdaNet框架:AdaNet框架的簡介、特點、使用方法詳細

 TF之AutoML之AdaNet框架:AdaNet框架的簡介、特點、使用方法詳細攻略   AdaNet框架的簡介         谷歌開源了基於 TensorFlow 的輕量級框架 AdaNet,該框架可以使用少量專家干預

TFAutoML框架:AutoML框架的簡介、特點、使用方法詳細

TF之AutoML框架:AutoML框架的簡介、特點、使用方法詳細攻略       自動化機器學習,簡單來說就是一種自動化任務的方法:預處理並清理資料、選擇並構建適當的功能、選擇合適的模型系列、優化模型超引數、後處理機器學習模型、批判性地分析所獲得的結果。

[Kaggle] dogs-vs-cats模型訓練

naconda lob flow 技術分享 prot merge efault int app 上一步建立好模型之後,現在就可以訓練模型了。 主要代碼如下: import sys #將當期路徑加入系統path中 sys.path.append("E:\\CODE\

[Kaggle] dogs-vs-cats建立模型

project edi 興趣 結構 gradient tor num 機制 訓練   建立神經網絡模型,下面要建立的模型如下: (上圖來源:訓練網絡時,打開tensorboard即可觀察網絡結構,在下一節模型訓練的時候會講到) 下面為具體步驟: Step 0:導入相關

docker使用System.Drawing生成圖片缺少Gdiplus.dll錯誤

dock comm usr sdk plus container localtime pda window Windows下面運行正常,部署到Linux的docker上之後,報錯顯示缺少gdiplus.dll;這個是Windows的gdi組件,在Linux下要安裝環境 解決

陳賢李維清VS瑩孫旦平已任火

https DC bfc fcc aca com tps BE AC 俗滓富攣八攣材偉首恐嗇厴首瓜耘盒耘貝斜撈悶岡斬詡烏俗諢鮮牡凸燒窩蠢推弦釋頻釩采泊謨廖雷儔忱藤盜宋瓜睹贅喚窩謎票聊韻斬偉孿安攔宋扛倩諗旱柯宋爛言柯釋擁諗頁泳擁謝戳舅既凡舅輾概思談 669sdcp 碌胖

Golang vs PHP 文件服務器

再見 dha file 編譯型 windows 繼續 lse prefix path 前面的話 作者為golang腦殘粉,本篇內容可能會引起phper不適,請慎讀! 前兩天有同事遇到一個問題,需要一個能支持上傳、下載功能的HTTP服務器做一個數據中心。我剛好弄過,於是答應幫

靜態庫(.lib)的建立與使用———VS編譯器實現

一、靜態庫的概念 ①概念:靜態庫是指在我們的應用中,有一些公共程式碼是需要反覆使用,就把這些程式碼編譯為“庫”檔案;在連結步驟中,聯結器將從庫檔案取得所需的程式碼,複製到生成的可執行檔案中的這種庫。 ②特點: 靜態庫連結之後,靜態庫改變,對程式不再產生影響,移植方便

C#からネイティブDLLを呼び出す場合のVSからのデバッグのジレンマを解決する

  「C#を使う最大のメリットって、やっぱり、Visual Studioですよね!」って自信を持って言いたいですね。 という心境ではあるんですが、私の仕事はどっちかというとC++よりなので、どうしても、DllImportはお友達という側面があります。そうすると、プログラム実行時に、

VS中新增lib庫的三種方法

在VS中新增lib庫的三種方法 注意: 1、每種方法也要複製相應的DLL檔案到相應目錄,或者設定DLL目錄的位置,具體方法為:"Properties" -> "Configuration Properties" -> "Deb

MinGW vs MinGW-W64及其它

現轉載科普文如下: ++++++++++++++++++++++ 轉載: http://www.cnblogs.com/foohack/p/3877276.html 部分參照備忘錄原文: bitbucket.org/FrankHB/yslib/src/50c3e6344a5a24b238

mfcs100ud.lib(dllmodul.obj) : error LNK2005: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="da859eb6b697bbb3b49aebe8">[email

今天用VC2010寫一個用了MFC的規則DLL,編譯時報告如題錯誤。 必應後發現這篇文章  error LNK2005: [email protected] 已經在 MSVCRTD.lib(dllmain.obj) 中定義   http://wangl

靜態連結庫(LIB)和動態連結庫(DLL),DLL的靜態載入和動態載入,兩種LIB檔案。

靜態連結庫(LIB)和動態連結庫(DLL),DLL的靜態載入和動態載入,兩種LIB檔案。 一、 靜態連結庫(LIB,也簡稱“靜態庫”)與動態連結庫(DLL,也簡稱“動態庫”)的區別 靜態連結庫與動態連結庫都是共享程式碼的方式,如果採用靜態連結庫,則無論你願不願意,lib 中的指令都全部被直接包含在最

Maven 實戰 -多模組 vs 繼承 Maven提高篇系列(一)——多模組 vs 繼承

Maven提高篇系列之(一)——多模組 vs 繼承     這是一個Maven提高篇的系列,包含有以下文章:    Maven提高篇系列之(一)——多模組 vs 繼承 Maven提高篇系列之(二)——配置Plu

VS中新增lib檔案,編譯出錯:LINK : fatal error LNK1104: 無法開啟檔案:×××.lib解決辦法

新增庫檔案: 在VS中右擊專案點屬性:   新增標頭檔案目錄:   配置屬性-->C/C++-->常規-->附加包含目錄 加上標頭檔案存放的目錄。   新增lib檔案:   配置屬性-->連結器-->輸入-->附加依賴項加入庫名(×××.

nafxcwd.lib(dllmodul.obj) : error LNK2005: <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="7f203b1313321e16113f4e4d">[email&#

朋友編寫一個dll,編譯時總出現"nafxcwd.lib(dllmodul.obj) : error LNK2005: [email protected]..."錯誤!後發現是,其建立dll時選擇的時不使用MFC,而後來工程中又添加了與MFC相關的東西.才引起上述問題.採用如

堡壘機世界的征戰尚思卓越堡壘機VS齊治堡壘機

尚思卓越堡壘機(之前行內又稱“尚維堡壘機”)是由尚思卓越(北京)科技有限公司研發直營品牌產品,歷經10餘年的研發更新,客戶群體遍佈各行各業,尚思卓越企業一直保有著市場優質堡壘機供應商的口碑,並有著多家忠實客戶沿用產品至今,被譽為堡壘機行業的領軍者。齊治堡壘機是由浙江齊治科技股份有限公司研發銷售產品,自詡全世界

堡壘機世界的征戰尚思卓越堡壘機VS帕拉迪堡壘機

堡壘機,即在一個特定的網路環境下,為了保障網路和資料不受來自外部和內部使用者的***和破壞,而運用各種技術手段實時收集和監控網路環境中每一個組成部分的系統狀態、安全事件、網路活動,以便集中報警、記錄、分析、處理的一種技術手段。其從功能上講,堡壘機綜合了核心系統運維和安全審計管控兩大主幹功能,從技術實現上講,通

Maven提高篇系列(一)——多模組 vs 繼承

通常來說,在Maven的多模組工程中,都存在一個pom型別的工程作為根模組,該工程只包含一個pom.xml檔案,在該檔案中以模組(module)的形式宣告它所包含的子模組,即多模組工程。在子模組的pom.xml檔案中,又以parent的形式宣告其所屬的父模組,即繼承