我在 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 在 中art/runtime/thread.cc
,在访问map.data_[1]
map.RegWidth() 时崩溃,从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