背景
去年,我使用了 nlohmann json 库[1],并使用 GCC 5.x arm-linux-gnueabi-*
在 x86_64 上进行交叉编译,没有任何警告。当我将 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(应用程序二进制接口)是一种标准,它定义了高级语言中的低级概念与特定硬件/操作系统平台的机器代码的能力之间的映射。这包括: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 具有这些 regs,因此需要澄清现有的 ABI)。
因此,ABI 是首要组件,其中一个组件(“硬件/CPU 特定”细节)是 psABI。
我的问题
我遇到的问题是
我不喜欢在不了解其含义的情况下普遍禁用警告。 “使用 -Wno-psabi 使注释消失”的建议对于编译器升级后“突然出现”的这些类型的诊断注释似乎是非常常见的建议。[2][3][4]甚至 GCC 开发人员之一也建议这样做。 [5] GCC 手册中没有记录 -Wpsabi 和 -Wno-psabi [6]。[7]
因此,我不确定 -Wno-psabi
究竟会影响什么,不会影响什么。相关选项 -Wabi
已记录:[8]
-Wabi(仅限 C、Objective-C、C++ 和 Objective-C++)当 G++ 生成的代码可能与供应商中立的 C++ ABI 不兼容时发出警告...它还警告与 psABI 相关的更改。此时已知的 psABI 更改包括: 对于 SysV/x86-64,具有长双精度成员的联合在 psABI 中指定的内存中传递。例如: union U { long double ld;诠释我; }; union U 总是在内存中传递。
我对这一切的理解是
-Wabi 将在 psABI 更改时生成警告。 GCC 7 修复了 GCC 5 中引入的影响 ARM 目标的 ABI 错误[9]。在发行说明中声明“这是一个 ABI 更改。”[10] 出于某种原因,发行说明声明相关的诊断说明是在使用未记录的 -Wpsabi 而不是记录的 -Wabi 时生成的。手册中未提及此 ABI 更改。将“这是 ABI 更改”和“使用 -Wpsabi”放在一起,在我看来,这具体是 psABI 更改,而不是不同类型的 ABI 更改。 (实际上这是 GCC 对 psABI 实施的改变,而不是 psABI 本身)
我知道文档并不总是最新的,尤其是对于已知的未记录选项。但我担心的是,“使用-Wno-psabi
”似乎是几种不同类型的这些神秘诊断说明的标准响应。但是,在我对 ABI 的基本理解中,ABI 的变化不是很大吗?我不应该担心 ABI 的变化,而不仅仅是让消息消失吗?在未记录的内容和 ABI 与 psABI 的一些更精细的细节之间,我不太确定......
例如,如果我将 -Wno-psabi
添加到我的 makefile 以使这些注释消失,如果将来有另一个 ABI 更改是否影响我的项目怎么办?我是否有效地消除了未来可能重要的警告或注释?
此外,即使我们被告知“如果您重新编译所有代码,则无需担心”[5] 究竟什么是“所有代码”?那是我的源代码吗? glibc?我可能正在使用的任何其他系统范围的共享库?
参考
https://github.com/nlohmann/json https://github.com/nlohmann/json/issues/658 https://stackoverflow.com/a/48149400 https://stackoverflow.com/a/13915796/10270632 https://gcc.gnu.org/ml/gcc/2017-05/msg00073.html https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81831 https://gcc.gnu.org/ onlinedocs/gcc-8.2.0/gcc https://gcc.gnu.org/onlinedocs/gcc-8.2.0/gcc/C_002b_002b-Dialect-Options.html https://gcc.gnu.org/bugzilla/show_bug。 cgi?id=77728 https://gcc.gnu.org/gcc-7/changes.html https://stackoverflow.com/a/8063350
当您跨越图书馆边界时,您只需要担心 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
不定期副业成功案例分享