为新注入的类设置类路径

发布于 2024-10-03 10:54:31 字数 3095 浏览 1 评论 0原文

语境: 一个新类 Bar,在运行时被注入到 JVM 中。这个类属于一个名为 com.foo 的包。 对该类的引用被注入到属于同一包的另一个类中。 新类每次加载时可能有不同的名称 - 因此不能将其指定为任何配置文件的一部分 - 例如不能在 build.xml 中指定为包含在 jar 文件中。

问题: 在类加载时,jvm 抛出错误 - java Result 1。虽然我无法最终确定根本原因,但看起来类加载器未找到新注入的类。 服务器以详细模式运行,显示 JVM 加载的类列表,并且可以看到这个新注入的类已加载。

问题: 新注入的类是否已在类路径中?如果不是怎么设置呢?

[编辑] - 在问题中添加一些代码。

代码段 - 1:下面的代码段是从 PreMain 方法调用的 - Premain 方法将由 JVM 代理调用,并在运行时注入检测引用。 Premain 方法注入 1 个新类 - Bar - 以及从方法 - returnsABool() - 现有类 - ExistingClass 中对该新类的 1 个引用。

public static void premain(String agentArgs, Instrumentation inst) {

        // 1. Create and load the new class - Bar
        String className = "Bar";
        byte [] b = getBytesForNewClass();
        //override classDefine (as it is protected) and define the class.
        Class clazz = null;
        try {
          ClassLoader loader = ClassLoader.getSystemClassLoader();
          Class cls = Class.forName("java.lang.ClassLoader");
          java.lang.reflect.Method method =
            cls.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
          // protected method invocation
          method.setAccessible(true);
          try {
            Object[] args = new Object[] { className, b, new Integer(0), new Integer(b.length)};
            clazz = (Class) method.invoke(loader, args);
          } finally {
            method.setAccessible(false);
          }
        } catch (Exception e) {
         System.err.println(
            "AllocationInstrumenter was unable to create new class" + e.getMessage());
         e.printStackTrace();
        }

        // 2. Inject some lines of code into the returnsABool method in ExistingClass class that references Bar
        inst.addTransformer(new CustomInstrumenter(), true);

        // end of premain method
}

代码段 2:方法 returnsABool() 需要使用注释进行字节注入 如下所示的线。字节注入的代码也是从 PreMain 方法调用的。

public class ExistingClass{

    public static boolean returnsABool() {
     // Code within comments is byte-injected, again as part of the pre-main method

     /*
     String str = Bar.get();
     if (str != "someValue") {
      return true;
     }
     */

        return false;
    }
}

ExistingClass 的字节代码注入 - 使用 asm 库完成

{  
    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);  
    mv.visitCode();  
    Label l0 = new Label();  
    mv.visitLabel(l0);   
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");        
    mv.visitLdcInsn("some constant here");   
    Label l1 = new Label();   
    mv.visitJumpInsn(Opcodes.IF_ACMPNE, l1);   
    mv.visitInsn(Opcodes.ICONST_0); Label l2 = new Label();   
    mv.visitJumpInsn(Opcodes.GOTO, l2);   
    mv.visitLabel(l1);   
    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);   
    mv.visitInsn(Opcodes.ICONST_1); 
    mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {Opcodes.INTEGER});  
    mv.visitInsn(Opcodes.IRETURN);   
    mv.visitMaxs(2, 0);   
    mv.visitEnd();   
}

Context:
A new class say Bar, is injected into the JVM at run-time. This class belongs to a package say com.foo.
A reference to this class is injected into another class that belongs to the same package.
The new class may have a different name each time it is loaded - so this cannot be specified as part of any config file - e.g. cannot be specified in build.xml to be included as part of a jar file.

Issue:
At class load time, jvm throws an error - java Result 1. Although I cannot conclusively determine the root cause, it looks like the newly injected class is not being found by class loader.
The server was run in verbose mode which shows the list of classes loaded by the JVM and this newly injected class is seen loaded.

Question:
Is the newly injected class already in the classpath? If not how to set it?

[Edit] - adding some code to the question.

Code segment - 1: This code segment below is called from the PreMain method - Premain method will be called by JVM agent and will inject the instrumentation reference at run time. Premain method injects 1 new class - Bar - and 1 reference to this new class from a method - returnsABool() - in an existing class - ExistingClass.

public static void premain(String agentArgs, Instrumentation inst) {

        // 1. Create and load the new class - Bar
        String className = "Bar";
        byte [] b = getBytesForNewClass();
        //override classDefine (as it is protected) and define the class.
        Class clazz = null;
        try {
          ClassLoader loader = ClassLoader.getSystemClassLoader();
          Class cls = Class.forName("java.lang.ClassLoader");
          java.lang.reflect.Method method =
            cls.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
          // protected method invocation
          method.setAccessible(true);
          try {
            Object[] args = new Object[] { className, b, new Integer(0), new Integer(b.length)};
            clazz = (Class) method.invoke(loader, args);
          } finally {
            method.setAccessible(false);
          }
        } catch (Exception e) {
         System.err.println(
            "AllocationInstrumenter was unable to create new class" + e.getMessage());
         e.printStackTrace();
        }

        // 2. Inject some lines of code into the returnsABool method in ExistingClass class that references Bar
        inst.addTransformer(new CustomInstrumenter(), true);

        // end of premain method
}

Code sement 2: The method returnsABool() needs to be byte-injected with the commented
lines shown below. The code to byte inject this is also called from the PreMain method.

public class ExistingClass{

    public static boolean returnsABool() {
     // Code within comments is byte-injected, again as part of the pre-main method

     /*
     String str = Bar.get();
     if (str != "someValue") {
      return true;
     }
     */

        return false;
    }
}

Byte code injection for ExistingClass - done using asm library

{  
    MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);  
    mv.visitCode();  
    Label l0 = new Label();  
    mv.visitLabel(l0);   
    mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");        
    mv.visitLdcInsn("some constant here");   
    Label l1 = new Label();   
    mv.visitJumpInsn(Opcodes.IF_ACMPNE, l1);   
    mv.visitInsn(Opcodes.ICONST_0); Label l2 = new Label();   
    mv.visitJumpInsn(Opcodes.GOTO, l2);   
    mv.visitLabel(l1);   
    mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);   
    mv.visitInsn(Opcodes.ICONST_1); 
    mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {Opcodes.INTEGER});  
    mv.visitInsn(Opcodes.IRETURN);   
    mv.visitMaxs(2, 0);   
    mv.visitEnd();   
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风苍溪 2024-10-10 10:54:31

我怀疑你的字节码生成有问题,以下 ASM 代码对我有用:

        mv.visitCode();
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");
        Label l1 = new Label();
        mv.visitLdcInsn("some constant here");
        mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitInsn(Opcodes.ICONST_1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
        mv.visitInsn(Opcodes.IRETURN);
        mv.visitLabel(l1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
        mv.visitInsn(Opcodes.IRETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();

另请注意:

  • 比较字符串的方式很可能会导致问题,你应该使用 str.equals(str2)< /code>
  • 您正在替换整个方法,而不是在开头注入您的自定义代码(您的注释似乎表明您要注入,而不是替换)

I would suspect you have something wrong with your bytecode generation, the following ASM code works for me:

        mv.visitCode();
        Label l0 = new Label();
        mv.visitLabel(l0);
        mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");
        Label l1 = new Label();
        mv.visitLdcInsn("some constant here");
        mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitInsn(Opcodes.ICONST_1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
        mv.visitInsn(Opcodes.IRETURN);
        mv.visitLabel(l1);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
        mv.visitInsn(Opcodes.ICONST_0);
        mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
        mv.visitInsn(Opcodes.IRETURN);
        mv.visitMaxs(2, 1);
        mv.visitEnd();

Also note that:

  • the way you are comparing Strings will most likely lead to issues, you should use str.equals(str2)
  • you are replacing the entire method, instead of injecting your custom code in the beginning (your comments seem to indicate that you want to inject, instead of replace)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文