I'm trying to do this by re-writing the bytecode of the class using ASM 4.0 to replace all the native
methods with non-native
stubs.
So far I have this:
class ClassAdapter extends ClassVisitor {
public ClassAdapter(ClassVisitor cv) {
super(Opcodes.ASM4, cv);
}
@Override
public MethodVisitor visitMethod(int access, String base, String desc, String signature, String[] exceptions) {
return cv.visitMethod(access & ~Opcodes.ACC_NATIVE, base, desc, signature, exceptions);
}
}
which is executed by
private static byte[] instrument(byte[] originalBytes, ClassLoader loader) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassAdapter adapter = new ClassAdapter(cw);
ClassReader cr = new ClassReader(originalBytes);
cr.accept(adapter, ClassReader.SKIP_FRAMES);
return cw.toByteArray();
}
Which seems simple enough: I strip the ACC_NATIVE
off of the method in visitMethod()
and leave everything else unchanged. However, when I do this to java.lang.Object
, it dies with a
Exception in thread "main"
Exception: java.lang.StackOverflowError thrown from the UncaughtExceptionHandler in thread "main"
The StackOverflow happens at instrumentation time, not at runtime, which I think is rather unusual. However, if I remove the & ~Opcodes.ACC_NATIVE
modifier, java.lang.Object
gets rewritten (in this case unchanged) and executes perfectly.
Clearly I am not doing something right, and replacing the native
method with a non-native
method isn't quite as simple as stripping off the native
modifier on the method, but I have no idea where to start. The ASM Docs don't talk about working with native
methods at all. Does anyone with experience working with ASM know what I need to do to get the native
method re-writing to work?
Sorry, that short, useless message was what e.printStackTrace()
was giving me, but using e.getStackTrace()
I managed to get something useful:
java.util.concurrent.ConcurrentHashMap.hash(ConcurrentHashMap.java:332)
java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1124)
java.util.Collections$SetFromMap.add(Collections.java:3903)
sandbox.classloader.MyClassLoader.instrument(Unknown Source)
sandbox.classloader.MyClassLoader.loadClass(Unknown Source)
java.lang.ClassLoader.defineClass1(Native Method)
java.lang.ClassLoader.defineClass(ClassLoader.java:791)
java.lang.ClassLoader.defineClass(ClassLoader.java:634)
sandbox.classloader.MyClassLoader.findClass(Unknown Source)
sandbox.classloader.MyClassLoader.loadClass(Unknown Source)
sandbox.Tester.main(Unknown Source)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:601)
com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
So it seems to me that the error was in fact happening at execution time (e.g. I was mistaken in thinking it was at instrumentation time) and is the result of calling hashCode()
. As it so happens, hashCode()
one of the native methods which I (probably incorrectly) stripped of it's native
modifier. So clearly it's calling the native
-stripped methods which is causing the problem.
What seems really odd is that the stack trace is only 16 frames deep; I'd have expected kinda more given that it was a StackOverflowError
.
It's not quite as simple to replace native code with stubs but it's not far from that
If you look at ClassVisitor#visitMethod(int access, String name, String desc, String signature, String[] exceptions)
you'll see that it returns a MethodVisitor
MethodVisitor which you now have to make use of. If you want to make abstract stubs, you should add at least the call to methodVisitor.visitEnd()
If you want to make empty stubs, you have to add visitCode
and also return a value if necessary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With