[Bug,general/33103] New: elfutils fails in-tree build due to ./stack binary #included via <stack>

Message ID bug-33103-10460@http.sourceware.org/bugzilla/
State Not applicable
Headers
Series [Bug,general/33103] New: elfutils fails in-tree build due to ./stack binary #included via <stack> |

Commit Message

mark at klomp dot org June 24, 2025, 8:02 a.m. UTC
  https://sourceware.org/bugzilla/show_bug.cgi?id=33103

            Bug ID: 33103
           Summary: elfutils fails in-tree build due to ./stack binary
                    #included via <stack>
           Product: elfutils
           Version: unspecified
            Status: UNCONFIRMED
          Severity: normal
          Priority: P2
         Component: general
          Assignee: unassigned at sourceware dot org
          Reporter: slyich at gmail dot com
                CC: elfutils-devel at sourceware dot org
  Target Milestone: ---

https://sourceware.org/pipermail/elfutils-devel/2024q3/007281.html has a bit of
prior discussion and even a has fix submitted
(https://sourceware.org/git/?p=elfutils.git;a=commitdiff;h=b426c4db31e7c80d4262abdd845d2ece0c9a841c).
Unfortunately the fix still fails in some scenarios: in-tree libc++ builds.

The simplest way to reproduce the build on `gcc` failure is to add `#include
<stack>` into `src/srcfiles.cxx`:


Now we can fail the build on `gcc`:

$ autoreconf -ifv && ./configure --enable-maintainer-mode && make && make check
...
Making all in src
  CXX      srcfiles.o
In file included from srcfiles.cxx:43:
./stack:1:1: error: stray '\177' in program
    1 | <U+007F>ELF<U+0002><U+0001><U+0001><U+0000><

This happens because src/Makefile still contains `-I .`:

    srcdir = .
    ...
    AM_CPPFLAGS = -iquote . -I$(srcdir) ...

As a workaround nixpkgs now uses out-of-tree builds as:

        mkdir build-tree
        cd build-tree
        ../configure ...

But it would be nice to fix the collision for in-tree builds.

For libc++ build failure `<stack>` is included transitively via <iostream>:

In file included from srcfiles.cxx:50:
In file included from
/nix/store/7mk6c0p1jvxm3vq2my5swi5jlidby1h7-libcxx-x86_64-unknown-linux-gnu-19.1.7-dev/include/c++/v1/iostream:43:
In file included from
/nix/store/7mk6c0p1jvxm3vq2my5swi5jlidby1h7-libcxx-x86_64-unknown-linux-gnu-19.1.7-dev/include/c++/v1/istream:1367:
In file included from
/nix/store/7mk6c0p1jvxm3vq2my5swi5jlidby1h7-libcxx-x86_64-unknown-linux-gnu-19.1.7-dev/include/c++/v1/ostream:194:
In file included from
/nix/store/7mk6c0p1jvxm3vq2my5swi5jlidby1h7-libcxx-x86_64-unknown-linux-gnu-19.1.7-dev/include/c++/v1/format:246:
./stack:1:1: error: expected unqualified-id
    1 | <U+007F>ELF<U+0002><U+0001><U+0001>...
  

Comments

mark at klomp dot org June 24, 2025, 10:12 p.m. UTC | #1
https://sourceware.org/bugzilla/show_bug.cgi?id=33103

Mark Wielaard <mark at klomp dot org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mark at klomp dot org

--- Comment #1 from Mark Wielaard <mark at klomp dot org> ---
Do you know why libc++ does this transitive include of stack?
Is this an issue with libc++ for any standard header name because they might be
indirectly included in libc++ headers?
Do you know why this doesn't seem to be a problem when using libstdc++?
  
mark at klomp dot org June 25, 2025, 5:28 a.m. UTC | #2
https://sourceware.org/bugzilla/show_bug.cgi?id=33103

--- Comment #2 from Sergei Trofimovich <slyich at gmail dot com> ---
I don't think c++ standard guarantees exact transitive headers included by
other headers. I think the difference is the implementation detail of libstdc++
vs libc++.

libstdc++'s <format> does not include <stack> (and also does not get enabled
without -std=c++20):
-
https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/std/format;h=46bd5d5ee6a0e72520776ea21610e84ef5c6aca9;hb=HEAD#l45
-
https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libstdc%2B%2B-v3/include/std/ostream;h=3a0a0d35df1d69f620bfbe0e1d94a790904193f2;hb=HEAD#l44

libc++'s <format> does include it (and only does it before -std=c++20):
-
https://github.com/llvm/llvm-project/blob/08b8d467d4253373e77a075c03e25281dee8ad15/libcxx/include/format#L252
-
https://github.com/llvm/llvm-project/blob/08b8d467d4253373e77a075c03e25281dee8ad15/libcxx/include/ostream#L201

Both implementations slowly clean their header structure up going forward.
  
mark at klomp dot org June 25, 2025, 8:59 p.m. UTC | #3
https://sourceware.org/bugzilla/show_bug.cgi?id=33103

--- Comment #4 from Sergei Trofimovich <slyich at gmail dot com> ---
(In reply to Mark Wielaard from comment #3)
> I don't understand why this is only a problem with srcdir == builddir.

AFAIU the difference in in-tree/out-of-tree is the following:

in-tree:

    - builder process is in src/
    - src/ contains freshly built 'stack' binary
    - clang is ran as 'clang++ -iquote . -I . srcfiles.cxx'
    - <stack> is looked up from '-I .' and finds ./stack ELF

out-of-tree:

    - builder process is in build-tree/src/
    - build-tree/src/ contains freshly built 'stack' binary
    - clang is ran as 'clang++ -iquote . -I ../../src srcfiles.cxx'
    - <stack> is looked up from '-I ../../src' and does not find ./stack (as
it's in current ./stack build tree, source tree is clean)

> But maybe we can extend the original solution to use -iquote for srcdir. And
> only have -I for lib and the top builddir (for <config.h>)?
> 
> Does the following work for you?
> 
> diff --git a/config/eu.am b/config/eu.am
> index 5157a34ee552..1361044804f5 100644
> --- a/config/eu.am
> +++ b/config/eu.am
> @@ -31,7 +31,7 @@
>  ##
>  
>  DEFS = -D_GNU_SOURCE -DHAVE_CONFIG_H -DLOCALEDIR='"${localedir}"'
> -AM_CPPFLAGS = -iquote . -I$(srcdir) -I$(top_srcdir)/lib -I..
> +AM_CPPFLAGS = -iquote. -iquote$(srcdir) -I$(top_srcdir)/lib
> -I$(abs_top_builddir)
>  
>  # Drop the 'u' flag that automake adds by default. It is incompatible
>  # with deterministic archives.
> diff --git a/libdw/libdwP.h b/libdw/libdwP.h
> index 25d53d31c7c8..b77db1423fe0 100644
> --- a/libdw/libdwP.h
> +++ b/libdw/libdwP.h
> @@ -32,8 +32,8 @@
>  #include <stdbool.h>
>  #include <pthread.h>
>  
> -#include <libdw.h>
> -#include <dwarf.h>
> +#include "libdw.h"
> +#include "dwarf.h"
>  #include "eu-search.h"

Yes, I confirm that fixes build failure for my in-tree clang setup.
  
mark at klomp dot org June 26, 2025, 1:20 p.m. UTC | #4
https://sourceware.org/bugzilla/show_bug.cgi?id=33103

Mark Wielaard <mark at klomp dot org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |RESOLVED
         Resolution|---                         |FIXED

--- Comment #5 from Mark Wielaard <mark at klomp dot org> ---
(In reply to Sergei Trofimovich from comment #4)
> Yes, I confirm that fixes build failure for my in-tree clang setup.

Thanks for testing, I committed that fix plus some comments to hopefully make
debugging these issues easier next time.

commit 76bd5f6bea9bbeed68b1434455e4904d0fc22b04
Author: Mark Wielaard <mark@klomp.org>
Date:   Thu Jun 26 15:06:07 2025 +0200

    config: Adjust AM_CPPFLAGS for srcdir and .. path includes

    When building with clang and libc++ some standard headers might try
    including <stack> even when no source file requests such include
    directly. When building with srcdir == builddir this might clash in
    the src dir since we then build a stack binary there and the src dir
    also has srcfiles.cxx which might include some standard c++ headers.

    Work around this by removing -I.. from AM_CPPFLAGS and replacing it
    with -I(abs_top_builddir) where the <config.h> file can be found. And
    use -iquote for srcdir so it doesn't get included in the search path
    for the system <header> includes (only for "header" includes).

    Note that DEFAULT_INCLUDES might add . and srcdir back.  So
    DEFAULT_INCLUDES is disabled explicitly in src/Makefile.am (where the
    stack binary is build). We could also use the nostdinc automake option
    to completely suppress that, but that needs more auditing of various
    installed vs not-installed header files.

            * config/eu.am (AM_CPPFLAGS): Use -iquote for $(srcdir) and
            replace -I.. with -I$(abs_top_builddir).
            * libdw/libdwP.h: Include "libdw.h" and "dwarf.h" instead of
            the system headers <libdw.h> and <dwarf.h>.

    https://sourceware.org/bugzilla/show_bug.cgi?id=33103

    Signed-off-by: Mark Wielaard <mark@klomp.org>
  
mark at klomp dot org June 26, 2025, 8:09 p.m. UTC | #5
https://sourceware.org/bugzilla/show_bug.cgi?id=33103

--- Comment #6 from Mark Wielaard <mark at klomp dot org> ---
Note that a followup commit was necessary to fix building one testcase that
broke make check:

commit 65d383a7e653524388ff2ea382be3eac1d04061f (HEAD -> main)
Author: Mark Wielaard <mark@klomp.org>
Date:   Thu Jun 26 21:36:04 2025 +0200

    libdw: Add DEFAULT_INCLUDES to CHECK_DEF_FLAGS

    DEFAULT_INCLUDES includes -I. which is needed for compiling the
    testcases with -DMAIN_CHECK=1.

    This fixes make check after commit 76bd5f6bea9b "config: Adjust
    AM_CPPFLAGS for srcdir and .. path includes"

            * libdw/Makefile.am (CHECK_DEF_FLAGS): Add DEFAULT_INCLUDES.

    Signed-off-by: Mark Wielaard <mark@klomp.org>
  

Patch

--- a/src/srcfiles.cxx
+++ b/src/srcfiles.cxx
@@ -40,6 +40,7 @@ 
 #include <cassert>
 #include <gelf.h>
 #include <memory>
+#include <stack> /* simulate transitive include on libc++ */

 #ifdef ENABLE_LIBDEBUGINFOD
 #include "debuginfod.h"