networkpx Project Blog: Introducing Subjective-C, an objc_msgSend[_[st|fp]ret]? logger.
You can find the header file in subjc.h.
Starts logging all objective-C calls. The result will be written to the file f, up to a maximum call-tree depth maximum_depth. You may use the boolean parameters print_arguments and print_return_value to control whether to format the arguments and return values. Not doing so may save some time.
Stops logging. This function must be called at the same level as SubjC_start.
The SubjC_initialize() function does the initialization things. It will be called automatically in SubjC_start if not done, so you don't need to explicitly call it.
Subjective-C is not thread-safe. I repeat: Subjective-C is not thread-safe. If there are ≥1 threads that uses Objective-C during logging you're almost surely doomed to crash. OK it won't crash now by changing the global lr stack to thread-local, but it's still not completely thread-safe.
Variadic arguments can never be logged.
+initialize messages cannot be logged. This is because Subjective-C cannot work if the class is not initialized, so it implicitly initialize the class before logging.
All Objective-C calls (except the manually cached ones) will go through objc_msgSend, objc_msgSend_stret, and in x86, objc_msgSend_fpret. Therefore, if we replace these functions with our custom one the every message can be logged.
The tricky part is to call the original function after logging is finished. GCC has __builtin_apply_args and friends to construct the calls, but it assumes you know the maximum bytes of the arguments, because the approach taken by these is to copy the content of stack. This leads to the buggy behavior in the old logger.
To avoid this, the new logger was written in assembly. The approach (and the name) was inspired by saurik's Aspective-C (thanks for informing this work). In the new logger, every action before calling the original function was done without modifying the stack pointer and the content above it, nor the registers.
Print the returned value and return.
The situation is easier on x86. Every argument, and the return address must be on the stack. Therefore the "Store r0-r3" steps can be ignored. Nevertheless, we need to replace the return address to ensure the original objc_msgSend return to our place, so the custom stack is still needed.
Source code for the whole hk.kennytm.Peace subproject (for argument formatting).
APELite, if you compile for x86.
MobileSubstrate, if you compile for ARM.
subjc.mf contains the Makefile for ARM, and subjc.x86 for x86.