Building with recent GCC versions: gdbsupport/gdb_assert.h:35:4: error: 'nonnull' argument 'filename' compared to NULL [-Werror=nonnull-compare]

Tom de Vries tdevries@suse.de
Tue Jul 27 13:38:19 GMT 2021


On 7/27/21 1:49 PM, Tom de Vries wrote:
> On 7/27/21 1:35 PM, Andrew Burgess wrote:
>> * Tom de Vries <tdevries@suse.de> [2021-07-27 12:44:10 +0200]:
>>
>>> On 7/27/21 12:03 PM, Andrew Burgess wrote:
>>>> * Jan-Benedict Glaw <jbglaw@lug-owl.de> [2021-07-26 23:11:01 +0200]:
>>>>
>>>>> Hi!
>>>>>
>>>>> I'm running some CI builds and noticed that, when building GDB with
>>>>> quite recent GCC versions, it breaks.
>>>>>
>>>>> With ie. this "gcc-snapshot" GCC from Debian:
>>>>>
>>>>> /usr/lib/gcc-snapshot/bin/gcc --version
>>>>> gcc (Debian 20210630-1) 12.0.0 20210630 (experimental) [master revision 6bf383c37e6:93c270320bb:35da8a98026849bd20d16bbf9210ac1d0b44ea6a]
>>>>>
>>>>> we see:
>>>>>
>>>>> ./configure --target=i686-linux --prefix=/tmp/gdb-i686-linux
>>>>> [...]
>>>>> all make V=1 all-gdb
>>>>> [...]
>>>>> [all 2021-07-26 20:39:22] /usr/lib/gcc-snapshot/bin/g++ -x c++    -I. -I. -I./config -DLOCALEDIR="\"/tmp/gdb-i686-linux/share/locale\"" -DHAVE_CONFIG_H -I./../include/opcode -I./../readline/readline/.. -I./../zlib -I../bfd -I./../bfd -I./../include -I../libdecnumber -I./../libdecnumber  -I./../gnulib/import -I../gnulib/import -I./.. -I..  -DTUI=1    -I./.. -pthread  -Wall -Wpointer-arith -Wno-unused -Wunused-value -Wunused-variable -Wunused-function -Wno-switch -Wno-char-subscripts -Wempty-body -Wunused-but-set-parameter -Wunused-but-set-variable -Wno-sign-compare -Wno-error=maybe-uninitialized -Wno-mismatched-tags -Wsuggest-override -Wimplicit-fallthrough=3 -Wduplicated-cond -Wshadow=local -Wdeprecated-copy -Wdeprecated-copy-dtor -Wredundant-move -Wmissing-declarations -Wstrict-null-sentinel -Wformat -Wformat-nonliteral -Werror -g -O2   -c -o compile/compile.o -MT compile/compile.o -MMD -MP -MF compile/.deps/compile.Tpo compile/compile.c
>>>>> [all 2021-07-26 20:39:26] In file included from ./../gdbsupport/common-defs.h:126,
>>>>> [all 2021-07-26 20:39:26]                  from ./defs.h:28,
>>>>> [all 2021-07-26 20:39:26]                  from compile/compile.c:20:
>>>>> [all 2021-07-26 20:39:26] ./../gdbsupport/gdb_unlinker.h: In constructor 'gdb::unlinker::unlinker(const char*)':
>>>>> [all 2021-07-26 20:39:26] ./../gdbsupport/gdb_assert.h:35:4: error: 'nonnull' argument 'filename' compared to NULL [-Werror=nonnull-compare]
>>>>> [all 2021-07-26 20:39:26]    35 |   ((void) ((expr) ? 0 :                                                       \
>>>>> [all 2021-07-26 20:39:26]       |   ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>>> [all 2021-07-26 20:39:26]    36 |            (gdb_assert_fail (#expr, __FILE__, __LINE__, FUNCTION_NAME), 0)))
>>>>> [all 2021-07-26 20:39:26]       |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>>>>> [all 2021-07-26 20:39:26] ./../gdbsupport/gdb_unlinker.h:38:5: note: in expansion of macro 'gdb_assert'
>>>>> [all 2021-07-26 20:39:26]    38 |     gdb_assert (filename != NULL);
>>>>> [all 2021-07-26 20:39:26]       |     ^~~~~~~~~~
>>>>> [all 2021-07-26 20:39:27] cc1plus: all warnings being treated as errors
>>>>> [all 2021-07-26 20:39:27] make[1]: *** [Makefile:1642: compile/compile.o] Error 1
>>>>> [all 2021-07-26 20:39:27] make[1]: Leaving directory '/var/lib/laminar/run/gdb-i686-linux/4/binutils-gdb/gdb'
>>>>> [all 2021-07-26 20:39:27] make: *** [Makefile:11410: all-gdb] Error 2
>>>>>
>>>>>
>>>>> I also discussed this on the GCC patches mailing list
>>>>> (https://gcc.gnu.org/pipermail/gcc-patches/2021-July/575568.html),
>>>>> where Martin suggested that this should be fixed here in GDB.
>>>>>
>>>>> Any thoughts about this?
>>>>
>>>> As I understand it the nonnull attribute only provides compile time
>>>> protection against explicitly passing NULL, there's no compiled in
>>>> non-null check (well, maybe with -fisolate-erroneous-paths-attribute,
>>>> but the assert might give a better error message).
>>>>
>>>> This means its still possible to pass NULL to a nonnull function, its
>>>> just the behaviour of the program is undefined in that case.
>>>>
>>>> So, it doesn't seem crazy that we might want to both (a) have a
>>>> function declared nonnull, to prevent explicitly passing NULL, and (b)
>>>> have a NULL check inside the function to catch logic bugs that result
>>>> in NULL being passed.
>>>>
>>>> We could, of course, push the assert outside of the function, but that
>>>> would really suck due to code duplication, and the risk of missing an
>>>> assert, so that seems like a non-starter.
>>>>
>>>> We could drop either the assert, or the nonnull attribute, but that
>>>> would suck as both give a valuable, but different form of protection.
>>>>
>>>> After some experimenting, I suspect that the assert is being optimised
>>>> away anyway, which kind of makes sense, as we're telling the compiler
>>>> it can assume that the pointer is non-null.
>>>>
>>>
>>> Yes, in fact that's what the nonnull-compare warning specifically warns
>>> against: there's some code that may be optimized away, due to the
>>> nonnull attribute.
>>>
>>>> So, what we probably want is someway to tell (or trick) GCC into
>>>> including the null check even in the nonnull function....
>>>>
>>>> ... here's what I came up with, add this somewhere:
>>>>
>>>>  template<typename T>
>>>>  bool __attribute__ ((noinline))
>>>>  nonnull_arg_is_really_not_null (const T *ptr)
>>>>  {
>>>>    return ptr != nullptr;
>>>>  }
>>>>
>>>> then change the assert to:
>>>>
>>>>  gdb_assert (nonnull_arg_is_really_not_null (filename));
>>>>
>>>> Seems to keep the assert, and silence the warning.  Thoughts?
>>>>
>>>
>>> I understand why it works, but it seems fragile to me.  At some point
>>> some compiler may get smart enough to also optimize this case, and then
>>> we're back in the same situation.
>>
>> Good point.
>>
>> The GCC documentation for noinline[1] suggests we can avoid the call
>> being removed by adding 'asm ("");' into the function:
>>
>>   template<typename T>
>>   bool __attribute__ ((noinline))
>>   nonnull_arg_is_really_not_null (const T *ptr)
>>   {
>>     asm ("");
>>     return ptr != nullptr;
>>   }
>>
>> I'm not really arguing for this approach over any other, just sharing
>> what I discovered.
>>
> 
> Ack, understood.  Note that the added asm doesn't stop a compiler from
> doing:
> ...
> gdb_assert (nonnull_arg_is_really_not_null (filename));
> ...
> ->
> ...
> nonnull_arg_is_really_not_null (filename);
> gdb_assert (true);
> ...
> 
> Thanks,
> - Tom
> 
>> Thanks,
>> Andrew
>>
>> [1] https://gcc.gnu.org/onlinedocs/gcc-11.1.0/gcc/Common-Function-Attributes.html#Common-Function-Attributes
>>
>>>
>>> I wonder whether using volatile is a better idea (can't try this out
>>> right now).
>>>

I was thinking of something like this:
...
diff --git a/gdbsupport/gdb_unlinker.h b/gdbsupport/gdb_unlinker.h
index bda6fe7ab54..3d99b41e7ad 100644
--- a/gdbsupport/gdb_unlinker.h
+++ b/gdbsupport/gdb_unlinker.h
@@ -20,6 +20,13 @@
 #ifndef COMMON_GDB_UNLINKER_H
 #define COMMON_GDB_UNLINKER_H

+template<typename T>
+const T *volatile
+ignore_nonnull (const T *ptr)
+{
+  return ptr;
+}
+
 namespace gdb
 {

@@ -35,7 +42,7 @@ class unlinker
   unlinker (const char *filename) ATTRIBUTE_NONNULL (2)
     : m_filename (filename)
   {
-    gdb_assert (filename != NULL);
+    gdb_assert (ignore_nonnull (filename) != NULL);
   }

   ~unlinker ()
...

This builds for me, though I haven't got a setup yet where the warning
reproduces, so I can't check whether it actually fixes things.

Thanks,
- Tom


More information about the Gdb mailing list