c++ - GCC的-Wpsabi選項究竟做了什麼? 抑制它有什麼影響?



linux gcc-warning (1)

背景

去年我使用的是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( 應用程序二進制接口 )是一種標準,它定義了高級語言中的低級概念與特定硬件/ 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 而不是記錄的 -Wpsabi 時生成相關的診斷說明。
    • 手冊中未提及此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

當您跨越圖書館邊界時,您只需要擔心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




abi