LWN:讓Fedora的C代碼更加現(xiàn)代化!
關(guān)注了就能看到更多這么棒的文章哦~
Modernizing Fedora's C code
By Jake Edge
November 2, 2022
DeepL assisted translation
https://lwn.net/Articles/913505/
人們通常很少能看到針對(duì) 18 個(gè)月之后才發(fā)布的 Fedora 版本所提出的修改建議,但最近郵件列表中就有這么一個(gè)例子。它是針對(duì)該發(fā)行版的無(wú)數(shù)軟件包中的 C 源代碼的改動(dòng),將會(huì) fix 那些使用了一些古老功能的兼容性代碼,原因是這些特性已經(jīng)在 C99 標(biāo)準(zhǔn)中刪除了,不過(guò) GCC 仍然支持。從提前這么久就發(fā)布這個(gè)提案就可以猜到,要達(dá)到這個(gè)目的,會(huì)有相當(dāng)多的工作。
按通常進(jìn)行 Fedora 提案的慣例,這個(gè)建議最早是由 Fedora 項(xiàng)目經(jīng)理 Ben Cotton 代表這個(gè)提案的負(fù)責(zé)人 Florian Weimer 發(fā)布到 Fedora 開(kāi)發(fā)郵件列表的;它也可以在 Fedora wiki 上看到更新版本。目前,F(xiàn)edora 37 即將面世,但該提案的目標(biāo)版本是 Fedora 40,時(shí)間點(diǎn)暫定于 2024 年北半球春天。正如標(biāo)題所描述的,目標(biāo)是 "將 Fedora 移植到 Modern C 語(yǔ)言"。
Old C
C 語(yǔ)言中有一些功能在 C99 標(biāo)準(zhǔn)中被刪除了,但在 GCC 中仍然默認(rèn)是接受的,所以在組成 Fedora 的巨大的代碼庫(kù)中還存在使用這些功能的地方。我們打算開(kāi)始著手清理這些功能,希望能與其他發(fā)行版合作,并將這些改動(dòng)納入 upstream 項(xiàng)目。主要針對(duì)六種不同的 construct,但最重要的是移除了隱式函數(shù)聲明(implicit function declaration):
這個(gè)兼容傳統(tǒng)的功能會(huì)導(dǎo)致編譯器自動(dòng)提供一個(gè) int function()類(lèi)型的聲明,用在某個(gè)函數(shù)沒(méi)有被聲明但是作為一個(gè)函數(shù)被調(diào)用的情況下。然而,使用 int 作為隱含的返回類(lèi)型,會(huì)導(dǎo)致跟返回指針的函數(shù)不兼容,如果編譯器沒(méi)有給出 warning 的話(huà),就會(huì)導(dǎo)致 debug 過(guò)程很困難,如 https://developers.redhat.com/blog/2019/04/22/implicit-function-declarations-flexs-use-of-reallocarray 這里所述。
上面這個(gè)鏈接的文章提到,在 64 位系統(tǒng)上隱式返回 32 位 int 會(huì)導(dǎo)致指針被截?cái)唷M瑯樱陔[式加上 int 聲明后調(diào)用的返回 Bool 類(lèi)型的函數(shù),其返回值也可能會(huì)被曲解,因?yàn)樗鼈儧](méi)有修改 return register 中所有的 32 個(gè) bit。
人們發(fā)現(xiàn)的另一個(gè)問(wèn)題是,在 C99 之前,下面的代碼會(huì)隱式地定義兩個(gè) int 變量,因?yàn)榇a里沒(méi)有指定具體類(lèi)型:
static i = 1.0;
auto j = 2.0;
由于在 Fedora 里的許多軟件包中使用了 Autoconf,這就導(dǎo)致更加難以檢測(cè)并刪除這兩個(gè) "implicit-int":
這兩個(gè)改動(dòng)都不是很容易實(shí)現(xiàn)的,因?yàn)槿绻o這些情況下報(bào)出 error 的話(huà)(這是按照 C99 的要求)會(huì)改變 autoconf 配置檢查的結(jié)果。例如,相當(dāng)多的此類(lèi)檢查都使用了隱式聲明的 exit 函數(shù)。這些問(wèn)題其實(shí)跟被測(cè)試的功能沒(méi)有什么關(guān)系。如果編譯構(gòu)建系統(tǒng)寫(xiě)得很好的話(huà),構(gòu)建仍然會(huì)成功,相關(guān)的特性會(huì)在測(cè)試套件中自動(dòng)禁用掉,并且從 reference ABI 列表中刪除掉,而且不會(huì)立即發(fā)現(xiàn)該特性已經(jīng)消失了。因此,需要注意一下不要發(fā)生這種改動(dòng),需要將軟件包移植到 C99。
正如 LLVM 論壇網(wǎng)站上的一條消息所指出的,該項(xiàng)目中包含的 Autconf 腳本可能依賴(lài)于隱式聲明,任何編譯器 warning 或 error 都可能由于 Autoconf 的運(yùn)行方式而被丟失。人們正在開(kāi)發(fā)一些工具來(lái)幫助找到有問(wèn)題的代碼,而不破壞 Autoconf 的這個(gè)工具。該提案說(shuō),跟其他發(fā)行版配合,也會(huì)有助于這一過(guò)程。
其他還需要一些改動(dòng)來(lái) fix 一些棘手的情況。bool、true 和 false 關(guān)鍵字會(huì)需要被強(qiáng)制使用;所有哪些自己重新定義了這些關(guān)鍵字的代碼就需要被改變。此外,GCC 在看到把指針賦值給 int 變量的時(shí)候不會(huì)認(rèn)為這是一個(gè) error,這也需要 fix。舊方式的函數(shù)定義,例如:
int sum(a, b)
char *a;
int b;
{ ...
也需要進(jìn)行清理。最后,函數(shù)聲明中的空括號(hào)的使用也需要收緊:
在早期的 C 語(yǔ)言版本中,聲明 int function() 的時(shí)候會(huì)聲明函數(shù)接受不確定的數(shù)量的未知類(lèi)型的參數(shù)。這意味著 function(1)和 function("one")都可以正常編譯,盡管它們可能無(wú)法正確地運(yùn)行。在較新版本的 C 標(biāo)準(zhǔn)中,int function() 就將意味著函數(shù)不接受任何參數(shù)(就像在 C++中一樣,或者就像在目前的 C 中寫(xiě)成 int function(void)一樣)。因此,那些指定了參數(shù)的調(diào)用都會(huì)導(dǎo)致編譯錯(cuò)誤。
根據(jù)該提議的說(shuō)法,F(xiàn)edora 這樣做之后有雙重收益。有時(shí)一些難以追蹤的、"看起來(lái)很像編譯器或 ABI 錯(cuò)誤 "的 bug 就可以被避免,因?yàn)槟切┛赡苋菀妆缓鲆暤?warning 都會(huì)變成編譯 error。此外,人們認(rèn)為其中一些遺留功能阻礙了 GCC 的進(jìn)步。該提案期待著 GCC 14 的發(fā)布,該版本預(yù)計(jì)將包含在 Fedora 40 中。有人認(rèn)為 GCC 14 會(huì)默認(rèn)禁用對(duì)這些特性的支持,但即使這一點(diǎn)沒(méi)有成為現(xiàn)實(shí)的話(huà),F(xiàn)edora 40 也會(huì)改變其默認(rèn)值,以便之后不會(huì)引入 regression 問(wèn)題。
Questions
Daniel P. Berrangé 指出,當(dāng)他看到這個(gè)提案時(shí),立即想到了 Autoconf 這個(gè)難題。他指出,其他通過(guò)編譯各種測(cè)試程序來(lái)探測(cè)功能的構(gòu)建系統(tǒng)也可能會(huì)受到影響。Weimer 說(shuō),他已經(jīng)在 Python 的 setuptools 中發(fā)現(xiàn)了這樣的一個(gè)問(wèn)題。它生成測(cè)試程序從而確定環(huán)境中是否存在某些功能,但這些程序依賴(lài)于 implicit int,所以它們可能在嚴(yán)格的編譯器情況下會(huì)失敗,但其實(shí)系統(tǒng)中并不缺乏想要檢查的這個(gè)功能。
Berrangé 還詢(xún)問(wèn)了開(kāi)發(fā)者如何測(cè)試他們的代碼中是否有這些問(wèn)題。畢竟這不是簡(jiǎn)單地設(shè)置 -std=gnu99 就可以實(shí)現(xiàn)的,因?yàn)檫@樣設(shè)置之后仍然會(huì)允許舊有方式的代碼(至少現(xiàn)在是這樣)。Weimer 介紹了所需的編譯選項(xiàng)("是-Werror=implicit-int -Werror=implicit-function-declaration,最好再加上-Werror=int-conversion -Werror=strict-prototypes -Werror=old-style-definition。");他還更新了 wiki 上的提案,以便將每個(gè)編譯選項(xiàng)都跟相應(yīng)的過(guò)時(shí)的功能聯(lián)系起來(lái)。
Alexander Sosedkin 想知道,為什么移植工作不可以更悠閑一些,按部就班地進(jìn)行分配和 fix。開(kāi)始時(shí),每一個(gè)軟件包都可以被標(biāo)記為不支持這個(gè)改動(dòng),并且可以為所有支持這個(gè)改動(dòng)的軟件包切換一下 GCC 的默認(rèn)選項(xiàng);這時(shí)雖然什么都沒(méi)有改變,但軟件包可以慢慢地進(jìn)行所需的改動(dòng)以及修改相關(guān)的標(biāo)記。盡管 Weimer 希望可以這樣來(lái)實(shí)現(xiàn),但他真的不認(rèn)為這種分布式的移植方式是可行的;"我們必須教更多的人了解這些 C 語(yǔ)言的奧秘,以及 autoconf 這些特殊情況。說(shuō)實(shí)話(huà),我不認(rèn)為這不太值得花時(shí)間去學(xué)習(xí)"。
Vit Ondruch 詢(xún)問(wèn)了這項(xiàng)工作的當(dāng)前狀況,以及將受其影響的軟件包的數(shù)量。Weimer 說(shuō),他還在試圖弄清楚這一點(diǎn),但他初步發(fā)現(xiàn)大約有 10% 的 Fedora 軟件包會(huì)受到影響,他認(rèn)為這個(gè)數(shù)字可能高估了。這個(gè)數(shù)字已經(jīng)很令人望而生畏了,因?yàn)?Fedora 的軟件庫(kù)中有 6 萬(wàn)多個(gè)軟件包,其中大部分是用 C 語(yǔ)言編寫(xiě)的。
關(guān)于這個(gè)議題,目前沒(méi)有聽(tīng)到什么強(qiáng)烈反對(duì)意見(jiàn)。Gentoo 正在為 Clang 16 進(jìn)行類(lèi)似的修改,它已經(jīng)拒絕了傳統(tǒng)方式 C 語(yǔ)言寫(xiě)法;如果運(yùn)氣好的話(huà),這應(yīng)該可以把工作量減少一半。更多的發(fā)行版加入到這個(gè)行列中的話(huà),就可以進(jìn)一步減少相關(guān)工作。Fedora 工程指導(dǎo)委員會(huì)(FESCo)將需要考慮這個(gè)提議,來(lái)決定發(fā)行版是否應(yīng)該繼續(xù)執(zhí)行;目前,這個(gè)提議仍處于討論階段。這是一項(xiàng)艱巨的工作(事實(shí)上也是一項(xiàng)無(wú)用的工作)但會(huì)幫助很多項(xiàng)目來(lái)實(shí)現(xiàn)代碼的現(xiàn)代化。Fedora 顯然也會(huì)從這種現(xiàn)代化過(guò)程中受益。
全文完
LWN 文章遵循 CC BY-SA 4.0 許可協(xié)議。
長(zhǎng)按下面二維碼關(guān)注,關(guān)注 LWN 深度文章以及開(kāi)源社區(qū)的各種新近言論~
