LWN:让Fedora的C代码更加现代化!

共 3619字,需浏览 8分钟

 ·

2022-11-17 15:26

关注了就能看到更多这么棒的文章哦~

Modernizing Fedora's C code

By Jake Edge
November 2, 2022
DeepL assisted translation
https://lwn.net/Articles/913505/

人们通常很少能看到针对 18 个月之后才发布的 Fedora 版本所提出的修改建议,但最近邮件列表中就有这么一个例子。它是针对该发行版的无数软件包中的 C 源代码的改动,将会 fix 那些使用了一些古老功能的兼容性代码,原因是这些特性已经在 C99 标准中删除了,不过 GCC 仍然支持。从提前这么久就发布这个提案就可以猜到,要达到这个目的,会有相当多的工作。

按通常进行 Fedora 提案的惯例,这个建议最早是由 Fedora 项目经理 Ben Cotton 代表这个提案的负责人 Florian Weimer 发布到 Fedora 开发邮件列表的;它也可以在 Fedora wiki 上看到更新版本。目前,Fedora 37 即将面世,但该提案的目标版本是 Fedora 40,时间点暂定于 2024 年北半球春天。正如标题所描述的,目标是 "将 Fedora 移植到 Modern C 语言"。

Old C

C 语言中有一些功能在 C99 标准中被删除了,但在 GCC 中仍然默认是接受的,所以在组成 Fedora 的巨大的代码库中还存在使用这些功能的地方。我们打算开始着手清理这些功能,希望能与其他发行版合作,并将这些改动纳入 upstream 项目。主要针对六种不同的 construct,但最重要的是移除了隐式函数声明(implicit function declaration):

这个兼容传统的功能会导致编译器自动提供一个 int function()类型的声明,用在某个函数没有被声明但是作为一个函数被调用的情况下。然而,使用 int 作为隐含的返回类型,会导致跟返回指针的函数不兼容,如果编译器没有给出 warning 的话,就会导致 debug 过程很困难,如 https://developers.redhat.com/blog/2019/04/22/implicit-function-declarations-flexs-use-of-reallocarray 这里所述。

上面这个链接的文章提到,在 64 位系统上隐式返回 32 位 int 会导致指针被截断。同样,在隐式加上 int 声明后调用的返回 Bool 类型的函数,其返回值也可能会被曲解,因为它们没有修改 return register 中所有的 32 个 bit。

人们发现的另一个问题是,在 C99 之前,下面的代码会隐式地定义两个 int 变量,因为代码里没有指定具体类型:

static i = 1.0;
auto j = 2.0;

由于在 Fedora 里的许多软件包中使用了 Autoconf,这就导致更加难以检测并删除这两个 "implicit-int":


这两个改动都不是很容易实现的,因为如果给这些情况下报出 error 的话(这是按照 C99 的要求)会改变 autoconf 配置检查的结果。例如,相当多的此类检查都使用了隐式声明的 exit 函数。这些问题其实跟被测试的功能没有什么关系。如果编译构建系统写得很好的话,构建仍然会成功,相关的特性会在测试套件中自动禁用掉,并且从 reference ABI 列表中删除掉,而且不会立即发现该特性已经消失了。因此,需要注意一下不要发生这种改动,需要将软件包移植到 C99。

正如 LLVM 论坛网站上的一条消息所指出的,该项目中包含的 Autconf 脚本可能依赖于隐式声明,任何编译器 warning 或 error 都可能由于 Autoconf 的运行方式而被丢失。人们正在开发一些工具来帮助找到有问题的代码,而不破坏 Autoconf 的这个工具。该提案说,跟其他发行版配合,也会有助于这一过程。

其他还需要一些改动来 fix 一些棘手的情况。bool、true 和 false 关键字会需要被强制使用;所有哪些自己重新定义了这些关键字的代码就需要被改变。此外,GCC 在看到把指针赋值给 int 变量的时候不会认为这是一个 error,这也需要 fix。旧方式的函数定义,例如:

int sum(a, b)
char *a;
int b;
{ ...

也需要进行清理。最后,函数声明中的空括号的使用也需要收紧:

在早期的 C 语言版本中,声明 int function() 的时候会声明函数接受不确定的数量的未知类型的参数。这意味着 function(1)和 function("one")都可以正常编译,尽管它们可能无法正确地运行。在较新版本的 C 标准中,int function() 就将意味着函数不接受任何参数(就像在 C++中一样,或者就像在目前的 C 中写成 int function(void)一样)。因此,那些指定了参数的调用都会导致编译错误。

根据该提议的说法,Fedora 这样做之后有双重收益。有时一些难以追踪的、"看起来很像编译器或 ABI 错误 "的 bug 就可以被避免,因为那些可能容易被忽视的 warning 都会变成编译 error。此外,人们认为其中一些遗留功能阻碍了 GCC 的进步。该提案期待着 GCC 14 的发布,该版本预计将包含在 Fedora 40 中。有人认为 GCC 14 会默认禁用对这些特性的支持,但即使这一点没有成为现实的话,Fedora 40 也会改变其默认值,以便之后不会引入 regression 问题。

Questions

Daniel P. Berrangé 指出,当他看到这个提案时,立即想到了 Autoconf 这个难题。他指出,其他通过编译各种测试程序来探测功能的构建系统也可能会受到影响。Weimer 说,他已经在 Python 的 setuptools 中发现了这样的一个问题。它生成测试程序从而确定环境中是否存在某些功能,但这些程序依赖于 implicit int,所以它们可能在严格的编译器情况下会失败,但其实系统中并不缺乏想要检查的这个功能。

Berrangé 还询问了开发者如何测试他们的代码中是否有这些问题。毕竟这不是简单地设置 -std=gnu99 就可以实现的,因为这样设置之后仍然会允许旧有方式的代码(至少现在是这样)。Weimer 介绍了所需的编译选项("是-Werror=implicit-int -Werror=implicit-function-declaration,最好再加上-Werror=int-conversion -Werror=strict-prototypes -Werror=old-style-definition。");他还更新了 wiki 上的提案,以便将每个编译选项都跟相应的过时的功能联系起来。

Alexander Sosedkin 想知道,为什么移植工作不可以更悠闲一些,按部就班地进行分配和 fix。开始时,每一个软件包都可以被标记为不支持这个改动,并且可以为所有支持这个改动的软件包切换一下 GCC 的默认选项;这时虽然什么都没有改变,但软件包可以慢慢地进行所需的改动以及修改相关的标记。尽管 Weimer 希望可以这样来实现,但他真的不认为这种分布式的移植方式是可行的;"我们必须教更多的人了解这些 C 语言的奥秘,以及 autoconf 这些特殊情况。说实话,我不认为这不太值得花时间去学习"。

Vit Ondruch 询问了这项工作的当前状况,以及将受其影响的软件包的数量。Weimer 说,他还在试图弄清楚这一点,但他初步发现大约有 10% 的 Fedora 软件包会受到影响,他认为这个数字可能高估了。这个数字已经很令人望而生畏了,因为 Fedora 的软件库中有 6 万多个软件包,其中大部分是用 C 语言编写的。

关于这个议题,目前没有听到什么强烈反对意见。Gentoo 正在为 Clang 16 进行类似的修改,它已经拒绝了传统方式 C 语言写法;如果运气好的话,这应该可以把工作量减少一半。更多的发行版加入到这个行列中的话,就可以进一步减少相关工作。Fedora 工程指导委员会(FESCo)将需要考虑这个提议,来决定发行版是否应该继续执行;目前,这个提议仍处于讨论阶段。这是一项艰巨的工作(事实上也是一项无用的工作)但会帮助很多项目来实现代码的现代化。Fedora 显然也会从这种现代化过程中受益。

全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~



浏览 22
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报