NixOS / nixpkgs

Nix Packages collection & NixOS
MIT License
18.3k stars 14.27k forks source link

clang++ should not statically link with the standard C++ library by default #192665

Open Atry opened 2 years ago

Atry commented 2 years ago

Describe the bug

clang++ should not statically link with the standard C++ library by default

Steps To Reproduce

Steps to reproduce the behavior:

$ nix-shell -p clang
these 6 paths will be fetched (105.93 MiB download, 901.65 MiB unpacked):
  /nix/store/032vsv2sjnyvacbzbqrcbalfdy4f99y6-clang-11.1.0
  /nix/store/7slpywh97cc5my5ifgv9g860f0p53v0f-clang-wrapper-11.1.0
  /nix/store/gmwhy341givkxp6jngalbf9479j3f5k9-compiler-rt-libc-11.1.0-dev
  /nix/store/npr6bcwpijhgg9vsra9kh2krmd9myqma-compiler-rt-libc-11.1.0
  /nix/store/sxpdk3y4m46lv6mq1zrf9yr7zg1lg92x-clang-11.1.0-lib
  /nix/store/ysy4jg12jhzh1cw9qiiqxfmry0dvqz5z-llvm-11.1.0-lib
copying path '/nix/store/npr6bcwpijhgg9vsra9kh2krmd9myqma-compiler-rt-libc-11.1.0' from 'https://cache.nixos.org'...
copying path '/nix/store/ysy4jg12jhzh1cw9qiiqxfmry0dvqz5z-llvm-11.1.0-lib' from 'https://cache.nixos.org'...
copying path '/nix/store/gmwhy341givkxp6jngalbf9479j3f5k9-compiler-rt-libc-11.1.0-dev' from 'https://cache.nixos.org'...
copying path '/nix/store/sxpdk3y4m46lv6mq1zrf9yr7zg1lg92x-clang-11.1.0-lib' from 'https://cache.nixos.org'...
copying path '/nix/store/032vsv2sjnyvacbzbqrcbalfdy4f99y6-clang-11.1.0' from 'https://cache.nixos.org'...
copying path '/nix/store/7slpywh97cc5my5ifgv9g860f0p53v0f-clang-wrapper-11.1.0' from 'https://cache.nixos.org'...

[nix-shell:/workspaces/hhvm]$ echo '
  #include <iostream>
  int main(){std::cout<<"Hello, World!"<<std::endl;}
' > hello.cpp &&
clang++ hello.cpp &&
objdump -T a.out

a.out:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000  w   D  *UND*  0000000000000000  Base        _ITM_addUserCommitAction
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __strtof_l
0000000000000000  w   D  *UND*  0000000000000000  Base        _ITM_memcpyRtWn
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fileno
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __strcoll_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __nl_langinfo_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) dgettext
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_GetRegionStart
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fseeko64
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wmemcpy
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) memset
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) mbrtowc
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_SetGR
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wcslen
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) close
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __duplocale
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_GetDataRelBase
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) ioctl
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) abort
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) memchr
0000000000000000  w   D  *UND*  0000000000000000  Base        __gmon_start__
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) bindtextdomain
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wmemcmp
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.3)  __strftime_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) setvbuf
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __strxfrm_l
0000000000000000  w   D  *UND*  0000000000000000  Base        _ITM_RU1
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) mbsnrtowcs
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) read
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) strncmp
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) malloc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) gettext
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.3)  strtold_l
0000000000000000      DO *UND*  0000000000000000 (GLIBC_2.32) __libc_single_threaded
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) ungetwc
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_DeleteException
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __wctype_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __cxa_atexit
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.34) pthread_once
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __towupper_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __wcsxfrm_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) iconv_open
0000000000000000  w   D  *UND*  0000000000000000  Base        _ZGTtdlPv
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_GetLanguageSpecificData
0000000000000000      DF *UND*  0000000000000000 (GCC_3.3)    _Unwind_Resume_or_Rethrow
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) ungetc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __wcscoll_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fputc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) free
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) strlen
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wmemchr
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_RaiseException
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __ctype_get_mb_cur_max
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wctob
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.3)  __wcsftime_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __iswctype_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.3)  __tls_get_addr
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) sprintf
0000000000000000      DO *UND*  0000000000000000 (GLIBC_2.2.5) stdin
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fdopen
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) syscall
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) iconv
0000000000000000  w   D  *UND*  0000000000000000  Base        _ITM_RU8
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __newlocale
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) poll
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) strerror
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.33) fstat64
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fputs
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) mbsrtowcs
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wcrtomb
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) putwc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) putc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) memmove
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) vsnprintf
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fread
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wmemmove
0000000000000000  w   D  *UND*  0000000000000000  Base        _ITM_memcpyRnWt
0000000000000000      DF *UND*  0000000000000000 (GCC_4.2.0)  _Unwind_GetIPInfo
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __errno_location
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) strdup
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.3)  __uselocale
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) strcmp
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __strtod_l
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) getwc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) memcmp
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) writev
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fclose
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) lseek64
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_GetTextRelBase
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __freelocale
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) bind_textdomain_codeset
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wcsnrtombs
0000000000000000      DO *UND*  0000000000000000 (GLIBC_2.2.5) stderr
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) btowc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fopen64
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wcscmp
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fwrite
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) pthread_mutex_lock
0000000000000000  w   D  *UND*  0000000000000000  Base        _ZGTtnam
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) realloc
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) setlocale
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.34) __libc_start_main
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) write
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_Resume
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) ftello64
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) pthread_mutex_unlock
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.14) memcpy
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) open
0000000000000000      DF *UND*  0000000000000000 (GCC_3.0)    _Unwind_SetIP
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) iconv_close
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __towlower_l
0000000000000000      DO *UND*  0000000000000000 (GLIBC_2.2.5) stdout
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) wmemset
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) fflush
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) getc

Expected behavior

clang++ should dynamically link with the standard C++ library by default. For example, on Debian the program is linked with GLIBCXX_3.4 by default:

$ echo '
  #include <iostream>
  int main(){std::cout<<"Hello, World!"<<std::endl;}
' > hello.cpp &&
clang++ hello.cpp &&
objdump -T a.out

a.out:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __cxa_atexit
0000000000000000      DF *UND*  0000000000000000  GLIBCXX_3.4 _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
0000000000000000      DF *UND*  0000000000000000  GLIBCXX_3.4 _ZNSolsEPFRSoS_E
0000000000000000      DF *UND*  0000000000000000  GLIBCXX_3.4 _ZNSt8ios_base4InitC1Ev
0000000000000000      DF *UND*  0000000000000000  GLIBC_2.2.5 __libc_start_main
0000000000000000  w   D  *UND*  0000000000000000              __gmon_start__
0000000000401030      DF *UND*  0000000000000000  GLIBCXX_3.4 _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
0000000000404080 g    DO .bss   0000000000000110  GLIBCXX_3.4 _ZSt4cout
0000000000401080      DF *UND*  0000000000000000  GLIBCXX_3.4 _ZNSt8ios_base4InitD1Ev

Additional context

nix-support/cc-ldflags in the clang-wrapper now includes a non-existing path:

$ cat /nix/store/7slpywh97cc5my5ifgv9g860f0p53v0f-clang-wrapper-11.1.0/nix-support/cc-ldflags 
-L/nix/store/l4dvshb0bw0ipyx17f6rknzk83mdx81y-gcc-11.3.0/lib/gcc/x86_64-unknown-linux-gnu/11.3.0 -L/nix/store/4v2bk6almk03mfnz4122dfz8vcxynvs3-gcc-11.3.0-lib/x86_64-unknown-linux-gnu/lib  -L/nix/store/sxpdk3y4m46lv6mq1zrf9yr7zg1lg92x-clang-11.1.0-lib/lib 
$ ls /nix/store/4v2bk6almk03mfnz4122dfz8vcxynvs3-gcc-11.3.0-lib/x86_64-unknown-linux-gnu/lib
ls: cannot access '/nix/store/4v2bk6almk03mfnz4122dfz8vcxynvs3-gcc-11.3.0-lib/x86_64-unknown-linux-gnu/lib': No such file or directory

As a result, clang++ cannot find libstdc++.so from search path.

As a workaround, you can add gcc-unwrapped.lib to library search path:

$ nix-shell -p gcc-unwrapped.lib -p clang

[nix-shell:/tmp/sas]$ echo '
  #include <iostream>
  int main(){std::cout<<"Hello, World!"<<std::endl;}
' > hello.cpp &&
clang++ hello.cpp &&
objdump -T a.out

a.out:     file format elf64-x86-64

DYNAMIC SYMBOL TABLE:
0000000000000000      DF *UND*  0000000000000000 (GLIBCXX_3.4) _ZNSo3putEc
0000000000000000      DF *UND*  0000000000000000 (GLIBCXX_3.4) _ZNSo5flushEv
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.34) __libc_start_main
0000000000000000      DF *UND*  0000000000000000 (GLIBC_2.2.5) __cxa_atexit
0000000000000000      DF *UND*  0000000000000000 (GLIBCXX_3.4.9) _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
0000000000000000      DF *UND*  0000000000000000 (GLIBCXX_3.4.11) _ZNKSt5ctypeIcE13_M_widen_initEv
0000000000000000      DF *UND*  0000000000000000 (GLIBCXX_3.4) _ZSt16__throw_bad_castv
0000000000000000      DO *UND*  0000000000000000 (GLIBCXX_3.4) _ZSt4cout
0000000000000000      DF *UND*  0000000000000000 (GLIBCXX_3.4) _ZNSt8ios_base4InitC1Ev
0000000000000000  w   D  *UND*  0000000000000000  Base        __gmon_start__
0000000000000000      DF *UND*  0000000000000000 (GLIBCXX_3.4) _ZNSt8ios_base4InitD1Ev

Now the program is dynamically linked with the standard C++ library

Notify maintainers

@primeos @dtzWill @7c6f434c @lovek323

Metadata

Please run nix-shell -p nix-info --run "nix-info -m" and paste the result.

$ nix-shell -p nix-info --run "nix-info -m"
this path will be fetched (0.00 MiB download, 0.00 MiB unpacked):
  /nix/store/nc0g25p24z5jj4jm509wfczg6r2xxk9m-nix-info
copying path '/nix/store/nc0g25p24z5jj4jm509wfczg6r2xxk9m-nix-info' from 'https://cache.nixos.org'...
 - system: `"x86_64-linux"`
 - host os: `Linux 5.4.0-1090-azure, Debian GNU/Linux, 11 (bullseye), nobuild`
 - multi-user?: `no`
 - sandbox: `no`
 - version: `nix-env (Nix) 2.11.1`
 - channels(code): `"nixpkgs"`
 - nixpkgs: `/home/code/.nix-defexpr/channels/nixpkgs`
danielbarter commented 2 years ago

confirmed. nix-support/cc-ldflags is being set incorrectly somewhere in https://github.com/NixOS/nixpkgs/tree/master/pkgs/build-support/cc-wrapper. I've been digging around a lot in there recently and would be happy to fix it, unless you want to take a crack!

danielbarter commented 2 years ago

this should do the trick: https://github.com/NixOS/nixpkgs/pull/193355. I am a little worried about cross compilation, but will check that out.

I need to build it locally, so should know by the end of the day

danielbarter commented 2 years ago

Here is the section of the builder that are moving the sanitizer and C++ standard library shared objects into place:

https://github.com/NixOS/nixpkgs/blob/52392d42c156db5b889db7f3cc3e9909e4259b2a/pkgs/development/compilers/gcc/builder.sh#L210

It might be the case that this should be fixed in GCC rather than the compiler wrapper. I don't have a good enough understanding of the cross compilation infrastructure to make that call.

danielbarter commented 2 years ago

So this does seem like an issue with where the native gcc is moving these shared objects. Here is an environment which uses clang to cross compile for 64 bit arm:

{ nixpkgs ? <nixpkgs>
, pkgs ? (import nixpkgs {}).pkgsCross.aarch64-multiplatform
}:

( pkgs.mkShell.override { stdenv = pkgs.llvmPackages_latest.stdenv;} )
{}

In this environment, we have

$ cat cc-ldflags
-L/nix/store/2ywip1q8w21m0rxx7vhdjawy902vqsdf-aarch64-unknown-linux-gnu-stage-final-gcc-debug-9.3.0/lib/gcc/aarch64-unknown-linux-gnu/9.3.0 
-L/nix/store/b0qsfcpsppz6wkipjh8wzncnnwxyg6ib-aarch64-unknown-linux-gnu-stage-final-gcc-debug-9.3.0-lib/aarch64-unknown-linux-gnu/lib  
-L/nix/store/awzsfm1gkh9cwrynjl0z98ydxjhm3kzg-clang-13.0.1-lib/aarch64-unknown-linux-gnu/lib

and that second path is populated with the expected shared library files. In this case, that third path is missing and /nix/store/awzsfm1gkh9cwrynjl0z98ydxjhm3kzg-clang-13.0.1-lib/lib is filled with x86-64 files.

This example is kind of contrived, since you probably are going to be using static libs for cross compiling

Also, for this environment, the C++ standard library headers are missing, so it can't compile programs using the C++ standard lib anyway, but that is another issue.