2

I am attempting a method swizzle in Obj-C but I would like to pass it a pure C function. This means I need to somehow assign a selector and/or manually build an objc_method struct. Maybe somehow leverage NSInvocation?

My understanding is that due to the fact that Obj-C is a strict superset of C and therefor fully compatible.

What I have going now:

main.m :

#include....

CFStringRef strRet(void) {
    return CFSTR("retString");
}

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        SEL _strRet = sel_registerName("strRet");
        //I also tried: SEL _strRet = NSSelectorFromString(@"strRet");

        Class bundle = objc_getClass("NSBundle");

        method_exchangeImplementations(
            class_getInstanceMethod(bundle, sel_registerName("anySelector")), 
            class_getInstanceMethod(bundle, sel_registerName("_strRet")
        );

I have tried putting the C function inside @implementation (which I would like to avoid) and even then it did not work.

1
  • correct me if i understood wrong. You want to pass pure C function ?? in your code snippet no C function shown. can you modify you query little more code ?? Commented Dec 20, 2017 at 6:39

2 Answers 2

3

You can't swizzle a C function per se; swizzling is based on method lookup which goes through method descriptions (which are represented by the Method type by the runtime functions) and C functions do not have a method description.

However the implementation of a method is just a C function. Such a C function must take a minimum of two arguments, being the object the method is invoked on (the Objective-C implicit parameter self) and the selector (the Objective-C implicit parameter _cmd). When you swizzle a method the replacement implementation, a C function, must have exactly the same type as the original – complete with the two implicit arguments – so your strRet() would not be suitable as is, you would need to change it to:

CFStringRef strRet(NSObject *self, CMD sel, void)
{
   return CFSTR("retString");
}

So you have three main choices:

  1. The easiest way is to define a method whose body is your "pure" C function, then swizzle the recommended way (taking care to handle inheritance correctly, see this answer).

  2. If you really want to write a C function and that C function does not need to call the original implementation of the method then:

    (a) You need to convert your C function into one which can be used as a method implementation. You can:

    • If you are writing/have the source of the C function you simply define it to take the two implicit arguments as above. Take the address of this function and cast it to IMP, which is just a typedef for a C function pointer of the appropriate type, for use below.
    • If you are using a C function whose definition you cannot change then you can do one of:
      • Write a C wrapper function which takes the extra arguments, ignores them and calls your target C function. Take the address of this wrapper function and cast it to IMP for use below.
      • Wrap the call to your C function in a block and use imp_implementationWithBlock() to produce an IMP value from it. You can read this article for a description of using imp_implementationWithBlock().

    (b) use method_setImplementation() to set the implementation to the IMP value you produced in (a).

  3. If you really want to write a C function and that C function does need to call the original implementation of the method then you will need to add a method to your class whose implementation is your C function – modified/wrapped as in (2), then swizzle your added method with your original method as under (1) so that the original implementation is still available as a method. To add a method you use class_addMethod()

HTH

Sign up to request clarification or add additional context in comments.

3 Comments

You can also use imp_implementationWithBlock(). It makes it easy to wrap a c function in a block that can be plugged in as a method in the runtime.
@bbum - Duh, forgot that one. Edited the answer to include it as an option. Thanks.
This is great! Thank you for the resources!
2

The key here is finding a mechanism that maps between the function pointer and your context. The simplest way to do that is by generating a new function pointer. You can use imp_implementationWithBlock(), MABlockClosure, or roll your own.

The simplest mechanism to create a new function pointer I've found is to remap the entire function to a new address space. The new resulting address can be used as a key to the required data.

#import <mach/mach_init.h>
#import <mach/vm_map.h>

void *remap_address(void* address, int page_count)
{
    vm_address_t source_address = (vm_address_t) address;
    vm_address_t source_page = source_address & ~PAGE_MASK;

    vm_address_t destination_page = 0;
    vm_prot_t cur_prot;
    vm_prot_t max_prot;
    kern_return_t status = vm_remap(mach_task_self(),
                                &destination_page,
                                PAGE_SIZE*(page_count ? page_count : 4),
                                0,
                                VM_FLAGS_ANYWHERE,
                                mach_task_self(),
                                source_page,
                                FALSE,
                                &cur_prot,
                                &max_prot,
                                VM_INHERIT_NONE);

    if (status != KERN_SUCCESS)
    {
        return NULL;
    }

    vm_address_t destination_address = destination_page | (source_address & PAGE_MASK);

    return (void*) destination_address;
}

Note that page_count should be large enough to contain all of your original function. Also, remember to handle pages that aren't required anymore and note that it takes a lot more memory per invocation than MABlockClosure.

(Tested on iOS)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.