4

获取帧指针的方法

在此处输入图像描述

在 iPhone 5s Device / Xcode 7 上运行的演示应用程序上,我尝试使用 获取任意frame pointer线程,但总是导致错误:thread_get_state

- (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;
}

我像这样读取帧指针:uintptr_t fp = machineContext.__ss.__fp;根据Apple Doc(ARMv6ARM64),

寄存器 R7 用作 ARMv6 上的帧指针

而 ARM64 上的 x29

帧指针寄存器 (x29) 必须始终寻址有效的帧记录,尽管某些函数(例如叶函数或尾调用)可能会选择不在此列表中创建条目。因此,堆栈跟踪总是有意义的,即使没有调试信息。

_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 */
};

证明帧指针错误的方法

如何证明 fp ( machineContext.__ss.__fp) 不是正确的帧指针?

  1. 如果参数是当前线程,例如 mach_thread_self(),fp 始终为 0,与 ; 不同__builtin_frame_address(0)

  2. 如果参数不是当前线程,例如主线程,指向前一帧指针的指针fp会在两到三帧内为NULL,对于普通的栈帧链表来说太短了;

  3. 仍然不是当前线程,我打印出backtrace之前在主线程上使用的调用堆栈地址sleep。然后在另一个线程上,我挂起主线程并读取thread_get_state用于遍历调用堆栈的帧指针,两个回溯缓冲区完全不同;

什么让我困惑

Apple Doc帧指针寄存器 (x29) 必须始终寻址有效的帧记录,但我可以从中读取值。

此外,ARM Doc状态在过程调用标准的所有变体中,寄存器 r16、r17、r29 和 r30 具有特殊的作用。在这些角色中,当用于保存地址时,它们被标记为 IP0、IP1、FP 和 LR

下面是_STRUCT_MCONTEXT's 部分值的示例:

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 = {

我在找什么

我现在正在寻找一种方法来获取Frame Pointer任意线程的正确值。

谢谢你的帮助!

更新 1

当然,我想要的是获取任意线程的叶子栈帧的正确性,Frame Pointer然后沿着Previous Pointer.Frame pointer

在此之前,我已经阅读了这些链接:

获取其他线程的回溯

如何在 iPad 应用程序中循环遍历所有活动线程

从另一个线程打印堆栈跟踪

再次感谢。

更新 2

我在其他平台上尝试过,但结果相同:错误的帧指针

- (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
}

返回的值framePointerOfMachineContext__builtin_frame_address(0).

更新 3

受一位同事的启发,我尝试了 inline asm 并在 i386 上实现了:

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

uintptr_t builtinFP = (uintptr_t)__builtin_frame_address(0);

现在ebp变量与builtinFP's 保持相同的值。但是如何在任意线程上做到这一点?

4

1 回答 1

6

终于发现问题了

- (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很好,但参数应该是特定于体系结构的,例如x86_THREAD_STATE32,x86_THREAD_STATE64等等。

我误解了宏MACHINE_THREAD_STATE,它给了我不同架构的普遍意义。

于 2015-10-22T03:31:16.053 回答