0

We are experiencing Apple WatchOS build failures on Travis for a C++ library. Other configurations, like iPhone, iPhoneSimulator and AppleTV are OK.

The library compiles OK but it fails to link. A typical compile invocation is:

clang++ -DNDEBUG -g2 -O3 -fPIC -pipe -Wall -arch armv7 -stdlib=libc++ -isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.1.sdk \
-c cryptlib.cpp

clang++ -DNDEBUG -g2 -O3 -fPIC -pipe -Wall -arch armv7 -stdlib=libc++ -isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.1.sdk \
-c cpu.cpp

clang++ -DNDEBUG -g2 -O3 -fPIC -pipe -Wall -arch armv7 -stdlib=libc++ -isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.1.sdk \
-c integer.cpp

...

After compile there are lots of linker errors as shown below. We are not sure if these are false positives or if we missed a necessary option or library.

I've tried searching Apple Developer but I'm having trouble finding information specific to Watch builds. I found some similar questions like Undefined symbol compiling boost context for iphone but they are unanswered (and the wrong platform).

I have an older MacBook for testing, but it runs OS X 10.9 and has Xcode 6. Its too old to try and work this problem. For example, it does not have WatchOS and can't decode the missing symbol name:

$ echo __Unwind_SjLj_Register | c++filt
__Unwind_SjLj_Register

Are the linking errors expected when testing this configuration? If not, then is the architecture correct? Maybe, which option or libraries are we missing for Watch and TV?


I found this recently in Clang source code, ToolChain.cpp:

bool Darwin::UseSjLjExceptions(const ArgList &Args) const {
   // Darwin uses SjLj exceptions on ARM.
   if (getTriple().getArch() != llvm::Triple::arm &&
       getTriple().getArch() != llvm::Triple::thumb)
     return false;

   // Only watchOS uses the new DWARF/Compact unwinding method.
   llvm::Triple Triple(ComputeLLVMTriple(Args));
   return !Triple.isWatchABI();
 }

Here's what link looks like. We drive it through the compiler, and we use the same CXXFLAGS.

clang++ -o cryptest.exe -DNDEBUG -g2 -O3 -fPIC -pipe -Wall -arch armv7 -isysroot \
/Applications/Xcode.app/Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs/WatchOS3.1.sdk \
-stdlib=libc++ adhoc.o test.o bench1.o bench2.o validat0.o validat1.o validat2.o validat3.o \
 datatest.o regtest1.o regtest2.o regtest3.o fipsalgt.o dlltest.o ./libcryptopp.a 

Undefined symbols for architecture armv7:

  "__Unwind_SjLj_Register", referenced from:
      CryptoPP::CipherModeFinalTemplate_CipherHolder<CryptoPP::BlockCipherFinal<(CryptoPP::CipherDir)0, CryptoPP::Rijndael::Enc>, CryptoPP::ConcretePolicyHolder<CryptoPP::Empty, CryptoPP::AdditiveCipherTemplate<CryptoPP::AbstractPolicyHolder<CryptoPP::AdditiveCipherAbstractPolicy, CryptoPP::OFB_ModePolicy> >, CryptoPP::AdditiveCipherAbstractPolicy> >::~CipherModeFinalTemplate_CipherHolder() in test.o    
      CryptoPP::Test::scoped_main(int, char**) in test.o    
      std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > CryptoPP::IntToString<long>(long, unsigned int) in test.o    
      std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::operator>><char, std::__1::char_traits<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&, char*) in test.o    
      std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::ws<char, std::__1::char_traits<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&) in test.o    
      CryptoPP::Test::GenerateRSAKey(unsigned int, char const*, char const*, char const*) in test.o    
      CryptoPP::Test::RSASignFile(char const*, char const*, char const*) in test.o    
      ...

  "__Unwind_SjLj_Resume", referenced from:
      CryptoPP::CipherModeFinalTemplate_CipherHolder<CryptoPP::BlockCipherFinal<(CryptoPP::CipherDir)0, CryptoPP::Rijndael::Enc>, CryptoPP::ConcretePolicyHolder<CryptoPP::Empty, CryptoPP::AdditiveCipherTemplate<CryptoPP::AbstractPolicyHolder<CryptoPP::AdditiveCipherAbstractPolicy, CryptoPP::OFB_ModePolicy> >, CryptoPP::AdditiveCipherAbstractPolicy> >::~CipherModeFinalTemplate_CipherHolder() in test.o   
      CryptoPP::Test::scoped_main(int, char**) in test.o
      std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > CryptoPP::IntToString<long>(long, unsigned int) in test.o    
      std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::operator>><char, std::__1::char_traits<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&, char*) in test.o    
      std::__1::basic_istream<char, std::__1::char_traits<char> >& std::__1::ws<char, std::__1::char_traits<char> >(std::__1::basic_istream<char, std::__1::char_traits<char> >&) in test.o    
      CryptoPP::Test::GenerateRSAKey(unsigned int, char const*, char const*, char const*) in test.o    
      CryptoPP::Test::RSASignFile(char const*, char const*, char const*) in test.o    
      ...
jww
  • 97,681
  • 90
  • 411
  • 885
  • You noticed that `__Unwind_SjLj_Register` is an unmangled symbol? How was the library that's supposed to contain it compiled? Is that some startup code module? – user0042 Aug 14 '17 at 09:41
  • @user0042 - `__Unwind_SjLj_Register` looks mangled to me, but I could be wrong. The library being compiled is shown above. Its my [testing clone](https://github.com/noloader/cryptopp) of Wei Dai's [Crypto++](https://cryptopp.com/). – jww Aug 14 '17 at 09:49
  • @user0042 - Part of the pain is, I don't have an interactive shell to look around and test things. I have to surmise offline, and then check-in to see if it works. Its a crummy way to develop. – jww Aug 14 '17 at 09:52
  • The symbol sounds like something from a `startup.o` module (exceptions/stack unwinding). That's usually part of the standard library. Some issue with a compiler flag regarding exception behavior of the code? Sorry, I can also just guess about that here. I feel your pain. – user0042 Aug 14 '17 at 09:56

1 Answers1

-1

I think this may have been fixed by LLVM in September 2017 at Fix SJLJ exception handling when manually chosen on a platform where it isn't default.

I also recently came across this from a patch, which may fix the issue. cfe/docs/ClangCommandLineReference.rst:

.. option:: -fsjlj-exceptions

Use SjLj style exceptions
jww
  • 97,681
  • 90
  • 411
  • 885