Способ получить указатель кадра
В демонстрационном приложении, работающем на устройстве iPhone 5s / 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 (ARMv6 и ARM64),
Регистр R7 используется в качестве указателя кадра на ARMv6.
а x29 на ARM64
Регистр указателя фрейма (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
) не является правильным указателем кадра?
Если параметром является текущий поток, например, mach_thread_self(), fp всегда равен 0, что отличается от
__builtin_frame_address(0)
;Если параметр не является текущим потоком, например, основным потоком, указатель на предыдущий указатель кадра
fp
будет NULL в двух или трех кадрах, что слишком мало для обычного списка ссылок кадров стека;Все еще не текущий поток, я распечатываю адреса стека вызовов, используя
backtrace
в основном потоке доsleep
. Затем в другом потоке я приостанавливаю основной поток и читаю указатель кадра, используяthread_get_state
для обхода стека вызовов, два буфера обратной трассировки совершенно разные;
Что меня смущает
Apple Doc говорит Регистр указателя кадра (x29) всегда должен адресовать действительную запись кадра, но я мог прочитать из него значение ZERO.
Кроме того, в ARM Doc
указано В во всех вариантах вызова процедуры стандартные регистры r16, r17, r29 и r30 имеют специальные роли. В этих ролях они помечаются как IP0, IP1, FP и LR, когда используются для хранения адресов.
Ниже приведен пример части значения _STRUCT_MCONTEXT
:
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
Вдохновленный коллегой, я попробовал встроенный ассемблер и сделал это на i386:
uintptr_t ebp = 1;
__asm__ ("mov %%ebp, %0;"
:"=r"(ebp));
uintptr_t builtinFP = (uintptr_t)__builtin_frame_address(0);
Теперь переменная ebp
содержит то же значение, что и builtinFP
. Но КАК сделать это на произвольном потоке?