Breaking free of the chains:
This class goes through a lengthy process to shatter the restraints of JPMS without requiring the end-user, launcher, or mod loader to inject any JVM args.
First, we get a reference to Unsafe.theUnsafe
via reflection, which is not yet locked down by
the ongoing encapsulation crusade.
Then, using this reference, we get the static field base and offset of MethodHandles.Lookup.IMPL_LOOKUP
.
As far as I am aware, this is the only instance of MethodHandles.Lookup
with an
allowedModes
value of TRUSTED
. We then
check the OOP pointer width and, equipped with these, use our reference to Unsafe.theUnsafe
to copy
the OOP pointer stored in the static field into a 1-element array on the LVT.
Now might be a good time to interject and point out how pointless it is for the JDK to hide
MethodHandles.Lookup.allowedModes
(see MethodHandles.java
line 1433:
Reflection.registerFieldsToFilter(Lookup.class, Set.of("lookupClass", "allowedModes"));
) from reflection
without also hiding MethodHandles.Lookup.IMPL_LOOKUP
.
Now equipped with an unrestricted MethodHandles.Lookup
, we get a MethodHandle
for
Module.implAddExportsOrOpens(String, Module, boolean, boolean)
.
Phew, almost there! Now we get a reference to this and the java.base
Module
s via
Class.getModule()
(UnsafeUtil.class.getModule()
and String.class.getModule()
). Next, we use
that handle we just got to export the jdk.internal.misc
package in the java.base
module to this
module.
Finally, references to jdk.internal.misc.Unsafe#theUnsafe
and a usable MethodHandles.Lookup
(the
one we borrowed earlier doesn't have access to any of our classes): For the former, we simply call
jdk.internal.misc.Unsafe#getUnsafe()
. It's a bit more complicated for the latter, but nothing compared to
what we've accomplished thus far.
First, we set a static final
field to the value of MethodHandles.lookup()
. Then, we get the
offset of the allowedModes
field using our shiny new reference to
jdk.internal.misc.Unsafe#theUnsafe
and then, once again using jdk.internal.misc.Unsafe#theUnsafe
, set
the value to MethodHandles.Lookup.TRUSTED
.
And that's it! We now have full access to the classpath and the memory space of the JVM.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final Class<?>
static final int
static final int
The header size of an object instance, excluding the extra 4 bytes representing an array's length.static final MethodHandles.Lookup
static final MethodHandles.Lookup
Method handle lookup with theallowedModes
field set toMethodHandles.Lookup.TRUSTED
, giving it unrestricted access to every class in the JVM.static final Object
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic boolean
static boolean
When true, the JVM is using compressed pointers (this is the default).static long
classFieldsLength
(Class<?> clazz) Returns the total length of all the class's fields.static long
classMaxFieldOffset
(Class<?> clazz) Returns the maximum offset of the class's fields.static long
compressOop
(long oop) If CompressesOops are enabled (seeareOopsCompressed()
), returns theoop
with compression applied.static Object
dereferenceOop
(long oop) Dereferences an OOP.static Object
dereferenceOopAssume32bit
(int oop) Dereferences an OOP, assuming that it is 32-bits wide.static Object
dereferenceOopAssume64bit
(long oop) Dereferences an OOP, assuming that it is 64-bits wide.static Object
dereferenceOopAssumeCompressed
(int oop) Dereferences an OOP, assuming that it is compressed.static void
Exports thepkgName
from themodule
totarget
.static void
Forces this class to be static-initialized.static long
getKlassPointer
(long oop) static long
getOopOfObject
(Object obj) Gets the OOP of the object.static int
Gets the OOP of the object, assuming that it is 32-bits wide.static long
Gets the OOP of the object, assuming that it is 64-bits wide.static int
Gets the OOP of the object.static MethodHandles.Lookup
static long
Gets the OOP of the object and decompresses it if it is compressed.static int
Width of a klass pointer.static long
maybeCompressPtr
(long oop, boolean enableCompression) IfenableCompression
, returns theptr
with compression applied.static long
maybeUncompressPtr
(long oop, boolean enableCompression) IfenableCompression
, returns theptr
with compression removed.static int
oopWidth()
Width of an Ordinary Object Pointer as stored in the JVM.static int
The width of a native pointer.static long
uncompressOop
(long oop) If CompressesOops are enabled (seeareOopsCompressed()
), returns theoop
with compression removed.static AssertionError
-
Field Details
-
theJdkTrustedLookup
-
C_Unsafe
-
theUnsafe
-
theTrustedLookup
Method handle lookup with theallowedModes
field set toMethodHandles.Lookup.TRUSTED
, giving it unrestricted access to every class in the JVM.Note that this will not be trusted until part of the way through static init
-
COMPRESSED_OOP_SHIFT
public static final int COMPRESSED_OOP_SHIFT -
OBJECT_HEADER_SIZE
public static final int OBJECT_HEADER_SIZEThe header size of an object instance, excluding the extra 4 bytes representing an array's length.
-
-
Constructor Details
-
Unfettered
public Unfettered()
-
-
Method Details
-
getTrustedLookup
-
forceInit
public static void forceInit()Forces this class to be static-initialized. Calling this method more than once will have no effect. Calling this method is not even necessary before using any of the exposed unsafe APIs. -
export
Exports thepkgName
from themodule
totarget
. Ifopens
istrue
, then thetarget
will have access to package-private members in addition to public ones. -
pointerWidth
public static int pointerWidth()The width of a native pointer. -
oopWidth
public static int oopWidth()Width of an Ordinary Object Pointer as stored in the JVM. These may be compressed (seeareOopsCompressed()
on 64-bit JVMs. -
klassPtrWidth
public static int klassPtrWidth()Width of a klass pointer. -
unexpectedOopWidth
-
areOopsCompressed
public static boolean areOopsCompressed()When true, the JVM is using compressed pointers (this is the default). This will only be true on 64-bit JVMs. -
areKpsCompressed
public static boolean areKpsCompressed() -
getOopOfObject
Gets the OOP of the object. If CompressedOops is enabled (seeareOopsCompressed()
) then the returned value will not be valid native pointer. To make it so, useuncompressOop(long)
. -
getUncompressedOopOfObject
Gets the OOP of the object and decompresses it if it is compressed. -
getOopOfObjectAssumeCompressed
Gets the OOP of the object. If CompressedOops is enabled (see
areOopsCompressed()
) then the returned value will not be valid native pointer. To make it so, useuncompressOop(long)
.You probably want
getOopOfObjectAssume32bit(Object)
instead. -
getOopOfObjectAssume32bit
Gets the OOP of the object, assuming that it is 32-bits wide. -
getOopOfObjectAssume64bit
Gets the OOP of the object, assuming that it is 64-bits wide. -
dereferenceOop
Dereferences an OOP. -
dereferenceOopAssumeCompressed
Dereferences an OOP, assuming that it is compressed. -
dereferenceOopAssume32bit
Dereferences an OOP, assuming that it is 32-bits wide. -
dereferenceOopAssume64bit
Dereferences an OOP, assuming that it is 64-bits wide. -
uncompressOop
public static long uncompressOop(long oop) If CompressesOops are enabled (seeareOopsCompressed()
), returns theoop
with compression removed. Otherwise, returns theoop
itself. -
compressOop
public static long compressOop(long oop) If CompressesOops are enabled (seeareOopsCompressed()
), returns theoop
with compression applied. Otherwise, returns theoop
itself. -
maybeUncompressPtr
public static long maybeUncompressPtr(long oop, boolean enableCompression) IfenableCompression
, returns theptr
with compression removed. Otherwise, returns theptr
itself. -
maybeCompressPtr
public static long maybeCompressPtr(long oop, boolean enableCompression) IfenableCompression
, returns theptr
with compression applied. Otherwise, returns theptr
itself. -
classMaxFieldOffset
Returns the maximum offset of the class's fields. This is not an efficient operation. Callers are strongly encouraged to calculate the result once and store it for reuse. -
classFieldsLength
Returns the total length of all the class's fields. This is not an efficient operation. Callers are strongly encouraged to calculate the result once and store it for reuse. -
getKlassPointer
public static long getKlassPointer(long oop)
-