Skip to content

Commit

Permalink
Define both native and Java breakpoints to trace Class#getSigners.
Browse files Browse the repository at this point in the history
  • Loading branch information
DSouzaM committed Jan 2, 2025
1 parent 261c38a commit caccf0e
Showing 1 changed file with 57 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,13 @@ private static boolean getSigners(JNIEnvironment jni, JNIObjectHandle thread, Br
return handleGetClasses(jni, thread, bp, state);
}

private static boolean handleGetClasses(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
private static boolean handleGetClasses(JNIEnvironment jni, JNIObjectHandle thread, AbstractBreakpoint<?> bp, InterceptedState state) {
JNIObjectHandle self = getReceiver(thread);
return handleGetClassesWithReceiver(jni, self, bp, state);
}

private static boolean handleGetClassesWithReceiver(JNIEnvironment jni, JNIObjectHandle self, AbstractBreakpoint<?> bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
traceReflectBreakpoint(jni, self, nullHandle(), callerClass, bp.specification.methodName, null, state.getFullStackTraceOrNull());
return true;
}
Expand Down Expand Up @@ -366,10 +370,10 @@ private interface AllocateInstanceFunctionPointer extends CFunctionPointer {
@CEntryPoint
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
static long nativeAllocateInstance(JNIEnvironment jni, JNIObjectHandle self, JNIObjectHandle clazz) {
VMError.guarantee(NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed != null &&
NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed.replacedFunction.isNonNull(), "incompletely installed");
NativeBreakpoint breakpoint = NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed;
VMError.guarantee(breakpoint != null && breakpoint.replacedFunction.isNonNull(), "incompletely installed");

AllocateInstanceFunctionPointer original = (AllocateInstanceFunctionPointer) NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC.installed.replacedFunction;
AllocateInstanceFunctionPointer original = (AllocateInstanceFunctionPointer) breakpoint.replacedFunction;
long result = original.invoke(jni, self, clazz);
if (!Support.isInitialized()) { // in case of a (very) late call
return result;
Expand All @@ -392,6 +396,36 @@ private static void traceAllocateInstance(JNIEnvironment jni, JNIObjectHandle cl
}
}

private static final CEntryPointLiteral<AllocateInstanceFunctionPointer> nativeGetSigners = CEntryPointLiteral.create(
BreakpointInterceptor.class, "nativeGetSigners", JNIEnvironment.class, JNIObjectHandle.class);
private static final NativeBreakpointSpecification NATIVE_GET_SIGNERS_BREAKPOINT_SPEC = new NativeBreakpointSpecification(
"java/lang/Class", "getSigners", "()[Ljava/lang/Object;", nativeGetSigners);

private interface GetSignersFunctionPointer extends CFunctionPointer {
@InvokeCFunctionPointer
JNIObjectHandle invoke(JNIEnvironment jni, JNIObjectHandle self);
}

/**
* Before Java 24, {@code java/lang/Class#getSigners} was declared as a native method, so we use
* a native breakpoint to trace it. Either this breakpoint or the Java breakpoint will be set
* (but not both).
*/
@CEntryPoint
@CEntryPointOptions(prologue = AgentIsolate.Prologue.class)
static JNIObjectHandle nativeGetSigners(JNIEnvironment jni, JNIObjectHandle self) {
NativeBreakpoint breakpoint = NATIVE_GET_SIGNERS_BREAKPOINT_SPEC.installed;
VMError.guarantee(breakpoint != null && breakpoint.replacedFunction.isNonNull(), "incompletely installed");
GetSignersFunctionPointer original = (GetSignersFunctionPointer) breakpoint.replacedFunction;
JNIObjectHandle result = original.invoke(jni, self);
if (!Support.isInitialized()) {
return result;
}
InterceptedState state = interceptedStateSupplier.get();
handleGetClassesWithReceiver(jni, self, breakpoint, state);
return result;
}

private static boolean objectFieldOffsetByName(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle self = getReceiver(thread);
Expand Down Expand Up @@ -1326,17 +1360,13 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer
BreakpointInterceptor.experimentalClassDefineSupport = exptlClassDefineSupport;
BreakpointInterceptor.experimentalUnsafeAllocationSupport = exptlUnsafeAllocationSupport;
BreakpointInterceptor.trackReflectionMetadata = trackReflectionData;
BreakpointInterceptor.boundNativeMethods = new HashMap<>();

JvmtiCapabilities capabilities = UnmanagedMemory.calloc(SizeOf.get(JvmtiCapabilities.class));
check(jvmti.getFunctions().GetCapabilities().invoke(jvmti, capabilities));
capabilities.setCanGenerateBreakpointEvents(1);
capabilities.setCanAccessLocalVariables(1);

if (exptlUnsafeAllocationSupport) {
capabilities.setCanGenerateNativeMethodBindEvents(1);
callbacks.setNativeMethodBind(onNativeMethodBindLiteral.getFunctionPointer());
BreakpointInterceptor.boundNativeMethods = new HashMap<>();
}
capabilities.setCanGenerateNativeMethodBindEvents(1);

if (exptlClassLoaderSupport) {
capabilities.setCanGetBytecodes(1);
Expand All @@ -1348,8 +1378,10 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer
}
check(jvmti.getFunctions().AddCapabilities().invoke(jvmti, capabilities));
UnmanagedMemory.free(capabilities);
check(jvmti.getFunctions().SetEventNotificationMode().invoke(jvmti, JvmtiEventMode.JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, nullHandle()));

callbacks.setBreakpoint(onBreakpointLiteral.getFunctionPointer());
callbacks.setNativeMethodBind(onNativeMethodBindLiteral.getFunctionPointer());

if (exptlClassDefineSupport) {
callbacks.setClassFileLoadHook(onClassFileLoadHookLiteral.getFunctionPointer());
Expand All @@ -1359,9 +1391,6 @@ public static void onLoad(JvmtiEnv jvmti, JvmtiEventCallbacks callbacks, Tracer
callbacks.setClassPrepare(onClassPrepareLiteral.getFunctionPointer());
}

if (exptlUnsafeAllocationSupport) {
Support.check(jvmti.getFunctions().SetEventNotificationMode().invoke(jvmti, JvmtiEventMode.JVMTI_ENABLE, JVMTI_EVENT_NATIVE_METHOD_BIND, nullHandle()));
}
}

public static void onVMInit(JvmtiEnv jvmti, JNIEnvironment jni) {
Expand Down Expand Up @@ -1412,9 +1441,7 @@ public static void onVMInit(JvmtiEnv jvmti, JNIEnvironment jni) {
}
installedBreakpoints = breakpoints;

if (experimentalUnsafeAllocationSupport) {
setupNativeBreakpoints(jni, lastClass, lastClassName);
}
setupNativeBreakpoints(jni, lastClass, lastClassName);

if (experimentalClassDefineSupport) {
setupClassLoadEvent(jvmti, jni);
Expand All @@ -1431,8 +1458,12 @@ private static void setupNativeBreakpoints(JNIEnvironment jni, JNIObjectHandle p
String lastClassName = previousClassName;
nativeBreakpointsInitLock.lock();
try {
nativeBreakpoints = new HashMap<>(NATIVE_BREAKPOINT_SPECIFICATIONS.length);
for (NativeBreakpointSpecification br : NATIVE_BREAKPOINT_SPECIFICATIONS) {
List<NativeBreakpointSpecification> nativeBreakpointsList = new ArrayList<>(Arrays.asList(NATIVE_BREAKPOINT_SPECIFICATIONS));
if (experimentalUnsafeAllocationSupport) {
nativeBreakpointsList.addAll(Arrays.asList(EXPERIMENTAL_UNSAFE_ALLOCATION_NATIVE_BREAKPOINT_SPECIFICATIONS));
}
nativeBreakpoints = new HashMap<>(nativeBreakpointsList.size());
for (NativeBreakpointSpecification br : nativeBreakpointsList) {
JNIObjectHandle clazz;
if (lastClassName != null && lastClassName.equals(br.className)) {
clazz = lastClass;
Expand All @@ -1446,7 +1477,8 @@ private static void setupNativeBreakpoints(JNIEnvironment jni, JNIObjectHandle p
NativeBreakpoint bp = new NativeBreakpoint(br, clazz, method);
nativeBreakpoints.put(method.rawValue(), bp);
Long original = boundNativeMethods.get(method.rawValue());
if (original != null) { // already bound, replace
// only bind a native breakpoint if method was bound as a native method
if (original != null) {
bindNativeBreakpoint(jni, bp, WordFactory.pointer(original), nullPointer());
}
}
Expand Down Expand Up @@ -1710,7 +1742,7 @@ private interface BreakpointHandler {
optionalBrk("java/lang/Class", "getNestMembers", "()[Ljava/lang/Class;",
BreakpointInterceptor::getNestMembers),
optionalBrk("java/lang/Class", "getSigners", "()[Ljava/lang/Object;",
BreakpointInterceptor::getSigners)
BreakpointInterceptor::getSigners),
};

private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thread, @SuppressWarnings("unused") Breakpoint bp, InterceptedState state) {
Expand All @@ -1724,6 +1756,10 @@ private static boolean allocateInstance(JNIEnvironment jni, JNIObjectHandle thre
"(Ljava/lang/String;)Ljava/lang/Class;", BreakpointInterceptor::loadClass);

private static final NativeBreakpointSpecification[] NATIVE_BREAKPOINT_SPECIFICATIONS = {
NATIVE_GET_SIGNERS_BREAKPOINT_SPEC,
};

private static final NativeBreakpointSpecification[] EXPERIMENTAL_UNSAFE_ALLOCATION_NATIVE_BREAKPOINT_SPECIFICATIONS = {
NATIVE_ALLOCATE_INSTANCE_BREAKPOINT_SPEC
};

Expand Down

0 comments on commit caccf0e

Please sign in to comment.