Main menu:

Site search

 

November 2017
M T W T F S S
« Oct    
 12345
6789101112
13141516171819
20212223242526
27282930  

Categories

Archives

Links:

Shared/Dynamic libraries in OS X

A while ago I struggled with the problem of building a shared library (also known as dynamic library), let it link to a GUI build on top of it and let it be part of a clickable application. There doesn’t seems to be much information on the Internet, so here’s my story on how I achieved this using a very simple example.

The completed application

The final application will be a very simple GUI converter where you enter an angle in degrees (instead of the more usual radian) and let it calculate the sine and cosine of that angle. Calculating the sine and cosine will be done by code in a minimal mathematical shared library (note that the sin and cos functions provided by the C math library expects input in radian not in degrees, thats why I make my own small mathematical library).

sharedLibExampleFinishedApplication.png

My Mathematical library

Well, as you can guess, the code is straightforward. For the CoCoa project, we’ll have to keep the header file so that we can distribute it together with our library. The actual source code doesn’t have to be present in your project since you’re going to link against the library.
So, here’s the header file:

//
//  myMathLibrary.h
//  sharedLibsExample
//
//  Created by Koenraad Van Nieuwenhove on 06/12/08.
//  Copyright 2008 CoCoa Crumbs. All rights reserved.
//

float sine(float angleInDegrees);
float cosine(float angleInDegrees);

And here’s the implementation:

//
//  myMathLibrary.c
//  sharedLibsExample
//
//  Created by Koenraad Van Nieuwenhove on 06/12/08.
//  Copyright 2008 CoCoa Crumbs. All rights reserved.
//
#include "myMathLibrary.h"
#include <math.h>

float convertToRadian(float angleInDegrees)
{
    return (angleInDegrees*3.14159265)/180;
} /* end convertToRadian */

float sine(float angleInDegrees)
{
    return sin(convertToRadian(angleInDegrees));
} /* end sine */

float cosine(float angleInDegrees)
{
    return cos(convertToRadian(angleInDegrees));
} /* end cosine */

Building the library

Instead of using the Xcode IDE, I simply compile and build the library from the command line in a terminal (as is often the case when you would compile an open source library from e.g. MacPorts). To do this, I placed the following commands in a [bash] shell script. Lets call this script buildSharedLibrary.sh To execute the bash shell script you’ll need to open a terminal, navigate to the directory containing the source code and the shell script. You then run the shell script like this:
. ./buildSharedLibrary.sh
(Note that the dots really need to be there!!!)

    1 gcc -arch i386 -arch ppc -c myMathLibrary.c
    2
    3 gcc -arch i386 -arch ppc \
    4     -dynamiclib \
    5     -o myMathLibrary.dylib \
    6     myMathLibrary.o \
    7     -install_name @executable_path/myLibs/myMathLibrary.dylib
    8 

Line 1 simply compiles and assembles the myMathLibrary.c file into an object file. Because I used the -c command line option it stops there and it doesn’t try to link (if this command line option wasn’t there you would see an error complaining that the main function is missing). So next to our source code files, you’ll now find a new file named myMathLibrary.o
The -arch i386 and -arch ppc command line options are there to tell the compiler to build for both the Intel and PowerPC architectures. The GCC tool chain is smart enough to mix both architectures in one object file. If you want to build for PowerPC only then you can drop the -arch 386 switch of course.

Lines 3 to 7 are actually one line but I split it over several lines (using the \ character) for better readability. Lets go over it line by line.
Line 3 just says again that the GCC tool chain has to do its magic for both Intel and PowerPC architectures.
Line 4 instructs the GCC tool chain that we want a dynamic library (confusingly also [and maybe better] known as a shared library).
Line 5 tells the GCC tool chain that the resulting output filename has to be myMathLibrary.dylib
Line 6 is our object file that resulted from the compilation on line 1. For this simple example I only have one object file. However, you can specify plenty of object files here. All of them will then end up in the shared library.
Line 7 was the tricky one and the main reason why I write this blog entry. We’ll see why this is needed later.

So, if everything went well, you should now have your shared library ready to be used in your application.

The Xcode project

sharedLibExampleProject.png

As can be seen in the above screenshot, the project doesn’t look that much different compared to what you are used to. Instead of the myMathLibrary.c source code you add your compiled library to the project. Compiling and linking your application will go fine.

Running the application

When you try to run the application, you will be unpleasantly surprised by this:

sharedLibExampleRunningError.png

Clearly, the application had trouble to find our library. The reason for this is quite simple. We build the library ourselves without Xcode, so we need to instruct Xcode what to do with it, which in this case means copying the library to a suitable place so that the application can find it. If we would have stayed with our source code file, Xcode would have compiled it and taken care that the resulting object code ends up in the application. Which leaves us with the question, where do we want to copy our library to?

For this small application I prefer to put the shared library inside the application itself (another option could have been to put it somewhere in the frameworks directory). If you control click on the application icon, you can see it’s contents as shown below.

sharedLibExampleShowPackage.png

sharedLibExamplePackage01.png

As you might already know, an application is nothing more than a directory with an application icon and with a different behavior when you double click on it.
As you can see, the raw executable is located in the path /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/.
We can now use Xcode to copy our library inside the application bundle by simply adding a new build phase. So we go back to our Xcode project and click on the Targets line where we see our sharedLibsExample. Which we double click also. Now we see the 3 build phases which Xcode already made for itself.

sharedLibExampleAddBuildPhase.png

So, we now simply add a new Copy Files Build Phase:

sharedLibExampleAddBuildPhaseMyLibs.png

By default, the Destination popup will show Resources. I selected Executables for this project. Secondly, I added an [optional] path, name myLibs.
What’s going to happen now, is that, when Xcode fires this build phase, it will copy all the files which I dragged into this build phase into a folder called myLibs which will be next to my executable (that’s why I selected the Executables option from the popup menu, if I stayed with the default, the folder would have been created in the Resources folder of the bundle instead).
The only thing that remains is dragging my library to this build phase:

sharedLibExampleAddMyLib.png

When you now build the application, Xcode will create a myLibs directory next to the executable and copy our shared library in that directory.
Try running the application this time and you’ll see it now runs as expected.

But why this install_name option?

Remember that line 7 that I didn’t explain yet? Well, rebuild our library without the -install_name @executable_path/myLibs/myMathLibrary.dylib part and run it again. Strange enough, we get a very similar error.

sharedLibExampleRunningAgainError.png

If we look inside the application bundle, we see that the library is actually there. So what went wrong this time?

sharedLibExamplePackage02.png

As it turns out, our library needs to specify some extra info for the linker. Basically it needs to tell the linker where it can be found by the main application. It’s the application who loads the shared library in RAM so that it can be executed. For this, the application needs to know where it can search for this library. To make matters a bit more difficult, our application can be located everywhere on your hard disk, so specifying a fixed path at build time is impossible. Let’s take a look again to line 7:

    7     -install_name @executable_path/myLibs/myMathLibrary.dylib

On that line we see the mention of @executable_path/ At runtime, this part will be translated to the path from root up to the executable inside the application bundle. In my case this would be /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/ To that path, we then add myLibs/myMathLibrary.dylib, so the resulting path is /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/myLibs/myMathLibrary.dylib which is exactly the full path name where my library is located. If I would move my application to another place, the @executable_path/ takes care that the path is adapted accordingly.
When I now double click on my application icon, the application will search for my library (to be loaded in RAM) at /Users/koen/Desktop/sharedLibsExample.app/Contents/MacOS/myLibs/. Since I used @executable_path/myLibs/myMathLibrary.dylib when I build my library and Xcode copied my library in the myLibs directory, the application will find it there, load it RAM and execute my code.

Problem solved.

A sample project can be downloaded here: sharedlibsexample

Write a comment

You need to login to post comments!