GCC的-Wpsabi选项到底能做什么?抑制它意味着什么?


69

背景

去年,我使用了nlohmann json库[1],并且使用GCC 5.x在x86_64上进行了交叉编译arm-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)的基础。作为参考,此答案[11]简要介绍了ABI:

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

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

这些问题的答案是:

  • 特定于语言的语言(因此,您拥有C ABI,C ++ ABI,Fortran ABI,Pascal ABI……甚至Java字节码规范,尽管它针对的是“虚拟”处理器而不是真正的硬件,但它都是ABI),
  • 特定于操作系统的(同一硬件上的MS Windows和Linux使用不同的ABI),
  • 特定硬件/ CPU(ARM和x86 ABI不同)。
  • 随着时间的推移而发展(现有ABI经常被更新/修订,以便可以利用新的CPU功能,例如,指定应用程序如何使用x86 SSE寄存器当然只有一次CPU具有这些规则,因此需要澄清现有的ABI。

因此,ABI是总体组件,而其组件之一(“特定于硬件/ CPU”的细节)是psABI。

我的问题

我遇到的问题是

  1. 我不喜欢在不理解其含义的情况下普遍禁用警告。
  2. 对于-Wno-psabi编译器升级后“突然出现”的这些类型的诊断注释,“用于使注释消失的建议”似乎是非常普遍的建议。[2] [3] [4]甚至一位GCC开发人员都建议这样做。[5]
  3. GCC手册中也-Wpsabi没有-Wno-psabi记录[6][7]

结果,我不太确定到底-Wno-psabi会影响什么。一个相关的选项-Wabi 记载:[8]

-Wabi (C, Objective-C, C++ and Objective-C++ only)

在G ++发出警告时,它生成的代码可能与与供应商无关的C ++ ABI不兼容...

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

  • 对于SysV / x86-64,具有长双精度成员的并集将按照psABI中的指定在内存中传递。例如:

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

union U 总是在内存中传递。

我对这一切的理解是

  1. -Wabi psABI更改时,将生成警告。
  2. GCC 7修复了GCC 5中引入的会影响ARM目标的ABI错误[9]
    • 在发行说明中指出“这是ABI更改”。[10]
    • 出于某种原因,发行说明指出,相关的诊断说明是在使用未说明-Wpsabi而不是未说明的文档时生成的-Wabi
    • 手册中未提及此ABI更改。
  3. 将“这是ABI更改”和“使用-Wpsabi”放在一起,在我看来这是专门的一个psABI改变,而不是一种别样ABI的变化。(实际上,这是GCC对psABI实施的更改,而不是psABI本身)

我知道文档并不总是最新的,尤其是对于某些已知的无文档记录的选项。但我担心的是-Wno-psabi”似乎是这些神秘诊断笔记中几种不同类型的标准响应。但是,以我对ABI的基本理解,ABI的变化不是很大吗?我是否应该关注ABI的变化,而不仅仅是让信息消失?在未记录的内容和ABI与psABI的一些更详细的细节之间,我不确定...

例如,如果我添加-Wno-psabi到我的makefile文件,使这些笔记消失,如果有什么,在未来的另一ABI的变化影响了我的项目怎么办?我是否有效地消除了将来可能很重要的警告或注意事项?

同样,即使我们被告知“如果重新编译所有代码,也没有什么可担心的”,[5] “所有代码”到底是什么?那是我的源代码吗?glibc?我可能正在使用其他系统范围的共享库吗?

参考文献

  1. https://github.com/nlohmann/json
  2. https://github.com/nlohmann/json/issues/658
  3. https://stackoverflow.com/a/48149400
  4. https://stackoverflow.com/a/13915796/10270632
  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://stackoverflow.com/a/8063350

7
令人耳目一新的好问题;投票。
Jesper Juhl

@ jesper-juhl谢谢。我搜寻了SO,邮件列表等,寻找答案。我什至花了一些时间在GCC源代码中试图弄清楚(也许可以提供补丁来修复丢失的文档),但是我对GCC内部并不十分了解。感觉就像用大锤敲钉子,然后忘记了普通的锤子存在……
rmc

我很伤心,我没有一个很好的答案。但是希望有人会拒绝谁做。:)
Jesper Juhl

@ jesper-juhl这使我们两个人:)我想不可避免的是,有一天我会遇到一个尚无SO答案的问题,因此将被迫提出一个问题。
rmc

我认为这不会影响您。如果标准库是使用与您可能使用的相同的GCC版本(我的意思是使用相同abi的版本)编译的,那么一切都应该没问题。
geza

Answers:


5

跨库边界时,您只需要担心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

1
是的,不能保证它会崩溃,其行为将是不确定的
Alan Birtles

undefined是一个危险区域,当事情“看起来还不错”时,当一切都崩溃时,它会给粗心的人一种错误的安全感。相反,应用程序无法启动,问题得以解决
hhafez
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.