aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorcomex2015-01-16 06:32:58 -0500
committercomex2015-01-16 06:32:58 -0500
commit4ac639ebb0172560f6719f3eed45d1a3f054efe5 (patch)
tree6d9d2dd67d92928a0463d0585ff827d4c332a876 /lib
parenthandle oom and silly machos and stuff (diff)
downloadsubstitute-4ac639ebb0172560f6719f3eed45d1a3f054efe5.tar.gz
and now for something completely different: assembly maybestret-IMPL
forwarding functions for atomicity
Diffstat (limited to 'lib')
-rw-r--r--lib/objc-asm.S81
-rw-r--r--lib/substitute.h32
2 files changed, 113 insertions, 0 deletions
diff --git a/lib/objc-asm.S b/lib/objc-asm.S
new file mode 100644
index 0000000..d3dae8a
--- /dev/null
+++ b/lib/objc-asm.S
@@ -0,0 +1,81 @@
+/* These all try to re-invoke old_ptr. To make things worse, we don't know
+ * whether it will be called as stret or not, so we have to guess. */
+.globl _temp_block_invoke
+.section __TEXT,__text,regular,pure_instructions
+#if defined(__x86_64__)
+_temp_block_invoke:
+ push %rcx
+ mov 0x10(%rsi), %rax; /* block if stret, else self */
+ lea _temp_block_invoke(%rip), %rcx
+ cmp %rax, %rcx
+ pop %rcx
+ je 2f
+ mov %rdi, %rax
+ mov %rsi, %rdi
+ mov 0x28(%rax), %rsi
+ mov 0x20(%rax), %rax
+ jmp *(%rax)
+2: /* stret */
+ mov %rsi, %rax
+ mov %rdx, %rsi
+ mov 0x28(%rsi), %rdx
+ mov 0x20(%rsi), %rax
+ jmp *(%rax)
+#elif defined(__i386__)
+_temp_block_invoke:
+ call 1f
+1:
+ pop %edx
+ lea _temp_block_invoke-1b(%edx), %edx
+ mov 8(%esp), %ecx; /* block if stret, else self */
+ mov 0xc(%ecx), %eax
+ cmp %eax, %edx
+ je 2f
+ mov 4(%esp), %eax; /* block */
+ mov %ecx, 4(%esp)
+ mov 0x18(%eax), %ecx
+ mov %ecx, 8(%esp)
+ mov 0x14(%eax), %eax
+ jmp *(%eax)
+2: /* stret */
+ mov 12(%esp), %ecx; /* self */
+ mov 8(%esp), %eax; /* block */
+ mov %ecx, 4(%esp)
+ mov 0x18(%eax), %ecx
+ mov %ecx, 12(%esp)
+ mov 0x14(%eax), %eax
+ jmp *(%eax)
+#elif defined(__arm__)
+.thumb_func _temp_block_invoke
+.thumb
+_temp_block_invoke:
+1:
+ ldr r9, [r1, #0xc]
+ adr r12, 1b
+ cmp r9, r12
+ beq 2f
+ mov r9, r0
+ mov r0, r1
+ ldr r1, [r9, #0x18]
+ ldr r9, [r9, #0x14]
+ ldr r9, [r9]
+ bx r9
+2: /* stret */
+ mov r9, r1
+ mov r1, r2
+ ldr r2, [r9, #0x18]
+ ldr r9, [r9, #0x14]
+ ldr r9, [r9]
+ bx r9
+#elif defined(__arm64__)
+.align 2
+_temp_block_invoke:
+ mov x9, x0
+ mov x0, x1
+ ldr x1, [x9, #0x28]
+ ldr x9, [x9, #0x20]
+ ldr x9, [x9]
+ br x9
+#else
+#error No forwarding assembly definition for this arch
+#endif
diff --git a/lib/substitute.h b/lib/substitute.h
index 11141bf..181fb7c 100644
--- a/lib/substitute.h
+++ b/lib/substitute.h
@@ -43,6 +43,10 @@ enum {
/* substitute_interpose_imports: couldn't redo relocation for an import
* because the type was unknown */
SUBSTITUTE_ERR_UNKNOWN_RELOCATION_TYPE,
+
+ /* substitute_hook_objc_message: no such selector existed in the class's
+ * inheritance tree */
+ SUBSTITUTE_ERR_NO_SUCH_SELECTOR,
};
struct substitute_function_hook {
@@ -167,6 +171,34 @@ int substitute_interpose_imports(const struct substitute_image *handle,
#endif /* 1 */
+#if defined(__APPLE__) && __BLOCKS__
+#include <objc/runtime.h>
+/* Hook a method implementation for a given Objective-C class. By itself, this
+ * function is thread safe: it is simply a wrapper for the atomic Objective-C
+ * runtime call class_replaceMethod, plus the superclass-call generation
+ * functionality described below, and some code to ensure atomicity if the
+ * method is called while the function is in progress. However, it will race
+ * with code that modifies class methods without using atomic runtime calls,
+ * such as Substrate.
+ *
+ * @klass the class
+ * @selector the selector
+ * @replacement the new implementation
+ * @old_ptr optional - out pointer to the 'old implementation'.
+ * If there is no old implementation, a custom IMP is
+ * returned that delegates to the superclass. This IMP is
+ * created with imp_implementationWithBlock, so it can be
+ * freed if desired with imp_removeBlock.
+ * @created_imp_ptr optional - out pointer to whether a fake superclass-call
+ * IMP has been placed in <old_ptr>
+ *
+ * @return SUBSTITUTE_OK
+ * SUBSTITUTE_ERR_NO_SUCH_SELECTOR
+ */
+int substitute_hook_objc_message(Class klass, SEL selector, IMP replacement,
+ IMP *old_ptr, bool *created_imp_ptr);
+#endif
+
#ifdef __cplusplus
} /* extern */
#endif