4

フレームポインタを取得する方法

ここに画像の説明を入力

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 でフレーム ポインタとして使用されます。

一方、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 */
};

フレームポインタが間違っていることを証明する方法

machineContext.__ss.__fpfp ( ) が正しいフレーム ポインタではないことを証明するにはどうすればよいですか?

  1. パラメータが現在のスレッド、たとえば mach_thread_self() の場合、fp は常に 0__builtin_frame_address(0)であり、これは;とは異なります。

  2. パラメーターが現在のスレッド (メイン スレッドなど) でない場合、前のフレーム ポインターへのポインターはfp、2 つまたは 3 つのフレームで NULL になります。これは、スタック フレームの通常のリンク リストには短すぎます。

  3. まだ現在のスレッドではありませんが、backtraceメイン スレッドの前に使用してコール スタック アドレスを出力しますsleep。次に、別のスレッドで、メイン スレッドを中断しthread_get_state、コール スタックをウォークするために使用するフレーム ポインターを読み取ります。2 つのバックトレース バッファーはまったく異なります。

何が私を混乱させますか

Apple Docによると、 The frame pointer register (x29) must always address a valid frame record ですが、そこから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

同僚に触発されて、インライン asm を試し、i386 で作成しました。

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

uintptr_t builtinFP = (uintptr_t)__builtin_frame_address(0);

ebp変数は と同じ値を保持するようになりましたbuiltinFP。しかし、任意のスレッドでこれを行う方法は?

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 に答える