What does Swift have?
The only wall The Apple engineer has built for me is: there is no way to get a pointer to a function in Swift:
Note that the C function pointer is not imported into Swift (from "Using Swift with Cocoa and Objective-c")
But how do we know the address of the hook and where to jump in this case? Let's take a closer look and see what Swift's Func is at the bytecode level.
When you pass a generic parameter to a function, Swift does not pass its address directly, but rather a pointer to the trampoline function (see below) with some function metadata information. and trampoline itself is part of the structure that wraps the original function.
What does that mean?
Let's use it to give an example:
Copy Code code as follows:
Func call_function (f: ()-> Int) {
Let B = f ()
}
Func someFunction ()-> Int {
return 0
}
In Swift we write only call_function (someFunction).
But after the Swift compiler has processed the code, performance is much better than calling Call_function (&somefunction)
Copy Code code as follows:
struct Swift_func_wrapper *wrapper = ... * Configure wrapper for someFunction () * *
struct Swift_func_type_metadata *type_metadata = .../* Information about function ' s arguments and return type * *
Call_function (Wrapper->trampoline, type_metadata);
The structure of a wrapper is as follows:
Copy Code code as follows:
struct Swift_func_wrapper {
uint64_t **trampoline_ptr_ptr; = &trampoline_ptr
uint64_t *trampoline_ptr;
struct Swift_func_object *object;
}
What is the Swift_func_object type? To create objects, Swift uses a global constant called metadata[n] (each function call is unique, like your func as a generic parameter, so for the following code:
Copy Code code as follows:
Func Callf (f: ()-> ()) {
f ();
}
CALLF (someFunction);
CALLF (someFunction);
Constants metadata and METADATA2 are created).
The structure of a metadata[n] is kind of like this:
Copy Code code as follows:
struct Metadata {
uint64_t *destructor_func;
uint64_t *unknown0;
const char type:1; I ' m not sure about this and padding,
Char padding[7]; Maybe it ' s just a uint64_t too ...
uint64_t *self;
}
Initially, Metadatan has only 2 field collections: Destructor_func and type. The former is a function pointer that allocates memory as an object created using Swift_allocobject (). The latter is the object type recognizer (the 0x40 or ' @ ' of the function or method) and is (in some form) swift_allocobject () used to create a correct object for our func:
Swift_allocobject (&metadata2->type, 0x20, 0x7);
Once the Func object is created, it has the following structure:
Copy Code code as follows:
struct Swift_func_object {
uint64_t *original_type_ptr;
uint64_t *unknown0;
uint64_t function_address;
uint64_t *self;
}
The first field is a pointer that corresponds to the Metadata[n]->type value, and the second field appears to be 0x4 | 1 << (0x100000004) and hints at some possible (I don't know what). Function_address is where our actual hooks are interested, and self is (immediately) its own pointer (if our object represents an ordinary function, this field is NULL).
Okay, so what do I start with the framework? In fact, I don't understand why Swift needs them when it runs, but anyway, that's what they look like:
Copy Code code as follows:
void* somefunction_trampoline (void *unknown, void *arg, struct swift_func_object)
{
void* target_function = (void *) desc->function_address;
uint64_t *self = desc->self;
Swift_retain_noresult (desc->self); Yeah, retaining self is cool!
Swift_release (DESC);
_swift_trampoline (Unknown, ARG, target_function, self);
return unknown;
}
void *_swift_trampoline (void *unknown, void *arg, void *target_function, void *self)
{
Target_function (ARG, self);
return unknown;
}
Let's create it
Imagine that you have these functions in your swift code:
Copy Code code as follows:
Func takesfunc<t> (f:t) {
...
}
Func someFunction () {
...
}
And you imagine generating them like this:
Copy Code code as follows:
This line of code translates into a fairly large C program:
Copy Code code as follows:
struct Swift_func_wrapper *wrapper = malloc (sizeof (*wrapper));
Wrapper->trampoline_ptr = &someFunction_Trampoline;
Wrapper->trampoline_ptr_ptr = & (Wrapper.trampoline);
Wrapper->object = ({
Let ' s say the metadata for this function is ' metadata2 '
struct Swift_func_object *object = Swift_allocobject (&metadata2->type, 0x20, 0x7);
Object->function_address = &someFunction;
Object->self = NULL;
Object
});
Global constant for the type of someFunction ' s arguments
const void *arg_type = &kSomeFunctionArgumentsTypeDescription;
Global constant for the return type of someFunction
const void *return_type = &kSomeFunctionReturnTypeDescription;
struct Swift_func_type_metadata *type_metadata = Swift_getfunctiontypemetadata (Arg_type, return_type);
Takesfunc (Wrapper->trampoline_ptr, type_metadata);
The structure "swift_func_type_metadata" is opaque, so I don't have much to say.
Back to function pointer
Now that we know how a function is represented as a generic type parameter, let's use this to your purpose: Get a pointer that really points to a function!
All we have to do is note that we already have a trampoline_ptr pointer field address passed as the first parameter, so the offset of the object field is only 0x8. Everything else is easy to combine:
Copy Code code as follows:
uint64_t _rd_get_func_impl (void *trampoline_ptr)
{
struct Swift_func_object *obj = (struct Swift_func_object *) * (uint64_t *) (trampoline_ptr + 0x8);
Return obj->function_address;
}
Looks like it's time to write.
Copy Code code as follows:
Rd_route (
_rd_get_func_impl (firstfunction),
_rd_get_func_impl (secondfunction),
Nil
)
But how do we call these C functions from Swift?
To this end, we will use Swift's non-public features: A @asmname attribute that allows us to provide a swift interface to the C function. Usage is as follows:
Copy Code code as follows:
@asmname ("_rd_get_func_impl")
Func rd_get_func_impl<q> (Q)-> UInt64;
@asmname ("Rd_route")
Func Rd_route (UInt64, UInt64, cmutablepointer<uint64>)-> CInt;
That's all we need to use Rd_route () in Swift.
But it can't handle any functions!
That is, you can't hook up any function with a generic parameter (this may be Swift's bug, or it may not, I haven't figured it out) with Rd_route (). But you can use extensions to easily override them, specifying the type of parameter directly:
Copy Code code as follows:
Class DemoClass {
Class func Template <T:CVarArg> (Arg:t, _ Num:int)-> String {
Return "\ (arg) and \ (num)";
}
}
Democlass.template ("Test", 5)//"Test and 5"
Extension DemoClass {
Class func Template (Arg:string, _ Num:int)-> String {
Return "{String}";
}
Class func Template (Arg:int, _ Num:int)-> String {
Return "{Int}";
}
}
--Your extension ' s methods for String and INT would be preferred over the original ones * *
Democlass.template ("Test", 5)--"{String}"
Democlass.template (5)--"{Int}"
--But for the other types ' template (T, Int) ' would be used
Democlass.template (["Array", "Item"], 5)---"[Array, item] and 5"
Swroute
To easily hook up a function in Swift, I created a package called Swroute-it's just a small class and a C function we wrote before:
Copy Code code as follows:
_rd_get_func_impl ():
Class Swiftroute {
class Func replace<methodt> (function Targetmethod:methodt, with Replacement:methodt)-> Int
{
Return Int (Rd_route (Rd_get_func_impl (Targetmethod), Rd_get_func_impl (replacement), nil));
}
}
Note that we do type checking for free because Swift requires the target method and the replacement to have the same methot type.
And we cannot use a copy of the original implementation, so I can only pass nil as another parameter to the function Rd_route (). If you have an opinion on how to integrate this pointer into the SWIFT code, please tell me!
You can find a large number of Swroute instances in the repository.
That's all that's out there.