Tuesday, January 4, 2011

OS X Library basics 2

Here is a second example, of a dynamic library (adapted from a Stack Overflow answer here). The same .c files are used from the first example (here).

step 1, create libadd.dylib



$ gcc -g -Wall -c add*.c

$ file add1.o
add1.o: Mach-O 64-bit object x86_64

$ gcc -dynamiclib -current_version 1.0 add*.o -o libadd.dylib

$ file libadd.dylib
libadd.dylib: Mach-O 64-bit dynamically linked shared library x86_64

$ otool -L libadd.dylib
libadd.dylib:
libadd.dylib (compatibility version 0.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.1)

Find out more about otool by doing

man otool | col -b > result.txt

col helps to format the output from man properly. I learned we can do this:

$ otool -tvV "libadd.a(add1.o)"
libadd.a(add1.o):
(__TEXT,__text) section
_f1:
00000000 pushl %ebp
00000001 movl %esp,%ebp
00000003 pushl %ebx
00000004 subl $0x14,%esp
00000007 calll 0x0000000c
0000000c popl %ebx
0000000d movl 0x08(%ebp),%eax
00000010 movl %eax,0x04(%esp)
00000014 leal 0x2fd-0xc(%ebx),%eax
0000001a movl %eax,(%esp)
0000001d calll _printf+0x100000000
00000022 movl 0x08(%ebp),%eax
00000025 incl %eax
00000026 addl $0x14,%esp
00000029 popl %ebx
0000002a leave
0000002b ret

I don't speak assembly, but I can get the general idea.
Step 2, create useadd


$ gcc -c useadd.c

$ gcc -v useadd.o ./libadd.dylib -o useadd
Using built-in specs.
Target: i686-apple-darwin10
Configured with: /var/tmp/gcc/gcc-5664~89/src/configure --disable-checking --enable-werror --prefix=/usr --mandir=/usr/share/man --enable-languages=c,objc,c++,obj-c++ --program-transform-name=/^[cg][^.-]*$/s/$/-4.2/ --with-slibdir=/usr/lib --build=i686-apple-darwin10 --with-gxx-include-dir=/usr/include/c++/4.2.1 --host=i686-apple-darwin10 --target=i686-apple-darwin10
Thread model: posix
gcc version 4.2.1 (Apple Inc. build 5664)
/usr/libexec/gcc/i686-apple-darwin10/4.2.1/collect2 -dynamic -arch i386 -macosx_version_min 10.6.5 -weak_reference_mismatches non-weak -o useadd -lcrt1.10.6.o -L/usr/lib/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../../i686-apple-darwin10/4.2.1 -L/usr/lib/gcc/i686-apple-darwin10/4.2.1/../../.. useadd.o ./libadd.dylib -lSystem -lgcc -lSystem

$ nm -gpv useadd
0000202c D _NXArgc
00002030 D _NXArgv
00002038 D ___progname
00001000 A __mh_execute_header
00002034 D _environ
00001f02 T _main
00001ec4 T start
U _exit
U _f1
U _f2
U _printf
U dyld_stub_binder

$ ./useadd
f1: 1; main 2
f2: 10; main 12

Step 3. Use an environment variable to give output when libraries are loaded:

$ export DYLD_PRINT_LIBRARIES=1

$ ./useadd
dyld: loaded: /Users/telliott_admin/Desktop/add/./useadd
dyld: loaded: /Users/telliott_admin/Desktop/add/libadd.dylib
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib
f1: 1; main 2
f2: 10; main 12

$ mv libadd.dylib /tmp
dyld: loaded: /bin/mv
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib

$ export DYLD_LIBRARY_PATH=/tmp

$ ./useadd
dyld: loaded: /Users/telliott_admin/Desktop/add/./useadd
dyld: loaded: /tmp/libadd.dylib
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib
f1: 1; main 2
f2: 10; main 12

$ unset DYLD_LIBRARY_PATH

Notice in the second run of ./useadd, we loaded /tmp/libadd.dylib.
Finally, let's snoop on Python:

$ python
dyld: loaded: /usr/bin/python
dyld: loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /usr/lib/libauto.dylib
dyld: loaded: /usr/lib/libicucore.A.dylib
dyld: loaded: /usr/lib/libobjc.A.dylib
dyld: loaded: /usr/lib/libz.1.dylib
dyld: loaded: /usr/lib/libstdc++.6.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib
dyld: loaded: /System/Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python
dyld: loaded: /System/Library/Frameworks/Python.framework/Versions/2.6/Python
dyld: loaded: /usr/lib/libSystem.B.dylib
dyld: loaded: /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation
dyld: loaded: /usr/lib/libauto.dylib
dyld: loaded: /usr/lib/libicucore.A.dylib
dyld: loaded: /usr/lib/libobjc.A.dylib
dyld: loaded: /usr/lib/libz.1.dylib
dyld: loaded: /usr/lib/libstdc++.6.dylib
dyld: loaded: /usr/lib/system/libmathCommon.A.dylib
Python 2.6.1 (r261:67515, Jun 24 2010, 21:47:49)
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
dyld: loaded: /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload/readline.so
dyld: loaded: /usr/lib/libedit.2.dylib
dyld: loaded: /usr/lib/libncurses.5.4.dylib
>>> import string
dyld: loaded: /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload/strop.so
>>> import sys
>>> import math
dyld: loaded: /System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload/math.so
>>>
[1]+ Stopped python

$ unset DYLD_PRINT_LIBRARIES

We can see that some .so libraries can be loaded. However, there are some fundamental differences between the Unix method of shared object .so libraries and the OS X / Mach model of bundles and frameworks.
That's for the future!