[MacPorts] #62426: libc++: using a newer libc++ to build software on older macos systems

MacPorts noreply at macports.org
Mon Sep 26 03:31:12 UTC 2022


#62426: libc++: using a newer libc++ to build software on older macos systems
-------------------------------------+--------------------
  Reporter:  kencu                   |      Owner:  kencu
      Type:  enhancement             |     Status:  closed
  Priority:  Normal                  |  Milestone:
 Component:  ports                   |    Version:
Resolution:  fixed                   |   Keywords:
      Port:  libcxx macports-libcxx  |
-------------------------------------+--------------------

Comment (by kencu):

 OK, I have an example for you where mixing libc++ versions causes
 unexpected issues, I believe. This comes from an example I was looking
 over on another package manager.

 Take this small {{{test.cxx}}} program, that looks for a nonexistent file
 and should generate an error if it is not found:

 {{{
 #include "llvm/Support/Errc.h"
 #include "clang/Basic/FileManager.h"

 #include <iostream>

 int main() {
   clang::FileManager fileMgr((clang::FileSystemOptions()));

   auto file = fileMgr.getFileRef("./nonexistant.h", /*OpenFile=*/true);

   std::error_code EC = llvm::errorToErrorCode(file.takeError());

   std::cout << "EC.message is " << EC.message() << "\n";

   bool not_no_such = (EC != llvm::errc::no_such_file_or_directory);
   bool is_no_such = !not_no_such;

   std::cout << "EC == no_such_file_or_directory is " << is_no_such <<
 "\n";

   return 0;
 }
 }}}

 compile it like this (I used clang-14 for this, on Monterey):
 {{{
 clang++-mp-14  -c test.cxx -I/opt/local/libexec/llvm-14/include -std=c++17
 -stdlib=libc++ -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS
 -D__STDC_LIMIT_MACROS
 }}}

 and now link it like this:
 {{{
 clang++-mp-14 test.o -o test1 -Wl,-rpath,/opt/local/libexec/llvm-14/lib
 -L/opt/local/libexec/llvm-14/lib -Wl,-search_paths_first
 -Wl,-headerpad_max_install_names -lclangFrontend -lclangSerialization
 -lclangDriver -lclangCodeGen -lclangParse -lclangSema -lclangAnalysis
 -lclangEdit -lclangASTMatchers -lclangAST -lclangLex -lclangBasic -lLLVM
 }}}

 check the link, and you will see it is linked against the new
 libc++.1.dylib from llvm14:
 {{{
 % otool -L test1
 test1:
         @rpath/libLLVM.dylib (compatibility version 1.0.0, current version
 14.0.6)
         /opt/local/libexec/llvm-14/lib/libc++.1.dylib (compatibility
 version 1.0.0, current version 1.0.0)
         /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current
 version 1311.100.3)
 }}}
 now run it, and you get this unexpected output:
 {{{
 % ls *.h
 zsh: no matches found: *.h
 % ./test1
 EC.message is No such file or directory
 EC == no_such_file_or_directory is 0

 % touch nonexistant.h
 cunningh at MacBookPro-2012 ~ % ./test1
 EC.message is Undefined error: 0
 EC == no_such_file_or_directory is 0
 }}}

 so you can see that is is not working properly.

 Now, let's build it again, this time linking against
 /usr/lib/libc++.1.dylib. We'll just manually change it to keep things
 simple:
 {{{
 clang++-mp-14 test.o -o test2 -Wl,-rpath,/opt/local/libexec/llvm-14/lib
 -L/opt/local/libexec/llvm-14/lib -Wl,-search_paths_first
 -Wl,-headerpad_max_install_names -lclangFrontend -lclangSerialization
 -lclangDriver -lclangCodeGen -lclangParse -lclangSema -lclangAnalysis
 -lclangEdit -lclangASTMatchers -lclangAST -lclangLex -lclangBasic -lLLVM

 install_name_tool -change /opt/local/libexec/llvm-14/lib/libc++.1.dylib
 /usr/lib/libc++.1.dylib test2
 }}}
 and the linkage is as we expect:
 {{{
  % otool -L test2
 test2:
         @rpath/libLLVM.dylib (compatibility version 1.0.0, current version
 14.0.6)
         /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current
 version 1.0.0)
         /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current
 version 1311.100.3)
 }}}
 and it works properly:
 {{{
 % ls *.h
 zsh: no matches found: *.h
 % ./test2
 EC.message is No such file or directory
 EC == no_such_file_or_directory is 1
 % touch nonexistant.h
 % ./test2
 EC.message is Undefined error: 0
 EC == no_such_file_or_directory is 0
 }}}


 The reason this happens is because
 {{{/opt/local/libexec/llvm-14/lib/libLLVM.dylib}}} is linked against
 {{{/usr/lib/libc++.dylib}}}:
 {{{
 % otool -L /opt/local/libexec/llvm-14/lib/libLLVM.dylib
 /opt/local/libexec/llvm-14/lib/libLLVM.dylib:
         @rpath/libLLVM.dylib (compatibility version 1.0.0, current version
 14.0.6)
         /opt/local/lib/libffi.8.dylib (compatibility version 10.0.0,
 current version 10.0.0)
         /opt/local/lib/libedit.0.dylib (compatibility version 1.0.0,
 current version 1.68.0)
         /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current
 version 1311.0.0)
         /opt/local/lib/libz.1.dylib (compatibility version 1.0.0, current
 version 1.2.12)
         /opt/local/lib/libncurses.6.dylib (compatibility version 6.0.0,
 current version 6.0.0)
         /opt/local/lib/libxml2.2.dylib (compatibility version 12.0.0,
 current version 12.14.0)
         /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current
 version 1200.3.0)
 }}}

 and if {{{test}}} is linked against a different libc++.dylib, then bad
 things can happen.

 Just to really prove it, let's leave {{{test1}}} linked against the new
 libc++, but we'll change
 {{{/opt/local/libexec/llvm-14/lib/libLLVM.dylib}}} to link against the new
 one too. If this theory is right, test1 should now work properly, and
 test2 will be broken:

 {{{
 sudo install_name_tool -change /usr/lib/libc++.1.dylib
 /opt/local/libexec/llvm-14/lib/libc++.1.dylib
 /opt/local/libexec/llvm-14/lib/libLLVM.dylib
 }}}

 {{{
 % ls *.h
 zsh: no matches found: *.h
 % ./test1
 EC.message is No such file or directory
 EC == no_such_file_or_directory is 1
 % ./test2
 EC.message is No such file or directory
 EC == no_such_file_or_directory is 0
 % touch nonexistant.h
 % ./test1
 EC.message is Undefined error: 0
 EC == no_such_file_or_directory is 0
 % ./test2
 EC.message is Undefined error: 0
 EC == no_such_file_or_directory is 0
 }}}

 so the message here is that when {{{test.cxx}}} is linked against the same
 {{{libc++.1.dylib}}} that
 {{{/opt/local/libexec/llvm-14/lib/libLLVM.dylib}}} is linked against, all
 is well.

 If they are linked against different versions of {{{libc++.1.dylib}}} ===>
 BOOM. You get garbage.

 Now I have to change the linkage in libLLVM.dylib back to how it was
 originally, before I forget :>

 {{{
 % sudo install_name_tool -change
 /opt/local/libexec/llvm-14/lib/libc++.1.dylib /usr/lib/libc++.1.dylib
 /opt/local/libexec/llvm-14/lib/libLLVM.dylib
 }}}

-- 
Ticket URL: <https://trac.macports.org/ticket/62426#comment:64>
MacPorts <https://www.macports.org/>
Ports system for macOS


More information about the macports-tickets mailing list