Tuesday, January 4, 2011

OS X Library basics

This is an elementary post about libraries on OS X. As in other areas, I'm just getting started with this, so if you spot an error, please let me know. The primary purpose is to orient myself to help in troubleshooting when a software install at the command line runs into a problem.

If we have our code split up into a bunch of different files, compiled separately, we can link them into a single executable as discussed in a previous post (here), which uses the make tool.

It also useful to define libraries that may contain a number of different modules which are already pre-compiled and linked. A primary distinction is between static libraries, which are copied into the target application, and dynamic libraries which are not.

We'll start with the static version in this post. The example is adapted from Dalrymple & Hillegass (Advanced Mac OS X Programming) which is still a valuable resource even though it's a bit dated. It looks like there may be a new edition later this year. Here is a simple file add1.c with a nice function:

#include <stdio.h>

int f1(int x)
{
printf( "f1: %d;", x );
return x+1;
}

A similar function f2 is defined in add2.c We do the following:

gcc -g -Wall -c add*.c

(-g adds debugging symbols, -Wall shows all warnings). The result is familiar: two object files add1.o and add2.o.

These functions are used by your program, defined in useadd.c:

#include <stdio.h>
extern int f1(int x);
extern int f2(int x);

int main(int argc, char** argv){
printf("  main %d\n", f1(1));
printf("  main %d\n", f2(10));
return 0;
}

Rather than referring to a header file for the definitions of f1 and f2, we make a declaration of extern. Now the book says to do:

ar crl libadd.a add*.o
gcc -g -Wall -o useadd useadd.c -L. -ladd

with the flags to ar:

c create if needed
r replace / add
l next token is name of file to generate

and flags passed through gcc to ld are a search path and file description as described here.

It works:

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


The ar tool is Unix archive utility, which has for the most part been superseded by tar. Here is what the output file looks like for a run of ar on two simple text files. I think you can guess what they contain:

!<arch>
file1.txt       1294193315  501   20    100644  9         `
my data1

file2.txt       1294193320  501   20    100644  9         `
my data2

Now, Apple suggests that you use libtool:

libtool -static add*.o -o libadd.a

[UPDATE: The docs (here) say that everything must be compiled as well as linked with -static for this to work]

Also, for this example, you don't need the search paths, but could just do:

$ gcc -g -Wall -o useadd useadd.c libadd.a


It's important to list the library after useadd.c, otherwise the linker will discard the library symbols as unneeded. We can explore the symbols defined in our library (or any object file for that matter):

$ nm libadd.a 

libadd.a(add1.o):
00000000 T _f1
U _printf

libadd.a(add2.o):
00000000 T _f2
U _printf

The function names are preceded by an underscore. The U means that printf is as yet undefined, and the T stands for (Text)---not sure what that refers to.

$ gcc -g -Wall -c useadd.c
$ nm useadd.o
U _f1
U _f2
00000000 T _main
U _printf

Here we see that in useadd.o the functions f1 and f2 are as yet undefined. Another very interesting thing is to set the environment variable:

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

We can watch as the libraries are loaded. There are a bunch of other things here.