Development is a complex process and consists of a lot of parts. Sometimes we use parts that we didn’t write and so have no control over them.

In this case, we must think about an alternative solution, or even about partial functionality replacement. To do so we have a lot of techniques. One of them - swizzling was covered in one of my prev posts.

Working with Swift and Obj-c is good, but sometimes not enough, and we may need to use another language for example C. Let’s review the way how implementation can be replaced in C.

C function

For example, let’s use for experiment atoll function - a function that converts string to long long integer. In swift this function is declared as:

public func atoll(_: UnsafePointer<CChar>!) -> Int64

What we need - is a c-style declaration:

long long int atoll (const char * str);

If we execute the function, we receive the expected output:

atoll_original.png



With the C function it’s easy to do a full replacement - just declare a function with the same definition and u done. Let’s do this:

long long atoll(const char * str) {
  return 100;
}

Let’s run again the code:

atoll_replacement



Looks easy. And in most cases it’s ok, but sometimes we need to replace functionality only under certain conditions. For such behavior, we need to check this condition and if it’s true - call our logic, in other cases - original implementation.

To do so, we need to use some help from dyld (dynamic library loader) with dlopen and dlsym.

dlopen - The function dlopen() loads the dynamic shared object (shared library) file named by the null-terminated string filename and returns an opaque “handle” for the loaded object.

dlsym, dlvsym - obtain the address of a symbol in a shared object or executable

This function helps us to obtain a reference to a module where the target function is declared and implemented and to obtain the function address.

Of cause, this can’t be used without our debugger - lldb.

We will use a command for dumping the implementation address of the function - image lookup:

image_lookup_result.png



As u can see - I used image lookup -rn atoll where:

 -n <function-or-symbol> ( --name <function-or-symbol> )
    Lookup a function or symbol by name in one or more target modules.
 -r ( --regex )
    The <name> argument for name lookups is regular expressions.

to look up more check the command help image lookup. Note, that image - is an abbreviation for target modules

RuntimeRoot - is a place from where all our code is executed, so the interested path is usr/lib/system/libsystem_c.dylib.

The next step - is to obtain the address of our function:

#import <dlfcn.h>
#import <assert.h>

// ... then in u'r function

void *handle;
long long (*original)(const char *);

handle = dlopen("/usr/lib/system/libsystem_c.dylib", RTLD_NOW);
assert(handle);
original = dlsym(handle, "atoll");

handle will store the reference to the lib, and original will hold the original implementation of the atoll

Now, we may use some predicate to decide when to use the original implementation and when - our own.

bool needToReplace = strcmp(input, "222") == 0;
if (needToReplace) {
   return 111;
}

The good moment here - is that we may store our handle and original as a static variable once and reuse them later. For this we may use singleton pattern and gcd from #import <dispatch/dispatch.h>:

static void *handle;
static long long (*original)(const char *);

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
	handle = dlopen("/usr/lib/system/libsystem_c.dylib", RTLD_NOW);
	assert(handle);
	original = dlsym(handle, "atoll");
});
demo.gif



Complete solution

long long atoll(const char * input) {
  static void *handle;
  static long long (*original)(const char *);

  static dispatch_once_t onceToken;
  dispatch_once(&onceToken, ^{
    handle = dlopen("/usr/lib/system/libsystem_c.dylib", RTLD_NOW);
    assert(handle);
    original = dlsym(handle, "atoll");
  });

  bool needToReplace = strcmp(input, "222") == 0;
  if (needToReplace) {
    return 111;
  }

  return original(input);
}

resources