GCC的-Wpsabi选项到底是什么?禁用它会有什么影响?

98

背景

去年我使用了nlohmann json库[1],并且在x86_64上使用GCC 5.xarm-linux-gnueabi-*进行交叉编译时没有收到任何警告。当我将GCC更新到新版本时,GCC会生成大量晦涩难懂的诊断信息。例如,下面是其中一个诊断信息的示例:

In file included from /usr/arm-linux-gnueabi/include/c++/7/vector:69:0,
             from include/json.hpp:58,
             from src/write_hsi.cpp:23:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc: In member function ‘void std::vector<_Tp, _Alloc>::_M_realloc_insert(std::vector<_Tp, _Alloc>::iterator, _Args&& ...) [with _Args = {nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long int, long long unsigned int, double, std::allocator, nlohmann::adl_serializer>}; _Tp = nlohmann::basic_json<>; _Alloc = std::allocator<nlohmann::basic_json<> >]’:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc:394:7: note: parameter passing for argument of type ‘std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > >::iterator {aka __gnu_cxx::__normal_iterator<nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >}’ changed in GCC 7.1
   vector<_Tp, _Alloc>::
   ^~~~~~~~~~~~~~~~~~~
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc: In member function ‘nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer> nlohmann::basic_json<ObjectType, ArrayType, StringType, BooleanType, NumberIntegerType, NumberUnsignedType, NumberFloatType, AllocatorType, JSONSerializer>::parser::parse_internal(bool) [with ObjectType = std::map; ArrayType = std::vector; StringType = std::__cxx11::basic_string<char>; BooleanType = bool; NumberIntegerType = long long int; NumberUnsignedType = long long unsigned int; NumberFloatType = double; AllocatorType = std::allocator; JSONSerializer = nlohmann::adl_serializer]’:
/usr/arm-linux-gnueabi/include/c++/7/bits/vector.tcc:105:21: note: parameter passing for argument of type ‘__gnu_cxx::__normal_iterator<nlohmann::basic_json<>*, std::vector<nlohmann::basic_json<>, std::allocator<nlohmann::basic_json<> > > >’ changed in GCC 7.1
_M_realloc_insert(end(), std::forward<_Args>(__args)...);
~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

解决方案很简单,只需将-Wno-psabi添加到编译器选项中即可。实际上,这也是库中实现的修复措施。[2]

我了解应用程序二进制接口(ABI)和处理器特定ABI(psABI)的基本知识。有关ABI的快速概述,请参见此答案[11]

ABI(应用程序二进制接口)是一种标准,它定义了高级语言中的低级概念与特定硬件/操作系统平台机器代码的功能之间的映射。这包括诸如:

  • C/C++/Fortran/ ...数据类型在内存中的布局方式(数据大小/对齐方式)
  • 嵌套的函数调用如何工作(返回到函数调用者的信息存储在哪里以及如何存储在CPU寄存器和/或内存中的函数参数传递方式)
  • 程序启动/初始化如何工作(“可执行文件”具有什么数据格式,代码/数据如何从中加载,DLL如何工作...)

这些问题的答案是:

  • 特定于语言(因此您有C ABI、C++ ABI、Fortran ABI、Pascal ABI等,即使是针对虚拟处理器而不是实际硬件的Java字节码规范也是ABI)
  • 特定于操作系统(MS Windows和Linux在同一硬件上使用不同的ABI)
  • 特定于硬件/CPU(ARM和x86 ABIs不同)。
  • 随着时间的推移而不断发展(现有ABI经常被更新/修订,以便可以利用新的CPU功能,例如指定应用程序如何使用x86 SSE寄存器当然只有在CPU拥有这些寄存器后才有可能,因此需要澄清现有ABI)。

因此,ABI是一个总体组件,其中之一组成部分(“硬件/CPU特定”细节)是psABI。

我的问题

我遇到的问题是

  1. 我不喜欢在不了解影响的情况下普遍禁用警告。
  2. 建议“使用-Wno-psabi使笔记消失”似乎是这些编译器升级后“突然出现”的此类诊断笔记的常见建议。[2][3][4]甚至GCC开发人员中的一个也建议这样做。[5]
  3. 无论是-Wpsabi还是-Wno-psabi都没有在GCC手册[7]中记录[6]

因此,我不太确定-Wno-psabi会影响什么以及不会影响什么。相关选项-Wabi已记录:[8]

-Wabi(仅限C、Objective-C、C++和Objective-C ++)

当 G++ 生成与厂商中性 C++ ABI 不兼容的代码时,它会发出警告...

它还警告有关 psABI 相关更改的情况。此时已知的 psABI 更改包括:

  • 对于 SysV/x86-64,具有 long double 成员的 union 按照 psABI 规定在内存中传递。例如:

union U { long double ld; int i; };

union U 总是在内存中传递。

参考文献

  1. https://github.com/nlohmann/json
  2. https://github.com/nlohmann/json/issues/658
  3. https://dev59.com/v1YM5IYBdhLWcg3w-Dtf#48149400
  4. https://dev59.com/u2Yr5IYBdhLWcg3wJ3CU#13915796
  5. https://gcc.gnu.org/ml/gcc/2017-05/msg00073.html
  6. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81831
  7. https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc
  8. https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/C_002b_002b-Dialect-Options.html
  9. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77728
  10. https://gcc.gnu.org/gcc-7/changes.html
  11. https://dev59.com/gGsz5IYBdhLWcg3wNE5q#8063350

@jesper-juhl 谢谢。我在 Stack Overflow、邮件列表等地方搜寻答案,甚至花了一些时间在 GCC 源代码中尝试弄清楚(也许能够贡献一个修复缺失文档的补丁),但我对 GCC 内部不够熟悉,无法解决问题。感觉就像用大锤敲钉子,然后忘记普通的锤子存在... - rmc
@jesper-juhl 我和你一样 :) 我想这是不可避免的,总有一天我会有一个SO上没有答案的问题,被迫注册账号来询问。 - rmc
我认为这不会影响你。如果标准库使用与你使用的GCC版本相同的编译器(也就是说,使用相同的abi版本),那么一切都应该没问题。 - geza
1
@Peter,是的,我们使用nlohmann作为头文件。 - rmc
@FrankHB 我认为这实际上正是我担心禁用“-Wpsabi”会遇到的情况。我经历过ABI更改警告,这是在从GCC 5.x更新到更新版本后发生的(我不记得它是新的5.x还是6.x)。你提供的GCC 9更改是针对一个错误,“在Arm目标中...在GCC 6、7和8版本中...这是一个ABI更改。如果启用了选项-Wpsabi...编译器将发出诊断注释...”如果我两年前在使用GCC 5.x时在我的makefile中加入了“-Wno-psabi”,那么它就会抑制此后ABI更改的警告。 - rmc
显示剩余7条评论
1个回答

15

只有在跨库边界时才需要担心ABI。对于您自己的应用程序/库,ABI并不重要,因为可以假定所有对象文件都是使用相同的编译器版本和开关编译的。

如果您有一个使用ABI1编译的库,以及一个使用ABI2编译的应用程序,则当应用程序尝试调用来自库的函数时,它会崩溃,因为它无法正确传递参数。要解决崩溃问题,您需要使用ABI2重新编译该库(以及任何其他依赖于它的库)。

在您的特定情况下,只要您将nlohmann与应用程序使用相同的编译器版本进行编译(或仅将nlohmann用作头文件),则无需担心ABI更改。

全局禁止警告似乎是一种危险的选择,因为它将防止您查看任何未来的ABI问题。更好的选择是使用#pragma仅针对相关函数禁用警告,例如:

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-psabi"
void foo()
{
}
#pragma GCC diagnostic pop

抱歉重新激活一个非常古老的帖子,但您说当从使用ABI2编译的应用程序调用使用ABI1编译的库时,应用程序将崩溃,这是保证的行为还是您使用术语“崩溃”来描述“应用程序将表现不良”。以Redhat为例,他们只是说它不受支持,不能保证工作,而不是说它会崩溃。老实说,我更喜欢崩溃,但想了解这个声明的来源。 - hhafez
4
是的,不能保证它不会崩溃,行为将是未定义的。 - Alan Birtles
未定义是一个危险的区域,当事情“看起来没问题”时,它可能会给不谨慎的人一种虚假的安全感,但实际上一旦进入其中,一切都会崩溃。最好应用程序无法启动并解决问题。 - hhafez
@alan-birtles 哇,我已经好几年没登录了,甚至都忘了我曾经问过这个问题。我会将你的答案标记为被接受的,因为我认为它涵盖了我一直在想的所有要点。如果我没记错的话,由于这是针对嵌入式目标的,并且我们拥有所有第三方库的源代码,我们最终只是使用更新的交叉编译器重新编译了所有内容。 - rmc
4
使用“-Wno-psabi”这个参数无法被gcc诊断指示符所识别。代码中包含此行将无法构建。 - fuzzyTew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接