Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

llvmPackages_*.libcxx: include libcxxabi within libcxx #307184

Merged
merged 1 commit into from
May 10, 2024

Conversation

pwaller
Copy link
Contributor

@pwaller pwaller commented Apr 27, 2024

Test case: nixpkgs#pkgsStatic.pkgsLLVM.ncurses

Prior to this patch, this fails with errors such as:

error: undefined symbol: __cxa_throw

I think this is a reasonable solution because in #292043, libcxxabi was
'merged into libcxx', however, the commit message suggests that only
dynamic linking was accounted for, because it says:

* linux/freebsd `libc++.so` is a linker script `LINK(libc++.so.1, -lc++abi)` making `-lc++` sufficient.

Whereas, I found that if I tried linking a "hello world" C++ program
with a static hostPlatform, it failed unless -lc++abi was passed.

Signed-off-by: Peter Waller p@pwaller.net

Description of changes

Set -DLIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY=On for hostPlatform.isStatic so that we get the equivalent behaviour is for dynamic linking that -lc++ is enough to pull in all the needed code.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.05 Release Notes (or backporting 23.05 and 23.11 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@pwaller
Copy link
Contributor Author

pwaller commented Apr 27, 2024

I sent this to master rather than staging on the understanding that it should be reasonable to do this for pkgsStatic.pkgsLLVM, which is likely broken without this change anyway.

One thing I've not considered in the above is if there is some other static platform where it is legitimate to combine libc++ with a different ABI other than libc++abi - I guess in that case you actually don't want c++abi included within libc++. If anyone has any thoughts as to whether that could be problematic.

@ghost
Copy link

ghost commented Apr 27, 2024

seems reasonable. see also #305876 (that was my PR as was the libcxx / libcxxabi merge but i deleted my github account).

updated 305876.diff
diff --git a/pkgs/development/compilers/llvm/common/libcxx/default.nix b/pkgs/development/compilers/llvm/common/libcxx/default.nix
index 0e91f50551c5..11d9ced609cf 100644
--- a/pkgs/development/compilers/llvm/common/libcxx/default.nix
+++ b/pkgs/development/compilers/llvm/common/libcxx/default.nix
@@ -126,6 +126,31 @@ stdenv.mkDerivation (rec {
   postInstall = lib.optionalString (cxxabi != null) ''
     lndir ${lib.getDev cxxabi}/include $dev/include/c++/v1
     lndir ${lib.getLib cxxabi}/lib $out/lib
+    libcxxabi=$out/lib/lib${cxxabi.libName}.a
+  ''
+  # LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY=ON doesn't work for LLVM < 16 or
+  # external cxxabi libraries so merge libc++abi.a into libc++.a ourselves.
+
+  # GNU binutils emits objects in LIFO order in MRI scripts so after the merge
+  # the objects are in reversed order so a second MRI script is required so the
+  # objects in the archive are listed in proper order (libc++.a, libc++abi.a)
+  + ''
+    libcxxabi=''${libcxxabi-$out/lib/libc++abi.a}
+    if [[ -f $out/lib/libc++.a && -e $libcxxabi ]]; then
+      $AR -M <<MRI
+        create $out/lib/libc++.a
+        addlib $out/lib/libc++.a
+        addlib $libcxxabi
+        save
+        end
+    MRI
+      $AR -M <<MRI
+        create $out/lib/libc++.a
+        addlib $out/lib/libc++.a
+        save
+        end
+    MRI
+    fi
   '';
 
   passthru = {
diff --git a/pkgs/test/cc-wrapper/default.nix b/pkgs/test/cc-wrapper/default.nix
index a0088751d4a2..f0da2dbddeb8 100644
--- a/pkgs/test/cc-wrapper/default.nix
+++ b/pkgs/test/cc-wrapper/default.nix
@@ -46,6 +46,16 @@ in stdenv.mkDerivation {
       $READELF -d ./atomics.so | grep libatomic.so && echo "ok" >&2 || echo "failed" >&2
     ''}
 
+    ${lib.optionalString isCxx ''
+      echo "checking whether can link with libc++... " >&2
+      $CXX ${./cxx-main.cc} -c -o cxx-main.o
+      $CC cxx-main.o -lc++ -o cxx-main
+      $CC cxx-main.o ${lib.getLib stdenv.cc.libcxx}/lib/libc++.a -o cxx-main-static
+      ${emulator} ./cxx-main
+      ${emulator} ./cxx-main-static
+      rm cxx-main{,-static,.o}
+    ''}
+
     ${lib.optionalString (stdenv.isDarwin && stdenv.cc.isClang) ''
       echo "checking whether compiler can build with CoreFoundation.framework... " >&2
       mkdir -p foo/lib

@pwaller
Copy link
Contributor Author

pwaller commented Apr 27, 2024

Thanks for the reference to the closed PR, I had missed that and the additional context. I would like to see this fix landed so I'll leave this open for comments; I take it that this won't fix things for older LLVMs. Advice welcomed.

I'd be interested if the community might also accept this since it fixes things more recent toolchains at least, even if it doesn't fix all of them (which could be fixed at a later date).

I've pulled across the test you wrote for #305876.

@pwaller
Copy link
Contributor Author

pwaller commented Apr 27, 2024

/cc potentially interested reviewers from the other thread:
@Ericson2314 @rrbutani @RossComputerGuy @Mindavi

Copy link
Contributor

@rrbutani rrbutani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good, thank you!

Just one nit/question:

pkgs/development/compilers/llvm/common/libcxx/default.nix Outdated Show resolved Hide resolved
@ghost
Copy link

ghost commented Apr 27, 2024

@ofborg build tests.cc-wrapper

@pwaller
Copy link
Contributor Author

pwaller commented Apr 28, 2024

@ofborg build tests.cc-wrapper

@pwaller pwaller changed the title libcxx: hostPlatform.isStatic: include libcxxabi within libcxx llvmPackages_*.libcxx: include libcxxabi within libcxx Apr 28, 2024
@pwaller
Copy link
Contributor Author

pwaller commented Apr 28, 2024

I think I've accounted for everything that was known (and mentioned in the other thread #305876).

Most recent update:

  • For versions before 16, merge the two archive files with ar.
  • Fix the cc-wrapper tests.
  • Update PR title / commit message.
  • Do the merging differently if cxxabi != null.
  • Move the LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY flag from cxxabiCMakeFlags to cxxCMakeFlags. It relates to the ABI but it is a libcxx flag.

Copy link
Member

@sternenseemann sternenseemann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an extra comment to the two conditionals (!lib.versionAtLeast release_version "16" and lib.versionAtLeast release_version "16") noting that the other versions have a different way of achieving the same, so that readers of the code will be able to connect the dots?

@pwaller
Copy link
Contributor Author

pwaller commented Apr 28, 2024

@sternenseemann wrote:

Can you add an extra comment to the two conditionals

Done. Latest patch also removes touch $out/lib/workaround-present from postInstall which I never intended to push.

@ofborg ofborg bot requested a review from sternenseemann April 28, 2024 17:06
Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will now need need to target staging.

to support cxxabi != null and all LLVM versions my preference is to use the diff embedded in #307184 (comment) since that is the minimal change. I don't see the value in using both LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY and ar given the extra complexity and the fact that the ar script is 4 lines long.

also note that using LIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY prepends libc++abi.a to libc++.a which is the reverse order of what the shared libs do in the linker script. it probably doesn't really matter except if there are objects that in libc++abi and libc++ which contain the same symbol.

nonetheless, some minor comments inline.

pkgs/development/compilers/llvm/common/libcxx/default.nix Outdated Show resolved Hide resolved
pkgs/development/compilers/llvm/common/libcxx/default.nix Outdated Show resolved Hide resolved
@pwaller pwaller changed the base branch from master to staging April 29, 2024 19:18
@pwaller
Copy link
Contributor Author

pwaller commented Apr 29, 2024

I've reinstated @annaleeleaves's diff for generating the AR and retested on x86_64-linux. I wasn't able to get a freebsd test to work.

@pwaller
Copy link
Contributor Author

pwaller commented Apr 29, 2024

Also retargeted staging.

@ofborg ofborg bot requested a review from sternenseemann April 29, 2024 19:41
@ghost
Copy link

ghost commented Apr 30, 2024

@pwaller thanks for working on this but unfortunately the change still is not correct.
nix-build -A libcxx && ar t result/lib/libc++.a shows duplicate objects in libc++.a

suggested changes
diff --git a/pkgs/development/compilers/llvm/common/libcxx/default.nix b/pkgs/development/compilers/llvm/common/libcxx/default.nix
index 8cad3067ca6d..5b4c2ca56ae1 100644
--- a/pkgs/development/compilers/llvm/common/libcxx/default.nix
+++ b/pkgs/development/compilers/llvm/common/libcxx/default.nix
@@ -67,12 +67,12 @@ let
 
   cxxCMakeFlags = [
     "-DLIBCXX_CXX_ABI=${cxxabiName}"
-  ] ++ lib.optionals (cxxabi == null && (lib.versionAtLeast release_version "16")) [
+  ] ++ lib.optionals (cxxabi == null && lib.versionAtLeast release_version "16") [
     # Note: llvm < 16 doesn't support this flag (or it's broken); handled in postInstall instead.
     # Include libc++abi symbols within libc++.a for static linking libc++;
     # dynamic linking includes them through libc++.so being a linker script
     # which includes both shared objects.
-    "-DLIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY=On"
+    "-DLIBCXX_STATICALLY_LINK_ABI_IN_STATIC_LIBRARY=ON"
   ] ++ lib.optionals (cxxabi != null) [
     "-DLIBCXX_CXX_ABI_INCLUDE_PATHS=${lib.getDev cxxabi}/include"
   ] ++ lib.optionals (stdenv.hostPlatform.isMusl || stdenv.hostPlatform.isWasi) [
@@ -129,9 +129,7 @@ stdenv.mkDerivation (rec {
   # libc++.so is a linker script which expands to multiple libraries,
   # libc++.so.1 and libc++abi.so or the external cxxabi. ld-wrapper doesn't
   # support linker scripts so the external cxxabi needs to be symlinked in
-  postInstall = ''
-    libcxxabi=$out/lib/libc++abi.a
-  '' + lib.optionalString (cxxabi != null) ''
+  postInstall = lib.optionalString (cxxabi != null) ''
     lndir ${lib.getDev cxxabi}/include $dev/include/c++/v1
     lndir ${lib.getLib cxxabi}/lib $out/lib
     libcxxabi=$out/lib/lib${cxxabi.libName}.a
@@ -142,7 +140,7 @@ stdenv.mkDerivation (rec {
   # GNU binutils emits objects in LIFO order in MRI scripts so after the merge
   # the objects are in reversed order so a second MRI script is required so the
   # objects in the archive are listed in proper order (libc++.a, libc++abi.a)
-  + ''
+  + lib.optionalString (cxxabi != null || lib.versionOlder release_version "16") ''
     libcxxabi=''${libcxxabi-$out/lib/libc++abi.a}
     if [[ -f $out/lib/libc++.a && -e $libcxxabi ]]; then
       $AR -M <<MRI

Key test case: nixpkgs#pkgsStatic.pkgsLLVM.ncurses

Prior to this patch, this fails with errors such as:

```
error: undefined symbol: __cxa_throw
```

I think this is a reasonable solution because in NixOS#292043, libcxxabi was
'merged into libcxx', however, the commit message suggests that only
dynamic linking was accounted for, because it says:

```
* linux/freebsd `libc++.so` is a linker script `LINK(libc++.so.1, -lc++abi)` making `-lc++` sufficient.
```

Whereas, I found that if I tried linking a "hello world" C++ program
with a static hostPlatform, it failed unless -lc++abi was passed.

Signed-off-by: Peter Waller <p@pwaller.net>
@pwaller
Copy link
Contributor Author

pwaller commented May 3, 2024

Thanks @annaleeleaves - Note that I didn't get a notification for your edit which added suggested changes, so I was unaware of it until now.

Also ambiguous to me is whether the suggestions address your comment that the symbols are duplicated. I had a quick look for duplicated symbols with nm building pkgsStatic.pkgsLLVM.llvmPackages_17.libcxx, but I don't see them. If the problem is still present please would you mind sharing what test I can use as to whether it's right?

@pwaller
Copy link
Contributor Author

pwaller commented May 3, 2024

Forgot to mention in the above comment: I believe I've applied your suggestions.

@ghost
Copy link

ghost commented May 3, 2024

Also ambiguous to me is whether the suggestions address your comment that the symbols are duplicated. I had a quick look for duplicated symbols with nm building pkgsStatic.pkgsLLVM.llvmPackages_17.libcxx, but I don't see them. If the problem is still present please would you mind sharing what test I can use as to whether it's right?

it is fixed with the suggested changes. i listed a test in #307184 (comment) nix-build -A libcxx && ar t result/lib/libc++.a. ar t result/lib/libc++.a lists the objects in the archive. before this change the objects from libc++abi.a were added twice for llvm16+, with the change only once. nm also shows the duplicates. before this change there were 8024 lines listed for llvm17 libc++.a, after this change there are 6364.

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

change looks good. my preference is still the diff in #307184 (comment) but this works too.

tested linux x64 nix-build -A tests.cc-wrapper and also tested the changes work when libcxxrt is used via slight modifications to libcxxrt and libcxx.

also tested nix-build -A tests.cc-wrapper.llvmTests on darwin x64 with minor changes to skip llvm16 so to not require building stdenv.

@ghost
Copy link

ghost commented May 3, 2024

@ofborg build tests.cc-wrapper.llvmTests
(failures are timeouts)

Copy link
Contributor

@rrbutani rrbutani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@annaleeleaves, @pwaller: Thanks for your work on this.

@ghost
Copy link

ghost commented May 8, 2024

@wegank
would be good to get this in for 24.05 #303285 as there are only one or two staging cycles left. don't think this would be considered a breaking change as it fixes a bug introduced in #292043 (which removed -lc++abi from cc-wrapper for clang++ and thus changed the behaviour for linking static libs).

this change not only rectifies that bug but also fixes long standing bug clang -x c++ -lc++ hello.cxx for static linking ( #292043 fixed it for shared libs).

@pwaller
Copy link
Contributor Author

pwaller commented May 10, 2024

I don't have commit rights, please can someone merge?

Thanks for the review input, all!

@alyssais alyssais merged commit 3c03811 into NixOS:staging May 10, 2024
27 checks passed
@rrbutani rrbutani added the 6.topic: llvm/clang Issues related to llvmPackages, clangStdenv and related label May 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants