Mobile Development 21 min read

How to Hook objc_msgSend for Precise iOS Method Timing with Assembly

This article explains how to accurately measure the execution time of Objective‑C methods on iOS by implementing a custom objc_msgSend hook in ARM64 assembly, replacing the original implementation with fishhook, and visualizing the results using Chrome tracing, offering a low‑cost, non‑intrusive performance profiling solution.

Huolala Tech
Huolala Tech
Huolala Tech
How to Hook objc_msgSend for Precise iOS Method Timing with Assembly

Preface

Measuring the execution time of functions or methods is a common challenge for developers. The naive approach works for a few calls, but becomes impractical when profiling thousands of functions. This article presents a solution for iOS based on hooking objc_msgSend at the assembly level.

Background

Huolala's mobile business lines run strict performance tests before each release, including a startup test that uses the open‑source tidevice tool (similar to libimobiledevice). The current pipeline has several pain points:

Hard to locate the responsible component when performance issues arise.

Performance testing is at the end of the pipeline, causing delays.

Long analysis time makes the process unsustainable.

Xcode Instruments are powerful but the results are not persistent.

The goal is a low‑cost, non‑intrusive, high‑performance, visualizable solution.

Exploration

We reviewed existing solutions such as static instrumentation, Messier, Xcode Instruments, and objc_msgSend hooking. After comparing their pros and cons, we chose the objc_msgSend hook for its low intrusion, high performance, and visualizable output.

Comparison Table

Solution

Advantages

Disadvantages

Static Instrumentation

Can instrument entry and exit of every function

Increases binary size, cannot instrument closed‑source libraries, high integration cost

Messier

Visual analysis with timeline

Depends on third‑party maintenance, some iOS versions unsupported

Xcode Instruments

Powerful, supports multithreaded analysis

High learning curve, results are not persistent objc_msgSend Hook

Low intrusion, high performance, visualizable, supports timeline

Does not work on simulators, only for Objective‑C methods

Core Principles

objc_msgSend

is the universal entry point for all Objective‑C method calls. It is implemented in assembly for performance and to handle variable argument lists. To replace it safely, we must preserve the register state before and after the original call.

ARM Assembly Basics

ARM64 provides 31 general‑purpose 64‑bit registers ( x0x30) and a stack pointer ( sp). Registers x0x7 are used for argument passing, fp ( x29) is the frame pointer, and lr ( x30) holds the return address. The br and ret instructions perform unconditional jumps.

Implementing a Custom objc_msgSend

The custom implementation follows three steps:

Save all registers ( GDN_STORE_REGISTERS macro).

Call a pre‑hook function to record the start time and method information.

Invoke the original objc_msgSend and then call a post‑hook to compute the elapsed time and restore registers.

.macro GDN_STORE_REGISTERS
    stp fp, lr, [sp, #-0x10]!
    mov fp, sp
    sub sp, sp, #0x80
    // Save x0‑x7, x19‑x28, etc.
.endm

.macro GDN_LOAD_REGISTERS
    ldp x0, x1, [sp], #0x10
    ldp x2, x3, [sp], #0x10
    // Restore other registers
    ldp fp, lr, [sp], #0x10
    ret
.endm

.globl _gdn_objc_msgSend
_gdn_objc_msgSend:
    GDN_STORE_REGISTERS
    bl _needs_profiler
    cbnz x0, GDN_NEEDS_PROFILER
    GDN_LOAD_REGISTERS
    // Call original objc_msgSend
    bl _origin_objc_msgSend
    GDN_STORE_REGISTERS
    bl _post_objc_msgSend
    GDN_LOAD_REGISTERS
    ret

Replacing the Original objc_msgSend with Fishhook

We use Facebook’s fishhook library to rebind the symbol at runtime. The process iterates over all loaded Mach‑O images, locates the LC_SYMTAB and LC_DYSYMTAB commands, builds a list of symbols, and replaces the address of objc_msgSend with our custom implementation.

struct rebinding {
    const char *name;
    void *replacement;
    void **replaced;
};

rebind_symbols((struct rebinding[1]){{"objc_msgSend", (void *)gdn_objc_msgSend, (void **)&origin_objc_msgSend}}, 1);

Choosing the Right Hook Timing

To minimize the blind spot, the hook should be installed as early as possible. Placing the code in a dynamic library whose +load method is the first to run (by ordering the library first in the target’s link phase) ensures the replacement occurs before most other code executes.

+ (void)load {
    // Replace original objc_msgSend with custom implementation
}

Usage

1. Add the dependency via CocoaPods:

pod 'Guldan'

2. Import the tracer and start/stop measurement around the code you want to profile:

#import "GDNOCMethodTracer.h"
[GDNOCMethodTracer start];
// ... code to profile ...
[GDNOCMethodTracer stop];

3. Retrieve the recorded data:

[GDNOCMethodTracer handleRecordsWithComplete:^(NSArray<NSString *> * _Nonnull filePaths) {
    // Process trace files
}];

4. Export the trace files from the app’s container (Xcode → Devices → Download Container) and open them in Chrome’s chrome://tracing UI for visual analysis.

Result Demonstration

Desktop trace view:

Desktop trace
Desktop trace

Mobile trace view:

Mobile trace
Mobile trace

Conclusion and Outlook

We identified the performance‑profiling pain points in Huolala’s iOS apps, evaluated existing tools, and built a custom objc_msgSend hook solution that is low‑cost, non‑intrusive, and provides visualizable timing data. Future work includes:

Simplifying trace file extraction.

Adding real‑time monitoring.

Providing data analysis dashboards.

Supporting black‑listing of methods.

Extending the framework with more features.

References

[1] Shi Bin. “ARM Assembly Language and C/C++ Mixed Programming Methods.” *Electronic Measurement Technology*, 2006.

[2] Wang Yingjun et al. “Implementation of ARM Assembly and C Mixed Programming.” *Science & Technology Information*, 2010.

[3] Liu Feng et al. *ARM Assembly Language*. University of Electronic Science and Technology of China Press, 2010.

[4] Wen Quangang. *Assembly Language Programming Based on ARM Architecture*. Beijing University of Aeronautics and Astronautics Press, 2007.

[5] “Assembly Language Tutorial – Ruan Yifeng’s Blog.”

[6] https://github.com/DavidGoldman/InspectiveC

[7] https://github.com/ming1016/GCDFetchFeed

[8] https://opensource.apple.com/source/objc4/objc4-723/runtime/

[9] https://github.com/facebook/fishhook

[10] Dai Ming. “How to Optimize and Monitor App Startup Speed.”

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

iOSperformance profilingAssemblyobjc_msgSendmethod timing
Huolala Tech
Written by

Huolala Tech

Technology reshapes logistics

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.