Вопрос

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:

  1. 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)
  2. 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)
  3. 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)
      • Setting DYLD_PRINT_LIBRARIES shows libdyldtest.3.0.dylib being loaded

The 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:

  • Library set to version 0.9, compatibility 0.0
    • 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)
        • Running binary results in Reason: Incompatible library version: binary requires version 1.0.0 or later, but libdyldtest.0.9.dylib, as expected.

Thanks!


Sample code

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.

Другие советы

Take a look at Dynamic Library Programming Topics -> "Managing Client Compatibility With Dependent Libraries" -> "Specifying Version Information". You can set the correct compatiblity using major version and naming.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top