由 dll 檔建立 lib 檔(修改 lib/dll 名稱)


通常一個 Windows 下 C/C++ 的動態連結函式庫(Dynamic-link library、維基百科微軟的文件),會有三種東西:開發時使用的 header 檔、連結階段需要的 lib 檔、以及執行階段才需要的 dll 檔。

在這個架構下,實際上所有的程式都已經編譯好、儲存在 dll 檔案裡面了,實際上真的需要,也是可以在執行階段直接去讀取 dll、抓出裡面的函式來用的;但是在有 header 和 lib 的時候,開發上還是比較方便的。

而實際上,lib 檔主要的功能,就是告訴編譯器(其實是連結器)這個 dll 檔裡面有哪些函式可以用而已;在編譯的時候,系統只會去檢查 lib 的內容,而不會去管 dll 檔。

一般 C++ 的函式庫專案,在建置後自然就會有這三項東西了~只要按照正常的流程,就可以使用。


不過,由於 lib 裡面會決定在呼叫某個函式的時候,要去從哪個 dll 檔裡面找,所以實際上 lib 檔的檔名雖然可以隨便修改,但是對應的 dll 卻不行。

比如說有一個函式庫有 libHello.liblibHello.dll 這兩個檔案,如果自己手動改成 libHellod.liblibHellod.dll 來區別 debug / release 的話(很多函式庫預設是這樣在檔名最後加個 d 來標示 debug 版),實際上在執行的時候,他還是會回去找 libHello.dll 的。

雖然一般來說,可以透過修改 Visual C++ 的專案設定,來調整要輸出的檔案名稱,但是有的時候卻很難做到…

而在有需要修改 lib / dll 的檔案名稱、但是卻又沒辦法在建置階段就先改好,該怎麼辦呢?一個方法,就是根據既有的 dll 重新產生一次對應的 lib 檔。

Heresy 這邊的作法,是參考《Modify the dll file name》這篇文章來做的。

這邊的操作,基本上要使用 Visual Studio 提供的兩個工具程式:DUUMPBIN(官方文件)以及 LIB(官方文件),所以在使用的時候,會建議在 Visual Studio 的 Command Prompt 下執行;其主要流程基本上是:

  1. 透過 DUMPBIN 從 dll 中擷取出定義(def 檔)
  2. 修改 def 檔的內容以符合 LIB 工具的需求
  3. 使用 LIB、由修改好的 def 檔產生對應的 lib 檔

透過 DUMPBIN 從 dll 中擷取出定義(def 檔)

這部份很簡單,只要執行下面的指令就可以了:

DUMPBIN libHellod.dll /EXPORTS /OUT:libHellod.def

這樣他就會產生我們需要的 def 檔了。

而 Heresy 這邊的作法,是先把 dll 檔改名好、以免混淆。


修改 def 檔的內容

DUMPBIN 產生的 def 檔案,它的內容形式大概會是下面的樣子(這邊是使用 NVIDIA AIAA Client 做例子、GitHub):

Dump of file NvidiaAIAAClient.dll

File Type: DLL

  Section contains the following exports for NvidiaAIAAClient.dll

    00000000 characteristics
    FFFFFFFF time date stamp
        0.00 version
           1 ordinal base
          96 number of functions
          96 number of names

    ordinal hint RVA      name

          1    0 000877E0 ??0Client@aiaa@nvidia@@QEAA@$QEAV012@@Z
          2    1 00087820 ??0Client@aiaa@nvidia@@QEAA@AEBV012@@Z
          3    2 00087850 ??0Client@aiaa@nvidia@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z
          ...
          ...
         95   5E 0009F0A0 ?toString@Model@aiaa@nvidia@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4ModelType@123@@Z
         96   5F 000A4AC0 ?to_lower@Utils@aiaa@nvidia@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V45@@Z

  Summary

       24000 .data
       2A000 .pdata
      25F000 .rdata
       23000 .reloc
        1000 .rsrc
      4AE000 .text

前面有一些檔頭的資訊,後面也有總結(Summary);而這邊需要的,則只有中間黃色的部分。

這一區的資料,基本上分成四欄:ordinal、hint、RVA、name,最後要用到的只有 ordinal 和 name 而已;在這個例子裡面,總共有 96 個函式。

而這邊的修改的方法,就是第一行輸入「EXPORTS」,然後把黃色區域的定義中的 name(第四欄)放到最前面,後面再加上一個「@」、然後接 ordinal(第一欄的數字);這樣的結果大概會是:

EXPORTS

??0Client@aiaa@nvidia@@QEAA@$QEAV012@@Z @1
??0Client@aiaa@nvidia@@QEAA@AEBV012@@Z @2
??0Client@aiaa@nvidia@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z @3
...
...
?toString@Model@aiaa@nvidia@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@W4ModelType@123@@Z @95
?to_lower@Utils@aiaa@nvidia@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@V45@@Z @96

然後把所有的函式定義都處理好(人工其實很麻煩),就算完成了~


使用 LIB 產生需要的 lib 檔

在修改好 def 檔後,只要執行下面的指令,就可以產生需要的 lib 檔了。

LIB /DEF:libHellod.def /machine:X64 /OUT:libHellod.lib

如此一來,搭配一開始改名好的 libHellod.dll,這邊就有對應的 libHellod.lib 了~

不過也要注意,這邊是針對 x64 的版本來寫的。


GenLibFromDll

由於要用人工去處理 def 檔的修改有點繁瑣,而 Heresy 這邊又想弄成腳本自動建置的形式,所以後來就自己用 C++ 寫了一個按照上面的流程、從 dll 檔案產生 lib 檔的程式了~

專案是:https://github.com/KHeresy/GenLibFromDll

在 release 裡面也有提供建置好的檔案(x64)。

執行的時候,只要在 Visual Studio 的命令提示字元下執行

GenLibFromDll.exe libHello.dll

就可以了。

DUMPBIN 和 LIB 的呼叫、以及 def 的修改,都會在程式裡做掉。


不過這邊也要注意,在 def 的處理上,其實比想像中的複雜一點。

一般的 dll 確實是只會有 ordinal、hint、RVA、name 這四個項目,可以簡單地用空白來切割;但是如果是 debug 版的 dll,根據編譯參數,格式可能會是下面的樣子:

    ordinal hint RVA      name

          1    0 00088277 qt_plugin_instance = @ILT+17010(qt_plugin_instance)
          2    1 0008480C qt_plugin_query_metadata = @ILT+2055(qt_plugin_query_metadata)

也就是第四欄的 name 會是 xxxx = yyy 這樣的形式。

所以要做字串切割的話,一般的 dll 在這邊會切成四項;但是遇到這種狀況,則會是六項。

處理後則會變成下面的樣子:

EXPORTS

qt_plugin_instance = @ILT+17010(qt_plugin_instance) @1
qt_plugin_query_metadata = @ILT+2055(qt_plugin_query_metadata) @2

而更麻煩的是,在某些狀況下,還會出現比六項還多的狀況…而在最後面的東西,感覺則像是註釋或標記?如果留下來不刪除了的話,執行 LIB 會認為是語法錯誤。

所以其實 Heresy 後來的處理方法是只針對可以拆成四項、六項的來做處理;第 6 項以後的東西就都強制丟了。這樣會不會碰到其他問題?有碰到再說吧~

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.