Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change Subroutine Names at Build Time to Avoid Collisions in Xcode

BACKGROUND

I'm building an iOS app (which I'll just call MyApp from here) that will rely on calculations done by several separate static libraries (which I'll call Lib1, Lib2, Lib3,...). Each library is built in it's own project, then imported into a single workspace (so the workspace will contain MyApp, Lib1, Lib2, ...). More details on how this is set up here. The libraries are used by other products that are independent from MyApp, so I want to minimize any changes in the libraries. The libraries are also written in (plain) C, so there are no header files.

Certain function names are used by multiple libraries (so both Lib1 and Lib2 might each have a DoStuff method). Functions with the same name generally do the same thing, but there are some specifics about how that do it that can be different between libraries, so the actual code in DoStuff on Lib1 might be quite different than the code in DoStuff on Lib2. It would be very difficult to write one universal DoStuff that would be exactly the same in each library.

THE ISSUE

While the app is running, it isn't calling the correct DoStuff from the correct library. I found out about this because the wrong function was called during a debug session (which eventually caused the app to crash, due to the subtle differences in the DoStuff functions).

WHAT I'M LOOKING FOR

Each library has only one entry point from MyApp, and each entry point is uniquely named. If DoStuff is called from the entry point method of Lib1 (or any other method on Lib1, for that matter), then I want it to call the DoStuff method on Lib1. What's the best way to make that happen?

Is there any way (maybe through a setting somewhere in XCode) I can make it so that each library is it's own namespace? That would be my preferred way to fix the issue. I guess I could go through and rename the duplicate functions so that they are all unique (so the DoStuff method on Lib1 could be renamed to Lib1DoStuff, or something similar), but there are hundreds of functions that could have duplicate names, and we are going to be adding hundreds of libraries to the project, so having to go in and rename all the functions by hand and fix all the calls to them would take a significant amount of time, and my boss doesn't see that as a viable option.


UPDATE

After looking at the comments from Josh Caswell and some of the links he provided, it looks like it might be possible to automatically rename all the functions when the libraries are compiled, and that would be the best way to try to fix THE ISSUE above. From what I've seen, the objcopy that gets mentioned in a couple of the links in the comments isn't support on iOS. I eventually came across this blog entry, which talks about creating custom build rules for Xcode targets, and this blog that talks about custom build settings and build phases.

Am I right to assume that I can use scripts at some point in the build process to automatically append to the name of all the functions in each of my libraries, instead of doing it manually as I described in the last paragraph of the WHAT I'M LOOKING FOR section above? If so, which is the correct part of the build process to make those changes? Lastly, what would the syntax look like for doing something like that? The 'scripts' used in the different parts of the build processes certainly doesn't look like Obj-C. I've never used these 'scripts' before, so I'm completely in the dark on how I'd use them, and that's what I'm looking for help with.

I tried to be as clear as I could, but if there are any questions on what I'm asking please let me know.

like image 719
GeneralMike Avatar asked Oct 30 '22 17:10

GeneralMike


1 Answers

Why isn't xcode calling the correct library function?

Let's say I have 3 C libraries as mentioned by you. Let's say it has the following code.

Library 1 - test1lib.a with code:

#include <stdio.h>

void doStuff()
{
  printf("\nDoing stuff for lib1\n");
}

void uniqueEntryPoint1()
{
  printf("\nUnique entry point for lib1\n");
  doStuff();
}

Library 2 - test2lib.a with code:

#include <stdio.h>

void doStuff()
{
  printf("\nDoing stuff for lib2\n");
}

void uniqueEntryPoint2()
{
  printf("\nUnique entry point for lib2\n");
  doStuff();
}

Library 3 - test3lib.a with code:

#include <stdio.h>

void doStuff()
{
  printf("\nDoing stuff for lib3\n");
}

void uniqueEntryPoint3()
{
  printf("\nUnique entry point for lib3\n");
  doStuff();
}

Here each library has a unique function and one common function doStuff()

When we add these 3 libraries to xcode and link them. xcode links but does not load all the objects files. Let's say the objective C code is like this:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    uniqueEntryPoint1();

}

The output is

Unique entry point for lib1

Doing stuff for lib1

In this case xcode will only load symbols which are referred (library 1 objects) in this case.

If you are read about linker flags/options such as -all_load, -force_load and -objC, you will have better understanding.

If we add -all_load linker option, it will force linker to load all the objects of libraries so we will get the following error in xcode

ld: 2 duplicate symbols for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

The reason this fails as the linker detects that doStuff() is redefined multiple times.

The only way around this problem is change the linker input i.e. symbols present in these 3 libraries. This is already mentioned by Josh in the comments. I will add my $0.02 to it.

Possible Solutions

Solution 1:

The best solution (self explanatory) is to change the source code if you have access to the same.

Solution 2:

Use objcopy to rename or prefix the function as provided in the answer of this How to deal with symbol collisions between statically linked libraries?

Now your doubt on how to find the objcopy.

Option 1: You can use this project https://github.com/RodAtDISA/llvm-objcopy. This will be tricky to compile as it builds along with llvm. You will have to follow instructions at http://llvm.org/docs/GettingStarted.html and http://llvm.org/docs/CMake.html.

If you rewrite the https://github.com/RodAtDISA/llvm-objcopy/blob/master/llvm-objcopy.cpp, you can probably reuse the parsing and object rewriting logic without depending upon llvm.

Option 2:

Compile and reuse binutils objcopy from https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;a=blob;f=binutils/objcopy.c;h=2636ab4bcb34cf1e1e54db9933018a805b366727;hb=HEAD

Solution 3:

You can follow the answer provided by Richard in this link Rewriting symbols in static iOS libraries. This is more of a hack but using a hex editor you can rewrite the symbols if their length is kept the same. If you have more symbols and its a big library, you can consider using https://sourceforge.net/projects/bbe-/ and nm to write a script.

All of this is a considerable effort but apparently there is no shortcut.

like image 89
manishg Avatar answered Nov 13 '22 02:11

manishg