是否可以靜態鏈接動態鏈接庫?
我們知道靜態鏈接是在鏈接的時候把相關代碼從a文件或者o文件拷貝到相應的二進位文件裡面。而動態鏈接只是通過lib文件(Win下)將動態鏈接庫裡面的函數地址拷進二進位文件,而函數是不拷貝的。運行時則載入動態鏈接庫,通過地址調用動態鏈接庫裡面的函數。而我可不可以用lib和動態鏈接庫實現靜態鏈接呢?從lib裡面讀取函數地址,然後把相關代碼從動態鏈接庫拷到二進位文件,原理上應該是可以的啊。Linux底下更簡單了,直接一個so文件搞定,lib都沒有。so和a文件有什麼區別呢?
搞清楚dll文件結構可以從內存LoadLibraryFromMemory,實在嫌麻煩py2exe的代碼里有一份現成的,實現還算穩定,伴隨py2exe跑了這麼多年了,因為它要從zip里或者數組裡直接載入dll而不生成臨時文件。
這樣你把你的dll弄到資源里或者生成成數組數據(還可以加密一下)啟動時再直接內存載入即可實現你要的效果。DLL是已經經過編譯和鏈接過程的可執行代碼了,有些事情已經在這個過程確定了,並且無法更改。
比如 全局函數和全局變數問題。
DLL的全局函數和變數是私有的,並不和其它DLL共享。
所以,DLL a和DLL b里可能都有一個叫 foo 的全局變數,和一個叫 bar()的函數,是不會衝突的。
但是如果你靜態鏈接就有問題了,lib a和lib b里的變數和函數是不能重名的。
你這樣把 DLL 當作靜態庫來鏈接就會遇到變數函數名衝突問題,同樣名字的函數不見得實現也是一樣的,不能隨便選一個,所以你沒辦法解決這個衝突。
如果要解決,只能去到兩個DLL的源代碼里去修改,但是你既然能修改源代碼,那麼何不直接把編譯選項就改成靜態庫呢?
總之,雖然你這麼做存在一點點理論上的可能性,但是做起來遇到的麻煩非常多,完全不值得。
如果只是追求發布單一EXE,不想帶上很多 DLL,那麼 @王維一 的方案簡單可行。
調用靜態鏈接和動態鏈接的函數的代碼是不一樣的,真的要這麼做,還得去改exe,然後混合若干dll的初始化代碼,這一步超級複雜,不一定可以做對。
看看這本書:《程序員的自我修養,鏈接、裝載與庫》
對於dll有這麼個東西
Convert DLLs into static libraries
不過坑不是一般的多。
也可以從內存中載入dll,google一下有相關的文章,當然也是各種坑。
linux不熟悉,只回答windows下的首先在win下,鏈接動態鏈接庫不一定需要lib,也可以在運行時通過API手動鏈接dll(LoadLibrary和GetProcAddress),只要知道dll文件名和函數名即可鏈接。lib僅僅是方便自動連接dll的媒介而已。所以在這個思路下,就有了這樣的辦法做到「靜態鏈接」到動態鏈接庫:
把動態鏈接庫dll直接打包到exe里(比如作為資源),程序啟動時先將dll釋放到程序目錄下,然後再手動鏈接該dll。
好吧這方法確實比較作弊了。等待更正常的解答理論上是可以的,前提是技術要夠牛。這裡面有非常多的細節要考慮,比如說,dll裡面可能會用到全局變數或者TLS(Thread Local Storage),你拷代碼過去的時候怎麼處理?如果你能回答這個問題,技術上應該能搞定。
有這種技術,名為Memory (Re)LoadLibrary
之所以加Re是因為這種技術可以用來重新載入系統模塊過r3的inline hook…也用來保護軟體模塊不被竊取
說說實現吧
很多殼都帶了這個功能,比如Safengine就有DLLBox插件
當然不想加殼也行
自己模擬LoadLibrary進行庫載入,將Dll寫成一個大數組,然後直接在內存里載入就好
百度谷歌一下重寫LoadLibrary一堆實現…
當然還有一種方法…直接把彙編扣出來…
動態鏈接庫正常講當然不能靜態連接。如果編譯時連接了某個動態鏈接庫,我拿 macOS 舉例,它會在可執行文件 Mach-O 頭中寫入一個 LC_LOAD_DYLIB 命令,這樣可執行文件運行時就會去載入相應的 dylib(也就是 Windows 中的 dll 或 *NIX 下的 so),此時如果 dylib 不存在就會報 image not found 的錯誤。如果你不想編譯時連接,就只能在運行時用 dlopen 打開這個動態連接庫,然後用 dlsym 去取需要的符號地址,不過這樣編程的時候就很麻煩,頭文件中的函數就不能直接拿來用,頂多使用一下裡面聲明的結構體。
如果題主單純是想發布時不帶一堆 dll,可以寫一個 bootstrap,也可以用 zlib 壓縮一下資源,運行時先釋放到 AppData 再執行。
如果你的目的是為了發布的時候只有一個文件,那麼可以這麼干:把動態鏈接庫轉換成資源文件,然後附加在可執行文件上面,執行的時候讀出資源文件輸出為動態鏈接庫,然後再用動態載入的方式載入。
so是動態,a是靜態的。跟dll和lib對應
最神的方法是一次弄出dll和lib兩個庫,然後想用哪個用哪個
你這樣做失去了動態鏈接的意義,不管鏈接是靜態還是動態目的都是為了共享,之所以出現了動態鏈接,就是因為靜態鏈接將會拷貝重複的簡單函數(如printf)到每個可重定位目標文件,在大型系統里這無疑是浪費存儲器的一種做法。而在動態鏈接中,所有引用該庫的可執行目標文件共享這個.so文件。總而言之,共享庫的出現就是為了彌補靜態庫的缺陷。
我很早就想這樣做了……就是將dll以資源嵌入到exe中,執行時動態載入dll
問題是dll中數據的重定位等工作是操作系統完成的,這就要求dll是存放在執行目錄或系統變數路徑中,所以只能在程序執行後立即釋放dll到磁碟(然而這很不清真!)
當然,說不定有那種程序自己解析dll的黑科技呢~
不知道為啥沒法改問題了。。。這裡我把題目更新寫在這裡
過了一年多這個問題突然火起來了haha估計是被輪子哥翻牌子了。。
這裡我補充一下我當時提出這個問題的動機吧:
眾所周知Qt的dll超級大而且很多類都用不到。。佔到百分之九十 所以我在想能不能這麼做來減小空間 因為Qt庫不是人人電腦都有的
現在題主已經通過靜態編譯Qt解決了這個問題。。
Linux下面確實在用法上搞不清楚有什麼區別,靜態和動態都是直接鏈接然後調用
感覺原理上可以,實際操作起來可能需要看操作系統和具體細節
我一向較反對用DLL的隱式調用(直接採用DLL的導出符號,而不用 LoadLibrary和GetProcAddress ),公司的同事一向喜歡如此,極奇葩,關鍵是LINUX編譯時必須要鏈接.so如果則編譯無法完成,完全葬送了使用DLL設計的初衷! 完全沒有設計面向介面編程的思想,測試也無法進行,除了DLL導出類的方式我覺得靜態鏈接DLL完全沒有使用場景。
推薦閱讀:
※C#如何輸出dxf文件?
※C++ 中 const 的實現原理是什麼?
※庫代碼中是否應該檢查malloc的返回值?
※python如何畫出這樣漂亮的地圖呢?
※C++中編寫強異常安全的代碼真的有必要麼?