JSPatch 支持了动态调用 C 函数,无需在编译前桥接每个要调用的 C 函数,只需要在 JS 里调用前声明下这个函数,就可以直接调用:
1 2 3 |
require('JPEngine').addExtensions(['JPCFunction']) defineCFunction("malloc", "void *, size_t") malloc(10) |
我们一步步来看看怎样可以做到动态调用 C 函数。
函数地址
首先若要动态调用 C 函数,第一步就是需要通过传入一个函数名字符串找到这个函数地址,这里一个必要的前提条件就是 C 编译后的可执行文件里必须有原函数名的信息,才有可能做到通过函数名字符串找到函数地址。我们写个简单的程序来看看它编译后可执行文件的内容有没有这个信息:
1 2 3 4 5 6 7 |
//main.m void test() { } int main() { return 0; } |
编译这个文件,并用otool看下它的汇编:
1
|
gcc main.m -o main.o
otool -tV main.o
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
main.o: (__TEXT,__text) section _test: 0000000100000f90 pushq %rbp 0000000100000f91 movq %rsp, %rbp 0000000100000f94 popq %rbp 0000000100000f95 retq 0000000100000f96 nopw %cs:(%rax,%rax) _main: 0000000100000fa0 pushq %rbp 0000000100000fa1 movq %rsp, %rbp 0000000100000fa4 xorl %eax, %eax 0000000100000fa6 movl $0x0, -0x4(%rbp) 0000000100000fad popq %rbp 0000000100000fae retq |
可以看到函数名 test 和 main 都清楚地记录在可执行文件里,只不过前面多了个下划线_,所以完全可以在运行时通过函数名字符串查到这个函数地址。
dlsym()
实际上动态链接器已经提供一个 API:dlsym()
,本来是用于动态加载库(DLL),然后通过这个接口拿到函数地址,它也可以应用于当前可执行文件镜像,原理是一样的。