summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorAndy McFadden <>2009-03-27 12:07:54 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-27 12:07:54 -0700
commit753f32ab1db6de174a1de68210f9f30a1e327612 (patch)
tree20ae517673c641595fcaaf2c022222b88707a3dd /tools
parent12d6d4c0ea192b6a924df0df1e3b14ce1ed5793b (diff)
downloadandroid_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.txt28
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/DexData.java107
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/FieldRef.java9
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/Main.java7
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/MethodRef.java45
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/Output.java227
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);
}
}
}