C程序中讓兩個不同版本的庫共存
原文連接:http://blog.gotocoding.com/archives/875
今天有同學提出,如何在一個C程序中讓兩個不同版本的庫共存。
首先想到的方案是,把其中一個版本的庫函數全部重命名,比如把每一個函數名都加一個_v2的後綴。
人工替換到沒什麽,但是如果函數個數超過10個,就有點不拿人當人使了。
而使有工具去替換就會遇到一些棘手的問題,如何識別哪些是函數,哪些是系統函數(系統函數不需要添加後綴)等。
隨後想到的另一個解決方案是C++的方案,為其中一個版本庫中的所有文件添加命名空間。然後使用g++將這部分代碼編譯成.o文件,之後再使用gcc將這些.o文件與整個程序中的其他代碼進行鏈接。
不過需要註意的是,g++編譯後所有導出接口名都會變化得不那麽直觀。
第三種方案完全解決了以上兩種方案的痛點。
考慮一個C語言的編譯鏈接過程。
首先會將每個c文件編譯成.o文件。
在編譯過程中,導出函數並不會被實際分配地址,而是將函數名以F符號的方式存在.o文件的符號表中。
在本c文件調用的函數如果不存在於本文件,也會生成一個UND的符號存在.o文件的符號表中。
在鏈接過程中,鏈接器接收輸入的.o文件,為每個.o文件中的符號分存地址,並生成可執行文件。
有了這幾點事實,問題就變得的簡單多了。
首先將其中一個版本的庫中所有代碼編譯為.o文件。然後收集所有.o文件中的F符號。
由於整個庫代碼有內部依賴關系,收集到的F符號必然是所有.o文件中UND符號的超集。
換句話說,所有的F符號名就是我們要重命名的所有函數名。
這裏我們需要借助objdump和objcopy工具。objdump -t 用於列表.o文件的符號表,objcopy用於重命名符號。
我隨手寫了一段用於過慮F符號的lua腳本
12345678910111213 | --rename.lua local list = {} local reg = "([^%s]+)%s+([^%s]+)%s+([^%s]+)" .. "%s+([^%s]+)%s+([^%s]+)%s+([^%s]+)" for l in io .stdin:lines() do local a,b,c,d,e,f = string .match(l, reg) if a and c == "F" then list[#list + 1] = " --redefine-sym " list[#list + 1] = string . format ( "%s=%s_v2" , f, f) end end print ( "#/bin/sh" ) print ( "objcopy " .. table .concat(list) .. " $1" ) |
我們可以使用如下命令來收集所有.o文件的F符號, 並產生修改符號所用的腳本
1 | find . -name ‘*.o‘ | xargs objdump -t | ./lua rename > rename.sh |
現在我們只需要再執行一條命令就可以把所有函數名增加一個_v2的後綴.
1 | find . -name ‘*.o‘ | xargs -n 1 sh ./rename.sh |
至此,我們這個版本的庫代碼的所有函數名已經全部增加了_v2後綴。
這些被處理過的.o文件與我們將所有.c代碼中函數名重命名之後編譯出的.o文件完全一等價。
8月2號補充:
在實際使用中發現, 局部函數(static 函數)符號有可能會被gcc做修飾,將被修飾的符號重命名會給我們帶來一些麻煩,而我們原本也不需要去處理局部函數。
因此對rename.lua做如下修改,過慮掉非全局符號:
12345678910111213 | --rename.lua local list = {} local reg = "([^%s]+)%s+([^%s]+)%s+([^%s]+)" .. "%s+([^%s]+)%s+([^%s]+)%s+([^%s]+)" for l in io .stdin:lines() do local a,b,c,d,e,f = string .match(l, reg) if a and c == "F" and b == "g" then list[#list + 1] = " --redefine-sym " list[#list + 1] = string . format ( "%s=%s_v2" , f, f) end end print ( "#/bin/sh" ) print ( "objcopy " .. table .concat(list) .. " $1" ) |
本文出自 “重歸混沌” 博客,請務必保留此出處http://findstr.blog.51cto.com/3604646/1953660
C程序中讓兩個不同版本的庫共存