Search code examples
iosclangstatic-linking

Link against two versions of the same library (same symbols)


I'm developing an iOS app and want to link against a particular library. However a forked/old version of that same library (with colliding symbols) has been statically linked into a framework that I'm also using. Because the version pulled in by the framework is forked and out-dated ideally I'd like to somehow use the new library for my purposes, and allow the old/forked version to continue to be used by the framework, all in the one iOS binary.

I don't have control over the old/forked version of the library, but I can compile the new version however I please.

Is there something I can do to automatically prefix/namespace the symbols in the new version of the library so that I can use them without colliding with symbols in the old version?


Solution

  • Combining information from the following two stack overflow posts:

    and by taking a look at PLCrashReporter, I came up with the following:

    1. Create a new Cocoa Touch Static library "Wrapper" project in XCode.

    2. Change the "Mach-O Type" of the new static library to "Relocatable Object File".

    3. Pick which version of the duplicate library you don't want to be able to debug i.e. You're going to have to thrown away symbols for one copy of the duplicated library, so you won't be able to debug it.

      In my case I chose to throw away symbols for the pre-compiled dependency which had brought in the older version of the library/symbols. I made this decision because the pre-compiled dependency is from a third-party and it didn't have debug symbols anyway.

    4. In the new Cocoa Touch "Wrapper" project enable "Perform Single Object Pre-Link" and populate "Prelink libraries" with the relative path to the library/dependency you chose not to be able to debug.

    5. I suspect this step isn't actually required, but I did it because it seemed to make my intent more explicit (with regards to build settings).

      Set "Symbols Hidden by Default" to "Yes" in the "Wrapper" project and then mark any Objective-C classes, C++ methods/classes etc. you intend to export with:

      attribute((visibility("default")))

    6. In the "Wrapper" project set "Deployment Preprocessing" to "Yes" and "Strip Style" to "Non-Global Symbols" (in our case this caused Objective-C categories in the library to be incorrectly omitted).

    7. Write Objective-C classes (C or C++ code would work equally well) to wrap up the functionality you need from the dependency (whose symbols are about to be discarded), and expose a small API (for use in your original project).

    8. This step was key for me - add a Exported Symbols File and in it specify precisely which symbols you want exported (i.e. the small API mentioned above). Set the "Exported Symbols File" build settings as the relative path to this file (again in the "Wrapper" project).

    9. Go to the original project, remove the dependency which you've just created wrapper APIs for, from the "Link Binary with Libraries" Build Phase and replace it with your new wrapper library. You'll also need to update any code in your project to make use of the new wrapper API.

    10. If you haven't already, add the new version of the library/libraries (that were previously causing symbol conflicts) to the main project and use those APIs as per normal.