8

我正在试验一种进行静态分析的工具。该工具适用于字节码而不是源代码。(但是,我也有源代码)。

该工具从字节码中输出一些行号,现在我需要一种简单的方法来映射回源代码。Netbeans/Eclipse 一直这样做(当我单击包含库中的方法时,IDE 会将我带到源(如果可用)),所以我知道这是可能的。我只是想不出办法。

例如,考虑以下 hello world 程序:

package mypackage;
import java.io.*;
class MyMainClass {
  public static void main(String[] args) { 
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); 
    String name0 = "Alice";
    String name1 = "Bob";
    try {
        name0 = in.readLine();
    }
    catch(Exception e) {
        System.out.println("Caught an exception!"); 
    }       
    System.out.println("Hello " + name0 + "!"); 
    System.out.println("Hello " + name1 + "!"); 
  }
}

生成的字节码(从 javap 获得)是:

Compiled from "MyMainClass.java"
class mypackage.MyMainClass extends java.lang.Object{
mypackage.MyMainClass();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #2; //class java/io/BufferedReader
   3:   dup
   4:   new     #3; //class java/io/InputStreamReader
   7:   dup
   8:   getstatic       #4; //Field java/lang/System.in:Ljava/io/InputStream;
   11:  invokespecial   #5; //Method java/io/InputStreamReader."<init>":(Ljava/io/InputStream;)V
   14:  invokespecial   #6; //Method java/io/BufferedReader."<init>":(Ljava/io/Reader;)V
   17:  astore_1
   18:  ldc     #7; //String Alice
   20:  astore_2
   21:  ldc     #8; //String Bob
   23:  astore_3
   24:  aload_1
   25:  invokevirtual   #9; //Method java/io/BufferedReader.readLine:()Ljava/lang/String;
   28:  astore_2
   29:  goto    42
   32:  astore  4
   34:  getstatic       #11; //Field java/lang/System.out:Ljava/io/PrintStream;
   37:  ldc     #12; //String Caught an exception!
   39:  invokevirtual   #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   42:  getstatic       #11; //Field java/lang/System.out:Ljava/io/PrintStream;
   45:  new     #14; //class java/lang/StringBuilder
   48:  dup
   49:  invokespecial   #15; //Method java/lang/StringBuilder."<init>":()V
   52:  ldc     #16; //String Hello
   54:  invokevirtual   #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   57:  aload_2
   58:  invokevirtual   #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   61:  ldc     #18; //String !
   63:  invokevirtual   #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   66:  invokevirtual   #19; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   69:  invokevirtual   #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   72:  getstatic       #11; //Field java/lang/System.out:Ljava/io/PrintStream;
   75:  new     #14; //class java/lang/StringBuilder
   78:  dup
   79:  invokespecial   #15; //Method java/lang/StringBuilder."<init>":()V
   82:  ldc     #16; //String Hello
   84:  invokevirtual   #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   87:  aload_3
   88:  invokevirtual   #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   91:  ldc     #18; //String !
   93:  invokevirtual   #17; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   96:  invokevirtual   #19; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   99:  invokevirtual   #13; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   102: return
  Exception table:
   from   to  target type
    24    29    32   Class java/lang/Exception


}

该工具的输出类似于:

<mypackage.MyMainClass> 39, 69, 99

这些对应于字节码行号。手动我可以弄清楚这些行必须对应于源代码中的以下行:

System.out.println("Caught an exception!"); 
System.out.println("Hello " + name0 + "!"); 
System.out.println("Hello " + name1 + "!"); 

但是,我需要自动化这个过程。任何帮助,将不胜感激。

4

4 回答 4

3

如果您可以同时访问源文件和相应的行号,那么任务应该很简单,只需逐行加载文件,只需选择与该行号对应的文件即可。

您的方法的问题在于,它依赖于按照class文件格式属性形式存储在编译代码中的可选元数据。有问题的两个,即SourceFileLineNumberTable都是可选的,这意味着不能保证它们会出现在您的代码中。验证您正在分析的类文件是否已编译为包含此信息!

Throwable.getStackTrace注意:如果您想知道,这些相同的属性用于通过 提供堆栈跟踪信息。

于 2012-05-24T19:32:21.237 回答
1

另一个需要考虑的选择是利用现有的静态分析工具,比如 FindBugs,它有很多功能可以显示标记代码部分的行号,以及生成漂亮的 HTML 报告。您可以相对轻松地使用自己的字节码分析来扩展 FindBugs。我已经成功地遵循了这里的教程:http: //www.ibm.com/developerworks/library/j-findbug2/

于 2012-05-21T08:05:03.507 回答
1

烟灰为你做这件事。您将通过 Soot 运行您的类文件,它将其转换为Jimple,然后您可以要求任何代码行的 ,并且ValueBox有一个方法:根据他们的文档,返回源的行号或列号代码。.getJavaSourceStartLineNumber().getJavaSourceStartColumnNumber()

也就是说,到目前为止,这是最简单的方法。Soot 是专为 Java 构建的静态分析工具。

于 2015-06-24T17:10:34.150 回答
-1

您可以根据自己的目的使用JD。拥有该工具后,您可以使用以下命令通过提示进行反编译:

jdi-gui.exe YourClassFile.class.

您可以开始subprocess使用Process该类,反编译您的.class文件,然后使用相应的行号重新编写一个新文件。然后,仅选择工具返回的那些行号。

于 2012-05-21T07:28:03.737 回答