如何获得iOS中任意线程的正确帧指针? [英] HOWTO get the correct Frame Pointer of an arbitrary thread in iOS?
问题描述
在运行于iPhone 5s Device/Xcode 7的演示应用程序上,我尝试使用
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;
}
根据Apple Doc( ARM64 ),
I read the frame pointer like this: uintptr_t fp = machineContext.__ss.__fp;
, according to Apple Doc (ARMv6 and ARM64),
寄存器R7用作ARMv6上的帧指针
Register R7 is used as a frame pointer on ARMv6
在ARM64上为x29时
while x29 on ARM64
帧指针寄存器(x29)必须始终寻址有效的帧记录,尽管某些功能(例如叶函数或尾部调用)可能选择不在此列表中创建条目.结果,即使没有调试信息,堆栈跟踪也将始终有意义.
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 */
};
如何证明帧指针是错误的
如何证明fp(machineContext.__ss.__fp
)不是正确的帧指针?
Way to prove the Frame Pointer is wrong
How do I prove that the fp (machineContext.__ss.__fp
) is not a correct frame pointer?
-
如果参数是当前线程,例如mach_thread_self(),则 fp始终为0 ,与
__builtin_frame_address(0)
;
如果参数不是当前线程(例如主线程),则指向fp
的前一帧指针的指针在两三个帧中将为NULL,这对于常规链接列表而言太短了堆栈帧数;
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;
仍然不是当前线程,我在sleep
之前在主线程上使用backtrace
打印出调用堆栈地址.然后在另一个线程上,我挂起主线程,并使用thread_get_state
读取帧指针以遍历调用堆栈,这两个回溯缓冲区完全不同;
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.
此外, ARM Doc
状态在过程调用标准的所有变体中,寄存器r16,r17,r29和r30都具有特殊作用.在这些角色中,他们
在用于保存地址时被标记为IP0,IP1,FP和LR .
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.
以下是_STRUCT_MCONTEXT
值的一部分的示例:
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 = {
我在寻找什么
我正在寻找一种方法来获取任意线程的Frame Pointer
的正确值.
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.
感谢您的帮助!
当然,我想要的是获取任意线程的叶堆栈框架的正确Frame Pointer
,然后沿着Frame pointer
的Previous Pointer
遍历调用堆栈.
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:
再次感谢.
我在其他平台上尝试过,但结果相同:错误的帧指针.
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
}
framePointerOfMachineContext
返回的值与__builtin_frame_address(0)
不同.
受同事的启发,我尝试了内联汇编并将其安装到i386上:
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);
现在,ebp
变量的值与builtinFP
相同.但是,如何在任意线程上执行此操作?
Now ebp
variable holds the same value to builtinFP
's. But HOWTO do this on an arbitrary thread?
推荐答案
我最终发现了问题,
- (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
等.
thread_get_state
is fine, but the parameter should be architecture-specific, such as x86_THREAD_STATE32
, x86_THREAD_STATE64
and etc.
我误解了宏MACHINE_THREAD_STATE
,它赋予了我不同体系结构的通用含义.
I misunderstood the macro MACHINE_THREAD_STATE
, which gave me an universal meaning for different architectures.
这篇关于如何获得iOS中任意线程的正确帧指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!