What exactly does GCC's -Wpsabi option do? What are the implications of supressing it?
Background
In the last year I was using the nlohmann json library[1] and was cross-compiling on x86_64 using GCC 5.x arm-linux-gnueabi-*
with no warnings. When I updated GCC to a newer version, GCC would generate pages of cryptic diagnostic notes. For example, here is one of the notes
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)...);
~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
It was easy to find a solution, namely by adding -Wno-psabi
to the compiler options. In fact, that was the fix implemented in the library.[2]
I understand the basics of Application Binary Interfaces (ABIs) and processor-specific ABIs (psABIs). For reference, this answer[11] gives a quick overview of ABIs:
An ABI (Application Binary Interface) is a standard that defines a mapping between low-level concepts in high-level languages and the abilities of a specific hardware/OS platform's machine code. That includes things like:
- how C/C++/Fortran/... data types are laid out in memory (data sizes / alignments)
- how nested function calls work (where and how the information on how to return to a function's caller is stored, where in the CPU registers and/or in memory function arguments are passed)
- how program startup / initialization works (what data format an "executable" has, how the code / data is loaded from there, how DLLs work ...)
The answers to these are:
- language-specific (hence you've got a C ABI, C++ ABI, Fortran ABI, Pascal ABI, ... even the Java bytecode spec, although targeting a "virtual" processor instead of real hardware, is an ABI),
- operating-system specific (MS Windows and Linux on the same hardware use a different ABI),
- hardware/CPU-specific (the ARM and x86 ABIs are different).
- evolving over (long) time (existing ABIs have often been updated / rev'ed so that new CPU features could be made use of, like, say, specifying how the x86 SSE registers are to be used by apps was of course only possible once CPUs had these regs, therefore existing ABIs needed to be clarified).
So the ABI is the overarching component, and one of its components (the "hardware/CPU-specific" details) is the psABI.
My Issue
The problem I am having is
- I don't like universally disabling warnings without understanding the implications.
- The advice "use
-Wno-psabi
to make the notes go away" seems to be pretty common advice for these types of diagnostic notes that "suddenly appear" after a compiler upgrade.[2][3][4] Even the one of the GCC developers suggest doing this.[5] - Neither
-Wpsabi
nor-Wno-psabi
are documented[6] in the GCC manual.[7]
As a result I am not really sure what exactly -Wno-psabi
will and will not affect. A related option -Wabi
is documented:[8]
-Wabi (C, Objective-C, C++ and Objective-C++ only)
Warn when G++ it generates code that is probably not compatible with the vendor-neutral C++ ABI...
It also warns about psABI-related changes. The known psABI changes at this point include:
- For SysV/x86-64, unions with long double members are passed in memory as specified in psABI. For example:
union U {
long double ld;
int i;
};
union U
is always passed in memory.
My understanding of this all is
-
-Wabi
will generate warnings when there is a psABI change. - GCC 7 fixed an ABI bug[9] introduced in GCC 5 that affects ARM targets.
- In the release notes it is stated "this is an ABI change."[10]
- For some reason the release notes state that the related diagnostic notes are generated when using the undocumented
-Wpsabi
, not the documented-Wabi
. - This ABI change is not mentioned in the manual.
- Putting together "this is an ABI change" and "use
-Wpsabi
", it appears to me this is specifically a psABI change, not a different kind of ABI change. (In reality it is a change in GCC's implementation of the psABI, not the psABI itself)
I know that documentation isn't always up to date, especially for something that is a known undocumented option. But my concern is that "use -Wno-psabi
" seems to be the standard response for several different kinds of these cryptic diagnostic notes. But, in my basic understanding of ABIs, isn't an ABI change a big deal? Shouldn't I be concerned about an ABI change, rather than just making the message go away? Between the undocumented stuff and some of the finer details of ABI vs psABI, I'm not really sure...
For example, if I add -Wno-psabi
to my makefile to make those notes go away, what if there is another ABI change in the future that does affect my project? Have I effectively silenced future warnings or notes that may be important?
Also, even though we are told "if you recompile all the code, there is nothing to worry about,"[5] what exactly is "all the code"? Is that my source code? glibc? Any other system-wide shared library I might be using?
References
- 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
You only need to worry about ABIs when you are crossing library boundaries. Within your own applications/libraries the ABI doesn't really matter as presumably all your object files are compiled with the same compiler version and switches.
If you have a library compiled with ABI1 and an application compiled with ABI2 then the application will crash when it tries to call functions from the library as it wont pass the arguments correctly. To fix the crash you would need to recompile the library (and any other libraries it depends on) with ABI2.
In your specific case as long as you compile nlohmann with the same compiler version as your application (or are just using nlohmann as a header) then you don't need to worry about the ABI change.
Globally suppressing the warning seems to be a dangerous option as it will prevent you from seeing any future ABI issues. A better option would be to use #pragma
to disable the warning just for the functions in question, e.g.:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-psabi"
void foo()
{
}
#pragma GCC diagnostic pop