HOWTO get the correct Frame Pointer of an arbitrary thread in iOS?

只谈情不闲聊 提交于 2020-01-13 04:44:30

问题


Way to get the Frame Pointer

On a Demo App running on iPhone 5s Device / Xcode 7, I tried to get the frame pointer of an arbitrary thread using thread_get_state , but always result in an incorrect one :

- (BOOL)fillThreadState:(thread_t)thread intoMachineContext:(_STRUCT_MCONTEXT *)machineContext {
    mach_msg_type_number_t state_count = MACHINE_THREAD_STATE_COUNT;
    kern_return_t kr = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count);
    if (kr != KERN_SUCCESS) {
        char *str = mach_error_string(kr);
        printf("%s\n", str);
        return NO;
    }

    return YES;
}

I read the frame pointer like this: uintptr_t fp = machineContext.__ss.__fp;, according to Apple Doc (ARMv6 and ARM64),

Register R7 is used as a frame pointer on ARMv6

while x29 on ARM64

The frame pointer register (x29) must always address a valid frame record, although some functions—such as leaf functions or tail calls—may elect not to create an entry in this list. As a result, stack traces will always be meaningful, even without debug information.

_STRUCT_ARM_THREAD_STATE64
{
    __uint64_t    __x[29];  /* General purpose registers x0-x28 */
    __uint64_t    __fp;     /* Frame pointer x29 */
    __uint64_t    __lr;     /* Link register x30 */
    __uint64_t    __sp;     /* Stack pointer x31 */
    __uint64_t    __pc;     /* Program counter */
    __uint32_t    __cpsr;   /* Current program status register */
    __uint32_t    __pad;    /* Same size for 32-bit or 64-bit clients */
};

Way to prove the Frame Pointer is wrong

How do I prove that the fp (machineContext.__ss.__fp) is not a correct frame pointer?

  1. If the parameter is the current thread, mach_thread_self() for example, the fp is always 0, which is different from __builtin_frame_address(0);

  2. If the parameter is not the current thread, main thread for example, the pointer to the previous frame pointer of fp will be NULL in two or three frames, which is too short for a normal link-list of stack frames;

  3. Still not the current thread, I print out the call stack addresses using backtrace on main thread before sleep. Then on another thread, I suspend the main thread and read the frame pointer using thread_get_state to walk the call stack, the two backtrace buffers are totally different;

What confuses me

Apple Doc says The frame pointer register (x29) must always address a valid frame record, but I could read a ZERO value from it.

Furthermore, ARM Doc states In all variants of the procedure call standard, registers r16, r17, r29 and r30 have special roles. In these roles they are labeled IP0, IP1, FP and LR when being used for holding addresses.

Below is an example of part of _STRUCT_MCONTEXT's value:

Printing description of machineContext:
(__darwin_mcontext64) machineContext = {
  __es = (__far = 0, __esr = 0, __exception = 0)
  __ss = {
    __x = {
      [0] = 292057776134
      [1] = 6142843584
      [2] = 3
      [3] = 40
      [4] = 624
      [5] = 17923
      [6] = 0
      [7] = 0
      [8] = 3968
      [9] = 4294966207
      [10] = 3603
      [11] = 70
      [12] = 0
      [13] = 33332794515418112
      [14] = 0
      [15] = 4294967295
      [16] = 4294967284
      [17] = 18446744073709551585
      [18] = 4295035980
      [19] = 0
      [20] = 0
      [21] = 0
      [22] = 17923
      [23] = 624
      [24] = 6142843584
      [25] = 3
      [26] = 40
      [27] = 3
      [28] = 0
    }
    __fp = 0
    __lr = 6142843568
    __sp = 6877072044
    __pc = 6142843488
    __cpsr = 2582105136
    __pad = 1
  }
  __ns = {

What I am looking for

I'm now searching for a method to get the correct value of the Frame Pointer of an arbitrary thread.

Thanks for any help!

UPDATE 1

Of course, What I want is to get the correct Frame Pointer of the leaf stack frame of an arbitrary thread, and then walk the call stack along the Previous Pointer of the Frame pointer.

Before this, I've read these links:

Getting a backtrace of other thread

How To Loop Through All Active Thread in iPad app

Printing a stack trace from another thread

Thanks again.

UPDATE 2

I've tried on other platforms, but yet the same result : wrong frame pointer.

- (uintptr_t)framePointerOfMachineContext:(_STRUCT_MCONTEXT *)machineContext {
#if defined (__i386__)
    return machineContext->__ss.__ebp;
#endif

#if defined (__x86_64__)
    return machineContext->__ss.__rbp;
#endif

#if defined (__arm64__)
    return machineContext->__ss.__fp;
#endif
}

The value framePointerOfMachineContext returns is not the same to __builtin_frame_address(0).

UPDATE 3

Inspired by a colleague, I tried inline asm and made it on i386:

uintptr_t ebp = 1;
__asm__ ("mov %%ebp, %0;"
         :"=r"(ebp));

uintptr_t builtinFP = (uintptr_t)__builtin_frame_address(0);

Now ebp variable holds the same value to builtinFP's. But HOWTO do this on an arbitrary thread?


回答1:


I found the problem eventually,

- (BOOL)fillThreadState:(thread_t)thread intoMachineContext:(_STRUCT_MCONTEXT *)machineContext {
    mach_msg_type_number_t state_count = MACHINE_THREAD_STATE_COUNT;
    kern_return_t kr = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count);
    if (kr != KERN_SUCCESS) {
        char *str = mach_error_string(kr);
        printf("%s\n", str);
        return NO;
    }

    return YES;
}

thread_get_state is fine, but the parameter should be architecture-specific, such as x86_THREAD_STATE32, x86_THREAD_STATE64 and etc.

I misunderstood the macro MACHINE_THREAD_STATE, which gave me an universal meaning for different architectures.



来源:https://stackoverflow.com/questions/33143102/howto-get-the-correct-frame-pointer-of-an-arbitrary-thread-in-ios

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!