1

ART でDexposedをテストしていたところ、art::ReferenceMapVisitor::VisitQuickFrame() でランダムにクラッシュしました。dexposed がスタック フレームを正しく処理しない可能性があると思います。

     .extern artQuickDexposedInvokeHandler
ENTRY art_quick_dexposed_invoke_handler
    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
    str     r0, [sp, #0]           @ place proxy method at bottom of frame
    mov     r2, r9                 @ pass Thread::Current
    mov     r3, sp                 @ pass SP
    blx     artQuickDexposedInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
    ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
    add     sp, #16                @ skip r1-r3, 4 bytes padding.
    .cfi_adjust_cfa_offset -16
    cbnz    r2, 1f                 @ success if no exception is pending
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    bx      lr                     @ return on success
1:
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    DELIVER_PENDING_EXCEPTION
END art_quick_dexposed_invoke_handler

hookMethodNative で使用されます。

static void com_taobao_android_dexposed_DexposedBridge_hookMethodNative(JNIEnv *env, jclass, jobject java_method......) {
    ArtMethod *art_method = ArtMethod::FromReflectedMethod(soa, java_method);
    ......
    art_method->SetEntryPointFromQuickCompiledCode((void *) art_quick_dexposed_invoke_handler);
    art_method->SetAccessFlags((art_method->GetAccessFlags() & ~kAccNative));
}

関数 VisitQuickFrame は にあり、から取得した map.RegWidth()art/runtime/thread.ccにアクセスするとクラッシュしましたmap.data_[1]ArtMethod::GetEntryPointFromQuickCompiledCode

private:
void VisitQuickFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
  StackReference<mirror::ArtMethod>* cur_quick_frame = GetCurrentQuickFrame();
  mirror::ArtMethod* m = cur_quick_frame->AsMirrorPtr();
  mirror::ArtMethod* old_method = m;
  visitor_(reinterpret_cast<mirror::Object**>(&m), 0 /*ignored*/, this);
  if (m != old_method) {
    cur_quick_frame->Assign(m);
  }

  // Process register map (which native and runtime methods don't have)
  if (!m->IsNative() && !m->IsRuntimeMethod() && !m->IsProxyMethod()) {
    const uint8_t* native_gc_map = m->GetNativeGcMap(sizeof(void*));
    CHECK(native_gc_map != nullptr) << PrettyMethod(m);
    const DexFile::CodeItem* code_item = m->GetCodeItem();
    DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be nullptr or how would we compile its instructions?
    NativePcOffsetToReferenceMap map(native_gc_map);
    size_t num_regs = std::min(map.RegWidth() * 8,
                               static_cast<size_t>(code_item->registers_size_));
    if (num_regs > 0) {
  ......

上記のアセンブル コードは からコピーされart/runtime/arch/arm/quick_entrypoints_arm.Sます。

    /*
     * Called by managed code that is attempting to call a method on a proxy class. On entry
     * r0 holds the proxy method and r1 holds the receiver; r2 and r3 may contain arguments. The
     * frame size of the invoked proxy method agrees with a ref and args callee save frame.
     */
     .extern artQuickProxyInvokeHandler
ENTRY art_quick_proxy_invoke_handler
    SETUP_REF_AND_ARGS_CALLEE_SAVE_FRAME
    str     r0, [sp, #0]           @ place proxy method at bottom of frame
    mov     r2, r9                 @ pass Thread::Current
    mov     r3, sp                 @ pass SP
    blx     artQuickProxyInvokeHandler  @ (Method* proxy method, receiver, Thread*, SP)
    ldr     r2, [r9, #THREAD_EXCEPTION_OFFSET]  @ load Thread::Current()->exception_
    add     sp, #16                @ skip r1-r3, 4 bytes padding.
    .cfi_adjust_cfa_offset -16
    cbnz    r2, 1f                 @ success if no exception is pending
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    bx      lr                     @ return on success
1:
    RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
    DELIVER_PENDING_EXCEPTION
END art_quick_proxy_invoke_handler

スタック フレームを正しく保存/復元しますか?

クラッシュ バックトレース:

*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/hammerhead/hammerhead:5.1.1/LMY48I/2074855:user/release-keys'
Revision: '11'
ABI: 'arm'
pid: 16711, tid: 16727, name: Binder_1  >>> joker.li.dexposeltest <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0xd101755d
    r0 b487a600  r1 71db5d14  r2 71db5d14  r3 711e23a8
    r4 b3749ba8  r5 d101755c  r6 b47faa70  r7 001c1448
    r8 b3749a00  r9 c52d968f  sl b47f83e8  fp b4623ad1
    ip fffffaa8  sp b37499b8  lr 0000fff8  pc b4732696  cpsr a0070030
    d0  0000000000000000  d1  0000000000000000
    d2  0000000000000000  d3  0000000000000000
    d4  b4a23400b4a24800  d5  b4a25800b4a24c00
    d6  b4a27400aec3b400  d7  b4a2a800b4a28800
    d8  0000000000001a50  d9  0000000000000000
    d10 0000000000000000  d11 0000000000000000
    d12 0000000000000000  d13 0000000000000000
    d14 0000000000000000  d15 0000000000000000
    d16 be9b62b000000000  d17 0000000000004000
    d18 0000000000004000  d19 0000000000000000
    d20 00000000c8baf094  d21 00000000003d3dcb
    d22 70f7634070f76340  d23 0000000004352858
    d24 0000000000014d1e  d25 000000000000039d
    d26 00000000000150bb  d27 00000000000d0e49
    d28 00000000044236a1  d29 0000000000004000
    d30 0000000000000001  d31 0000000000000000
    scr 80000011

backtrace:
    #00 pc 0023d696  /system/lib/libart.so (art::ReferenceMapVisitor<art::RootCallbackVisitor>::VisitQuickFrame()+333)
    #01 pc 0023dcb9  /system/lib/libart.so (art::ReferenceMapVisitor<art::RootCallbackVisitor>::VisitFrame()+224)
    #02 pc 00231959  /system/lib/libart.so (art::StackVisitor::WalkStack(bool)+276)
    #03 pc 002336b3  /system/lib/libart.so (art::Thread::VisitRoots(void (*)(art::mirror::Object**, void*, art::RootInfo const&), void*)+994)
    #04 pc 0012db67  /system/lib/libart.so (art::gc::collector::CheckpointMarkThreadRoots::Run(art::Thread*)+126)
    #05 pc 00240455  /system/lib/libart.so (art::ThreadList::RunCheckpoint(art::Closure*)+296)
    #06 pc 0012c7b1  /system/lib/libart.so (art::gc::collector::MarkSweep::MarkRootsCheckpoint(art::Thread*, bool)+96)
    #07 pc 0013021d  /system/lib/libart.so (art::gc::collector::MarkSweep::PreCleanCards()+172)
    #08 pc 00130393  /system/lib/libart.so (art::gc::collector::MarkSweep::MarkingPhase()+126)
    #09 pc 00130479  /system/lib/libart.so (art::gc::collector::MarkSweep::RunPhases()+176)
    #10 pc 00127067  /system/lib/libart.so (art::gc::collector::GarbageCollector::Run(art::gc::GcCause, bool)+246)
    #11 pc 001460af  /system/lib/libart.so (art::gc::Heap::CollectGarbageInternal(art::gc::collector::GcType, art::gc::GcCause, bool)+1406)
    #12 pc 00201357  /system/lib/libart.so (art::VMDebug_countInstancesOfClass(_JNIEnv*, _jclass*, _jclass*, unsigned char)+294)
    #13 pc 0001a3ed  /data/dalvik-cache/arm/system@framework@boot.oat
4

1 に答える 1

0

1、アートフックはアートのスタックレイアウトを壊します:

class ArtMethod{

  ...

  // Total size in bytes of the frame

  size_t frame_size_in_bytes_;

  ...

}

「frame_size_in_bytes は、Quick フレームに対応するフレームの一部です。つまり、ArtMethod* ストレージから次の Quick フレームまでのフレームのサイズです。内部的に、JNI フレームはそのアドレスで登録され、フレームはsize はスタック ウォーキングで使用されます。」--- https://code.google.com/p/android/issues/detail?id=79204

そのため、ランタイムはスタックで間違った ArtMethod ポインターを取得します。

2、StackVisitor::WalkStack() は、コンパイルされたコードが dex に対応するオペコードを持っているかどうかをチェックします。

アートフックはこれらを修正するか、WalkStack() を呼び出さないようにする必要があります。

于 2016-05-23T05:19:36.923 に答える