preface
I saw an article before java reflection calls private related
A screenshot of the boss inside shows why the generated method accessor generated at runtime can access all methods
Hehe, to tell you the truth, this problem has not been considered before
However, the screenshot of the boss is still a little abstract and is not connected with the context of the specific runtime
Hehe, this article will sort out these things
This article mainly takes reflection call} as an example to walk through the general process
There are some basic articles about recurse that can be referred to
21 process of method invocation (invokestatic as an example)
14 debugging of compilation and execution of HelloWorld bytecode
15 stack frame information of main method
07 rewrite of runtime constant pool index
The following code, screenshot based on jdk9
test case
package com.hx.test05; import com.hx.test03.Test26MethodOrder; import java.lang.reflect.Method; /** * GenerateMethodAccessor * * @author Jerry.X.He <970655147@qq.com> * @version 1.0 * @date 2020-04-25 19:17 */ public class Test16GenerateMethodAccessor { // Test16GenerateMethodAccessor public static void main(String[] args) throws Exception { Method method = Test26MethodOrder.class.getDeclaredMethod("func008"); method.setAccessible(true); for(int i=0; i<=16; i++) { method.invoke(null); } } }
Test26MethodOrder.func008 is as follows
// funcN private static int counter = 0; private static void func008() { System.out.println(counter++); if((counter == 16) || (counter == 17)) { Test25SynchronizeObject.doClone(new Test25SynchronizeObject()); } }
clion based debugging
First, in Reflection::verify_class_access, Reflection::verify_ field_ Make a breakpoint in access
In JVM cpp JVM_ Put a breakpoint on clone
1. Then there will be hit with two or three breakpoints, which has been mentioned here
These two checks_ klass_ Accessibility mainly comes from {nativemethodaccessorimpl Invoke accesses these two classes, and then triggers class loading, verification, and so on. These two checks_ klass_ Accessibility is not that important for this article
p ((Method*)0x010f1c2f78)->print() {method} - this oop: 0x000000010f1c2f78 - method holder: 'jdk/internal/reflect/NativeMethodAccessorImpl' - constants: 0x000000010f1c2a18 constant pool [97] {0x000000010f1c2a18} for 'jdk/internal/reflect/NativeMethodAccessorImpl' cache=0x000000010f1c3170 - access: 0x1 public - name: 'invoke' - signature: '(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;' - max stack: 8 - max locals: 4 - size of params: 3 - method size: 11 - vtable index: 5 - i2i entry: 0x000000011302b700 - adapters: AHE@0x00007fa181041690: 0xbbb00000 i2c: 0x00000001131d37e0 c2i: 0x00000001131d3922 c2iUV: 0x00000001131d38f5 - compiled entry 0x00000001131d3922 - code size: 104 - code start: 0x000000010f1c2ec0 - code end (excl): 0x000000010f1c2f28 - checked ex length: 2 - checked ex start: 0x000000010f1c2f72 - linenumber start: 0x000000010f1c2f28 - localvar length: 4 - localvar start: 0x000000010f1c2f40
2. Another verification comes from verifying whether the parent class can be accessed when loading after the GeneratedMethodAccessor1 is created
3. Then there are two more verifications. When creating the GeneratedMethodAccessor1 instance, you need to access the parent class and its constructor
4. Then the breakpoint comes to the} JVM_ Inside clone
This is test26methodorder Counter in func008 = = 16 breakpoint entered by condition
p obj()->print() com.hx.test04.Test25SynchronizeObject {0x00000007bfb6f6b8} - klass: 'com/hx/test04/Test25SynchronizeObject' - ---- fields (total size 5 words): - 'f01' 'I' @12 0 - 'f02' 'I' @16 0 - 'f03' 'I' @20 0 - 'f04' 'I' @24 0 - 'f05' 'I' @28 0 - private 'identStr' 'Ljava/lang/String;' @32 "xyz"{0x00000007bfb6f6e0} (f7f6dedc 0)
5. Then, generate methodaccessor1 needs to access test26methodorder Relevant verification of func008
These two verify_ class_ access, verify_ field_ One is to ensure access to Test26MethodOrder, and the other is to ensure access to Test26MethodOrder func008
This corresponds to 21 process of method invocation (invokestatic as an example) Load relevant classes and methods, replace the symbolic reference in the constant pool with a direct reference, and some preparation work related to calling methods
6. The last breakpoint is test26methodorder counter == 17 in func008 = breakpoint of condition entry
p obj()->print() com.hx.test04.Test25SynchronizeObject {0x00000007bfb6f7d8} - klass: 'com/hx/test04/Test25SynchronizeObject' - ---- fields (total size 5 words): - 'f01' 'I' @12 0 - 'f02' 'I' @16 0 - 'f03' 'I' @20 0 - 'f04' 'I' @24 0 - 'f05' 'I' @28 0 - private 'identStr' 'Ljava/lang/String;' @32 "xyz"{0x00000007bfb6f6e0} (f7f6dedc 0)
Take a look at the breakpoints above
The first four breakpoints are in the related business processing of {NativeMethodAccessorImpl
The last two breakpoints are triggered in the generated method accessor1
Why can {MagicAccessorImpl access all classes and methods
LinkResolver::resolve_ index in invoke?
As shown in the figure below, the # generated methodaccessor1 accesses # test26methodorder Analysis triggered when func008
Hehe, shit, isn't invokestatic followed by a MethodRef index?, Why is it so big?
Where the index is obtained, you can see that the original index should be 0, but the actual pass through is 65536
About CP_CACHE_INDEX_TAG is defined as follows
When debugging in the program, CPCACHE_INDEX_TAG shows 0, but the real use is 0x10000. Hehe, I don't know if it's a bug
Then, if such a large index is passed, how to calculate the index in the constant pool corresponding to the operand in the future?
Attach the constant pool information of # generated methodaccessor1
{constant pool} - holder: 0x00000007c0098430 - cache: 0x000000011200e5c8 - resolved_references: 0x0000000000000000 - reference_map: 0x0000000000000000 - 1 : Utf8 : 'jdk/internal/reflect/GeneratedMethodAccessor1' - 2 : Unresolved Class : 'jdk/internal/reflect/GeneratedMethodAccessor1' - 3 : Utf8 : 'jdk/internal/reflect/MethodAccessorImpl' - 4 : Class : 'jdk/internal/reflect/MethodAccessorImpl' {0x00000007c0009b18} - 5 : Utf8 : 'com/hx/test03/Test26MethodOrder' - 6 : Unresolved Class : 'com/hx/test03/Test26MethodOrder' - 7 : Utf8 : 'func008' - 8 : Utf8 : '()V' - 9 : NameAndType : name_index=7 signature_index=8 - 10 : Method : klass_index=6 name_and_type_index=9 - 11 : Utf8 : 'invoke' - 12 : Utf8 : '(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;' - 13 : Utf8 : 'java/lang/Throwable' - 14 : Unresolved Class : 'java/lang/Throwable' - 15 : Utf8 : 'java/lang/ClassCastException' - 16 : Unresolved Class : 'java/lang/ClassCastException' - 17 : Utf8 : 'java/lang/NullPointerException' - 18 : Unresolved Class : 'java/lang/NullPointerException' - 19 : Utf8 : 'java/lang/IllegalArgumentException' - 20 : Unresolved Class : 'java/lang/IllegalArgumentException' - 21 : Utf8 : 'java/lang/reflect/InvocationTargetException' - 22 : Unresolved Class : 'java/lang/reflect/InvocationTargetException' - 23 : Utf8 : '<init>' - 24 : Utf8 : '()V' - 25 : NameAndType : name_index=23 signature_index=24 - 26 : Method : klass_index=18 name_and_type_index=25 - 27 : Method : klass_index=20 name_and_type_index=25 - 28 : Utf8 : '(Ljava/lang/String;)V' - 29 : NameAndType : name_index=23 signature_index=28 - 30 : Method : klass_index=20 name_and_type_index=29 - 31 : Utf8 : '(Ljava/lang/Throwable;)V' - 32 : NameAndType : name_index=23 signature_index=31 - 33 : Method : klass_index=22 name_and_type_index=32 - 34 : Method : klass_index=4 name_and_type_index=25 - 35 : Utf8 : 'java/lang/Object' - 36 : Unresolved Class : 'java/lang/Object' - 37 : Utf8 : 'toString' - 38 : Utf8 : '()Ljava/lang/String;' - 39 : NameAndType : name_index=37 signature_index=38 - 40 : Method : klass_index=36 name_and_type_index=39 - 41 : Utf8 : 'Code' - 42 : Utf8 : 'Exceptions' - 43 : Utf8 : 'valueOf' - 44 : Utf8 : 'java/lang/Boolean' - 45 : Unresolved Class : 'java/lang/Boolean' - 46 : Utf8 : '(Z)Ljava/lang/Boolean;' - 47 : NameAndType : name_index=43 signature_index=46 - 48 : Method : klass_index=45 name_and_type_index=47 - 49 : Utf8 : 'booleanValue' - 50 : Utf8 : '()Z' - 51 : NameAndType : name_index=49 signature_index=50 - 52 : Method : klass_index=45 name_and_type_index=51 - 53 : Utf8 : 'java/lang/Byte' - 54 : Unresolved Class : 'java/lang/Byte' - 55 : Utf8 : '(B)Ljava/lang/Byte;' - 56 : NameAndType : name_index=43 signature_index=55 - 57 : Method : klass_index=54 name_and_type_index=56 - 58 : Utf8 : 'byteValue' - 59 : Utf8 : '()B' - 60 : NameAndType : name_index=58 signature_index=59 - 61 : Method : klass_index=54 name_and_type_index=60 - 62 : Utf8 : 'java/lang/Character' - 63 : Unresolved Class : 'java/lang/Character' - 64 : Utf8 : '(C)Ljava/lang/Character;' - 65 : NameAndType : name_index=43 signature_index=64 - 66 : Method : klass_index=63 name_and_type_index=65 - 67 : Utf8 : 'charValue' - 68 : Utf8 : '()C' - 69 : NameAndType : name_index=67 signature_index=68 - 70 : Method : klass_index=63 name_and_type_index=69 - 71 : Utf8 : 'java/lang/Double' - 72 : Unresolved Class : 'java/lang/Double' - 73 : Utf8 : '(D)Ljava/lang/Double;' - 74 : NameAndType : name_index=43 signature_index=73 - 75 : Method : klass_index=72 name_and_type_index=74 - 76 : Utf8 : 'doubleValue' - 77 : Utf8 : '()D' - 78 : NameAndType : name_index=76 signature_index=77 - 79 : Method : klass_index=72 name_and_type_index=78 - 80 : Utf8 : 'java/lang/Float' - 81 : Unresolved Class : 'java/lang/Float' - 82 : Utf8 : '(F)Ljava/lang/Float;' - 83 : NameAndType : name_index=43 signature_index=82 - 84 : Method : klass_index=81 name_and_type_index=83 - 85 : Utf8 : 'floatValue' - 86 : Utf8 : '()F' - 87 : NameAndType : name_index=85 signature_index=86 - 88 : Method : klass_index=81 name_and_type_index=87 - 89 : Utf8 : 'java/lang/Integer' - 90 : Unresolved Class : 'java/lang/Integer' - 91 : Utf8 : '(I)Ljava/lang/Integer;' - 92 : NameAndType : name_index=43 signature_index=91 - 93 : Method : klass_index=90 name_and_type_index=92 - 94 : Utf8 : 'intValue' - 95 : Utf8 : '()I' - 96 : NameAndType : name_index=94 signature_index=95 - 97 : Method : klass_index=90 name_and_type_index=96 - 98 : Utf8 : 'java/lang/Long' - 99 : Unresolved Class : 'java/lang/Long' - 100 : Utf8 : '(J)Ljava/lang/Long;' - 101 : NameAndType : name_index=43 signature_index=100 - 102 : Method : klass_index=99 name_and_type_index=101 - 103 : Utf8 : 'longValue' - 104 : Utf8 : '()J' - 105 : NameAndType : name_index=103 signature_index=104 - 106 : Method : klass_index=99 name_and_type_index=105 - 107 : Utf8 : 'java/lang/Short' - 108 : Unresolved Class : 'java/lang/Short' - 109 : Utf8 : '(S)Ljava/lang/Short;' - 110 : NameAndType : name_index=43 signature_index=109 - 111 : Method : klass_index=108 name_and_type_index=110 - 112 : Utf8 : 'shortValue' - 113 : Utf8 : '()S' - 114 : NameAndType : name_index=112 signature_index=113 - 115 : Method : klass_index=108 name_and_type_index=114
reference resources
java reflection calls private related
21 process of method invocation (invokestatic as an example)
14 debugging of compilation and execution of HelloWorld bytecode