| 1348 | } |
| 1349 | |
| 1350 | static uint64_t java_hook_call_original(int hook_index, uint64_t* saved_regs) { |
| 1351 | if (hook_index < 0 || hook_index >= g_java_hook_count) { |
| 1352 | LOGE("java_hook_call_original: Invalid hook index: %d", hook_index); |
| 1353 | return 0; |
| 1354 | } |
| 1355 | |
| 1356 | JavaHookInfo* hook = &g_java_hooks[hook_index]; |
| 1357 | |
| 1358 | LOGI("java_hook_call_original: hook #%d, was_nativized=%d, method_id=%p", |
| 1359 | hook_index, hook->was_nativized, hook->method_id); |
| 1360 | |
| 1361 | if (hook->skip_original) { |
| 1362 | LOGI(" Skipping original call (skip_original set by onEnter)"); |
| 1363 | return 0; |
| 1364 | } |
| 1365 | |
| 1366 | // Note: We cannot use JNI reflection (call_original_via_jni) from the trampoline context |
| 1367 | // because saved_regs values are raw ART pointers/compressed OOPs, not JNI references. |
| 1368 | // Converting them with raw_ptr_to_jni_ref fails because they're not valid mirror::Object*. |
| 1369 | // We always use the interpreter bridge path which handles ART internals correctly. |
| 1370 | // String capture is not supported in this path. |
| 1371 | const ArtMethodOffsets* offsets = get_art_method_offsets(); |
| 1372 | uint32_t* access_flags_ptr = (uint32_t*)((uintptr_t)hook->art_method + offsets->access_flags_offset); |
| 1373 | void** entry_point_ptr = (void**)((uintptr_t)hook->art_method + offsets->entry_point_offset); |
| 1374 | |
| 1375 | uint32_t current_flags = *access_flags_ptr; |
| 1376 | void* current_entry = *entry_point_ptr; |
| 1377 | |
| 1378 | void* page = (void*)((uintptr_t)access_flags_ptr & ~(PAGE_SIZE - 1)); |
| 1379 | if (mprotect(page, PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) { |
| 1380 | LOGE("java_hook_call_original: mprotect failed: %s", strerror(errno)); |
| 1381 | return 0; |
| 1382 | } |
| 1383 | |
| 1384 | *access_flags_ptr = hook->original_access_flags; |
| 1385 | *entry_point_ptr = hook->original_entry_point; |
| 1386 | __builtin___clear_cache((char*)access_flags_ptr, (char*)access_flags_ptr + 4); |
| 1387 | __builtin___clear_cache((char*)entry_point_ptr, (char*)entry_point_ptr + 8); |
| 1388 | |
| 1389 | LOGI(" Temporarily restored: flags 0x%x -> 0x%x, entry %p -> %p", |
| 1390 | current_flags, hook->original_access_flags, |
| 1391 | current_entry, hook->original_entry_point); |
| 1392 | |
| 1393 | uint64_t result = call_interpreter_bridge_asm(hook->original_entry_point, saved_regs); |
| 1394 | |
| 1395 | { |
| 1396 | JNIEnv* env = get_jni_env(); |
| 1397 | if (env && (*env)->ExceptionCheck(env)) { |
| 1398 | LOGI(" Clearing pending Java exception for hooked method"); |
| 1399 | (*env)->ExceptionClear(env); |
| 1400 | } |
| 1401 | } |
| 1402 | |
| 1403 | LOGI(" Original entry point returned: 0x%llx", (unsigned long long)result); |
| 1404 | |
| 1405 | *access_flags_ptr = current_flags; |
| 1406 | *entry_point_ptr = current_entry; |
| 1407 | __builtin___clear_cache((char*)access_flags_ptr, (char*)access_flags_ptr + 4); |
nothing calls this directly
no test coverage detected