溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C程序中讓兩個(gè)不同版本的庫(kù)共存

發(fā)布時(shí)間:2020-10-03 15:43:20 來源:網(wǎng)絡(luò) 閱讀:494 作者:重歸混沌 欄目:編程語(yǔ)言

原文連接:http://blog.gotocoding.com/archives/875


今天有同學(xué)提出,如何在一個(gè)C程序中讓兩個(gè)不同版本的庫(kù)共存。


首先想到的方案是,把其中一個(gè)版本的庫(kù)函數(shù)全部重命名,比如把每一個(gè)函數(shù)名都加一個(gè)_v2的后綴。

人工替換到?jīng)]什么,但是如果函數(shù)個(gè)數(shù)超過10個(gè),就有點(diǎn)不拿人當(dāng)人使了。


而使有工具去替換就會(huì)遇到一些棘手的問題,如何識(shí)別哪些是函數(shù),哪些是系統(tǒng)函數(shù)(系統(tǒng)函數(shù)不需要添加后綴)等。


隨后想到的另一個(gè)解決方案是C++的方案,為其中一個(gè)版本庫(kù)中的所有文件添加命名空間。然后使用g++將這部分代碼編譯成.o文件,之后再使用gcc將這些.o文件與整個(gè)程序中的其他代碼進(jìn)行鏈接。


不過需要注意的是,g++編譯后所有導(dǎo)出接口名都會(huì)變化得不那么直觀。



第三種方案完全解決了以上兩種方案的痛點(diǎn)。


考慮一個(gè)C語(yǔ)言的編譯鏈接過程。


首先會(huì)將每個(gè)c文件編譯成.o文件。


在編譯過程中,導(dǎo)出函數(shù)并不會(huì)被實(shí)際分配地址,而是將函數(shù)名以F符號(hào)的方式存在.o文件的符號(hào)表中。


在本c文件調(diào)用的函數(shù)如果不存在于本文件,也會(huì)生成一個(gè)UND的符號(hào)存在.o文件的符號(hào)表中。


在鏈接過程中,鏈接器接收輸入的.o文件,為每個(gè).o文件中的符號(hào)分存地址,并生成可執(zhí)行文件。


有了這幾點(diǎn)事實(shí),問題就變得的簡(jiǎn)單多了。


首先將其中一個(gè)版本的庫(kù)中所有代碼編譯為.o文件。然后收集所有.o文件中的F符號(hào)。


由于整個(gè)庫(kù)代碼有內(nèi)部依賴關(guān)系,收集到的F符號(hào)必然是所有.o文件中UND符號(hào)的超集。


換句話說,所有的F符號(hào)名就是我們要重命名的所有函數(shù)名。


這里我們需要借助objdump和objcopy工具。objdump -t 用于列表.o文件的符號(hào)表,objcopy用于重命名符號(hào)。


我隨手寫了一段用于過慮F符號(hào)的lua腳本

1
2
3
4
5
6
7
8
9
10
11
12
13
--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符號(hào), 并產(chǎn)生修改符號(hào)所用的腳本

1
find . -name '*.o' | xargs objdump -t | ./lua rename > rename.sh

現(xiàn)在我們只需要再執(zhí)行一條命令就可以把所有函數(shù)名增加一個(gè)_v2的后綴.

1
find . -name '*.o' | xargs -n 1 sh ./rename.sh

至此,我們這個(gè)版本的庫(kù)代碼的所有函數(shù)名已經(jīng)全部增加了_v2后綴。

這些被處理過的.o文件與我們將所有.c代碼中函數(shù)名重命名之后編譯出的.o文件完全一等價(jià)。


8月2號(hào)補(bǔ)充:

在實(shí)際使用中發(fā)現(xiàn), 局部函數(shù)(static 函數(shù))符號(hào)有可能會(huì)被gcc做修飾,將被修飾的符號(hào)重命名會(huì)給我們帶來一些麻煩,而我們?cè)疽膊恍枰ヌ幚砭植亢瘮?shù)。


因此對(duì)rename.lua做如下修改,過慮掉非全局符號(hào):

1
2
3
4
5
6
7
8
9
10
11
12
13
--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")


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI