I'm attempting to version a shared library under OS X (10.7.5), but I am having trouble getting dyld
to recognize a maximum compatibility version (minimum versions are okay). Consider the test code below and the following scenario:
Binary linked against library at version 1.0, compatibility version 1.0
binary
works as expected
otool -L binary
: libdyldtest.dylib (compatibility version 1.0.0, current version 1.0.0)
otool -L libdyldtest.dylib
: libdyldtest.dylib (compatibility version 1.0.0, current version 1.0.0)
Library is updated and recompiled, moved to version 2.0, compatibility version 1.0
binary
should still work, and does, since compatibility of library is old enough
otool -L binary
: libdyldtest.dylib (compatibility version 1.0.0, current version 1.0.0)
otool -L libdyldtest.dylib
: libdyldtest.dylib (compatibility version 1.0.0, current version 2.0.0)
Library is updated and recompiled to an incompatible version. Set both version and compatibility version to 3.0. Binary is not recompiled.
binary
should stop working because it wants something compatible with 1.0 but libdyldtest
is only compatible with 3.0 and later. Why does this work?
otool -L binary
: libdyldtest.dylib (compatibility version 1.0.0, current version 1.0.0)
otool -L libdyldtest.dylib
: libdyldtest.dylib (compatibility version 3.0.0, current version 3.0.0)
DYLD_PRINT_LIBRARIES
shows libdyldtest.3.0.dylib
being loadedThe problem is that 3 above works, and I don't think it should. Is this expected behavior? If not, how can I correct this?
NOTE: The relevant dyld source code for 10.7.5 doesn't seem to make use of the maxVersion
member of the LibraryInfo
struct, only minVersion
. Thus, setting the minimum compatibility version lower works as expected:
binary
stops working, as expected.
otool -L binary
: libdyldtest.dylib (compatibility version 1.0.0, current version 1.0.0)
otool -L libdyldtest.dylib
: libdyldtest.dylib (compatibility version 0.0.0, current version 0.9.0)
binary
results in Reason: Incompatible library version: binary requires version 1.0.0 or later, but libdyldtest.0.9.dylib
, as expected.Thanks!
library.h:
#ifndef __LIBRARY_H__
#define __LIBRARY_H__
void functionFromLibrary();
#endif /* __LIBRARY_H__ */
library.c:
#include "library.h"
#include <stdio.h>
void
functionFromLibrary()
{
printf("functionFromLibrary()\n");
}
binary.c:
#include "library.h"
int
main(
int argc,
char *argv[])
{
functionFromLibrary();
return (0);
}
Makefile:
.PHONY: all library binary
MAJOR=1
MINOR=0
COMPAT=1.0
LIBNAME=dyldtest
BINNAME=binary
all: library binary
binary: binary.o
$(CC) $< -L. -l$(LIBNAME) -o $(BINNAME)
library: library.o
$(CC) -dynamiclib $< -Wl,-current_version,$(MAJOR).$(MINOR) \
-Wl,-compatibility_version,$(COMPAT) -Wl,-macosx_version_min,10.6 \
-Wl,-install_name,lib$(LIBNAME).dylib \
-o lib$(LIBNAME).$(MAJOR).$(MINOR).dylib
ln -f -s lib$(LIBNAME).$(MAJOR).$(MINOR).dylib \
lib$(LIBNAME).$(MAJOR).dylib
ln -f -s lib$(LIBNAME).$(MAJOR).dylib lib$(LIBNAME).dylib
clean:
$(RM) *.o lib$(LIBNAME)*.dylib $(BINNAME)
This is expected behavior.
The version number checks that dyld
performs are limited to ensuring that the compatibility version of the library being loaded is higher than the compatibility version of the library that was used at build time. The current version of the library is something that's programmatically accessible, but isn't used by dyld
when evaluating whether it should load a particular library. You can learn more about these two version numbers by looking at the -compatibility_version
and -current_version
sections of the ld
man page.
You can achieve the effect you're after by making use of the library's install name. You can see this by looking at how libSystem.dylib
is used:
mrowe@angara:~$ ls -lha /usr/lib/libSystem.{,B.}dylib
-rwxr-xr-x 1 root wheel 53K Jul 9 2012 /usr/lib/libSystem.B.dylib
lrwxr-xr-x 1 root wheel 17B Jul 9 2012 /usr/lib/libSystem.dylib -> libSystem.B.dylib
mrowe@angara:~$ otool -L /usr/lib/libSystem.dylib | head -2
/usr/lib/libSystem.dylib:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 169.3.0)
mrowe@angara:~$
Note how the install name in the second line of otool
output points to the version of the dylib whose name is qualified with the version number (in the case B). If Apple were to introduce a backwards-incompatible revision of libSystem.dylib
they could put it at /usr/lib/libSystem.C.dylib
and update the symlink at libSystem.dylib
to point it. Existing programs would still look for libSystem.B.dylib
since that's the install name that was written in to their LC_LOAD_DYLIB
load command back when they were linked. Any program that was newly linked against libSystem.dylib
would find libSystem.C.dylib
and have its install name written in to their LC_LOAD_DYLIB
load command. Such a program would fail to launch on a system which lacked libSystem.C.dylib
.