https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118636

            Bug ID: 118636
           Summary: `-Werror` and `-w` do not affect linker warnings, but
                    the docs say they should [docs bug?]
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: driver
          Assignee: unassigned at gcc dot gnu.org
          Reporter: evaned at gmail dot com
  Target Milestone: ---

[Detailed information, including the input file, are at the end of the report.]

This could be considered either a behavior bug or a documentation bug. There
wasn't a Docs component to select and the bug writing guidelines don't provide
info on filing documentation bugs, so please excuse if this isn't quite the
format you want for that.

I discovered during an online discussion that `-Werror` does not affect linker
warnings:

    /tmp$ gcc -Werror linker-warning.c -o linker-warning
    /usr/bin/ld: warning: /tmp/cciBYjrB.o: requires executable stack (because
the .note.GNU-stack section is executable)

    /tmp$ echo $?
    0
    /tmp$ ./linker-warning 
    /tmp$ 

Should it? According to the documentation, yes.

There's a corresponding problem where `-w` does not suppress linker warnings,
even though the documentation says it should.


I'll argue that the docs do in fact say that `-Werror` should fail a
compilation when the linker warns, discuss if this is a behavior that should be
changed, propose doc changes, and then provide the detailed info about my
environment.


THE DOCS SAY IT SHOULD WORK
===========================
Here's what the docs say the flags should do:

    -w
        Inhibit all warning messages.
    -Werror
        Make all warnings into errors.

Emphasis on the word "all", and lack of any mention that linker warnings are
not included.

Maybe the section they are in has that qualification? Let's see:

    3.9 Options to Request or Suppress Warnings

    Warnings are diagnostic messages that report constructions that
    are not inherently erroneous but that are risky or suggest there
    may have been an error.

    The following language-independent options do not enable specific
    warnings but control the kinds of diagnostics produced by GCC.

I would say not really.

"Produced by GCC" is moving in that direction, but at best it's ambiguous. You
could read it as appling to just the compiler proper, but this has two
problems. First, I feel that this interpretation is one of those things where a
prerequisite to understanding something is to already understand it. Second,
and worse, is that the very first sentence of the "description" of GCC in the
manpage is "When you invoke GCC, it normally does preprocessing, compilation,
assembly and linking." But if "GCC" is supposed to refer to the compiler
proper, clearly the description of what GCC *is* is incorrect.

Further, even if the section intro had such a clarification, I think the
documentation would still be quite poor on this point, and not reflective of
how people most use reference docs like manpages, which is to search for the
specific thing of interest.

Ironically enough, I *was* going to say the following:

    The closest thing to an unambiguous statement that `-Werror`
    refers to just compiler warnings is if you note that it is part of
    the output of `--help=warnings`, the behavior of which is
    described as "Display all of the options controlling warning
    messages produced by the compiler," with emphasis on "by the
    compiler."

... but THAT IS WRONG! `-Werror` is actually NOT included in that output!
(Another bug?)

I feel quite confident in claiming that the documentation says that `-Werror`
should affect linker warnings.


DOC OR BEHAVIOR BUG?
====================
This bug could be viewed in a couple of ways.

The first is a behavior bug, where the GCC driver should pass
`--fatal-warnings` to the linker. I think there are some obvious attractions to
this change, and it should be considered.

That said, I don't know how universal that flag is. I checked ld.bfd, ld.gold,
lld, and mold and all support that flag, so maybe it's universal enough (or any
remaining problems can be papered over in the linker script), but I don't say
that with confidence.

Potentially a bigger problem is that while `-Werror` would be improved, it
introduces a gulf with some other warning flags. I mentioned how `-w` doesn't
suppress linker warnings, but I don't see an analogue to `-w` for the linker
that the driver could pass; just a bunch of individual no-warning flags that
vary by linker and linker version. And of course the GCC driver allows
controlling compiler warnings with `-Wnonnull` or whatever, and I have
layperson concerns about if GCC could similarly control linker warnings; and
assuming those concerns are well-founded, that's another rough edge.

So I'm kind of expecting you to not change the behavior, and that's why I'm
kind of framing this so much around the documentation bug aspect.


DOCUMENTATION FIXES
===================

Doc fixes seem like they'd be easy. Something like this seems like it'd be my
ideal:

    -w
        Inhibit all compiler warning messages.

    -Werror
        Make all compiler warnings into errors. For linker warnings,
        also pass -Wl,--fatal-warnings, which works with most linkers.

I considered "non-linker" instead of "compiler". And of course massage the "for
linker warnings" sentence as you wish.

The "For linker warnings" sentence serves two purposes. First, it emphasizes
that "compiler" is not just a redundant word but is in contrast to linker
warnings. Even if you don't want to suggest `--fatal-warnings` here to remain
more decoupled, I think this is valuable to keep around; e.g. "Make all
compiler warnings into errors. Linker warnings are not affected" or something
similar. Second, it of course provides a very helpful nudge into what users who
want the extra checking should do.

Some of the surrounding text could be improved as well, however I think the
documentation for `-w` and `-Werror` is the most important and surrounding
improvements should not be viewed as a substitute.

some suggestions for other improvements:

   -Werror=
       Make the specified compiler warning into an error. ...

   -Wfatal-errors
       ....

   You can request many specific compiler warnings with options
   beginning with -W, for example -Wimplicit to request warnings on
   implicit declarations. ...

and then I would suggest a new paragraph before `-Wpedantic`, in that section
about warnings in general:

    The GCC driver provides flags for controlling warnings in the
    preprocessor and compiler only. Warnings from the assembler and
    linker can be controlled by passing command line options to the
    assembler with -Wa and the linker with -Wl; for example,
    -Wl,--fatal-warnings will cause the linker to exit with an error
    if it would otherwise produce a warning. See the documentation for
    your assembler and linker for the relevant flags.

I decided to be inclusive with the assembler in that description, but I'm not
sure how important that is.


DETAILED INFORMATION
====================

The copy/paste sessions were gathered from the `gcc:latest` Docker image,
sha256:c8a47caa..., running on an Ubuntu 20.04 amd64 host. That said, I
discovered this on older versions and I suspect that this problem has been
around for approximately forever and is universal, because of its nature as
mostly a docs bug.

The input file is adapted from
https://www.redhat.com/en/blog/linkers-warnings-about-executable-stacks-and-segments,
though I originally discovered it when having a discussion of some things
related to `gets`. (I just wanted an example that wasn't using an obviously
terrible feature.)

    /tmp$ cat linker-warning.c 
    int foo(int (*p)(int)) {
      return p(7);
    }

    int bar (int arg) {
      int baz (int arg2) { return arg2 * arg; }
      return foo (& baz) + arg;
    }

    int main() {
        return bar(1);
    }

And then all the GCC information:

    /tmp$ gcc -v -save-temps -Werror linker-warning.c 
    Using built-in specs.
    COLLECT_GCC=gcc
   
COLLECT_LTO_WRAPPER=/usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/lto-wrapper
    Target: x86_64-linux-gnu
    Configured with: /usr/src/gcc/configure --build=x86_64-linux-gnu
--disable-multilib --enable-languages=c,c++,fortran,go
    Thread model: posix
    Supported LTO compression algorithms: zlib zstd
    gcc version 14.2.0 (GCC) 
    COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Werror' '-mtune=generic'
'-march=x86-64' '-dumpdir' 'a-'
     /usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/cc1 -E -quiet -v
-imultiarch x86_64-linux-gnu linker-warning.c -mtune=generic -march=x86-64
-Werror -fpch-preprocess -o a-linker-warning.i
    ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu"
    ignoring nonexistent directory
"/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/include-fixed/x86_64-linux-gnu"
    ignoring nonexistent directory
"/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../x86_64-linux-gnu/include"
    #include "..." search starts here:
    #include <...> search starts here:
     /usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/include
     /usr/local/include
     /usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/include-fixed
     /usr/include/x86_64-linux-gnu
     /usr/include
    End of search list.
    COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Werror' '-mtune=generic'
'-march=x86-64' '-dumpdir' 'a-'
     /usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/cc1 -fpreprocessed
a-linker-warning.i -quiet -dumpdir a- -dumpbase linker-warning.c -dumpbase-ext
.c -mtune=generic -march=x86-64 -Werror -version -o a-linker-warning.s
    GNU C17 (GCC) version 14.2.0 (x86_64-linux-gnu)
        compiled by GNU C version 14.2.0, GMP version 6.2.1, MPFR version
4.1.0, MPC version 1.2.1, isl version isl-0.24-GMP

    GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
    Compiler executable checksum: b4330599559c5c656cd247a77c8981b6
    COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Werror' '-mtune=generic'
'-march=x86-64' '-dumpdir' 'a-'
     as -v --64 -o a-linker-warning.o a-linker-warning.s
    GNU assembler version 2.40 (x86_64-linux-gnu) using BFD version (GNU
Binutils for Debian) 2.40
   
COMPILER_PATH=/usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/:/usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/:/usr/local/libexec/gcc/x86_64-linux-gnu/:/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/:/usr/local/lib/gcc/x86_64-linux-gnu/
   
LIBRARY_PATH=/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/:/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../lib64/:/lib/x86_64-linux-gnu/:/lib/../lib64/:/usr/lib/x86_64-linux-gnu/:/usr/lib/../lib64/:/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/../../../:/lib/:/usr/lib/
    COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Werror' '-mtune=generic'
'-march=x86-64' '-dumpdir' 'a.'
     /usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/collect2 -plugin
/usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/liblto_plugin.so
-plugin-opt=/usr/local/libexec/gcc/x86_64-linux-gnu/14.2.0/lto-wrapper
-plugin-opt=-fresolution=a.res -plugin-opt=-pass-through=-lgcc
-plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s
--eh-frame-hdr -m elf_x86_64 -dynamic-linker /lib64/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/crt1.o /lib/x86_64-linux-gnu/crti.o
/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/crtbegin.o
-L/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0
-L/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/../../../../lib64
-L/lib/x86_64-linux-gnu -L/lib/../lib64 -L/usr/lib/x86_64-linux-gnu
-L/usr/lib/../lib64 -L/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/../../..
a-linker-warning.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc
--push-state --as-needed -lgcc_s --pop-state
/usr/local/lib/gcc/x86_64-linux-gnu/14.2.0/crtend.o
/lib/x86_64-linux-gnu/crtn.o
    /usr/bin/ld: warning: a-linker-warning.o: requires executable stack
(because the .note.GNU-stack section is executable)
    COLLECT_GCC_OPTIONS='-v' '-save-temps' '-Werror' '-mtune=generic'
'-march=x86-64' '-dumpdir' 'a.'

For the documentation excerpts, I copied/pasted from
https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html as the docker container
doesn't have `man` installed, but it substantially agrees across the other
versions of GCC I have installed.

Reply via email to