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?
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)
;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;Still not the current thread, I print out the call stack addresses using
backtrace
on main thread beforesleep
. Then on another thread, I suspend the main thread and read the frame pointer usingthread_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?
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