Handling "dyld: lazy symbol binding failed: Symbol not found" error when nm does not find symbol
Asked Answered
C

3

9

I have a fat (32- and 64-bit) Intel binary called myBinary that fails to run on another workstation running Mac OS X 10.8.2:

$ myBinary
dyld: lazy symbol binding failed: Symbol not found: __ZNSt8__detail15_List_node_base7_M_hookEPS0_
  Referenced from: /usr/local/bin/myBinary
  Expected in: /usr/lib/libstdc++.6.dylib

dyld: Symbol not found: __ZNSt8__detail15_List_node_base7_M_hookEPS0_
  Referenced from: /usr/local/bin/myBinary
  Expected in: /usr/lib/libstdc++.6.dylib

Trace/BPT trap: 5

I compiled it from a Mac OS X 10.8.2 workstation running GCC 4.7.2:

$ gcc --version
gcc (MacPorts gcc47 4.7.2_2+universal) 4.7.2

I ran nm and the symbol is undefined:

$ nm /usr/local/bin/myBinary | grep __ZNSt8__detail15_List_node_base7_M_hookEPS0_
     U __ZNSt8__detail15_List_node_base7_M_hookEPS0_

What did I miss or do wrong when compiling myBinary? I'm not sure what I can do about a missing symbol in /usr/lib/libstdc++.6.dylib — should I have statically compiled the C++ library into myBinary?

Cavy answered 21/3, 2013 at 7:27 Comment(3)
It appears that my answer did not satisfy you. Could you provide more details then? Is another system has GCC 4.7 installed?Norvin
It seems like you must be linking to a non-standard libstdc++. I don't have those symbols in my copy either. It really shouldn't be in /usr/lib in that case though. May I ask why you aren't using clang? Why use gcc from MacPorts? Why not at least use Apple's gcc?Gabrielagabriele
Clang does not support some of the C++11 features we are using. Additionally, I'd like to make sure our tools can be built with GCC compilers, and learn why this is failing and what I can do to resolve this.Cavy
S
2

yeah you have 2 options, either not use libraries that the customer won't have... (you can provide them as a dyld or framework.)

or just statically link the library... this will actually end up being smaller in memory and disk space if your package is only one process, because you can strip symbols that you don't use.

Strunk answered 30/3, 2013 at 5:14 Comment(2)
I used this technique to ensure that I had the same c++ lib on 10.5-8... when you are compiling your own or using a package you will want to make sure that the lib will run on all of the versions you are interested in... which can be problematic if you are supporting 10.5 and 32 bit. I think it is absolutely possible though.Strunk
the concerns about compatibility are the same for shipping a dyld as statically linking.Strunk
N
1

Each C++ compiler has its own implementation of Standard C++ Library. Since you're using external compiler (GCC 4.7) its library is not available among standard installations of Mac OS X.

Your only options is to either bundle the library within your app or statically link it.

To bundle the library with the app:

  1. Update install name of the library using install_name_tool

  2. Ensure your app will find it when needed

E.g. you can put the dylib into .app/Contents/Frameworks, set its install name to @rpath and compile you app with the -rpath @executable_path/../Frameworks.

Norvin answered 23/3, 2013 at 9:44 Comment(1)
Or use the system tool chain and the system C++ library.Thighbone
M
0

I ran into this same issue when compiling on 10.6 with MacPorts GCC 4.8, and then trying to run my application on a fresh 10.9 install without MacPorts. Luckily I found your question, and Kentzo's answer steered me in the right direction as to why the problem occurred... but it didn't really provide the solution I was looking for.

First, I'll explain why it runs on your system correctly: MacPorts has provided your system with the version of libstdc++ that GCC 4.7 is providing symbols for, under /opt/local/lib rather than /usr/lib. Here's what it looks like for me (with GCC 4.8 universal):

$ find /opt/local/lib -name 'libstdc++.*'
/opt/local/lib/gcc48/i386/libstdc++.6.dylib
/opt/local/lib/gcc48/i386/libstdc++.a
/opt/local/lib/gcc48/i386/libstdc++.a-gdb.py
/opt/local/lib/gcc48/i386/libstdc++.dylib
/opt/local/lib/gcc48/i386/libstdc++.la
/opt/local/lib/gcc48/libstdc++.6.dylib
/opt/local/lib/gcc48/libstdc++.a
/opt/local/lib/gcc48/libstdc++.a-gdb.py
/opt/local/lib/gcc48/libstdc++.dylib
/opt/local/lib/gcc48/libstdc++.la
/opt/local/lib/libgcc/libstdc++.6.dylib
/opt/local/lib/libstdc++.6.dylib

And you can see what your application is linking to with otool -L:

$ otool -L myBinary
myBinary:
    /opt/local/lib/libgcc/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.18.0)
    /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 832.0.0)
    /opt/local/lib/libgcc/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)

The easiest way to improve portability is -static-libstdc++ -static-libgcc in your final gcc build step (the one that calls the linker). You need both because dynamic libgcc will bring dynamic libstdc++ binding with it, so it's not enough to simply request static libstdc++. For a simple application, here's what your gcc line might look like:

g++ -static-libstdc++ -static-libgcc myBinary.cpp -o myBinary

However, according to the gcc man page about linker options, statically linking libgcc can cause problems when handling exceptions across libraries. I haven't run into issues with it, but you might.

So, to do it Kentzo's way, first you should get the newest install_name_tool from MacPorts so it won't be confused by unknown load commands:

sudo port install cctools +universal

Now, let's change the path so it searches the executable's directory:

/opt/local/bin/install_name_tool -change /opt/local/lib/libgcc/libstdc++.6.dylib '@executable_path/libstdc++.6.dylib'
/opt/local/bin/install_name_tool -change /opt/local/lib/libgcc/libgcc_s.1.dylib '@executable_path/libgcc_s.1.dylib'

Now you just need to distribute these dylibs with the application. If you're making a .app, copy the dylibs to myBinary.app/Contents/MacOS/.

One final note: If you're having trouble making a good universal binary, it is possible to build the architectures separately (with different compilers and options) and then merge them with lipo:

/usr/bin/g++ -arch i686 -mmacosx-version-min=10.5 -isysroot /Developer/SDKs/MacOSX10.5.sdk myBinary.cpp -o myBinary_32
/opt/local/bin/g++ -arch x86_64 -static-libstdc++ -static-libgcc myBinary.cpp -o myBinary_64
lipo myBinary_32 myBinary_64 -create -output myBinary
Martinmartina answered 8/5, 2014 at 6:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.