frida-java-bridge 6.1.0 → 6.1.1
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 +45 -67
- 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 () {
|
|
@@ -3559,6 +3533,10 @@ function deoptimizeEverything (vm, env) {
|
|
|
3559
3533
|
function deoptimizeBootImage (vm, env) {
|
|
3560
3534
|
const api = getApi();
|
|
3561
3535
|
|
|
3536
|
+
if (getAndroidApiLevel() < 26) {
|
|
3537
|
+
throw new Error('This API is only available on Android >= 8.0');
|
|
3538
|
+
}
|
|
3539
|
+
|
|
3562
3540
|
withRunnableArtThread(vm, env, thread => {
|
|
3563
3541
|
api['art::Runtime::DeoptimizeBootImage'](api.artRuntime);
|
|
3564
3542
|
});
|
|
@@ -3568,7 +3546,7 @@ function requestDeoptimization (vm, env, kind, method) {
|
|
|
3568
3546
|
const api = getApi();
|
|
3569
3547
|
|
|
3570
3548
|
if (getAndroidApiLevel() < 24) {
|
|
3571
|
-
throw new Error('This API is only available on
|
|
3549
|
+
throw new Error('This API is only available on Android >= 7.0');
|
|
3572
3550
|
}
|
|
3573
3551
|
|
|
3574
3552
|
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
|
+
};
|