How to get this basic C++ build to work in a nix-shell?

I recently posted a question related to a problem I am having with running a bigger build system in nix-shell (link), but I think the question was too specific and not very approachable. So I built a small, very basic C++ build (two g++ invocations) that reproduces the problem.

Here it is, I would like to be able to run this script inside a nix-shell:

#!/usr/bin/env bash
set -u
set -o pipefail

cat > tmp.cpp << EOF
#include <iostream>
void test_tmp() {
  std::cout << "test_tmp\n";
}
EOF

g++ -o shared.so -shared -fPIC -x c++ tmp.cpp - << EOF
#include <iostream>
void test() {
  std::cout << "test\n";
}
EOF

if [ $? -eq 0 ]
then
  echo "shared worked!"
else
  echo "shared FAILED!"
fi

g++ -o static -static -x c++ - << EOF
#include <iostream>
int main() {
  std::cout << "Hello World!\n";
}
EOF

if [ $? -eq 0 ]
then
  echo "static worked!"
else
  echo "static FAILED!"
fi

In summary, this script links two source files into a shared library shared.so, and then compiles a simple “Hello World”-program into a static binary static.

Sanity check, to make sure this issue is actually related to nix/nixos:

$ docker run --rm -ti -v "$(pwd)":/test ubuntu:20.04 bash -c "apt update && apt install g++ && cd /test && ./build.sh"
[...snipped apt output...]
shared worked!
static worked!

So this small script runs fine on a non-nix(os) linux.

Ok, now to the actual problem, and the different shell.nixs I have tried to make it work.

Attempt #1, a basic shell.nix:

{ pkgs ? import <nixpkgs> {} }:
  pkgs.mkShell {
    nativeBuildInputs = with pkgs; [
      gcc
    ];
}

With result:

$ nix-shell --pure
[nix-shell]$ ./build.sh
shared worked!
/nix/store/vfqlryhvm8063hs7ax9k2vb8wmch5v0v-binutils-2.31.1/bin/ld: cannot find -lm
/nix/store/vfqlryhvm8063hs7ax9k2vb8wmch5v0v-binutils-2.31.1/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
static FAILED!

Ok, so no static libraries available by default, but there is glibc.static, which provides them.
Attempt #2:

{ pkgs ? import <nixpkgs> {} }:
  pkgs.mkShell {
    nativeBuildInputs = with pkgs; [
      gcc
      glibc.static
    ];
}

And output:

$ nix-shell --pure
[nix-shell]$ ./build.sh 
/nix/store/vfqlryhvm8063hs7ax9k2vb8wmch5v0v-binutils-2.31.1/bin/ld: /nix/store/zxcqmz6sah8h9qqy4w7kknlbkd2m6d0p-glibc-2.31-74-static/lib/libc.a(dl-trampoline.o): relocation R_X86_64_PC32 against symbol `_dl_x86_cpu_features' can not be used when making a shared object; recompile with -fPIC
/nix/store/vfqlryhvm8063hs7ax9k2vb8wmch5v0v-binutils-2.31.1/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
shared FAILED!
static worked!

So with glibc.static, the static build works, but the shared build fails.

Maybe glibc.static overrides the shared glibc, and this is not available anymore? If so, how about including both?

Attempt #3:

{ pkgs ? import <nixpkgs> {} }:
  pkgs.mkShell {
    nativeBuildInputs = with pkgs; [
      gcc
      glibc.static
      glibc
    ];
}

And output:

$ nix-shell --pure
[nix-shell]$ ./build.sh 
In file included from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ext/string_conversions.h:41,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/basic_string.h:6493,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/string:55,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/locale_classes.h:40,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/ios_base.h:41,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ios:42,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ostream:38,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/iostream:39,
                 from tmp.cpp:1:
/nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
   75 | #include_next <stdlib.h>
      |               ^~~~~~~~~~
compilation terminated.
In file included from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ext/string_conversions.h:41,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/basic_string.h:6493,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/string:55,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/locale_classes.h:40,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/ios_base.h:41,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ios:42,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ostream:38,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/iostream:39,
                 from <stdin>:1:
/nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
   75 | #include_next <stdlib.h>
      |               ^~~~~~~~~~
compilation terminated.
shared FAILED!
In file included from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ext/string_conversions.h:41,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/basic_string.h:6493,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/string:55,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/locale_classes.h:40,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/bits/ios_base.h:41,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ios:42,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/ostream:38,
                 from /nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/iostream:39,
                 from <stdin>:1:
/nix/store/fvf3qjqa5qpcjjkq37pb6ypnk1mzhf5h-gcc-9.3.0/include/c++/9.3.0/cstdlib:75:15: fatal error: stdlib.h: No such file or directory
   75 | #include_next <stdlib.h>
      |               ^~~~~~~~~~
compilation terminated.
static FAILED!

Boom. Now both fail, and with pretty evil errors. Googling them turns up this related issue: ardour: fix build with gcc6 by disassembler · Pull Request #28748 · NixOS/nixpkgs · GitHub
The fix for that issue was to remove glibc from the buildInputs. However, this means that I’m back to the stage where either only shared linking or only static linking works (Attempt #1 or #2, depending on whether glibc.static is added to the buildInputs).

At this point I’m rather lost, and don’t really know how to continue, and neither do I know much about nix’ internals and how, i.e., the gcc-wrapper works. What am I doing wrong/am I missing/could I try?

1 Like

nativeBuilInputs is for buildtime tools such as gcc, buildInputs is for runtime dependencies such as glibc

Adding

    buildInputs = with pkgs; [
      glibc.static
    ];

should fix your problem.

2 Likes

Thanks, I had missed trying out buildInputs! Unfortunately, whether I add glibc.static to the nativeBuildInputs or the buildInputs doesn’t seem to make a difference, and I get the exact same behavior (the shared linking step now fails because only a static glibc is available).

I tried this shell.nix (attempt #4):

{ pkgs ? import <nixpkgs> {} }:
  pkgs.mkShell {
    nativeBuildInputs = with pkgs; [
      gcc
    ];
    buildInputs = with pkgs; [
      glibc.static
    ];
}

And output:

$ nix-shell --pure
[nix-shell]$ ./build.sh 
/nix/store/vfqlryhvm8063hs7ax9k2vb8wmch5v0v-binutils-2.31.1/bin/ld: /nix/store/zxcqmz6sah8h9qqy4w7kknlbkd2m6d0p-glibc-2.31-74-static/lib/libc.a(dl-trampoline.o): relocation R_X86_64_PC32 against symbol `_dl_x86_cpu_features' can not be used when making a shared object; recompile with -fPIC
/nix/store/vfqlryhvm8063hs7ax9k2vb8wmch5v0v-binutils-2.31.1/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
shared FAILED!
static worked!

I also tried variations of this, i.e., leaving the gcc dependency out, adding it to buildInputs instead of nativeBuildInputs, trying nativeBuildInputs = [ glibc.static ] and buildInputs = [ gcc ], but they all result in the same result/output as above.

Also, as long as I add glibc to either the nativeBuildInputs or the buildInputs, the same “cannot find stdlib-headers”-bug from attempt #3 occurs.

Is this what you meant or am I missing something? Thanks!

@vcunat We ran into a problem that seems to have the same solution that you implemented in chntpw: fix #11474, build with multiple-output glibc · NixOS/nixpkgs@d130116 · GitHub

or @dtzWill (Merge pull request #34645 from dtzWill/feature/musl · NixOS/nixpkgs@a267ae4 · GitHub)

Changing glibc into glibc.out seems to avoid certain -isystem setting logic in

Is there a rule or logic behind using glibc.out vs glibc or glibc.dev?

Possibly related: stdenv: principial linking problems when putting static and dynamic libraries into different directories · Issue #12085 · NixOS/nixpkgs · GitHub

1 Like