KoblentsBlog Photography
Contact About
Ches
dyld: lazy symbol binding failed
Well, I had a really good one today. Thankfully, I only wasted about an hour.
Here's the outline: I have a userspace driver that does some relatively generic things that I compiled as a dynamic library (let's call it libmydev_ctrl.dylib). On top of that, I have a device-specific userspace driver for specific functionality that I compiled as a dynamic library (let's call it libmydev_ctrl_spec.dylib). On top of that, I have binaries that use device-specific drivers (let's call it myexecutable).
Compiling is fairly straightforward. First, the "generic" dylib:
12345678910
DYLIB_ALL_NAME          := libmydev_ctrl.dylib
TARGET_DYLIB_ALL        := $(TARGET_DIR)/$(DYLIB_ALL_NAME)
INST_DYLIB_ALL          := $(INSTALL_DIR)/$(DYLIB_ALL_NAME)

$(TARGET_DYLIB_ALL): $(OBJECTS_ALL) | $(TARGET_DIR)
        @printf "%-16s %-8s %s\n" "link" "LLD" $(notdir $@)

        @$(COMPILER) -o $@                                      \
                -install_name $(INST_DYLIB_ALL) -dynamiclib     \
                $^ $(FLAGS) $(INCLUDES) $(LIBRARIES)
And then we do something very similar to compile the device-specific dylib; as you can see, the only real difference in the makefile is that the "generic" dylib is a pre-requisite of the "specific" dylib:
12345678910
DYLIB_PLAT_NAME         := libmydev_ctrl_$(TARGET_NAME).dylib
TARGET_DYLIB_PLAT       := $(TARGET_DIR)/$(DYLIB_PLAT_NAME)
INST_DYLIB_PLAT         := $(INSTALL_DIR)/$(DYLIB_PLAT_NAME)

$(TARGET_DYLIB_PLAT): $(OBJECTS_PLAT) $(TARGET_DYLIB_ALL) | $(TARGET_DIR)
        @printf "%-16s %-8s %s\n" "link" "LLD" $(notdir $@)

        @$(COMPILER) -o $@                                      \
                -install_name $(INST_DYLIB_PLAT) -dynamiclib    \
                $^ $(FLAGS) $(INCLUDES) $(LIBRARIES)
Finally, we compile the binary/executable; we make the "generic" and "specific" dylibs pre-requisites, along with the source file with the main function:
1234
$(TARGET): $(TARGET_DYLIB_ALL) $(TARGET_DYLIB_PLAT) $(SRC)
        @printf "%-16s %-8s %s\n" "compile/link" "C++/LLD" $(notdir $@)

        @$(COMPILER) -o $@ $^ $(FLAGS) $(INCLUDES) $(LIBRARIES)
If you're trying to follow along without make, the commands look like this:
1234567891011121314151617181920
c++ -o libmydev_ctrl.dylib							\
	-install_name /usr/local/opt/libmydev_ctrl.dylib -dynamiclib		\
	source1.o source2.o source3.o						\
	-Wall -Werror -Wextra -Wno-unused-variable				\
	-Wno-unused-parameter -Wno-unused-function				\
	-Wno-unused-private-field						\
	-std=c++1y -g

c++ -o libmydev_ctrl_spec.dylib							\
	-install_name /usr/local/opt/libmydev_ctrl_spec.dylib -dynamiclib	\
	spec_source.o libmydev_ctrl.dylib					\
	-Wall -Werror -Wextra -Wno-unused-variable				\
	-Wno-unused-parameter -Wno-unused-function				\
	-Wno-unused-private-field						\
	-std=c++1y -g


c++ -o myexecutable libmydev_ctrl.dylib libmydev_ctrl_spec.dylib dostuff.cpp	\
	-Wall -Werror -Wextra -Wno-unused-variable -Wno-unused-parameter	\
	-Wno-unused-function -Wno-unused-private-field -std=c++1y -g
And then I run it:
1
$ ./myexecutable
But what falls out? Not good things. Solution after the jump:
1234567
dyld: lazy symbol binding failed: Symbol not found: __Z4init8dev_type
  Referenced from: myexecutable
  Expected in: /usr/local/opt/libmydev_ctrl.dylib

dyld: Symbol not found: __Z4init8dev_type
  Referenced from: myexecutable
  Expected in: /usr/local/opt/libmydev_ctrl.dylib
But this makes no sense, because
__Z4init8dev_type
does exist, except it's in libmydev_ctrl_spec.dylib.
Easy to check that it was linked properly:
123456
$ otool -L myexecutable
myexecutable:
	/usr/local/opt/libmydev_ctrl.dylib (compatibility version 0.0.0, current version 0.0.0)
	/usr/local/opt/libmydev_ctrl_spec.dylib (compatibility version 0.0.0, current version 0.0.0)
	/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)
And of course using
ls
shows that the files definitely exist. Next, we use
nm
:
12345678
$ nm -gU /usr/local/opt/libmydev_ctrl_spec.dylib 
00000000000016a0 T __Z10read_adc_vh
00000000000016e0 T __Z10set_dac_mvht
...snip...
0000000000000b40 T __Z4init8dev_type
...snip....
00000000000018d0 T __Z9set_dac_vhd
0000000000001020 T __ZN12PlatformCtrl8is_validEv
And of course it does not exist in libmydev_ctrl.dylib because that's not where it belongs.
So the next thing I tried was to change the compile order and the same thing happened, just in the other direction - it became clear that it was successfully looking into the first dylib but not the second.
Here's the solution:
The files libmydev_ctrl.dylib and libmydev_ctrl_spec.dylib are named similarly, so based on the standard dylib way of finding libraries, the "name.dylib" and "name_xxx.dylib" pattern makes it look like the second dylib is another version of the first; that is, they're the same library, and if one works there's no need to look further.
The answer is then obvious: just change the names a bit. EG, libmydev_ctrl.dylib and libmydev_spec_ctrl.dylib. That was easy.
Ches Koblents
April 16, 2018
 
« Newer Older »
© Copyright Koblents.com, 2012-2024