duplicate symbol errors with third party library

We use different commercial third party libraries when making iPhone applications. One of them got an update a few weeks back, but when we tried to integrate it into our application xcode threw out the following errors (actual library name hidden);

duplicate symbol _FBLoginViewButtonPressedPNG_retina in:
    SSSSS.embeddedframework/SSSSS.framework/DDDDD(FBLoginViewButtonPressedPNG.o)
    ./FacebookSDK.framework/FacebookSDK(FBLoginViewButtonPressedPNG.o)
duplicate symbol _FBLoginViewButtonPressedPNG_standard in:
    SSSSS.embeddedframework/SSSSS.framework/DDDDD(FBLoginViewButtonPressedPNG.o)
    ./FacebookSDK.framework/FacebookSDK(FBLoginViewButtonPressedPNG.o)
duplicate symbol _OBJC_METACLASS_$_FBLoginViewButtonPressedPNG in:
    SSSSS.embeddedframework/SSSSS.framework/DDDDD(FBLoginViewButtonPressedPNG.o)
    ./FacebookSDK.framework/FacebookSDK(FBLoginViewButtonPressedPNG.o)

At first glance, the problem appears obvious. We are (inadvertently) including Facebook’s libraries twice and the compiler (or rather the linker) is confused which reference to use. The difficulty is that we only included the Facebook library once. So where was this second reference coming from? Did we accidentally include it without realizing it? A very far-fetched theory, but it wouldn’t hurt to check, so we removed the one reference we knew we included and tried building the application again.

Not surprisingly, we got a lot of “missing ABC” errors this time.

A bit of thought led to the conclusion that our third-party library might be embedding the facebook libraries within itself. The theory made sense – the problem started happening after we tried to integrate an update to their library.

Deciding that the problem was isolated, we reached out to our third-party library vendor for assistance. Initially, we got responses from them that the issue was under investigation. Then a week passed. Two weeks. I even got engaged. And the problem still persisted.

Quite frankly, I was more than willing to throw away the so-called “update” to the library and just use the older version. That at least, worked with our code. Unfortunately, the customer we were working for insisted to use the newer version. The old version you see, was meant for iOS6. With all the visual differences between iOS6 and iOS7, the interface didn’t really mesh well.

So what was left to do? The issue was caused by the third-party vendor’s new update, but they wouldn’t give us a timely fix. What options were left open to us? Throw our hands up in despair?

I was literally, at the end of my rope. Stuck between a rock and a hard place, I asked myself – would it be possible to manually remove the references to facebook’s API from the third party vendor’s library? Reverse-engineering the binary would probably by a violation of the Terms Of Service we agreed on when obtaining the library, but I was really desperate here.

So I tried it.

First step was to find out what exactly I was working with. For this, I used the “lipo” tool to analyze the binary library our third party vendor provided.

$ lipo -info SSSSS
Architectures in the fat file: SSSSS are: armv7 armv7s i386

Hmm… the library comes with 3 different architectures bundled in? Better to check them out one-by-one.

$ lipo -thin armv7 SSSS -output SSSS-armv7

What the above command does, is to split the static library named “SSSS” into a separate file for the architecture “armv7” (works because SSSSS is a fat file).

Next we need to analyze the file to list out its included object files. The “ar” tool comes to our rescue here.

$ ar -t SSSSS-armv7
__.SYMDEF
A2BlockInvocation.o
A2DynamicDelegate.o
AFHTTPRequestOperation.o
AFHTTPRequestOperationManager.o
AFHTTPSessionManager.o
AFNetworkActivityIndicatorManager.o
AFNetworkReachabilityManager.o
...

A little down the list I came across these tell-tale entries,

...
Facebook.o
FBGraphObjectTableDataSource.o
FBRequestBody.o
FBSystemAccountStoreAdapter.o
FBFriendPickerViewController.o
FBAppEvents.o
FBFetchedAppSettings.o
FBUtility.o
...

Looks like the Facebook API to me!

Having identified the source of all the “duplicate symbol” problems, the next step is to get rid of them. For this, I created a new empty directory and unpacked the object files from the archive into it.

$ mkdir SSSSS-armv7.folder
$ cd SSSSS-armv7.folder
$ ar -x ../SSSSS-armv7

All that’s left to is to delete the problematic object files from this folder and repack the archive, like so;

$ rm FB*.o
$ libtool -static *.o -o ../SSSSS-armv7

You can use the “ar” tool to reconfirm that the libraries have been removed.

At this point, only the armv7 architecture had been cleaned. The above process needed to be repeated for all the architectures present in the third-party vendor’s library.

The second-to-last step is to recombine all the thin files into a big fat file again (for xcode to use).

$ lipo -create SSSSS-armv7 SSSSS-armv7s SSSSS-i386 -output SSSSS-noFacebook

Final step was of course, to attempt using this “reduced” library with our application in xcode. Happily enough, it worked! As a developer I was so relieved that it all came together – after so much pain too. And obviously, our customer is happy as well. As for the third-party library vendor? Who knows whats going on with them? I’m still a little annoyed that they put me through all those weeks of pain.

References:

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

Advertisements