Created attachment 26233 [details] concatated ma2.i and ma2_test.i from -save-temps ma2.c and ma2_test.c Attached code gives erroneous warning when compiled with - gcc -O3 -flto -Wall -Wextra -D_FORTIFY_SOURCE=2 -c ma2.c gcc -O3 -flto -Wall -Wextra -D_FORTIFY_SOURCE=2 -c ma2_test.c gcc -O3 -flto -Wall -Wextra -D_FORTIFY_SOURCE=2 -o ma2_test ma2_test.o ma2.o ma2.o (symbol from plugin): warning: memset used with constant zero length parameter; this could be due to transposed parameters Compiling without -flto or without -D_FORTIFY_SOURCE=2 does not give warning. Generated code seems to work ok anyway.
I think this is a linker bug, GCC optimizes away the function (seeing that the argument is _not_ zero), but the linker warns about it anyway and it is still output for some reason: 72: 00000000004006f0 2 FUNC GLOBAL HIDDEN 15 __warn_memset_zero_le n resolution file: 2 ma2.o 4 84 cd7721f0 PREVAILING_DEF_IRONLY ma_init 95 cd7721f0 PREVAILING_DEF_IRONLY ma_pool 105 cd7721f0 PREVAILING_DEF_IRONLY ma_get_cell 124 cd7721f0 RESOLVED_EXEC __warn_memset_zero_len ma2_test.o 4 86 64da28d6 PREVAILING_DEF main 108 64da28d6 RESOLVED_IR ma_get_cell 113 64da28d6 RESOLVED_IR ma_pool 117 64da28d6 RESOLVED_IR ma_init
Created attachment 28514 [details] MUCH simpler test case
This may be a bug in gcc OR the linker. I don't know, BUT I have a more precise description of the issue and a much simpler minimal test case. The problem occurs when all of the following conditions are true: 1) gcc is invoked with -O2 and -flto 2) memset is called with a non-constant length argument 3) memset is called with a NON zero fill value argument. The minimal test case is: #include <string.h> void main (int argc, char **argv) { char buf[5000]; memset (buf, 0xFF, argc); } To conform to the bug submission guidelines, the preprocessed file for the this test is attached.
typedef __SIZE_TYPE__ size_t; extern void *memset (void *__s, int __c, size_t __n) __attribute__ ((__nothrow__)) __attribute__ ((__nonnull__ (1))); extern void __warn_memset_zero_len (void) __attribute__((__warning__ ("memset used with constant zero length parameter; this could be due to transposed parameters"))); extern __inline __attribute__((__always_inline__)) __attribute__((__artificial__)) void * __attribute__ ((__nothrow__)) memset (void *__dest, int __ch, size_t __len) { if (__builtin_constant_p (__len) && __len == 0 && (!__builtin_constant_p (__ch) || __ch != 0)) { __warn_memset_zero_len (); return __dest; } return __builtin___memset_chk (__dest, __ch, __len, __builtin_object_size (__dest, 0)); } void main (int argc, char **argv) { char buf[5000]; memset (buf, 0xFF, argc); } This breaks a lot of applications if you build them with LTO and -D_FORTIFY_SOURCE=2. The reason this happens is that when LTO bytecode is output we still have <bb 2>: _2 = (long unsigned int) argc_1(D); _6 = __builtin_constant_p (_2); if (_6 != 0) goto <bb 3>; else goto <bb 5>; <bb 3>: if (_2 == 0) goto <bb 4>; else goto <bb 5>; <bb 4>: __warn_memset_zero_len (); goto <bb 6>; <bb 5>: __memset_chk (&buf, 255, _2, 5000); <bb 6>: buf ={v} {CLOBBER}; thus __builtin_constant_p is not yet forced to be evaluated. This means that we put __warn_memset_zero_len into the LTO symbol table which is queried by the linker and this causes it to warn at the "beginning" of link-time. Also (as can be seen with the cases where we introduce a call late) the linker wants to see a final set of symbols at this time, thus it won't drop the reference to __warn_memset_zero_len even if during LTRANS phase we optimize it away.
It also (sadly) means this "works" with -fno-use-linker-plugin. It also means that not outputting the UNDEF into the LTO symbol table for this case doesn't work as the executable will not link (we optimize the symbol away) if we don't fold away the reference to it later. I see no better way than either forcing the linker to re-scan needed symbols and warn at a "second" stage only or to fold __builtin_constant_p earlier.
Btw, it works when using gold ...
Thanks for reducing this test case further. That illuminates something for me: Specifically: Notice in the following lines: if (__builtin_constant_p (__len) && __len == 0 && (!__builtin_constant_p (__ch) || __ch != 0)) { The part of the expression "|| __ch != 0)" What could possibly be the purpose of this check? That is exactly why the 3rd condition I previously mentioned: > 3) memset is called with a NON zero fill value argument. triggers this issue. The warning is about the length, but it is checking the value being set. How could that make sense? Clearly this check lets the vast majority of uses of memset pass without issue since all of those cases use 0 as the value to be set. From my point of view, if that check wasn't there (i.e. remove "|| __ch != 0" from the expression), the problem would not exist.
(In reply to Mark Pizzolato from comment #7) > Thanks for reducing this test case further. That illuminates something for > me: > > Specifically: > > Notice in the following lines: > > if (__builtin_constant_p (__len) && __len == 0 > && (!__builtin_constant_p (__ch) || __ch != 0)) > { > > The part of the expression "|| __ch != 0)" > > What could possibly be the purpose of this check? > > That is exactly why the 3rd condition I previously mentioned: > > > 3) memset is called with a NON zero fill value argument. > > triggers this issue. The warning is about the length, but it is checking > the value being set. How could that make sense? Clearly this check lets > the vast majority of uses of memset pass without issue since all of those > cases use 0 as the value to be set. > > From my point of view, if that check wasn't there (i.e. remove "|| __ch != > 0" from the expression), the problem would not exist. When val == 0 then the case of len == 0 is ambiguous and you can't really tell the user they swapped val and len (because they are equal).
> When val == 0 then the case of len == 0 is ambiguous and you can't really tell the user they swapped val and len (because they are equal). That is certainly true. I'm not sure how that specifically relates to the problem since the warning message is only about the length being 0. The problem is that the warning is being issued incorrectly when the set value is != 0 and the length is not a constant.
It's a GNU ld bug I believe. https://sourceware.org/bugzilla/show_bug.cgi?id=16746
This has now been fixed on mainline binutils