frida-java-bridge 6.1.0 → 6.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +1 -1
- package/lib/android.js +97 -81
- package/lib/jvm.js +148 -43
- package/lib/machine-code.js +22 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -233,7 +233,7 @@ class Runtime {
|
|
|
233
233
|
|
|
234
234
|
const visitClassLoaders = api['art::ClassLinker::VisitClassLoaders'];
|
|
235
235
|
if (visitClassLoaders === undefined) {
|
|
236
|
-
throw new Error('This API is only available on
|
|
236
|
+
throw new Error('This API is only available on Android >= 7.0');
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
const ClassLoader = factory.use('java.lang.ClassLoader');
|
package/lib/android.js
CHANGED
|
@@ -4,6 +4,7 @@ const {
|
|
|
4
4
|
jvmtiCapabilities,
|
|
5
5
|
EnvJvmti
|
|
6
6
|
} = require('./jvmti');
|
|
7
|
+
const { parseInstructionsAt } = require('./machine-code');
|
|
7
8
|
const memoize = require('./memoize');
|
|
8
9
|
const { checkJniResult, JNI_OK } = require('./result');
|
|
9
10
|
const VM = require('./vm');
|
|
@@ -661,25 +662,12 @@ const instrumentationOffsetParsers = {
|
|
|
661
662
|
};
|
|
662
663
|
|
|
663
664
|
function tryDetectInstrumentationOffset (api) {
|
|
664
|
-
|
|
665
|
-
if (
|
|
665
|
+
const impl = api['art::Runtime::DeoptimizeBootImage'];
|
|
666
|
+
if (impl === undefined) {
|
|
666
667
|
return null;
|
|
667
668
|
}
|
|
668
669
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
for (let i = 0; i !== 30; i++) {
|
|
672
|
-
const insn = Instruction.parse(cur);
|
|
673
|
-
|
|
674
|
-
const offset = tryParse(insn);
|
|
675
|
-
if (offset !== null) {
|
|
676
|
-
return offset;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
cur = insn.next;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
return null;
|
|
670
|
+
return parseInstructionsAt(impl, instrumentationOffsetParsers[Process.arch], { limit: 30 });
|
|
683
671
|
}
|
|
684
672
|
|
|
685
673
|
function parsex86InstrumentationOffset (insn) {
|
|
@@ -748,27 +736,17 @@ const jniIdsIndirectionOffsetParsers = {
|
|
|
748
736
|
};
|
|
749
737
|
|
|
750
738
|
function tryDetectJniIdsIndirectionOffset () {
|
|
751
|
-
|
|
752
|
-
if (
|
|
739
|
+
const impl = Module.findExportByName('libart.so', '_ZN3art7Runtime12SetJniIdTypeENS_9JniIdTypeE');
|
|
740
|
+
if (impl === null) {
|
|
753
741
|
return null;
|
|
754
742
|
}
|
|
755
743
|
|
|
756
|
-
const
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
for (let i = 0; i !== 20; i++) {
|
|
760
|
-
const insn = Instruction.parse(cur);
|
|
761
|
-
|
|
762
|
-
const offset = tryParse(insn, prevInsn);
|
|
763
|
-
if (offset !== null) {
|
|
764
|
-
return offset;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
cur = insn.next;
|
|
768
|
-
prevInsn = insn;
|
|
744
|
+
const offset = parseInstructionsAt(impl, jniIdsIndirectionOffsetParsers[Process.arch], { limit: 20 });
|
|
745
|
+
if (offset === null) {
|
|
746
|
+
throw new Error('Unable to determine Runtime.jni_ids_indirection_ offset');
|
|
769
747
|
}
|
|
770
748
|
|
|
771
|
-
|
|
749
|
+
return offset;
|
|
772
750
|
}
|
|
773
751
|
|
|
774
752
|
function parsex86JniIdsIndirectionOffset (insn) {
|
|
@@ -1979,31 +1957,29 @@ function validateGetOatQuickMethodHeaderInlinedMatchArm ({ address, size }) {
|
|
|
1979
1957
|
targetWhenRuntimeMethod = targetWhenFalse;
|
|
1980
1958
|
}
|
|
1981
1959
|
|
|
1982
|
-
|
|
1983
|
-
for (let i = 0; i !== 3; i++) {
|
|
1984
|
-
const insn = Instruction.parse(cursor);
|
|
1985
|
-
cursor = insn.next;
|
|
1960
|
+
return parseInstructionsAt(targetWhenRegularMethod.or(1), tryParse, { limit: 3 });
|
|
1986
1961
|
|
|
1962
|
+
function tryParse (insn) {
|
|
1987
1963
|
const { mnemonic } = insn;
|
|
1988
1964
|
if (!(mnemonic === 'ldr' || mnemonic === 'ldr.w')) {
|
|
1989
|
-
|
|
1965
|
+
return null;
|
|
1990
1966
|
}
|
|
1991
1967
|
|
|
1992
1968
|
const { base, disp } = insn.operands[1].value;
|
|
1993
|
-
if (base === methodReg && disp === 0x14) {
|
|
1994
|
-
return
|
|
1995
|
-
methodReg,
|
|
1996
|
-
scratchReg,
|
|
1997
|
-
target: {
|
|
1998
|
-
whenTrue: targetWhenTrue,
|
|
1999
|
-
whenRegularMethod: targetWhenRegularMethod,
|
|
2000
|
-
whenRuntimeMethod: targetWhenRuntimeMethod
|
|
2001
|
-
}
|
|
2002
|
-
};
|
|
1969
|
+
if (!(base === methodReg && disp === 0x14)) {
|
|
1970
|
+
return null;
|
|
2003
1971
|
}
|
|
2004
|
-
}
|
|
2005
1972
|
|
|
2006
|
-
|
|
1973
|
+
return {
|
|
1974
|
+
methodReg,
|
|
1975
|
+
scratchReg,
|
|
1976
|
+
target: {
|
|
1977
|
+
whenTrue: targetWhenTrue,
|
|
1978
|
+
whenRegularMethod: targetWhenRegularMethod,
|
|
1979
|
+
whenRuntimeMethod: targetWhenRuntimeMethod
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
2007
1983
|
}
|
|
2008
1984
|
|
|
2009
1985
|
function validateGetOatQuickMethodHeaderInlinedMatchArm64 ({ address, size }) {
|
|
@@ -2024,30 +2000,28 @@ function validateGetOatQuickMethodHeaderInlinedMatchArm64 ({ address, size }) {
|
|
|
2024
2000
|
targetWhenRuntimeMethod = targetWhenFalse;
|
|
2025
2001
|
}
|
|
2026
2002
|
|
|
2027
|
-
|
|
2028
|
-
for (let i = 0; i !== 3; i++) {
|
|
2029
|
-
const insn = Instruction.parse(cursor);
|
|
2030
|
-
cursor = insn.next;
|
|
2003
|
+
return parseInstructionsAt(targetWhenRegularMethod, tryParse, { limit: 3 });
|
|
2031
2004
|
|
|
2005
|
+
function tryParse (insn) {
|
|
2032
2006
|
if (insn.mnemonic !== 'ldr') {
|
|
2033
|
-
|
|
2007
|
+
return null;
|
|
2034
2008
|
}
|
|
2035
2009
|
|
|
2036
2010
|
const { base, disp } = insn.operands[1].value;
|
|
2037
|
-
if (base === methodReg && disp === 0x18) {
|
|
2038
|
-
return
|
|
2039
|
-
methodReg,
|
|
2040
|
-
scratchReg,
|
|
2041
|
-
target: {
|
|
2042
|
-
whenTrue: targetWhenTrue,
|
|
2043
|
-
whenRegularMethod: targetWhenRegularMethod,
|
|
2044
|
-
whenRuntimeMethod: targetWhenRuntimeMethod
|
|
2045
|
-
}
|
|
2046
|
-
};
|
|
2011
|
+
if (!(base === methodReg && disp === 0x18)) {
|
|
2012
|
+
return null;
|
|
2047
2013
|
}
|
|
2048
|
-
}
|
|
2049
2014
|
|
|
2050
|
-
|
|
2015
|
+
return {
|
|
2016
|
+
methodReg,
|
|
2017
|
+
scratchReg,
|
|
2018
|
+
target: {
|
|
2019
|
+
whenTrue: targetWhenTrue,
|
|
2020
|
+
whenRegularMethod: targetWhenRegularMethod,
|
|
2021
|
+
whenRuntimeMethod: targetWhenRuntimeMethod
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2051
2025
|
}
|
|
2052
2026
|
|
|
2053
2027
|
function maybeInstrumentGetOatQuickMethodHeaderInlineCopies () {
|
|
@@ -2088,6 +2062,10 @@ function maybeInstrumentGetOatQuickMethodHeaderInlineCopies () {
|
|
|
2088
2062
|
}
|
|
2089
2063
|
}
|
|
2090
2064
|
|
|
2065
|
+
if (impls.length === 0) {
|
|
2066
|
+
return false;
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2091
2069
|
impls.forEach(handler.instrument);
|
|
2092
2070
|
|
|
2093
2071
|
return true;
|
|
@@ -2808,7 +2786,7 @@ const artQuickCodeReplacementTrampolineWriters = {
|
|
|
2808
2786
|
arm64: writeArtQuickCodeReplacementTrampolineArm64
|
|
2809
2787
|
};
|
|
2810
2788
|
|
|
2811
|
-
function writeArtQuickCodeReplacementTrampolineIA32 (trampoline, target, redirectSize, vm) {
|
|
2789
|
+
function writeArtQuickCodeReplacementTrampolineIA32 (trampoline, target, redirectSize, constraints, vm) {
|
|
2812
2790
|
const threadOffsets = getArtThreadSpec(vm).offset;
|
|
2813
2791
|
const artMethodOffsets = getArtMethodSpec(vm).offset;
|
|
2814
2792
|
|
|
@@ -2871,7 +2849,7 @@ function writeArtQuickCodeReplacementTrampolineIA32 (trampoline, target, redirec
|
|
|
2871
2849
|
return offset;
|
|
2872
2850
|
}
|
|
2873
2851
|
|
|
2874
|
-
function writeArtQuickCodeReplacementTrampolineX64 (trampoline, target, redirectSize, vm) {
|
|
2852
|
+
function writeArtQuickCodeReplacementTrampolineX64 (trampoline, target, redirectSize, constraints, vm) {
|
|
2875
2853
|
const threadOffsets = getArtThreadSpec(vm).offset;
|
|
2876
2854
|
const artMethodOffsets = getArtMethodSpec(vm).offset;
|
|
2877
2855
|
|
|
@@ -2934,7 +2912,7 @@ function writeArtQuickCodeReplacementTrampolineX64 (trampoline, target, redirect
|
|
|
2934
2912
|
return offset;
|
|
2935
2913
|
}
|
|
2936
2914
|
|
|
2937
|
-
function writeArtQuickCodeReplacementTrampolineArm (trampoline, target, redirectSize, vm) {
|
|
2915
|
+
function writeArtQuickCodeReplacementTrampolineArm (trampoline, target, redirectSize, constraints, vm) {
|
|
2938
2916
|
const artMethodOffsets = getArtMethodSpec(vm).offset;
|
|
2939
2917
|
|
|
2940
2918
|
const targetAddress = target.and(THUMB_BIT_REMOVAL_MASK);
|
|
@@ -3021,7 +2999,7 @@ function writeArtQuickCodeReplacementTrampolineArm (trampoline, target, redirect
|
|
|
3021
2999
|
return offset;
|
|
3022
3000
|
}
|
|
3023
3001
|
|
|
3024
|
-
function writeArtQuickCodeReplacementTrampolineArm64 (trampoline, target, redirectSize, vm) {
|
|
3002
|
+
function writeArtQuickCodeReplacementTrampolineArm64 (trampoline, target, redirectSize, { availableScratchRegs }, vm) {
|
|
3025
3003
|
const artMethodOffsets = getArtMethodSpec(vm).offset;
|
|
3026
3004
|
|
|
3027
3005
|
let offset;
|
|
@@ -3090,8 +3068,9 @@ function writeArtQuickCodeReplacementTrampolineArm64 (trampoline, target, redire
|
|
|
3090
3068
|
relocator.writeAll();
|
|
3091
3069
|
|
|
3092
3070
|
if (!relocator.eoi) {
|
|
3093
|
-
|
|
3094
|
-
writer.
|
|
3071
|
+
const scratchReg = Array.from(availableScratchRegs)[0];
|
|
3072
|
+
writer.putLdrRegAddress(scratchReg, target.add(offset));
|
|
3073
|
+
writer.putBrReg(scratchReg);
|
|
3095
3074
|
}
|
|
3096
3075
|
|
|
3097
3076
|
writer.putLabel('invoke_replacement');
|
|
@@ -3168,7 +3147,7 @@ class ArtQuickCodeInterceptor {
|
|
|
3168
3147
|
this.overwrittenPrologueLength = 0;
|
|
3169
3148
|
}
|
|
3170
3149
|
|
|
3171
|
-
_canRelocateCode (relocationSize) {
|
|
3150
|
+
_canRelocateCode (relocationSize, constraints) {
|
|
3172
3151
|
const Writer = thunkWriters[Process.arch];
|
|
3173
3152
|
const Relocator = thunkRelocators[Process.arch];
|
|
3174
3153
|
|
|
@@ -3178,14 +3157,44 @@ class ArtQuickCodeInterceptor {
|
|
|
3178
3157
|
const relocator = new Relocator(quickCodeAddress, writer);
|
|
3179
3158
|
|
|
3180
3159
|
let offset;
|
|
3181
|
-
|
|
3182
|
-
|
|
3183
|
-
|
|
3160
|
+
if (Process.arch === 'arm64') {
|
|
3161
|
+
let availableScratchRegs = new Set(['x16', 'x17']);
|
|
3162
|
+
|
|
3163
|
+
do {
|
|
3164
|
+
const nextOffset = relocator.readOne();
|
|
3165
|
+
|
|
3166
|
+
const nextScratchRegs = new Set(availableScratchRegs);
|
|
3167
|
+
const { read, written } = relocator.input.regsAccessed;
|
|
3168
|
+
for (const regs of [read, written]) {
|
|
3169
|
+
for (const reg of regs) {
|
|
3170
|
+
let name;
|
|
3171
|
+
if (reg.startsWith('w')) {
|
|
3172
|
+
name = 'x' + reg.substring(1);
|
|
3173
|
+
} else {
|
|
3174
|
+
name = reg;
|
|
3175
|
+
}
|
|
3176
|
+
nextScratchRegs.delete(name);
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
if (nextScratchRegs.size === 0) {
|
|
3180
|
+
break;
|
|
3181
|
+
}
|
|
3182
|
+
|
|
3183
|
+
offset = nextOffset;
|
|
3184
|
+
availableScratchRegs = nextScratchRegs;
|
|
3185
|
+
} while (offset < relocationSize && !relocator.eoi);
|
|
3186
|
+
|
|
3187
|
+
constraints.availableScratchRegs = availableScratchRegs;
|
|
3188
|
+
} else {
|
|
3189
|
+
do {
|
|
3190
|
+
offset = relocator.readOne();
|
|
3191
|
+
} while (offset < relocationSize && !relocator.eoi);
|
|
3192
|
+
}
|
|
3184
3193
|
|
|
3185
3194
|
return offset >= relocationSize;
|
|
3186
3195
|
}
|
|
3187
3196
|
|
|
3188
|
-
|
|
3197
|
+
_allocateTrampoline () {
|
|
3189
3198
|
if (trampolineAllocator === null) {
|
|
3190
3199
|
const trampolineSize = (pointerSize === 4) ? 128 : 256;
|
|
3191
3200
|
trampolineAllocator = makeCodeAllocator(trampolineSize);
|
|
@@ -3195,7 +3204,8 @@ class ArtQuickCodeInterceptor {
|
|
|
3195
3204
|
|
|
3196
3205
|
let redirectSize, spec;
|
|
3197
3206
|
let alignment = 1;
|
|
3198
|
-
|
|
3207
|
+
const constraints = {};
|
|
3208
|
+
if (pointerSize === 4 || this._canRelocateCode(maxRedirectSize, constraints)) {
|
|
3199
3209
|
redirectSize = maxRedirectSize;
|
|
3200
3210
|
|
|
3201
3211
|
spec = {};
|
|
@@ -3215,6 +3225,8 @@ class ArtQuickCodeInterceptor {
|
|
|
3215
3225
|
|
|
3216
3226
|
this.redirectSize = redirectSize;
|
|
3217
3227
|
this.trampoline = trampolineAllocator.allocateSlice(spec, alignment);
|
|
3228
|
+
|
|
3229
|
+
return constraints;
|
|
3218
3230
|
}
|
|
3219
3231
|
|
|
3220
3232
|
_destroyTrampoline () {
|
|
@@ -3222,12 +3234,12 @@ class ArtQuickCodeInterceptor {
|
|
|
3222
3234
|
}
|
|
3223
3235
|
|
|
3224
3236
|
activate (vm) {
|
|
3225
|
-
this.
|
|
3237
|
+
const constraints = this._allocateTrampoline();
|
|
3226
3238
|
|
|
3227
3239
|
const { trampoline, quickCode, redirectSize } = this;
|
|
3228
3240
|
|
|
3229
3241
|
const writeTrampoline = artQuickCodeReplacementTrampolineWriters[Process.arch];
|
|
3230
|
-
const prologueLength = writeTrampoline(trampoline, quickCode, redirectSize, vm);
|
|
3242
|
+
const prologueLength = writeTrampoline(trampoline, quickCode, redirectSize, constraints, vm);
|
|
3231
3243
|
this.overwrittenPrologueLength = prologueLength;
|
|
3232
3244
|
|
|
3233
3245
|
this.overwrittenPrologue = Memory.dup(this.quickCodeAddress, prologueLength);
|
|
@@ -3559,6 +3571,10 @@ function deoptimizeEverything (vm, env) {
|
|
|
3559
3571
|
function deoptimizeBootImage (vm, env) {
|
|
3560
3572
|
const api = getApi();
|
|
3561
3573
|
|
|
3574
|
+
if (getAndroidApiLevel() < 26) {
|
|
3575
|
+
throw new Error('This API is only available on Android >= 8.0');
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3562
3578
|
withRunnableArtThread(vm, env, thread => {
|
|
3563
3579
|
api['art::Runtime::DeoptimizeBootImage'](api.artRuntime);
|
|
3564
3580
|
});
|
|
@@ -3568,7 +3584,7 @@ function requestDeoptimization (vm, env, kind, method) {
|
|
|
3568
3584
|
const api = getApi();
|
|
3569
3585
|
|
|
3570
3586
|
if (getAndroidApiLevel() < 24) {
|
|
3571
|
-
throw new Error('This API is only available on
|
|
3587
|
+
throw new Error('This API is only available on Android >= 7.0');
|
|
3572
3588
|
}
|
|
3573
3589
|
|
|
3574
3590
|
withRunnableArtThread(vm, env, thread => {
|
package/lib/jvm.js
CHANGED
|
@@ -3,6 +3,7 @@ const {
|
|
|
3
3
|
jvmtiCapabilities,
|
|
4
4
|
EnvJvmti
|
|
5
5
|
} = require('./jvmti');
|
|
6
|
+
const { parseInstructionsAt } = require('./machine-code');
|
|
6
7
|
const memoize = require('./memoize');
|
|
7
8
|
const { checkJniResult } = require('./result');
|
|
8
9
|
const VM = require('./vm');
|
|
@@ -22,6 +23,7 @@ const nativeFunctionOptions = {
|
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const getJvmMethodSpec = memoize(_getJvmMethodSpec);
|
|
26
|
+
const getJvmInstanceKlassSpec = memoize(_getJvmInstanceKlassSpec);
|
|
25
27
|
const getJvmThreadSpec = memoize(_getJvmThreadSpec);
|
|
26
28
|
|
|
27
29
|
let cachedApi = null;
|
|
@@ -57,6 +59,9 @@ function _getApi () {
|
|
|
57
59
|
_ZN6Method4sizeEb: ['Method::size', 'int', ['int']],
|
|
58
60
|
_ZN6Method19set_native_functionEPhb: ['Method::set_native_function', 'void', ['pointer', 'pointer', 'int']],
|
|
59
61
|
_ZN6Method21clear_native_functionEv: ['Method::clear_native_function', 'void', ['pointer']],
|
|
62
|
+
// JDK >= 17
|
|
63
|
+
_ZN6Method24restore_unshareable_infoEP10JavaThread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']],
|
|
64
|
+
// JDK < 17
|
|
60
65
|
_ZN6Method24restore_unshareable_infoEP6Thread: ['Method::restore_unshareable_info', 'void', ['pointer', 'pointer']],
|
|
61
66
|
_ZN6Method10jmethod_idEv: ['Method::jmethod_id', 'pointer', ['pointer']],
|
|
62
67
|
_ZN6Method10clear_codeEv: function (address) {
|
|
@@ -73,8 +78,6 @@ function _getApi () {
|
|
|
73
78
|
};
|
|
74
79
|
},
|
|
75
80
|
|
|
76
|
-
_ZNK5Klass15start_of_vtableEv: ['Klass::start_of_vtable', 'pointer', ['pointer']],
|
|
77
|
-
_ZNK13InstanceKlass6vtableEv: ['InstanceKlass::vtable', 'pointer', ['pointer']],
|
|
78
81
|
// JDK >= 13
|
|
79
82
|
_ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass: ['VM_RedefineClasses::mark_dependent_code', 'void', ['pointer', 'pointer']],
|
|
80
83
|
_ZN18VM_RedefineClasses20flush_dependent_codeEv: ['VM_RedefineClasses::flush_dependent_code', 'void', []],
|
|
@@ -157,6 +160,16 @@ function _getApi () {
|
|
|
157
160
|
_ZNK18VM_RedefineClasses14print_on_errorEP12outputStream: function (address) {
|
|
158
161
|
this.redefineClassesOnError = address;
|
|
159
162
|
},
|
|
163
|
+
|
|
164
|
+
// JDK >= 17
|
|
165
|
+
_ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread: function (address) {
|
|
166
|
+
this.createNewDefaultVtableIndices = address;
|
|
167
|
+
},
|
|
168
|
+
// JDK < 17
|
|
169
|
+
_ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread: function (address) {
|
|
170
|
+
this.createNewDefaultVtableIndices = address;
|
|
171
|
+
},
|
|
172
|
+
|
|
160
173
|
_ZN19Abstract_VM_Version19jre_release_versionEv: function (address) {
|
|
161
174
|
const getVersion = new NativeFunction(address, 'pointer', [], nativeFunctionOptions);
|
|
162
175
|
const versionS = getVersion().readCString();
|
|
@@ -167,6 +180,7 @@ function _getApi () {
|
|
|
167
180
|
: parseInt(versionS.slice(0, 2), 10);
|
|
168
181
|
this.versionS = versionS;
|
|
169
182
|
},
|
|
183
|
+
|
|
170
184
|
_ZN14NMethodSweeper11_traversalsE: function (address) {
|
|
171
185
|
this.traversals = address;
|
|
172
186
|
},
|
|
@@ -178,36 +192,41 @@ function _getApi () {
|
|
|
178
192
|
}
|
|
179
193
|
},
|
|
180
194
|
optionals: [
|
|
195
|
+
'_ZN6Method24restore_unshareable_infoEP10JavaThread',
|
|
196
|
+
'_ZN6Method24restore_unshareable_infoEP6Thread',
|
|
197
|
+
'_ZN6Method10clear_codeEv',
|
|
198
|
+
'_ZN6Method10clear_codeEb',
|
|
199
|
+
|
|
181
200
|
'_ZN18VM_RedefineClasses19mark_dependent_codeEP13InstanceKlass',
|
|
182
201
|
'_ZN18VM_RedefineClasses20flush_dependent_codeEv',
|
|
183
202
|
'_ZN18VM_RedefineClasses20flush_dependent_codeEP13InstanceKlassP6Thread',
|
|
184
203
|
'_ZN18VM_RedefineClasses20flush_dependent_codeE19instanceKlassHandleP6Thread',
|
|
185
204
|
|
|
186
|
-
'_ZNK5Klass15start_of_vtableEv',
|
|
187
|
-
'_ZNK13InstanceKlass6vtableEv',
|
|
188
|
-
|
|
189
205
|
'_ZN19ResolvedMethodTable21adjust_method_entriesEPb',
|
|
190
206
|
'_ZN15MemberNameTable21adjust_method_entriesEP13InstanceKlassPb',
|
|
191
207
|
|
|
192
208
|
'_ZN17ConstantPoolCache21adjust_method_entriesEPb',
|
|
193
209
|
'_ZN17ConstantPoolCache21adjust_method_entriesEP13InstanceKlassPb',
|
|
194
210
|
|
|
211
|
+
'_ZN20ClassLoaderDataGraph22clean_deallocate_listsEb',
|
|
212
|
+
|
|
213
|
+
'_ZN10JavaThread27thread_from_jni_environmentEP7JNIEnv_',
|
|
214
|
+
|
|
215
|
+
'_ZN14NMethodSweeper11force_sweepEv',
|
|
216
|
+
'_ZN14NMethodSweeper17sweep_in_progressEv',
|
|
217
|
+
|
|
195
218
|
'_ZN18VM_RedefineClasses14_the_class_oopE',
|
|
196
219
|
'_ZN18VM_RedefineClasses10_the_classE',
|
|
197
220
|
'_ZN18VM_RedefineClasses25AdjustCpoolCacheAndVtable8do_klassEP5Klass',
|
|
198
221
|
'_ZN18VM_RedefineClasses22AdjustAndCleanMetadata8do_klassEP5Klass',
|
|
199
|
-
|
|
200
222
|
'_ZN18VM_RedefineClassesD0Ev',
|
|
201
223
|
'_ZN18VM_RedefineClassesD1Ev',
|
|
202
224
|
'_ZNK18VM_RedefineClasses14print_on_errorEP12outputStream',
|
|
203
225
|
|
|
204
|
-
'
|
|
205
|
-
'
|
|
226
|
+
'_ZN13InstanceKlass33create_new_default_vtable_indicesEiP10JavaThread',
|
|
227
|
+
'_ZN13InstanceKlass33create_new_default_vtable_indicesEiP6Thread',
|
|
206
228
|
|
|
207
|
-
'
|
|
208
|
-
'_ZN14NMethodSweeper11force_sweepEv',
|
|
209
|
-
'_ZN14NMethodSweeper21_sweep_fractions_leftE',
|
|
210
|
-
'_ZN14NMethodSweeper17sweep_in_progressEv'
|
|
229
|
+
'_ZN14NMethodSweeper21_sweep_fractions_leftE'
|
|
211
230
|
]
|
|
212
231
|
}];
|
|
213
232
|
|
|
@@ -292,6 +311,10 @@ function _getApi () {
|
|
|
292
311
|
|
|
293
312
|
temporaryApi.jvmti = getEnvJvmti(temporaryApi);
|
|
294
313
|
|
|
314
|
+
if (temporaryApi['JavaThread::thread_from_jni_environment'] === undefined) {
|
|
315
|
+
temporaryApi['JavaThread::thread_from_jni_environment'] = makeThreadFromJniHelper(temporaryApi);
|
|
316
|
+
}
|
|
317
|
+
|
|
295
318
|
return temporaryApi;
|
|
296
319
|
}
|
|
297
320
|
|
|
@@ -315,6 +338,44 @@ function getEnvJvmti (api) {
|
|
|
315
338
|
return env;
|
|
316
339
|
}
|
|
317
340
|
|
|
341
|
+
const threadOffsetParsers = {
|
|
342
|
+
x64: parseX64ThreadOffset
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
function makeThreadFromJniHelper (api) {
|
|
346
|
+
let offset = null;
|
|
347
|
+
|
|
348
|
+
const tryParse = threadOffsetParsers[Process.arch];
|
|
349
|
+
if (tryParse !== undefined) {
|
|
350
|
+
const vm = new VM(api);
|
|
351
|
+
const findClassImpl = vm.perform(env => env.handle.readPointer().add(6 * pointerSize).readPointer());
|
|
352
|
+
offset = parseInstructionsAt(findClassImpl, tryParse, { limit: 10 });
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (offset === null) {
|
|
356
|
+
return () => {
|
|
357
|
+
throw new Error('Unable to make thread_from_jni_environment() helper for the current architecture');
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return env => {
|
|
362
|
+
return env.add(offset);
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function parseX64ThreadOffset (insn) {
|
|
367
|
+
if (insn.mnemonic !== 'lea') {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const { base, disp } = insn.operands[1].value;
|
|
372
|
+
if (!(base === 'rdi' && disp < 0)) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return disp;
|
|
377
|
+
}
|
|
378
|
+
|
|
318
379
|
function ensureClassInitialized (env, classRef) {
|
|
319
380
|
}
|
|
320
381
|
|
|
@@ -489,13 +550,15 @@ function withJvmThread (fn, fnPrologue, fnEpilogue) {
|
|
|
489
550
|
const doIt = new NativeCallback(fn, 'void', ['pointer']);
|
|
490
551
|
vtableDup.add(doItOffset).writePointer(doIt);
|
|
491
552
|
|
|
553
|
+
let prologue = null;
|
|
492
554
|
if (fnPrologue !== undefined) {
|
|
493
|
-
|
|
555
|
+
prologue = new NativeCallback(fnPrologue, 'int', ['pointer']);
|
|
494
556
|
vtableDup.add(prologueOffset).writePointer(prologue);
|
|
495
557
|
}
|
|
496
558
|
|
|
559
|
+
let epilogue = null;
|
|
497
560
|
if (fnEpilogue !== undefined) {
|
|
498
|
-
|
|
561
|
+
epilogue = new NativeCallback(fnEpilogue, 'void', ['pointer']);
|
|
499
562
|
vtableDup.add(epilogueOffset).writePointer(epilogue);
|
|
500
563
|
}
|
|
501
564
|
|
|
@@ -686,24 +749,19 @@ function readJvmMethod (method, constMethod, constMethodSize) {
|
|
|
686
749
|
const instanceKlass = constantPool.add(spec.constantPool.instanceKlassOffset).readPointer();
|
|
687
750
|
const cache = constantPool.add(spec.constantPool.cacheOffset).readPointer();
|
|
688
751
|
|
|
689
|
-
|
|
690
|
-
const klassVtable = api['InstanceKlass::vtable'](instanceKlass);
|
|
691
|
-
const vtableOffset = klassVtable.add(pointerSize).readS32();
|
|
692
|
-
const klassSpec = getJvmKlassSpec(vtableOffset);
|
|
693
|
-
spec.instanceKlass = klassSpec;
|
|
694
|
-
}
|
|
752
|
+
const instanceKlassSpec = getJvmInstanceKlassSpec();
|
|
695
753
|
|
|
696
|
-
const methods = instanceKlass.add(
|
|
754
|
+
const methods = instanceKlass.add(instanceKlassSpec.methodsOffset).readPointer();
|
|
697
755
|
const methodsCount = methods.readS32();
|
|
698
756
|
const methodsArray = methods.add(pointerSize);
|
|
699
757
|
const methodIndex = constMethod.add(spec.constMethod.methodIdnumOffset).readU16();
|
|
700
758
|
const vtableIndexPtr = method.add(spec.method.vtableIndexOffset);
|
|
701
759
|
const vtableIndex = vtableIndexPtr.readS32();
|
|
702
|
-
const vtable = instanceKlass.add(
|
|
703
|
-
const oopMapCache = instanceKlass.add(
|
|
760
|
+
const vtable = instanceKlass.add(instanceKlassSpec.vtableOffset);
|
|
761
|
+
const oopMapCache = instanceKlass.add(instanceKlassSpec.oopMapCacheOffset).readPointer();
|
|
704
762
|
|
|
705
763
|
const memberNames = (api.version >= 10)
|
|
706
|
-
? instanceKlass.add(
|
|
764
|
+
? instanceKlass.add(instanceKlassSpec.memberNamesOffset).readPointer()
|
|
707
765
|
: NULL;
|
|
708
766
|
|
|
709
767
|
return {
|
|
@@ -741,39 +799,48 @@ function revertJvmMethod (method) {
|
|
|
741
799
|
|
|
742
800
|
function _getJvmMethodSpec () {
|
|
743
801
|
const api = getApi();
|
|
802
|
+
const { version } = api;
|
|
744
803
|
|
|
745
|
-
|
|
804
|
+
let adapterHandlerLocation;
|
|
805
|
+
if (version >= 17) {
|
|
806
|
+
adapterHandlerLocation = 'method:early';
|
|
807
|
+
} else if (version >= 9 && version <= 16) {
|
|
808
|
+
adapterHandlerLocation = 'const-method';
|
|
809
|
+
} else {
|
|
810
|
+
adapterHandlerLocation = 'method:late';
|
|
811
|
+
}
|
|
746
812
|
|
|
747
813
|
const isNative = 1;
|
|
748
814
|
const methodSize = api['Method::size'](isNative) * pointerSize;
|
|
749
815
|
const constMethodOffset = pointerSize;
|
|
750
816
|
const methodDataOffset = 2 * pointerSize;
|
|
751
817
|
const methodCountersOffset = 3 * pointerSize;
|
|
752
|
-
const
|
|
818
|
+
const adapterInMethodEarlyOffset = 4 * pointerSize;
|
|
819
|
+
const adapterInMethodEarlySize = (adapterHandlerLocation === 'method:early') ? pointerSize : 0;
|
|
820
|
+
const accessFlagsOffset = adapterInMethodEarlyOffset + adapterInMethodEarlySize;
|
|
753
821
|
const vtableIndexOffset = accessFlagsOffset + 4;
|
|
754
|
-
const i2iEntryOffset = vtableIndexOffset + 4 +
|
|
822
|
+
const i2iEntryOffset = vtableIndexOffset + 4 + 8;
|
|
823
|
+
const adapterInMethodLateOffset = i2iEntryOffset + pointerSize;
|
|
824
|
+
const adapterInMethodOffset = (adapterInMethodEarlySize !== 0) ? adapterInMethodEarlyOffset : adapterInMethodLateOffset;
|
|
755
825
|
const nativeFunctionOffset = methodSize - 2 * pointerSize;
|
|
756
826
|
const signatureHandlerOffset = methodSize - pointerSize;
|
|
757
827
|
|
|
758
|
-
const constantPoolOffset =
|
|
759
|
-
const stackmapDataOffset =
|
|
760
|
-
const
|
|
828
|
+
const constantPoolOffset = 8;
|
|
829
|
+
const stackmapDataOffset = constantPoolOffset + pointerSize;
|
|
830
|
+
const adapterInConstMethodOffset = stackmapDataOffset + pointerSize;
|
|
831
|
+
const adapterInConstMethodSize = (adapterHandlerLocation === 'const-method') ? pointerSize : 0;
|
|
832
|
+
const constMethodSizeOffset = adapterInConstMethodOffset + adapterInConstMethodSize;
|
|
761
833
|
const methodIdnumOffset = constMethodSizeOffset + 0xe;
|
|
762
834
|
|
|
763
835
|
const cacheOffset = 2 * pointerSize;
|
|
764
836
|
const instanceKlassOffset = 3 * pointerSize;
|
|
765
|
-
let klassSpec;
|
|
766
|
-
if ('Klass::start_of_vtable' in api) {
|
|
767
|
-
const vtableOffset = api['Klass::start_of_vtable'](NULL).toInt32();
|
|
768
|
-
klassSpec = getJvmKlassSpec(vtableOffset);
|
|
769
|
-
}
|
|
770
837
|
|
|
771
|
-
const getAdapterPointer =
|
|
838
|
+
const getAdapterPointer = (adapterInConstMethodSize !== 0)
|
|
772
839
|
? function (method, constMethod) {
|
|
773
|
-
return constMethod.add(
|
|
840
|
+
return constMethod.add(adapterInConstMethodOffset);
|
|
774
841
|
}
|
|
775
842
|
: function (method, constMethod) {
|
|
776
|
-
return method.add(
|
|
843
|
+
return method.add(adapterInMethodOffset);
|
|
777
844
|
};
|
|
778
845
|
|
|
779
846
|
return {
|
|
@@ -798,15 +865,28 @@ function _getJvmMethodSpec () {
|
|
|
798
865
|
constantPool: {
|
|
799
866
|
cacheOffset,
|
|
800
867
|
instanceKlassOffset
|
|
801
|
-
}
|
|
802
|
-
instanceKlass: klassSpec
|
|
868
|
+
}
|
|
803
869
|
};
|
|
804
870
|
}
|
|
805
871
|
|
|
806
|
-
|
|
807
|
-
|
|
872
|
+
const vtableOffsetParsers = {
|
|
873
|
+
x64: parseX64VTableOffset
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
function _getJvmInstanceKlassSpec () {
|
|
877
|
+
const { version: jvmVersion, createNewDefaultVtableIndices } = getApi();
|
|
878
|
+
|
|
879
|
+
const tryParse = vtableOffsetParsers[Process.arch];
|
|
880
|
+
if (tryParse === undefined) {
|
|
881
|
+
throw new Error(`Missing vtable offset parser for ${Process.arch}`);
|
|
882
|
+
}
|
|
808
883
|
|
|
809
|
-
const
|
|
884
|
+
const vtableOffset = parseInstructionsAt(createNewDefaultVtableIndices, tryParse, { limit: 32 });
|
|
885
|
+
if (vtableOffset === null) {
|
|
886
|
+
throw new Error('Unable to deduce vtable offset');
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
const oopMultiplier = ((jvmVersion >= 10 && jvmVersion <= 11) || jvmVersion >= 15) ? 17 : 18;
|
|
810
890
|
|
|
811
891
|
const methodsOffset = vtableOffset - (7 * pointerSize);
|
|
812
892
|
const memberNamesOffset = vtableOffset - (17 * pointerSize);
|
|
@@ -820,6 +900,31 @@ function getJvmKlassSpec (vtableOffset) {
|
|
|
820
900
|
};
|
|
821
901
|
}
|
|
822
902
|
|
|
903
|
+
function parseX64VTableOffset (insn) {
|
|
904
|
+
if (insn.mnemonic !== 'mov') {
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const dst = insn.operands[0];
|
|
909
|
+
if (dst.type !== 'mem') {
|
|
910
|
+
return null;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
const { value: dstValue } = dst;
|
|
914
|
+
if (dstValue.scale !== 1) {
|
|
915
|
+
return null;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
const { disp } = dstValue;
|
|
919
|
+
if (disp < 0x100) {
|
|
920
|
+
return null;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
const defaultVtableIndicesOffset = disp;
|
|
924
|
+
|
|
925
|
+
return defaultVtableIndicesOffset + 16;
|
|
926
|
+
}
|
|
927
|
+
|
|
823
928
|
function deoptimizeEverything (vm, env) {
|
|
824
929
|
}
|
|
825
930
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
function parseInstructionsAt (address, tryParse, { limit }) {
|
|
2
|
+
let cursor = address;
|
|
3
|
+
let prevInsn = null;
|
|
4
|
+
|
|
5
|
+
for (let i = 0; i !== limit; i++) {
|
|
6
|
+
const insn = Instruction.parse(cursor);
|
|
7
|
+
|
|
8
|
+
const value = tryParse(insn, prevInsn);
|
|
9
|
+
if (value !== null) {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
cursor = insn.next;
|
|
14
|
+
prevInsn = insn;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
parseInstructionsAt
|
|
22
|
+
};
|