Avoiding duplicate symbol errors during linking by removing classes from static libraries

If you’re using a static library in your project and specifying the -all_load flag to ensure files containing Objective-C categories are loaded correctly, you’ll likely experience “duplicate symbol” errors at link time if the static library includes the same classes used in your project or another library.

e.g. A third party has provided you with a static library (libProprietary.a) which uses KissXML internally for parsing & generating XML. You also have an internal static library (libBitsAndBobs.a) which also uses KissXML. If you use both libraries, you’ll get duplicate symbol errors for all the KissXML classes (DDXMLDocument, DDXMLElement & DDXMLNode).

A brutish yet simple workaround is to remove the offending classes from one of the libraries, so I’ll show you how it’s done.

Firstly, we need to see if the static library is a “fat file”, which means it contains code for multiple architectures. You’ll find most iOS static libraries are fat files, since they need to work both on the device (armv6/armv7) and in the Simulator (i386).

$ lipo -info libProprietary.a
Architectures in the fat file: libProprietary.a are: armv6 i386

As you can see, we have a fat file on our hands.

The tools we’ll be using require us to split the static library into seperate files for each architecture if it’s a fat file. To do that, we’ll use lipo again.

$ lipo -thin armv6 libProprietary.a -output libProprietary-armv6.a

We can now use ar to peek inside one of these thin archive files, listing the included object files.

$ ar -t libProprietary-armv6.a
__.SYMDEF SORTED
FOOBar.o
DDXMLDocument.o
DDXMLElement.o
DDXMLElementAdditions.o
DDXMLNode.o

As expected, in addition to the FOOBar.o file containing code specific to libProprietary.a, we also have object files for each of the classes defined in the KissXML project. To delete those, we’ll need to unpack the object files from the archive, delete the object files we don’t want, and repack the archive.

First, the extraction:

$ mkdir libProprietary-armv6
$ cd libProprietary-armv6
$ ar -x ../libProprietary-armv6.a

We now have a directory containing the contents of the archive:

$ ls -1
DDXMLDocument.o
DDXMLElement.o
DDXMLElementAdditions.o
DDXMLNode.o
FOOBar.o
__.SYMDEF SORTED

Simply remove the KissXML related object files and repack the archive like so:

$ rm DDXML*.o
$ libtool  -static *.o -output ../libProprietary-armv6.a

And to confirm we’ve successfully removed the correct object files from our archive, we can list the contents again:

$ ar -t ../libProprietary-armv6.a
__.SYMDEF SORTED
FOOBar.o

So far we’ve only stripped the armv6 archive, so we need to repeat the process for the i386 archive:

$ lipo -thin i386 libProprietary.a -output libProprietary-i386.a
$ mkdir libProprietary-i386 && cd libProprietary-i386
$ ar -x ../libProprietary-i386.a
$ rm DDXML*.o
$ libtool  -static *.o -output ../libProprietary-i386.a

Now for the final step, we recombine the thin files into a fat file again:

lipo -create libProprietary-armv6.a libProprietary-i386.a -output libProprietary-noKissXML.a

If you’ve done everything correctly you can now replace libProprietary.a with libProprietary-noKissXML.a and you won’t have any more duplicate symbol errors.