diff options
author | Andy McFadden <> | 2009-03-27 12:07:54 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-27 12:07:54 -0700 |
commit | 753f32ab1db6de174a1de68210f9f30a1e327612 (patch) | |
tree | 20ae517673c641595fcaaf2c022222b88707a3dd /tools | |
parent | 12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b (diff) | |
download | android_dalvik-753f32ab1db6de174a1de68210f9f30a1e327612.tar.gz android_dalvik-753f32ab1db6de174a1de68210f9f30a1e327612.tar.bz2 android_dalvik-753f32ab1db6de174a1de68210f9f30a1e327612.zip |
AI 143120: Added simple XML output. Shuffled stuff around.
Automated import of CL 143120
Diffstat (limited to 'tools')
-rw-r--r-- | tools/dexdeps/README.txt | 28 | ||||
-rw-r--r-- | tools/dexdeps/src/com/android/dexdeps/DexData.java | 107 | ||||
-rw-r--r-- | tools/dexdeps/src/com/android/dexdeps/FieldRef.java | 9 | ||||
-rw-r--r-- | tools/dexdeps/src/com/android/dexdeps/Main.java | 7 | ||||
-rw-r--r-- | tools/dexdeps/src/com/android/dexdeps/MethodRef.java | 45 | ||||
-rw-r--r-- | tools/dexdeps/src/com/android/dexdeps/Output.java | 227 |
6 files changed, 306 insertions, 117 deletions
diff --git a/tools/dexdeps/README.txt b/tools/dexdeps/README.txt new file mode 100644 index 000000000..14d65b0d8 --- /dev/null +++ b/tools/dexdeps/README.txt @@ -0,0 +1,28 @@ +dexdeps -- DEX external dependency dump + + +This tool dumps a list of fields and methods that a DEX file uses but does +not define. When combined with a list of public APIs, it can be used to +determine whether an APK is accessing fields and calling methods that it +shouldn't be. It may also be useful in determining whether an application +requires a certain minimum API level to execute. + +Basic usage: + + dexdeps [options] <file.{dex,apk,jar}> + +For zip archives (including .jar and .apk), dexdeps will look for a +"classes.dex" entry. + +Supported options are: + + --format={brief,xml} + + Specifies the output format. + + "brief" produces one line of output for each field and method. Field + and argument types are shown as descriptor strings. + + "xml" produces a larger output file, readable with an XML browser. Types + are shown in a more human-readable form (e.g. "[I" becomes "int[]"). + diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java index 0bfd20253..fa79d60b6 100644 --- a/tools/dexdeps/src/com/android/dexdeps/DexData.java +++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java @@ -310,107 +310,34 @@ public class DexData { */ /** - * Converts a single-character primitive type into its human-readable - * equivalent. + * Returns the class name, given an index into the type_ids table. */ - private String primitiveTypeLabel(char typeChar) { - /* primitive type; substitute human-readable name in */ - switch (typeChar) { - case 'B': return "byte"; - case 'C': return "char"; - case 'D': return "double"; - case 'F': return "float"; - case 'I': return "int"; - case 'J': return "long"; - case 'S': return "short"; - case 'V': return "void"; - case 'Z': return "boolean"; - default: - /* huh? */ - System.err.println("Unexpected class char " + typeChar); - assert false; - return "UNKNOWN"; - } + private String classNameFromTypeIndex(int idx) { + return mStrings[mTypeIds[idx].descriptorIdx]; } /** - * Converts a descriptor to dotted form. For example, - * "Ljava/lang/String;" becomes "java.lang.String", and "[I" becomes - * "int[]. + * Returns an array of method argument type strings, given an index + * into the proto_ids table. */ - private String descriptorToDot(String descr) { - int targetLen = descr.length(); - int offset = 0; - int arrayDepth = 0; - - /* strip leading [s; will be added to end */ - while (targetLen > 1 && descr.charAt(offset) == '[') { - offset++; - targetLen--; - } - arrayDepth = offset; - - if (targetLen == 1) { - descr = primitiveTypeLabel(descr.charAt(offset)); - offset = 0; - targetLen = descr.length(); - } else { - /* account for leading 'L' and trailing ';' */ - if (targetLen >= 2 && descr.charAt(offset) == 'L' && - descr.charAt(offset+targetLen-1) == ';') - { - targetLen -= 2; /* two fewer chars to copy */ - offset++; /* skip the 'L' */ - } - } - - char[] buf = new char[targetLen + arrayDepth * 2]; - - /* copy class name over */ - int i; - for (i = 0; i < targetLen; i++) { - char ch = descr.charAt(offset + i); - buf[i] = (ch == '/') ? '.' : ch; - } + private String[] argArrayFromProtoIndex(int idx) { + ProtoIdItem protoId = mProtoIds[idx]; + String[] result = new String[protoId.types.length]; - /* add the appopriate number of brackets for arrays */ - while (arrayDepth-- > 0) { - buf[i++] = '['; - buf[i++] = ']'; + for (int i = 0; i < protoId.types.length; i++) { + result[i] = mStrings[mTypeIds[protoId.types[i]].descriptorIdx]; } - assert i == buf.length; - return new String(buf); - } - - /** - * Returns the dot-form class name, given an index into the type_ids - * table. - */ - private String classNameFromTypeIndex(int idx) { - String descriptor = mStrings[mTypeIds[idx].descriptorIdx]; - return descriptorToDot(descriptor); + return result; } /** - * Returns the method prototype descriptor, given an index into the - * proto_ids table. + * Returns a string representing the method's return type, given an + * index into the proto_ids table. */ - private String protoStringFromProtoIndex(int idx) { - StringBuilder builder = new StringBuilder(); + private String returnTypeFromProtoIndex(int idx) { ProtoIdItem protoId = mProtoIds[idx]; - - builder.append("("); - for (int i = 0; i < protoId.types.length; i++) { - String elem = mStrings[mTypeIds[protoId.types[i]].descriptorIdx]; - builder.append(elem); - } - - builder.append(")"); - String ret = mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx]; - builder.append(ret); - - return builder.toString(); + return mStrings[mTypeIds[protoId.returnTypeIdx].descriptorIdx]; } /** @@ -465,7 +392,8 @@ public class DexData { MethodIdItem methodId = mMethodIds[i]; methodRefs[count++] = new MethodRef(classNameFromTypeIndex(methodId.classIdx), - protoStringFromProtoIndex(methodId.protoIdx), + argArrayFromProtoIndex(methodId.protoIdx), + returnTypeFromProtoIndex(methodId.protoIdx), mStrings[methodId.nameIdx]); } } @@ -475,6 +403,7 @@ public class DexData { return methodRefs; } + /* * ======================================================================= * Basic I/O functions diff --git a/tools/dexdeps/src/com/android/dexdeps/FieldRef.java b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java index 96e0ef01f..2726a7a7d 100644 --- a/tools/dexdeps/src/com/android/dexdeps/FieldRef.java +++ b/tools/dexdeps/src/com/android/dexdeps/FieldRef.java @@ -28,14 +28,23 @@ public class FieldRef { mFieldName = fieldName; } + /** + * Gets the name of the field's declaring class. + */ public String getDeclClassName() { return mDeclClass; } + /** + * Gets the type name. Examples: "Ljava/lang/String;", "[I". + */ public String getTypeName() { return mFieldType; } + /** + * Gets the field name. + */ public String getName() { return mFieldName; } diff --git a/tools/dexdeps/src/com/android/dexdeps/Main.java b/tools/dexdeps/src/com/android/dexdeps/Main.java index 43698458f..7eba3aa80 100644 --- a/tools/dexdeps/src/com/android/dexdeps/Main.java +++ b/tools/dexdeps/src/com/android/dexdeps/Main.java @@ -55,7 +55,8 @@ public class Main { usage(); System.exit(2); } catch (IOException ioe) { - /* a message was already reported, just bail quietly */ + if (ioe.getMessage() != null) + System.err.println("Failed: " + ioe); System.exit(1); } catch (DexDataException dde) { /* a message was already reported, just bail quietly */ @@ -127,7 +128,7 @@ public class Main { * to ensure it doesn't hang around if we fail. */ File tempFile = File.createTempFile("dexdeps", ".dex"); - System.out.println("+++ using temp " + tempFile); + //System.out.println("+++ using temp " + tempFile); RandomAccessFile raf = new RandomAccessFile(tempFile, "rw"); tempFile.delete(); @@ -173,7 +174,7 @@ public class Main { System.err.println("Unknown format '" + mOutputFormat +"'"); throw new UsageException(); } - System.out.println("Using format " + mOutputFormat); + //System.out.println("+++ using format " + mOutputFormat); } else { System.err.println("Unknown option '" + arg + "'"); throw new UsageException(); diff --git a/tools/dexdeps/src/com/android/dexdeps/MethodRef.java b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java index 1ca68528d..96522eb92 100644 --- a/tools/dexdeps/src/com/android/dexdeps/MethodRef.java +++ b/tools/dexdeps/src/com/android/dexdeps/MethodRef.java @@ -17,14 +17,17 @@ package com.android.dexdeps; public class MethodRef { - private String mDeclClass, mDescriptor, mMethodName; + private String mDeclClass, mReturnType, mMethodName; + private String[] mArgTypes; /** * Initializes a new field reference. */ - public MethodRef(String declClass, String descriptor, String methodName) { + public MethodRef(String declClass, String[] argTypes, String returnType, + String methodName) { mDeclClass = declClass; - mDescriptor = descriptor; + mArgTypes = argTypes; + mReturnType = returnType; mMethodName = methodName; } @@ -39,7 +42,7 @@ public class MethodRef { * Gets the method's descriptor. */ public String getDescriptor() { - return mDescriptor; + return descriptorFromProtoArray(mArgTypes, mReturnType); } /** @@ -50,20 +53,36 @@ public class MethodRef { } /** - * Gets the method arguments as an array of type strings. + * Gets an array of method argument types. */ - public String[] getArguments() { - // TODO - return null; + public String[] getArgumentTypeNames() { + return mArgTypes; } /** - * Gets the method's return type. + * Gets the method's return type. Examples: "Ljava/lang/String;", "[I". */ - public String getReturnType() { - // TODO - return null; + public String getReturnTypeName() { + return mReturnType; } -} + /** + * Returns the method descriptor, given the argument and return type + * prototype strings. + */ + private static String descriptorFromProtoArray(String[] protos, + String returnType) { + StringBuilder builder = new StringBuilder(); + + builder.append("("); + for (int i = 0; i < protos.length; i++) { + builder.append(protos[i]); + } + + builder.append(")"); + builder.append(returnType); + + return builder.toString(); + } +} diff --git a/tools/dexdeps/src/com/android/dexdeps/Output.java b/tools/dexdeps/src/com/android/dexdeps/Output.java index 0c350b68a..0039b3302 100644 --- a/tools/dexdeps/src/com/android/dexdeps/Output.java +++ b/tools/dexdeps/src/com/android/dexdeps/Output.java @@ -21,40 +21,243 @@ package com.android.dexdeps; */ public class Output { public static void generate(DexData dexData, String format) { - FieldRef[] externFieldRefs; - MethodRef[] externMethodRefs; - - externFieldRefs = dexData.getExternalFieldReferences(); - externMethodRefs = dexData.getExternalMethodReferences(); - if (format.equals("brief")) { - printFieldRefs(externFieldRefs); - printMethodRefs(externMethodRefs); + printBrief(dexData); } else if (format.equals("xml")) { - // ... + printXml(dexData); } else { /* should've been trapped in arg handler */ throw new RuntimeException("unknown output format"); } } + /** + * Prints the data in a simple human-readable format. + */ + static void printBrief(DexData dexData) { + FieldRef[] externFieldRefs = dexData.getExternalFieldReferences(); + MethodRef[] externMethodRefs = dexData.getExternalMethodReferences(); + + printFieldRefs(externFieldRefs); + printMethodRefs(externMethodRefs); + } + + /** + * Prints the list of fields in a simple human-readable format. + */ static void printFieldRefs(FieldRef[] fields) { System.out.println("Fields:"); for (int i = 0; i < fields.length; i++) { FieldRef ref = fields[i]; - System.out.println(ref.getDeclClassName() + "." + + System.out.println(descriptorToDot(ref.getDeclClassName()) + "." + ref.getName() + " : " + ref.getTypeName()); } } + /** + * Prints the list of methods in a simple human-readable format. + */ static void printMethodRefs(MethodRef[] methods) { System.out.println("Methods:"); for (int i = 0; i < methods.length; i++) { MethodRef ref = methods[i]; - System.out.println(ref.getDeclClassName() + "." + - ref.getName() + " : " + ref.getDescriptor()); + System.out.println(descriptorToDot(ref.getDeclClassName()) + + "." + ref.getName() + " : " + ref.getDescriptor()); + } + } + + + /** + * Prints the output in XML format. + * + * We shouldn't need to XML-escape the field/method info. + */ + static void printXml(DexData dexData) { + final String IN0 = ""; + final String IN1 = " "; + final String IN2 = " "; + final String IN3 = " "; + FieldRef[] externFieldRefs = dexData.getExternalFieldReferences(); + MethodRef[] externMethodRefs = dexData.getExternalMethodReferences(); + String prevClass = null; + + System.out.println(IN0 + "<external>"); + + /* print fields */ + for (int i = 0; i < externFieldRefs.length; i++) { + FieldRef fref = externFieldRefs[i]; + String declClassName = fref.getDeclClassName(); + + if (prevClass != null && !prevClass.equals(declClassName)) { + System.out.println(IN1 + "</class>"); + } + if (!declClassName.equals(prevClass)) { + String className = classNameOnly(declClassName); + String packageName = packageNameOnly(declClassName); + System.out.println(IN1 + "<class package=\"" + packageName + + "\" name=\"" + className + "\">"); + prevClass = declClassName; + } + + System.out.println(IN2 + "<field name=\"" + fref.getName() + + "\" type=\"" + descriptorToDot(fref.getTypeName()) + "\"/>"); + } + + /* print methods */ + for (int i = 0; i < externMethodRefs.length; i++) { + MethodRef mref = externMethodRefs[i]; + String declClassName = mref.getDeclClassName(); + boolean constructor; + + if (prevClass != null && !prevClass.equals(declClassName)) { + System.out.println(IN1 + "</class>"); + } + if (!declClassName.equals(prevClass)) { + String className = classNameOnly(declClassName); + String packageName = packageNameOnly(declClassName); + System.out.println(IN1 + "<class package=\"" + packageName + + "\" name=\"" + className + "\">"); + prevClass = declClassName; + } + + constructor = mref.getName().equals("<init>"); + if (constructor) { + /* use class name instead of method name */ + System.out.println(IN2 + "<constructor name=\"" + + classNameOnly(declClassName) + "\" return=\"" + + descriptorToDot(mref.getReturnTypeName()) + "\">"); + } else { + System.out.println(IN2 + "<method name=\"" + mref.getName() + + "\" return=\"" + descriptorToDot(mref.getReturnTypeName()) + + "\">"); + } + String[] args = mref.getArgumentTypeNames(); + for (int j = 0; j < args.length; j++) { + System.out.println(IN3 + "<parameter type=\"" + + descriptorToDot(args[j]) + "\"/>"); + } + if (constructor) { + System.out.println(IN2 + "</constructor>"); + } else { + System.out.println(IN2 + "</method>"); + } + } + + if (prevClass != null) + System.out.println(IN1 + "</class>"); + System.out.println(IN0 + "</external>"); + } + + + /* + * ======================================================================= + * Utility functions + * ======================================================================= + */ + + /** + * Converts a single-character primitive type into its human-readable + * equivalent. + */ + static String primitiveTypeLabel(char typeChar) { + /* primitive type; substitute human-readable name in */ + switch (typeChar) { + case 'B': return "byte"; + case 'C': return "char"; + case 'D': return "double"; + case 'F': return "float"; + case 'I': return "int"; + case 'J': return "long"; + case 'S': return "short"; + case 'V': return "void"; + case 'Z': return "boolean"; + default: + /* huh? */ + System.err.println("Unexpected class char " + typeChar); + assert false; + return "UNKNOWN"; + } + } + + /** + * Converts a type descriptor to human-readable "dotted" form. For + * example, "Ljava/lang/String;" becomes "java.lang.String", and + * "[I" becomes "int[]. + */ + static String descriptorToDot(String descr) { + int targetLen = descr.length(); + int offset = 0; + int arrayDepth = 0; + + /* strip leading [s; will be added to end */ + while (targetLen > 1 && descr.charAt(offset) == '[') { + offset++; + targetLen--; + } + arrayDepth = offset; + + if (targetLen == 1) { + descr = primitiveTypeLabel(descr.charAt(offset)); + offset = 0; + targetLen = descr.length(); + } else { + /* account for leading 'L' and trailing ';' */ + if (targetLen >= 2 && descr.charAt(offset) == 'L' && + descr.charAt(offset+targetLen-1) == ';') + { + targetLen -= 2; /* two fewer chars to copy */ + offset++; /* skip the 'L' */ + } + } + + char[] buf = new char[targetLen + arrayDepth * 2]; + + /* copy class name over */ + int i; + for (i = 0; i < targetLen; i++) { + char ch = descr.charAt(offset + i); + buf[i] = (ch == '/') ? '.' : ch; + } + + /* add the appopriate number of brackets for arrays */ + while (arrayDepth-- > 0) { + buf[i++] = '['; + buf[i++] = ']'; + } + assert i == buf.length; + + return new String(buf); + } + + /** + * Extracts the class name from a type descriptor. + */ + static String classNameOnly(String typeName) { + String dotted = descriptorToDot(typeName); + + int start = dotted.lastIndexOf("."); + if (start < 0) { + return dotted; + } else { + return dotted.substring(start+1); + } + } + + /** + * Extracts the package name from a type descriptor, and returns it in + * dotted form. + */ + static String packageNameOnly(String typeName) { + String dotted = descriptorToDot(typeName); + + int end = dotted.lastIndexOf("."); + if (end < 0) { + /* lives in default package */ + return ""; + } else { + return dotted.substring(0, end); } } } |