diff --git a/lib/android.js b/lib/android.js index 11daebe..a87ec27 100644 --- a/lib/android.js +++ b/lib/android.js @@ -1187,7 +1187,11 @@ function _getArtMethodSpec (vm) { const entrypointFieldSize = (apiLevel <= 21) ? 8 : pointerSize; const expectedAccessFlags = kAccPublic | kAccStatic | kAccFinal | kAccNative; - const relevantAccessFlagsMask = ~(kAccFastInterpreterToInterpreterInvoke | kAccPublicApi | kAccNterpInvokeFastPathFlag) >>> 0; + // kAccNterpEntryPointFastPathFlag must be masked off too: on Android 17 the + // probe method (Process.getElapsedCpuTime) carries it, so leaving it set makes + // the offset-4 access_flags fail to match, and the scan latches onto the next + // ArtMethod's flags one stride over, corrupting every method's flag offset. + const relevantAccessFlagsMask = ~(kAccFastInterpreterToInterpreterInvoke | kAccPublicApi | kAccNterpInvokeFastPathFlag | kAccNterpEntryPointFastPathFlag) >>> 0; let jniCodeOffset = null; let accessFlagsOffset = null; @@ -4866,15 +4870,23 @@ function recompileExceptionClearForArm64 (buffer, pc, exceptionClearImpl, nextFu const writer = new Arm64Writer(buffer, { pc }); - writer.putBLabel('performTransition'); - + // Emit the invokeCallback subroutine first and flush the literal pool right + // after it. putCallAddressWithArguments() loads `callback` through a + // PC-relative literal whose pool entry has only ±1 MiB of reach. The relocated + // ExceptionClear body that follows can exceed that on Android 17 (ExceptionClear + // and the next function sit ~2 MiB apart), so deferring the pool to dispose() + // leaves the load as an unresolved `ldr xN, #0` that reads its own bytes and + // branches to garbage (PC-alignment SIGBUS). Flushing here keeps the pool in + // range; it sits after a ret, so it is never executed, and the transition body + // that follows becomes the entrypoint. const invokeCallback = pc.add(writer.offset); writer.putPushAllXRegisters(); writer.putCallAddressWithArguments(callback, ['x0']); writer.putPopAllXRegisters(); writer.putRet(); + writer.flush(); - writer.putLabel('performTransition'); + const performEntry = pc.add(writer.offset); let foundCore = false; let threadReg = null; @@ -5004,7 +5016,7 @@ function recompileExceptionClearForArm64 (buffer, pc, exceptionClearImpl, nextFu throwThreadStateTransitionParseError(); } - return new NativeFunction(pc, 'void', ['pointer'], nativeFunctionOptions); + return new NativeFunction(performEntry, 'void', ['pointer'], nativeFunctionOptions); } function throwThreadStateTransitionParseError () {