summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2020-04-28 20:27:19 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2020-04-28 20:27:19 +0000
commitf9e8e0e80832d630d3b67368ce386b60883d3016 (patch)
tree73ea7b17d529b31d2818cbdafdd46d9c83d9280b
parent064265659b79fb290b760409c18445b340202ac1 (diff)
parent9a1d3ec50905d5dabbd1ce393e5715df43cc3d1e (diff)
downloaddalvik-android10-android13-mainline-tzdata-release.tar.gz
Change-Id: I5de0589107877e5a67a2a28acc2bb53fa64bd77a
-rw-r--r--Android.bp2
-rw-r--r--OWNERS5
-rw-r--r--dexdump/Android.bp53
-rw-r--r--dexdump/DexDump.cpp2287
-rw-r--r--dexdump/NOTICE190
-rw-r--r--dx/src/com/android/dex/DexFormat.java10
-rw-r--r--dx/src/com/android/dex/Mutf8.java2
-rwxr-xr-xdx/tests/127-merge-stress/run2
-rwxr-xr-xdx/tests/137-dexmerger-dex38/run2
-rwxr-xr-xdx/tests/run-all-tests2
-rw-r--r--libdex/Android.bp56
-rw-r--r--libdex/CmdUtils.cpp228
-rw-r--r--libdex/CmdUtils.h73
-rw-r--r--libdex/DexCatch.cpp90
-rw-r--r--libdex/DexCatch.h162
-rw-r--r--libdex/DexClass.cpp191
-rw-r--r--libdex/DexClass.h162
-rw-r--r--libdex/DexDataMap.cpp138
-rw-r--r--libdex/DexDataMap.h73
-rw-r--r--libdex/DexDebugInfo.cpp281
-rw-r--r--libdex/DexDebugInfo.h55
-rw-r--r--libdex/DexFile.cpp539
-rw-r--r--libdex/DexFile.h1084
-rw-r--r--libdex/DexInlines.cpp33
-rw-r--r--libdex/DexOpcodes.cpp299
-rw-r--r--libdex/DexOpcodes.h620
-rw-r--r--libdex/DexOptData.cpp128
-rw-r--r--libdex/DexOptData.h42
-rw-r--r--libdex/DexProto.cpp523
-rw-r--r--libdex/DexProto.h229
-rw-r--r--libdex/DexSwapVerify.cpp3070
-rw-r--r--libdex/DexUtf.cpp304
-rw-r--r--libdex/DexUtf.h131
-rw-r--r--libdex/InstrUtils.cpp702
-rw-r--r--libdex/InstrUtils.h201
-rw-r--r--libdex/Leb128.cpp65
-rw-r--r--libdex/Leb128.h164
-rw-r--r--libdex/OptInvocation.cpp177
-rw-r--r--libdex/OptInvocation.h30
-rw-r--r--libdex/SysUtil.cpp370
-rw-r--r--libdex/SysUtil.h109
-rw-r--r--libdex/ZipArchive.h86
-rw-r--r--libdex/sha1.cpp512
-rw-r--r--libdex/sha1.h20
-rw-r--r--tools/dexdeps/src/com/android/dexdeps/DexData.java140
45 files changed, 13555 insertions, 87 deletions
diff --git a/Android.bp b/Android.bp
index 702d27da4..44a1433b8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,4 +1,6 @@
subdirs = [
+ "dexdump",
"dx",
+ "libdex",
"tools/hprof-conv",
]
diff --git a/OWNERS b/OWNERS
index 91d08a51e..8ac8ceda5 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,9 +1,10 @@
+benoitlamarche@google.com
+jplesot@google.com
+yroussel@google.com
# Studio
gavra@google.com
cmw@google.com
# ART
-mathieuc@google.com
narayan@google.com
ngeoffray@google.com
oth@google.com
-sehr@google.com
diff --git a/dexdump/Android.bp b/dexdump/Android.bp
new file mode 100644
index 000000000..2dbb1e42d
--- /dev/null
+++ b/dexdump/Android.bp
@@ -0,0 +1,53 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//
+// dexdump, similar in purpose to objdump.
+//
+
+cc_binary {
+ name: "dexdump",
+ host_supported: true,
+
+ srcs: ["DexDump.cpp"],
+ include_dirs: ["dalvik"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ target: {
+ android: {
+ static_libs: [
+ "libdex",
+ "libbase",
+ ],
+ shared_libs: [
+ "libz",
+ "liblog",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libdex",
+ "libbase",
+ "liblog",
+ "libz",
+ ],
+ },
+ windows: {
+ enabled: true,
+ },
+ },
+}
diff --git a/dexdump/DexDump.cpp b/dexdump/DexDump.cpp
new file mode 100644
index 000000000..9b1ac3ab1
--- /dev/null
+++ b/dexdump/DexDump.cpp
@@ -0,0 +1,2287 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * The "dexdump" tool is intended to mimic "objdump". When possible, use
+ * similar command-line arguments.
+ *
+ * TODO: rework the "plain" output format to be more regexp-friendly
+ *
+ * Differences between XML output and the "current.xml" file:
+ * - classes in same package are not all grouped together; generally speaking
+ * nothing is sorted
+ * - no "deprecated" on fields and methods
+ * - no "value" on fields
+ * - no parameter names
+ * - no generic signatures on parameters, e.g. type="java.lang.Class&lt;?&gt;"
+ * - class shows declared fields and methods; does not show inherited fields
+ */
+
+#include "libdex/DexFile.h"
+
+#include "libdex/CmdUtils.h"
+#include "libdex/DexCatch.h"
+#include "libdex/DexClass.h"
+#include "libdex/DexDebugInfo.h"
+#include "libdex/DexOpcodes.h"
+#include "libdex/DexProto.h"
+#include "libdex/InstrUtils.h"
+#include "libdex/SysUtil.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <errno.h>
+#include <assert.h>
+#include <inttypes.h>
+
+static const char* gProgName = "dexdump";
+
+enum OutputFormat {
+ OUTPUT_PLAIN = 0, /* default */
+ OUTPUT_XML, /* fancy */
+};
+
+/* command-line options */
+struct Options {
+ bool checksumOnly;
+ bool disassemble;
+ bool showFileHeaders;
+ bool showSectionHeaders;
+ bool ignoreBadChecksum;
+ bool dumpRegisterMaps;
+ OutputFormat outputFormat;
+ const char* tempFileName;
+ bool exportsOnly;
+ bool verbose;
+};
+
+struct Options gOptions;
+
+/* basic info about a field or method */
+struct FieldMethodInfo {
+ const char* classDescriptor;
+ const char* name;
+ const char* signature;
+};
+
+
+/* basic info about a prototype */
+struct ProtoInfo {
+ char* parameterTypes; // dynamically allocated with malloc
+ const char* returnType;
+};
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static inline u2 get2LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static inline u4 get4LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24);
+}
+
+/*
+ * Converts a single-character primitive type into its human-readable
+ * equivalent.
+ */
+static const char* primitiveTypeLabel(char typeChar)
+{
+ 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:
+ 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 char* descriptorToDot(const char* str)
+{
+ int targetLen = strlen(str);
+ int offset = 0;
+ int arrayDepth = 0;
+ char* newStr;
+
+ /* strip leading [s; will be added to end */
+ while (targetLen > 1 && str[offset] == '[') {
+ offset++;
+ targetLen--;
+ }
+ arrayDepth = offset;
+
+ if (targetLen == 1) {
+ /* primitive type */
+ str = primitiveTypeLabel(str[offset]);
+ offset = 0;
+ targetLen = strlen(str);
+ } else {
+ /* account for leading 'L' and trailing ';' */
+ if (targetLen >= 2 && str[offset] == 'L' &&
+ str[offset+targetLen-1] == ';')
+ {
+ targetLen -= 2;
+ offset++;
+ }
+ }
+
+ newStr = (char*)malloc(targetLen + arrayDepth * 2 +1);
+
+ /* copy class name over */
+ int i;
+ for (i = 0; i < targetLen; i++) {
+ char ch = str[offset + i];
+ newStr[i] = (ch == '/') ? '.' : ch;
+ }
+
+ /* add the appropriate number of brackets for arrays */
+ while (arrayDepth-- > 0) {
+ newStr[i++] = '[';
+ newStr[i++] = ']';
+ }
+ newStr[i] = '\0';
+ assert(i == targetLen + arrayDepth * 2);
+
+ return newStr;
+}
+
+/*
+ * Retrieves the class name portion of a type descriptor.
+ *
+ * Returns a newly-allocated string.
+ */
+static char* descriptorClassToName(const char* str)
+{
+ const char* lastSlash;
+ char* newStr;
+
+ /* reduce to just the class name, trimming trailing ';' */
+ lastSlash = strrchr(str, '/');
+ if (lastSlash == NULL)
+ lastSlash = str + 1; /* start past 'L' */
+ else
+ lastSlash++; /* start past '/' */
+
+ newStr = strdup(lastSlash);
+ newStr[strlen(lastSlash)-1] = '\0';
+
+ return newStr;
+}
+
+/*
+ * Returns a quoted string representing the boolean value.
+ */
+static const char* quotedBool(bool val)
+{
+ if (val)
+ return "\"true\"";
+ else
+ return "\"false\"";
+}
+
+static const char* quotedVisibility(u4 accessFlags)
+{
+ if ((accessFlags & ACC_PUBLIC) != 0)
+ return "\"public\"";
+ else if ((accessFlags & ACC_PROTECTED) != 0)
+ return "\"protected\"";
+ else if ((accessFlags & ACC_PRIVATE) != 0)
+ return "\"private\"";
+ else
+ return "\"package\"";
+}
+
+/*
+ * Count the number of '1' bits in a word.
+ */
+static int countOnes(u4 val)
+{
+ int count = 0;
+
+ val = val - ((val >> 1) & 0x55555555);
+ val = (val & 0x33333333) + ((val >> 2) & 0x33333333);
+ count = (((val + (val >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
+
+ return count;
+}
+
+/*
+ * Flag for use with createAccessFlagStr().
+ */
+enum AccessFor {
+ kAccessForClass = 0, kAccessForMethod = 1, kAccessForField = 2,
+ kAccessForMAX
+};
+
+/*
+ * Create a new string with human-readable access flags.
+ *
+ * In the base language the access_flags fields are type u2; in Dalvik
+ * they're u4.
+ */
+static char* createAccessFlagStr(u4 flags, AccessFor forWhat)
+{
+#define NUM_FLAGS 18
+ static const char* kAccessStrings[kAccessForMAX][NUM_FLAGS] = {
+ {
+ /* class, inner class */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "?", /* 0x0020 */
+ "?", /* 0x0040 */
+ "?", /* 0x0080 */
+ "?", /* 0x0100 */
+ "INTERFACE", /* 0x0200 */
+ "ABSTRACT", /* 0x0400 */
+ "?", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "ANNOTATION", /* 0x2000 */
+ "ENUM", /* 0x4000 */
+ "?", /* 0x8000 */
+ "VERIFIED", /* 0x10000 */
+ "OPTIMIZED", /* 0x20000 */
+ },
+ {
+ /* method */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "SYNCHRONIZED", /* 0x0020 */
+ "BRIDGE", /* 0x0040 */
+ "VARARGS", /* 0x0080 */
+ "NATIVE", /* 0x0100 */
+ "?", /* 0x0200 */
+ "ABSTRACT", /* 0x0400 */
+ "STRICT", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "?", /* 0x2000 */
+ "?", /* 0x4000 */
+ "MIRANDA", /* 0x8000 */
+ "CONSTRUCTOR", /* 0x10000 */
+ "DECLARED_SYNCHRONIZED", /* 0x20000 */
+ },
+ {
+ /* field */
+ "PUBLIC", /* 0x0001 */
+ "PRIVATE", /* 0x0002 */
+ "PROTECTED", /* 0x0004 */
+ "STATIC", /* 0x0008 */
+ "FINAL", /* 0x0010 */
+ "?", /* 0x0020 */
+ "VOLATILE", /* 0x0040 */
+ "TRANSIENT", /* 0x0080 */
+ "?", /* 0x0100 */
+ "?", /* 0x0200 */
+ "?", /* 0x0400 */
+ "?", /* 0x0800 */
+ "SYNTHETIC", /* 0x1000 */
+ "?", /* 0x2000 */
+ "ENUM", /* 0x4000 */
+ "?", /* 0x8000 */
+ "?", /* 0x10000 */
+ "?", /* 0x20000 */
+ },
+ };
+ const int kLongest = 21; /* strlen of longest string above */
+ int i, count;
+ char* str;
+ char* cp;
+
+ /*
+ * Allocate enough storage to hold the expected number of strings,
+ * plus a space between each. We over-allocate, using the longest
+ * string above as the base metric.
+ */
+ count = countOnes(flags);
+ cp = str = (char*) malloc(count * (kLongest+1) +1);
+
+ for (i = 0; i < NUM_FLAGS; i++) {
+ if (flags & 0x01) {
+ const char* accessStr = kAccessStrings[forWhat][i];
+ int len = strlen(accessStr);
+ if (cp != str)
+ *cp++ = ' ';
+
+ memcpy(cp, accessStr, len);
+ cp += len;
+ }
+ flags >>= 1;
+ }
+ *cp = '\0';
+
+ return str;
+}
+
+
+/*
+ * Copy character data from "data" to "out", converting non-ASCII values
+ * to printf format chars or an ASCII filler ('.' or '?').
+ *
+ * The output buffer must be able to hold (2*len)+1 bytes. The result is
+ * NUL-terminated.
+ */
+static void asciify(char* out, const unsigned char* data, size_t len)
+{
+ while (len--) {
+ if (*data < 0x20) {
+ /* could do more here, but we don't need them yet */
+ switch (*data) {
+ case '\0':
+ *out++ = '\\';
+ *out++ = '0';
+ break;
+ case '\n':
+ *out++ = '\\';
+ *out++ = 'n';
+ break;
+ default:
+ *out++ = '.';
+ break;
+ }
+ } else if (*data >= 0x80) {
+ *out++ = '?';
+ } else {
+ *out++ = *data;
+ }
+ data++;
+ }
+ *out = '\0';
+}
+
+/*
+ * Dump the file header.
+ */
+void dumpFileHeader(const DexFile* pDexFile)
+{
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ const DexHeader* pHeader = pDexFile->pHeader;
+ char sanitized[sizeof(pHeader->magic)*2 +1];
+
+ assert(sizeof(pHeader->magic) == sizeof(pOptHeader->magic));
+
+ if (pOptHeader != NULL) {
+ printf("Optimized DEX file header:\n");
+
+ asciify(sanitized, pOptHeader->magic, sizeof(pOptHeader->magic));
+ printf("magic : '%s'\n", sanitized);
+ printf("dex_offset : %d (0x%06x)\n",
+ pOptHeader->dexOffset, pOptHeader->dexOffset);
+ printf("dex_length : %d\n", pOptHeader->dexLength);
+ printf("deps_offset : %d (0x%06x)\n",
+ pOptHeader->depsOffset, pOptHeader->depsOffset);
+ printf("deps_length : %d\n", pOptHeader->depsLength);
+ printf("opt_offset : %d (0x%06x)\n",
+ pOptHeader->optOffset, pOptHeader->optOffset);
+ printf("opt_length : %d\n", pOptHeader->optLength);
+ printf("flags : %08x\n", pOptHeader->flags);
+ printf("checksum : %08x\n", pOptHeader->checksum);
+ printf("\n");
+ }
+
+ printf("DEX file header:\n");
+ asciify(sanitized, pHeader->magic, sizeof(pHeader->magic));
+ printf("magic : '%s'\n", sanitized);
+ printf("checksum : %08x\n", pHeader->checksum);
+ printf("signature : %02x%02x...%02x%02x\n",
+ pHeader->signature[0], pHeader->signature[1],
+ pHeader->signature[kSHA1DigestLen-2],
+ pHeader->signature[kSHA1DigestLen-1]);
+ printf("file_size : %d\n", pHeader->fileSize);
+ printf("header_size : %d\n", pHeader->headerSize);
+ printf("link_size : %d\n", pHeader->linkSize);
+ printf("link_off : %d (0x%06x)\n",
+ pHeader->linkOff, pHeader->linkOff);
+ printf("string_ids_size : %d\n", pHeader->stringIdsSize);
+ printf("string_ids_off : %d (0x%06x)\n",
+ pHeader->stringIdsOff, pHeader->stringIdsOff);
+ printf("type_ids_size : %d\n", pHeader->typeIdsSize);
+ printf("type_ids_off : %d (0x%06x)\n",
+ pHeader->typeIdsOff, pHeader->typeIdsOff);
+ printf("proto_ids_size : %d\n", pHeader->protoIdsSize);
+ printf("proto_ids_off : %d (0x%06x)\n",
+ pHeader->protoIdsOff, pHeader->protoIdsOff);
+ printf("field_ids_size : %d\n", pHeader->fieldIdsSize);
+ printf("field_ids_off : %d (0x%06x)\n",
+ pHeader->fieldIdsOff, pHeader->fieldIdsOff);
+ printf("method_ids_size : %d\n", pHeader->methodIdsSize);
+ printf("method_ids_off : %d (0x%06x)\n",
+ pHeader->methodIdsOff, pHeader->methodIdsOff);
+ printf("class_defs_size : %d\n", pHeader->classDefsSize);
+ printf("class_defs_off : %d (0x%06x)\n",
+ pHeader->classDefsOff, pHeader->classDefsOff);
+ printf("data_size : %d\n", pHeader->dataSize);
+ printf("data_off : %d (0x%06x)\n",
+ pHeader->dataOff, pHeader->dataOff);
+ printf("\n");
+}
+
+/*
+ * Dump the "table of contents" for the opt area.
+ */
+void dumpOptDirectory(const DexFile* pDexFile)
+{
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ if (pOptHeader == NULL)
+ return;
+
+ printf("OPT section contents:\n");
+
+ const u4* pOpt = (const u4*) ((u1*) pOptHeader + pOptHeader->optOffset);
+
+ if (*pOpt == 0) {
+ printf("(1.0 format, only class lookup table is present)\n\n");
+ return;
+ }
+
+ /*
+ * The "opt" section is in "chunk" format: a 32-bit identifier, a 32-bit
+ * length, then the data. Chunks start on 64-bit boundaries.
+ */
+ while (*pOpt != kDexChunkEnd) {
+ const char* verboseStr;
+
+ u4 size = *(pOpt+1);
+
+ switch (*pOpt) {
+ case kDexChunkClassLookup:
+ verboseStr = "class lookup hash table";
+ break;
+ case kDexChunkRegisterMaps:
+ verboseStr = "register maps";
+ break;
+ default:
+ verboseStr = "(unknown chunk type)";
+ break;
+ }
+
+ printf("Chunk %08x (%c%c%c%c) - %s (%d bytes)\n", *pOpt,
+ *pOpt >> 24, (char)(*pOpt >> 16), (char)(*pOpt >> 8), (char)*pOpt,
+ verboseStr, size);
+
+ size = (size + 8 + 7) & ~7;
+ pOpt += size / sizeof(u4);
+ }
+ printf("\n");
+}
+
+/*
+ * Dump a class_def_item.
+ */
+void dumpClassDef(DexFile* pDexFile, int idx)
+{
+ const DexClassDef* pClassDef;
+ const u1* pEncodedData;
+ DexClassData* pClassData;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+ if (pClassData == NULL) {
+ fprintf(stderr, "Trouble reading class data\n");
+ return;
+ }
+
+ printf("Class #%d header:\n", idx);
+ printf("class_idx : %d\n", pClassDef->classIdx);
+ printf("access_flags : %d (0x%04x)\n",
+ pClassDef->accessFlags, pClassDef->accessFlags);
+ printf("superclass_idx : %d\n", pClassDef->superclassIdx);
+ printf("interfaces_off : %d (0x%06x)\n",
+ pClassDef->interfacesOff, pClassDef->interfacesOff);
+ printf("source_file_idx : %d\n", pClassDef->sourceFileIdx);
+ printf("annotations_off : %d (0x%06x)\n",
+ pClassDef->annotationsOff, pClassDef->annotationsOff);
+ printf("class_data_off : %d (0x%06x)\n",
+ pClassDef->classDataOff, pClassDef->classDataOff);
+ printf("static_fields_size : %d\n", pClassData->header.staticFieldsSize);
+ printf("instance_fields_size: %d\n",
+ pClassData->header.instanceFieldsSize);
+ printf("direct_methods_size : %d\n", pClassData->header.directMethodsSize);
+ printf("virtual_methods_size: %d\n",
+ pClassData->header.virtualMethodsSize);
+ printf("\n");
+
+ free(pClassData);
+}
+
+/*
+ * Dump an interface that a class declares to implement.
+ */
+void dumpInterface(const DexFile* pDexFile, const DexTypeItem* pTypeItem,
+ int i)
+{
+ const char* interfaceName =
+ dexStringByTypeIdx(pDexFile, pTypeItem->typeIdx);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : '%s'\n", i, interfaceName);
+ } else {
+ char* dotted = descriptorToDot(interfaceName);
+ printf("<implements name=\"%s\">\n</implements>\n", dotted);
+ free(dotted);
+ }
+}
+
+/*
+ * Dump the catches table associated with the code.
+ */
+void dumpCatches(DexFile* pDexFile, const DexCode* pCode)
+{
+ u4 triesSize = pCode->triesSize;
+
+ if (triesSize == 0) {
+ printf(" catches : (none)\n");
+ return;
+ }
+
+ printf(" catches : %d\n", triesSize);
+
+ const DexTry* pTries = dexGetTries(pCode);
+ u4 i;
+
+ for (i = 0; i < triesSize; i++) {
+ const DexTry* pTry = &pTries[i];
+ u4 start = pTry->startAddr;
+ u4 end = start + pTry->insnCount;
+ DexCatchIterator iterator;
+
+ printf(" 0x%04x - 0x%04x\n", start, end);
+
+ dexCatchIteratorInit(&iterator, pCode, pTry->handlerOff);
+
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+ const char* descriptor;
+
+ if (handler == NULL) {
+ break;
+ }
+
+ descriptor = (handler->typeIdx == kDexNoIndex) ? "<any>" :
+ dexStringByTypeIdx(pDexFile, handler->typeIdx);
+
+ printf(" %s -> 0x%04x\n", descriptor,
+ handler->address);
+ }
+ }
+}
+
+static int dumpPositionsCb(void * /* cnxt */, u4 address, u4 lineNum)
+{
+ printf(" 0x%04x line=%d\n", address, lineNum);
+ return 0;
+}
+
+/*
+ * Dump the positions list.
+ */
+void dumpPositions(DexFile* pDexFile, const DexCode* pCode,
+ const DexMethod *pDexMethod)
+{
+ printf(" positions : \n");
+ const DexMethodId *pMethodId
+ = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ const char *classDescriptor
+ = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+ pDexMethod->accessFlags, dumpPositionsCb, NULL, NULL);
+}
+
+static void dumpLocalsCb(void * /* cnxt */, u2 reg, u4 startAddress,
+ u4 endAddress, const char *name, const char *descriptor,
+ const char *signature)
+{
+ printf(" 0x%04x - 0x%04x reg=%d %s %s %s\n",
+ startAddress, endAddress, reg, name, descriptor,
+ signature);
+}
+
+/*
+ * Dump the locals list.
+ */
+void dumpLocals(DexFile* pDexFile, const DexCode* pCode,
+ const DexMethod *pDexMethod)
+{
+ printf(" locals : \n");
+
+ const DexMethodId *pMethodId
+ = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ const char *classDescriptor
+ = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx,
+ pDexMethod->accessFlags, NULL, dumpLocalsCb, NULL);
+}
+
+/*
+ * Get information about a method.
+ */
+bool getMethodInfo(DexFile* pDexFile, u4 methodIdx, FieldMethodInfo* pMethInfo)
+{
+ const DexMethodId* pMethodId;
+
+ if (methodIdx >= pDexFile->pHeader->methodIdsSize)
+ return false;
+
+ pMethodId = dexGetMethodId(pDexFile, methodIdx);
+ pMethInfo->name = dexStringById(pDexFile, pMethodId->nameIdx);
+ pMethInfo->signature = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+ pMethInfo->classDescriptor =
+ dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+ return true;
+}
+
+/*
+ * Get information about a field.
+ */
+bool getFieldInfo(DexFile* pDexFile, u4 fieldIdx, FieldMethodInfo* pFieldInfo)
+{
+ const DexFieldId* pFieldId;
+
+ if (fieldIdx >= pDexFile->pHeader->fieldIdsSize)
+ return false;
+
+ pFieldId = dexGetFieldId(pDexFile, fieldIdx);
+ pFieldInfo->name = dexStringById(pDexFile, pFieldId->nameIdx);
+ pFieldInfo->signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ pFieldInfo->classDescriptor =
+ dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+ return true;
+}
+
+/*
+ * Get information about a ProtoId.
+ */
+bool getProtoInfo(DexFile* pDexFile, u4 protoIdx, ProtoInfo* pProtoInfo)
+{
+ if (protoIdx >= pDexFile->pHeader->protoIdsSize) {
+ return false;
+ }
+
+ const DexProtoId* protoId = dexGetProtoId(pDexFile, protoIdx);
+
+ // Get string for return type.
+ if (protoId->returnTypeIdx >= pDexFile->pHeader->typeIdsSize) {
+ return false;
+ }
+ pProtoInfo->returnType = dexStringByTypeIdx(pDexFile, protoId->returnTypeIdx);
+
+ // Build string for parameter types.
+ size_t bufSize = 1;
+ char* buf = (char*)malloc(bufSize);
+ if (buf == NULL) {
+ return false;
+ }
+
+ buf[0] = '\0';
+ size_t bufUsed = 1;
+
+ const DexTypeList* paramTypes = dexGetProtoParameters(pDexFile, protoId);
+ if (paramTypes == NULL) {
+ // No parameters.
+ pProtoInfo->parameterTypes = buf;
+ return true;
+ }
+
+ for (u4 i = 0; i < paramTypes->size; ++i) {
+ if (paramTypes->list[i].typeIdx >= pDexFile->pHeader->typeIdsSize) {
+ free(buf);
+ return false;
+ }
+ const char* param = dexStringByTypeIdx(pDexFile, paramTypes->list[i].typeIdx);
+ size_t paramLen = strlen(param);
+ size_t newUsed = bufUsed + paramLen;
+ if (newUsed > bufSize) {
+ char* newBuf = (char*)realloc(buf, newUsed);
+ if (newBuf == NULL) {
+ free(buf);
+ return false;
+ }
+ buf = newBuf;
+ bufSize = newUsed;
+ }
+ memcpy(buf + bufUsed - 1, param, paramLen + 1);
+ bufUsed = newUsed;
+ }
+
+ pProtoInfo->parameterTypes = buf;
+ return true;
+}
+
+/*
+ * Look up a class' descriptor.
+ */
+const char* getClassDescriptor(DexFile* pDexFile, u4 classIdx)
+{
+ return dexStringByTypeIdx(pDexFile, classIdx);
+}
+
+/*
+ * Helper for dumpInstruction(), which builds the string
+ * representation for the index in the given instruction. This will
+ * first try to use the given buffer, but if the result won't fit,
+ * then this will allocate a new buffer to hold the result. A pointer
+ * to the buffer which holds the full result is always returned, and
+ * this can be compared with the one passed in, to see if the result
+ * needs to be free()d.
+ */
+static char* indexString(DexFile* pDexFile, const DecodedInstruction* pDecInsn, size_t bufSize)
+{
+ char* buf = (char*)malloc(bufSize);
+ if (buf == NULL) {
+ return NULL;
+ }
+
+ int outSize;
+ u4 index;
+ u4 secondaryIndex = 0;
+ u4 width;
+
+ /* TODO: Make the index *always* be in field B, to simplify this code. */
+ switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
+ case kFmt20bc:
+ case kFmt21c:
+ case kFmt35c:
+ case kFmt35ms:
+ case kFmt3rc:
+ case kFmt3rms:
+ case kFmt35mi:
+ case kFmt3rmi:
+ index = pDecInsn->vB;
+ width = 4;
+ break;
+ case kFmt31c:
+ index = pDecInsn->vB;
+ width = 8;
+ break;
+ case kFmt22c:
+ case kFmt22cs:
+ index = pDecInsn->vC;
+ width = 4;
+ break;
+ case kFmt45cc:
+ case kFmt4rcc:
+ index = pDecInsn->vB; // method index
+ secondaryIndex = pDecInsn->arg[4]; // proto index
+ width = 4;
+ break;
+ default:
+ index = 0;
+ width = 4;
+ break;
+ }
+
+ switch (pDecInsn->indexType) {
+ case kIndexUnknown:
+ /*
+ * This function shouldn't ever get called for this type, but do
+ * something sensible here, just to help with debugging.
+ */
+ outSize = snprintf(buf, bufSize, "<unknown-index>");
+ break;
+ case kIndexNone:
+ /*
+ * This function shouldn't ever get called for this type, but do
+ * something sensible here, just to help with debugging.
+ */
+ outSize = snprintf(buf, bufSize, "<no-index>");
+ break;
+ case kIndexVaries:
+ /*
+ * This one should never show up in a dexdump, so no need to try
+ * to get fancy here.
+ */
+ outSize = snprintf(buf, bufSize, "<index-varies> // thing@%0*x",
+ width, index);
+ break;
+ case kIndexTypeRef:
+ if (index < pDexFile->pHeader->typeIdsSize) {
+ outSize = snprintf(buf, bufSize, "%s // type@%0*x",
+ getClassDescriptor(pDexFile, index), width, index);
+ } else {
+ outSize = snprintf(buf, bufSize, "<type?> // type@%0*x", width, index);
+ }
+ break;
+ case kIndexStringRef:
+ if (index < pDexFile->pHeader->stringIdsSize) {
+ outSize = snprintf(buf, bufSize, "\"%s\" // string@%0*x",
+ dexStringById(pDexFile, index), width, index);
+ } else {
+ outSize = snprintf(buf, bufSize, "<string?> // string@%0*x",
+ width, index);
+ }
+ break;
+ case kIndexMethodRef:
+ {
+ FieldMethodInfo methInfo;
+ if (getMethodInfo(pDexFile, index, &methInfo)) {
+ outSize = snprintf(buf, bufSize, "%s.%s:%s // method@%0*x",
+ methInfo.classDescriptor, methInfo.name,
+ methInfo.signature, width, index);
+ free((void *) methInfo.signature);
+ } else {
+ outSize = snprintf(buf, bufSize, "<method?> // method@%0*x",
+ width, index);
+ }
+ }
+ break;
+ case kIndexFieldRef:
+ {
+ FieldMethodInfo fieldInfo;
+ if (getFieldInfo(pDexFile, index, &fieldInfo)) {
+ outSize = snprintf(buf, bufSize, "%s.%s:%s // field@%0*x",
+ fieldInfo.classDescriptor, fieldInfo.name,
+ fieldInfo.signature, width, index);
+ } else {
+ outSize = snprintf(buf, bufSize, "<field?> // field@%0*x",
+ width, index);
+ }
+ }
+ break;
+ case kIndexInlineMethod:
+ outSize = snprintf(buf, bufSize, "[%0*x] // inline #%0*x",
+ width, index, width, index);
+ break;
+ case kIndexVtableOffset:
+ outSize = snprintf(buf, bufSize, "[%0*x] // vtable #%0*x",
+ width, index, width, index);
+ break;
+ case kIndexFieldOffset:
+ outSize = snprintf(buf, bufSize, "[obj+%0*x]", width, index);
+ break;
+ case kIndexMethodAndProtoRef:
+ {
+ FieldMethodInfo methInfo;
+ ProtoInfo protoInfo;
+ protoInfo.parameterTypes = NULL;
+ if (getMethodInfo(pDexFile, index, &methInfo) &&
+ getProtoInfo(pDexFile, secondaryIndex, &protoInfo)) {
+ outSize = snprintf(buf, bufSize, "%s.%s:%s, (%s)%s // method@%0*x, proto@%0*x",
+ methInfo.classDescriptor, methInfo.name, methInfo.signature,
+ protoInfo.parameterTypes, protoInfo.returnType,
+ width, index, width, secondaryIndex);
+ } else {
+ outSize = snprintf(buf, bufSize, "<method?>, <proto?> // method@%0*x, proto@%0*x",
+ width, index, width, secondaryIndex);
+ }
+ free(protoInfo.parameterTypes);
+ }
+ break;
+ case kIndexCallSiteRef:
+ outSize = snprintf(buf, bufSize, "call_site@%0*x", width, index);
+ break;
+ case kIndexMethodHandleRef:
+ outSize = snprintf(buf, bufSize, "methodhandle@%0*x", width, index);
+ break;
+ case kIndexProtoRef:
+ {
+ ProtoInfo protoInfo;
+ if (getProtoInfo(pDexFile, index, &protoInfo)) {
+ outSize = snprintf(buf, bufSize, "(%s)%s // proto@%0*x",
+ protoInfo.parameterTypes, protoInfo.returnType,
+ width, index);
+
+ } else {
+ outSize = snprintf(buf, bufSize, "<proto?> // proto@%0*x",
+ width, secondaryIndex);
+ }
+ free(protoInfo.parameterTypes);
+ }
+ break;
+ default:
+ outSize = snprintf(buf, bufSize, "<?>");
+ break;
+ }
+
+ if (outSize >= (int) bufSize) {
+ /*
+ * The buffer wasn't big enough; allocate and retry. Note:
+ * snprintf() doesn't count the '\0' as part of its returned
+ * size, so we add explicit space for it here.
+ */
+ free(buf);
+ return indexString(pDexFile, pDecInsn, outSize + 1);
+ } else {
+ return buf;
+ }
+}
+
+/*
+ * Dump a single instruction.
+ */
+void dumpInstruction(DexFile* pDexFile, const DexCode* pCode, int insnIdx,
+ int insnWidth, const DecodedInstruction* pDecInsn)
+{
+ const u2* insns = pCode->insns;
+ int i;
+
+ // Address of instruction (expressed as byte offset).
+ printf("%06zx:", ((u1*)insns - pDexFile->baseAddr) + insnIdx*2);
+
+ for (i = 0; i < 8; i++) {
+ if (i < insnWidth) {
+ if (i == 7) {
+ printf(" ... ");
+ } else {
+ /* print 16-bit value in little-endian order */
+ const u1* bytePtr = (const u1*) &insns[insnIdx+i];
+ printf(" %02x%02x", bytePtr[0], bytePtr[1]);
+ }
+ } else {
+ fputs(" ", stdout);
+ }
+ }
+
+ if (pDecInsn->opcode == OP_NOP) {
+ u2 instr = get2LE((const u1*) &insns[insnIdx]);
+ if (instr == kPackedSwitchSignature) {
+ printf("|%04x: packed-switch-data (%d units)",
+ insnIdx, insnWidth);
+ } else if (instr == kSparseSwitchSignature) {
+ printf("|%04x: sparse-switch-data (%d units)",
+ insnIdx, insnWidth);
+ } else if (instr == kArrayDataSignature) {
+ printf("|%04x: array-data (%d units)",
+ insnIdx, insnWidth);
+ } else {
+ printf("|%04x: nop // spacer", insnIdx);
+ }
+ } else {
+ printf("|%04x: %s", insnIdx, dexGetOpcodeName(pDecInsn->opcode));
+ }
+
+ // Provide an initial buffer that usually suffices, although indexString()
+ // may reallocate the buffer if more space is needed.
+ char* indexBuf = NULL;
+ if (pDecInsn->indexType != kIndexNone) {
+ indexBuf = indexString(pDexFile, pDecInsn, 200);
+ }
+
+ switch (dexGetFormatFromOpcode(pDecInsn->opcode)) {
+ case kFmt10x: // op
+ break;
+ case kFmt12x: // op vA, vB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt11n: // op vA, #+B
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, (s4)pDecInsn->vB, (u1)pDecInsn->vB);
+ break;
+ case kFmt11x: // op vAA
+ printf(" v%d", pDecInsn->vA);
+ break;
+ case kFmt10t: // op +AA
+ case kFmt20t: // op +AAAA
+ {
+ s4 targ = (s4) pDecInsn->vA;
+ printf(" %04x // %c%04x",
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt22x: // op vAA, vBBBB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt21t: // op vAA, +BBBB
+ {
+ s4 targ = (s4) pDecInsn->vB;
+ printf(" v%d, %04x // %c%04x", pDecInsn->vA,
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt21s: // op vAA, #+BBBB
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, (s4)pDecInsn->vB, (u2)pDecInsn->vB);
+ break;
+ case kFmt21h: // op vAA, #+BBBB0000[00000000]
+ // The printed format varies a bit based on the actual opcode.
+ if (pDecInsn->opcode == OP_CONST_HIGH16) {
+ s4 value = pDecInsn->vB << 16;
+ printf(" v%d, #int %d // #%x",
+ pDecInsn->vA, value, (u2)pDecInsn->vB);
+ } else {
+ s8 value = ((s8) pDecInsn->vB) << 48;
+ printf(" v%d, #long %" PRId64 " // #%x",
+ pDecInsn->vA, value, (u2)pDecInsn->vB);
+ }
+ break;
+ case kFmt21c: // op vAA, thing@BBBB
+ case kFmt31c: // op vAA, thing@BBBBBBBB
+ printf(" v%d, %s", pDecInsn->vA, indexBuf);
+ break;
+ case kFmt23x: // op vAA, vBB, vCC
+ printf(" v%d, v%d, v%d", pDecInsn->vA, pDecInsn->vB, pDecInsn->vC);
+ break;
+ case kFmt22b: // op vAA, vBB, #+CC
+ printf(" v%d, v%d, #int %d // #%02x",
+ pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u1)pDecInsn->vC);
+ break;
+ case kFmt22t: // op vA, vB, +CCCC
+ {
+ s4 targ = (s4) pDecInsn->vC;
+ printf(" v%d, v%d, %04x // %c%04x", pDecInsn->vA, pDecInsn->vB,
+ insnIdx + targ,
+ (targ < 0) ? '-' : '+',
+ (targ < 0) ? -targ : targ);
+ }
+ break;
+ case kFmt22s: // op vA, vB, #+CCCC
+ printf(" v%d, v%d, #int %d // #%04x",
+ pDecInsn->vA, pDecInsn->vB, (s4)pDecInsn->vC, (u2)pDecInsn->vC);
+ break;
+ case kFmt22c: // op vA, vB, thing@CCCC
+ case kFmt22cs: // [opt] op vA, vB, field offset CCCC
+ printf(" v%d, v%d, %s", pDecInsn->vA, pDecInsn->vB, indexBuf);
+ break;
+ case kFmt30t:
+ printf(" #%08x", pDecInsn->vA);
+ break;
+ case kFmt31i: // op vAA, #+BBBBBBBB
+ {
+ /* this is often, but not always, a float */
+ union {
+ float f;
+ u4 i;
+ } conv;
+ conv.i = pDecInsn->vB;
+ printf(" v%d, #float %f // #%08x",
+ pDecInsn->vA, conv.f, pDecInsn->vB);
+ }
+ break;
+ case kFmt31t: // op vAA, offset +BBBBBBBB
+ printf(" v%d, %08x // +%08x",
+ pDecInsn->vA, insnIdx + pDecInsn->vB, pDecInsn->vB);
+ break;
+ case kFmt32x: // op vAAAA, vBBBB
+ printf(" v%d, v%d", pDecInsn->vA, pDecInsn->vB);
+ break;
+ case kFmt35c: // op {vC, vD, vE, vF, vG}, thing@BBBB
+ case kFmt35ms: // [opt] invoke-virtual+super
+ case kFmt35mi: // [opt] inline invoke
+ {
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->arg[i]);
+ else
+ printf(", v%d", pDecInsn->arg[i]);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
+ case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
+ case kFmt3rms: // [opt] invoke-virtual+super/range
+ case kFmt3rmi: // [opt] execute-inline/range
+ {
+ /*
+ * This doesn't match the "dx" output when some of the args are
+ * 64-bit values -- dx only shows the first register.
+ */
+ fputs(" {", stdout);
+ for (i = 0; i < (int) pDecInsn->vA; i++) {
+ if (i == 0)
+ printf("v%d", pDecInsn->vC + i);
+ else
+ printf(", v%d", pDecInsn->vC + i);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
+ case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ {
+ /* this is often, but not always, a double */
+ union {
+ double d;
+ u8 j;
+ } conv;
+ conv.j = pDecInsn->vB_wide;
+ printf(" v%d, #double %f // #%016" PRIx64,
+ pDecInsn->vA, conv.d, pDecInsn->vB_wide);
+ }
+ break;
+ case kFmt00x: // unknown op or breakpoint
+ break;
+ case kFmt45cc:
+ {
+ fputs(" {", stdout);
+ printf("v%d", pDecInsn->vC);
+ for (int i = 0; i < (int) pDecInsn->vA - 1; ++i) {
+ printf(", v%d", pDecInsn->arg[i]);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
+ case kFmt4rcc:
+ {
+ fputs(" {", stdout);
+ printf("v%d", pDecInsn->vC);
+ for (int i = 1; i < (int) pDecInsn->vA; ++i) {
+ printf(", v%d", pDecInsn->vC + i);
+ }
+ printf("}, %s", indexBuf);
+ }
+ break;
+ default:
+ printf(" ???");
+ break;
+ }
+
+ putchar('\n');
+
+ free(indexBuf);
+}
+
+/*
+ * Dump a bytecode disassembly.
+ */
+void dumpBytecodes(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+ const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+ const u2* insns;
+ int insnIdx;
+ FieldMethodInfo methInfo;
+ int startAddr;
+ char* className = NULL;
+
+ assert(pCode->insnsSize > 0);
+ insns = pCode->insns;
+
+ methInfo.classDescriptor =
+ methInfo.name =
+ methInfo.signature = NULL;
+
+ getMethodInfo(pDexFile, pDexMethod->methodIdx, &methInfo);
+ startAddr = ((u1*)pCode - pDexFile->baseAddr);
+ className = descriptorToDot(methInfo.classDescriptor);
+
+ printf("%06x: |[%06x] %s.%s:%s\n",
+ startAddr, startAddr,
+ className, methInfo.name, methInfo.signature);
+ free((void *) methInfo.signature);
+
+ insnIdx = 0;
+ while (insnIdx < (int) pCode->insnsSize) {
+ int insnWidth;
+ DecodedInstruction decInsn;
+ u2 instr;
+
+ /*
+ * Note: This code parallels the function
+ * dexGetWidthFromInstruction() in InstrUtils.c, but this version
+ * can deal with data in either endianness.
+ *
+ * TODO: Figure out if this really matters, and possibly change
+ * this to just use dexGetWidthFromInstruction().
+ */
+ instr = get2LE((const u1*)insns);
+ if (instr == kPackedSwitchSignature) {
+ insnWidth = 4 + get2LE((const u1*)(insns+1)) * 2;
+ } else if (instr == kSparseSwitchSignature) {
+ insnWidth = 2 + get2LE((const u1*)(insns+1)) * 4;
+ } else if (instr == kArrayDataSignature) {
+ int width = get2LE((const u1*)(insns+1));
+ int size = get2LE((const u1*)(insns+2)) |
+ (get2LE((const u1*)(insns+3))<<16);
+ // The plus 1 is to round up for odd size and width.
+ insnWidth = 4 + ((size * width) + 1) / 2;
+ } else {
+ Opcode opcode = dexOpcodeFromCodeUnit(instr);
+ insnWidth = dexGetWidthFromOpcode(opcode);
+ if (insnWidth == 0) {
+ fprintf(stderr,
+ "GLITCH: zero-width instruction at idx=0x%04x\n", insnIdx);
+ break;
+ }
+ }
+
+ dexDecodeInstruction(insns, &decInsn);
+ dumpInstruction(pDexFile, pCode, insnIdx, insnWidth, &decInsn);
+
+ insns += insnWidth;
+ insnIdx += insnWidth;
+ }
+
+ free(className);
+}
+
+/*
+ * Dump a "code" struct.
+ */
+void dumpCode(DexFile* pDexFile, const DexMethod* pDexMethod)
+{
+ const DexCode* pCode = dexGetCode(pDexFile, pDexMethod);
+
+ printf(" registers : %d\n", pCode->registersSize);
+ printf(" ins : %d\n", pCode->insSize);
+ printf(" outs : %d\n", pCode->outsSize);
+ printf(" insns size : %d 16-bit code units\n", pCode->insnsSize);
+
+ if (gOptions.disassemble)
+ dumpBytecodes(pDexFile, pDexMethod);
+
+ dumpCatches(pDexFile, pCode);
+ /* both of these are encoded in debug info */
+ dumpPositions(pDexFile, pCode, pDexMethod);
+ dumpLocals(pDexFile, pCode, pDexMethod);
+}
+
+/*
+ * Dump a method.
+ */
+void dumpMethod(DexFile* pDexFile, const DexMethod* pDexMethod, int i)
+{
+ const DexMethodId* pMethodId;
+ const char* backDescriptor;
+ const char* name;
+ char* typeDescriptor = NULL;
+ char* accessStr = NULL;
+
+ if (gOptions.exportsOnly &&
+ (pDexMethod->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ name = dexStringById(pDexFile, pMethodId->nameIdx);
+ typeDescriptor = dexCopyDescriptorFromMethodId(pDexFile, pMethodId);
+
+ backDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
+
+ accessStr = createAccessFlagStr(pDexMethod->accessFlags,
+ kAccessForMethod);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pDexMethod->accessFlags, accessStr);
+
+ if (pDexMethod->codeOff == 0) {
+ printf(" code : (none)\n");
+ } else {
+ printf(" code -\n");
+ dumpCode(pDexFile, pDexMethod);
+ }
+
+ if (gOptions.disassemble)
+ putchar('\n');
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ bool constructor = (name[0] == '<');
+
+ if (constructor) {
+ char* tmp;
+
+ tmp = descriptorClassToName(backDescriptor);
+ printf("<constructor name=\"%s\"\n", tmp);
+ free(tmp);
+
+ tmp = descriptorToDot(backDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+ } else {
+ printf("<method name=\"%s\"\n", name);
+
+ const char* returnType = strrchr(typeDescriptor, ')');
+ if (returnType == NULL) {
+ fprintf(stderr, "bad method type descriptor '%s'\n",
+ typeDescriptor);
+ goto bail;
+ }
+
+ char* tmp = descriptorToDot(returnType+1);
+ printf(" return=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" abstract=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" native=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_NATIVE) != 0));
+
+ bool isSync =
+ (pDexMethod->accessFlags & ACC_SYNCHRONIZED) != 0 ||
+ (pDexMethod->accessFlags & ACC_DECLARED_SYNCHRONIZED) != 0;
+ printf(" synchronized=%s\n", quotedBool(isSync));
+ }
+
+ printf(" static=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pDexMethod->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pDexMethod->accessFlags));
+
+ printf(">\n");
+
+ /*
+ * Parameters.
+ */
+ if (typeDescriptor[0] != '(') {
+ fprintf(stderr, "ERROR: bad descriptor '%s'\n", typeDescriptor);
+ goto bail;
+ }
+
+ char tmpBuf[strlen(typeDescriptor)+1]; /* more than big enough */
+ int argNum = 0;
+
+ const char* base = typeDescriptor+1;
+
+ while (*base != ')') {
+ char* cp = tmpBuf;
+
+ while (*base == '[')
+ *cp++ = *base++;
+
+ if (*base == 'L') {
+ /* copy through ';' */
+ do {
+ *cp = *base++;
+ } while (*cp++ != ';');
+ } else {
+ /* primitive char, copy it */
+ if (strchr("ZBCSIFJD", *base) == NULL) {
+ fprintf(stderr, "ERROR: bad method signature '%s'\n", base);
+ goto bail;
+ }
+ *cp++ = *base++;
+ }
+
+ /* null terminate and display */
+ *cp++ = '\0';
+
+ char* tmp = descriptorToDot(tmpBuf);
+ printf("<parameter name=\"arg%d\" type=\"%s\">\n</parameter>\n",
+ argNum++, tmp);
+ free(tmp);
+ }
+
+ if (constructor)
+ printf("</constructor>\n");
+ else
+ printf("</method>\n");
+ }
+
+bail:
+ free(typeDescriptor);
+ free(accessStr);
+}
+
+/*
+ * Dump a static (class) field.
+ */
+void dumpSField(const DexFile* pDexFile, const DexField* pSField, int i)
+{
+ const DexFieldId* pFieldId;
+ const char* backDescriptor;
+ const char* name;
+ const char* typeDescriptor;
+ char* accessStr;
+
+ if (gOptions.exportsOnly &&
+ (pSField->accessFlags & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
+ {
+ return;
+ }
+
+ pFieldId = dexGetFieldId(pDexFile, pSField->fieldIdx);
+ name = dexStringById(pDexFile, pFieldId->nameIdx);
+ typeDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
+ backDescriptor = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
+
+ accessStr = createAccessFlagStr(pSField->accessFlags, kAccessForField);
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" #%d : (in %s)\n", i, backDescriptor);
+ printf(" name : '%s'\n", name);
+ printf(" type : '%s'\n", typeDescriptor);
+ printf(" access : 0x%04x (%s)\n",
+ pSField->accessFlags, accessStr);
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* tmp;
+
+ printf("<field name=\"%s\"\n", name);
+
+ tmp = descriptorToDot(typeDescriptor);
+ printf(" type=\"%s\"\n", tmp);
+ free(tmp);
+
+ printf(" transient=%s\n",
+ quotedBool((pSField->accessFlags & ACC_TRANSIENT) != 0));
+ printf(" volatile=%s\n",
+ quotedBool((pSField->accessFlags & ACC_VOLATILE) != 0));
+ // "value=" not knowable w/o parsing annotations
+ printf(" static=%s\n",
+ quotedBool((pSField->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pSField->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pSField->accessFlags));
+ printf(">\n</field>\n");
+ }
+
+ free(accessStr);
+}
+
+/*
+ * Dump an instance field.
+ */
+void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i)
+{
+ dumpSField(pDexFile, pIField, i);
+}
+
+/*
+ * Dump the class.
+ *
+ * Note "idx" is a DexClassDef index, not a DexTypeId index.
+ *
+ * If "*pLastPackage" is NULL or does not match the current class' package,
+ * the value will be replaced with a newly-allocated string.
+ */
+void dumpClass(DexFile* pDexFile, int idx, char** pLastPackage)
+{
+ const DexTypeList* pInterfaces;
+ const DexClassDef* pClassDef;
+ DexClassData* pClassData = NULL;
+ const u1* pEncodedData;
+ const char* fileName;
+ const char* classDescriptor;
+ const char* superclassDescriptor;
+ char* accessStr = NULL;
+ int i;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+
+ if (gOptions.exportsOnly && (pClassDef->accessFlags & ACC_PUBLIC) == 0) {
+ //printf("<!-- omitting non-public class %s -->\n",
+ // classDescriptor);
+ goto bail;
+ }
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+
+ if (pClassData == NULL) {
+ printf("Trouble reading class data (#%d)\n", idx);
+ goto bail;
+ }
+
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ /*
+ * For the XML output, show the package name. Ideally we'd gather
+ * up the classes, sort them, and dump them alphabetically so the
+ * package name wouldn't jump around, but that's not a great plan
+ * for something that needs to run on the device.
+ */
+ if (!(classDescriptor[0] == 'L' &&
+ classDescriptor[strlen(classDescriptor)-1] == ';'))
+ {
+ /* arrays and primitives should not be defined explicitly */
+ fprintf(stderr, "Malformed class name '%s'\n", classDescriptor);
+ /* keep going? */
+ } else if (gOptions.outputFormat == OUTPUT_XML) {
+ char* mangle;
+ char* lastSlash;
+ char* cp;
+
+ mangle = strdup(classDescriptor + 1);
+ mangle[strlen(mangle)-1] = '\0';
+
+ /* reduce to just the package name */
+ lastSlash = strrchr(mangle, '/');
+ if (lastSlash != NULL) {
+ *lastSlash = '\0';
+ } else {
+ *mangle = '\0';
+ }
+
+ for (cp = mangle; *cp != '\0'; cp++) {
+ if (*cp == '/')
+ *cp = '.';
+ }
+
+ if (*pLastPackage == NULL || strcmp(mangle, *pLastPackage) != 0) {
+ /* start of a new package */
+ if (*pLastPackage != NULL)
+ printf("</package>\n");
+ printf("<package name=\"%s\"\n>\n", mangle);
+ free(*pLastPackage);
+ *pLastPackage = mangle;
+ } else {
+ free(mangle);
+ }
+ }
+
+ accessStr = createAccessFlagStr(pClassDef->accessFlags, kAccessForClass);
+
+ if (pClassDef->superclassIdx == kDexNoIndex) {
+ superclassDescriptor = NULL;
+ } else {
+ superclassDescriptor =
+ dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf("Class #%d -\n", idx);
+ printf(" Class descriptor : '%s'\n", classDescriptor);
+ printf(" Access flags : 0x%04x (%s)\n",
+ pClassDef->accessFlags, accessStr);
+
+ if (superclassDescriptor != NULL)
+ printf(" Superclass : '%s'\n", superclassDescriptor);
+
+ printf(" Interfaces -\n");
+ } else {
+ char* tmp;
+
+ tmp = descriptorClassToName(classDescriptor);
+ printf("<class name=\"%s\"\n", tmp);
+ free(tmp);
+
+ if (superclassDescriptor != NULL) {
+ tmp = descriptorToDot(superclassDescriptor);
+ printf(" extends=\"%s\"\n", tmp);
+ free(tmp);
+ }
+ printf(" abstract=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_ABSTRACT) != 0));
+ printf(" static=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_STATIC) != 0));
+ printf(" final=%s\n",
+ quotedBool((pClassDef->accessFlags & ACC_FINAL) != 0));
+ // "deprecated=" not knowable w/o parsing annotations
+ printf(" visibility=%s\n",
+ quotedVisibility(pClassDef->accessFlags));
+ printf(">\n");
+ }
+ pInterfaces = dexGetInterfacesList(pDexFile, pClassDef);
+ if (pInterfaces != NULL) {
+ for (i = 0; i < (int) pInterfaces->size; i++)
+ dumpInterface(pDexFile, dexGetTypeItem(pInterfaces, i), i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Static fields -\n");
+ for (i = 0; i < (int) pClassData->header.staticFieldsSize; i++) {
+ dumpSField(pDexFile, &pClassData->staticFields[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Instance fields -\n");
+ for (i = 0; i < (int) pClassData->header.instanceFieldsSize; i++) {
+ dumpIField(pDexFile, &pClassData->instanceFields[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Direct methods -\n");
+ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+ dumpMethod(pDexFile, &pClassData->directMethods[i], i);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN)
+ printf(" Virtual methods -\n");
+ for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+ dumpMethod(pDexFile, &pClassData->virtualMethods[i], i);
+ }
+
+ // TODO: Annotations.
+
+ if (pClassDef->sourceFileIdx != kDexNoIndex)
+ fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx);
+ else
+ fileName = "unknown";
+
+ if (gOptions.outputFormat == OUTPUT_PLAIN) {
+ printf(" source_file_idx : %d (%s)\n",
+ pClassDef->sourceFileIdx, fileName);
+ printf("\n");
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML) {
+ printf("</class>\n");
+ }
+
+bail:
+ free(pClassData);
+ free(accessStr);
+}
+
+
+/*
+ * Dump a map in the "differential" format.
+ *
+ * TODO: show a hex dump of the compressed data. (We can show the
+ * uncompressed data if we move the compression code to libdex; otherwise
+ * it's too complex to merit a fast & fragile implementation here.)
+ */
+void dumpDifferentialCompressedMap(const u1** pData)
+{
+ const u1* data = *pData;
+ const u1* dataStart = data -1; // format byte already removed
+ u1 regWidth;
+ u2 numEntries;
+
+ /* standard header */
+ regWidth = *data++;
+ numEntries = *data++;
+ numEntries |= (*data++) << 8;
+
+ /* compressed data begins with the compressed data length */
+ int compressedLen = readUnsignedLeb128(&data);
+ int addrWidth = 1;
+ if ((*data & 0x80) != 0)
+ addrWidth++;
+
+ int origLen = 4 + (addrWidth + regWidth) * numEntries;
+ int compLen = (data - dataStart) + compressedLen;
+
+ printf(" (differential compression %d -> %d [%d -> %d])\n",
+ origLen, compLen,
+ (addrWidth + regWidth) * numEntries, compressedLen);
+
+ /* skip past end of entry */
+ data += compressedLen;
+
+ *pData = data;
+}
+
+/*
+ * Dump register map contents of the current method.
+ *
+ * "*pData" should point to the start of the register map data. Advances
+ * "*pData" to the start of the next map.
+ */
+void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx,
+ const u1** pData)
+{
+ const u1* data = *pData;
+ const DexMethodId* pMethodId;
+ const char* name;
+ int offset = data - (u1*) pDexFile->pOptHeader;
+
+ pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx);
+ name = dexStringById(pDexFile, pMethodId->nameIdx);
+ printf(" #%d: 0x%08x %s\n", idx, offset, name);
+
+ u1 format;
+ int addrWidth;
+
+ format = *data++;
+ if (format == 1) { /* kRegMapFormatNone */
+ /* no map */
+ printf(" (no map)\n");
+ addrWidth = 0;
+ } else if (format == 2) { /* kRegMapFormatCompact8 */
+ addrWidth = 1;
+ } else if (format == 3) { /* kRegMapFormatCompact16 */
+ addrWidth = 2;
+ } else if (format == 4) { /* kRegMapFormatDifferential */
+ dumpDifferentialCompressedMap(&data);
+ goto bail;
+ } else {
+ printf(" (unknown format %d!)\n", format);
+ /* don't know how to skip data; failure will cascade to end of class */
+ goto bail;
+ }
+
+ if (addrWidth > 0) {
+ u1 regWidth;
+ u2 numEntries;
+ int idx, addr, byte;
+
+ regWidth = *data++;
+ numEntries = *data++;
+ numEntries |= (*data++) << 8;
+
+ for (idx = 0; idx < numEntries; idx++) {
+ addr = *data++;
+ if (addrWidth > 1)
+ addr |= (*data++) << 8;
+
+ printf(" %4x:", addr);
+ for (byte = 0; byte < regWidth; byte++) {
+ printf(" %02x", *data++);
+ }
+ printf("\n");
+ }
+ }
+
+bail:
+ //if (addrWidth >= 0)
+ // *pData = align32(data);
+ *pData = data;
+}
+
+/*
+ * Dump the contents of the register map area.
+ *
+ * These are only present in optimized DEX files, and the structure is
+ * not really exposed to other parts of the VM itself. We're going to
+ * dig through them here, but this is pretty fragile. DO NOT rely on
+ * this or derive other code from it.
+ */
+void dumpRegisterMaps(DexFile* pDexFile)
+{
+ const u1* pClassPool = (const u1*)pDexFile->pRegisterMapPool;
+ const u4* classOffsets;
+ const u1* ptr;
+ u4 numClasses;
+ int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader;
+ int idx;
+
+ if (pClassPool == NULL) {
+ printf("No register maps found\n");
+ return;
+ }
+
+ ptr = pClassPool;
+ numClasses = get4LE(ptr);
+ ptr += sizeof(u4);
+ classOffsets = (const u4*) ptr;
+
+ printf("RMAP begins at offset 0x%07x\n", baseFileOffset);
+ printf("Maps for %d classes\n", numClasses);
+ for (idx = 0; idx < (int) numClasses; idx++) {
+ const DexClassDef* pClassDef;
+ const char* classDescriptor;
+
+ pClassDef = dexGetClassDef(pDexFile, idx);
+ classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx],
+ baseFileOffset + classOffsets[idx], classDescriptor);
+
+ if (classOffsets[idx] == 0)
+ continue;
+
+ /*
+ * What follows is a series of RegisterMap entries, one for every
+ * direct method, then one for every virtual method.
+ */
+ DexClassData* pClassData;
+ const u1* pEncodedData;
+ const u1* data = (u1*) pClassPool + classOffsets[idx];
+ u2 methodCount;
+ int i;
+
+ pEncodedData = dexGetClassData(pDexFile, pClassDef);
+ pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL);
+ if (pClassData == NULL) {
+ fprintf(stderr, "Trouble reading class data\n");
+ continue;
+ }
+
+ methodCount = *data++;
+ methodCount |= (*data++) << 8;
+ data += 2; /* two pad bytes follow methodCount */
+ if (methodCount != pClassData->header.directMethodsSize
+ + pClassData->header.virtualMethodsSize)
+ {
+ printf("NOTE: method count discrepancy (%d != %d + %d)\n",
+ methodCount, pClassData->header.directMethodsSize,
+ pClassData->header.virtualMethodsSize);
+ /* this is bad, but keep going anyway */
+ }
+
+ printf(" direct methods: %d\n",
+ pClassData->header.directMethodsSize);
+ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) {
+ dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data);
+ }
+
+ printf(" virtual methods: %d\n",
+ pClassData->header.virtualMethodsSize);
+ for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) {
+ dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data);
+ }
+
+ free(pClassData);
+ }
+}
+
+static const DexMapItem* findMapItem(const DexFile* pDexFile, u4 type)
+{
+ const u4 offset = pDexFile->pHeader->mapOff;
+ const DexMapList* list = (const DexMapList*)(pDexFile->baseAddr + offset);
+ for (u4 i = 0; i < list->size; ++i) {
+ if (list->list[i].type == type) {
+ return &list->list[i];
+ }
+ }
+ return nullptr;
+}
+
+static void dumpMethodHandles(DexFile* pDexFile)
+{
+ const DexMapItem* item = findMapItem(pDexFile, kDexTypeMethodHandleItem);
+ if (item == nullptr) return;
+ const DexMethodHandleItem* method_handles =
+ (const DexMethodHandleItem*)(pDexFile->baseAddr + item->offset);
+ for (u4 i = 0; i < item->size; ++i) {
+ const DexMethodHandleItem& mh = method_handles[i];
+ const char* type;
+ bool is_invoke;
+ bool is_static;
+ switch ((MethodHandleType) mh.methodHandleType) {
+ case MethodHandleType::STATIC_PUT:
+ type = "put-static";
+ is_invoke = false;
+ is_static = true;
+ break;
+ case MethodHandleType::STATIC_GET:
+ type = "get-static";
+ is_invoke = false;
+ is_static = true;
+ break;
+ case MethodHandleType::INSTANCE_PUT:
+ type = "put-instance";
+ is_invoke = false;
+ is_static = false;
+ break;
+ case MethodHandleType::INSTANCE_GET:
+ type = "get-instance";
+ is_invoke = false;
+ is_static = false;
+ break;
+ case MethodHandleType::INVOKE_STATIC:
+ type = "invoke-static";
+ is_invoke = true;
+ is_static = true;
+ break;
+ case MethodHandleType::INVOKE_INSTANCE:
+ type = "invoke-instance";
+ is_invoke = true;
+ is_static = false;
+ break;
+ case MethodHandleType::INVOKE_CONSTRUCTOR:
+ type = "invoke-constructor";
+ is_invoke = true;
+ is_static = false;
+ break;
+ case MethodHandleType::INVOKE_DIRECT:
+ type = "invoke-direct";
+ is_invoke = true;
+ is_static = false;
+ break;
+ case MethodHandleType::INVOKE_INTERFACE:
+ type = "invoke-interface";
+ is_invoke = true;
+ is_static = false;
+ break;
+ default:
+ printf("Unknown method handle type 0x%02x, skipped.", mh.methodHandleType);
+ continue;
+ }
+
+ FieldMethodInfo info;
+ if (is_invoke) {
+ if (!getMethodInfo(pDexFile, mh.fieldOrMethodIdx, &info)) {
+ printf("Unknown method handle target method@%04x, skipped.", mh.fieldOrMethodIdx);
+ continue;
+ }
+ } else {
+ if (!getFieldInfo(pDexFile, mh.fieldOrMethodIdx, &info)) {
+ printf("Unknown method handle target field@%04x, skipped.", mh.fieldOrMethodIdx);
+ continue;
+ }
+ }
+
+ const char* instance = is_static ? "" : info.classDescriptor;
+
+ if (gOptions.outputFormat == OUTPUT_XML) {
+ printf("<method_handle index index=\"%u\"\n", i);
+ printf(" type=\"%s\"\n", type);
+ printf(" target_class=\"%s\"\n", info.classDescriptor);
+ printf(" target_member=\"%s\"\n", info.name);
+ printf(" target_member_type=\"%c%s%s\"\n",
+ info.signature[0], instance, info.signature + 1);
+ printf("</method_handle>\n");
+ } else {
+ printf("Method Handle #%u:\n", i);
+ printf(" type : %s\n", type);
+ printf(" target : %s %s\n", info.classDescriptor, info.name);
+ printf(" target_type : %c%s%s\n", info.signature[0], instance, info.signature + 1);
+ }
+ }
+}
+
+/* Helper for dumpCallSites(), which reads a 1- to 8- byte signed
+ * little endian value. */
+static u8 readSignedLittleEndian(const u1** pData, u4 size) {
+ const u1* data = *pData;
+ u8 result = 0;
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ result = (result >> 8) | (((int64_t)*data++) << 56);
+ }
+
+ result >>= (8 - size) * 8;
+ *pData = data;
+ return result;
+}
+
+/* Helper for dumpCallSites(), which reads a 1- to 8- byte unsigned
+ * little endian value. */
+static u8 readUnsignedLittleEndian(const u1** pData, u4 size, bool fillOnRight = false) {
+ const u1* data = *pData;
+ u8 result = 0;
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ result = (result >> 8) | (((u8)*data++) << 56);
+ }
+
+ if (!fillOnRight) {
+ result >>= (8u - size) * 8;
+ }
+
+ *pData = data;
+ return result;
+}
+
+static void dumpCallSites(DexFile* pDexFile)
+{
+ const DexMapItem* item = findMapItem(pDexFile, kDexTypeCallSiteIdItem);
+ if (item == nullptr) return;
+ const DexCallSiteId* ids = (const DexCallSiteId*)(pDexFile->baseAddr + item->offset);
+ for (u4 index = 0; index < item->size; ++index) {
+ bool doXml = (gOptions.outputFormat == OUTPUT_XML);
+ printf(doXml ? "<call_site index=\"%u\" offset=\"%u\">\n" : "Call Site #%u // offset %u\n",
+ index, ids[index].callSiteOff);
+ const u1* data = pDexFile->baseAddr + ids[index].callSiteOff;
+ u4 count = readUnsignedLeb128(&data);
+ for (u4 i = 0; i < count; ++i) {
+ printf(doXml ? "<link_argument index=\"%u\" " : " link_argument[%u] : ", i);
+ u1 headerByte = *data++;
+ u4 valueType = headerByte & kDexAnnotationValueTypeMask;
+ u4 valueArg = headerByte >> kDexAnnotationValueArgShift;
+ switch (valueType) {
+ case kDexAnnotationByte: {
+ printf(doXml ? "type=\"byte\" value=\"%d\"/>" : "%d (byte)", (int)*data++);
+ break;
+ }
+ case kDexAnnotationShort: {
+ printf(doXml ? "type=\"short\" value=\"%d\"/>" : "%d (short)",
+ (int) readSignedLittleEndian(&data, valueArg + 1));
+ break;
+ }
+ case kDexAnnotationChar: {
+ printf(doXml ? "type=\"short\" value=\"%u\"/>" : "%u (char)",
+ (u2) readUnsignedLittleEndian(&data, valueArg + 1));
+ break;
+ }
+ case kDexAnnotationInt: {
+ printf(doXml ? "type=\"int\" value=\"%d\"/>" : "%d (int)",
+ (int) readSignedLittleEndian(&data, valueArg + 1));
+ break;
+ }
+ case kDexAnnotationLong: {
+ printf(doXml ? "type=\"long\" value=\"%" PRId64 "\"/>" : "%" PRId64 " (long)",
+ (int64_t) readSignedLittleEndian(&data, valueArg + 1));
+ break;
+ }
+ case kDexAnnotationFloat: {
+ u4 rawValue = (u4) (readUnsignedLittleEndian(&data, valueArg + 1, true) >> 32);
+ printf(doXml ? "type=\"float\" value=\"%g\"/>" : "%g (float)",
+ *((float*) &rawValue));
+ break;
+ }
+ case kDexAnnotationDouble: {
+ u8 rawValue = readUnsignedLittleEndian(&data, valueArg + 1, true);
+ printf(doXml ? "type=\"double\" value=\"%g\"/>" : "%g (double)",
+ *((double*) &rawValue));
+ break;
+ }
+ case kDexAnnotationMethodType: {
+ u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
+ ProtoInfo protoInfo;
+ memset(&protoInfo, 0, sizeof(protoInfo));
+ getProtoInfo(pDexFile, idx, &protoInfo);
+ printf(doXml ? "type=\"MethodType\" value=\"(%s)%s\"/>" : "(%s)%s (MethodType)",
+ protoInfo.parameterTypes, protoInfo.returnType);
+ free(protoInfo.parameterTypes);
+ break;
+ }
+ case kDexAnnotationMethodHandle: {
+ u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
+ printf(doXml ? "type=\"MethodHandle\" value=\"%u\"/>" : "%u (MethodHandle)",
+ idx);
+ break;
+ }
+ case kDexAnnotationString: {
+ u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
+ printf(doXml ? "type=\"String\" value=\"%s\"/>" : "%s (String)",
+ dexStringById(pDexFile, idx));
+ break;
+ }
+ case kDexAnnotationType: {
+ u4 idx = (u4) readUnsignedLittleEndian(&data, valueArg + 1);
+ printf(doXml ? "type=\"Class\" value=\"%s\"/>" : "%s (Class)",
+ dexStringByTypeIdx(pDexFile, idx));
+ break;
+ }
+ case kDexAnnotationNull: {
+ printf(doXml ? "type=\"null\" value=\"null\"/>" : "null (null)");
+ break;
+ }
+ case kDexAnnotationBoolean: {
+ printf(doXml ? "type=\"boolean\" value=\"%s\"/>" : "%s (boolean)",
+ (valueArg & 1) == 0 ? "false" : "true");
+ break;
+ }
+ default:
+ // Other types are not anticipated being reached here.
+ printf("Unexpected type found, bailing on call site info.\n");
+ i = count;
+ break;
+ }
+ printf("\n");
+ }
+
+ if (doXml) {
+ printf("</callsite>\n");
+ }
+ }
+}
+
+/*
+ * Dump the requested sections of the file.
+ */
+void processDexFile(const char* fileName, DexFile* pDexFile)
+{
+ char* package = NULL;
+ int i;
+
+ if (gOptions.verbose) {
+ printf("Opened '%s', DEX version '%.3s'\n", fileName,
+ pDexFile->pHeader->magic +4);
+ }
+
+ if (gOptions.dumpRegisterMaps) {
+ dumpRegisterMaps(pDexFile);
+ return;
+ }
+
+ if (gOptions.showFileHeaders) {
+ dumpFileHeader(pDexFile);
+ dumpOptDirectory(pDexFile);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("<api>\n");
+
+ for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) {
+ if (gOptions.showSectionHeaders)
+ dumpClassDef(pDexFile, i);
+
+ dumpClass(pDexFile, i, &package);
+ }
+
+ dumpMethodHandles(pDexFile);
+ dumpCallSites(pDexFile);
+
+ /* free the last one allocated */
+ if (package != NULL) {
+ printf("</package>\n");
+ free(package);
+ }
+
+ if (gOptions.outputFormat == OUTPUT_XML)
+ printf("</api>\n");
+}
+
+
+/*
+ * Process one file.
+ */
+int process(const char* fileName)
+{
+ DexFile* pDexFile = NULL;
+ MemMapping map;
+ bool mapped = false;
+ int result = -1;
+
+ if (gOptions.verbose)
+ printf("Processing '%s'...\n", fileName);
+
+ if (dexOpenAndMap(fileName, gOptions.tempFileName, &map, false) != 0) {
+ return result;
+ }
+ mapped = true;
+
+ int flags = kDexParseVerifyChecksum;
+ if (gOptions.ignoreBadChecksum)
+ flags |= kDexParseContinueOnError;
+
+ pDexFile = dexFileParse((u1*)map.addr, map.length, flags);
+ if (pDexFile == NULL) {
+ fprintf(stderr, "ERROR: DEX parse failed\n");
+ goto bail;
+ }
+
+ if (gOptions.checksumOnly) {
+ printf("Checksum verified\n");
+ } else {
+ processDexFile(fileName, pDexFile);
+ }
+
+ result = 0;
+
+bail:
+ if (mapped)
+ sysReleaseShmem(&map);
+ if (pDexFile != NULL)
+ dexFileFree(pDexFile);
+ return result;
+}
+
+
+/*
+ * Show usage.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+ fprintf(stderr,
+ "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n",
+ gProgName);
+ fprintf(stderr, "\n");
+ fprintf(stderr, " -c : verify checksum and exit\n");
+ fprintf(stderr, " -d : disassemble code sections\n");
+ fprintf(stderr, " -f : display summary information from file header\n");
+ fprintf(stderr, " -h : display file header details\n");
+ fprintf(stderr, " -i : ignore checksum failures\n");
+ fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
+ fprintf(stderr, " -m : dump register maps (and nothing else)\n");
+ fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n");
+}
+
+/*
+ * Parse args.
+ *
+ * I'm not using getopt_long() because we may not have it in libc.
+ */
+int main(int argc, char* const argv[])
+{
+ bool wantUsage = false;
+ int ic;
+
+ memset(&gOptions, 0, sizeof(gOptions));
+ gOptions.verbose = true;
+
+ while (1) {
+ ic = getopt(argc, argv, "cdfhil:mt:");
+ if (ic < 0)
+ break;
+
+ switch (ic) {
+ case 'c': // verify the checksum then exit
+ gOptions.checksumOnly = true;
+ break;
+ case 'd': // disassemble Dalvik instructions
+ gOptions.disassemble = true;
+ break;
+ case 'f': // dump outer file header
+ gOptions.showFileHeaders = true;
+ break;
+ case 'h': // dump section headers, i.e. all meta-data
+ gOptions.showSectionHeaders = true;
+ break;
+ case 'i': // continue even if checksum is bad
+ gOptions.ignoreBadChecksum = true;
+ break;
+ case 'l': // layout
+ if (strcmp(optarg, "plain") == 0) {
+ gOptions.outputFormat = OUTPUT_PLAIN;
+ } else if (strcmp(optarg, "xml") == 0) {
+ gOptions.outputFormat = OUTPUT_XML;
+ gOptions.verbose = false;
+ gOptions.exportsOnly = true;
+ } else {
+ wantUsage = true;
+ }
+ break;
+ case 'm': // dump register maps only
+ gOptions.dumpRegisterMaps = true;
+ break;
+ case 't': // temp file, used when opening compressed Jar
+ gOptions.tempFileName = optarg;
+ break;
+ default:
+ wantUsage = true;
+ break;
+ }
+ }
+
+ if (optind == argc) {
+ fprintf(stderr, "%s: no file specified\n", gProgName);
+ wantUsage = true;
+ }
+
+ if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) {
+ fprintf(stderr, "Can't specify both -c and -i\n");
+ wantUsage = true;
+ }
+
+ if (wantUsage) {
+ usage();
+ return 2;
+ }
+
+ int result = 0;
+ while (optind < argc) {
+ result |= process(argv[optind++]);
+ }
+
+ return (result != 0);
+}
diff --git a/dexdump/NOTICE b/dexdump/NOTICE
new file mode 100644
index 000000000..c5b1efa7a
--- /dev/null
+++ b/dexdump/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/dx/src/com/android/dex/DexFormat.java b/dx/src/com/android/dex/DexFormat.java
index ab995667b..66b4460d7 100644
--- a/dx/src/com/android/dex/DexFormat.java
+++ b/dx/src/com/android/dex/DexFormat.java
@@ -23,9 +23,6 @@ package com.android.dex;
public final class DexFormat {
private DexFormat() {}
- /** API level to target in order to allow spaces in SimpleName */
- public static final int API_SPACES_IN_SIMPLE_NAME = 10000;
-
/** API level to target in order to generate const-method-handle and const-method-type */
public static final int API_CONST_METHOD_HANDLE = 28;
@@ -51,9 +48,6 @@ public final class DexFormat {
*/
public static final int API_CURRENT = API_CONST_METHOD_HANDLE;
- /** dex file version number for API level 10000 and earlier */
- public static final String VERSION_FOR_API_10000 = "040";
-
/** dex file version number for API level 28 and earlier */
public static final String VERSION_FOR_API_28 = "039";
@@ -133,8 +127,6 @@ public final class DexFormat {
return API_METHOD_HANDLES;
} else if (version.equals(VERSION_FOR_API_28)) {
return API_CONST_METHOD_HANDLE;
- } else if (version.equals(VERSION_FOR_API_10000)) {
- return API_SPACES_IN_SIMPLE_NAME;
} else if (version.equals(VERSION_CURRENT)) {
return API_CURRENT;
}
@@ -153,8 +145,6 @@ public final class DexFormat {
if (targetApiLevel >= API_CURRENT) {
version = VERSION_CURRENT;
- } else if (targetApiLevel >= API_SPACES_IN_SIMPLE_NAME) {
- version = VERSION_FOR_API_10000;
} else if (targetApiLevel >= API_CONST_METHOD_HANDLE) {
version = VERSION_FOR_API_28;
} else if (targetApiLevel >= API_METHOD_HANDLES) {
diff --git a/dx/src/com/android/dex/Mutf8.java b/dx/src/com/android/dex/Mutf8.java
index df3f3159d..c64da331b 100644
--- a/dx/src/com/android/dex/Mutf8.java
+++ b/dx/src/com/android/dex/Mutf8.java
@@ -21,6 +21,8 @@ import java.io.UTFDataFormatException;
/**
* Modified UTF-8 as described in the dex file format spec.
+ *
+ * <p>Derived from libcore's MUTF-8 encoder at java.nio.charset.ModifiedUtf8.
*/
public final class Mutf8 {
private Mutf8() {}
diff --git a/dx/tests/127-merge-stress/run b/dx/tests/127-merge-stress/run
index a189cb195..40a98b487 100755
--- a/dx/tests/127-merge-stress/run
+++ b/dx/tests/127-merge-stress/run
@@ -58,7 +58,7 @@ for dex in $candidates; do
fi
checksums[$checksum]=$checksum
- dexdump -c $dex >/dev/null 2>&1
+ dexdump2 -c $dex >/dev/null 2>&1
if [ $? -eq 0 ]; then
validdexes+=("$dex")
fi
diff --git a/dx/tests/137-dexmerger-dex38/run b/dx/tests/137-dexmerger-dex38/run
index 65e70b7e9..af1dc9e35 100755
--- a/dx/tests/137-dexmerger-dex38/run
+++ b/dx/tests/137-dexmerger-dex38/run
@@ -23,4 +23,4 @@ jar xf invokecustom.jar
)
java -cp $(dirname $(which dx))/../framework/dx.jar com.android.dx.merge.DexMerger \
out.dex invokecustom/*.dex >& /dev/null
-dexdump -d out.dex
+dexdump2 -d out.dex
diff --git a/dx/tests/run-all-tests b/dx/tests/run-all-tests
index 1822b22b8..3734a98f1 100755
--- a/dx/tests/run-all-tests
+++ b/dx/tests/run-all-tests
@@ -33,7 +33,7 @@ cd "${progdir}"
progdir=`pwd`
prog="${progdir}"/`basename "${prog}"`
-skip_tests="127-merge-stress"
+skip_tests=""
# Command-line options
sequential="no"
diff --git a/libdex/Android.bp b/libdex/Android.bp
new file mode 100644
index 000000000..737e097cd
--- /dev/null
+++ b/libdex/Android.bp
@@ -0,0 +1,56 @@
+// Copyright (C) 2008 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_static {
+ name: "libdex",
+ host_supported: true,
+
+ srcs: [
+ "CmdUtils.cpp",
+ "DexCatch.cpp",
+ "DexClass.cpp",
+ "DexDataMap.cpp",
+ "DexDebugInfo.cpp",
+ "DexFile.cpp",
+ "DexInlines.cpp",
+ "DexOptData.cpp",
+ "DexOpcodes.cpp",
+ "DexProto.cpp",
+ "DexSwapVerify.cpp",
+ "DexUtf.cpp",
+ "InstrUtils.cpp",
+ "Leb128.cpp",
+ "OptInvocation.cpp",
+ "sha1.cpp",
+ "SysUtil.cpp",
+ ],
+ include_dirs: [
+ "dalvik",
+ "external/zlib",
+ ],
+ static_libs: ["liblog"],
+ whole_static_libs: ["libziparchive"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wimplicit-fallthrough",
+ ],
+ target: {
+ windows: {
+ enabled: true,
+ cflags: ["-Wno-unused-parameter"],
+ },
+ },
+}
diff --git a/libdex/CmdUtils.cpp b/libdex/CmdUtils.cpp
new file mode 100644
index 000000000..8244a1189
--- /dev/null
+++ b/libdex/CmdUtils.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Some utility functions for use with command-line utilities.
+ */
+#include "DexFile.h"
+#include "ZipArchive.h"
+#include "CmdUtils.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+/*
+ * Extract "classes.dex" from archive file.
+ *
+ * If "quiet" is set, don't report common errors.
+ */
+UnzipToFileResult dexUnzipToFile(const char* zipFileName,
+ const char* outFileName, bool quiet)
+{
+ UnzipToFileResult result = kUTFRSuccess;
+ static const char* kFileToExtract = "classes.dex";
+ ZipArchiveHandle archive;
+ ZipEntry entry;
+ bool unlinkOnFailure = false;
+ int fd = -1;
+
+ if (dexZipOpenArchive(zipFileName, &archive) != 0) {
+ if (!quiet) {
+ fprintf(stderr, "Unable to open '%s' as zip archive\n",
+ zipFileName);
+ }
+ result = kUTFRNotZip;
+ goto bail;
+ }
+
+ fd = open(outFileName, O_RDWR | O_CREAT | O_EXCL | O_BINARY, 0600);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to create output file '%s': %s\n",
+ outFileName, strerror(errno));
+ result = kUTFROutputFileProblem;
+ goto bail;
+ }
+
+ unlinkOnFailure = true;
+
+ if (dexZipFindEntry(archive, kFileToExtract, &entry) != 0) {
+ if (!quiet) {
+ fprintf(stderr, "Unable to find '%s' in '%s'\n",
+ kFileToExtract, zipFileName);
+ }
+ result = kUTFRNoClassesDex;
+ goto bail;
+ }
+
+ if (dexZipExtractEntryToFile(archive, &entry, fd) != 0) {
+ fprintf(stderr, "Extract of '%s' from '%s' failed\n",
+ kFileToExtract, zipFileName);
+ result = kUTFRBadZip;
+ goto bail;
+ }
+
+bail:
+ if (fd >= 0)
+ close(fd);
+ if (unlinkOnFailure && result != kUTFRSuccess)
+ unlink(outFileName);
+ dexZipCloseArchive(archive);
+ return result;
+}
+
+/*
+ * Map the specified DEX file read-only (possibly after expanding it into a
+ * temp file from a Jar). Pass in a MemMapping struct to hold the info.
+ * If the file is an unoptimized DEX file, then byte-swapping and structural
+ * verification are performed on it before the memory is made read-only.
+ *
+ * The temp file is deleted after the map succeeds.
+ *
+ * This is intended for use by tools (e.g. dexdump) that need to get a
+ * read-only copy of a DEX file that could be in a number of different states.
+ *
+ * If "tempFileName" is NULL, a default value is used. The temp file is
+ * deleted after the map succeeds.
+ *
+ * If "quiet" is set, don't report common errors.
+ *
+ * Returns 0 (kUTFRSuccess) on success.
+ */
+UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
+ MemMapping* pMap, bool quiet)
+{
+ UnzipToFileResult result = kUTFRGenericFailure;
+ int len = strlen(fileName);
+ char tempNameBuf[32];
+ bool removeTemp = false;
+ int fd = -1;
+
+ if (len < 5) {
+ if (!quiet) {
+ fprintf(stderr,
+ "ERROR: filename must end in .dex, .zip, .jar, or .apk\n");
+ }
+ result = kUTFRBadArgs;
+ goto bail;
+ }
+
+ if (strcasecmp(fileName + len -3, "dex") != 0) {
+ if (tempFileName == NULL) {
+ /*
+ * Try .zip/.jar/.apk, all of which are Zip archives with
+ * "classes.dex" inside. We need to extract the compressed
+ * data to a temp file, the location of which varies.
+ *
+ * On the device we must use /sdcard because most other
+ * directories aren't writable (either because of permissions
+ * or because the volume is mounted read-only). On desktop
+ * it's nice to use the designated temp directory.
+ */
+ if (access("/tmp", W_OK) == 0) {
+ sprintf(tempNameBuf, "/tmp/dex-temp-%d", getpid());
+ } else if (access("/sdcard", W_OK) == 0) {
+ sprintf(tempNameBuf, "/sdcard/dex-temp-%d", getpid());
+ } else {
+ fprintf(stderr,
+ "NOTE: /tmp and /sdcard unavailable for temp files\n");
+ sprintf(tempNameBuf, "dex-temp-%d", getpid());
+ }
+
+ tempFileName = tempNameBuf;
+ }
+
+ result = dexUnzipToFile(fileName, tempFileName, quiet);
+
+ if (result == kUTFRSuccess) {
+ //printf("+++ Good unzip to '%s'\n", tempFileName);
+ fileName = tempFileName;
+ removeTemp = true;
+ } else if (result == kUTFRNotZip) {
+ if (!quiet) {
+ fprintf(stderr, "Not Zip, retrying as DEX\n");
+ }
+ } else {
+ if (!quiet && result == kUTFRNoClassesDex) {
+ fprintf(stderr, "Zip has no classes.dex\n");
+ }
+ goto bail;
+ }
+ }
+
+ result = kUTFRGenericFailure;
+
+ /*
+ * Pop open the (presumed) DEX file.
+ */
+ fd = open(fileName, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ if (!quiet) {
+ fprintf(stderr, "ERROR: unable to open '%s': %s\n",
+ fileName, strerror(errno));
+ }
+ goto bail;
+ }
+
+ if (sysMapFileInShmemWritableReadOnly(fd, pMap) != 0) {
+ fprintf(stderr, "ERROR: Unable to map '%s'\n", fileName);
+ goto bail;
+ }
+
+ /*
+ * This call will fail if the file exists on a filesystem that
+ * doesn't support mprotect(). If that's the case, then the file
+ * will have already been mapped private-writable by the previous
+ * call, so we don't need to do anything special if this call
+ * returns non-zero.
+ */
+ sysChangeMapAccess(pMap->addr, pMap->length, true, pMap);
+
+ if (dexSwapAndVerifyIfNecessary((u1*) pMap->addr, pMap->length)) {
+ fprintf(stderr, "ERROR: Failed structural verification of '%s'\n",
+ fileName);
+ goto bail;
+ }
+
+ /*
+ * Similar to above, this call will fail if the file wasn't ever
+ * read-only to begin with. This is innocuous, though it is
+ * undesirable from a memory hygiene perspective.
+ */
+ sysChangeMapAccess(pMap->addr, pMap->length, false, pMap);
+
+ /*
+ * Success! Close the file and return with the start/length in pMap.
+ */
+ result = kUTFRSuccess;
+
+bail:
+ if (fd >= 0)
+ close(fd);
+ if (removeTemp) {
+ /* this will fail if the OS doesn't allow removal of a mapped file */
+ if (unlink(tempFileName) != 0) {
+ fprintf(stderr, "WARNING: unable to remove temp '%s'\n",
+ tempFileName);
+ }
+ }
+ return result;
+}
diff --git a/libdex/CmdUtils.h b/libdex/CmdUtils.h
new file mode 100644
index 000000000..887eed9ba
--- /dev/null
+++ b/libdex/CmdUtils.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Access .dex (Dalvik Executable Format) files. The code here assumes that
+ * the DEX file has been rewritten (byte-swapped, word-aligned) and that
+ * the contents can be directly accessed as a collection of C arrays. Please
+ * see docs/dalvik/dex-format.html for a detailed description.
+ *
+ * The structure and field names were chosen to match those in the DEX spec.
+ *
+ * It's generally assumed that the DEX file will be stored in shared memory,
+ * obviating the need to copy code and constant pool entries into newly
+ * allocated storage. Maintaining local pointers to items in the shared area
+ * is valid and encouraged.
+ *
+ * All memory-mapped structures are 32-bit aligned unless otherwise noted.
+ */
+#ifndef LIBDEX_CMDUTILS_H_
+#define LIBDEX_CMDUTILS_H_
+
+/* encode the result of unzipping to a file */
+enum UnzipToFileResult {
+ kUTFRSuccess = 0,
+ kUTFRGenericFailure,
+ kUTFRBadArgs,
+ kUTFRNotZip,
+ kUTFRNoClassesDex,
+ kUTFROutputFileProblem,
+ kUTFRBadZip,
+};
+
+/*
+ * Map the specified DEX file read-only (possibly after expanding it into a
+ * temp file from a Jar). Pass in a MemMapping struct to hold the info.
+ * If the file is an unoptimized DEX file, then byte-swapping and structural
+ * verification are performed on it before the memory is made read-only.
+ *
+ * The temp file is deleted after the map succeeds.
+ *
+ * This is intended for use by tools (e.g. dexdump) that need to get a
+ * read-only copy of a DEX file that could be in a number of different states.
+ *
+ * If "tempFileName" is NULL, a default value is used. The temp file is
+ * deleted after the map succeeds.
+ *
+ * If "quiet" is set, don't report common errors.
+ *
+ * Returns 0 (kUTFRSuccess) on success.
+ */
+UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
+ MemMapping* pMap, bool quiet);
+
+/*
+ * Utility function to open a Zip archive, find "classes.dex", and extract
+ * it to a file.
+ */
+UnzipToFileResult dexUnzipToFile(const char* zipFileName,
+ const char* outFileName, bool quiet);
+
+#endif // LIBDEX_CMDUTILS_H_
diff --git a/libdex/DexCatch.cpp b/libdex/DexCatch.cpp
new file mode 100644
index 000000000..ed97e87d9
--- /dev/null
+++ b/libdex/DexCatch.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with try-catch info.
+ */
+
+#include "DexCatch.h"
+
+/* Get the first handler offset for the given DexCode.
+ * It's not 0 because the handlers list is prefixed with its size
+ * (in entries) as a uleb128. */
+u4 dexGetFirstHandlerOffset(const DexCode* pCode) {
+ if (pCode->triesSize == 0) {
+ return 0;
+ }
+
+ const u1* baseData = dexGetCatchHandlerData(pCode);
+ const u1* data = baseData;
+
+ readUnsignedLeb128(&data);
+
+ return data - baseData;
+}
+
+/* Get count of handler lists for the given DexCode. */
+u4 dexGetHandlersSize(const DexCode* pCode) {
+ if (pCode->triesSize == 0) {
+ return 0;
+ }
+
+ const u1* data = dexGetCatchHandlerData(pCode);
+
+ return readUnsignedLeb128(&data);
+}
+
+/* Helper for dexFindCatchHandlerOffset(), which does an actual search
+ * in the tries table. Returns -1 if there is no applicable handler. */
+int dexFindCatchHandlerOffset0(u2 triesSize, const DexTry* pTries,
+ u4 address) {
+ // Note: Signed type is important for max and min.
+ int min = 0;
+ int max = triesSize - 1;
+
+ while (max >= min) {
+ int guess = (min + max) >> 1;
+ const DexTry* pTry = &pTries[guess];
+ u4 start = pTry->startAddr;
+
+ if (address < start) {
+ max = guess - 1;
+ continue;
+ }
+
+ u4 end = start + pTry->insnCount;
+
+ if (address >= end) {
+ min = guess + 1;
+ continue;
+ }
+
+ // We have a winner!
+ return (int) pTry->handlerOff;
+ }
+
+ // No match.
+ return -1;
+}
+
+/* Get the handler offset just past the end of the one just iterated over.
+ * This ends the iteration if it wasn't already. */
+u4 dexCatchIteratorGetEndOffset(DexCatchIterator* pIterator,
+ const DexCode* pCode) {
+ while (dexCatchIteratorNext(pIterator) != NULL) /* empty */ ;
+
+ return (u4) (pIterator->pEncodedData - dexGetCatchHandlerData(pCode));
+}
diff --git a/libdex/DexCatch.h b/libdex/DexCatch.h
new file mode 100644
index 000000000..cfea2d900
--- /dev/null
+++ b/libdex/DexCatch.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with try-catch info.
+ */
+
+#ifndef LIBDEX_DEXCATCH_H_
+#define LIBDEX_DEXCATCH_H_
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/*
+ * Catch handler entry, used while iterating over catch_handler_items.
+ */
+struct DexCatchHandler {
+ u4 typeIdx; /* type index of the caught exception type */
+ u4 address; /* handler address */
+};
+
+/* Get the first handler offset for the given DexCode.
+ * It's not 0 because the handlers list is prefixed with its size
+ * (in entries) as a uleb128. */
+u4 dexGetFirstHandlerOffset(const DexCode* pCode);
+
+/* Get count of handler lists for the given DexCode. */
+u4 dexGetHandlersSize(const DexCode* pCode);
+
+/*
+ * Iterator over catch handler data. This structure should be treated as
+ * opaque.
+ */
+struct DexCatchIterator {
+ const u1* pEncodedData;
+ bool catchesAll;
+ u4 countRemaining;
+ DexCatchHandler handler;
+};
+
+/* Initialize a DexCatchIterator to emptiness. This mostly exists to
+ * squelch innocuous warnings. */
+DEX_INLINE void dexCatchIteratorClear(DexCatchIterator* pIterator) {
+ pIterator->pEncodedData = NULL;
+ pIterator->catchesAll = false;
+ pIterator->countRemaining = 0;
+ pIterator->handler.typeIdx = 0;
+ pIterator->handler.address = 0;
+}
+
+/* Initialize a DexCatchIterator with a direct pointer to encoded handlers. */
+DEX_INLINE void dexCatchIteratorInitToPointer(DexCatchIterator* pIterator,
+ const u1* pEncodedData)
+{
+ s4 count = readSignedLeb128(&pEncodedData);
+
+ if (count <= 0) {
+ pIterator->catchesAll = true;
+ count = -count;
+ } else {
+ pIterator->catchesAll = false;
+ }
+
+ pIterator->pEncodedData = pEncodedData;
+ pIterator->countRemaining = count;
+}
+
+/* Initialize a DexCatchIterator to a particular handler offset. */
+DEX_INLINE void dexCatchIteratorInit(DexCatchIterator* pIterator,
+ const DexCode* pCode, u4 offset)
+{
+ dexCatchIteratorInitToPointer(pIterator,
+ dexGetCatchHandlerData(pCode) + offset);
+}
+
+/* Get the next item from a DexCatchIterator. Returns NULL if at end. */
+DEX_INLINE DexCatchHandler* dexCatchIteratorNext(DexCatchIterator* pIterator) {
+ if (pIterator->countRemaining == 0) {
+ if (! pIterator->catchesAll) {
+ return NULL;
+ }
+
+ pIterator->catchesAll = false;
+ pIterator->handler.typeIdx = kDexNoIndex;
+ } else {
+ u4 typeIdx = readUnsignedLeb128(&pIterator->pEncodedData);
+ pIterator->handler.typeIdx = typeIdx;
+ pIterator->countRemaining--;
+ }
+
+ pIterator->handler.address = readUnsignedLeb128(&pIterator->pEncodedData);
+ return &pIterator->handler;
+}
+
+/* Get the handler offset just past the end of the one just iterated over.
+ * This ends the iteration if it wasn't already. */
+u4 dexCatchIteratorGetEndOffset(DexCatchIterator* pIterator,
+ const DexCode* pCode);
+
+/* Helper for dexFindCatchHandler(). Do not call directly. */
+int dexFindCatchHandlerOffset0(u2 triesSize, const DexTry* pTries,
+ u4 address);
+
+/* Find the handler associated with a given address, if any.
+ * Initializes the given iterator and returns true if a match is
+ * found. Returns false if there is no applicable handler. */
+DEX_INLINE bool dexFindCatchHandler(DexCatchIterator *pIterator,
+ const DexCode* pCode, u4 address) {
+ u2 triesSize = pCode->triesSize;
+ int offset = -1;
+
+ // Short-circuit the overwhelmingly common cases.
+ switch (triesSize) {
+ case 0: {
+ break;
+ }
+ case 1: {
+ const DexTry* tries = dexGetTries(pCode);
+ u4 start = tries[0].startAddr;
+
+ if (address < start) {
+ break;
+ }
+
+ u4 end = start + tries[0].insnCount;
+
+ if (address >= end) {
+ break;
+ }
+
+ offset = tries[0].handlerOff;
+ break;
+ }
+ default: {
+ offset = dexFindCatchHandlerOffset0(triesSize, dexGetTries(pCode),
+ address);
+ }
+ }
+
+ if (offset < 0) {
+ dexCatchIteratorClear(pIterator); // This squelches warnings.
+ return false;
+ } else {
+ dexCatchIteratorInit(pIterator, pCode, offset);
+ return true;
+ }
+}
+
+#endif // LIBDEX_DEXCATCH_H_
diff --git a/libdex/DexClass.cpp b/libdex/DexClass.cpp
new file mode 100644
index 000000000..a33906c94
--- /dev/null
+++ b/libdex/DexClass.cpp
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to deal with class definition structures in DEX files
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include "DexClass.h"
+#include "Leb128.h"
+
+/* Helper for verification which reads and verifies a given number
+ * of uleb128 values. */
+static bool verifyUlebs(const u1* pData, const u1* pLimit, u4 count) {
+ bool okay = true;
+
+ while (okay && (count-- != 0)) {
+ readAndVerifyUnsignedLeb128(&pData, pLimit, &okay);
+ }
+
+ return okay;
+}
+
+/* Read and verify the header of a class_data_item. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure). */
+bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,
+ DexClassDataHeader *pHeader) {
+ if (! verifyUlebs(*pData, pLimit, 4)) {
+ return false;
+ }
+
+ dexReadClassDataHeader(pData, pHeader);
+ return true;
+}
+
+/* Read and verify an encoded_field. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags or indices
+ * are valid. */
+bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit,
+ DexField* pField, u4* lastIndex) {
+ if (! verifyUlebs(*pData, pLimit, 2)) {
+ return false;
+ }
+
+ dexReadClassDataField(pData, pField, lastIndex);
+ return true;
+}
+
+/* Read and verify an encoded_method. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit,
+ DexMethod* pMethod, u4* lastIndex) {
+ if (! verifyUlebs(*pData, pLimit, 3)) {
+ return false;
+ }
+
+ dexReadClassDataMethod(pData, pMethod, lastIndex);
+ return true;
+}
+
+/* Read, verify, and return an entire class_data_item. This updates
+ * the given data pointer to point past the end of the read data. This
+ * function allocates a single chunk of memory for the result, which
+ * must subsequently be free()d. This function returns NULL if there
+ * was trouble parsing the data. If this function is passed NULL, it
+ * returns an initialized empty DexClassData structure.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit) {
+ DexClassDataHeader header;
+ u4 lastIndex;
+
+ if (*pData == NULL) {
+ DexClassData* result = (DexClassData*) malloc(sizeof(DexClassData));
+ memset(result, 0, sizeof(*result));
+ return result;
+ }
+
+ if (! dexReadAndVerifyClassDataHeader(pData, pLimit, &header)) {
+ return NULL;
+ }
+
+ size_t resultSize = sizeof(DexClassData) +
+ (header.staticFieldsSize * sizeof(DexField)) +
+ (header.instanceFieldsSize * sizeof(DexField)) +
+ (header.directMethodsSize * sizeof(DexMethod)) +
+ (header.virtualMethodsSize * sizeof(DexMethod));
+
+ DexClassData* result = (DexClassData*) malloc(resultSize);
+ u1* ptr = ((u1*) result) + sizeof(DexClassData);
+ bool okay = true;
+ u4 i;
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ result->header = header;
+
+ if (header.staticFieldsSize != 0) {
+ result->staticFields = (DexField*) ptr;
+ ptr += header.staticFieldsSize * sizeof(DexField);
+ } else {
+ result->staticFields = NULL;
+ }
+
+ if (header.instanceFieldsSize != 0) {
+ result->instanceFields = (DexField*) ptr;
+ ptr += header.instanceFieldsSize * sizeof(DexField);
+ } else {
+ result->instanceFields = NULL;
+ }
+
+ if (header.directMethodsSize != 0) {
+ result->directMethods = (DexMethod*) ptr;
+ ptr += header.directMethodsSize * sizeof(DexMethod);
+ } else {
+ result->directMethods = NULL;
+ }
+
+ if (header.virtualMethodsSize != 0) {
+ result->virtualMethods = (DexMethod*) ptr;
+ } else {
+ result->virtualMethods = NULL;
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.staticFieldsSize); i++) {
+ okay = dexReadAndVerifyClassDataField(pData, pLimit,
+ &result->staticFields[i], &lastIndex);
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.instanceFieldsSize); i++) {
+ okay = dexReadAndVerifyClassDataField(pData, pLimit,
+ &result->instanceFields[i], &lastIndex);
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.directMethodsSize); i++) {
+ okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
+ &result->directMethods[i], &lastIndex);
+ }
+
+ lastIndex = 0;
+ for (i = 0; okay && (i < header.virtualMethodsSize); i++) {
+ okay = dexReadAndVerifyClassDataMethod(pData, pLimit,
+ &result->virtualMethods[i], &lastIndex);
+ }
+
+ if (! okay) {
+ free(result);
+ return NULL;
+ }
+
+ return result;
+}
diff --git a/libdex/DexClass.h b/libdex/DexClass.h
new file mode 100644
index 000000000..11b3b0eee
--- /dev/null
+++ b/libdex/DexClass.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to deal with class definition structures in DEX files
+ */
+
+#ifndef LIBDEX_DEXCLASS_H_
+#define LIBDEX_DEXCLASS_H_
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/* expanded form of a class_data_item header */
+struct DexClassDataHeader {
+ u4 staticFieldsSize;
+ u4 instanceFieldsSize;
+ u4 directMethodsSize;
+ u4 virtualMethodsSize;
+};
+
+/* expanded form of encoded_field */
+struct DexField {
+ u4 fieldIdx; /* index to a field_id_item */
+ u4 accessFlags;
+};
+
+/* expanded form of encoded_method */
+struct DexMethod {
+ u4 methodIdx; /* index to a method_id_item */
+ u4 accessFlags;
+ u4 codeOff; /* file offset to a code_item */
+};
+
+/* expanded form of class_data_item. Note: If a particular item is
+ * absent (e.g., no static fields), then the corresponding pointer
+ * is set to NULL. */
+struct DexClassData {
+ DexClassDataHeader header;
+ DexField* staticFields;
+ DexField* instanceFields;
+ DexMethod* directMethods;
+ DexMethod* virtualMethods;
+};
+
+/* Read and verify the header of a class_data_item. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure). */
+bool dexReadAndVerifyClassDataHeader(const u1** pData, const u1* pLimit,
+ DexClassDataHeader *pHeader);
+
+/* Read and verify an encoded_field. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags or indices
+ * are valid. */
+bool dexReadAndVerifyClassDataField(const u1** pData, const u1* pLimit,
+ DexField* pField, u4* lastIndex);
+
+/* Read and verify an encoded_method. This updates the
+ * given data pointer to point past the end of the read data and
+ * returns an "okay" flag (that is, false == failure).
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+bool dexReadAndVerifyClassDataMethod(const u1** pData, const u1* pLimit,
+ DexMethod* pMethod, u4* lastIndex);
+
+/* Read, verify, and return an entire class_data_item. This updates
+ * the given data pointer to point past the end of the read data. This
+ * function allocates a single chunk of memory for the result, which
+ * must subsequently be free()d. This function returns NULL if there
+ * was trouble parsing the data. If this function is passed NULL, it
+ * returns an initialized empty DexClassData structure.
+ *
+ * The verification done by this function is of the raw data format
+ * only; it does not verify that access flags, indices, or offsets
+ * are valid. */
+DexClassData* dexReadAndVerifyClassData(const u1** pData, const u1* pLimit);
+
+/*
+ * Get the DexCode for a DexMethod. Returns NULL if the class is native
+ * or abstract.
+ */
+DEX_INLINE const DexCode* dexGetCode(const DexFile* pDexFile,
+ const DexMethod* pDexMethod)
+{
+ if (pDexMethod->codeOff == 0)
+ return NULL;
+ return (const DexCode*) (pDexFile->baseAddr + pDexMethod->codeOff);
+}
+
+
+/* Read the header of a class_data_item without verification. This
+ * updates the given data pointer to point past the end of the read
+ * data. */
+DEX_INLINE void dexReadClassDataHeader(const u1** pData,
+ DexClassDataHeader *pHeader) {
+ pHeader->staticFieldsSize = readUnsignedLeb128(pData);
+ pHeader->instanceFieldsSize = readUnsignedLeb128(pData);
+ pHeader->directMethodsSize = readUnsignedLeb128(pData);
+ pHeader->virtualMethodsSize = readUnsignedLeb128(pData);
+}
+
+/* Read an encoded_field without verification. This updates the
+ * given data pointer to point past the end of the read data.
+ *
+ * The lastIndex value should be set to 0 before the first field in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ */
+DEX_INLINE void dexReadClassDataField(const u1** pData, DexField* pField,
+ u4* lastIndex) {
+ u4 index = *lastIndex + readUnsignedLeb128(pData);
+
+ pField->accessFlags = readUnsignedLeb128(pData);
+ pField->fieldIdx = index;
+ *lastIndex = index;
+}
+
+/* Read an encoded_method without verification. This updates the
+ * given data pointer to point past the end of the read data.
+ *
+ * The lastIndex value should be set to 0 before the first method in
+ * a list is read. It is updated as fields are read and used in the
+ * decode process.
+ */
+DEX_INLINE void dexReadClassDataMethod(const u1** pData, DexMethod* pMethod,
+ u4* lastIndex) {
+ u4 index = *lastIndex + readUnsignedLeb128(pData);
+
+ pMethod->accessFlags = readUnsignedLeb128(pData);
+ pMethod->codeOff = readUnsignedLeb128(pData);
+ pMethod->methodIdx = index;
+ *lastIndex = index;
+}
+
+#endif // LIBDEX_DEXCLASS_H_
diff --git a/libdex/DexDataMap.cpp b/libdex/DexDataMap.cpp
new file mode 100644
index 000000000..18e4a4519
--- /dev/null
+++ b/libdex/DexDataMap.cpp
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Verification-time map of data section items
+ */
+
+#include "DexDataMap.h"
+#include <stdlib.h>
+
+/*
+ * Allocate and initialize a DexDataMap. Returns NULL on failure.
+ */
+DexDataMap* dexDataMapAlloc(u4 maxCount) {
+ /*
+ * Allocate a single chunk for the DexDataMap itself as well as the
+ * two arrays.
+ */
+ size_t size = 0;
+ DexDataMap* map = NULL;
+
+ const u4 sizeOfItems = (u4) (sizeof(u4) + sizeof(u2));
+ if (__builtin_mul_overflow(maxCount, sizeOfItems, &size) ||
+ __builtin_add_overflow(size, sizeof(DexDataMap), &size)) {
+ return NULL;
+ }
+
+ map = (DexDataMap*) malloc(size);
+
+ if (map == NULL) {
+ return NULL;
+ }
+
+ map->count = 0;
+ map->max = maxCount;
+ map->offsets = (u4*) (map + 1);
+ map->types = (u2*) (map->offsets + maxCount);
+
+ return map;
+}
+
+/*
+ * Free a DexDataMap.
+ */
+void dexDataMapFree(DexDataMap* map) {
+ /*
+ * Since everything got allocated together, everything can be freed
+ * in one fell swoop. Also, free(NULL) is a nop (per spec), so we
+ * don't have to worry about an explicit test for that.
+ */
+ free(map);
+}
+
+/*
+ * Add a new element to the map. The offset must be greater than the
+ * all previously added offsets.
+ */
+void dexDataMapAdd(DexDataMap* map, u4 offset, u2 type) {
+ assert(map != NULL);
+ assert(map->count < map->max);
+
+ if ((map->count != 0) &&
+ (map->offsets[map->count - 1] >= offset)) {
+ ALOGE("Out-of-order data map offset: %#x then %#x",
+ map->offsets[map->count - 1], offset);
+ return;
+ }
+
+ map->offsets[map->count] = offset;
+ map->types[map->count] = type;
+ map->count++;
+}
+
+/*
+ * Get the type associated with the given offset. This returns -1 if
+ * there is no entry for the given offset.
+ */
+int dexDataMapGet(DexDataMap* map, u4 offset) {
+ assert(map != NULL);
+
+ // Note: Signed type is important for max and min.
+ int min = 0;
+ int max = map->count - 1;
+ u4* offsets = map->offsets;
+
+ while (max >= min) {
+ int guessIdx = (min + max) >> 1;
+ u4 guess = offsets[guessIdx];
+
+ if (offset < guess) {
+ max = guessIdx - 1;
+ } else if (offset > guess) {
+ min = guessIdx + 1;
+ } else {
+ // We have a winner!
+ return map->types[guessIdx];
+ }
+ }
+
+ // No match.
+ return -1;
+}
+
+/*
+ * Verify that there is an entry in the map, mapping the given offset to
+ * the given type. This will return true if such an entry exists and
+ * return false as well as log an error if not.
+ */
+bool dexDataMapVerify(DexDataMap* map, u4 offset, u2 type) {
+ int found = dexDataMapGet(map, offset);
+
+ if (found == type) {
+ return true;
+ }
+
+ if (found < 0) {
+ ALOGE("No data map entry found @ %#x; expected %x",
+ offset, type);
+ } else {
+ ALOGE("Unexpected data map entry @ %#x: expected %x, found %x",
+ offset, type, found);
+ }
+
+ return false;
+}
diff --git a/libdex/DexDataMap.h b/libdex/DexDataMap.h
new file mode 100644
index 000000000..7e43dc934
--- /dev/null
+++ b/libdex/DexDataMap.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Verification-time map of data section items
+ */
+
+#ifndef LIBDEX_DEXDATAMAP_H_
+#define LIBDEX_DEXDATAMAP_H_
+
+#include "DexFile.h"
+
+struct DexDataMap {
+ u4 count; /* number of items currently in the map */
+ u4 max; /* maximum number of items that may be held */
+ u4* offsets; /* array of item offsets */
+ u2* types; /* corresponding array of item types */
+};
+
+/*
+ * Allocate and initialize a DexDataMap. Returns NULL on failure.
+ */
+DexDataMap* dexDataMapAlloc(u4 maxCount);
+
+/*
+ * Free a DexDataMap.
+ */
+void dexDataMapFree(DexDataMap* map);
+
+/*
+ * Add a new element to the map. The offset must be greater than the
+ * all previously added offsets.
+ */
+void dexDataMapAdd(DexDataMap* map, u4 offset, u2 type);
+
+/*
+ * Get the type associated with the given offset. This returns -1 if
+ * there is no entry for the given offset.
+ */
+int dexDataMapGet(DexDataMap* map, u4 offset);
+
+/*
+ * Verify that there is an entry in the map, mapping the given offset to
+ * the given type. This will return true if such an entry exists and
+ * return false as well as log an error if not.
+ */
+bool dexDataMapVerify(DexDataMap* map, u4 offset, u2 type);
+
+/*
+ * Like dexDataMapVerify(), but also accept a 0 offset as valid.
+ */
+DEX_INLINE bool dexDataMapVerify0Ok(DexDataMap* map, u4 offset, u2 type) {
+ if (offset == 0) {
+ return true;
+ }
+
+ return dexDataMapVerify(map, offset, type);
+}
+
+#endif // LIBDEX_DEXDATAMAP_H_
diff --git a/libdex/DexDebugInfo.cpp b/libdex/DexDebugInfo.cpp
new file mode 100644
index 000000000..5f9ffbe0f
--- /dev/null
+++ b/libdex/DexDebugInfo.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handling of method debug info in a .dex file.
+ */
+
+#include "DexDebugInfo.h"
+#include "DexProto.h"
+#include "Leb128.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Reads a string index as encoded for the debug info format,
+ * returning a string pointer or NULL as appropriate.
+ */
+static const char* readStringIdx(const DexFile* pDexFile,
+ const u1** pStream) {
+ u4 stringIdx = readUnsignedLeb128(pStream);
+
+ // Remember, encoded string indicies have 1 added to them.
+ if (stringIdx == 0) {
+ return NULL;
+ } else {
+ return dexStringById(pDexFile, stringIdx - 1);
+ }
+}
+
+/*
+ * Reads a type index as encoded for the debug info format, returning
+ * a string pointer for its descriptor or NULL as appropriate.
+ */
+static const char* readTypeIdx(const DexFile* pDexFile,
+ const u1** pStream) {
+ u4 typeIdx = readUnsignedLeb128(pStream);
+
+ // Remember, encoded type indicies have 1 added to them.
+ if (typeIdx == 0) {
+ return NULL;
+ } else {
+ return dexStringByTypeIdx(pDexFile, typeIdx - 1);
+ }
+}
+
+struct LocalInfo {
+ const char *name;
+ const char *descriptor;
+ const char *signature;
+ u2 startAddress;
+ bool live;
+};
+
+static void emitLocalCbIfLive(void *cnxt, int reg, u4 endAddress,
+ LocalInfo *localInReg, DexDebugNewLocalCb localCb)
+{
+ if (localCb != NULL && localInReg[reg].live) {
+ localCb(cnxt, reg, localInReg[reg].startAddress, endAddress,
+ localInReg[reg].name,
+ localInReg[reg].descriptor,
+ localInReg[reg].signature == NULL
+ ? "" : localInReg[reg].signature );
+ }
+}
+
+static void invalidStream(const char* classDescriptor, const DexProto* proto) {
+ IF_ALOGE() {
+ char* methodDescriptor = dexProtoCopyMethodDescriptor(proto);
+ ALOGE("Invalid debug info stream. class %s; proto %s",
+ classDescriptor, methodDescriptor);
+ free(methodDescriptor);
+ }
+}
+
+static void dexDecodeDebugInfo0(
+ const DexFile* pDexFile,
+ const DexCode* pCode,
+ const char* classDescriptor,
+ u4 protoIdx,
+ u4 accessFlags,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+ void* cnxt,
+ const u1* stream,
+ LocalInfo* localInReg)
+{
+ DexProto proto = { pDexFile, protoIdx };
+ u4 line = readUnsignedLeb128(&stream);
+ u4 parametersSize = readUnsignedLeb128(&stream);
+ u2 argReg = pCode->registersSize - pCode->insSize;
+ u4 address = 0;
+
+ if ((accessFlags & ACC_STATIC) == 0) {
+ /*
+ * The code is an instance method, which means that there is
+ * an initial this parameter. Also, the proto list should
+ * contain exactly one fewer argument word than the insSize
+ * indicates.
+ */
+ assert(pCode->insSize == (dexProtoComputeArgsSize(&proto) + 1));
+ localInReg[argReg].name = "this";
+ localInReg[argReg].descriptor = classDescriptor;
+ localInReg[argReg].startAddress = 0;
+ localInReg[argReg].live = true;
+ argReg++;
+ } else {
+ assert(pCode->insSize == dexProtoComputeArgsSize(&proto));
+ }
+
+ DexParameterIterator iterator;
+ dexParameterIteratorInit(&iterator, &proto);
+
+ while (parametersSize-- != 0) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+ const char *name;
+ int reg;
+
+ if ((argReg >= pCode->registersSize) || (descriptor == NULL)) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ name = readStringIdx(pDexFile, &stream);
+ reg = argReg;
+
+ switch (descriptor[0]) {
+ case 'D':
+ case 'J':
+ argReg += 2;
+ break;
+ default:
+ argReg += 1;
+ break;
+ }
+
+ if (name != NULL) {
+ localInReg[reg].name = name;
+ localInReg[reg].descriptor = descriptor;
+ localInReg[reg].signature = NULL;
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ }
+ }
+
+ for (;;) {
+ u1 opcode = *stream++;
+ u2 reg;
+
+ switch (opcode) {
+ case DBG_END_SEQUENCE:
+ return;
+
+ case DBG_ADVANCE_PC:
+ address += readUnsignedLeb128(&stream);
+ break;
+
+ case DBG_ADVANCE_LINE:
+ line += readSignedLeb128(&stream);
+ break;
+
+ case DBG_START_LOCAL:
+ case DBG_START_LOCAL_EXTENDED:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ // Emit what was previously there, if anything
+ emitLocalCbIfLive(cnxt, reg, address,
+ localInReg, localCb);
+
+ localInReg[reg].name = readStringIdx(pDexFile, &stream);
+ localInReg[reg].descriptor = readTypeIdx(pDexFile, &stream);
+ if (opcode == DBG_START_LOCAL_EXTENDED) {
+ localInReg[reg].signature
+ = readStringIdx(pDexFile, &stream);
+ } else {
+ localInReg[reg].signature = NULL;
+ }
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ break;
+
+ case DBG_END_LOCAL:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
+ localInReg[reg].live = false;
+ break;
+
+ case DBG_RESTART_LOCAL:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ if (localInReg[reg].name == NULL
+ || localInReg[reg].descriptor == NULL) {
+ invalidStream(classDescriptor, &proto);
+ return;
+ }
+
+ /*
+ * If the register is live, the "restart" is superfluous,
+ * and we don't want to mess with the existing start address.
+ */
+ if (!localInReg[reg].live) {
+ localInReg[reg].startAddress = address;
+ localInReg[reg].live = true;
+ }
+ break;
+
+ case DBG_SET_PROLOGUE_END:
+ case DBG_SET_EPILOGUE_BEGIN:
+ case DBG_SET_FILE:
+ break;
+
+ default: {
+ int adjopcode = opcode - DBG_FIRST_SPECIAL;
+
+ address += adjopcode / DBG_LINE_RANGE;
+ line += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
+
+ if (posCb != NULL) {
+ int done;
+ done = posCb(cnxt, address, line);
+
+ if (done) {
+ // early exit
+ return;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+// TODO optimize localCb == NULL case
+void dexDecodeDebugInfo(
+ const DexFile* pDexFile,
+ const DexCode* pCode,
+ const char* classDescriptor,
+ u4 protoIdx,
+ u4 accessFlags,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+ void* cnxt)
+{
+ const u1* stream = dexGetDebugInfoStream(pDexFile, pCode);
+ LocalInfo localInReg[pCode->registersSize];
+
+ memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
+
+ if (stream != NULL) {
+ dexDecodeDebugInfo0(pDexFile, pCode, classDescriptor, protoIdx, accessFlags,
+ posCb, localCb, cnxt, stream, localInReg);
+ }
+
+ for (int reg = 0; reg < pCode->registersSize; reg++) {
+ emitLocalCbIfLive(cnxt, reg, pCode->insnsSize, localInReg, localCb);
+ }
+}
diff --git a/libdex/DexDebugInfo.h b/libdex/DexDebugInfo.h
new file mode 100644
index 000000000..bd0954c8c
--- /dev/null
+++ b/libdex/DexDebugInfo.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Handling of method debug info in a .dex file.
+ */
+
+#ifndef LIBDEX_DEXDEBUGINFO_H_
+#define LIBDEX_DEXDEBUGINFO_H_
+
+#include "DexFile.h"
+
+/*
+ * Callback for "new position table entry".
+ * Returning non-0 causes the decoder to stop early.
+ */
+typedef int (*DexDebugNewPositionCb)(void *cnxt, u4 address, u4 lineNum);
+
+/*
+ * Callback for "new locals table entry". "signature" is an empty string
+ * if no signature is available for an entry.
+ */
+typedef void (*DexDebugNewLocalCb)(void *cnxt, u2 reg, u4 startAddress,
+ u4 endAddress, const char *name, const char *descriptor,
+ const char *signature);
+
+/*
+ * Decode debug info for method.
+ *
+ * posCb is called in ascending address order.
+ * localCb is called in order of ascending end address.
+ */
+void dexDecodeDebugInfo(
+ const DexFile* pDexFile,
+ const DexCode* pDexCode,
+ const char* classDescriptor,
+ u4 protoIdx,
+ u4 accessFlags,
+ DexDebugNewPositionCb posCb, DexDebugNewLocalCb localCb,
+ void* cnxt);
+
+#endif // LIBDEX_DEXDEBUGINFO_H_
diff --git a/libdex/DexFile.cpp b/libdex/DexFile.cpp
new file mode 100644
index 000000000..d9acd5188
--- /dev/null
+++ b/libdex/DexFile.cpp
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Access the contents of a .dex file.
+ */
+
+#include "DexFile.h"
+#include "DexOptData.h"
+#include "DexProto.h"
+#include "DexCatch.h"
+#include "Leb128.h"
+#include "sha1.h"
+#include "ZipArchive.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/*
+ * Verifying checksums is good, but it slows things down and causes us to
+ * touch every page. In the "optimized" world, it doesn't work at all,
+ * because we rewrite the contents.
+ */
+static const bool kVerifySignature = false;
+
+/* (documented in header) */
+char dexGetPrimitiveTypeDescriptorChar(PrimitiveType type) {
+ const char* string = dexGetPrimitiveTypeDescriptor(type);
+
+ return (string == NULL) ? '\0' : string[0];
+}
+
+/* (documented in header) */
+const char* dexGetPrimitiveTypeDescriptor(PrimitiveType type) {
+ switch (type) {
+ case PRIM_VOID: return "V";
+ case PRIM_BOOLEAN: return "Z";
+ case PRIM_BYTE: return "B";
+ case PRIM_SHORT: return "S";
+ case PRIM_CHAR: return "C";
+ case PRIM_INT: return "I";
+ case PRIM_LONG: return "J";
+ case PRIM_FLOAT: return "F";
+ case PRIM_DOUBLE: return "D";
+ default: return NULL;
+ }
+
+ return NULL;
+}
+
+/* (documented in header) */
+const char* dexGetBoxedTypeDescriptor(PrimitiveType type) {
+ switch (type) {
+ case PRIM_VOID: return NULL;
+ case PRIM_BOOLEAN: return "Ljava/lang/Boolean;";
+ case PRIM_BYTE: return "Ljava/lang/Byte;";
+ case PRIM_SHORT: return "Ljava/lang/Short;";
+ case PRIM_CHAR: return "Ljava/lang/Character;";
+ case PRIM_INT: return "Ljava/lang/Integer;";
+ case PRIM_LONG: return "Ljava/lang/Long;";
+ case PRIM_FLOAT: return "Ljava/lang/Float;";
+ case PRIM_DOUBLE: return "Ljava/lang/Double;";
+ default: return NULL;
+ }
+}
+
+/* (documented in header) */
+PrimitiveType dexGetPrimitiveTypeFromDescriptorChar(char descriptorChar) {
+ switch (descriptorChar) {
+ case 'V': return PRIM_VOID;
+ case 'Z': return PRIM_BOOLEAN;
+ case 'B': return PRIM_BYTE;
+ case 'S': return PRIM_SHORT;
+ case 'C': return PRIM_CHAR;
+ case 'I': return PRIM_INT;
+ case 'J': return PRIM_LONG;
+ case 'F': return PRIM_FLOAT;
+ case 'D': return PRIM_DOUBLE;
+ default: return PRIM_NOT;
+ }
+}
+
+/* Return the UTF-8 encoded string with the specified string_id index,
+ * also filling in the UTF-16 size (number of 16-bit code points).*/
+const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,
+ u4* utf16Size) {
+ const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
+ const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
+
+ *utf16Size = readUnsignedLeb128(&ptr);
+ return (const char*) ptr;
+}
+
+/*
+ * Format an SHA-1 digest for printing. tmpBuf must be able to hold at
+ * least kSHA1DigestOutputLen bytes.
+ */
+const char* dvmSHA1DigestToStr(const unsigned char digest[], char* tmpBuf);
+
+/*
+ * Compute a SHA-1 digest on a range of bytes.
+ */
+static void dexComputeSHA1Digest(const unsigned char* data, size_t length,
+ unsigned char digest[])
+{
+ SHA1_CTX context;
+ SHA1Init(&context);
+ SHA1Update(&context, data, length);
+ SHA1Final(digest, &context);
+}
+
+/*
+ * Format the SHA-1 digest into the buffer, which must be able to hold at
+ * least kSHA1DigestOutputLen bytes. Returns a pointer to the buffer,
+ */
+static const char* dexSHA1DigestToStr(const unsigned char digest[],char* tmpBuf)
+{
+ static const char hexDigit[] = "0123456789abcdef";
+ char* cp;
+ int i;
+
+ cp = tmpBuf;
+ for (i = 0; i < kSHA1DigestLen; i++) {
+ *cp++ = hexDigit[digest[i] >> 4];
+ *cp++ = hexDigit[digest[i] & 0x0f];
+ }
+ *cp++ = '\0';
+
+ assert(cp == tmpBuf + kSHA1DigestOutputLen);
+
+ return tmpBuf;
+}
+
+/*
+ * Compute a hash code on a UTF-8 string, for use with internal hash tables.
+ *
+ * This may or may not be compatible with UTF-8 hash functions used inside
+ * the Dalvik VM.
+ *
+ * The basic "multiply by 31 and add" approach does better on class names
+ * than most other things tried (e.g. adler32).
+ */
+static u4 classDescriptorHash(const char* str)
+{
+ u4 hash = 1;
+
+ while (*str != '\0')
+ hash = hash * 31 + *str++;
+
+ return hash;
+}
+
+/*
+ * Add an entry to the class lookup table. We hash the string and probe
+ * until we find an open slot.
+ */
+static void classLookupAdd(DexFile* pDexFile, DexClassLookup* pLookup,
+ int stringOff, int classDefOff, int* pNumProbes)
+{
+ const char* classDescriptor =
+ (const char*) (pDexFile->baseAddr + stringOff);
+ u4 hash = classDescriptorHash(classDescriptor);
+ int mask = pLookup->numEntries-1;
+ int idx = hash & mask;
+
+ /*
+ * Find the first empty slot. We oversized the table, so this is
+ * guaranteed to finish.
+ */
+ int probes = 0;
+ while (pLookup->table[idx].classDescriptorOffset != 0) {
+ idx = (idx + 1) & mask;
+ probes++;
+ }
+ //if (probes > 1)
+ // ALOGW("classLookupAdd: probes=%d", probes);
+
+ pLookup->table[idx].classDescriptorHash = hash;
+ pLookup->table[idx].classDescriptorOffset = stringOff;
+ pLookup->table[idx].classDefOffset = classDefOff;
+ *pNumProbes = probes;
+}
+
+/*
+ * Create the class lookup hash table.
+ *
+ * Returns newly-allocated storage.
+ */
+DexClassLookup* dexCreateClassLookup(DexFile* pDexFile)
+{
+ DexClassLookup* pLookup;
+ int allocSize;
+ int i, numEntries;
+ int numProbes, totalProbes, maxProbes;
+
+ numProbes = totalProbes = maxProbes = 0;
+
+ assert(pDexFile != NULL);
+
+ /*
+ * Using a factor of 3 results in far less probing than a factor of 2,
+ * but almost doubles the flash storage requirements for the bootstrap
+ * DEX files. The overall impact on class loading performance seems
+ * to be minor. We could probably get some performance improvement by
+ * using a secondary hash.
+ */
+ numEntries = dexRoundUpPower2(pDexFile->pHeader->classDefsSize * 2);
+ allocSize = offsetof(DexClassLookup, table)
+ + numEntries * sizeof(pLookup->table[0]);
+
+ pLookup = (DexClassLookup*) calloc(1, allocSize);
+ if (pLookup == NULL)
+ return NULL;
+ pLookup->size = allocSize;
+ pLookup->numEntries = numEntries;
+
+ for (i = 0; i < (int)pDexFile->pHeader->classDefsSize; i++) {
+ const DexClassDef* pClassDef;
+ const char* pString;
+
+ pClassDef = dexGetClassDef(pDexFile, i);
+ pString = dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+
+ classLookupAdd(pDexFile, pLookup,
+ (u1*)pString - pDexFile->baseAddr,
+ (u1*)pClassDef - pDexFile->baseAddr, &numProbes);
+
+ if (numProbes > maxProbes)
+ maxProbes = numProbes;
+ totalProbes += numProbes;
+ }
+
+ ALOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
+ " total=%d max=%d",
+ pDexFile->pHeader->classDefsSize, numEntries,
+ (100 * pDexFile->pHeader->classDefsSize) / numEntries,
+ allocSize, totalProbes, maxProbes);
+
+ return pLookup;
+}
+
+
+/*
+ * Set up the basic raw data pointers of a DexFile. This function isn't
+ * meant for general use.
+ */
+void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data) {
+ DexHeader *pHeader = (DexHeader*) data;
+
+ pDexFile->baseAddr = data;
+ pDexFile->pHeader = pHeader;
+ pDexFile->pStringIds = (const DexStringId*) (data + pHeader->stringIdsOff);
+ pDexFile->pTypeIds = (const DexTypeId*) (data + pHeader->typeIdsOff);
+ pDexFile->pFieldIds = (const DexFieldId*) (data + pHeader->fieldIdsOff);
+ pDexFile->pMethodIds = (const DexMethodId*) (data + pHeader->methodIdsOff);
+ pDexFile->pProtoIds = (const DexProtoId*) (data + pHeader->protoIdsOff);
+ pDexFile->pClassDefs = (const DexClassDef*) (data + pHeader->classDefsOff);
+ pDexFile->pLinkData = (const DexLink*) (data + pHeader->linkOff);
+}
+
+/*
+ * Parse an optimized or unoptimized .dex file sitting in memory. This is
+ * called after the byte-ordering and structure alignment has been fixed up.
+ *
+ * On success, return a newly-allocated DexFile.
+ */
+DexFile* dexFileParse(const u1* data, size_t length, int flags)
+{
+ DexFile* pDexFile = NULL;
+ const DexHeader* pHeader;
+ const u1* magic;
+ int result = -1;
+
+ if (length < sizeof(DexHeader)) {
+ ALOGE("too short to be a valid .dex");
+ goto bail; /* bad file format */
+ }
+
+ pDexFile = (DexFile*) malloc(sizeof(DexFile));
+ if (pDexFile == NULL)
+ goto bail; /* alloc failure */
+ memset(pDexFile, 0, sizeof(DexFile));
+
+ /*
+ * Peel off the optimized header.
+ */
+ if (memcmp(data, DEX_OPT_MAGIC, 4) == 0) {
+ magic = data;
+ if (memcmp(magic+4, DEX_OPT_MAGIC_VERS, 4) != 0) {
+ ALOGE("bad opt version (0x%02x %02x %02x %02x)",
+ magic[4], magic[5], magic[6], magic[7]);
+ goto bail;
+ }
+
+ pDexFile->pOptHeader = (const DexOptHeader*) data;
+ ALOGV("Good opt header, DEX offset is %d, flags=0x%02x",
+ pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
+
+ /* parse the optimized dex file tables */
+ if (!dexParseOptData(data, length, pDexFile))
+ goto bail;
+
+ /* ignore the opt header and appended data from here on out */
+ data += pDexFile->pOptHeader->dexOffset;
+ length -= pDexFile->pOptHeader->dexOffset;
+ if (pDexFile->pOptHeader->dexLength > length) {
+ ALOGE("File truncated? stored len=%d, rem len=%d",
+ pDexFile->pOptHeader->dexLength, (int) length);
+ goto bail;
+ }
+ length = pDexFile->pOptHeader->dexLength;
+ }
+
+ dexFileSetupBasicPointers(pDexFile, data);
+ pHeader = pDexFile->pHeader;
+
+ if (!dexHasValidMagic(pHeader)) {
+ goto bail;
+ }
+
+ /*
+ * Verify the checksum(s). This is reasonably quick, but does require
+ * touching every byte in the DEX file. The base checksum changes after
+ * byte-swapping and DEX optimization.
+ */
+ if (flags & kDexParseVerifyChecksum) {
+ u4 adler = dexComputeChecksum(pHeader);
+ if (adler != pHeader->checksum) {
+ ALOGE("ERROR: bad checksum (%08x vs %08x)",
+ adler, pHeader->checksum);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ ALOGV("+++ adler32 checksum (%08x) verified", adler);
+ }
+
+ const DexOptHeader* pOptHeader = pDexFile->pOptHeader;
+ if (pOptHeader != NULL) {
+ adler = dexComputeOptChecksum(pOptHeader);
+ if (adler != pOptHeader->checksum) {
+ ALOGE("ERROR: bad opt checksum (%08x vs %08x)",
+ adler, pOptHeader->checksum);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ ALOGV("+++ adler32 opt checksum (%08x) verified", adler);
+ }
+ }
+ }
+
+ /*
+ * Verify the SHA-1 digest. (Normally we don't want to do this --
+ * the digest is used to uniquely identify the original DEX file, and
+ * can't be computed for verification after the DEX is byte-swapped
+ * and optimized.)
+ */
+ if (kVerifySignature) {
+ unsigned char sha1Digest[kSHA1DigestLen];
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum) +
+ kSHA1DigestLen;
+
+ dexComputeSHA1Digest(data + nonSum, length - nonSum, sha1Digest);
+ if (memcmp(sha1Digest, pHeader->signature, kSHA1DigestLen) != 0) {
+ char tmpBuf1[kSHA1DigestOutputLen];
+ char tmpBuf2[kSHA1DigestOutputLen];
+ ALOGE("ERROR: bad SHA1 digest (%s vs %s)",
+ dexSHA1DigestToStr(sha1Digest, tmpBuf1),
+ dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ ALOGV("+++ sha1 digest verified");
+ }
+ }
+
+ if (pHeader->fileSize != length) {
+ ALOGE("ERROR: stored file size (%d) != expected (%d)",
+ (int) pHeader->fileSize, (int) length);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ }
+
+ if (pHeader->classDefsSize == 0) {
+ ALOGE("ERROR: DEX file has no classes in it, failing");
+ goto bail;
+ }
+
+ /*
+ * Success!
+ */
+ result = 0;
+
+bail:
+ if (result != 0 && pDexFile != NULL) {
+ dexFileFree(pDexFile);
+ pDexFile = NULL;
+ }
+ return pDexFile;
+}
+
+/*
+ * Free up the DexFile and any associated data structures.
+ *
+ * Note we may be called with a partially-initialized DexFile.
+ */
+void dexFileFree(DexFile* pDexFile)
+{
+ if (pDexFile == NULL)
+ return;
+
+ free(pDexFile);
+}
+
+/*
+ * Look up a class definition entry by descriptor.
+ *
+ * "descriptor" should look like "Landroid/debug/Stuff;".
+ */
+const DexClassDef* dexFindClass(const DexFile* pDexFile,
+ const char* descriptor)
+{
+ const DexClassLookup* pLookup = pDexFile->pClassLookup;
+ u4 hash;
+ int idx, mask;
+
+ hash = classDescriptorHash(descriptor);
+ mask = pLookup->numEntries - 1;
+ idx = hash & mask;
+
+ /*
+ * Search until we find a matching entry or an empty slot.
+ */
+ while (true) {
+ int offset;
+
+ offset = pLookup->table[idx].classDescriptorOffset;
+ if (offset == 0)
+ return NULL;
+
+ if (pLookup->table[idx].classDescriptorHash == hash) {
+ const char* str;
+
+ str = (const char*) (pDexFile->baseAddr + offset);
+ if (strcmp(str, descriptor) == 0) {
+ return (const DexClassDef*)
+ (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
+ }
+ }
+
+ idx = (idx + 1) & mask;
+ }
+}
+
+
+/*
+ * Compute the DEX file checksum for a memory-mapped DEX file.
+ */
+u4 dexComputeChecksum(const DexHeader* pHeader)
+{
+ const u1* start = (const u1*) pHeader;
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+
+ return (u4) adler32(adler, start + nonSum, pHeader->fileSize - nonSum);
+}
+
+/*
+ * Compute the size, in bytes, of a DexCode.
+ */
+size_t dexGetDexCodeSize(const DexCode* pCode)
+{
+ /*
+ * The catch handler data is the last entry. It has a variable number
+ * of variable-size pieces, so we need to create an iterator.
+ */
+ u4 handlersSize;
+ u4 offset;
+ u4 ui;
+
+ if (pCode->triesSize != 0) {
+ handlersSize = dexGetHandlersSize(pCode);
+ offset = dexGetFirstHandlerOffset(pCode);
+ } else {
+ handlersSize = 0;
+ offset = 0;
+ }
+
+ for (ui = 0; ui < handlersSize; ui++) {
+ DexCatchIterator iterator;
+ dexCatchIteratorInit(&iterator, pCode, offset);
+ offset = dexCatchIteratorGetEndOffset(&iterator, pCode);
+ }
+
+ const u1* handlerData = dexGetCatchHandlerData(pCode);
+
+ //ALOGD("+++ pCode=%p handlerData=%p last offset=%d",
+ // pCode, handlerData, offset);
+
+ /* return the size of the catch handler + everything before it */
+ return (handlerData - (u1*) pCode) + offset;
+}
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+u4 dexRoundUpPower2(u4 val)
+{
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val++;
+
+ return val;
+}
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
new file mode 100644
index 000000000..7fc7d051c
--- /dev/null
+++ b/libdex/DexFile.h
@@ -0,0 +1,1084 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Access .dex (Dalvik Executable Format) files. The code here assumes that
+ * the DEX file has been rewritten (byte-swapped, word-aligned) and that
+ * the contents can be directly accessed as a collection of C arrays. Please
+ * see docs/dalvik/dex-format.html for a detailed description.
+ *
+ * The structure and field names were chosen to match those in the DEX spec.
+ *
+ * It's generally assumed that the DEX file will be stored in shared memory,
+ * obviating the need to copy code and constant pool entries into newly
+ * allocated storage. Maintaining local pointers to items in the shared area
+ * is valid and encouraged.
+ *
+ * All memory-mapped structures are 32-bit aligned unless otherwise noted.
+ */
+
+#ifndef LIBDEX_DEXFILE_H_
+#define LIBDEX_DEXFILE_H_
+
+/*
+ * Annotation to tell clang that we intend to fall through from one case to
+ * another in a switch. Sourced from android-base/macros.h.
+ */
+#define FALLTHROUGH_INTENDED [[clang::fallthrough]]
+
+#ifndef LOG_TAG
+# define LOG_TAG "libdex"
+#endif
+#include <log/log.h>
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <assert.h>
+
+/*
+ * If "very verbose" logging is enabled, make it equivalent to ALOGV.
+ * Otherwise, make it disappear.
+ *
+ * Define this above the #include "Dalvik.h" to enable for only a
+ * single file.
+ */
+/* #define VERY_VERBOSE_LOG */
+#if defined(VERY_VERBOSE_LOG)
+# define LOGVV ALOGV
+# define IF_LOGVV() IF_ALOGV()
+#else
+# define LOGVV(...) ((void)0)
+# define IF_LOGVV() if (false)
+#endif
+
+/*
+ * These match the definitions in the VM specification.
+ */
+typedef uint8_t u1;
+typedef uint16_t u2;
+typedef uint32_t u4;
+typedef uint64_t u8;
+typedef int8_t s1;
+typedef int16_t s2;
+typedef int32_t s4;
+typedef int64_t s8;
+
+#include "libdex/SysUtil.h"
+
+/*
+ * gcc-style inline management -- ensures we have a copy of all functions
+ * in the library, so code that links against us will work whether or not
+ * it was built with optimizations enabled.
+ */
+#ifndef _DEX_GEN_INLINES /* only defined by DexInlines.c */
+# define DEX_INLINE extern __inline__
+#else
+# define DEX_INLINE
+#endif
+
+/* DEX file magic number */
+#define DEX_MAGIC "dex\n"
+
+/* The version for android N, encoded in 4 bytes of ASCII. This differentiates dex files that may
+ * use default methods.
+ */
+#define DEX_MAGIC_VERS_37 "037\0"
+
+/* The version for android O, encoded in 4 bytes of ASCII. This differentiates dex files that may
+ * contain invoke-custom, invoke-polymorphic, call-sites, and method handles.
+ */
+#define DEX_MAGIC_VERS_38 "038\0"
+
+/* The version for android P, encoded in 4 bytes of ASCII. This differentiates dex files that may
+ * contain const-method-handle and const-proto.
+ */
+#define DEX_MAGIC_VERS_39 "039\0"
+
+/* current version, encoded in 4 bytes of ASCII */
+#define DEX_MAGIC_VERS "036\0"
+
+/*
+ * older but still-recognized version (corresponding to Android API
+ * levels 13 and earlier
+ */
+#define DEX_MAGIC_VERS_API_13 "035\0"
+
+/* same, but for optimized DEX header */
+#define DEX_OPT_MAGIC "dey\n"
+#define DEX_OPT_MAGIC_VERS "036\0"
+
+#define DEX_DEP_MAGIC "deps"
+
+/*
+ * 160-bit SHA-1 digest.
+ */
+enum { kSHA1DigestLen = 20,
+ kSHA1DigestOutputLen = kSHA1DigestLen*2 +1 };
+
+/* general constants */
+enum {
+ kDexEndianConstant = 0x12345678, /* the endianness indicator */
+ kDexNoIndex = 0xffffffff, /* not a valid index value */
+};
+
+/*
+ * Enumeration of all the primitive types.
+ */
+enum PrimitiveType {
+ PRIM_NOT = 0, /* value is a reference type, not a primitive type */
+ PRIM_VOID = 1,
+ PRIM_BOOLEAN = 2,
+ PRIM_BYTE = 3,
+ PRIM_SHORT = 4,
+ PRIM_CHAR = 5,
+ PRIM_INT = 6,
+ PRIM_LONG = 7,
+ PRIM_FLOAT = 8,
+ PRIM_DOUBLE = 9,
+};
+
+/*
+ * access flags and masks; the "standard" ones are all <= 0x4000
+ *
+ * Note: There are related declarations in vm/oo/Object.h in the ClassFlags
+ * enum.
+ */
+enum {
+ ACC_PUBLIC = 0x00000001, // class, field, method, ic
+ ACC_PRIVATE = 0x00000002, // field, method, ic
+ ACC_PROTECTED = 0x00000004, // field, method, ic
+ ACC_STATIC = 0x00000008, // field, method, ic
+ ACC_FINAL = 0x00000010, // class, field, method, ic
+ ACC_SYNCHRONIZED = 0x00000020, // method (only allowed on natives)
+ ACC_SUPER = 0x00000020, // class (not used in Dalvik)
+ ACC_VOLATILE = 0x00000040, // field
+ ACC_BRIDGE = 0x00000040, // method (1.5)
+ ACC_TRANSIENT = 0x00000080, // field
+ ACC_VARARGS = 0x00000080, // method (1.5)
+ ACC_NATIVE = 0x00000100, // method
+ ACC_INTERFACE = 0x00000200, // class, ic
+ ACC_ABSTRACT = 0x00000400, // class, method, ic
+ ACC_STRICT = 0x00000800, // method
+ ACC_SYNTHETIC = 0x00001000, // field, method, ic
+ ACC_ANNOTATION = 0x00002000, // class, ic (1.5)
+ ACC_ENUM = 0x00004000, // class, field, ic (1.5)
+ ACC_CONSTRUCTOR = 0x00010000, // method (Dalvik only)
+ ACC_DECLARED_SYNCHRONIZED =
+ 0x00020000, // method (Dalvik only)
+ ACC_CLASS_MASK =
+ (ACC_PUBLIC | ACC_FINAL | ACC_INTERFACE | ACC_ABSTRACT
+ | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM),
+ ACC_INNER_CLASS_MASK =
+ (ACC_CLASS_MASK | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC),
+ ACC_FIELD_MASK =
+ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
+ | ACC_VOLATILE | ACC_TRANSIENT | ACC_SYNTHETIC | ACC_ENUM),
+ ACC_METHOD_MASK =
+ (ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED | ACC_STATIC | ACC_FINAL
+ | ACC_SYNCHRONIZED | ACC_BRIDGE | ACC_VARARGS | ACC_NATIVE
+ | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | ACC_CONSTRUCTOR
+ | ACC_DECLARED_SYNCHRONIZED),
+};
+
+/* annotation constants */
+enum {
+ kDexVisibilityBuild = 0x00, /* annotation visibility */
+ kDexVisibilityRuntime = 0x01,
+ kDexVisibilitySystem = 0x02,
+
+ kDexAnnotationByte = 0x00,
+ kDexAnnotationShort = 0x02,
+ kDexAnnotationChar = 0x03,
+ kDexAnnotationInt = 0x04,
+ kDexAnnotationLong = 0x06,
+ kDexAnnotationFloat = 0x10,
+ kDexAnnotationDouble = 0x11,
+ kDexAnnotationMethodType = 0x15,
+ kDexAnnotationMethodHandle = 0x16,
+ kDexAnnotationString = 0x17,
+ kDexAnnotationType = 0x18,
+ kDexAnnotationField = 0x19,
+ kDexAnnotationMethod = 0x1a,
+ kDexAnnotationEnum = 0x1b,
+ kDexAnnotationArray = 0x1c,
+ kDexAnnotationAnnotation = 0x1d,
+ kDexAnnotationNull = 0x1e,
+ kDexAnnotationBoolean = 0x1f,
+
+ kDexAnnotationValueTypeMask = 0x1f, /* low 5 bits */
+ kDexAnnotationValueArgShift = 5,
+};
+
+/* map item type codes */
+enum {
+ kDexTypeHeaderItem = 0x0000,
+ kDexTypeStringIdItem = 0x0001,
+ kDexTypeTypeIdItem = 0x0002,
+ kDexTypeProtoIdItem = 0x0003,
+ kDexTypeFieldIdItem = 0x0004,
+ kDexTypeMethodIdItem = 0x0005,
+ kDexTypeClassDefItem = 0x0006,
+ kDexTypeCallSiteIdItem = 0x0007,
+ kDexTypeMethodHandleItem = 0x0008,
+ kDexTypeMapList = 0x1000,
+ kDexTypeTypeList = 0x1001,
+ kDexTypeAnnotationSetRefList = 0x1002,
+ kDexTypeAnnotationSetItem = 0x1003,
+ kDexTypeClassDataItem = 0x2000,
+ kDexTypeCodeItem = 0x2001,
+ kDexTypeStringDataItem = 0x2002,
+ kDexTypeDebugInfoItem = 0x2003,
+ kDexTypeAnnotationItem = 0x2004,
+ kDexTypeEncodedArrayItem = 0x2005,
+ kDexTypeAnnotationsDirectoryItem = 0x2006,
+};
+
+/* auxillary data section chunk codes */
+enum {
+ kDexChunkClassLookup = 0x434c4b50, /* CLKP */
+ kDexChunkRegisterMaps = 0x524d4150, /* RMAP */
+
+ kDexChunkEnd = 0x41454e44, /* AEND */
+};
+
+/* debug info opcodes and constants */
+enum {
+ DBG_END_SEQUENCE = 0x00,
+ DBG_ADVANCE_PC = 0x01,
+ DBG_ADVANCE_LINE = 0x02,
+ DBG_START_LOCAL = 0x03,
+ DBG_START_LOCAL_EXTENDED = 0x04,
+ DBG_END_LOCAL = 0x05,
+ DBG_RESTART_LOCAL = 0x06,
+ DBG_SET_PROLOGUE_END = 0x07,
+ DBG_SET_EPILOGUE_BEGIN = 0x08,
+ DBG_SET_FILE = 0x09,
+ DBG_FIRST_SPECIAL = 0x0a,
+ DBG_LINE_BASE = -4,
+ DBG_LINE_RANGE = 15,
+};
+
+/*
+ * Direct-mapped "header_item" struct.
+ */
+struct DexHeader {
+ u1 magic[8]; /* includes version number */
+ u4 checksum; /* adler32 checksum */
+ u1 signature[kSHA1DigestLen]; /* SHA-1 hash */
+ u4 fileSize; /* length of entire file */
+ u4 headerSize; /* offset to start of next section */
+ u4 endianTag;
+ u4 linkSize;
+ u4 linkOff;
+ u4 mapOff;
+ u4 stringIdsSize;
+ u4 stringIdsOff;
+ u4 typeIdsSize;
+ u4 typeIdsOff;
+ u4 protoIdsSize;
+ u4 protoIdsOff;
+ u4 fieldIdsSize;
+ u4 fieldIdsOff;
+ u4 methodIdsSize;
+ u4 methodIdsOff;
+ u4 classDefsSize;
+ u4 classDefsOff;
+ u4 dataSize;
+ u4 dataOff;
+};
+
+/*
+ * Direct-mapped "map_item".
+ */
+struct DexMapItem {
+ u2 type; /* type code (see kDexType* above) */
+ u2 unused;
+ u4 size; /* count of items of the indicated type */
+ u4 offset; /* file offset to the start of data */
+};
+
+/*
+ * Direct-mapped "map_list".
+ */
+struct DexMapList {
+ u4 size; /* #of entries in list */
+ DexMapItem list[1]; /* entries */
+};
+
+/*
+ * Direct-mapped "string_id_item".
+ */
+struct DexStringId {
+ u4 stringDataOff; /* file offset to string_data_item */
+};
+
+/*
+ * Direct-mapped "type_id_item".
+ */
+struct DexTypeId {
+ u4 descriptorIdx; /* index into stringIds list for type descriptor */
+};
+
+/*
+ * Direct-mapped "field_id_item".
+ */
+struct DexFieldId {
+ u2 classIdx; /* index into typeIds list for defining class */
+ u2 typeIdx; /* index into typeIds for field type */
+ u4 nameIdx; /* index into stringIds for field name */
+};
+
+/*
+ * Direct-mapped "method_id_item".
+ */
+struct DexMethodId {
+ u2 classIdx; /* index into typeIds list for defining class */
+ u2 protoIdx; /* index into protoIds for method prototype */
+ u4 nameIdx; /* index into stringIds for method name */
+};
+
+/*
+ * Direct-mapped "proto_id_item".
+ */
+struct DexProtoId {
+ u4 shortyIdx; /* index into stringIds for shorty descriptor */
+ u4 returnTypeIdx; /* index into typeIds list for return type */
+ u4 parametersOff; /* file offset to type_list for parameter types */
+};
+
+/*
+ * Direct-mapped "class_def_item".
+ */
+struct DexClassDef {
+ u4 classIdx; /* index into typeIds for this class */
+ u4 accessFlags;
+ u4 superclassIdx; /* index into typeIds for superclass */
+ u4 interfacesOff; /* file offset to DexTypeList */
+ u4 sourceFileIdx; /* index into stringIds for source file name */
+ u4 annotationsOff; /* file offset to annotations_directory_item */
+ u4 classDataOff; /* file offset to class_data_item */
+ u4 staticValuesOff; /* file offset to DexEncodedArray */
+};
+
+/*
+ * Direct-mapped "call_site_id_item"
+ */
+struct DexCallSiteId {
+ u4 callSiteOff; /* file offset to DexEncodedArray */
+};
+
+/*
+ * Enumeration of method handle type codes.
+ */
+enum MethodHandleType {
+ STATIC_PUT = 0x00,
+ STATIC_GET = 0x01,
+ INSTANCE_PUT = 0x02,
+ INSTANCE_GET = 0x03,
+ INVOKE_STATIC = 0x04,
+ INVOKE_INSTANCE = 0x05,
+ INVOKE_CONSTRUCTOR = 0x06,
+ INVOKE_DIRECT = 0x07,
+ INVOKE_INTERFACE = 0x08
+};
+
+/*
+ * Direct-mapped "method_handle_item"
+ */
+struct DexMethodHandleItem {
+ u2 methodHandleType; /* type of method handle */
+ u2 reserved1; /* reserved for future use */
+ u2 fieldOrMethodIdx; /* index of associated field or method */
+ u2 reserved2; /* reserved for future use */
+};
+
+/*
+ * Direct-mapped "type_item".
+ */
+struct DexTypeItem {
+ u2 typeIdx; /* index into typeIds */
+};
+
+/*
+ * Direct-mapped "type_list".
+ */
+struct DexTypeList {
+ u4 size; /* #of entries in list */
+ DexTypeItem list[1]; /* entries */
+};
+
+/*
+ * Direct-mapped "code_item".
+ *
+ * The "catches" table is used when throwing an exception,
+ * "debugInfo" is used when displaying an exception stack trace or
+ * debugging. An offset of zero indicates that there are no entries.
+ */
+struct DexCode {
+ u2 registersSize;
+ u2 insSize;
+ u2 outsSize;
+ u2 triesSize;
+ u4 debugInfoOff; /* file offset to debug info stream */
+ u4 insnsSize; /* size of the insns array, in u2 units */
+ u2 insns[1];
+ /* followed by optional u2 padding */
+ /* followed by try_item[triesSize] */
+ /* followed by uleb128 handlersSize */
+ /* followed by catch_handler_item[handlersSize] */
+};
+
+/*
+ * Direct-mapped "try_item".
+ */
+struct DexTry {
+ u4 startAddr; /* start address, in 16-bit code units */
+ u2 insnCount; /* instruction count, in 16-bit code units */
+ u2 handlerOff; /* offset in encoded handler data to handlers */
+};
+
+/*
+ * Link table. Currently undefined.
+ */
+struct DexLink {
+ u1 bleargh;
+};
+
+
+/*
+ * Direct-mapped "annotations_directory_item".
+ */
+struct DexAnnotationsDirectoryItem {
+ u4 classAnnotationsOff; /* offset to DexAnnotationSetItem */
+ u4 fieldsSize; /* count of DexFieldAnnotationsItem */
+ u4 methodsSize; /* count of DexMethodAnnotationsItem */
+ u4 parametersSize; /* count of DexParameterAnnotationsItem */
+ /* followed by DexFieldAnnotationsItem[fieldsSize] */
+ /* followed by DexMethodAnnotationsItem[methodsSize] */
+ /* followed by DexParameterAnnotationsItem[parametersSize] */
+};
+
+/*
+ * Direct-mapped "field_annotations_item".
+ */
+struct DexFieldAnnotationsItem {
+ u4 fieldIdx;
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+};
+
+/*
+ * Direct-mapped "method_annotations_item".
+ */
+struct DexMethodAnnotationsItem {
+ u4 methodIdx;
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+};
+
+/*
+ * Direct-mapped "parameter_annotations_item".
+ */
+struct DexParameterAnnotationsItem {
+ u4 methodIdx;
+ u4 annotationsOff; /* offset to DexAnotationSetRefList */
+};
+
+/*
+ * Direct-mapped "annotation_set_ref_item".
+ */
+struct DexAnnotationSetRefItem {
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+};
+
+/*
+ * Direct-mapped "annotation_set_ref_list".
+ */
+struct DexAnnotationSetRefList {
+ u4 size;
+ DexAnnotationSetRefItem list[1];
+};
+
+/*
+ * Direct-mapped "annotation_set_item".
+ */
+struct DexAnnotationSetItem {
+ u4 size;
+ u4 entries[1]; /* offset to DexAnnotationItem */
+};
+
+/*
+ * Direct-mapped "annotation_item".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+struct DexAnnotationItem {
+ u1 visibility;
+ u1 annotation[1]; /* data in encoded_annotation format */
+};
+
+/*
+ * Direct-mapped "encoded_array".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+struct DexEncodedArray {
+ u1 array[1]; /* data in encoded_array format */
+};
+
+/*
+ * Lookup table for classes. It provides a mapping from class name to
+ * class definition. Used by dexFindClass().
+ *
+ * We calculate this at DEX optimization time and embed it in the file so we
+ * don't need the same hash table in every VM. This is slightly slower than
+ * a hash table with direct pointers to the items, but because it's shared
+ * there's less of a penalty for using a fairly sparse table.
+ */
+struct DexClassLookup {
+ int size; // total size, including "size"
+ int numEntries; // size of table[]; always power of 2
+ struct {
+ u4 classDescriptorHash; // class descriptor hash code
+ int classDescriptorOffset; // in bytes, from start of DEX
+ int classDefOffset; // in bytes, from start of DEX
+ } table[1];
+};
+
+/*
+ * Header added by DEX optimization pass. Values are always written in
+ * local byte and structure padding. The first field (magic + version)
+ * is guaranteed to be present and directly readable for all expected
+ * compiler configurations; the rest is version-dependent.
+ *
+ * Try to keep this simple and fixed-size.
+ */
+struct DexOptHeader {
+ u1 magic[8]; /* includes version number */
+
+ u4 dexOffset; /* file offset of DEX header */
+ u4 dexLength;
+ u4 depsOffset; /* offset of optimized DEX dependency table */
+ u4 depsLength;
+ u4 optOffset; /* file offset of optimized data tables */
+ u4 optLength;
+
+ u4 flags; /* some info flags */
+ u4 checksum; /* adler32 checksum covering deps/opt */
+
+ /* pad for 64-bit alignment if necessary */
+};
+
+#define DEX_OPT_FLAG_BIG (1<<1) /* swapped to big-endian */
+
+#define DEX_INTERFACE_CACHE_SIZE 128 /* must be power of 2 */
+
+/*
+ * Structure representing a DEX file.
+ *
+ * Code should regard DexFile as opaque, using the API calls provided here
+ * to access specific structures.
+ */
+struct DexFile {
+ /* directly-mapped "opt" header */
+ const DexOptHeader* pOptHeader;
+
+ /* pointers to directly-mapped structs and arrays in base DEX */
+ const DexHeader* pHeader;
+ const DexStringId* pStringIds;
+ const DexTypeId* pTypeIds;
+ const DexFieldId* pFieldIds;
+ const DexMethodId* pMethodIds;
+ const DexProtoId* pProtoIds;
+ const DexClassDef* pClassDefs;
+ const DexLink* pLinkData;
+
+ /*
+ * These are mapped out of the "auxillary" section, and may not be
+ * included in the file.
+ */
+ const DexClassLookup* pClassLookup;
+ const void* pRegisterMapPool; // RegisterMapClassPool
+
+ /* points to start of DEX file data */
+ const u1* baseAddr;
+
+ /* track memory overhead for auxillary structures */
+ int overhead;
+
+ /* additional app-specific data structures associated with the DEX */
+ //void* auxData;
+};
+
+/*
+ * Utility function -- rounds up to the nearest power of 2.
+ */
+u4 dexRoundUpPower2(u4 val);
+
+/*
+ * Parse an optimized or unoptimized .dex file sitting in memory.
+ *
+ * On success, return a newly-allocated DexFile.
+ */
+DexFile* dexFileParse(const u1* data, size_t length, int flags);
+
+/* bit values for "flags" argument to dexFileParse */
+enum {
+ kDexParseDefault = 0,
+ kDexParseVerifyChecksum = 1,
+ kDexParseContinueOnError = (1 << 1),
+};
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do
+ * structural verification. This is only required for code that opens
+ * "raw" DEX files, such as the DEX optimizer.
+ *
+ * Return 0 on success.
+ */
+int dexSwapAndVerify(u1* addr, int len);
+
+/*
+ * Detect the file type of the given memory buffer via magic number.
+ * Call dexSwapAndVerify() on an unoptimized DEX file, do nothing
+ * but return successfully on an optimized DEX file, and report an
+ * error for all other cases.
+ *
+ * Return 0 on success.
+ */
+int dexSwapAndVerifyIfNecessary(u1* addr, size_t len);
+
+/*
+ * Check to see if the file magic and format version in the given
+ * header are recognized as valid. Returns true if they are
+ * acceptable.
+ */
+bool dexHasValidMagic(const DexHeader* pHeader);
+
+/*
+ * Compute DEX checksum.
+ */
+u4 dexComputeChecksum(const DexHeader* pHeader);
+
+/*
+ * Free a DexFile structure, along with any associated structures.
+ */
+void dexFileFree(DexFile* pDexFile);
+
+/*
+ * Create class lookup table.
+ */
+DexClassLookup* dexCreateClassLookup(DexFile* pDexFile);
+
+/*
+ * Find a class definition by descriptor.
+ */
+const DexClassDef* dexFindClass(const DexFile* pFile, const char* descriptor);
+
+/*
+ * Set up the basic raw data pointers of a DexFile. This function isn't
+ * meant for general use.
+ */
+void dexFileSetupBasicPointers(DexFile* pDexFile, const u1* data);
+
+/* return the DexMapList of the file, if any */
+DEX_INLINE const DexMapList* dexGetMap(const DexFile* pDexFile) {
+ u4 mapOff = pDexFile->pHeader->mapOff;
+
+ if (mapOff == 0) {
+ return NULL;
+ } else {
+ return (const DexMapList*) (pDexFile->baseAddr + mapOff);
+ }
+}
+
+/* return the const char* string data referred to by the given string_id */
+DEX_INLINE const char* dexGetStringData(const DexFile* pDexFile,
+ const DexStringId* pStringId) {
+ const u1* ptr = pDexFile->baseAddr + pStringId->stringDataOff;
+
+ // Skip the uleb128 length.
+ while (*(ptr++) > 0x7f) /* empty */ ;
+
+ return (const char*) ptr;
+}
+/* return the StringId with the specified index */
+DEX_INLINE const DexStringId* dexGetStringId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->stringIdsSize);
+ return &pDexFile->pStringIds[idx];
+}
+/* return the UTF-8 encoded string with the specified string_id index */
+DEX_INLINE const char* dexStringById(const DexFile* pDexFile, u4 idx) {
+ const DexStringId* pStringId = dexGetStringId(pDexFile, idx);
+ return dexGetStringData(pDexFile, pStringId);
+}
+
+/* Return the UTF-8 encoded string with the specified string_id index,
+ * also filling in the UTF-16 size (number of 16-bit code points).*/
+const char* dexStringAndSizeById(const DexFile* pDexFile, u4 idx,
+ u4* utf16Size);
+
+/* return the TypeId with the specified index */
+DEX_INLINE const DexTypeId* dexGetTypeId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->typeIdsSize);
+ return &pDexFile->pTypeIds[idx];
+}
+
+/*
+ * Get the descriptor string associated with a given type index.
+ * The caller should not free() the returned string.
+ */
+DEX_INLINE const char* dexStringByTypeIdx(const DexFile* pDexFile, u4 idx) {
+ const DexTypeId* typeId = dexGetTypeId(pDexFile, idx);
+ return dexStringById(pDexFile, typeId->descriptorIdx);
+}
+
+/* return the MethodId with the specified index */
+DEX_INLINE const DexMethodId* dexGetMethodId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->methodIdsSize);
+ return &pDexFile->pMethodIds[idx];
+}
+
+/* return the FieldId with the specified index */
+DEX_INLINE const DexFieldId* dexGetFieldId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->fieldIdsSize);
+ return &pDexFile->pFieldIds[idx];
+}
+
+/* return the ProtoId with the specified index */
+DEX_INLINE const DexProtoId* dexGetProtoId(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->protoIdsSize);
+ return &pDexFile->pProtoIds[idx];
+}
+
+/*
+ * Get the parameter list from a ProtoId. The returns NULL if the ProtoId
+ * does not have a parameter list.
+ */
+DEX_INLINE const DexTypeList* dexGetProtoParameters(
+ const DexFile *pDexFile, const DexProtoId* pProtoId) {
+ if (pProtoId->parametersOff == 0) {
+ return NULL;
+ }
+ return (const DexTypeList*)
+ (pDexFile->baseAddr + pProtoId->parametersOff);
+}
+
+/* return the ClassDef with the specified index */
+DEX_INLINE const DexClassDef* dexGetClassDef(const DexFile* pDexFile, u4 idx) {
+ assert(idx < pDexFile->pHeader->classDefsSize);
+ return &pDexFile->pClassDefs[idx];
+}
+
+/* given a ClassDef pointer, recover its index */
+DEX_INLINE u4 dexGetIndexForClassDef(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ assert(pClassDef >= pDexFile->pClassDefs &&
+ pClassDef < pDexFile->pClassDefs + pDexFile->pHeader->classDefsSize);
+ return pClassDef - pDexFile->pClassDefs;
+}
+
+/* get the interface list for a DexClass */
+DEX_INLINE const DexTypeList* dexGetInterfacesList(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ if (pClassDef->interfacesOff == 0)
+ return NULL;
+ return (const DexTypeList*)
+ (pDexFile->baseAddr + pClassDef->interfacesOff);
+}
+/* return the Nth entry in a DexTypeList. */
+DEX_INLINE const DexTypeItem* dexGetTypeItem(const DexTypeList* pList,
+ u4 idx)
+{
+ assert(idx < pList->size);
+ return &pList->list[idx];
+}
+/* return the type_idx for the Nth entry in a TypeList */
+DEX_INLINE u4 dexTypeListGetIdx(const DexTypeList* pList, u4 idx) {
+ const DexTypeItem* pItem = dexGetTypeItem(pList, idx);
+ return pItem->typeIdx;
+}
+
+/* get the static values list for a DexClass */
+DEX_INLINE const DexEncodedArray* dexGetStaticValuesList(
+ const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+ if (pClassDef->staticValuesOff == 0)
+ return NULL;
+ return (const DexEncodedArray*)
+ (pDexFile->baseAddr + pClassDef->staticValuesOff);
+}
+
+/* get the annotations directory item for a DexClass */
+DEX_INLINE const DexAnnotationsDirectoryItem* dexGetAnnotationsDirectoryItem(
+ const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+ if (pClassDef->annotationsOff == 0)
+ return NULL;
+ return (const DexAnnotationsDirectoryItem*)
+ (pDexFile->baseAddr + pClassDef->annotationsOff);
+}
+
+/* get the source file string */
+DEX_INLINE const char* dexGetSourceFile(
+ const DexFile* pDexFile, const DexClassDef* pClassDef)
+{
+ if (pClassDef->sourceFileIdx == 0xffffffff)
+ return NULL;
+ return dexStringById(pDexFile, pClassDef->sourceFileIdx);
+}
+
+/* get the size, in bytes, of a DexCode */
+size_t dexGetDexCodeSize(const DexCode* pCode);
+
+/* Get the list of "tries" for the given DexCode. */
+DEX_INLINE const DexTry* dexGetTries(const DexCode* pCode) {
+ const u2* insnsEnd = &pCode->insns[pCode->insnsSize];
+
+ // Round to four bytes.
+ if ((((uintptr_t) insnsEnd) & 3) != 0) {
+ insnsEnd++;
+ }
+
+ return (const DexTry*) insnsEnd;
+}
+
+/* Get the base of the encoded data for the given DexCode. */
+DEX_INLINE const u1* dexGetCatchHandlerData(const DexCode* pCode) {
+ const DexTry* pTries = dexGetTries(pCode);
+ return (const u1*) &pTries[pCode->triesSize];
+}
+
+/* get a pointer to the start of the debugging data */
+DEX_INLINE const u1* dexGetDebugInfoStream(const DexFile* pDexFile,
+ const DexCode* pCode)
+{
+ if (pCode->debugInfoOff == 0) {
+ return NULL;
+ } else {
+ return pDexFile->baseAddr + pCode->debugInfoOff;
+ }
+}
+
+/* DexClassDef convenience - get class descriptor */
+DEX_INLINE const char* dexGetClassDescriptor(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ return dexStringByTypeIdx(pDexFile, pClassDef->classIdx);
+}
+
+/* DexClassDef convenience - get superclass descriptor */
+DEX_INLINE const char* dexGetSuperClassDescriptor(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ if (pClassDef->superclassIdx == 0)
+ return NULL;
+ return dexStringByTypeIdx(pDexFile, pClassDef->superclassIdx);
+}
+
+/* DexClassDef convenience - get class_data_item pointer */
+DEX_INLINE const u1* dexGetClassData(const DexFile* pDexFile,
+ const DexClassDef* pClassDef)
+{
+ if (pClassDef->classDataOff == 0)
+ return NULL;
+ return (const u1*) (pDexFile->baseAddr + pClassDef->classDataOff);
+}
+
+/* Get an annotation set at a particular offset. */
+DEX_INLINE const DexAnnotationSetItem* dexGetAnnotationSetItem(
+ const DexFile* pDexFile, u4 offset)
+{
+ if (offset == 0) {
+ return NULL;
+ }
+ return (const DexAnnotationSetItem*) (pDexFile->baseAddr + offset);
+}
+/* get the class' annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetClassAnnotationSet(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ return dexGetAnnotationSetItem(pDexFile, pAnnoDir->classAnnotationsOff);
+}
+
+/* get the class' field annotation list */
+DEX_INLINE const DexFieldAnnotationsItem* dexGetFieldAnnotations(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ (void) pDexFile;
+ if (pAnnoDir->fieldsSize == 0)
+ return NULL;
+
+ // Skip past the header to the start of the field annotations.
+ return (const DexFieldAnnotationsItem*) &pAnnoDir[1];
+}
+
+/* get field annotation list size */
+DEX_INLINE int dexGetFieldAnnotationsSize(const DexFile* pDexFile,
+ const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ (void) pDexFile;
+ return pAnnoDir->fieldsSize;
+}
+
+/* return a pointer to the field's annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetFieldAnnotationSetItem(
+ const DexFile* pDexFile, const DexFieldAnnotationsItem* pItem)
+{
+ return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* get the class' method annotation list */
+DEX_INLINE const DexMethodAnnotationsItem* dexGetMethodAnnotations(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ (void) pDexFile;
+ if (pAnnoDir->methodsSize == 0)
+ return NULL;
+
+ /*
+ * Skip past the header and field annotations to the start of the
+ * method annotations.
+ */
+ const u1* addr = (const u1*) &pAnnoDir[1];
+ addr += pAnnoDir->fieldsSize * sizeof (DexFieldAnnotationsItem);
+ return (const DexMethodAnnotationsItem*) addr;
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetMethodAnnotationsSize(const DexFile* pDexFile,
+ const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ (void) pDexFile;
+ return pAnnoDir->methodsSize;
+}
+
+/* return a pointer to the method's annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetMethodAnnotationSetItem(
+ const DexFile* pDexFile, const DexMethodAnnotationsItem* pItem)
+{
+ return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* get the class' parameter annotation list */
+DEX_INLINE const DexParameterAnnotationsItem* dexGetParameterAnnotations(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ (void) pDexFile;
+ if (pAnnoDir->parametersSize == 0)
+ return NULL;
+
+ /*
+ * Skip past the header, field annotations, and method annotations
+ * to the start of the parameter annotations.
+ */
+ const u1* addr = (const u1*) &pAnnoDir[1];
+ addr += pAnnoDir->fieldsSize * sizeof (DexFieldAnnotationsItem);
+ addr += pAnnoDir->methodsSize * sizeof (DexMethodAnnotationsItem);
+ return (const DexParameterAnnotationsItem*) addr;
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetParameterAnnotationsSize(const DexFile* pDexFile,
+ const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ (void) pDexFile;
+ return pAnnoDir->parametersSize;
+}
+
+/* return the parameter annotation ref list */
+DEX_INLINE const DexAnnotationSetRefList* dexGetParameterAnnotationSetRefList(
+ const DexFile* pDexFile, const DexParameterAnnotationsItem* pItem)
+{
+ if (pItem->annotationsOff == 0) {
+ return NULL;
+ }
+ return (const DexAnnotationSetRefList*) (pDexFile->baseAddr + pItem->annotationsOff);
+}
+
+/* get method annotation list size */
+DEX_INLINE int dexGetParameterAnnotationSetRefSize(const DexFile* pDexFile,
+ const DexParameterAnnotationsItem* pItem)
+{
+ if (pItem->annotationsOff == 0) {
+ return 0;
+ }
+ return dexGetParameterAnnotationSetRefList(pDexFile, pItem)->size;
+}
+
+/* return the Nth entry from an annotation set ref list */
+DEX_INLINE const DexAnnotationSetRefItem* dexGetParameterAnnotationSetRef(
+ const DexAnnotationSetRefList* pList, u4 idx)
+{
+ assert(idx < pList->size);
+ return &pList->list[idx];
+}
+
+/* given a DexAnnotationSetRefItem, return the DexAnnotationSetItem */
+DEX_INLINE const DexAnnotationSetItem* dexGetSetRefItemItem(
+ const DexFile* pDexFile, const DexAnnotationSetRefItem* pItem)
+{
+ return dexGetAnnotationSetItem(pDexFile, pItem->annotationsOff);
+}
+
+/* return the Nth annotation offset from a DexAnnotationSetItem */
+DEX_INLINE u4 dexGetAnnotationOff(
+ const DexAnnotationSetItem* pAnnoSet, u4 idx)
+{
+ assert(idx < pAnnoSet->size);
+ return pAnnoSet->entries[idx];
+}
+
+/* return the Nth annotation item from a DexAnnotationSetItem */
+DEX_INLINE const DexAnnotationItem* dexGetAnnotationItem(
+ const DexFile* pDexFile, const DexAnnotationSetItem* pAnnoSet, u4 idx)
+{
+ u4 offset = dexGetAnnotationOff(pAnnoSet, idx);
+ if (offset == 0) {
+ return NULL;
+ }
+ return (const DexAnnotationItem*) (pDexFile->baseAddr + offset);
+}
+
+/*
+ * Get the type descriptor character associated with a given primitive
+ * type. This returns '\0' if the type is invalid.
+ */
+char dexGetPrimitiveTypeDescriptorChar(PrimitiveType type);
+
+/*
+ * Get the type descriptor string associated with a given primitive
+ * type.
+ */
+const char* dexGetPrimitiveTypeDescriptor(PrimitiveType type);
+
+/*
+ * Get the boxed type descriptor string associated with a given
+ * primitive type. This returns NULL for an invalid type, including
+ * particularly for type "void". In the latter case, even though there
+ * is a class Void, there's no such thing as a boxed instance of it.
+ */
+const char* dexGetBoxedTypeDescriptor(PrimitiveType type);
+
+/*
+ * Get the primitive type constant from the given descriptor character.
+ * This returns PRIM_NOT (note: this is a 0) if the character is invalid
+ * as a primitive type descriptor.
+ */
+PrimitiveType dexGetPrimitiveTypeFromDescriptorChar(char descriptorChar);
+
+#endif // LIBDEX_DEXFILE_H_
diff --git a/libdex/DexInlines.cpp b/libdex/DexInlines.cpp
new file mode 100644
index 000000000..cbedb6267
--- /dev/null
+++ b/libdex/DexInlines.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Generate non-inline copies of inline functions in header files.
+ */
+
+#define _DEX_GEN_INLINES
+
+#include "DexFile.h"
+
+#include "DexCatch.h"
+#include "DexClass.h"
+#include "DexDataMap.h"
+#include "DexUtf.h"
+#include "DexOpcodes.h"
+#include "DexProto.h"
+#include "InstrUtils.h"
+#include "Leb128.h"
+#include "ZipArchive.h"
diff --git a/libdex/DexOpcodes.cpp b/libdex/DexOpcodes.cpp
new file mode 100644
index 000000000..d3db79b67
--- /dev/null
+++ b/libdex/DexOpcodes.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Table of Dalvik opcode names.
+ *
+ * IMPORTANT NOTE: The contents of this file are mostly generated
+ * automatically by the opcode-gen tool. Any edits to the generated
+ * sections will get wiped out the next time the tool is run.
+ */
+
+#include "DexOpcodes.h"
+#include <assert.h>
+
+/*
+ * Dalvik opcode names.
+ */
+static const char* gOpNames[kNumPackedOpcodes] = {
+ // BEGIN(libdex-opcode-names); GENERATED AUTOMATICALLY BY opcode-gen
+ "nop",
+ "move",
+ "move/from16",
+ "move/16",
+ "move-wide",
+ "move-wide/from16",
+ "move-wide/16",
+ "move-object",
+ "move-object/from16",
+ "move-object/16",
+ "move-result",
+ "move-result-wide",
+ "move-result-object",
+ "move-exception",
+ "return-void",
+ "return",
+ "return-wide",
+ "return-object",
+ "const/4",
+ "const/16",
+ "const",
+ "const/high16",
+ "const-wide/16",
+ "const-wide/32",
+ "const-wide",
+ "const-wide/high16",
+ "const-string",
+ "const-string/jumbo",
+ "const-class",
+ "monitor-enter",
+ "monitor-exit",
+ "check-cast",
+ "instance-of",
+ "array-length",
+ "new-instance",
+ "new-array",
+ "filled-new-array",
+ "filled-new-array/range",
+ "fill-array-data",
+ "throw",
+ "goto",
+ "goto/16",
+ "goto/32",
+ "packed-switch",
+ "sparse-switch",
+ "cmpl-float",
+ "cmpg-float",
+ "cmpl-double",
+ "cmpg-double",
+ "cmp-long",
+ "if-eq",
+ "if-ne",
+ "if-lt",
+ "if-ge",
+ "if-gt",
+ "if-le",
+ "if-eqz",
+ "if-nez",
+ "if-ltz",
+ "if-gez",
+ "if-gtz",
+ "if-lez",
+ "unused-3e",
+ "unused-3f",
+ "unused-40",
+ "unused-41",
+ "unused-42",
+ "unused-43",
+ "aget",
+ "aget-wide",
+ "aget-object",
+ "aget-boolean",
+ "aget-byte",
+ "aget-char",
+ "aget-short",
+ "aput",
+ "aput-wide",
+ "aput-object",
+ "aput-boolean",
+ "aput-byte",
+ "aput-char",
+ "aput-short",
+ "iget",
+ "iget-wide",
+ "iget-object",
+ "iget-boolean",
+ "iget-byte",
+ "iget-char",
+ "iget-short",
+ "iput",
+ "iput-wide",
+ "iput-object",
+ "iput-boolean",
+ "iput-byte",
+ "iput-char",
+ "iput-short",
+ "sget",
+ "sget-wide",
+ "sget-object",
+ "sget-boolean",
+ "sget-byte",
+ "sget-char",
+ "sget-short",
+ "sput",
+ "sput-wide",
+ "sput-object",
+ "sput-boolean",
+ "sput-byte",
+ "sput-char",
+ "sput-short",
+ "invoke-virtual",
+ "invoke-super",
+ "invoke-direct",
+ "invoke-static",
+ "invoke-interface",
+ "unused-73",
+ "invoke-virtual/range",
+ "invoke-super/range",
+ "invoke-direct/range",
+ "invoke-static/range",
+ "invoke-interface/range",
+ "unused-79",
+ "unused-7a",
+ "neg-int",
+ "not-int",
+ "neg-long",
+ "not-long",
+ "neg-float",
+ "neg-double",
+ "int-to-long",
+ "int-to-float",
+ "int-to-double",
+ "long-to-int",
+ "long-to-float",
+ "long-to-double",
+ "float-to-int",
+ "float-to-long",
+ "float-to-double",
+ "double-to-int",
+ "double-to-long",
+ "double-to-float",
+ "int-to-byte",
+ "int-to-char",
+ "int-to-short",
+ "add-int",
+ "sub-int",
+ "mul-int",
+ "div-int",
+ "rem-int",
+ "and-int",
+ "or-int",
+ "xor-int",
+ "shl-int",
+ "shr-int",
+ "ushr-int",
+ "add-long",
+ "sub-long",
+ "mul-long",
+ "div-long",
+ "rem-long",
+ "and-long",
+ "or-long",
+ "xor-long",
+ "shl-long",
+ "shr-long",
+ "ushr-long",
+ "add-float",
+ "sub-float",
+ "mul-float",
+ "div-float",
+ "rem-float",
+ "add-double",
+ "sub-double",
+ "mul-double",
+ "div-double",
+ "rem-double",
+ "add-int/2addr",
+ "sub-int/2addr",
+ "mul-int/2addr",
+ "div-int/2addr",
+ "rem-int/2addr",
+ "and-int/2addr",
+ "or-int/2addr",
+ "xor-int/2addr",
+ "shl-int/2addr",
+ "shr-int/2addr",
+ "ushr-int/2addr",
+ "add-long/2addr",
+ "sub-long/2addr",
+ "mul-long/2addr",
+ "div-long/2addr",
+ "rem-long/2addr",
+ "and-long/2addr",
+ "or-long/2addr",
+ "xor-long/2addr",
+ "shl-long/2addr",
+ "shr-long/2addr",
+ "ushr-long/2addr",
+ "add-float/2addr",
+ "sub-float/2addr",
+ "mul-float/2addr",
+ "div-float/2addr",
+ "rem-float/2addr",
+ "add-double/2addr",
+ "sub-double/2addr",
+ "mul-double/2addr",
+ "div-double/2addr",
+ "rem-double/2addr",
+ "add-int/lit16",
+ "rsub-int",
+ "mul-int/lit16",
+ "div-int/lit16",
+ "rem-int/lit16",
+ "and-int/lit16",
+ "or-int/lit16",
+ "xor-int/lit16",
+ "add-int/lit8",
+ "rsub-int/lit8",
+ "mul-int/lit8",
+ "div-int/lit8",
+ "rem-int/lit8",
+ "and-int/lit8",
+ "or-int/lit8",
+ "xor-int/lit8",
+ "shl-int/lit8",
+ "shr-int/lit8",
+ "ushr-int/lit8",
+ "+iget-volatile",
+ "+iput-volatile",
+ "+sget-volatile",
+ "+sput-volatile",
+ "+iget-object-volatile",
+ "+iget-wide-volatile",
+ "+iput-wide-volatile",
+ "+sget-wide-volatile",
+ "+sput-wide-volatile",
+ "^breakpoint",
+ "^throw-verification-error",
+ "+execute-inline",
+ "+execute-inline/range",
+ "+invoke-object-init/range",
+ "+return-void-barrier",
+ "+iget-quick",
+ "unused-f3",
+ "unused-f4",
+ "unused-f5",
+ "unused-f6",
+ "unused-f7",
+ "unused-f8",
+ "unused-f9",
+ "invoke-polymorphic",
+ "invoke-polymorphic/range",
+ "invoke-custom",
+ "invoke-custom/range",
+ "const-method-handle",
+ "const-method-type",
+ // END(libdex-opcode-names)
+};
+
+/*
+ * Return the name of an opcode.
+ */
+const char* dexGetOpcodeName(Opcode op)
+{
+ assert(op >= 0 && op < kNumPackedOpcodes);
+ return gOpNames[op];
+}
diff --git a/libdex/DexOpcodes.h b/libdex/DexOpcodes.h
new file mode 100644
index 000000000..f4dcd6b51
--- /dev/null
+++ b/libdex/DexOpcodes.h
@@ -0,0 +1,620 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik opcode information.
+ *
+ * IMPORTANT NOTE: The contents of this file are mostly generated
+ * automatically by the opcode-gen tool. Any edits to the generated
+ * sections will get wiped out the next time the tool is run.
+ *
+ * See the file opcode-gen/README.txt for information about updating
+ * opcodes and instruction formats.
+ */
+
+#ifndef LIBDEX_DEXOPCODES_H_
+#define LIBDEX_DEXOPCODES_H_
+
+#include "DexFile.h"
+
+/*
+ * kMaxOpcodeValue: the highest possible raw (unpacked) opcode value
+ *
+ * kNumPackedOpcodes: the highest possible packed opcode value of a
+ * valid Dalvik opcode, plus one
+ *
+ * TODO: Change this once the rest of the code is prepared to deal with
+ * extended opcodes.
+ */
+// BEGIN(libdex-maximum-values); GENERATED AUTOMATICALLY BY opcode-gen
+#define kMaxOpcodeValue 0xffff
+#define kNumPackedOpcodes 0x100
+// END(libdex-maximum-values); GENERATED AUTOMATICALLY BY opcode-gen
+
+/*
+ * Switch table and array data signatures are a code unit consisting
+ * of "NOP" (0x00) in the low-order byte and a non-zero identifying
+ * code in the high-order byte. (A true NOP is 0x0000.)
+ */
+#define kPackedSwitchSignature 0x0100
+#define kSparseSwitchSignature 0x0200
+#define kArrayDataSignature 0x0300
+
+/*
+ * Enumeration of all Dalvik opcodes, where the enumeration value
+ * associated with each is the corresponding packed opcode number.
+ * This is different than the opcode value from the Dalvik bytecode
+ * spec for opcode values >= 0xff; see dexOpcodeFromCodeUnit() below.
+ *
+ * A note about the "breakpoint" opcode. This instruction is special,
+ * in that it should never be seen by anything but the debug
+ * interpreter. During debugging it takes the place of an arbitrary
+ * opcode, which means operations like "tell me the opcode width so I
+ * can find the next instruction" aren't possible. (This is
+ * correctable, but probably not useful.)
+ */
+enum Opcode {
+ // BEGIN(libdex-opcode-enum); GENERATED AUTOMATICALLY BY opcode-gen
+ OP_NOP = 0x00,
+ OP_MOVE = 0x01,
+ OP_MOVE_FROM16 = 0x02,
+ OP_MOVE_16 = 0x03,
+ OP_MOVE_WIDE = 0x04,
+ OP_MOVE_WIDE_FROM16 = 0x05,
+ OP_MOVE_WIDE_16 = 0x06,
+ OP_MOVE_OBJECT = 0x07,
+ OP_MOVE_OBJECT_FROM16 = 0x08,
+ OP_MOVE_OBJECT_16 = 0x09,
+ OP_MOVE_RESULT = 0x0a,
+ OP_MOVE_RESULT_WIDE = 0x0b,
+ OP_MOVE_RESULT_OBJECT = 0x0c,
+ OP_MOVE_EXCEPTION = 0x0d,
+ OP_RETURN_VOID = 0x0e,
+ OP_RETURN = 0x0f,
+ OP_RETURN_WIDE = 0x10,
+ OP_RETURN_OBJECT = 0x11,
+ OP_CONST_4 = 0x12,
+ OP_CONST_16 = 0x13,
+ OP_CONST = 0x14,
+ OP_CONST_HIGH16 = 0x15,
+ OP_CONST_WIDE_16 = 0x16,
+ OP_CONST_WIDE_32 = 0x17,
+ OP_CONST_WIDE = 0x18,
+ OP_CONST_WIDE_HIGH16 = 0x19,
+ OP_CONST_STRING = 0x1a,
+ OP_CONST_STRING_JUMBO = 0x1b,
+ OP_CONST_CLASS = 0x1c,
+ OP_MONITOR_ENTER = 0x1d,
+ OP_MONITOR_EXIT = 0x1e,
+ OP_CHECK_CAST = 0x1f,
+ OP_INSTANCE_OF = 0x20,
+ OP_ARRAY_LENGTH = 0x21,
+ OP_NEW_INSTANCE = 0x22,
+ OP_NEW_ARRAY = 0x23,
+ OP_FILLED_NEW_ARRAY = 0x24,
+ OP_FILLED_NEW_ARRAY_RANGE = 0x25,
+ OP_FILL_ARRAY_DATA = 0x26,
+ OP_THROW = 0x27,
+ OP_GOTO = 0x28,
+ OP_GOTO_16 = 0x29,
+ OP_GOTO_32 = 0x2a,
+ OP_PACKED_SWITCH = 0x2b,
+ OP_SPARSE_SWITCH = 0x2c,
+ OP_CMPL_FLOAT = 0x2d,
+ OP_CMPG_FLOAT = 0x2e,
+ OP_CMPL_DOUBLE = 0x2f,
+ OP_CMPG_DOUBLE = 0x30,
+ OP_CMP_LONG = 0x31,
+ OP_IF_EQ = 0x32,
+ OP_IF_NE = 0x33,
+ OP_IF_LT = 0x34,
+ OP_IF_GE = 0x35,
+ OP_IF_GT = 0x36,
+ OP_IF_LE = 0x37,
+ OP_IF_EQZ = 0x38,
+ OP_IF_NEZ = 0x39,
+ OP_IF_LTZ = 0x3a,
+ OP_IF_GEZ = 0x3b,
+ OP_IF_GTZ = 0x3c,
+ OP_IF_LEZ = 0x3d,
+ OP_UNUSED_3E = 0x3e,
+ OP_UNUSED_3F = 0x3f,
+ OP_UNUSED_40 = 0x40,
+ OP_UNUSED_41 = 0x41,
+ OP_UNUSED_42 = 0x42,
+ OP_UNUSED_43 = 0x43,
+ OP_AGET = 0x44,
+ OP_AGET_WIDE = 0x45,
+ OP_AGET_OBJECT = 0x46,
+ OP_AGET_BOOLEAN = 0x47,
+ OP_AGET_BYTE = 0x48,
+ OP_AGET_CHAR = 0x49,
+ OP_AGET_SHORT = 0x4a,
+ OP_APUT = 0x4b,
+ OP_APUT_WIDE = 0x4c,
+ OP_APUT_OBJECT = 0x4d,
+ OP_APUT_BOOLEAN = 0x4e,
+ OP_APUT_BYTE = 0x4f,
+ OP_APUT_CHAR = 0x50,
+ OP_APUT_SHORT = 0x51,
+ OP_IGET = 0x52,
+ OP_IGET_WIDE = 0x53,
+ OP_IGET_OBJECT = 0x54,
+ OP_IGET_BOOLEAN = 0x55,
+ OP_IGET_BYTE = 0x56,
+ OP_IGET_CHAR = 0x57,
+ OP_IGET_SHORT = 0x58,
+ OP_IPUT = 0x59,
+ OP_IPUT_WIDE = 0x5a,
+ OP_IPUT_OBJECT = 0x5b,
+ OP_IPUT_BOOLEAN = 0x5c,
+ OP_IPUT_BYTE = 0x5d,
+ OP_IPUT_CHAR = 0x5e,
+ OP_IPUT_SHORT = 0x5f,
+ OP_SGET = 0x60,
+ OP_SGET_WIDE = 0x61,
+ OP_SGET_OBJECT = 0x62,
+ OP_SGET_BOOLEAN = 0x63,
+ OP_SGET_BYTE = 0x64,
+ OP_SGET_CHAR = 0x65,
+ OP_SGET_SHORT = 0x66,
+ OP_SPUT = 0x67,
+ OP_SPUT_WIDE = 0x68,
+ OP_SPUT_OBJECT = 0x69,
+ OP_SPUT_BOOLEAN = 0x6a,
+ OP_SPUT_BYTE = 0x6b,
+ OP_SPUT_CHAR = 0x6c,
+ OP_SPUT_SHORT = 0x6d,
+ OP_INVOKE_VIRTUAL = 0x6e,
+ OP_INVOKE_SUPER = 0x6f,
+ OP_INVOKE_DIRECT = 0x70,
+ OP_INVOKE_STATIC = 0x71,
+ OP_INVOKE_INTERFACE = 0x72,
+ OP_UNUSED_73 = 0x73,
+ OP_INVOKE_VIRTUAL_RANGE = 0x74,
+ OP_INVOKE_SUPER_RANGE = 0x75,
+ OP_INVOKE_DIRECT_RANGE = 0x76,
+ OP_INVOKE_STATIC_RANGE = 0x77,
+ OP_INVOKE_INTERFACE_RANGE = 0x78,
+ OP_UNUSED_79 = 0x79,
+ OP_UNUSED_7A = 0x7a,
+ OP_NEG_INT = 0x7b,
+ OP_NOT_INT = 0x7c,
+ OP_NEG_LONG = 0x7d,
+ OP_NOT_LONG = 0x7e,
+ OP_NEG_FLOAT = 0x7f,
+ OP_NEG_DOUBLE = 0x80,
+ OP_INT_TO_LONG = 0x81,
+ OP_INT_TO_FLOAT = 0x82,
+ OP_INT_TO_DOUBLE = 0x83,
+ OP_LONG_TO_INT = 0x84,
+ OP_LONG_TO_FLOAT = 0x85,
+ OP_LONG_TO_DOUBLE = 0x86,
+ OP_FLOAT_TO_INT = 0x87,
+ OP_FLOAT_TO_LONG = 0x88,
+ OP_FLOAT_TO_DOUBLE = 0x89,
+ OP_DOUBLE_TO_INT = 0x8a,
+ OP_DOUBLE_TO_LONG = 0x8b,
+ OP_DOUBLE_TO_FLOAT = 0x8c,
+ OP_INT_TO_BYTE = 0x8d,
+ OP_INT_TO_CHAR = 0x8e,
+ OP_INT_TO_SHORT = 0x8f,
+ OP_ADD_INT = 0x90,
+ OP_SUB_INT = 0x91,
+ OP_MUL_INT = 0x92,
+ OP_DIV_INT = 0x93,
+ OP_REM_INT = 0x94,
+ OP_AND_INT = 0x95,
+ OP_OR_INT = 0x96,
+ OP_XOR_INT = 0x97,
+ OP_SHL_INT = 0x98,
+ OP_SHR_INT = 0x99,
+ OP_USHR_INT = 0x9a,
+ OP_ADD_LONG = 0x9b,
+ OP_SUB_LONG = 0x9c,
+ OP_MUL_LONG = 0x9d,
+ OP_DIV_LONG = 0x9e,
+ OP_REM_LONG = 0x9f,
+ OP_AND_LONG = 0xa0,
+ OP_OR_LONG = 0xa1,
+ OP_XOR_LONG = 0xa2,
+ OP_SHL_LONG = 0xa3,
+ OP_SHR_LONG = 0xa4,
+ OP_USHR_LONG = 0xa5,
+ OP_ADD_FLOAT = 0xa6,
+ OP_SUB_FLOAT = 0xa7,
+ OP_MUL_FLOAT = 0xa8,
+ OP_DIV_FLOAT = 0xa9,
+ OP_REM_FLOAT = 0xaa,
+ OP_ADD_DOUBLE = 0xab,
+ OP_SUB_DOUBLE = 0xac,
+ OP_MUL_DOUBLE = 0xad,
+ OP_DIV_DOUBLE = 0xae,
+ OP_REM_DOUBLE = 0xaf,
+ OP_ADD_INT_2ADDR = 0xb0,
+ OP_SUB_INT_2ADDR = 0xb1,
+ OP_MUL_INT_2ADDR = 0xb2,
+ OP_DIV_INT_2ADDR = 0xb3,
+ OP_REM_INT_2ADDR = 0xb4,
+ OP_AND_INT_2ADDR = 0xb5,
+ OP_OR_INT_2ADDR = 0xb6,
+ OP_XOR_INT_2ADDR = 0xb7,
+ OP_SHL_INT_2ADDR = 0xb8,
+ OP_SHR_INT_2ADDR = 0xb9,
+ OP_USHR_INT_2ADDR = 0xba,
+ OP_ADD_LONG_2ADDR = 0xbb,
+ OP_SUB_LONG_2ADDR = 0xbc,
+ OP_MUL_LONG_2ADDR = 0xbd,
+ OP_DIV_LONG_2ADDR = 0xbe,
+ OP_REM_LONG_2ADDR = 0xbf,
+ OP_AND_LONG_2ADDR = 0xc0,
+ OP_OR_LONG_2ADDR = 0xc1,
+ OP_XOR_LONG_2ADDR = 0xc2,
+ OP_SHL_LONG_2ADDR = 0xc3,
+ OP_SHR_LONG_2ADDR = 0xc4,
+ OP_USHR_LONG_2ADDR = 0xc5,
+ OP_ADD_FLOAT_2ADDR = 0xc6,
+ OP_SUB_FLOAT_2ADDR = 0xc7,
+ OP_MUL_FLOAT_2ADDR = 0xc8,
+ OP_DIV_FLOAT_2ADDR = 0xc9,
+ OP_REM_FLOAT_2ADDR = 0xca,
+ OP_ADD_DOUBLE_2ADDR = 0xcb,
+ OP_SUB_DOUBLE_2ADDR = 0xcc,
+ OP_MUL_DOUBLE_2ADDR = 0xcd,
+ OP_DIV_DOUBLE_2ADDR = 0xce,
+ OP_REM_DOUBLE_2ADDR = 0xcf,
+ OP_ADD_INT_LIT16 = 0xd0,
+ OP_RSUB_INT = 0xd1,
+ OP_MUL_INT_LIT16 = 0xd2,
+ OP_DIV_INT_LIT16 = 0xd3,
+ OP_REM_INT_LIT16 = 0xd4,
+ OP_AND_INT_LIT16 = 0xd5,
+ OP_OR_INT_LIT16 = 0xd6,
+ OP_XOR_INT_LIT16 = 0xd7,
+ OP_ADD_INT_LIT8 = 0xd8,
+ OP_RSUB_INT_LIT8 = 0xd9,
+ OP_MUL_INT_LIT8 = 0xda,
+ OP_DIV_INT_LIT8 = 0xdb,
+ OP_REM_INT_LIT8 = 0xdc,
+ OP_AND_INT_LIT8 = 0xdd,
+ OP_OR_INT_LIT8 = 0xde,
+ OP_XOR_INT_LIT8 = 0xdf,
+ OP_SHL_INT_LIT8 = 0xe0,
+ OP_SHR_INT_LIT8 = 0xe1,
+ OP_USHR_INT_LIT8 = 0xe2,
+ OP_IGET_VOLATILE = 0xe3,
+ OP_IPUT_VOLATILE = 0xe4,
+ OP_SGET_VOLATILE = 0xe5,
+ OP_SPUT_VOLATILE = 0xe6,
+ OP_IGET_OBJECT_VOLATILE = 0xe7,
+ OP_IGET_WIDE_VOLATILE = 0xe8,
+ OP_IPUT_WIDE_VOLATILE = 0xe9,
+ OP_SGET_WIDE_VOLATILE = 0xea,
+ OP_SPUT_WIDE_VOLATILE = 0xeb,
+ OP_BREAKPOINT = 0xec,
+ OP_THROW_VERIFICATION_ERROR = 0xed,
+ OP_EXECUTE_INLINE = 0xee,
+ OP_EXECUTE_INLINE_RANGE = 0xef,
+ OP_INVOKE_OBJECT_INIT_RANGE = 0xf0,
+ OP_RETURN_VOID_BARRIER = 0xf1,
+ OP_IGET_QUICK = 0xf2,
+ OP_UNUSED_F3 = 0xf3,
+ OP_UNUSED_F4 = 0xf4,
+ OP_UNUSED_F5 = 0xf5,
+ OP_UNUSED_F6 = 0xf6,
+ OP_UNUSED_F7 = 0xf7,
+ OP_UNUSED_F8 = 0xf8,
+ OP_UNUSED_F9 = 0xf9,
+ OP_INVOKE_POLYMORPHIC = 0xfa,
+ OP_INVOKE_POLYMORPHIC_RANGE = 0xfb,
+ OP_INVOKE_CUSTOM = 0xfc,
+ OP_INVOKE_CUSTOM_RANGE = 0xfd,
+ OP_CONST_METHOD_HANDLE = 0xfe,
+ OP_CONST_METHOD_TYPE = 0xff,
+ // END(libdex-opcode-enum)
+};
+
+/*
+ * Macro used to generate a computed goto table for use in implementing
+ * an interpreter in C.
+ */
+#define DEFINE_GOTO_TABLE(_name) \
+ static const void* (_name)[kNumPackedOpcodes] = { \
+ /* BEGIN(libdex-goto-table); GENERATED AUTOMATICALLY BY opcode-gen */ \
+ H(OP_NOP), \
+ H(OP_MOVE), \
+ H(OP_MOVE_FROM16), \
+ H(OP_MOVE_16), \
+ H(OP_MOVE_WIDE), \
+ H(OP_MOVE_WIDE_FROM16), \
+ H(OP_MOVE_WIDE_16), \
+ H(OP_MOVE_OBJECT), \
+ H(OP_MOVE_OBJECT_FROM16), \
+ H(OP_MOVE_OBJECT_16), \
+ H(OP_MOVE_RESULT), \
+ H(OP_MOVE_RESULT_WIDE), \
+ H(OP_MOVE_RESULT_OBJECT), \
+ H(OP_MOVE_EXCEPTION), \
+ H(OP_RETURN_VOID), \
+ H(OP_RETURN), \
+ H(OP_RETURN_WIDE), \
+ H(OP_RETURN_OBJECT), \
+ H(OP_CONST_4), \
+ H(OP_CONST_16), \
+ H(OP_CONST), \
+ H(OP_CONST_HIGH16), \
+ H(OP_CONST_WIDE_16), \
+ H(OP_CONST_WIDE_32), \
+ H(OP_CONST_WIDE), \
+ H(OP_CONST_WIDE_HIGH16), \
+ H(OP_CONST_STRING), \
+ H(OP_CONST_STRING_JUMBO), \
+ H(OP_CONST_CLASS), \
+ H(OP_MONITOR_ENTER), \
+ H(OP_MONITOR_EXIT), \
+ H(OP_CHECK_CAST), \
+ H(OP_INSTANCE_OF), \
+ H(OP_ARRAY_LENGTH), \
+ H(OP_NEW_INSTANCE), \
+ H(OP_NEW_ARRAY), \
+ H(OP_FILLED_NEW_ARRAY), \
+ H(OP_FILLED_NEW_ARRAY_RANGE), \
+ H(OP_FILL_ARRAY_DATA), \
+ H(OP_THROW), \
+ H(OP_GOTO), \
+ H(OP_GOTO_16), \
+ H(OP_GOTO_32), \
+ H(OP_PACKED_SWITCH), \
+ H(OP_SPARSE_SWITCH), \
+ H(OP_CMPL_FLOAT), \
+ H(OP_CMPG_FLOAT), \
+ H(OP_CMPL_DOUBLE), \
+ H(OP_CMPG_DOUBLE), \
+ H(OP_CMP_LONG), \
+ H(OP_IF_EQ), \
+ H(OP_IF_NE), \
+ H(OP_IF_LT), \
+ H(OP_IF_GE), \
+ H(OP_IF_GT), \
+ H(OP_IF_LE), \
+ H(OP_IF_EQZ), \
+ H(OP_IF_NEZ), \
+ H(OP_IF_LTZ), \
+ H(OP_IF_GEZ), \
+ H(OP_IF_GTZ), \
+ H(OP_IF_LEZ), \
+ H(OP_UNUSED_3E), \
+ H(OP_UNUSED_3F), \
+ H(OP_UNUSED_40), \
+ H(OP_UNUSED_41), \
+ H(OP_UNUSED_42), \
+ H(OP_UNUSED_43), \
+ H(OP_AGET), \
+ H(OP_AGET_WIDE), \
+ H(OP_AGET_OBJECT), \
+ H(OP_AGET_BOOLEAN), \
+ H(OP_AGET_BYTE), \
+ H(OP_AGET_CHAR), \
+ H(OP_AGET_SHORT), \
+ H(OP_APUT), \
+ H(OP_APUT_WIDE), \
+ H(OP_APUT_OBJECT), \
+ H(OP_APUT_BOOLEAN), \
+ H(OP_APUT_BYTE), \
+ H(OP_APUT_CHAR), \
+ H(OP_APUT_SHORT), \
+ H(OP_IGET), \
+ H(OP_IGET_WIDE), \
+ H(OP_IGET_OBJECT), \
+ H(OP_IGET_BOOLEAN), \
+ H(OP_IGET_BYTE), \
+ H(OP_IGET_CHAR), \
+ H(OP_IGET_SHORT), \
+ H(OP_IPUT), \
+ H(OP_IPUT_WIDE), \
+ H(OP_IPUT_OBJECT), \
+ H(OP_IPUT_BOOLEAN), \
+ H(OP_IPUT_BYTE), \
+ H(OP_IPUT_CHAR), \
+ H(OP_IPUT_SHORT), \
+ H(OP_SGET), \
+ H(OP_SGET_WIDE), \
+ H(OP_SGET_OBJECT), \
+ H(OP_SGET_BOOLEAN), \
+ H(OP_SGET_BYTE), \
+ H(OP_SGET_CHAR), \
+ H(OP_SGET_SHORT), \
+ H(OP_SPUT), \
+ H(OP_SPUT_WIDE), \
+ H(OP_SPUT_OBJECT), \
+ H(OP_SPUT_BOOLEAN), \
+ H(OP_SPUT_BYTE), \
+ H(OP_SPUT_CHAR), \
+ H(OP_SPUT_SHORT), \
+ H(OP_INVOKE_VIRTUAL), \
+ H(OP_INVOKE_SUPER), \
+ H(OP_INVOKE_DIRECT), \
+ H(OP_INVOKE_STATIC), \
+ H(OP_INVOKE_INTERFACE), \
+ H(OP_UNUSED_73), \
+ H(OP_INVOKE_VIRTUAL_RANGE), \
+ H(OP_INVOKE_SUPER_RANGE), \
+ H(OP_INVOKE_DIRECT_RANGE), \
+ H(OP_INVOKE_STATIC_RANGE), \
+ H(OP_INVOKE_INTERFACE_RANGE), \
+ H(OP_UNUSED_79), \
+ H(OP_UNUSED_7A), \
+ H(OP_NEG_INT), \
+ H(OP_NOT_INT), \
+ H(OP_NEG_LONG), \
+ H(OP_NOT_LONG), \
+ H(OP_NEG_FLOAT), \
+ H(OP_NEG_DOUBLE), \
+ H(OP_INT_TO_LONG), \
+ H(OP_INT_TO_FLOAT), \
+ H(OP_INT_TO_DOUBLE), \
+ H(OP_LONG_TO_INT), \
+ H(OP_LONG_TO_FLOAT), \
+ H(OP_LONG_TO_DOUBLE), \
+ H(OP_FLOAT_TO_INT), \
+ H(OP_FLOAT_TO_LONG), \
+ H(OP_FLOAT_TO_DOUBLE), \
+ H(OP_DOUBLE_TO_INT), \
+ H(OP_DOUBLE_TO_LONG), \
+ H(OP_DOUBLE_TO_FLOAT), \
+ H(OP_INT_TO_BYTE), \
+ H(OP_INT_TO_CHAR), \
+ H(OP_INT_TO_SHORT), \
+ H(OP_ADD_INT), \
+ H(OP_SUB_INT), \
+ H(OP_MUL_INT), \
+ H(OP_DIV_INT), \
+ H(OP_REM_INT), \
+ H(OP_AND_INT), \
+ H(OP_OR_INT), \
+ H(OP_XOR_INT), \
+ H(OP_SHL_INT), \
+ H(OP_SHR_INT), \
+ H(OP_USHR_INT), \
+ H(OP_ADD_LONG), \
+ H(OP_SUB_LONG), \
+ H(OP_MUL_LONG), \
+ H(OP_DIV_LONG), \
+ H(OP_REM_LONG), \
+ H(OP_AND_LONG), \
+ H(OP_OR_LONG), \
+ H(OP_XOR_LONG), \
+ H(OP_SHL_LONG), \
+ H(OP_SHR_LONG), \
+ H(OP_USHR_LONG), \
+ H(OP_ADD_FLOAT), \
+ H(OP_SUB_FLOAT), \
+ H(OP_MUL_FLOAT), \
+ H(OP_DIV_FLOAT), \
+ H(OP_REM_FLOAT), \
+ H(OP_ADD_DOUBLE), \
+ H(OP_SUB_DOUBLE), \
+ H(OP_MUL_DOUBLE), \
+ H(OP_DIV_DOUBLE), \
+ H(OP_REM_DOUBLE), \
+ H(OP_ADD_INT_2ADDR), \
+ H(OP_SUB_INT_2ADDR), \
+ H(OP_MUL_INT_2ADDR), \
+ H(OP_DIV_INT_2ADDR), \
+ H(OP_REM_INT_2ADDR), \
+ H(OP_AND_INT_2ADDR), \
+ H(OP_OR_INT_2ADDR), \
+ H(OP_XOR_INT_2ADDR), \
+ H(OP_SHL_INT_2ADDR), \
+ H(OP_SHR_INT_2ADDR), \
+ H(OP_USHR_INT_2ADDR), \
+ H(OP_ADD_LONG_2ADDR), \
+ H(OP_SUB_LONG_2ADDR), \
+ H(OP_MUL_LONG_2ADDR), \
+ H(OP_DIV_LONG_2ADDR), \
+ H(OP_REM_LONG_2ADDR), \
+ H(OP_AND_LONG_2ADDR), \
+ H(OP_OR_LONG_2ADDR), \
+ H(OP_XOR_LONG_2ADDR), \
+ H(OP_SHL_LONG_2ADDR), \
+ H(OP_SHR_LONG_2ADDR), \
+ H(OP_USHR_LONG_2ADDR), \
+ H(OP_ADD_FLOAT_2ADDR), \
+ H(OP_SUB_FLOAT_2ADDR), \
+ H(OP_MUL_FLOAT_2ADDR), \
+ H(OP_DIV_FLOAT_2ADDR), \
+ H(OP_REM_FLOAT_2ADDR), \
+ H(OP_ADD_DOUBLE_2ADDR), \
+ H(OP_SUB_DOUBLE_2ADDR), \
+ H(OP_MUL_DOUBLE_2ADDR), \
+ H(OP_DIV_DOUBLE_2ADDR), \
+ H(OP_REM_DOUBLE_2ADDR), \
+ H(OP_ADD_INT_LIT16), \
+ H(OP_RSUB_INT), \
+ H(OP_MUL_INT_LIT16), \
+ H(OP_DIV_INT_LIT16), \
+ H(OP_REM_INT_LIT16), \
+ H(OP_AND_INT_LIT16), \
+ H(OP_OR_INT_LIT16), \
+ H(OP_XOR_INT_LIT16), \
+ H(OP_ADD_INT_LIT8), \
+ H(OP_RSUB_INT_LIT8), \
+ H(OP_MUL_INT_LIT8), \
+ H(OP_DIV_INT_LIT8), \
+ H(OP_REM_INT_LIT8), \
+ H(OP_AND_INT_LIT8), \
+ H(OP_OR_INT_LIT8), \
+ H(OP_XOR_INT_LIT8), \
+ H(OP_SHL_INT_LIT8), \
+ H(OP_SHR_INT_LIT8), \
+ H(OP_USHR_INT_LIT8), \
+ H(OP_IGET_VOLATILE), \
+ H(OP_IPUT_VOLATILE), \
+ H(OP_SGET_VOLATILE), \
+ H(OP_SPUT_VOLATILE), \
+ H(OP_IGET_OBJECT_VOLATILE), \
+ H(OP_IGET_WIDE_VOLATILE), \
+ H(OP_IPUT_WIDE_VOLATILE), \
+ H(OP_SGET_WIDE_VOLATILE), \
+ H(OP_SPUT_WIDE_VOLATILE), \
+ H(OP_BREAKPOINT), \
+ H(OP_THROW_VERIFICATION_ERROR), \
+ H(OP_EXECUTE_INLINE), \
+ H(OP_EXECUTE_INLINE_RANGE), \
+ H(OP_INVOKE_OBJECT_INIT_RANGE), \
+ H(OP_RETURN_VOID_BARRIER), \
+ H(OP_IGET_QUICK), \
+ H(OP_UNUSED_F3), \
+ H(OP_UNUSED_F4), \
+ H(OP_UNUSED_F5), \
+ H(OP_UNUSED_F6), \
+ H(OP_UNUSED_F7), \
+ H(OP_UNUSED_F8), \
+ H(OP_UNUSED_F9), \
+ H(OP_INVOKE_POLYMORPHIC), \
+ H(OP_INVOKE_POLYMORPHIC_RANGE), \
+ H(OP_INVOKE_CUSTOM), \
+ H(OP_INVOKE_CUSTOM_RANGE), \
+ H(OP_CONST_METHOD_HANDLE), \
+ H(OP_CONST_METHOD_TYPE), \
+ /* END(libdex-goto-table) */ \
+ };
+
+/*
+ * Return the Opcode for a given raw opcode code unit (which may
+ * include data payload). The packed index is a zero-based index which
+ * can be used to point into various opcode-related tables. The Dalvik
+ * opcode space is inherently sparse, in that the opcode unit is 16
+ * bits wide, but for most opcodes, eight of those bits are for data.
+ */
+DEX_INLINE Opcode dexOpcodeFromCodeUnit(u2 codeUnit) {
+ /*
+ * This will want to become table-driven should the opcode layout
+ * get more complicated.
+ *
+ * Note: This has to match the corresponding code in opcode-gen, so
+ * that data tables get generated in a consistent way.
+ */
+ int lowByte = codeUnit & 0xff;
+ return (Opcode) lowByte;
+}
+
+/*
+ * Return the name of an opcode.
+ */
+const char* dexGetOpcodeName(Opcode op);
+
+#endif // LIBDEX_DEXOPCODES_H_
diff --git a/libdex/DexOptData.cpp b/libdex/DexOptData.cpp
new file mode 100644
index 000000000..109e851f9
--- /dev/null
+++ b/libdex/DexOptData.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to parse and manipulate the additional data tables added
+ * to optimized .dex files.
+ */
+
+#include <zlib.h>
+
+#include "DexOptData.h"
+
+/*
+ * Check to see if a given data pointer is a valid double-word-aligned
+ * pointer into the given memory range (from start inclusive to end
+ * exclusive). Returns true if valid.
+ */
+static bool isValidPointer(const void* ptr, const void* start, const void* end)
+{
+ return (ptr >= start) && (ptr < end) && (((uintptr_t) ptr & 7) == 0);
+}
+
+/* (documented in header file) */
+u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader)
+{
+ const u1* start = (const u1*) pOptHeader + pOptHeader->depsOffset;
+ const u1* end = (const u1*) pOptHeader +
+ pOptHeader->optOffset + pOptHeader->optLength;
+
+ uLong adler = adler32(0L, Z_NULL, 0);
+
+ return (u4) adler32(adler, start, end - start);
+}
+
+/* (documented in header file) */
+bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile)
+{
+ const void* pOptStart = data + pDexFile->pOptHeader->optOffset;
+ const void* pOptEnd = data + length;
+ const u4* pOpt = (const u4*) pOptStart;
+ u4 optLength = (const u1*) pOptEnd - (const u1*) pOptStart;
+
+ /*
+ * Make sure the opt data start is in range and aligned. This may
+ * seem like a superfluous check, but (a) if the file got
+ * truncated, it might turn out that pOpt >= pOptEnd; and (b)
+ * if the opt data header got corrupted, pOpt might not be
+ * properly aligned. This test will catch both of these cases.
+ */
+ if (!isValidPointer(pOpt, pOptStart, pOptEnd)) {
+ ALOGE("Bogus opt data start pointer");
+ return false;
+ }
+
+ /* Make sure that the opt data length is a whole number of words. */
+ if ((optLength & 3) != 0) {
+ ALOGE("Unaligned opt data area end");
+ return false;
+ }
+
+ /*
+ * Make sure that the opt data area is large enough to have at least
+ * one chunk header.
+ */
+ if (optLength < 8) {
+ ALOGE("Undersized opt data area (%u)", optLength);
+ return false;
+ }
+
+ /* Process chunks until we see the end marker. */
+ while (*pOpt != kDexChunkEnd) {
+ if (!isValidPointer(pOpt + 2, pOptStart, pOptEnd)) {
+ const u4 offset = ((const u1*) pOpt) - data;
+ ALOGE("Bogus opt data content pointer at offset %u", offset);
+ return false;
+ }
+
+ u4 size = *(pOpt + 1);
+ const u1* pOptData = (const u1*) (pOpt + 2);
+
+ /*
+ * The rounded size is 64-bit aligned and includes +8 for the
+ * type/size header (which was extracted immediately above).
+ */
+ u4 roundedSize = (size + 8 + 7) & ~7;
+ const u4* pNextOpt = pOpt + (roundedSize / sizeof(u4));
+
+ if (!isValidPointer(pNextOpt, pOptStart, pOptEnd)) {
+ const u4 offset = ((const u1*) pOpt) - data;
+ ALOGE("Opt data area problem for chunk of size %u at offset %u", size, offset);
+ return false;
+ }
+
+ switch (*pOpt) {
+ case kDexChunkClassLookup:
+ pDexFile->pClassLookup = (const DexClassLookup*) pOptData;
+ break;
+ case kDexChunkRegisterMaps:
+ ALOGV("+++ found register maps, size=%u", size);
+ pDexFile->pRegisterMapPool = pOptData;
+ break;
+ default:
+ ALOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in opt data area",
+ *pOpt,
+ (char) ((*pOpt) >> 24), (char) ((*pOpt) >> 16),
+ (char) ((*pOpt) >> 8), (char) (*pOpt),
+ size);
+ break;
+ }
+
+ pOpt = pNextOpt;
+ }
+
+ return true;
+}
diff --git a/libdex/DexOptData.h b/libdex/DexOptData.h
new file mode 100644
index 000000000..69eda0142
--- /dev/null
+++ b/libdex/DexOptData.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions to parse and manipulate the additional data tables added
+ * to optimized .dex files.
+ */
+
+#ifndef _LIBDEX_DEXOPTDATA
+#define _LIBDEX_DEXOPTDATA
+
+#include "libdex/DexFile.h"
+
+/*
+ * Parse the optimized data tables in the given dex file.
+ *
+ * @param data pointer to the start of the entire dex file
+ * @param length length of the entire dex file, in bytes
+ * @param pDexFile pointer to the associated dex file structure
+ */
+bool dexParseOptData(const u1* data, size_t length, DexFile* pDexFile);
+
+/*
+ * Compute the checksum of the optimized data tables pointed at by the given
+ * header.
+ */
+u4 dexComputeOptChecksum(const DexOptHeader* pOptHeader);
+
+#endif /* def _LIBDEX_DEXOPTDATA */
diff --git a/libdex/DexProto.cpp b/libdex/DexProto.cpp
new file mode 100644
index 000000000..06c59b37d
--- /dev/null
+++ b/libdex/DexProto.cpp
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with method prototypes
+ */
+
+#include "DexProto.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * ===========================================================================
+ * String Cache
+ * ===========================================================================
+ */
+
+/*
+ * Make sure that the given cache can hold a string of the given length,
+ * including the final '\0' byte.
+ */
+void dexStringCacheAlloc(DexStringCache* pCache, size_t length) {
+ if (pCache->allocatedSize != 0) {
+ if (pCache->allocatedSize >= length) {
+ return;
+ }
+ free((void*) pCache->value);
+ }
+
+ if (length <= sizeof(pCache->buffer)) {
+ pCache->value = pCache->buffer;
+ pCache->allocatedSize = 0;
+ } else {
+ pCache->value = (char*) malloc(length);
+ pCache->allocatedSize = length;
+ }
+}
+
+/*
+ * Initialize the given DexStringCache. Use this function before passing
+ * one into any other function.
+ */
+void dexStringCacheInit(DexStringCache* pCache) {
+ pCache->value = pCache->buffer;
+ pCache->allocatedSize = 0;
+ pCache->buffer[0] = '\0';
+}
+
+/*
+ * Release the allocated contents of the given DexStringCache, if any.
+ * Use this function after your last use of a DexStringCache.
+ */
+void dexStringCacheRelease(DexStringCache* pCache) {
+ if (pCache->allocatedSize != 0) {
+ free((void*) pCache->value);
+ pCache->value = pCache->buffer;
+ pCache->allocatedSize = 0;
+ }
+}
+
+/*
+ * If the given DexStringCache doesn't already point at the given value,
+ * make a copy of it into the cache. This always returns a writable
+ * pointer to the contents (whether or not a copy had to be made). This
+ * function is intended to be used after making a call that at least
+ * sometimes doesn't populate a DexStringCache.
+ */
+char* dexStringCacheEnsureCopy(DexStringCache* pCache, const char* value) {
+ if (value != pCache->value) {
+ size_t length = strlen(value) + 1;
+ dexStringCacheAlloc(pCache, length);
+ memcpy(pCache->value, value, length);
+ }
+
+ return pCache->value;
+}
+
+/*
+ * Abandon the given DexStringCache, and return a writable copy of the
+ * given value (reusing the string cache's allocation if possible).
+ * The return value must be free()d by the caller. Use this instead of
+ * dexStringCacheRelease() if you want the buffer to survive past the
+ * scope of the DexStringCache.
+ */
+char* dexStringCacheAbandon(DexStringCache* pCache, const char* value) {
+ if ((value == pCache->value) && (pCache->allocatedSize != 0)) {
+ char* result = pCache->value;
+ pCache->allocatedSize = 0;
+ pCache->value = pCache->buffer;
+ return result;
+ } else {
+ return strdup(value);
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * Method Prototypes
+ * ===========================================================================
+ */
+
+/*
+ * Return the DexProtoId from the given DexProto. The DexProto must
+ * actually refer to a DexProtoId.
+ */
+static inline const DexProtoId* getProtoId(const DexProto* pProto) {
+ return dexGetProtoId(pProto->dexFile, pProto->protoIdx);
+}
+
+/* (documented in header file) */
+const char* dexProtoGetShorty(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+
+ return dexStringById(pProto->dexFile, protoId->shortyIdx);
+}
+
+/* (documented in header file) */
+const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
+ DexStringCache* pCache) {
+ const DexFile* dexFile = pProto->dexFile;
+ const DexProtoId* protoId = getProtoId(pProto);
+ const DexTypeList* typeList = dexGetProtoParameters(dexFile, protoId);
+ size_t length = 3; // parens and terminating '\0'
+ u4 paramCount = (typeList == NULL) ? 0 : typeList->size;
+ u4 i;
+
+ for (i = 0; i < paramCount; i++) {
+ u4 idx = dexTypeListGetIdx(typeList, i);
+ length += strlen(dexStringByTypeIdx(dexFile, idx));
+ }
+
+ length += strlen(dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
+
+ dexStringCacheAlloc(pCache, length);
+
+ char *at = (char*) pCache->value;
+ *(at++) = '(';
+
+ for (i = 0; i < paramCount; i++) {
+ u4 idx = dexTypeListGetIdx(typeList, i);
+ const char* desc = dexStringByTypeIdx(dexFile, idx);
+ strcpy(at, desc);
+ at += strlen(desc);
+ }
+
+ *(at++) = ')';
+
+ strcpy(at, dexStringByTypeIdx(dexFile, protoId->returnTypeIdx));
+ return pCache->value;
+}
+
+/* (documented in header file) */
+char* dexProtoCopyMethodDescriptor(const DexProto* pProto) {
+ DexStringCache cache;
+
+ dexStringCacheInit(&cache);
+ return dexStringCacheAbandon(&cache,
+ dexProtoGetMethodDescriptor(pProto, &cache));
+}
+
+/* (documented in header file) */
+const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
+ DexStringCache* pCache) {
+ DexParameterIterator iterator;
+ size_t length = 1; /* +1 for the terminating '\0' */
+
+ dexParameterIteratorInit(&iterator, pProto);
+
+ for (;;) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+ if (descriptor == NULL) {
+ break;
+ }
+
+ length += strlen(descriptor);
+ }
+
+ dexParameterIteratorInit(&iterator, pProto);
+
+ dexStringCacheAlloc(pCache, length);
+ char *at = (char*) pCache->value;
+
+ for (;;) {
+ const char* descriptor = dexParameterIteratorNextDescriptor(&iterator);
+ if (descriptor == NULL) {
+ break;
+ }
+
+ strcpy(at, descriptor);
+ at += strlen(descriptor);
+ }
+
+ return pCache->value;
+}
+
+/* (documented in header file) */
+const char* dexProtoGetReturnType(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+ return dexStringByTypeIdx(pProto->dexFile, protoId->returnTypeIdx);
+}
+
+/* (documented in header file) */
+size_t dexProtoGetParameterCount(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+ const DexTypeList* typeList =
+ dexGetProtoParameters(pProto->dexFile, protoId);
+ return (typeList == NULL) ? 0 : typeList->size;
+}
+
+/* (documented in header file) */
+int dexProtoComputeArgsSize(const DexProto* pProto) {
+ const char* shorty = dexProtoGetShorty(pProto);
+ int count = 0;
+
+ /* Skip the return type. */
+ shorty++;
+
+ for (;;) {
+ switch (*(shorty++)) {
+ case '\0': {
+ return count;
+ }
+ case 'D':
+ case 'J': {
+ count += 2;
+ break;
+ }
+ default: {
+ count++;
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * Common implementation for dexProtoCompare() and dexProtoCompareParameters().
+ */
+static int protoCompare(const DexProto* pProto1, const DexProto* pProto2,
+ bool compareReturnType) {
+
+ if (pProto1 == pProto2) {
+ // Easy out.
+ return 0;
+ } else {
+ const DexFile* dexFile1 = pProto1->dexFile;
+ const DexProtoId* protoId1 = getProtoId(pProto1);
+ const DexTypeList* typeList1 =
+ dexGetProtoParameters(dexFile1, protoId1);
+ int paramCount1 = (typeList1 == NULL) ? 0 : typeList1->size;
+
+ const DexFile* dexFile2 = pProto2->dexFile;
+ const DexProtoId* protoId2 = getProtoId(pProto2);
+ const DexTypeList* typeList2 =
+ dexGetProtoParameters(dexFile2, protoId2);
+ int paramCount2 = (typeList2 == NULL) ? 0 : typeList2->size;
+
+ if (protoId1 == protoId2) {
+ // Another easy out.
+ return 0;
+ }
+
+ // Compare return types.
+
+ if (compareReturnType) {
+ int result =
+ strcmp(dexStringByTypeIdx(dexFile1, protoId1->returnTypeIdx),
+ dexStringByTypeIdx(dexFile2, protoId2->returnTypeIdx));
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ // Compare parameters.
+
+ int minParam = (paramCount1 > paramCount2) ? paramCount2 : paramCount1;
+ int i;
+
+ for (i = 0; i < minParam; i++) {
+ u4 idx1 = dexTypeListGetIdx(typeList1, i);
+ u4 idx2 = dexTypeListGetIdx(typeList2, i);
+ int result =
+ strcmp(dexStringByTypeIdx(dexFile1, idx1),
+ dexStringByTypeIdx(dexFile2, idx2));
+
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (paramCount1 < paramCount2) {
+ return -1;
+ } else if (paramCount1 > paramCount2) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+/* (documented in header file) */
+int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2) {
+ return protoCompare(pProto1, pProto2, true);
+}
+
+/* (documented in header file) */
+int dexProtoCompareParameters(const DexProto* pProto1, const DexProto* pProto2){
+ return protoCompare(pProto1, pProto2, false);
+}
+
+
+/*
+ * Helper for dexProtoCompareToDescriptor(), which gets the return type
+ * descriptor from a method descriptor string.
+ */
+static const char* methodDescriptorReturnType(const char* descriptor) {
+ const char* result = strchr(descriptor, ')');
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ // The return type is the character just past the ')'.
+ return result + 1;
+}
+
+/*
+ * Helper for dexProtoCompareToDescriptor(), which indicates the end
+ * of an embedded argument type descriptor, which is also the
+ * beginning of the next argument type descriptor. Since this is for
+ * argument types, it doesn't accept 'V' as a valid type descriptor.
+ */
+static const char* methodDescriptorNextType(const char* descriptor) {
+ // Skip any array references.
+
+ while (*descriptor == '[') {
+ descriptor++;
+ }
+
+ switch (*descriptor) {
+ case 'B': case 'C': case 'D': case 'F':
+ case 'I': case 'J': case 'S': case 'Z': {
+ return descriptor + 1;
+ }
+ case 'L': {
+ const char* result = strchr(descriptor + 1, ';');
+ if (result != NULL) {
+ // The type ends just past the ';'.
+ return result + 1;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Common implementation for dexProtoCompareToDescriptor() and
+ * dexProtoCompareToParameterDescriptors(). The descriptor argument
+ * can be either a full method descriptor (with parens and a return
+ * type) or an unadorned concatenation of types (e.g. a list of
+ * argument types).
+ */
+static int protoCompareToParameterDescriptors(const DexProto* proto,
+ const char* descriptor, bool expectParens) {
+ char expectedEndChar = expectParens ? ')' : '\0';
+ DexParameterIterator iterator;
+ dexParameterIteratorInit(&iterator, proto);
+
+ if (expectParens) {
+ // Skip the '('.
+ assert (*descriptor == '(');
+ descriptor++;
+ }
+
+ for (;;) {
+ const char* protoDesc = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (*descriptor == expectedEndChar) {
+ // It's the end of the descriptor string.
+ if (protoDesc == NULL) {
+ // It's also the end of the prototype's arguments.
+ return 0;
+ } else {
+ // The prototype still has more arguments.
+ return 1;
+ }
+ }
+
+ if (protoDesc == NULL) {
+ /*
+ * The prototype doesn't have arguments left, but the
+ * descriptor string does.
+ */
+ return -1;
+ }
+
+ // Both prototype and descriptor have arguments. Compare them.
+
+ const char* nextDesc = methodDescriptorNextType(descriptor);
+ assert(nextDesc != NULL);
+
+ for (;;) {
+ char c1 = *(protoDesc++);
+ char c2 = (descriptor < nextDesc) ? *(descriptor++) : '\0';
+
+ if (c1 < c2) {
+ // This includes the case where the proto is shorter.
+ return -1;
+ } else if (c1 > c2) {
+ // This includes the case where the desc is shorter.
+ return 1;
+ } else if (c1 == '\0') {
+ // The two types are equal in length. (c2 necessarily == '\0'.)
+ break;
+ }
+ }
+
+ /*
+ * If we made it here, the two arguments matched, and
+ * descriptor == nextDesc.
+ */
+ }
+}
+
+/* (documented in header file) */
+int dexProtoCompareToDescriptor(const DexProto* proto,
+ const char* descriptor) {
+ // First compare the return types.
+
+ const char *returnType = methodDescriptorReturnType(descriptor);
+ assert(returnType != NULL);
+
+ int result = strcmp(dexProtoGetReturnType(proto), returnType);
+
+ if (result != 0) {
+ return result;
+ }
+
+ // The return types match, so we have to check arguments.
+ return protoCompareToParameterDescriptors(proto, descriptor, true);
+}
+
+/* (documented in header file) */
+int dexProtoCompareToParameterDescriptors(const DexProto* proto,
+ const char* descriptors) {
+ return protoCompareToParameterDescriptors(proto, descriptors, false);
+}
+
+
+
+
+
+
+/*
+ * ===========================================================================
+ * Parameter Iterators
+ * ===========================================================================
+ */
+
+/*
+ * Initialize the given DexParameterIterator to be at the start of the
+ * parameters of the given prototype.
+ */
+void dexParameterIteratorInit(DexParameterIterator* pIterator,
+ const DexProto* pProto) {
+ pIterator->proto = pProto;
+ pIterator->cursor = 0;
+
+ pIterator->parameters =
+ dexGetProtoParameters(pProto->dexFile, getProtoId(pProto));
+ pIterator->parameterCount = (pIterator->parameters == NULL) ? 0
+ : pIterator->parameters->size;
+}
+
+/*
+ * Get the type_id index for the next parameter, if any. This returns
+ * kDexNoIndex if the last parameter has already been consumed.
+ */
+u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator) {
+ int cursor = pIterator->cursor;
+ int parameterCount = pIterator->parameterCount;
+
+ if (cursor >= parameterCount) {
+ // The iteration is complete.
+ return kDexNoIndex;
+ } else {
+ u4 idx = dexTypeListGetIdx(pIterator->parameters, cursor);
+ pIterator->cursor++;
+ return idx;
+ }
+}
+
+/*
+ * Get the type descriptor for the next parameter, if any. This returns
+ * NULL if the last parameter has already been consumed.
+ */
+const char* dexParameterIteratorNextDescriptor(
+ DexParameterIterator* pIterator) {
+ u4 idx = dexParameterIteratorNextIndex(pIterator);
+
+ if (idx == kDexNoIndex) {
+ return NULL;
+ }
+
+ return dexStringByTypeIdx(pIterator->proto->dexFile, idx);
+}
diff --git a/libdex/DexProto.h b/libdex/DexProto.h
new file mode 100644
index 000000000..dccae6c04
--- /dev/null
+++ b/libdex/DexProto.h
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for dealing with method prototypes
+ */
+
+#ifndef LIBDEX_DEXPROTO_H_
+#define LIBDEX_DEXPROTO_H_
+
+#include "DexFile.h"
+
+/*
+ * Single-thread single-string cache. This structure holds a pointer to
+ * a string which is semi-automatically manipulated by some of the
+ * method prototype functions. Functions which use in this struct
+ * generally return a string that is valid until the next
+ * time the same DexStringCache is used.
+ */
+struct DexStringCache {
+ char* value; /* the latest value */
+ size_t allocatedSize; /* size of the allocated buffer, if allocated */
+ char buffer[120]; /* buffer used to hold small-enough results */
+};
+
+/*
+ * Make sure that the given cache can hold a string of the given length,
+ * including the final '\0' byte.
+ */
+void dexStringCacheAlloc(DexStringCache* pCache, size_t length);
+
+/*
+ * Initialize the given DexStringCache. Use this function before passing
+ * one into any other function.
+ */
+void dexStringCacheInit(DexStringCache* pCache);
+
+/*
+ * Release the allocated contents of the given DexStringCache, if any.
+ * Use this function after your last use of a DexStringCache.
+ */
+void dexStringCacheRelease(DexStringCache* pCache);
+
+/*
+ * If the given DexStringCache doesn't already point at the given value,
+ * make a copy of it into the cache. This always returns a writable
+ * pointer to the contents (whether or not a copy had to be made). This
+ * function is intended to be used after making a call that at least
+ * sometimes doesn't populate a DexStringCache.
+ */
+char* dexStringCacheEnsureCopy(DexStringCache* pCache, const char* value);
+
+/*
+ * Abandon the given DexStringCache, and return a writable copy of the
+ * given value (reusing the string cache's allocation if possible).
+ * The return value must be free()d by the caller. Use this instead of
+ * dexStringCacheRelease() if you want the buffer to survive past the
+ * scope of the DexStringCache.
+ */
+char* dexStringCacheAbandon(DexStringCache* pCache, const char* value);
+
+/*
+ * Method prototype structure, which refers to a protoIdx in a
+ * particular DexFile.
+ */
+struct DexProto {
+ const DexFile* dexFile; /* file the idx refers to */
+ u4 protoIdx; /* index into proto_ids table of dexFile */
+};
+
+/*
+ * Set the given DexProto to refer to the prototype of the given MethodId.
+ */
+DEX_INLINE void dexProtoSetFromMethodId(DexProto* pProto,
+ const DexFile* pDexFile, const DexMethodId* pMethodId)
+{
+ pProto->dexFile = pDexFile;
+ pProto->protoIdx = pMethodId->protoIdx;
+}
+
+/*
+ * Get the short-form method descriptor for the given prototype. The
+ * prototype must be protoIdx-based.
+ */
+const char* dexProtoGetShorty(const DexProto* pProto);
+
+/*
+ * Get the full method descriptor for the given prototype.
+ */
+const char* dexProtoGetMethodDescriptor(const DexProto* pProto,
+ DexStringCache* pCache);
+
+/*
+ * Get a copy of the descriptor string associated with the given prototype.
+ * The returned pointer must be free()ed by the caller.
+ */
+char* dexProtoCopyMethodDescriptor(const DexProto* pProto);
+
+/*
+ * Get the parameter descriptors for the given prototype. This is the
+ * concatenation of all the descriptors for all the parameters, in
+ * order, with no other adornment.
+ */
+const char* dexProtoGetParameterDescriptors(const DexProto* pProto,
+ DexStringCache* pCache);
+
+/*
+ * Return the utf-8 encoded descriptor string from the proto of a MethodId.
+ */
+DEX_INLINE const char* dexGetDescriptorFromMethodId(const DexFile* pDexFile,
+ const DexMethodId* pMethodId, DexStringCache* pCache)
+{
+ DexProto proto;
+
+ dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+ return dexProtoGetMethodDescriptor(&proto, pCache);
+}
+
+/*
+ * Get a copy of the utf-8 encoded method descriptor string from the
+ * proto of a MethodId. The returned pointer must be free()ed by the
+ * caller.
+ */
+DEX_INLINE char* dexCopyDescriptorFromMethodId(const DexFile* pDexFile,
+ const DexMethodId* pMethodId)
+{
+ DexProto proto;
+
+ dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
+ return dexProtoCopyMethodDescriptor(&proto);
+}
+
+/*
+ * Get the type descriptor for the return type of the given prototype.
+ */
+const char* dexProtoGetReturnType(const DexProto* pProto);
+
+/*
+ * Get the parameter count of the given prototype.
+ */
+size_t dexProtoGetParameterCount(const DexProto* pProto);
+
+/*
+ * Compute the number of parameter words (u4 units) required by the
+ * given prototype. For example, if the method takes (int, long) and
+ * returns double, this would return 3 (one for the int, two for the
+ * long, and the return type isn't relevant).
+ */
+int dexProtoComputeArgsSize(const DexProto* pProto);
+
+/*
+ * Compare the two prototypes. The two prototypes are compared
+ * with the return type as the major order, then the first arguments,
+ * then second, etc. If two prototypes are identical except that one
+ * has extra arguments, then the shorter argument is considered the
+ * earlier one in sort order (similar to strcmp()).
+ */
+int dexProtoCompare(const DexProto* pProto1, const DexProto* pProto2);
+
+/*
+ * Compare the two prototypes, ignoring return type. The two
+ * prototypes are compared with the first argument as the major order,
+ * then second, etc. If two prototypes are identical except that one
+ * has extra arguments, then the shorter argument is considered the
+ * earlier one in sort order (similar to strcmp()).
+ */
+int dexProtoCompareParameters(const DexProto* pProto1,
+ const DexProto* pProto2);
+
+/*
+ * Compare a prototype and a string method descriptor. The comparison
+ * is done as if the descriptor were converted to a prototype and compared
+ * with dexProtoCompare().
+ */
+int dexProtoCompareToDescriptor(const DexProto* proto, const char* descriptor);
+
+/*
+ * Compare a prototype and a concatenation of type descriptors. The
+ * comparison is done as if the descriptors were converted to a
+ * prototype and compared with dexProtoCompareParameters().
+ */
+int dexProtoCompareToParameterDescriptors(const DexProto* proto,
+ const char* descriptors);
+
+/*
+ * Single-thread prototype parameter iterator. This structure holds a
+ * pointer to a prototype and its parts, along with a cursor.
+ */
+struct DexParameterIterator {
+ const DexProto* proto;
+ const DexTypeList* parameters;
+ int parameterCount;
+ int cursor;
+};
+
+/*
+ * Initialize the given DexParameterIterator to be at the start of the
+ * parameters of the given prototype.
+ */
+void dexParameterIteratorInit(DexParameterIterator* pIterator,
+ const DexProto* pProto);
+
+/*
+ * Get the type_id index for the next parameter, if any. This returns
+ * kDexNoIndex if the last parameter has already been consumed.
+ */
+u4 dexParameterIteratorNextIndex(DexParameterIterator* pIterator);
+
+/*
+ * Get the type descriptor for the next parameter, if any. This returns
+ * NULL if the last parameter has already been consumed.
+ */
+const char* dexParameterIteratorNextDescriptor(
+ DexParameterIterator* pIterator);
+
+#endif // LIBDEX_DEXPROTO_H_
diff --git a/libdex/DexSwapVerify.cpp b/libdex/DexSwapVerify.cpp
new file mode 100644
index 000000000..8ffc74371
--- /dev/null
+++ b/libdex/DexSwapVerify.cpp
@@ -0,0 +1,3070 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Byte-swapping and verification of dex files.
+ */
+
+#include "DexFile.h"
+#include "DexClass.h"
+#include "DexDataMap.h"
+#include "DexProto.h"
+#include "DexUtf.h"
+#include "Leb128.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#define SWAP2(_value) (_value)
+#define SWAP4(_value) (_value)
+#define SWAP8(_value) (_value)
+
+#define SWAP_FIELD2(_field) (_field) = SWAP2(_field)
+#define SWAP_FIELD4(_field) (_field) = SWAP4(_field)
+#define SWAP_FIELD8(_field) (_field) = SWAP8(_field)
+
+/*
+ * Some information we pass around to help verify values.
+ */
+struct CheckState {
+ const DexHeader* pHeader;
+ const u1* fileStart;
+ const u1* fileEnd; // points to fileStart + fileLen
+ u4 fileLen;
+ DexDataMap* pDataMap; // set after map verification
+ const DexFile* pDexFile; // set after intraitem verification
+ const DexMapItem* pCallSiteIds; // set after intraitem verification
+ const DexMapItem* pMethodHandleItems; // set after intraitem verification
+
+ /*
+ * bitmap of type_id indices that have been used to define classes;
+ * initialized immediately before class_def cross-verification, and
+ * freed immediately after it
+ */
+ u4* pDefinedClassBits;
+
+ const void* previousItem; // set during section iteration
+};
+
+/*
+ * Return the file offset of the given pointer.
+ */
+static inline u4 fileOffset(const CheckState* state, const void* ptr) {
+ return ((const u1*) ptr) - state->fileStart;
+}
+
+/*
+ * Return a pointer for the given file offset.
+ */
+static inline void* filePointer(const CheckState* state, u4 offset) {
+ return (void*) (state->fileStart + offset);
+}
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ */
+static inline bool checkPtrRange(const CheckState* state,
+ const void* start, const void* end, const char* label) {
+ const void* fileStart = state->fileStart;
+ const void* fileEnd = state->fileEnd;
+ if ((start < fileStart) || (start > fileEnd)
+ || (end < start) || (end > fileEnd)) {
+ ALOGW("Bad offset range for %s: %#x..%#x", label,
+ fileOffset(state, start), fileOffset(state, end));
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Verify that a range of offsets, start inclusive to end exclusive,
+ * are all valid. That is, the start must indicate a valid byte or may
+ * point at the byte just past the end of the file (but no further),
+ * and the end must be no less than the start and must also not point
+ * beyond the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_OFFSET_RANGE(_start, _end) { \
+ const u1* _startPtr = (const u1*) filePointer(state, (_start)); \
+ const u1* _endPtr = (const u1*) filePointer(state, (_end)); \
+ if (!checkPtrRange(state, _startPtr, _endPtr, \
+ #_start ".." #_end)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Verify that a pointer range, start inclusive to end exclusive, only
+ * covers bytes in the file and doesn't point beyond the end of the
+ * file. That is, the start must indicate a valid byte or may point at
+ * the byte just past the end of the file (but no further), and the
+ * end must be no less than the start and must also not point beyond
+ * the byte just past the end of the file.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define CHECK_PTR_RANGE(_start, _end) { \
+ if (!checkPtrRange(state, (_start), (_end), #_start ".." #_end)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Make sure a list of items fits entirely within the file.
+ *
+ * Assumes "const CheckState* state" and "typeof(_count) == typeof(_elemSize)"
+ * If the type sizes or signs are mismatched, this will return 0.
+ */
+#define CHECK_LIST_SIZE(_ptr, _count, _elemSize) { \
+ const u1* _start = (const u1*) (_ptr); \
+ const u1* _end = _start + ((_count) * (_elemSize)); \
+ u4 _dummy; \
+ if (__builtin_mul_overflow((_count), (_elemSize), &_dummy) || \
+ !checkPtrRange(state, _start, _end, #_ptr)) { \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap a field that is known to hold an absolute DEX file offset. Note:
+ * This does not check to see that the swapped offset points within the
+ * mapped file, since that should be handled (with even more rigor) by
+ * the cross-verification phase.
+ *
+ * Assumes "const CheckState* state".
+ */
+#define SWAP_OFFSET4(_field) { \
+ SWAP_FIELD4((_field)); \
+ }
+
+/*
+ * Verify that an index falls in a valid range.
+ */
+#define CHECK_INDEX(_field, _limit) { \
+ if ((_field) >= (_limit)) { \
+ ALOGW("Bad index: %s(%u) > %s(%u)", \
+ #_field, (u4)(_field), #_limit, (u4)(_limit)); \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX2(_field, _limit) { \
+ SWAP_FIELD2((_field)); \
+ CHECK_INDEX((_field), (_limit)); \
+ }
+
+/*
+ * Verify that an index falls in a valid range or is kDexNoIndex.
+ */
+#define CHECK_INDEX_OR_NOINDEX(_field, _limit) { \
+ if ((_field) != kDexNoIndex && (_field) >= (_limit)) { \
+ ALOGW("Bad index: %s(%u) > %s(%u)", \
+ #_field, (u4)(_field), #_limit, (u4)(_limit)); \
+ return 0; \
+ } \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range.
+ */
+#define SWAP_INDEX4(_field, _limit) { \
+ SWAP_FIELD4((_field)); \
+ CHECK_INDEX((_field), (_limit)); \
+ }
+
+/*
+ * Swap an index, and verify that it falls in a valid range or is
+ * kDexNoIndex.
+ */
+#define SWAP_INDEX4_OR_NOINDEX(_field, _limit) { \
+ SWAP_FIELD4((_field)); \
+ CHECK_INDEX_OR_NOINDEX((_field), (_limit)); \
+ }
+
+/* Verify the definer of a given field_idx. */
+static bool verifyFieldDefiner(const CheckState* state, u4 definingClass,
+ u4 fieldIdx) {
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx == definingClass;
+}
+
+/* Verify the definer of a given method_idx. */
+static bool verifyMethodDefiner(const CheckState* state, u4 definingClass,
+ u4 methodIdx) {
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx == definingClass;
+}
+
+/*
+ * Calculate the required size (in elements) of the array pointed at by
+ * pDefinedClassBits.
+ */
+static size_t calcDefinedClassBitsSize(const CheckState* state)
+{
+ // Divide typeIdsSize by 32 (0x20), rounding up.
+ return (state->pHeader->typeIdsSize + 0x1f) >> 5;
+}
+
+/*
+ * Set the given bit in pDefinedClassBits, returning its former value.
+ */
+static bool setDefinedClassBit(const CheckState* state, u4 typeIdx) {
+ u4 arrayIdx = typeIdx >> 5;
+ u4 bit = 1 << (typeIdx & 0x1f);
+ u4* element = &state->pDefinedClassBits[arrayIdx];
+ bool result = (*element & bit) != 0;
+
+ *element |= bit;
+
+ return result;
+}
+
+/*
+ * Swap the header_item.
+ */
+static bool swapDexHeader(const CheckState* state, DexHeader* pHeader)
+{
+ CHECK_PTR_RANGE(pHeader, pHeader + 1);
+
+ // magic is ok
+ SWAP_FIELD4(pHeader->checksum);
+ // signature is ok
+ SWAP_FIELD4(pHeader->fileSize);
+ SWAP_FIELD4(pHeader->headerSize);
+ SWAP_FIELD4(pHeader->endianTag);
+ SWAP_FIELD4(pHeader->linkSize);
+ SWAP_OFFSET4(pHeader->linkOff);
+ SWAP_OFFSET4(pHeader->mapOff);
+ SWAP_FIELD4(pHeader->stringIdsSize);
+ SWAP_OFFSET4(pHeader->stringIdsOff);
+ SWAP_FIELD4(pHeader->typeIdsSize);
+ SWAP_OFFSET4(pHeader->typeIdsOff);
+ SWAP_FIELD4(pHeader->fieldIdsSize);
+ SWAP_OFFSET4(pHeader->fieldIdsOff);
+ SWAP_FIELD4(pHeader->methodIdsSize);
+ SWAP_OFFSET4(pHeader->methodIdsOff);
+ SWAP_FIELD4(pHeader->protoIdsSize);
+ SWAP_OFFSET4(pHeader->protoIdsOff);
+ SWAP_FIELD4(pHeader->classDefsSize);
+ SWAP_OFFSET4(pHeader->classDefsOff);
+ SWAP_FIELD4(pHeader->dataSize);
+ SWAP_OFFSET4(pHeader->dataOff);
+
+ if (pHeader->endianTag != kDexEndianConstant) {
+ ALOGE("Unexpected endian_tag: %#x", pHeader->endianTag);
+ return false;
+ }
+
+ // Assign variables so the diagnostic is prettier. (Hooray for macros.)
+ u4 linkOff = pHeader->linkOff;
+ u4 linkEnd = linkOff + pHeader->linkSize;
+ u4 dataOff = pHeader->dataOff;
+ u4 dataEnd = dataOff + pHeader->dataSize;
+ CHECK_OFFSET_RANGE(linkOff, linkEnd);
+ CHECK_OFFSET_RANGE(dataOff, dataEnd);
+
+ /*
+ * Note: The offsets and ranges of the other header items end up getting
+ * checked during the first iteration over the map.
+ */
+
+ return true;
+}
+
+/* Check the header section for sanity. */
+static bool checkHeaderSection(const CheckState* state, u4 sectionOffset,
+ u4 sectionCount, u4* endOffset) {
+ if (sectionCount != 1) {
+ ALOGE("Multiple header items");
+ return false;
+ }
+
+ if (sectionOffset != 0) {
+ ALOGE("Header at %#x; not at start of file", sectionOffset);
+ return false;
+ }
+
+ const DexHeader* pHeader = (const DexHeader*) filePointer(state, 0);
+ *endOffset = pHeader->headerSize;
+ return true;
+}
+
+/*
+ * Helper for swapMap(), which turns a map type constant into a small
+ * one-bit-on integer, suitable for use in an int-sized bit set.
+ */
+static u4 mapTypeToBitMask(int mapType) {
+ switch (mapType) {
+ case kDexTypeHeaderItem: return 1 << 0;
+ case kDexTypeStringIdItem: return 1 << 1;
+ case kDexTypeTypeIdItem: return 1 << 2;
+ case kDexTypeProtoIdItem: return 1 << 3;
+ case kDexTypeFieldIdItem: return 1 << 4;
+ case kDexTypeMethodIdItem: return 1 << 5;
+ case kDexTypeClassDefItem: return 1 << 6;
+ case kDexTypeMapList: return 1 << 7;
+ case kDexTypeTypeList: return 1 << 8;
+ case kDexTypeAnnotationSetRefList: return 1 << 9;
+ case kDexTypeAnnotationSetItem: return 1 << 10;
+ case kDexTypeClassDataItem: return 1 << 11;
+ case kDexTypeCodeItem: return 1 << 12;
+ case kDexTypeStringDataItem: return 1 << 13;
+ case kDexTypeDebugInfoItem: return 1 << 14;
+ case kDexTypeAnnotationItem: return 1 << 15;
+ case kDexTypeEncodedArrayItem: return 1 << 16;
+ case kDexTypeAnnotationsDirectoryItem: return 1 << 17;
+ case kDexTypeCallSiteIdItem: return 1 << 18;
+ case kDexTypeMethodHandleItem: return 1 << 19;
+ default: {
+ ALOGE("Unknown map item type %04x", mapType);
+ return 0;
+ }
+ }
+}
+
+/*
+ * Helper for swapMap(), which indicates if an item type should appear
+ * in the data section.
+ */
+static bool isDataSectionType(int mapType) {
+ switch (mapType) {
+ case kDexTypeHeaderItem:
+ case kDexTypeStringIdItem:
+ case kDexTypeTypeIdItem:
+ case kDexTypeProtoIdItem:
+ case kDexTypeFieldIdItem:
+ case kDexTypeMethodIdItem:
+ case kDexTypeClassDefItem: {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * Swap the map_list and verify what we can about it. Also, if verification
+ * passes, allocate the state's DexDataMap.
+ */
+static bool swapMap(CheckState* state, DexMapList* pMap)
+{
+ DexMapItem* item = pMap->list;
+ u4 count;
+ u4 dataItemCount = 0; // Total count of items in the data section.
+ u4 dataItemsLeft = state->pHeader->dataSize; // See use below.
+ u4 usedBits = 0; // Bit set: one bit per section
+ bool first = true;
+ u4 lastOffset = 0;
+
+ SWAP_FIELD4(pMap->size);
+ count = pMap->size;
+ const u4 sizeOfItem = (u4) sizeof(DexMapItem);
+ CHECK_LIST_SIZE(item, count, sizeOfItem);
+
+ while (count--) {
+ SWAP_FIELD2(item->type);
+ SWAP_FIELD2(item->unused);
+ SWAP_FIELD4(item->size);
+ SWAP_OFFSET4(item->offset);
+
+ if (first) {
+ first = false;
+ } else if (lastOffset >= item->offset) {
+ ALOGE("Out-of-order map item: %#x then %#x",
+ lastOffset, item->offset);
+ return false;
+ }
+
+ if (item->offset >= state->pHeader->fileSize) {
+ ALOGE("Map item after end of file: %x, size %#x",
+ item->offset, state->pHeader->fileSize);
+ return false;
+ }
+
+ if (isDataSectionType(item->type)) {
+ u4 icount = item->size;
+
+ /*
+ * This sanity check on the data section items ensures that
+ * there are no more items than the number of bytes in
+ * the data section.
+ */
+ if (icount > dataItemsLeft) {
+ ALOGE("Unrealistically many items in the data section: "
+ "at least %d", dataItemCount + icount);
+ return false;
+ }
+
+ dataItemsLeft -= icount;
+ dataItemCount += icount;
+ }
+
+ u4 bit = mapTypeToBitMask(item->type);
+
+ if (bit == 0) {
+ return false;
+ }
+
+ if ((usedBits & bit) != 0) {
+ ALOGE("Duplicate map section of type %#x", item->type);
+ return false;
+ }
+
+ if (item->type == kDexTypeCallSiteIdItem) {
+ state->pCallSiteIds = item;
+ } else if (item->type == kDexTypeMethodHandleItem) {
+ state->pMethodHandleItems = item;
+ }
+
+ usedBits |= bit;
+ lastOffset = item->offset;
+ item++;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeHeaderItem)) == 0) {
+ ALOGE("Map is missing header entry");
+ return false;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeMapList)) == 0) {
+ ALOGE("Map is missing map_list entry");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeStringIdItem)) == 0)
+ && ((state->pHeader->stringIdsOff != 0)
+ || (state->pHeader->stringIdsSize != 0))) {
+ ALOGE("Map is missing string_ids entry");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem)) == 0)
+ && ((state->pHeader->typeIdsOff != 0)
+ || (state->pHeader->typeIdsSize != 0))) {
+ ALOGE("Map is missing type_ids entry");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem)) == 0)
+ && ((state->pHeader->protoIdsOff != 0)
+ || (state->pHeader->protoIdsSize != 0))) {
+ ALOGE("Map is missing proto_ids entry");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem)) == 0)
+ && ((state->pHeader->fieldIdsOff != 0)
+ || (state->pHeader->fieldIdsSize != 0))) {
+ ALOGE("Map is missing field_ids entry");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem)) == 0)
+ && ((state->pHeader->methodIdsOff != 0)
+ || (state->pHeader->methodIdsSize != 0))) {
+ ALOGE("Map is missing method_ids entry");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeClassDefItem)) == 0)
+ && ((state->pHeader->classDefsOff != 0)
+ || (state->pHeader->classDefsSize != 0))) {
+ ALOGE("Map is missing class_defs entry");
+ return false;
+ }
+
+ state->pDataMap = dexDataMapAlloc(dataItemCount);
+ if (state->pDataMap == NULL) {
+ ALOGE("Unable to allocate data map (size %#x)", dataItemCount);
+ return false;
+ }
+
+ return true;
+}
+
+/* Check the map section for sanity. */
+static bool checkMapSection(const CheckState* state, u4 sectionOffset,
+ u4 sectionCount, u4* endOffset) {
+ if (sectionCount != 1) {
+ ALOGE("Multiple map list items");
+ return false;
+ }
+
+ if (sectionOffset != state->pHeader->mapOff) {
+ ALOGE("Map not at header-defined offset: %#x, expected %#x",
+ sectionOffset, state->pHeader->mapOff);
+ return false;
+ }
+
+ const DexMapList* pMap = (const DexMapList*) filePointer(state, sectionOffset);
+
+ *endOffset =
+ sectionOffset + sizeof(u4) + (pMap->size * sizeof(DexMapItem));
+ return true;
+}
+
+/* Perform byte-swapping and intra-item verification on string_id_item. */
+static void* swapStringIdItem(const CheckState* state, void* ptr) {
+ DexStringId* item = (DexStringId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_OFFSET4(item->stringDataOff);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of string_id_item. */
+static void* crossVerifyStringIdItem(const CheckState* state, void* ptr) {
+ const DexStringId* item = (const DexStringId*) ptr;
+
+ if (!dexDataMapVerify(state->pDataMap,
+ item->stringDataOff, kDexTypeStringDataItem)) {
+ return NULL;
+ }
+
+ const DexStringId* item0 = (const DexStringId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering.
+ const char* s0 = dexGetStringData(state->pDexFile, item0);
+ const char* s1 = dexGetStringData(state->pDexFile, item);
+ if (dexUtf8Cmp(s0, s1) >= 0) {
+ ALOGE("Out-of-order string_ids: '%s' then '%s'", s0, s1);
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on type_id_item. */
+static void* swapTypeIdItem(const CheckState* state, void* ptr) {
+ DexTypeId* item = (DexTypeId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->descriptorIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of type_id_item. */
+static void* crossVerifyTypeIdItem(const CheckState* state, void* ptr) {
+ const DexTypeId* item = (const DexTypeId*) ptr;
+ const char* descriptor =
+ dexStringById(state->pDexFile, item->descriptorIdx);
+
+ if (!dexIsValidTypeDescriptor(descriptor)) {
+ ALOGE("Invalid type descriptor: '%s'", descriptor);
+ return NULL;
+ }
+
+ const DexTypeId* item0 = (const DexTypeId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on string_ids being in order.
+ if (item0->descriptorIdx >= item->descriptorIdx) {
+ ALOGE("Out-of-order type_ids: %#x then %#x",
+ item0->descriptorIdx, item->descriptorIdx);
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on proto_id_item. */
+static void* swapProtoIdItem(const CheckState* state, void* ptr) {
+ DexProtoId* item = (DexProtoId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->shortyIdx, state->pHeader->stringIdsSize);
+ SWAP_INDEX4(item->returnTypeIdx, state->pHeader->typeIdsSize);
+ SWAP_OFFSET4(item->parametersOff);
+
+ return item + 1;
+}
+
+/* Helper for crossVerifyProtoIdItem(), which checks a shorty character
+ * to see if it is compatible with a type descriptor. Returns true if
+ * so, false if not. */
+static bool shortyDescMatch(char shorty, const char* descriptor, bool
+ isReturnType) {
+ switch (shorty) {
+ case 'V': {
+ if (!isReturnType) {
+ ALOGE("Invalid use of void");
+ return false;
+ }
+ FALLTHROUGH_INTENDED;
+ }
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z': {
+ if ((descriptor[0] != shorty) || (descriptor[1] != '\0')) {
+ ALOGE("Shorty vs. primitive type mismatch: '%c', '%s'",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ case 'L': {
+ if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+ ALOGE("Shorty vs. type mismatch: '%c', '%s'",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ default: {
+ ALOGE("Bogus shorty: '%c'", shorty);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Perform cross-item verification of proto_id_item. */
+static void* crossVerifyProtoIdItem(const CheckState* state, void* ptr) {
+ const DexProtoId* item = (const DexProtoId*) ptr;
+ const char* shorty =
+ dexStringById(state->pDexFile, item->shortyIdx);
+
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->parametersOff, kDexTypeTypeList)) {
+ return NULL;
+ }
+
+ if (!shortyDescMatch(*shorty,
+ dexStringByTypeIdx(state->pDexFile, item->returnTypeIdx),
+ true)) {
+ return NULL;
+ }
+
+ u4 protoIdx = item - state->pDexFile->pProtoIds;
+ DexProto proto = { state->pDexFile, protoIdx };
+ DexParameterIterator iterator;
+
+ dexParameterIteratorInit(&iterator, &proto);
+ shorty++; // Skip the return type.
+
+ for (;;) {
+ const char *desc = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (desc == NULL) {
+ break;
+ }
+
+ if (*shorty == '\0') {
+ ALOGE("Shorty is too short");
+ return NULL;
+ }
+
+ if (!shortyDescMatch(*shorty, desc, false)) {
+ return NULL;
+ }
+
+ shorty++;
+ }
+
+ if (*shorty != '\0') {
+ ALOGE("Shorty is too long");
+ return NULL;
+ }
+
+ const DexProtoId* item0 = (const DexProtoId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on type_ids being in order.
+ if (item0->returnTypeIdx > item->returnTypeIdx) {
+ ALOGE("Out-of-order proto_id return types");
+ return NULL;
+ } else if (item0->returnTypeIdx == item->returnTypeIdx) {
+ bool badOrder = false;
+ DexProto proto0 = { state->pDexFile, protoIdx - 1 };
+ DexParameterIterator iterator0;
+
+ dexParameterIteratorInit(&iterator, &proto);
+ dexParameterIteratorInit(&iterator0, &proto0);
+
+ for (;;) {
+ u4 idx0 = dexParameterIteratorNextIndex(&iterator0);
+ u4 idx1 = dexParameterIteratorNextIndex(&iterator);
+
+ if (idx1 == kDexNoIndex) {
+ badOrder = true;
+ break;
+ }
+
+ if (idx0 == kDexNoIndex) {
+ break;
+ }
+
+ if (idx0 < idx1) {
+ break;
+ } else if (idx0 > idx1) {
+ badOrder = true;
+ break;
+ }
+ }
+
+ if (badOrder) {
+ ALOGE("Out-of-order proto_id arguments");
+ return NULL;
+ }
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on field_id_item. */
+static void* swapFieldIdItem(const CheckState* state, void* ptr) {
+ DexFieldId* item = (DexFieldId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX2(item->typeIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of field_id_item. */
+static void* crossVerifyFieldIdItem(const CheckState* state, void* ptr) {
+ const DexFieldId* item = (const DexFieldId*) ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsClassDescriptor(s)) {
+ ALOGE("Invalid descriptor for class_idx: '%s'", s);
+ return NULL;
+ }
+
+ s = dexStringByTypeIdx(state->pDexFile, item->typeIdx);
+ if (!dexIsFieldDescriptor(s)) {
+ ALOGE("Invalid descriptor for type_idx: '%s'", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ ALOGE("Invalid name: '%s'", s);
+ return NULL;
+ }
+
+ const DexFieldId* item0 = (const DexFieldId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on the other sections being in order.
+ bool done = false;
+ bool bogus = false;
+
+ if (item0->classIdx > item->classIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->classIdx < item->classIdx) {
+ done = true;
+ }
+
+ if (!done) {
+ if (item0->nameIdx > item->nameIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->nameIdx < item->nameIdx) {
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (item0->typeIdx >= item->typeIdx) {
+ bogus = true;
+ }
+ }
+
+ if (bogus) {
+ ALOGE("Out-of-order field_ids");
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on method_id_item. */
+static void* swapMethodIdItem(const CheckState* state, void* ptr) {
+ DexMethodId* item = (DexMethodId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX2(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_INDEX2(item->protoIdx, state->pHeader->protoIdsSize);
+ SWAP_INDEX4(item->nameIdx, state->pHeader->stringIdsSize);
+
+ return item + 1;
+}
+
+/* Perform cross-item verification of method_id_item. */
+static void* crossVerifyMethodIdItem(const CheckState* state, void* ptr) {
+ const DexMethodId* item = (const DexMethodId*) ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsReferenceDescriptor(s)) {
+ ALOGE("Invalid descriptor for class_idx: '%s'", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ ALOGE("Invalid name: '%s'", s);
+ return NULL;
+ }
+
+ const DexMethodId* item0 = (const DexMethodId*) state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on the other sections being in order.
+ bool done = false;
+ bool bogus = false;
+
+ if (item0->classIdx > item->classIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->classIdx < item->classIdx) {
+ done = true;
+ }
+
+ if (!done) {
+ if (item0->nameIdx > item->nameIdx) {
+ bogus = true;
+ done = true;
+ } else if (item0->nameIdx < item->nameIdx) {
+ done = true;
+ }
+ }
+
+ if (!done) {
+ if (item0->protoIdx >= item->protoIdx) {
+ bogus = true;
+ }
+ }
+
+ if (bogus) {
+ ALOGE("Out-of-order method_ids");
+ return NULL;
+ }
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform byte-swapping and intra-item verification on class_def_item. */
+static void* swapClassDefItem(const CheckState* state, void* ptr) {
+ DexClassDef* item = (DexClassDef*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_INDEX4(item->classIdx, state->pHeader->typeIdsSize);
+ SWAP_FIELD4(item->accessFlags);
+ SWAP_INDEX4_OR_NOINDEX(item->superclassIdx, state->pHeader->typeIdsSize);
+ SWAP_OFFSET4(item->interfacesOff);
+ SWAP_INDEX4_OR_NOINDEX(item->sourceFileIdx, state->pHeader->stringIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+ SWAP_OFFSET4(item->classDataOff);
+
+ if ((item->accessFlags & ~ACC_CLASS_MASK) != 0) {
+ // The VM specification says that unknown flags should be ignored.
+ ALOGV("Bogus class access flags %x", item->accessFlags);
+ item->accessFlags &= ACC_CLASS_MASK;
+ }
+
+ return item + 1;
+}
+
+/* defined below */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+ DexClassData* classData);
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+ const DexAnnotationsDirectoryItem* dir);
+
+/* Helper for crossVerifyClassDefItem(), which checks a class_data_item to
+ * make sure all its references are to a given class. */
+static bool verifyClassDataIsForDef(const CheckState* state, u4 offset,
+ u4 definerIdx) {
+ if (offset == 0) {
+ return true;
+ }
+
+ const u1* data = (const u1*) filePointer(state, offset);
+ DexClassData* classData = dexReadAndVerifyClassData(&data, NULL);
+
+ if (classData == NULL) {
+ // Shouldn't happen, but bail here just in case.
+ return false;
+ }
+
+ /*
+ * The class_data_item verification ensures that
+ * it consistently refers to the same definer, so all we need to
+ * do is check the first one.
+ */
+ u4 dataDefiner = findFirstClassDataDefiner(state, classData);
+ bool result = (dataDefiner == definerIdx) || (dataDefiner == kDexNoIndex);
+
+ free(classData);
+ return result;
+}
+
+/* Helper for crossVerifyClassDefItem(), which checks an
+ * annotations_directory_item to make sure all its references are to a
+ * given class. */
+static bool verifyAnnotationsDirectoryIsForDef(const CheckState* state,
+ u4 offset, u4 definerIdx) {
+ if (offset == 0) {
+ return true;
+ }
+
+ const DexAnnotationsDirectoryItem* dir =
+ (const DexAnnotationsDirectoryItem*) filePointer(state, offset);
+ u4 annoDefiner = findFirstAnnotationsDirectoryDefiner(state, dir);
+
+ return (annoDefiner == definerIdx) || (annoDefiner == kDexNoIndex);
+}
+
+/* Perform cross-item verification of class_def_item. */
+static void* crossVerifyClassDefItem(const CheckState* state, void* ptr) {
+ const DexClassDef* item = (const DexClassDef*) ptr;
+ u4 classIdx = item->classIdx;
+ const char* descriptor = dexStringByTypeIdx(state->pDexFile, classIdx);
+
+ if (!dexIsClassDescriptor(descriptor)) {
+ ALOGE("Invalid class: '%s'", descriptor);
+ return NULL;
+ }
+
+ if (setDefinedClassBit(state, classIdx)) {
+ ALOGE("Duplicate class definition: '%s'", descriptor);
+ return NULL;
+ }
+
+ bool okay =
+ dexDataMapVerify0Ok(state->pDataMap,
+ item->interfacesOff, kDexTypeTypeList)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->annotationsOff, kDexTypeAnnotationsDirectoryItem)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->classDataOff, kDexTypeClassDataItem)
+ && dexDataMapVerify0Ok(state->pDataMap,
+ item->staticValuesOff, kDexTypeEncodedArrayItem);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ if (item->superclassIdx != kDexNoIndex) {
+ descriptor = dexStringByTypeIdx(state->pDexFile, item->superclassIdx);
+ if (!dexIsClassDescriptor(descriptor)) {
+ ALOGE("Invalid superclass: '%s'", descriptor);
+ return NULL;
+ }
+ }
+
+ const DexTypeList* interfaces =
+ dexGetInterfacesList(state->pDexFile, item);
+ if (interfaces != NULL) {
+ u4 size = interfaces->size;
+ u4 i;
+
+ /*
+ * Ensure that all interfaces refer to classes (not arrays or
+ * primitives).
+ */
+ for (i = 0; i < size; i++) {
+ descriptor = dexStringByTypeIdx(state->pDexFile,
+ dexTypeListGetIdx(interfaces, i));
+ if (!dexIsClassDescriptor(descriptor)) {
+ ALOGE("Invalid interface: '%s'", descriptor);
+ return NULL;
+ }
+ }
+
+ /*
+ * Ensure that there are no duplicates. This is an O(N^2) test,
+ * but in practice the number of interfaces implemented by any
+ * given class is low. I will buy a milkshake for the
+ * first person to show me a realistic case for which this test
+ * would be unacceptably slow.
+ */
+ for (i = 1; i < size; i++) {
+ u4 idx1 = dexTypeListGetIdx(interfaces, i);
+ u4 j;
+ for (j = 0; j < i; j++) {
+ u4 idx2 = dexTypeListGetIdx(interfaces, j);
+ if (idx1 == idx2) {
+ ALOGE("Duplicate interface: '%s'",
+ dexStringByTypeIdx(state->pDexFile, idx1));
+ return NULL;
+ }
+ }
+ }
+ }
+
+ if (!verifyClassDataIsForDef(state, item->classDataOff, item->classIdx)) {
+ ALOGE("Invalid class_data_item");
+ return NULL;
+ }
+
+ if (!verifyAnnotationsDirectoryIsForDef(state, item->annotationsOff,
+ item->classIdx)) {
+ ALOGE("Invalid annotations_directory_item");
+ return NULL;
+ }
+
+ return (void*) (item + 1);
+}
+
+/* Perform cross-item verification of call_site_id. */
+static void* crossVerifyCallSiteId(const CheckState* state, void* ptr) {
+ const DexCallSiteId* item = (const DexCallSiteId*) ptr;
+ if (state->pCallSiteIds == nullptr) {
+ ALOGE("Verifying call site but expecting none");
+ return NULL;
+ }
+ if (item->callSiteOff < state->pHeader->dataOff ||
+ item->callSiteOff >= state->pHeader->dataOff + state->pHeader->dataSize) {
+ ALOGE("Bad call site offset: %u", item->callSiteOff);
+ return NULL;
+ }
+ return (void*) (item + 1);
+}
+
+/* Perform cross-item verification of method_handle_item. */
+static void* crossVerifyMethodHandleItem(const CheckState* state, void* ptr) {
+ const DexMethodHandleItem* item = (const DexMethodHandleItem*) ptr;
+ if (state->pMethodHandleItems == nullptr) {
+ ALOGE("Verifying method handle but expecting none");
+ return NULL;
+ }
+ if (item->methodHandleType > (u2) MethodHandleType::INVOKE_INTERFACE) {
+ ALOGE("Unknown method handle type: %u", item->methodHandleType);
+ return NULL;
+ }
+ switch ((MethodHandleType) item->methodHandleType) {
+ case MethodHandleType::STATIC_PUT:
+ case MethodHandleType::STATIC_GET:
+ case MethodHandleType::INSTANCE_PUT:
+ case MethodHandleType::INSTANCE_GET:
+ if (item->fieldOrMethodIdx >= state->pHeader->fieldIdsSize) {
+ ALOGE("Method handle has invalid field id: %u\n", item->fieldOrMethodIdx);
+ return NULL;
+ }
+ break;
+ case MethodHandleType::INVOKE_STATIC:
+ case MethodHandleType::INVOKE_INSTANCE:
+ case MethodHandleType::INVOKE_CONSTRUCTOR:
+ case MethodHandleType::INVOKE_DIRECT:
+ case MethodHandleType::INVOKE_INTERFACE:
+ if (item->fieldOrMethodIdx >= state->pHeader->methodIdsSize) {
+ ALOGE("Method handle has invalid method id: %u\n", item->fieldOrMethodIdx);
+ return NULL;
+ }
+ break;
+ }
+ return (void*) (item + 1);
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's field elements. */
+static u1* swapFieldAnnotations(const CheckState* state, u4 count, u1* addr) {
+ DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ const u4 sizeOfItem = (u4) sizeof(DexFieldAnnotationsItem);
+ CHECK_LIST_SIZE(item, count, sizeOfItem);
+
+ while (count--) {
+ SWAP_INDEX4(item->fieldIdx, state->pHeader->fieldIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->fieldIdx) {
+ ALOGE("Out-of-order field_idx: %#x then %#x", lastIdx,
+ item->fieldIdx);
+ return NULL;
+ }
+
+ lastIdx = item->fieldIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's method elements. */
+static u1* swapMethodAnnotations(const CheckState* state, u4 count, u1* addr) {
+ DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ const u4 sizeOfItem = (u4) sizeof(DexMethodAnnotationsItem);
+ CHECK_LIST_SIZE(item, count, sizeOfItem);
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ ALOGE("Out-of-order method_idx: %#x then %#x", lastIdx,
+ item->methodIdx);
+ return NULL;
+ }
+
+ lastIdx = item->methodIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Helper for swapAnnotationsDirectoryItem(), which performs
+ * byte-swapping and intra-item verification on an
+ * annotation_directory_item's parameter elements. */
+static u1* swapParameterAnnotations(const CheckState* state, u4 count,
+ u1* addr) {
+ DexParameterAnnotationsItem* item = (DexParameterAnnotationsItem*) addr;
+ bool first = true;
+ u4 lastIdx = 0;
+
+ const u4 sizeOfItem = (u4) sizeof(DexParameterAnnotationsItem);
+ CHECK_LIST_SIZE(item, count, sizeOfItem);
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ ALOGE("Out-of-order method_idx: %#x then %#x", lastIdx,
+ item->methodIdx);
+ return NULL;
+ }
+
+ lastIdx = item->methodIdx;
+ item++;
+ }
+
+ return (u1*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotations_directory_item. */
+static void* swapAnnotationsDirectoryItem(const CheckState* state, void* ptr) {
+ DexAnnotationsDirectoryItem* item = (DexAnnotationsDirectoryItem*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_OFFSET4(item->classAnnotationsOff);
+ SWAP_FIELD4(item->fieldsSize);
+ SWAP_FIELD4(item->methodsSize);
+ SWAP_FIELD4(item->parametersSize);
+
+ u1* addr = (u1*) (item + 1);
+
+ if (item->fieldsSize != 0) {
+ addr = swapFieldAnnotations(state, item->fieldsSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->methodsSize != 0) {
+ addr = swapMethodAnnotations(state, item->methodsSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->parametersSize != 0) {
+ addr = swapParameterAnnotations(state, item->parametersSize, addr);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ return addr;
+}
+
+static void* swapCallSiteId(const CheckState* state, void* ptr) {
+ DexCallSiteId* item = (DexCallSiteId*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_OFFSET4(item->callSiteOff);
+
+ return (item + 1);
+}
+
+static void* swapMethodHandleItem(const CheckState* state, void* ptr) {
+ DexMethodHandleItem* item = (DexMethodHandleItem*) ptr;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_FIELD2(item->methodHandleType);
+ SWAP_FIELD2(item->fieldOrMethodIdx);
+
+ return (item + 1);
+}
+
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * field elements. */
+static const u1* crossVerifyFieldAnnotations(const CheckState* state, u4 count,
+ const u1* addr, u4 definingClass) {
+ const DexFieldAnnotationsItem* item = (DexFieldAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyFieldDefiner(state, definingClass, item->fieldIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * method elements. */
+static const u1* crossVerifyMethodAnnotations(const CheckState* state,
+ u4 count, const u1* addr, u4 definingClass) {
+ const DexMethodAnnotationsItem* item = (DexMethodAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyAnnotationsDirectoryItem(), which checks the
+ * parameter elements. */
+static const u1* crossVerifyParameterAnnotations(const CheckState* state,
+ u4 count, const u1* addr, u4 definingClass) {
+ const DexParameterAnnotationsItem* item =
+ (DexParameterAnnotationsItem*) addr;
+
+ while (count--) {
+ if (!verifyMethodDefiner(state, definingClass, item->methodIdx)) {
+ return NULL;
+ }
+ if (!dexDataMapVerify(state->pDataMap, item->annotationsOff,
+ kDexTypeAnnotationSetRefList)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (const u1*) item;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyAnnotationsDirectoryItem(), which finds the type_idx of
+ * the definer of the first item in the data. */
+static u4 findFirstAnnotationsDirectoryDefiner(const CheckState* state,
+ const DexAnnotationsDirectoryItem* dir) {
+ if (dir->fieldsSize != 0) {
+ const DexFieldAnnotationsItem* fields =
+ dexGetFieldAnnotations(state->pDexFile, dir);
+ const DexFieldId* field =
+ dexGetFieldId(state->pDexFile, fields[0].fieldIdx);
+ return field->classIdx;
+ }
+
+ if (dir->methodsSize != 0) {
+ const DexMethodAnnotationsItem* methods =
+ dexGetMethodAnnotations(state->pDexFile, dir);
+ const DexMethodId* method =
+ dexGetMethodId(state->pDexFile, methods[0].methodIdx);
+ return method->classIdx;
+ }
+
+ if (dir->parametersSize != 0) {
+ const DexParameterAnnotationsItem* parameters =
+ dexGetParameterAnnotations(state->pDexFile, dir);
+ const DexMethodId* method =
+ dexGetMethodId(state->pDexFile, parameters[0].methodIdx);
+ return method->classIdx;
+ }
+
+ return kDexNoIndex;
+}
+
+/* Perform cross-item verification of annotations_directory_item. */
+static void* crossVerifyAnnotationsDirectoryItem(const CheckState* state,
+ void* ptr) {
+ const DexAnnotationsDirectoryItem* item = (const DexAnnotationsDirectoryItem*) ptr;
+ u4 definingClass = findFirstAnnotationsDirectoryDefiner(state, item);
+
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->classAnnotationsOff, kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+
+ const u1* addr = (const u1*) (item + 1);
+
+ if (item->fieldsSize != 0) {
+ addr = crossVerifyFieldAnnotations(state, item->fieldsSize, addr,
+ definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->methodsSize != 0) {
+ addr = crossVerifyMethodAnnotations(state, item->methodsSize, addr,
+ definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ if (item->parametersSize != 0) {
+ addr = crossVerifyParameterAnnotations(state, item->parametersSize,
+ addr, definingClass);
+ if (addr == NULL) {
+ return NULL;
+ }
+ }
+
+ return (void*) addr;
+}
+
+/* Perform byte-swapping and intra-item verification on type_list. */
+static void* swapTypeList(const CheckState* state, void* ptr)
+{
+ DexTypeList* pTypeList = (DexTypeList*) ptr;
+ DexTypeItem* pType;
+ u4 count;
+
+ CHECK_PTR_RANGE(pTypeList, pTypeList + 1);
+ SWAP_FIELD4(pTypeList->size);
+ count = pTypeList->size;
+ pType = pTypeList->list;
+
+ const u4 sizeOfItem = (u4) sizeof(DexTypeItem);
+ CHECK_LIST_SIZE(pType, count, sizeOfItem);
+
+ while (count--) {
+ SWAP_INDEX2(pType->typeIdx, state->pHeader->typeIdsSize);
+ pType++;
+ }
+
+ return pType;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_ref_list. */
+static void* swapAnnotationSetRefList(const CheckState* state, void* ptr) {
+ DexAnnotationSetRefList* list = (DexAnnotationSetRefList*) ptr;
+ DexAnnotationSetRefItem* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(list, list + 1);
+ SWAP_FIELD4(list->size);
+ count = list->size;
+ item = list->list;
+
+ const u4 sizeOfItem = (u4) sizeof(DexAnnotationSetRefItem);
+ CHECK_LIST_SIZE(item, count, sizeOfItem);
+
+ while (count--) {
+ SWAP_OFFSET4(item->annotationsOff);
+ item++;
+ }
+
+ return item;
+}
+
+/* Perform cross-item verification of annotation_set_ref_list. */
+static void* crossVerifyAnnotationSetRefList(const CheckState* state,
+ void* ptr) {
+ const DexAnnotationSetRefList* list = (const DexAnnotationSetRefList*) ptr;
+ const DexAnnotationSetRefItem* item = list->list;
+ int count = list->size;
+
+ while (count--) {
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ item->annotationsOff, kDexTypeAnnotationSetItem)) {
+ return NULL;
+ }
+ item++;
+ }
+
+ return (void*) item;
+}
+
+/* Perform byte-swapping and intra-item verification on
+ * annotation_set_item. */
+static void* swapAnnotationSetItem(const CheckState* state, void* ptr) {
+ DexAnnotationSetItem* set = (DexAnnotationSetItem*) ptr;
+ u4* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(set, set + 1);
+ SWAP_FIELD4(set->size);
+ count = set->size;
+ item = set->entries;
+
+ const u4 sizeOfItem = (u4) sizeof(u4);
+ CHECK_LIST_SIZE(item, count, sizeOfItem);
+
+ while (count--) {
+ SWAP_OFFSET4(*item);
+ item++;
+ }
+
+ return item;
+}
+
+/* Helper for crossVerifyAnnotationSetItem(), which extracts the type_idx
+ * out of an annotation_item. */
+static u4 annotationItemTypeIdx(const DexAnnotationItem* item) {
+ const u1* data = item->annotation;
+ return readUnsignedLeb128(&data);
+}
+
+/* Perform cross-item verification of annotation_set_item. */
+static void* crossVerifyAnnotationSetItem(const CheckState* state, void* ptr) {
+ const DexAnnotationSetItem* set = (const DexAnnotationSetItem*) ptr;
+ int count = set->size;
+ u4 lastIdx = 0;
+ bool first = true;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (!dexDataMapVerify0Ok(state->pDataMap,
+ dexGetAnnotationOff(set, i), kDexTypeAnnotationItem)) {
+ return NULL;
+ }
+
+ const DexAnnotationItem* annotation =
+ dexGetAnnotationItem(state->pDexFile, set, i);
+ u4 idx = annotationItemTypeIdx(annotation);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= idx) {
+ ALOGE("Out-of-order entry types: %#x then %#x",
+ lastIdx, idx);
+ return NULL;
+ }
+
+ lastIdx = idx;
+ }
+
+ return (void*) (set->entries + count);
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of fields. */
+static bool verifyFields(const CheckState* state, u4 size,
+ DexField* fields, bool expectStatic) {
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ DexField* field = &fields[i];
+ u4 accessFlags = field->accessFlags;
+ bool isStatic = (accessFlags & ACC_STATIC) != 0;
+
+ CHECK_INDEX(field->fieldIdx, state->pHeader->fieldIdsSize);
+
+ if (isStatic != expectStatic) {
+ ALOGE("Field in wrong list @ %d", i);
+ return false;
+ }
+
+ if ((accessFlags & ~ACC_FIELD_MASK) != 0) {
+ // The VM specification says that unknown flags should be ignored.
+ ALOGV("Bogus field access flags %x @ %d", accessFlags, i);
+ field->accessFlags &= ACC_FIELD_MASK;
+ }
+ }
+
+ return true;
+}
+
+/* Helper for verifyClassDataItem(), which checks a list of methods. */
+static bool verifyMethods(const CheckState* state, u4 size,
+ DexMethod* methods, bool expectDirect) {
+ u4 i;
+
+ for (i = 0; i < size; i++) {
+ DexMethod* method = &methods[i];
+
+ CHECK_INDEX(method->methodIdx, state->pHeader->methodIdsSize);
+
+ u4 accessFlags = method->accessFlags;
+ bool isDirect =
+ (accessFlags & (ACC_STATIC | ACC_PRIVATE | ACC_CONSTRUCTOR)) != 0;
+ bool expectCode = (accessFlags & (ACC_NATIVE | ACC_ABSTRACT)) == 0;
+ bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;
+ bool allowSynchronized = (accessFlags & ACC_NATIVE) != 0;
+
+ if (isDirect != expectDirect) {
+ ALOGE("Method in wrong list @ %d", i);
+ return false;
+ }
+
+ if (isSynchronized && !allowSynchronized) {
+ ALOGE("Bogus method access flags (synchronization) %x @ %d", accessFlags, i);
+ return false;
+ }
+
+ if ((accessFlags & ~ACC_METHOD_MASK) != 0) {
+ // The VM specification says that unknown flags should be ignored.
+ ALOGV("Bogus method access flags %x @ %d", accessFlags, i);
+ method->accessFlags &= ACC_METHOD_MASK;
+ }
+
+ if (expectCode) {
+ if (method->codeOff == 0) {
+ ALOGE("Unexpected zero code_off for access_flags %x",
+ accessFlags);
+ return false;
+ }
+ } else if (method->codeOff != 0) {
+ ALOGE("Unexpected non-zero code_off %#x for access_flags %x",
+ method->codeOff, accessFlags);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Helper for verifyClassDataItem(), which does most of the work. */
+static bool verifyClassDataItem0(const CheckState* state,
+ DexClassData* classData) {
+ bool okay;
+
+ okay = verifyFields(state, classData->header.staticFieldsSize,
+ classData->staticFields, true);
+
+ if (!okay) {
+ ALOGE("Trouble with static fields");
+ return false;
+ }
+
+ verifyFields(state, classData->header.instanceFieldsSize,
+ classData->instanceFields, false);
+
+ if (!okay) {
+ ALOGE("Trouble with instance fields");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.directMethodsSize,
+ classData->directMethods, true);
+
+ if (!okay) {
+ ALOGE("Trouble with direct methods");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.virtualMethodsSize,
+ classData->virtualMethods, false);
+
+ if (!okay) {
+ ALOGE("Trouble with virtual methods");
+ return false;
+ }
+
+ return true;
+}
+
+/* Perform intra-item verification on class_data_item. */
+static void* intraVerifyClassDataItem(const CheckState* state, void* ptr) {
+ const u1* data = (const u1*) ptr;
+ DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+
+ if (classData == NULL) {
+ ALOGE("Unable to parse class_data_item");
+ return NULL;
+ }
+
+ bool okay = verifyClassDataItem0(state, classData);
+
+ free(classData);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Helper for crossVerifyClassDefItem() and
+ * crossVerifyClassDataItem(), which finds the type_idx of the definer
+ * of the first item in the data. */
+static u4 findFirstClassDataDefiner(const CheckState* state,
+ DexClassData* classData) {
+ if (classData->header.staticFieldsSize != 0) {
+ u4 fieldIdx = classData->staticFields[0].fieldIdx;
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx;
+ }
+
+ if (classData->header.instanceFieldsSize != 0) {
+ u4 fieldIdx = classData->instanceFields[0].fieldIdx;
+ const DexFieldId* field = dexGetFieldId(state->pDexFile, fieldIdx);
+ return field->classIdx;
+ }
+
+ if (classData->header.directMethodsSize != 0) {
+ u4 methodIdx = classData->directMethods[0].methodIdx;
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx;
+ }
+
+ if (classData->header.virtualMethodsSize != 0) {
+ u4 methodIdx = classData->virtualMethods[0].methodIdx;
+ const DexMethodId* meth = dexGetMethodId(state->pDexFile, methodIdx);
+ return meth->classIdx;
+ }
+
+ return kDexNoIndex;
+}
+
+/* Perform cross-item verification of class_data_item. */
+static void* crossVerifyClassDataItem(const CheckState* state, void* ptr) {
+ const u1* data = (const u1*) ptr;
+ DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+ u4 definingClass = findFirstClassDataDefiner(state, classData);
+ bool okay = true;
+ u4 i;
+
+ for (i = classData->header.staticFieldsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexField* field = &classData->staticFields[i];
+ okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+ }
+
+ for (i = classData->header.instanceFieldsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexField* field = &classData->instanceFields[i];
+ okay = verifyFieldDefiner(state, definingClass, field->fieldIdx);
+ }
+
+ for (i = classData->header.directMethodsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexMethod* meth = &classData->directMethods[i];
+ okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+ kDexTypeCodeItem)
+ && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+ }
+
+ for (i = classData->header.virtualMethodsSize; okay && (i > 0); /*i*/) {
+ i--;
+ const DexMethod* meth = &classData->virtualMethods[i];
+ okay = dexDataMapVerify0Ok(state->pDataMap, meth->codeOff,
+ kDexTypeCodeItem)
+ && verifyMethodDefiner(state, definingClass, meth->methodIdx);
+ }
+
+ free(classData);
+
+ if (!okay) {
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Helper for swapCodeItem(), which fills an array with all the valid
+ * handlerOff values for catch handlers and also verifies the handler
+ * contents. */
+static u4 setHandlerOffsAndVerify(const CheckState* state,
+ DexCode* code, u4 firstOffset, u4 handlersSize, u4* handlerOffs) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* handlersBase = dexGetCatchHandlerData(code);
+ u4 offset = firstOffset;
+ bool okay = true;
+ u4 i;
+
+ for (i = 0; i < handlersSize; i++) {
+ const u1* ptr = handlersBase + offset;
+ int size = readAndVerifySignedLeb128(&ptr, fileEnd, &okay);
+ bool catchAll;
+
+ if (!okay) {
+ ALOGE("Bogus size");
+ return 0;
+ }
+
+ if ((size < -65536) || (size > 65536)) {
+ ALOGE("Invalid size: %d", size);
+ return 0;
+ }
+
+ if (size <= 0) {
+ catchAll = true;
+ size = -size;
+ } else {
+ catchAll = false;
+ }
+
+ handlerOffs[i] = offset;
+
+ while (size-- > 0) {
+ u4 typeIdx =
+ readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus type_idx");
+ return 0;
+ }
+
+ CHECK_INDEX(typeIdx, state->pHeader->typeIdsSize);
+
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ ALOGE("Invalid addr: %#x", addr);
+ return 0;
+ }
+ }
+
+ if (catchAll) {
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus catch_all_addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ ALOGE("Invalid catch_all_addr: %#x", addr);
+ return 0;
+ }
+ }
+
+ offset = ptr - handlersBase;
+ }
+
+ return offset;
+}
+
+/* Helper for swapCodeItem(), which does all the try-catch related
+ * swapping and verification. */
+static void* swapTriesAndCatches(const CheckState* state, DexCode* code) {
+ const u1* encodedHandlers = dexGetCatchHandlerData(code);
+ const u1* encodedPtr = encodedHandlers;
+ bool okay = true;
+ u4 handlersSize =
+ readAndVerifyUnsignedLeb128(&encodedPtr, state->fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus handlers_size");
+ return NULL;
+ }
+
+ if ((handlersSize == 0) || (handlersSize >= 65536)) {
+ ALOGE("Invalid handlers_size: %d", handlersSize);
+ return NULL;
+ }
+
+ u4 handlerOffs[handlersSize]; // list of valid handlerOff values
+ u4 endOffset = setHandlerOffsAndVerify(state, code,
+ encodedPtr - encodedHandlers,
+ handlersSize, handlerOffs);
+
+ if (endOffset == 0) {
+ return NULL;
+ }
+
+ DexTry* tries = (DexTry*) dexGetTries(code);
+ u4 count = code->triesSize;
+ u4 lastEnd = 0;
+
+ const u4 sizeOfItem = (u4) sizeof(DexTry);
+ CHECK_LIST_SIZE(tries, count, sizeOfItem);
+
+ while (count--) {
+ u4 i;
+
+ SWAP_FIELD4(tries->startAddr);
+ SWAP_FIELD2(tries->insnCount);
+ SWAP_FIELD2(tries->handlerOff);
+
+ if (tries->startAddr < lastEnd) {
+ ALOGE("Out-of-order try");
+ return NULL;
+ }
+
+ if (tries->startAddr >= code->insnsSize) {
+ ALOGE("Invalid start_addr: %#x", tries->startAddr);
+ return NULL;
+ }
+
+ for (i = 0; i < handlersSize; i++) {
+ if (tries->handlerOff == handlerOffs[i]) {
+ break;
+ }
+ }
+
+ if (i == handlersSize) {
+ ALOGE("Bogus handler offset: %#x", tries->handlerOff);
+ return NULL;
+ }
+
+ lastEnd = tries->startAddr + tries->insnCount;
+
+ if (lastEnd > code->insnsSize) {
+ ALOGE("Invalid insn_count: %#x (end addr %#x)",
+ tries->insnCount, lastEnd);
+ return NULL;
+ }
+
+ tries++;
+ }
+
+ return (u1*) encodedHandlers + endOffset;
+}
+
+/* Perform byte-swapping and intra-item verification on code_item. */
+static void* swapCodeItem(const CheckState* state, void* ptr) {
+ DexCode* item = (DexCode*) ptr;
+ u2* insns;
+ u4 count;
+
+ CHECK_PTR_RANGE(item, item + 1);
+ SWAP_FIELD2(item->registersSize);
+ SWAP_FIELD2(item->insSize);
+ SWAP_FIELD2(item->outsSize);
+ SWAP_FIELD2(item->triesSize);
+ SWAP_OFFSET4(item->debugInfoOff);
+ SWAP_FIELD4(item->insnsSize);
+
+ if (item->insSize > item->registersSize) {
+ ALOGE("insSize (%u) > registersSize (%u)", item->insSize,
+ item->registersSize);
+ return NULL;
+ }
+
+ if ((item->outsSize > 5) && (item->outsSize > item->registersSize)) {
+ /*
+ * It's okay for outsSize to be up to five, even if registersSize
+ * is smaller, since the short forms of method invocation allow
+ * repetition of a register multiple times within a single parameter
+ * list. Longer parameter lists, though, need to be represented
+ * in-order in the register file.
+ */
+ ALOGE("outsSize (%u) > registersSize (%u)", item->outsSize,
+ item->registersSize);
+ return NULL;
+ }
+
+ count = item->insnsSize;
+ insns = item->insns;
+
+ const u4 sizeOfItem = (u4) sizeof(u2);
+ CHECK_LIST_SIZE(insns, count, sizeOfItem);
+
+ while (count--) {
+ *insns = SWAP2(*insns);
+ insns++;
+ }
+
+ if (item->triesSize == 0) {
+ ptr = insns;
+ } else {
+ if ((((uintptr_t) insns) & 3) != 0) {
+ // Four-byte alignment for the tries. Verify the spacer is a 0.
+ if (*insns != 0) {
+ ALOGE("Non-zero padding: %#x", (u4) *insns);
+ return NULL;
+ }
+ }
+
+ ptr = swapTriesAndCatches(state, item);
+ }
+
+ return ptr;
+}
+
+/* Perform intra-item verification on string_data_item. */
+static void* intraVerifyStringDataItem(const CheckState* state, void* ptr) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* data = (const u1*) ptr;
+ bool okay = true;
+ u4 utf16Size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 i;
+
+ if (!okay) {
+ ALOGE("Bogus utf16_size");
+ return NULL;
+ }
+
+ for (i = 0; i < utf16Size; i++) {
+ if (data >= fileEnd) {
+ ALOGE("String data would go beyond end-of-file");
+ return NULL;
+ }
+
+ u1 byte1 = *(data++);
+
+ // Switch on the high four bits.
+ switch (byte1 >> 4) {
+ case 0x00: {
+ // Special case of bit pattern 0xxx.
+ if (byte1 == 0) {
+ ALOGE("String shorter than indicated utf16_size %#x",
+ utf16Size);
+ return NULL;
+ }
+ break;
+ }
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07: {
+ // Bit pattern 0xxx. No need for any extra bytes or checks.
+ break;
+ }
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f: {
+ /*
+ * Bit pattern 10xx or 1111, which are illegal start bytes.
+ * Note: 1111 is valid for normal UTF-8, but not the
+ * modified UTF-8 used here.
+ */
+ ALOGE("Illegal start byte %#x", byte1);
+ return NULL;
+ }
+ case 0x0e: {
+ // Bit pattern 1110, so there are two additional bytes.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ ALOGE("Illegal continuation byte %#x", byte2);
+ return NULL;
+ }
+ u1 byte3 = *(data++);
+ if ((byte3 & 0xc0) != 0x80) {
+ ALOGE("Illegal continuation byte %#x", byte3);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6)
+ | (byte3 & 0x3f);
+ if (value < 0x800) {
+ ALOGE("Illegal representation for value %x", value);
+ return NULL;
+ }
+ break;
+ }
+ case 0x0c:
+ case 0x0d: {
+ // Bit pattern 110x, so there is one additional byte.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ ALOGE("Illegal continuation byte %#x", byte2);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
+ if ((value != 0) && (value < 0x80)) {
+ ALOGE("Illegal representation for value %x", value);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ if (*(data++) != '\0') {
+ ALOGE("String longer than indicated utf16_size %#x", utf16Size);
+ return NULL;
+ }
+
+ return (void*) data;
+}
+
+/* Perform intra-item verification on debug_info_item. */
+static void* intraVerifyDebugInfoItem(const CheckState* state, void* ptr) {
+ const u1* fileEnd = state->fileEnd;
+ const u1* data = (const u1*) ptr;
+ bool okay = true;
+ u4 i;
+
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus line_start");
+ return NULL;
+ }
+
+ u4 parametersSize =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus parameters_size");
+ return NULL;
+ }
+
+ if (parametersSize > 65536) {
+ ALOGE("Invalid parameters_size: %#x", parametersSize);
+ return NULL;
+ }
+
+ for (i = 0; i < parametersSize; i++) {
+ u4 parameterName =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus parameter_name");
+ return NULL;
+ }
+
+ if (parameterName != 0) {
+ parameterName--;
+ CHECK_INDEX(parameterName, state->pHeader->stringIdsSize);
+ }
+ }
+
+ bool done = false;
+ while (!done) {
+ u1 opcode = *(data++);
+
+ switch (opcode) {
+ case DBG_END_SEQUENCE: {
+ done = true;
+ break;
+ }
+ case DBG_ADVANCE_PC: {
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ break;
+ }
+ case DBG_ADVANCE_LINE: {
+ readAndVerifySignedLeb128(&data, fileEnd, &okay);
+ break;
+ }
+ case DBG_START_LOCAL: {
+ u4 idx;
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ case DBG_END_LOCAL:
+ case DBG_RESTART_LOCAL: {
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ break;
+ }
+ case DBG_START_LOCAL_EXTENDED: {
+ u4 idx;
+ u4 regNum = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (regNum >= 65536) {
+ okay = false;
+ break;
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ case DBG_SET_FILE: {
+ u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ if (!okay) break;
+ if (idx != 0) {
+ idx--;
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ }
+ break;
+ }
+ default: {
+ // No arguments to parse for anything else.
+ }
+ }
+
+ if (!okay) {
+ ALOGE("Bogus syntax for opcode %02x", opcode);
+ return NULL;
+ }
+ }
+
+ return (void*) data;
+}
+
+/* defined below */
+static const u1* verifyEncodedValue(const CheckState* state, const u1* data,
+ bool crossVerify);
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+ const u1* data, bool crossVerify);
+
+/* Helper for verifyEncodedValue(), which reads a 1- to 4- byte unsigned
+ * little endian value. */
+static u4 readUnsignedLittleEndian(const CheckState* state, const u1** pData,
+ u4 size) {
+ const u1* data = *pData;
+ u4 result = 0;
+ u4 i;
+
+ CHECK_PTR_RANGE(data, data + size);
+
+ for (i = 0; i < size; i++) {
+ result |= ((u4) *(data++)) << (i * 8);
+ }
+
+ *pData = data;
+ return result;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_array. */
+static const u1* verifyEncodedArray(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ bool okay = true;
+ u4 size = readAndVerifyUnsignedLeb128(&data, state->fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus encoded_array size");
+ return NULL;
+ }
+
+ while (size--) {
+ data = verifyEncodedValue(state, data, crossVerify);
+ if (data == NULL) {
+ ALOGE("Bogus encoded_array value");
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+static u4 numberOfMethodHandles(const CheckState* state) {
+ if (state->pMethodHandleItems != nullptr) {
+ return state->pMethodHandleItems->size;
+ }
+ return 0;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_value. */
+static const u1* verifyEncodedValue(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ CHECK_PTR_RANGE(data, data + 1);
+
+ u1 headerByte = *(data++);
+ u4 valueType = headerByte & kDexAnnotationValueTypeMask;
+ u4 valueArg = headerByte >> kDexAnnotationValueArgShift;
+
+ switch (valueType) {
+ case kDexAnnotationByte: {
+ if (valueArg != 0) {
+ ALOGE("Bogus byte size %#x", valueArg);
+ return NULL;
+ }
+ data++;
+ break;
+ }
+ case kDexAnnotationShort:
+ case kDexAnnotationChar: {
+ if (valueArg > 1) {
+ ALOGE("Bogus char/short size %#x", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationInt:
+ case kDexAnnotationFloat: {
+ if (valueArg > 3) {
+ ALOGE("Bogus int/float size %#x", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationLong:
+ case kDexAnnotationDouble: {
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationMethodType: {
+ if (valueArg > 3) {
+ ALOGE("Bogus method type size %#x", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->protoIdsSize);
+ break;
+ }
+ case kDexAnnotationMethodHandle: {
+ if (valueArg > 3) {
+ ALOGE("Bogus method type size %#x", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, numberOfMethodHandles(state));
+ break;
+ }
+ case kDexAnnotationString: {
+ if (valueArg > 3) {
+ ALOGE("Bogus string size %#x", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ break;
+ }
+ case kDexAnnotationType: {
+ if (valueArg > 3) {
+ ALOGE("Bogus type size %#x", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+ break;
+ }
+ case kDexAnnotationField:
+ case kDexAnnotationEnum: {
+ if (valueArg > 3) {
+ ALOGE("Bogus field/enum size %#x", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->fieldIdsSize);
+ break;
+ }
+ case kDexAnnotationMethod: {
+ if (valueArg > 3) {
+ ALOGE("Bogus method size %#x", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->methodIdsSize);
+ break;
+ }
+ case kDexAnnotationArray: {
+ if (valueArg != 0) {
+ ALOGE("Bogus array value_arg %#x", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedArray(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationAnnotation: {
+ if (valueArg != 0) {
+ ALOGE("Bogus annotation value_arg %#x", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedAnnotation(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationNull: {
+ if (valueArg != 0) {
+ ALOGE("Bogus null value_arg %#x", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ case kDexAnnotationBoolean: {
+ if (valueArg > 1) {
+ ALOGE("Bogus boolean value_arg %#x", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ default: {
+ ALOGE("Bogus value_type %#x", valueType);
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Helper for *VerifyAnnotationItem() and *VerifyEncodedArrayItem(), which
+ * verifies an encoded_annotation. */
+static const u1* verifyEncodedAnnotation(const CheckState* state,
+ const u1* data, bool crossVerify) {
+ const u1* fileEnd = state->fileEnd;
+ bool okay = true;
+ u4 idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus encoded_annotation type_idx");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+
+ if (crossVerify) {
+ const char* descriptor = dexStringByTypeIdx(state->pDexFile, idx);
+ if (!dexIsClassDescriptor(descriptor)) {
+ ALOGE("Bogus annotation type: '%s'", descriptor);
+ return NULL;
+ }
+ }
+
+ u4 size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 lastIdx = 0;
+ bool first = true;
+
+ if (!okay) {
+ ALOGE("Bogus encoded_annotation size");
+ return NULL;
+ }
+
+ while (size--) {
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ ALOGE("Bogus encoded_annotation name_idx");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+
+ if (crossVerify) {
+ const char* name = dexStringById(state->pDexFile, idx);
+ if (!dexIsValidMemberName(name)) {
+ ALOGE("Bogus annotation member name: '%s'", name);
+ return NULL;
+ }
+ }
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= idx) {
+ ALOGE("Out-of-order encoded_annotation name_idx: %#x then %#x",
+ lastIdx, idx);
+ return NULL;
+ }
+
+ data = verifyEncodedValue(state, data, crossVerify);
+ lastIdx = idx;
+
+ if (data == NULL) {
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* Perform intra-item verification on encoded_array_item. */
+static void* intraVerifyEncodedArrayItem(const CheckState* state, void* ptr) {
+ return (void*) verifyEncodedArray(state, (const u1*) ptr, false);
+}
+
+/* Perform intra-item verification on annotation_item. */
+static void* intraVerifyAnnotationItem(const CheckState* state, void* ptr) {
+ const u1* data = (const u1*) ptr;
+
+ CHECK_PTR_RANGE(data, data + 1);
+
+ switch (*(data++)) {
+ case kDexVisibilityBuild:
+ case kDexVisibilityRuntime:
+ case kDexVisibilitySystem: {
+ break;
+ }
+ default: {
+ ALOGE("Bogus annotation visibility: %#x", *data);
+ return NULL;
+ }
+ }
+
+ return (void*) verifyEncodedAnnotation(state, data, false);
+}
+
+/*
+ * Function to visit an individual top-level item type.
+ */
+typedef void* ItemVisitorFunction(const CheckState* state, void* ptr);
+
+/*
+ * Iterate over all the items in a section, optionally updating the
+ * data map (done if mapType is passed as non-negative). The section
+ * must consist of concatenated items of the same type.
+ */
+static bool iterateSectionWithOptionalUpdate(CheckState* state,
+ u4 offset, u4 count, ItemVisitorFunction* func, u4 alignment,
+ u4* nextOffset, int mapType) {
+ u4 alignmentMask = alignment - 1;
+ u4 i;
+
+ state->previousItem = NULL;
+
+ for (i = 0; i < count; i++) {
+ u4 newOffset = (offset + alignmentMask) & ~alignmentMask;
+ u1* ptr = (u1*) filePointer(state, newOffset);
+
+ if (offset < newOffset) {
+ ptr = (u1*) filePointer(state, offset);
+ if (offset < newOffset) {
+ CHECK_OFFSET_RANGE(offset, newOffset);
+ while (offset < newOffset) {
+ if (*ptr != '\0') {
+ ALOGE("Non-zero padding 0x%02x @ %x", *ptr, offset);
+ return false;
+ }
+ ptr++;
+ offset++;
+ }
+ }
+ }
+
+ u1* newPtr = (u1*) func(state, ptr);
+ newOffset = fileOffset(state, newPtr);
+
+ if (newPtr == NULL) {
+ ALOGE("Trouble with item %d @ offset %#x", i, offset);
+ return false;
+ }
+
+ if (newOffset > state->fileLen) {
+ ALOGE("Item %d @ offset %#x ends out of bounds", i, offset);
+ return false;
+ }
+
+ if (mapType >= 0) {
+ dexDataMapAdd(state->pDataMap, offset, mapType);
+ }
+
+ state->previousItem = ptr;
+ offset = newOffset;
+ }
+
+ if (nextOffset != NULL) {
+ *nextOffset = offset;
+ }
+
+ return true;
+}
+
+/*
+ * Iterate over all the items in a section. The section must consist of
+ * concatenated items of the same type. This variant will not update the data
+ * map.
+ */
+static bool iterateSection(CheckState* state, u4 offset, u4 count,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+ return iterateSectionWithOptionalUpdate(state, offset, count, func,
+ alignment, nextOffset, -1);
+}
+
+/*
+ * Like iterateSection(), but also check that the offset and count match
+ * a given pair of expected values.
+ */
+static bool checkBoundsAndIterateSection(CheckState* state,
+ u4 offset, u4 count, u4 expectedOffset, u4 expectedCount,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset) {
+ if (offset != expectedOffset) {
+ ALOGE("Bogus offset for section: got %#x; expected %#x",
+ offset, expectedOffset);
+ return false;
+ }
+
+ if (count != expectedCount) {
+ ALOGE("Bogus size for section: got %#x; expected %#x",
+ count, expectedCount);
+ return false;
+ }
+
+ return iterateSection(state, offset, count, func, alignment, nextOffset);
+}
+
+/*
+ * Like iterateSection(), but also update the data section map and
+ * check that all the items fall within the data section.
+ */
+static bool iterateDataSection(CheckState* state, u4 offset, u4 count,
+ ItemVisitorFunction* func, u4 alignment, u4* nextOffset, int mapType) {
+ u4 dataStart = state->pHeader->dataOff;
+ u4 dataEnd = dataStart + state->pHeader->dataSize;
+
+ assert(nextOffset != NULL);
+
+ if ((offset < dataStart) || (offset >= dataEnd)) {
+ ALOGE("Bogus offset for data subsection: %#x", offset);
+ return false;
+ }
+
+ if (!iterateSectionWithOptionalUpdate(state, offset, count, func,
+ alignment, nextOffset, mapType)) {
+ return false;
+ }
+
+ if (*nextOffset > dataEnd) {
+ ALOGE("Out-of-bounds end of data subsection: %#x", *nextOffset);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Byte-swap all items in the given map except the header and the map
+ * itself, both of which should have already gotten swapped. This also
+ * does all possible intra-item verification, that is, verification
+ * that doesn't need to assume the sanctity of the contents of *other*
+ * items. The intra-item limitation is because at the time an item is
+ * asked to verify itself, it can't assume that the items it refers to
+ * have been byte-swapped and verified.
+ */
+static bool swapEverythingButHeaderAndMap(CheckState* state,
+ DexMapList* pMap) {
+ const DexMapItem* item = pMap->list;
+ u4 lastOffset = 0;
+ u4 count = pMap->size;
+ bool okay = true;
+
+ while (okay && count--) {
+ u4 sectionOffset = item->offset;
+ u4 sectionCount = item->size;
+ u2 type = item->type;
+
+ if (lastOffset < sectionOffset) {
+ CHECK_OFFSET_RANGE(lastOffset, sectionOffset);
+ const u1* ptr = (const u1*) filePointer(state, lastOffset);
+ while (lastOffset < sectionOffset) {
+ if (*ptr != '\0') {
+ ALOGE("Non-zero padding 0x%02x before section start @ %x",
+ *ptr, lastOffset);
+ okay = false;
+ break;
+ }
+ ptr++;
+ lastOffset++;
+ }
+ } else if (lastOffset > sectionOffset) {
+ ALOGE("Section overlap or out-of-order map: %x, %x",
+ lastOffset, sectionOffset);
+ okay = false;
+ }
+
+ if (!okay) {
+ break;
+ }
+
+ switch (type) {
+ case kDexTypeHeaderItem: {
+ /*
+ * The header got swapped very early on, but do some
+ * additional sanity checking here.
+ */
+ okay = checkHeaderSection(state, sectionOffset, sectionCount,
+ &lastOffset);
+ break;
+ }
+ case kDexTypeStringIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->stringIdsOff,
+ state->pHeader->stringIdsSize, swapStringIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeTypeIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->typeIdsOff,
+ state->pHeader->typeIdsSize, swapTypeIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeProtoIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->protoIdsOff,
+ state->pHeader->protoIdsSize, swapProtoIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeFieldIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->fieldIdsOff,
+ state->pHeader->fieldIdsSize, swapFieldIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeMethodIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->methodIdsOff,
+ state->pHeader->methodIdsSize, swapMethodIdItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeClassDefItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, state->pHeader->classDefsOff,
+ state->pHeader->classDefsSize, swapClassDefItem,
+ sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeCallSiteIdItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, sectionOffset, sectionCount,
+ swapCallSiteId, sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeMethodHandleItem: {
+ okay = checkBoundsAndIterateSection(state, sectionOffset,
+ sectionCount, sectionOffset, sectionCount,
+ swapMethodHandleItem, sizeof(u4), &lastOffset);
+ break;
+ }
+ case kDexTypeMapList: {
+ /*
+ * The map section was swapped early on, but do some
+ * additional sanity checking here.
+ */
+ okay = checkMapSection(state, sectionOffset, sectionCount,
+ &lastOffset);
+ break;
+ }
+ case kDexTypeTypeList: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapTypeList, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeAnnotationSetRefList: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationSetRefList, sizeof(u4), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationSetItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationSetItem, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeClassDataItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyClassDataItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeCodeItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapCodeItem, sizeof(u4), &lastOffset, type);
+ break;
+ }
+ case kDexTypeStringDataItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyStringDataItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeDebugInfoItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyDebugInfoItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyAnnotationItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeEncodedArrayItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ intraVerifyEncodedArrayItem, sizeof(u1), &lastOffset,
+ type);
+ break;
+ }
+ case kDexTypeAnnotationsDirectoryItem: {
+ okay = iterateDataSection(state, sectionOffset, sectionCount,
+ swapAnnotationsDirectoryItem, sizeof(u4), &lastOffset,
+ type);
+ break;
+ }
+ default: {
+ ALOGE("Unknown map item type %04x", type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ ALOGE("Swap of section type %04x failed", type);
+ }
+
+ item++;
+ }
+
+ return okay;
+}
+
+/*
+ * Perform cross-item verification on everything that needs it. This
+ * pass is only called after all items are byte-swapped and
+ * intra-verified (checked for internal consistency).
+ */
+static bool crossVerifyEverything(CheckState* state, DexMapList* pMap)
+{
+ const DexMapItem* item = pMap->list;
+ u4 count = pMap->size;
+ bool okay = true;
+
+ while (okay && count--) {
+ u4 sectionOffset = item->offset;
+ u4 sectionCount = item->size;
+
+ switch (item->type) {
+ case kDexTypeHeaderItem:
+ case kDexTypeMapList:
+ case kDexTypeTypeList:
+ case kDexTypeCodeItem:
+ case kDexTypeStringDataItem:
+ case kDexTypeDebugInfoItem:
+ case kDexTypeAnnotationItem:
+ case kDexTypeEncodedArrayItem: {
+ // There is no need for cross-item verification for these.
+ break;
+ }
+ case kDexTypeStringIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyStringIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeTypeIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyTypeIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeProtoIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyProtoIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeFieldIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyFieldIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeMethodIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyMethodIdItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeClassDefItem: {
+ // Allocate (on the stack) the "observed class_def" bits.
+ size_t arraySize = calcDefinedClassBitsSize(state);
+ u4 definedClassBits[arraySize];
+ memset(definedClassBits, 0, arraySize * sizeof(u4));
+ state->pDefinedClassBits = definedClassBits;
+
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyClassDefItem, sizeof(u4), NULL);
+
+ state->pDefinedClassBits = NULL;
+ break;
+ }
+ case kDexTypeCallSiteIdItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyCallSiteId, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeMethodHandleItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyMethodHandleItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeAnnotationSetRefList: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationSetRefList, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeAnnotationSetItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationSetItem, sizeof(u4), NULL);
+ break;
+ }
+ case kDexTypeClassDataItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyClassDataItem, sizeof(u1), NULL);
+ break;
+ }
+ case kDexTypeAnnotationsDirectoryItem: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyAnnotationsDirectoryItem, sizeof(u4), NULL);
+ break;
+ }
+ default: {
+ ALOGE("Unknown map item type %04x", item->type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ ALOGE("Cross-item verify of section type %04x failed",
+ item->type);
+ }
+
+ item++;
+ }
+
+ return okay;
+}
+
+/* (documented in header file) */
+bool dexHasValidMagic(const DexHeader* pHeader)
+{
+ const u1* magic = pHeader->magic;
+ const u1* version = &magic[4];
+
+ if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+ ALOGE("ERROR: unrecognized magic number (%02x %02x %02x %02x)",
+ magic[0], magic[1], magic[2], magic[3]);
+ return false;
+ }
+
+ if ((memcmp(version, DEX_MAGIC_VERS, 4) != 0) &&
+ (memcmp(version, DEX_MAGIC_VERS_API_13, 4) != 0) &&
+ (memcmp(version, DEX_MAGIC_VERS_37, 4) != 0) &&
+ (memcmp(version, DEX_MAGIC_VERS_38, 4) != 0) &&
+ (memcmp(version, DEX_MAGIC_VERS_39, 4) != 0)) {
+ /*
+ * Magic was correct, but this is an unsupported older or
+ * newer format variant.
+ */
+ ALOGE("ERROR: unsupported dex version (%02x %02x %02x %02x)",
+ version[0], version[1], version[2], version[3]);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do
+ * structural verification. This is only required for code that opens
+ * "raw" DEX files, such as the DEX optimizer.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerify(u1* addr, size_t len)
+{
+ DexHeader* pHeader;
+ CheckState state;
+ bool okay = true;
+
+ memset(&state, 0, sizeof(state));
+ ALOGV("+++ swapping and verifying");
+
+ /*
+ * Note: The caller must have verified that "len" is at least as
+ * large as a dex file header.
+ */
+ pHeader = (DexHeader*) addr;
+
+ if (!dexHasValidMagic(pHeader)) {
+ okay = false;
+ }
+
+ if (okay) {
+ u4 expectedLen = SWAP4(pHeader->fileSize);
+ if (len != expectedLen) {
+ ALOGE("ERROR: Bad length: expected %u, got %zu", expectedLen, len);
+ okay = false;
+ }
+ }
+
+ if (okay) {
+ /*
+ * Compute the adler32 checksum and compare it to what's stored in
+ * the file. This isn't free, but chances are good that we just
+ * unpacked this from a jar file and have all of the pages sitting
+ * in memory, so it's pretty quick.
+ *
+ * This might be a big-endian system, so we need to do this before
+ * we byte-swap the header.
+ */
+ uLong adler = adler32(0L, Z_NULL, 0);
+ const int nonSum = sizeof(pHeader->magic) + sizeof(pHeader->checksum);
+ u4 storedFileSize = SWAP4(pHeader->fileSize);
+ u4 expectedChecksum = SWAP4(pHeader->checksum);
+
+ adler = adler32(adler, ((const u1*) pHeader) + nonSum,
+ storedFileSize - nonSum);
+
+ if (adler != expectedChecksum) {
+ ALOGE("ERROR: bad checksum (%08lx, expected %08x)",
+ adler, expectedChecksum);
+ okay = false;
+ }
+ }
+
+ if (okay) {
+ state.fileStart = addr;
+ state.fileEnd = addr + len;
+ state.fileLen = len;
+ state.pDexFile = NULL;
+ state.pDataMap = NULL;
+ state.pDefinedClassBits = NULL;
+ state.previousItem = NULL;
+
+ /*
+ * Swap the header and check the contents.
+ */
+ okay = swapDexHeader(&state, pHeader);
+ }
+
+ if (okay) {
+ state.pHeader = pHeader;
+
+ if (pHeader->headerSize < sizeof(DexHeader)) {
+ ALOGE("ERROR: Small header size %d, struct %d",
+ pHeader->headerSize, (int) sizeof(DexHeader));
+ okay = false;
+ } else if (pHeader->headerSize > sizeof(DexHeader)) {
+ ALOGW("WARNING: Large header size %d, struct %d",
+ pHeader->headerSize, (int) sizeof(DexHeader));
+ // keep going?
+ }
+ }
+
+ if (okay) {
+ /*
+ * Look for the map. Swap it and then use it to find and swap
+ * everything else.
+ */
+ if (pHeader->mapOff != 0) {
+ DexFile dexFile;
+ DexMapList* pDexMap = (DexMapList*) (addr + pHeader->mapOff);
+
+ okay = okay && swapMap(&state, pDexMap);
+ okay = okay && swapEverythingButHeaderAndMap(&state, pDexMap);
+
+ dexFileSetupBasicPointers(&dexFile, addr);
+ state.pDexFile = &dexFile;
+
+ okay = okay && crossVerifyEverything(&state, pDexMap);
+ } else {
+ ALOGE("ERROR: No map found; impossible to byte-swap and verify");
+ okay = false;
+ }
+ }
+
+ if (!okay) {
+ ALOGE("ERROR: Byte swap + verify failed");
+ }
+
+ if (state.pDataMap != NULL) {
+ dexDataMapFree(state.pDataMap);
+ }
+
+ return !okay; // 0 == success
+}
+
+/*
+ * Detect the file type of the given memory buffer via magic number.
+ * Call dexSwapAndVerify() on an unoptimized DEX file, do nothing
+ * but return successfully on an optimized DEX file, and report an
+ * error for all other cases.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexSwapAndVerifyIfNecessary(u1* addr, size_t len)
+{
+ if (memcmp(addr, DEX_OPT_MAGIC, 4) == 0) {
+ // It is an optimized dex file.
+ return 0;
+ }
+
+ if (memcmp(addr, DEX_MAGIC, 4) == 0) {
+ // It is an unoptimized dex file.
+ return dexSwapAndVerify(addr, len);
+ }
+
+ ALOGE("ERROR: Bad magic number (0x%02x %02x %02x %02x)",
+ addr[0], addr[1], addr[2], addr[3]);
+
+ return 1;
+}
diff --git a/libdex/DexUtf.cpp b/libdex/DexUtf.cpp
new file mode 100644
index 000000000..df49d1831
--- /dev/null
+++ b/libdex/DexUtf.cpp
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Validate and manipulate MUTF-8 encoded string data.
+ */
+
+#include "DexUtf.h"
+
+/* Compare two '\0'-terminated modified UTF-8 strings, using Unicode
+ * code point values for comparison. This treats different encodings
+ * for the same code point as equivalent, except that only a real '\0'
+ * byte is considered the string terminator. The return value is as
+ * for strcmp(). */
+int dexUtf8Cmp(const char* s1, const char* s2) {
+ for (;;) {
+ if (*s1 == '\0') {
+ if (*s2 == '\0') {
+ return 0;
+ }
+ return -1;
+ } else if (*s2 == '\0') {
+ return 1;
+ }
+
+ int utf1 = dexGetUtf16FromUtf8(&s1);
+ int utf2 = dexGetUtf16FromUtf8(&s2);
+ int diff = utf1 - utf2;
+
+ if (diff != 0) {
+ return diff;
+ }
+ }
+}
+
+/* for dexIsValidMemberNameUtf8(), a bit vector indicating valid low ascii */
+u4 DEX_MEMBER_VALID_LOW_ASCII[4] = {
+ 0x00000000, // 00..1f low control characters; nothing valid
+ 0x03ff2010, // 20..3f digits and symbols; valid: '0'..'9', '$', '-'
+ 0x87fffffe, // 40..5f uppercase etc.; valid: 'A'..'Z', '_'
+ 0x07fffffe // 60..7f lowercase etc.; valid: 'a'..'z'
+};
+
+/* Helper for dexIsValidMemberNameUtf8(); do not call directly. */
+bool dexIsValidMemberNameUtf8_0(const char** pUtf8Ptr) {
+ /*
+ * It's a multibyte encoded character. Decode it and analyze. We
+ * accept anything that isn't (a) an improperly encoded low value,
+ * (b) an improper surrogate pair, (c) an encoded '\0', (d) a high
+ * control character, or (e) a high space, layout, or special
+ * character (U+00a0, U+2000..U+200f, U+2028..U+202f,
+ * U+fff0..U+ffff). This is all specified in the dex format
+ * document.
+ */
+
+ u2 utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+
+ // Perform follow-up tests based on the high 8 bits.
+ switch (utf16 >> 8) {
+ case 0x00: {
+ // It's only valid if it's above the ISO-8859-1 high space (0xa0).
+ return (utf16 > 0x00a0);
+ }
+ case 0xd8:
+ case 0xd9:
+ case 0xda:
+ case 0xdb: {
+ /*
+ * It's a leading surrogate. Check to see that a trailing
+ * surrogate follows.
+ */
+ utf16 = dexGetUtf16FromUtf8(pUtf8Ptr);
+ return (utf16 >= 0xdc00) && (utf16 <= 0xdfff);
+ }
+ case 0xdc:
+ case 0xdd:
+ case 0xde:
+ case 0xdf: {
+ // It's a trailing surrogate, which is not valid at this point.
+ return false;
+ }
+ case 0x20:
+ case 0xff: {
+ // It's in the range that has spaces, controls, and specials.
+ switch (utf16 & 0xfff8) {
+ case 0x2000:
+ case 0x2008:
+ case 0x2028:
+ case 0xfff0:
+ case 0xfff8: {
+ return false;
+ }
+ }
+ break;
+ }
+ }
+
+ return true;
+}
+
+/* Return whether the given string is a valid field or method name. */
+bool dexIsValidMemberName(const char* s) {
+ bool angleName = false;
+
+ switch (*s) {
+ case '\0': {
+ // The empty string is not a valid name.
+ return false;
+ }
+ case '<': {
+ /*
+ * '<' is allowed only at the start of a name, and if present,
+ * means that the name must end with '>'.
+ */
+ angleName = true;
+ s++;
+ break;
+ }
+ }
+
+ for (;;) {
+ switch (*s) {
+ case '\0': {
+ return !angleName;
+ }
+ case '>': {
+ return angleName && s[1] == '\0';
+ }
+ }
+ if (!dexIsValidMemberNameUtf8(&s)) {
+ return false;
+ }
+ }
+}
+
+/* Helper for validating type descriptors and class names, which is parametric
+ * with respect to type vs. class and dot vs. slash. */
+static bool isValidTypeDescriptorOrClassName(const char* s, bool isClassName,
+ bool dotSeparator) {
+ int arrayCount = 0;
+
+ while (*s == '[') {
+ arrayCount++;
+ s++;
+ }
+
+ if (arrayCount > 255) {
+ // Arrays may have no more than 255 dimensions.
+ return false;
+ }
+
+ if (arrayCount != 0) {
+ /*
+ * If we're looking at an array of some sort, then it doesn't
+ * matter if what is being asked for is a class name; the
+ * format looks the same as a type descriptor in that case, so
+ * treat it as such.
+ */
+ isClassName = false;
+ }
+
+ if (!isClassName) {
+ /*
+ * We are looking for a descriptor. Either validate it as a
+ * single-character primitive type, or continue on to check the
+ * embedded class name (bracketed by "L" and ";").
+ */
+ switch (*(s++)) {
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z': {
+ // These are all single-character descriptors for primitive types.
+ return (*s == '\0');
+ }
+ case 'V': {
+ // Non-array void is valid, but you can't have an array of void.
+ return (arrayCount == 0) && (*s == '\0');
+ }
+ case 'L': {
+ // Class name: Break out and continue below.
+ break;
+ }
+ default: {
+ // Oddball descriptor character.
+ return false;
+ }
+ }
+ }
+
+ /*
+ * We just consumed the 'L' that introduces a class name as part
+ * of a type descriptor, or we are looking for an unadorned class
+ * name.
+ */
+
+ bool sepOrFirst = true; // first character or just encountered a separator.
+ for (;;) {
+ u1 c = (u1) *s;
+ switch (c) {
+ case '\0': {
+ /*
+ * Premature end for a type descriptor, but valid for
+ * a class name as long as we haven't encountered an
+ * empty component (including the degenerate case of
+ * the empty string "").
+ */
+ return isClassName && !sepOrFirst;
+ }
+ case ';': {
+ /*
+ * Invalid character for a class name, but the
+ * legitimate end of a type descriptor. In the latter
+ * case, make sure that this is the end of the string
+ * and that it doesn't end with an empty component
+ * (including the degenerate case of "L;").
+ */
+ return !isClassName && !sepOrFirst && (s[1] == '\0');
+ }
+ case '/':
+ case '.': {
+ if (dotSeparator != (c == '.')) {
+ // The wrong separator character.
+ return false;
+ }
+ if (sepOrFirst) {
+ // Separator at start or two separators in a row.
+ return false;
+ }
+ sepOrFirst = true;
+ s++;
+ break;
+ }
+ default: {
+ if (!dexIsValidMemberNameUtf8(&s)) {
+ return false;
+ }
+ sepOrFirst = false;
+ break;
+ }
+ }
+ }
+}
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s) {
+ return isValidTypeDescriptorOrClassName(s, false, false);
+}
+
+/* (documented in header) */
+bool dexIsValidClassName(const char* s, bool dotSeparator) {
+ return isValidTypeDescriptorOrClassName(s, true, dotSeparator);
+}
+
+/* Return whether the given string is a valid reference descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class or array and not a primitive type. */
+bool dexIsReferenceDescriptor(const char* s) {
+ if (!dexIsValidTypeDescriptor(s)) {
+ return false;
+ }
+
+ return (s[0] == 'L') || (s[0] == '[');
+}
+
+/* Return whether the given string is a valid class descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class and not an array or primitive type. */
+bool dexIsClassDescriptor(const char* s) {
+ if (!dexIsValidTypeDescriptor(s)) {
+ return false;
+ }
+
+ return s[0] == 'L';
+}
+
+/* Return whether the given string is a valid field type descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for anything but "void". */
+bool dexIsFieldDescriptor(const char* s) {
+ if (!dexIsValidTypeDescriptor(s)) {
+ return false;
+ }
+
+ return s[0] != 'V';
+}
+
diff --git a/libdex/DexUtf.h b/libdex/DexUtf.h
new file mode 100644
index 000000000..cb3d919ae
--- /dev/null
+++ b/libdex/DexUtf.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Validate and manipulate MUTF-8 (modified UTF-8) encoded string data.
+ */
+
+#ifndef LIBDEX_DEXUTF_H_
+#define LIBDEX_DEXUTF_H_
+
+#include "DexFile.h"
+
+/*
+ * Retrieve the next UTF-16 character from a UTF-8 string.
+ *
+ * Advances "*pUtf8Ptr" to the start of the next character.
+ *
+ * WARNING: If a string is corrupted by dropping a '\0' in the middle
+ * of a 3-byte sequence, you can end up overrunning the buffer with
+ * reads (and possibly with the writes if the length was computed and
+ * cached before the damage). For performance reasons, this function
+ * assumes that the string being parsed is known to be valid (e.g., by
+ * already being verified). Most strings we process here are coming
+ * out of dex files or other internal translations, so the only real
+ * risk comes from the JNI NewStringUTF call.
+ */
+DEX_INLINE u2 dexGetUtf16FromUtf8(const char** pUtf8Ptr)
+{
+ unsigned int one, two, three;
+
+ one = *(*pUtf8Ptr)++;
+ if ((one & 0x80) != 0) {
+ /* two- or three-byte encoding */
+ two = *(*pUtf8Ptr)++;
+ if ((one & 0x20) != 0) {
+ /* three-byte encoding */
+ three = *(*pUtf8Ptr)++;
+ return ((one & 0x0f) << 12) |
+ ((two & 0x3f) << 6) |
+ (three & 0x3f);
+ } else {
+ /* two-byte encoding */
+ return ((one & 0x1f) << 6) |
+ (two & 0x3f);
+ }
+ } else {
+ /* one-byte encoding */
+ return one;
+ }
+}
+
+/* Compare two '\0'-terminated modified UTF-8 strings, using Unicode
+ * code point values for comparison. This treats different encodings
+ * for the same code point as equivalent, except that only a real '\0'
+ * byte is considered the string terminator. The return value is as
+ * for strcmp(). */
+int dexUtf8Cmp(const char* s1, const char* s2);
+
+/* for dexIsValidMemberNameUtf8(), a bit vector indicating valid low ascii */
+extern u4 DEX_MEMBER_VALID_LOW_ASCII[4];
+
+/* Helper for dexIsValidMemberUtf8(); do not call directly. */
+bool dexIsValidMemberNameUtf8_0(const char** pUtf8Ptr);
+
+/* Return whether the pointed-at modified-UTF-8 encoded character is
+ * valid as part of a member name, updating the pointer to point past
+ * the consumed character. This will consume two encoded UTF-16 code
+ * points if the character is encoded as a surrogate pair. Also, if
+ * this function returns false, then the given pointer may only have
+ * been partially advanced. */
+DEX_INLINE bool dexIsValidMemberNameUtf8(const char** pUtf8Ptr) {
+ u1 c = (u1) **pUtf8Ptr;
+ if (c <= 0x7f) {
+ // It's low-ascii, so check the table.
+ u4 wordIdx = c >> 5;
+ u4 bitIdx = c & 0x1f;
+ (*pUtf8Ptr)++;
+ return (DEX_MEMBER_VALID_LOW_ASCII[wordIdx] & (1 << bitIdx)) != 0;
+ }
+
+ /*
+ * It's a multibyte encoded character. Call a non-inline function
+ * for the heavy lifting.
+ */
+ return dexIsValidMemberNameUtf8_0(pUtf8Ptr);
+}
+
+/* Return whether the given string is a valid field or method name. */
+bool dexIsValidMemberName(const char* s);
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s);
+
+/* Return whether the given string is a valid internal-form class
+ * name, with components separated either by dots or slashes as
+ * specified. A class name is like a type descriptor, except that it
+ * can't name a primitive type (including void). In terms of syntax,
+ * the form is either (a) the name of the class without adornment
+ * (that is, not bracketed by "L" and ";"); or (b) identical to the
+ * type descriptor syntax for array types. */
+bool dexIsValidClassName(const char* s, bool dotSeparator);
+
+/* Return whether the given string is a valid reference descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class or array and not a primitive type. */
+bool dexIsReferenceDescriptor(const char* s);
+
+/* Return whether the given string is a valid class descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for a class and not an array or primitive type. */
+bool dexIsClassDescriptor(const char* s);
+
+/* Return whether the given string is a valid field type descriptor. This
+ * is true if dexIsValidTypeDescriptor() returns true and the descriptor
+ * is for anything but "void". */
+bool dexIsFieldDescriptor(const char* s);
+
+#endif // LIBDEX_DEXUTF_H_
diff --git a/libdex/InstrUtils.cpp b/libdex/InstrUtils.cpp
new file mode 100644
index 000000000..56f5dd80b
--- /dev/null
+++ b/libdex/InstrUtils.cpp
@@ -0,0 +1,702 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik instruction utility functions.
+ *
+ * IMPORTANT NOTE: Much of the contents of this file are generated
+ * automatically by the opcode-gen tool. Any edits to the generated
+ * sections will get wiped out the next time the tool is run.
+ */
+
+#include "InstrUtils.h"
+#include <stdlib.h>
+
+/*
+ * Table that maps each opcode to the full width of instructions that
+ * use that opcode, in (16-bit) code units. Unimplemented opcodes as
+ * well as the "breakpoint" opcode have a width of zero.
+ */
+static InstructionWidth gInstructionWidthTable[kNumPackedOpcodes] = {
+ // BEGIN(libdex-widths); GENERATED AUTOMATICALLY BY opcode-gen
+ 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 2, 3, 2, 2, 3, 5, 2, 2, 3, 2, 1, 1, 2,
+ 2, 1, 2, 2, 3, 3, 3, 1, 1, 2, 3, 3, 3, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0,
+ 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3,
+ 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 3, 3,
+ 3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 4, 4, 3, 3, 2, 2,
+ // END(libdex-widths)
+};
+
+/*
+ * Table that maps each opcode to the flags associated with that
+ * opcode.
+ */
+static u1 gOpcodeFlagsTable[kNumPackedOpcodes] = {
+ // BEGIN(libdex-flags); GENERATED AUTOMATICALLY BY opcode-gen
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanReturn,
+ kInstrCanReturn,
+ kInstrCanReturn,
+ kInstrCanReturn,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue,
+ kInstrCanThrow,
+ kInstrCanBranch,
+ kInstrCanBranch,
+ kInstrCanBranch,
+ kInstrCanContinue|kInstrCanSwitch,
+ kInstrCanContinue|kInstrCanSwitch,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ kInstrCanContinue|kInstrCanBranch,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ 0,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ 0,
+ 0,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ 0,
+ kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanReturn,
+ kInstrCanContinue|kInstrCanThrow,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+ kInstrCanContinue|kInstrCanThrow,
+ kInstrCanContinue|kInstrCanThrow,
+ // END(libdex-flags)
+};
+
+/*
+ * Table that maps each opcode to the instruction format associated
+ * that opcode.
+ */
+static u1 gInstructionFormatTable[kNumPackedOpcodes] = {
+ // BEGIN(libdex-formats); GENERATED AUTOMATICALLY BY opcode-gen
+ kFmt10x, kFmt12x, kFmt22x, kFmt32x, kFmt12x, kFmt22x, kFmt32x,
+ kFmt12x, kFmt22x, kFmt32x, kFmt11x, kFmt11x, kFmt11x, kFmt11x,
+ kFmt10x, kFmt11x, kFmt11x, kFmt11x, kFmt11n, kFmt21s, kFmt31i,
+ kFmt21h, kFmt21s, kFmt31i, kFmt51l, kFmt21h, kFmt21c, kFmt31c,
+ kFmt21c, kFmt11x, kFmt11x, kFmt21c, kFmt22c, kFmt12x, kFmt21c,
+ kFmt22c, kFmt35c, kFmt3rc, kFmt31t, kFmt11x, kFmt10t, kFmt20t,
+ kFmt30t, kFmt31t, kFmt31t, kFmt23x, kFmt23x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt22t, kFmt22t, kFmt22t, kFmt22t, kFmt22t, kFmt22t,
+ kFmt21t, kFmt21t, kFmt21t, kFmt21t, kFmt21t, kFmt21t, kFmt00x,
+ kFmt00x, kFmt00x, kFmt00x, kFmt00x, kFmt00x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt22c, kFmt22c,
+ kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c,
+ kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt22c, kFmt21c, kFmt21c,
+ kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c,
+ kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt21c, kFmt35c, kFmt35c,
+ kFmt35c, kFmt35c, kFmt35c, kFmt00x, kFmt3rc, kFmt3rc, kFmt3rc,
+ kFmt3rc, kFmt3rc, kFmt00x, kFmt00x, kFmt12x, kFmt12x, kFmt12x,
+ kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x,
+ kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x,
+ kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt23x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x, kFmt23x,
+ kFmt23x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x,
+ kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x,
+ kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x,
+ kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x,
+ kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt12x, kFmt22s, kFmt22s,
+ kFmt22s, kFmt22s, kFmt22s, kFmt22s, kFmt22s, kFmt22s, kFmt22b,
+ kFmt22b, kFmt22b, kFmt22b, kFmt22b, kFmt22b, kFmt22b, kFmt22b,
+ kFmt22b, kFmt22b, kFmt22b, kFmt22c, kFmt22c, kFmt21c, kFmt21c,
+ kFmt22c, kFmt22c, kFmt22c, kFmt21c, kFmt21c, kFmt00x, kFmt20bc,
+ kFmt35mi, kFmt3rmi, kFmt35c, kFmt10x, kFmt22cs, kFmt00x, kFmt00x,
+ kFmt00x, kFmt00x, kFmt00x, kFmt00x, kFmt00x, kFmt45cc, kFmt4rcc,
+ kFmt35c, kFmt3rc, kFmt21c, kFmt21c,
+ // END(libdex-formats)
+};
+
+/*
+ * Table that maps each opcode to the index type implied by that
+ * opcode.
+ */
+static u1 gInstructionIndexTypeTable[kNumPackedOpcodes] = {
+ // BEGIN(libdex-index-types); GENERATED AUTOMATICALLY BY opcode-gen
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexStringRef,
+ kIndexStringRef, kIndexTypeRef, kIndexNone,
+ kIndexNone, kIndexTypeRef, kIndexTypeRef,
+ kIndexNone, kIndexTypeRef, kIndexTypeRef,
+ kIndexTypeRef, kIndexTypeRef, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexUnknown,
+ kIndexUnknown, kIndexUnknown, kIndexUnknown,
+ kIndexUnknown, kIndexUnknown, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexMethodRef,
+ kIndexMethodRef, kIndexMethodRef, kIndexMethodRef,
+ kIndexMethodRef, kIndexUnknown, kIndexMethodRef,
+ kIndexMethodRef, kIndexMethodRef, kIndexMethodRef,
+ kIndexMethodRef, kIndexUnknown, kIndexUnknown,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexNone,
+ kIndexNone, kIndexNone, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexFieldRef,
+ kIndexFieldRef, kIndexFieldRef, kIndexUnknown,
+ kIndexVaries, kIndexInlineMethod, kIndexInlineMethod,
+ kIndexMethodRef, kIndexNone, kIndexFieldOffset,
+ kIndexUnknown, kIndexUnknown, kIndexUnknown,
+ kIndexUnknown, kIndexUnknown, kIndexUnknown,
+ kIndexUnknown, kIndexMethodAndProtoRef, kIndexMethodAndProtoRef,
+ kIndexCallSiteRef, kIndexCallSiteRef, kIndexMethodHandleRef,
+ kIndexProtoRef,
+ // END(libdex-index-types)
+};
+
+/*
+ * Global InstructionInfoTables struct.
+ */
+InstructionInfoTables gDexOpcodeInfo = {
+ gInstructionFormatTable,
+ gInstructionIndexTypeTable,
+ gOpcodeFlagsTable,
+ gInstructionWidthTable
+};
+
+/*
+ * Handy macros for helping decode instructions.
+ */
+#define FETCH(_offset) (insns[(_offset)])
+#define FETCH_u4(_offset) (fetch_u4_impl((_offset), insns))
+#define INST_A(_inst) (((u2)(_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((u2)(_inst) >> 12)
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/* Helper for FETCH_u4, above. */
+static inline u4 fetch_u4_impl(u4 offset, const u2* insns) {
+ return insns[offset] | ((u4) insns[offset+1] << 16);
+}
+
+/*
+ * Decode the instruction pointed to by "insns".
+ *
+ * Fills out the pieces of "pDec" that are affected by the current
+ * instruction. Does not touch anything else.
+ */
+void dexDecodeInstruction(const u2* insns, DecodedInstruction* pDec)
+{
+ u2 inst = *insns;
+ Opcode opcode = dexOpcodeFromCodeUnit(inst);
+ InstructionFormat format = dexGetFormatFromOpcode(opcode);
+
+ pDec->opcode = opcode;
+ pDec->indexType = dexGetIndexTypeFromOpcode(opcode);
+
+ switch (format) {
+ case kFmt10x: // op
+ /* nothing to do; copy the AA bits out for the verifier */
+ pDec->vA = INST_AA(inst);
+ break;
+ case kFmt12x: // op vA, vB
+ pDec->vA = INST_A(inst);
+ pDec->vB = INST_B(inst);
+ break;
+ case kFmt11n: // op vA, #+B
+ pDec->vA = INST_A(inst);
+ pDec->vB = (s4) (INST_B(inst) << 28) >> 28; // sign extend 4-bit value
+ break;
+ case kFmt11x: // op vAA
+ pDec->vA = INST_AA(inst);
+ break;
+ case kFmt10t: // op +AA
+ pDec->vA = (s1) INST_AA(inst); // sign-extend 8-bit value
+ break;
+ case kFmt20t: // op +AAAA
+ pDec->vA = (s2) FETCH(1); // sign-extend 16-bit value
+ break;
+ case kFmt20bc: // [opt] op AA, thing@BBBB
+ case kFmt21c: // op vAA, thing@BBBB
+ case kFmt22x: // op vAA, vBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1);
+ break;
+ case kFmt21s: // op vAA, #+BBBB
+ case kFmt21t: // op vAA, +BBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = (s2) FETCH(1); // sign-extend 16-bit value
+ break;
+ case kFmt21h: // op vAA, #+BBBB0000[00000000]
+ pDec->vA = INST_AA(inst);
+ /*
+ * The value should be treated as right-zero-extended, but we don't
+ * actually do that here. Among other things, we don't know if it's
+ * the top bits of a 32- or 64-bit value.
+ */
+ pDec->vB = FETCH(1);
+ break;
+ case kFmt23x: // op vAA, vBB, vCC
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1) & 0xff;
+ pDec->vC = FETCH(1) >> 8;
+ break;
+ case kFmt22b: // op vAA, vBB, #+CC
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1) & 0xff;
+ pDec->vC = (s1) (FETCH(1) >> 8); // sign-extend 8-bit value
+ break;
+ case kFmt22s: // op vA, vB, #+CCCC
+ case kFmt22t: // op vA, vB, +CCCC
+ pDec->vA = INST_A(inst);
+ pDec->vB = INST_B(inst);
+ pDec->vC = (s2) FETCH(1); // sign-extend 16-bit value
+ break;
+ case kFmt22c: // op vA, vB, thing@CCCC
+ case kFmt22cs: // [opt] op vA, vB, field offset CCCC
+ pDec->vA = INST_A(inst);
+ pDec->vB = INST_B(inst);
+ pDec->vC = FETCH(1);
+ break;
+ case kFmt30t: // op +AAAAAAAA
+ pDec->vA = FETCH_u4(1); // signed 32-bit value
+ break;
+ case kFmt31t: // op vAA, +BBBBBBBB
+ case kFmt31c: // op vAA, string@BBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH_u4(1); // 32-bit value
+ break;
+ case kFmt32x: // op vAAAA, vBBBB
+ pDec->vA = FETCH(1);
+ pDec->vB = FETCH(2);
+ break;
+ case kFmt31i: // op vAA, #+BBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH_u4(1); // signed 32-bit value
+ break;
+ case kFmt35c: // op {vC, vD, vE, vF, vG}, thing@BBBB
+ case kFmt35ms: // [opt] invoke-virtual+super
+ case kFmt35mi: // [opt] inline invoke
+ {
+ /*
+ * Note that the fields mentioned in the spec don't appear in
+ * their "usual" positions here compared to most formats. This
+ * was done so that the field names for the argument count and
+ * reference index match between this format and the corresponding
+ * range formats (3rc and friends).
+ *
+ * Bottom line: The argument count is always in vA, and the
+ * method constant (or equivalent) is always in vB.
+ */
+ u2 regList;
+ int count;
+
+ pDec->vA = INST_B(inst); // This is labeled A in the spec.
+ pDec->vB = FETCH(1);
+ regList = FETCH(2);
+
+ count = pDec->vA;
+
+ /*
+ * Copy the argument registers into the arg[] array, and
+ * also copy the first argument (if any) into vC. (The
+ * DecodedInstruction structure doesn't have separate
+ * fields for {vD, vE, vF, vG}, so there's no need to make
+ * copies of those.) Note that cases 5..2 fall through.
+ */
+ switch (count) {
+ case 5: {
+ if (format == kFmt35mi) {
+ /* A fifth arg is verboten for inline invokes. */
+ ALOGW("Invalid arg count in 35mi (5)");
+ goto bail;
+ }
+ /*
+ * Per note at the top of this format decoder, the
+ * fifth argument comes from the A field in the
+ * instruction, but it's labeled G in the spec.
+ */
+ pDec->arg[4] = INST_A(inst);
+ FALLTHROUGH_INTENDED;
+ }
+ case 4: pDec->arg[3] = (regList >> 12) & 0x0f; FALLTHROUGH_INTENDED;
+ case 3: pDec->arg[2] = (regList >> 8) & 0x0f; FALLTHROUGH_INTENDED;
+ case 2: pDec->arg[1] = (regList >> 4) & 0x0f; FALLTHROUGH_INTENDED;
+ case 1: pDec->vC = pDec->arg[0] = regList & 0x0f; break;
+ case 0: break; // Valid, but no need to do anything.
+ default:
+ ALOGW("Invalid arg count in 35c/35ms/35mi (%d)", count);
+ goto bail;
+ }
+ }
+ break;
+ case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+ case kFmt3rms: // [opt] invoke-virtual+super/range
+ case kFmt3rmi: // [opt] execute-inline/range
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1);
+ pDec->vC = FETCH(2);
+ break;
+ case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB_wide = FETCH_u4(1) | ((u8) FETCH_u4(3) << 32);
+ break;
+ case kFmt45cc:
+ {
+ // AG op BBBB FEDC HHHH
+ pDec->vA = INST_B(inst); // This is labelled A in the spec.
+ pDec->vB = FETCH(1); // vB meth@BBBB
+ u2 fedc = FETCH(2);
+ pDec->vC = fedc & 0xf;
+ pDec->arg[0] = (fedc >> 4) & 0xf; // vD
+ pDec->arg[1] = (fedc >> 8) & 0xf; // vE
+ pDec->arg[2] = (fedc >> 12); // vF
+ pDec->arg[3] = INST_A(inst); // vG
+ pDec->arg[4] = FETCH(3); // vH proto@HHHH
+ }
+ break;
+ case kFmt4rcc:
+ {
+ // AA op BBBB CCCC HHHH
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1);
+ pDec->vC = FETCH(2);
+ pDec->arg[4] = FETCH(3); // vH proto@HHHH
+ }
+ break;
+ default:
+ ALOGW("Can't decode unexpected format %d (op=%d)", format, opcode);
+ assert(false);
+ break;
+ }
+
+bail:
+ ;
+}
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined. Also
+ * works for special OP_NOP entries, including switch statement data tables
+ * and array data.
+ */
+size_t dexGetWidthFromInstruction(const u2* insns)
+{
+ size_t width;
+
+ if (*insns == kPackedSwitchSignature) {
+ width = 4 + insns[1] * 2;
+ } else if (*insns == kSparseSwitchSignature) {
+ width = 2 + insns[1] * 4;
+ } else if (*insns == kArrayDataSignature) {
+ u2 elemWidth = insns[1];
+ u4 len = insns[2] | (((u4)insns[3]) << 16);
+ // The plus 1 is to round up for odd size and width.
+ width = 4 + (elemWidth * len + 1) / 2;
+ } else {
+ width = dexGetWidthFromOpcode(dexOpcodeFromCodeUnit(insns[0]));
+ }
+
+ return width;
+}
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
new file mode 100644
index 000000000..c5bf77cb0
--- /dev/null
+++ b/libdex/InstrUtils.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Dalvik instruction utility functions.
+ */
+#ifndef LIBDEX_INSTRUTILS_H_
+#define LIBDEX_INSTRUTILS_H_
+
+#include "DexFile.h"
+#include "DexOpcodes.h"
+
+/*
+ * Possible instruction formats associated with Dalvik opcodes.
+ *
+ * See the file opcode-gen/README.txt for information about updating
+ * opcodes and instruction formats.
+ */
+enum InstructionFormat {
+ kFmt00x = 0, // unknown format (also used for "breakpoint" opcode)
+ kFmt10x, // op
+ kFmt12x, // op vA, vB
+ kFmt11n, // op vA, #+B
+ kFmt11x, // op vAA
+ kFmt10t, // op +AA
+ kFmt20bc, // [opt] op AA, thing@BBBB
+ kFmt20t, // op +AAAA
+ kFmt22x, // op vAA, vBBBB
+ kFmt21t, // op vAA, +BBBB
+ kFmt21s, // op vAA, #+BBBB
+ kFmt21h, // op vAA, #+BBBB00000[00000000]
+ kFmt21c, // op vAA, thing@BBBB
+ kFmt23x, // op vAA, vBB, vCC
+ kFmt22b, // op vAA, vBB, #+CC
+ kFmt22t, // op vA, vB, +CCCC
+ kFmt22s, // op vA, vB, #+CCCC
+ kFmt22c, // op vA, vB, thing@CCCC
+ kFmt22cs, // [opt] op vA, vB, field offset CCCC
+ kFmt30t, // op +AAAAAAAA
+ kFmt32x, // op vAAAA, vBBBB
+ kFmt31i, // op vAA, #+BBBBBBBB
+ kFmt31t, // op vAA, +BBBBBBBB
+ kFmt31c, // op vAA, string@BBBBBBBB
+ kFmt35c, // op {vC,vD,vE,vF,vG}, thing@BBBB
+ kFmt35ms, // [opt] invoke-virtual+super
+ kFmt3rc, // op {vCCCC .. v(CCCC+AA-1)}, thing@BBBB
+ kFmt3rms, // [opt] invoke-virtual+super/range
+ kFmt51l, // op vAA, #+BBBBBBBBBBBBBBBB
+ kFmt35mi, // [opt] inline invoke
+ kFmt3rmi, // [opt] inline invoke/range
+ kFmt45cc, // op {vC, vD, vE, vF, vG}, meth@BBBB, proto@HHHH
+ kFmt4rcc, // op {VCCCC .. v(CCCC+AA-1)}, meth@BBBB, proto@HHHH
+};
+
+/*
+ * Types of indexed reference that are associated with opcodes whose
+ * formats include such an indexed reference (e.g., 21c and 35c).
+ */
+enum InstructionIndexType {
+ kIndexUnknown = 0,
+ kIndexNone, // has no index
+ kIndexVaries, // "It depends." Used for throw-verification-error
+ kIndexTypeRef, // type reference index
+ kIndexStringRef, // string reference index
+ kIndexMethodRef, // method reference index
+ kIndexFieldRef, // field reference index
+ kIndexInlineMethod, // inline method index (for inline linked methods)
+ kIndexVtableOffset, // vtable offset (for static linked methods)
+ kIndexFieldOffset, // field offset (for static linked fields)
+ kIndexMethodAndProtoRef, // method index and proto index
+ kIndexCallSiteRef, // call site index
+ kIndexMethodHandleRef, // constant method handle reference index
+ kIndexProtoRef, // constant prototype reference index
+};
+
+/*
+ * Instruction width implied by an opcode's format; a value in the
+ * range 0 to 5. Note that there are special "pseudo-instructions"
+ * which are used to encode switch and data tables, and these don't
+ * have a fixed width. See dexGetWidthFromInstruction(), below.
+ */
+typedef u1 InstructionWidth;
+
+/*
+ * Opcode control flow flags, used by the verifier and JIT.
+ */
+typedef u1 OpcodeFlags;
+enum OpcodeFlagsBits {
+ kInstrCanBranch = 1, // conditional or unconditional branch
+ kInstrCanContinue = 1 << 1, // flow can continue to next statement
+ kInstrCanSwitch = 1 << 2, // switch statement
+ kInstrCanThrow = 1 << 3, // could cause an exception to be thrown
+ kInstrCanReturn = 1 << 4, // returns, no additional statements
+ kInstrInvoke = 1 << 5, // a flavor of invoke
+};
+
+/*
+ * Struct that includes a pointer to each of the opcode information
+ * tables.
+ *
+ * Note: We use "u1*" here instead of the names of the enumerated
+ * types to guarantee that elements don't use much space. We hold out
+ * hope for a standard way to indicate the size of an enumerated type
+ * that works for both C and C++, but in the mean time, this will
+ * suffice.
+ */
+struct InstructionInfoTables {
+ u1* formats; /* InstructionFormat elements */
+ u1* indexTypes; /* InstructionIndexType elements */
+ OpcodeFlags* flags;
+ InstructionWidth* widths;
+};
+
+/*
+ * Global InstructionInfoTables struct.
+ */
+extern InstructionInfoTables gDexOpcodeInfo;
+
+/*
+ * Holds the contents of a decoded instruction.
+ */
+struct DecodedInstruction {
+ u4 vA;
+ u4 vB;
+ u8 vB_wide; /* for kFmt51l */
+ u4 vC;
+ u4 arg[5]; /* vC/D/E/F/G in invoke or filled-new-array */
+ Opcode opcode;
+ InstructionIndexType indexType;
+};
+
+/*
+ * Return the instruction width of the specified opcode, or 0 if not defined.
+ */
+DEX_INLINE size_t dexGetWidthFromOpcode(Opcode opcode)
+{
+ assert((u4) opcode < kNumPackedOpcodes);
+ return gDexOpcodeInfo.widths[opcode];
+}
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined. Also
+ * works for special OP_NOP entries, including switch statement data tables
+ * and array data.
+ */
+size_t dexGetWidthFromInstruction(const u2* insns);
+
+/*
+ * Returns the flags for the specified opcode.
+ */
+DEX_INLINE OpcodeFlags dexGetFlagsFromOpcode(Opcode opcode)
+{
+ assert((u4) opcode < kNumPackedOpcodes);
+ return gDexOpcodeInfo.flags[opcode];
+}
+
+/*
+ * Returns true if the given flags represent a goto (unconditional branch).
+ */
+DEX_INLINE bool dexIsGoto(OpcodeFlags flags)
+{
+ return (flags & (kInstrCanBranch | kInstrCanContinue)) == kInstrCanBranch;
+}
+
+/*
+ * Return the instruction format for the specified opcode.
+ */
+DEX_INLINE InstructionFormat dexGetFormatFromOpcode(Opcode opcode)
+{
+ assert((u4) opcode < kNumPackedOpcodes);
+ return (InstructionFormat) gDexOpcodeInfo.formats[opcode];
+}
+
+/*
+ * Return the instruction index type for the specified opcode.
+ */
+DEX_INLINE InstructionIndexType dexGetIndexTypeFromOpcode(Opcode opcode)
+{
+ assert((u4) opcode < kNumPackedOpcodes);
+ return (InstructionIndexType) gDexOpcodeInfo.indexTypes[opcode];
+}
+
+/*
+ * Decode the instruction pointed to by "insns".
+ */
+void dexDecodeInstruction(const u2* insns, DecodedInstruction* pDec);
+
+#endif // LIBDEX_INSTRUTILS_H_
diff --git a/libdex/Leb128.cpp b/libdex/Leb128.cpp
new file mode 100644
index 000000000..ed09e19aa
--- /dev/null
+++ b/libdex/Leb128.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for interpreting LEB128 (little endian base 128) values
+ */
+
+#include "Leb128.h"
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit,
+ bool* okay) {
+ const u1* ptr = *pStream;
+ int result = readUnsignedLeb128(pStream);
+
+ if (((limit != NULL) && (*pStream > limit))
+ || (((*pStream - ptr) == 5) && (ptr[4] > 0x0f))) {
+ *okay = false;
+ }
+
+ return result;
+}
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifySignedLeb128(const u1** pStream, const u1* limit,
+ bool* okay) {
+ const u1* ptr = *pStream;
+ int result = readSignedLeb128(pStream);
+
+ if (((limit != NULL) && (*pStream > limit))
+ || (((*pStream - ptr) == 5) && (ptr[4] > 0x0f))) {
+ *okay = false;
+ }
+
+ return result;
+}
diff --git a/libdex/Leb128.h b/libdex/Leb128.h
new file mode 100644
index 000000000..21f4edaa1
--- /dev/null
+++ b/libdex/Leb128.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Functions for interpreting LEB128 (little endian base 128) values
+ */
+
+#ifndef LIBDEX_LEB128_H_
+#define LIBDEX_LEB128_H_
+
+#include "DexFile.h"
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value. This function tolerates
+ * non-zero high-order bits in the fifth encoded byte.
+ */
+DEX_INLINE int readUnsignedLeb128(const u1** pStream) {
+ const u1* ptr = *pStream;
+ int result = *(ptr++);
+
+ if (result > 0x7f) {
+ int cur = *(ptr++);
+ result = (result & 0x7f) | ((cur & 0x7f) << 7);
+ if (cur > 0x7f) {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 14;
+ if (cur > 0x7f) {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 21;
+ if (cur > 0x7f) {
+ /*
+ * Note: We don't check to see if cur is out of
+ * range here, meaning we tolerate garbage in the
+ * high four-order bits.
+ */
+ cur = *(ptr++);
+ result |= cur << 28;
+ }
+ }
+ }
+ }
+
+ *pStream = ptr;
+ return result;
+}
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value. This function tolerates
+ * non-zero high-order bits in the fifth encoded byte.
+ */
+DEX_INLINE int readSignedLeb128(const u1** pStream) {
+ const u1* ptr = *pStream;
+ int result = *(ptr++);
+
+ if (result <= 0x7f) {
+ result = (result << 25) >> 25;
+ } else {
+ int cur = *(ptr++);
+ result = (result & 0x7f) | ((cur & 0x7f) << 7);
+ if (cur <= 0x7f) {
+ result = (result << 18) >> 18;
+ } else {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 14;
+ if (cur <= 0x7f) {
+ result = (result << 11) >> 11;
+ } else {
+ cur = *(ptr++);
+ result |= (cur & 0x7f) << 21;
+ if (cur <= 0x7f) {
+ result = (result << 4) >> 4;
+ } else {
+ /*
+ * Note: We don't check to see if cur is out of
+ * range here, meaning we tolerate garbage in the
+ * high four-order bits.
+ */
+ cur = *(ptr++);
+ result |= cur << 28;
+ }
+ }
+ }
+ }
+
+ *pStream = ptr;
+ return result;
+}
+
+/*
+ * Reads an unsigned LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifyUnsignedLeb128(const u1** pStream, const u1* limit,
+ bool* okay);
+
+/*
+ * Reads a signed LEB128 value, updating the given pointer to point
+ * just past the end of the read value and also indicating whether the
+ * value was syntactically valid. The only syntactically *invalid*
+ * values are ones that are five bytes long where the final byte has
+ * any but the low-order four bits set. Additionally, if the limit is
+ * passed as non-NULL and bytes would need to be read past the limit,
+ * then the read is considered invalid.
+ */
+int readAndVerifySignedLeb128(const u1** pStream, const u1* limit, bool* okay);
+
+
+/*
+ * Writes a 32-bit value in unsigned ULEB128 format.
+ *
+ * Returns the updated pointer.
+ */
+DEX_INLINE u1* writeUnsignedLeb128(u1* ptr, u4 data)
+{
+ while (true) {
+ u1 out = data & 0x7f;
+ if (out != data) {
+ *ptr++ = out | 0x80;
+ data >>= 7;
+ } else {
+ *ptr++ = out;
+ break;
+ }
+ }
+
+ return ptr;
+}
+
+/*
+ * Returns the number of bytes needed to encode "val" in ULEB128 form.
+ */
+DEX_INLINE int unsignedLeb128Size(u4 data)
+{
+ int count = 0;
+
+ do {
+ data >>= 7;
+ count++;
+ } while (data != 0);
+
+ return count;
+}
+
+#endif
diff --git a/libdex/OptInvocation.cpp b/libdex/OptInvocation.cpp
new file mode 100644
index 000000000..35d6262e5
--- /dev/null
+++ b/libdex/OptInvocation.cpp
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Utility functions for dealing with optimized dex files.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <errno.h>
+
+#include "OptInvocation.h"
+#include "DexFile.h"
+
+static const char* kCacheDirectoryName = "dalvik-cache";
+
+#if defined(__aarch64__)
+static const char* kInstructionSet = "arm64";
+#elif defined(__arm__)
+static const char* kInstructionSet = "arm";
+#elif defined(__i386__)
+static const char* kInstructionSet = "x86";
+#elif defined(__mips__)
+static const char* kInstructionSet = "mips";
+#elif defined(__x86_64__)
+static const char* kInstructionSet = "x86_64";
+#else
+#error Unsupported instruction set.
+#endif
+
+static int dexOptMkdir(const char* path, int mode)
+{
+#ifdef _WIN32
+ return mkdir(path);
+#else
+ return mkdir(path, mode);
+#endif
+}
+
+/*
+ * Given the filename of a .jar or .dex file, construct the DEX file cache
+ * name.
+ *
+ * For a Jar, "subFileName" is the name of the entry (usually "classes.dex").
+ * For a DEX, it may be NULL.
+ *
+ * Returns a newly-allocated string, or NULL on failure.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName, const char* subFileName)
+{
+ char nameBuf[512];
+ char absoluteFile[sizeof(nameBuf)];
+ const size_t kBufLen = sizeof(nameBuf) - 1;
+ const char* dataRoot;
+ char* cp;
+
+ /*
+ * Get the absolute path of the Jar or DEX file.
+ */
+ absoluteFile[0] = '\0';
+ if (fileName[0] != '/') {
+ /*
+ * Generate the absolute path. This doesn't do everything it
+ * should, e.g. if filename is "./out/whatever" it doesn't crunch
+ * the leading "./" out, but it'll do.
+ */
+ if (getcwd(absoluteFile, kBufLen) == NULL) {
+ ALOGE("Can't get CWD while opening jar file");
+ return NULL;
+ }
+ strncat(absoluteFile, "/", kBufLen - strlen(absoluteFile));
+ }
+ strncat(absoluteFile, fileName, kBufLen - strlen(absoluteFile));
+
+ /*
+ * Append the name of the Jar file entry, if any. This is not currently
+ * required, but will be if we start putting more than one DEX file
+ * in a Jar.
+ */
+ if (subFileName != NULL) {
+ strncat(absoluteFile, "/", kBufLen - strlen(absoluteFile));
+ strncat(absoluteFile, subFileName, kBufLen - strlen(absoluteFile));
+ }
+
+ /* Turn the path into a flat filename by replacing
+ * any slashes after the first one with '@' characters.
+ */
+ cp = absoluteFile + 1;
+ while (*cp != '\0') {
+ if (*cp == '/') {
+ *cp = '@';
+ }
+ cp++;
+ }
+
+ /* Build the name of the cache directory.
+ */
+ dataRoot = getenv("ANDROID_DATA");
+ if (dataRoot == NULL)
+ dataRoot = "/data";
+ snprintf(nameBuf, kBufLen, "%s/%s", dataRoot, kCacheDirectoryName);
+ if (strcmp(dataRoot, "/data") != 0) {
+ int result = dexOptMkdir(nameBuf, 0700);
+ if (result != 0 && errno != EEXIST) {
+ ALOGE("Failed to create dalvik-cache directory %s: %s", nameBuf, strerror(errno));
+ return NULL;
+ }
+ }
+ snprintf(nameBuf, kBufLen, "%s/%s/%s", dataRoot, kCacheDirectoryName, kInstructionSet);
+ if (strcmp(dataRoot, "/data") != 0) {
+ int result = dexOptMkdir(nameBuf, 0700);
+ if (result != 0 && errno != EEXIST) {
+ ALOGE("Failed to create dalvik-cache directory %s: %s", nameBuf, strerror(errno));
+ return NULL;
+ }
+ }
+
+ /* Tack on the file name for the actual cache file path.
+ */
+ strncat(nameBuf, absoluteFile, kBufLen - strlen(nameBuf));
+
+ ALOGV("Cache file for '%s' '%s' is '%s'", fileName, subFileName, nameBuf);
+ return strdup(nameBuf);
+}
+
+/*
+ * Create a skeletal "opt" header in a new file. Most of the fields are
+ * initialized to garbage, but we fill in "dexOffset" so others can
+ * see how large the header is.
+ *
+ * "fd" must be positioned at the start of the file. On return, it will
+ * be positioned just past the header, and the place where the DEX data
+ * should go.
+ *
+ * Returns 0 on success, errno on failure.
+ */
+int dexOptCreateEmptyHeader(int fd)
+{
+ DexOptHeader optHdr;
+ ssize_t actual;
+
+ assert(lseek(fd, 0, SEEK_CUR) == 0);
+
+ /*
+ * The data is only expected to be readable on the current system, so
+ * we just write the structure. We do need the file offset to be 64-bit
+ * aligned to fulfill a DEX requirement.
+ */
+ assert((sizeof(optHdr) & 0x07) == 0);
+ memset(&optHdr, 0xff, sizeof(optHdr));
+ optHdr.dexOffset = sizeof(optHdr);
+ actual = write(fd, &optHdr, sizeof(optHdr));
+ if (actual != sizeof(optHdr)) {
+ ALOGE("opt header write failed: %s", strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
diff --git a/libdex/OptInvocation.h b/libdex/OptInvocation.h
new file mode 100644
index 000000000..3f32b94bf
--- /dev/null
+++ b/libdex/OptInvocation.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Utility functions related to "dexopt".
+ */
+#ifndef LIBDEX_OPTINVOCATION_H_
+#define LIBDEX_OPTINVOCATION_H_
+
+/*
+ * Utility routines, used by the VM.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName,
+ const char* subFileName);
+int dexOptCreateEmptyHeader(int fd);
+
+#endif // LIBDEX_OPTINVOCATION_H_
diff --git a/libdex/SysUtil.cpp b/libdex/SysUtil.cpp
new file mode 100644
index 000000000..3a1cba3b5
--- /dev/null
+++ b/libdex/SysUtil.cpp
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * System utilities.
+ */
+#include "DexFile.h"
+#include "SysUtil.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#if !defined(__MINGW32__)
+# include <sys/mman.h>
+#endif
+#include <limits.h>
+#include <errno.h>
+
+/*
+ * TEMP_FAILURE_RETRY is defined by some, but not all, versions of
+ * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
+ * not already defined, then define it here.
+ */
+#ifndef TEMP_FAILURE_RETRY
+/* Used to retry syscalls that can return EINTR. */
+#define TEMP_FAILURE_RETRY(exp) ({ \
+ typeof (exp) _rc; \
+ do { \
+ _rc = (exp); \
+ } while (_rc == -1 && errno == EINTR); \
+ _rc; })
+#endif
+
+/*
+ * Create an anonymous shared memory segment large enough to hold "length"
+ * bytes. The actual segment may be larger because mmap() operates on
+ * page boundaries (usually 4K).
+ */
+static void* sysCreateAnonShmem(size_t length)
+{
+#if !defined(__MINGW32__)
+ void* ptr;
+
+ ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ if (ptr == MAP_FAILED) {
+ ALOGW("mmap(%d, RW, SHARED|ANON) failed: %s", (int) length,
+ strerror(errno));
+ return NULL;
+ }
+
+ return ptr;
+#else
+ ALOGE("sysCreateAnonShmem not implemented.");
+ return NULL;
+#endif
+}
+
+/*
+ * Create a private anonymous storage area.
+ */
+int sysCreatePrivateMap(size_t length, MemMapping* pMap)
+{
+ void* memPtr;
+
+ memPtr = sysCreateAnonShmem(length);
+ if (memPtr == NULL)
+ return -1;
+
+ pMap->addr = pMap->baseAddr = memPtr;
+ pMap->length = pMap->baseLength = length;
+ return 0;
+}
+
+/*
+ * Determine the current offset and remaining length of the open file.
+ */
+static int getFileStartAndLength(int fd, off_t *start_, size_t *length_)
+{
+ off_t start, end;
+ size_t length;
+
+ assert(start_ != NULL);
+ assert(length_ != NULL);
+
+ start = lseek(fd, 0L, SEEK_CUR);
+ end = lseek(fd, 0L, SEEK_END);
+ (void) lseek(fd, start, SEEK_SET);
+
+ if (start == (off_t) -1 || end == (off_t) -1) {
+ ALOGE("could not determine length of file");
+ return -1;
+ }
+
+ length = end - start;
+ if (length == 0) {
+ ALOGE("file is empty");
+ return -1;
+ }
+
+ *start_ = start;
+ *length_ = length;
+
+ return 0;
+}
+
+#if defined(__MINGW32__)
+int sysFakeMapFile(int fd, MemMapping* pMap)
+{
+ /* No MMAP, just fake it by copying the bits.
+ For Win32 we could use MapViewOfFile if really necessary
+ (see libs/utils/FileMap.cpp).
+ */
+ off_t start;
+ size_t length;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = malloc(length);
+ if (read(fd, memPtr, length) < 0) {
+ ALOGW("read(fd=%d, start=%d, length=%d) failed: %s", (int) length,
+ fd, (int) start, strerror(errno));
+ free(memPtr);
+ return -1;
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+}
+#endif
+
+/*
+ * Map a file (from fd's current offset) into a private, read-write memory
+ * segment that will be marked read-only (a/k/a "writable read-only"). The
+ * file offset must be a multiple of the system page size.
+ *
+ * In some cases the mapping will be fully writable (e.g. for files on
+ * FAT filesystems).
+ *
+ * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap)
+{
+#if !defined(__MINGW32__)
+ off_t start;
+ size_t length;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_FILE | MAP_PRIVATE,
+ fd, start);
+ if (memPtr == MAP_FAILED) {
+ ALOGW("mmap(%d, R/W, FILE|PRIVATE, %d, %d) failed: %s", (int) length,
+ fd, (int) start, strerror(errno));
+ return -1;
+ }
+ if (mprotect(memPtr, length, PROT_READ) < 0) {
+ /* this fails with EACCESS on FAT filesystems, e.g. /sdcard */
+ int err = errno;
+ ALOGV("mprotect(%p, %zd, PROT_READ) failed: %s",
+ memPtr, length, strerror(err));
+ ALOGD("mprotect(RO) failed (%d), file will remain read-write", err);
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#else
+ return sysFakeMapFile(fd, pMap);
+#endif
+}
+
+/*
+ * Map part of a file into a shared, read-only memory segment. The "start"
+ * offset is absolute, not relative.
+ *
+ * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
+ MemMapping* pMap)
+{
+#if !defined(__MINGW32__)
+ size_t actualLength;
+ off_t actualStart;
+ int adjust;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ /* adjust to be page-aligned */
+ adjust = start % SYSTEM_PAGE_SIZE;
+ actualStart = start - adjust;
+ actualLength = length + adjust;
+
+ memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
+ fd, actualStart);
+ if (memPtr == MAP_FAILED) {
+ ALOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s",
+ (int) actualLength, fd, (int) actualStart, strerror(errno));
+ return -1;
+ }
+
+ pMap->baseAddr = memPtr;
+ pMap->baseLength = actualLength;
+ pMap->addr = (char*)memPtr + adjust;
+ pMap->length = length;
+
+ LOGVV("mmap seg (st=%d ln=%d): bp=%p bl=%d ad=%p ln=%d",
+ (int) start, (int) length,
+ pMap->baseAddr, (int) pMap->baseLength,
+ pMap->addr, (int) pMap->length);
+
+ return 0;
+#else
+ ALOGE("sysMapFileSegmentInShmem not implemented.");
+ return -1;
+#endif
+}
+
+/*
+ * Change the access rights on one or more pages to read-only or read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+ MemMapping* pMap)
+{
+#if !defined(__MINGW32__)
+ /*
+ * Verify that "addr" is part of this mapping file.
+ */
+ if (addr < pMap->baseAddr ||
+ (u1*)addr >= (u1*)pMap->baseAddr + pMap->baseLength)
+ {
+ ALOGE("Attempted to change %p; map is %p - %p",
+ addr, pMap->baseAddr, (u1*)pMap->baseAddr + pMap->baseLength);
+ return -1;
+ }
+
+ /*
+ * Align "addr" to a page boundary and adjust "length" appropriately.
+ * (The address must be page-aligned, the length doesn't need to be,
+ * but we do need to ensure we cover the same range.)
+ */
+ u1* alignAddr = (u1*) ((uintptr_t) addr & ~(SYSTEM_PAGE_SIZE-1));
+ size_t alignLength = length + ((u1*) addr - alignAddr);
+
+ //ALOGI("%p/%zd --> %p/%zd", addr, length, alignAddr, alignLength);
+ int prot = wantReadWrite ? (PROT_READ|PROT_WRITE) : (PROT_READ);
+ if (mprotect(alignAddr, alignLength, prot) != 0) {
+ ALOGV("mprotect (%p,%zd,%d) failed: %s",
+ alignAddr, alignLength, prot, strerror(errno));
+ return (errno != 0) ? errno : -1;
+ }
+#endif
+
+ /* for "fake" mapping, no need to do anything */
+ return 0;
+}
+
+/*
+ * Release a memory mapping.
+ */
+void sysReleaseShmem(MemMapping* pMap)
+{
+#if !defined(__MINGW32__)
+ if (pMap->baseAddr == NULL && pMap->baseLength == 0)
+ return;
+
+ if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
+ ALOGW("munmap(%p, %zd) failed: %s",
+ pMap->baseAddr, pMap->baseLength, strerror(errno));
+ } else {
+ ALOGV("munmap(%p, %zd) succeeded", pMap->baseAddr, pMap->baseLength);
+ pMap->baseAddr = NULL;
+ pMap->baseLength = 0;
+ }
+#else
+ /* Free the bits allocated by sysMapFileInShmem. */
+ if (pMap->baseAddr != NULL) {
+ free(pMap->baseAddr);
+ pMap->baseAddr = NULL;
+ }
+ pMap->baseLength = 0;
+#endif
+}
+
+/*
+ * Make a copy of a MemMapping.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src)
+{
+ memcpy(dst, src, sizeof(MemMapping));
+}
+
+/*
+ * Write until all bytes have been written.
+ *
+ * Returns 0 on success, or an errno value on failure.
+ */
+int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg)
+{
+ while (count != 0) {
+ ssize_t actual = TEMP_FAILURE_RETRY(write(fd, buf, count));
+ if (actual < 0) {
+ int err = errno;
+ ALOGE("%s: write failed: %s", logMsg, strerror(err));
+ return err;
+ } else if (actual != (ssize_t) count) {
+ ALOGD("%s: partial write (will retry): (%d of %zd)",
+ logMsg, (int) actual, count);
+ buf = (const void*) (((const u1*) buf) + actual);
+ }
+ count -= actual;
+ }
+
+ return 0;
+}
+
+/* See documentation comment in header file. */
+int sysCopyFileToFile(int outFd, int inFd, size_t count)
+{
+ const size_t kBufSize = 32768;
+ unsigned char buf[kBufSize];
+
+ while (count != 0) {
+ size_t getSize = (count > kBufSize) ? kBufSize : count;
+
+ ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize));
+ if (actual != (ssize_t) getSize) {
+ ALOGW("sysCopyFileToFile: copy read failed (%d vs %zd)",
+ (int) actual, getSize);
+ return -1;
+ }
+
+ if (sysWriteFully(outFd, buf, getSize, "sysCopyFileToFile") != 0)
+ return -1;
+
+ count -= getSize;
+ }
+
+ return 0;
+}
diff --git a/libdex/SysUtil.h b/libdex/SysUtil.h
new file mode 100644
index 000000000..c02ec6eda
--- /dev/null
+++ b/libdex/SysUtil.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * System utilities.
+ */
+#ifndef LIBDEX_SYSUTIL_H_
+#define LIBDEX_SYSUTIL_H_
+
+#include <sys/types.h>
+
+/*
+ * System page size. Normally you're expected to get this from
+ * sysconf(_SC_PAGESIZE) or some system-specific define (usually PAGESIZE
+ * or PAGE_SIZE). If we use a simple #define the compiler can generate
+ * appropriate masks directly, so we define it here and verify it as the
+ * VM is starting up.
+ *
+ * Must be a power of 2.
+ */
+#ifdef PAGE_SHIFT
+#define SYSTEM_PAGE_SIZE (1<<PAGE_SHIFT)
+#else
+#define SYSTEM_PAGE_SIZE 4096
+#endif
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+struct MemMapping {
+ void* addr; /* start of data */
+ size_t length; /* length of data */
+
+ void* baseAddr; /* page-aligned base address */
+ size_t baseLength; /* length of mapping */
+};
+
+/*
+ * Copy a map.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src);
+
+/*
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment that can be made writable. (In some cases, such as when
+ * mapping a file on a FAT filesystem, the result may be fully writable.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmemWritableReadOnly(int fd, MemMapping* pMap);
+
+/*
+ * Map part of a file into a shared, read-only memory segment.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, size_t length,
+ MemMapping* pMap);
+
+/*
+ * Create a private anonymous mapping, useful for large allocations.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysCreatePrivateMap(size_t length, MemMapping* pMap);
+
+/*
+ * Change the access rights on one or more pages. If "wantReadWrite" is
+ * zero, the pages will be made read-only; otherwise they will be read-write.
+ *
+ * Returns 0 on success.
+ */
+int sysChangeMapAccess(void* addr, size_t length, int wantReadWrite,
+ MemMapping* pmap);
+
+/*
+ * Release the pages associated with a shared memory segment.
+ *
+ * This does not free "pMap"; it just releases the memory.
+ */
+void sysReleaseShmem(MemMapping* pMap);
+
+/*
+ * Write until all bytes have been written.
+ *
+ * Returns 0 on success, or an errno value on failure.
+ */
+int sysWriteFully(int fd, const void* buf, size_t count, const char* logMsg);
+
+/*
+ * Copy the given number of bytes from one fd to another. Returns
+ * 0 on success, -1 on failure.
+ */
+int sysCopyFileToFile(int outFd, int inFd, size_t count);
+
+#endif // LIBDEX_SYSUTIL_H_
diff --git a/libdex/ZipArchive.h b/libdex/ZipArchive.h
new file mode 100644
index 000000000..206afd55c
--- /dev/null
+++ b/libdex/ZipArchive.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Read-only access to Zip archives, with minimal heap allocation.
+ */
+#ifndef LIBDEX_ZIPARCHIVE_H_
+#define LIBDEX_ZIPARCHIVE_H_
+
+#include <ziparchive/zip_archive.h>
+
+#include "SysUtil.h"
+#include "DexFile.h" // need DEX_INLINE
+
+/*
+ * Open a Zip archive.
+ *
+ * On success, returns 0 and populates "pArchive". Returns nonzero errno
+ * value on failure.
+ */
+DEX_INLINE int dexZipOpenArchive(const char* fileName, ZipArchiveHandle* pArchive) {
+ return OpenArchive(fileName, pArchive);
+}
+
+/*
+ * Like dexZipOpenArchive, but takes a file descriptor open for reading
+ * at the start of the file. The descriptor must be mappable (this does
+ * not allow access to a stream).
+ *
+ * "debugFileName" will appear in error messages, but is not otherwise used.
+ */
+DEX_INLINE int dexZipOpenArchiveFd(int fd, const char* debugFileName,
+ ZipArchiveHandle* pArchive) {
+ return OpenArchiveFd(fd, debugFileName, pArchive);
+}
+
+/*
+ * Close archive, releasing resources associated with it.
+ *
+ * Depending on the implementation this could unmap pages used by classes
+ * stored in a Jar. This should only be done after unloading classes.
+ */
+DEX_INLINE void dexZipCloseArchive(ZipArchiveHandle archive) {
+ CloseArchive(archive);
+}
+
+/*
+ * Return the archive's file descriptor.
+ */
+DEX_INLINE int dexZipGetArchiveFd(const ZipArchiveHandle pArchive) {
+ return GetFileDescriptor(pArchive);
+}
+
+/*
+ * Find an entry in the Zip archive, by name. Returns NULL if the entry
+ * was not found.
+ */
+DEX_INLINE int dexZipFindEntry(const ZipArchiveHandle pArchive,
+ const char* entryName, ZipEntry* data) {
+ return FindEntry(pArchive, ZipString(entryName), data);
+}
+
+/*
+ * Uncompress and write an entry to a file descriptor.
+ *
+ * Returns 0 on success.
+ */
+DEX_INLINE int dexZipExtractEntryToFile(ZipArchiveHandle handle,
+ ZipEntry* entry, int fd) {
+ return ExtractEntryToFile(handle, entry, fd);
+}
+
+#endif // LIBDEX_ZIPARCHIVE_H_
diff --git a/libdex/sha1.cpp b/libdex/sha1.cpp
new file mode 100644
index 000000000..d59493451
--- /dev/null
+++ b/libdex/sha1.cpp
@@ -0,0 +1,512 @@
+/*
+ * Tweaked in various ways for Google/Android:
+ * - Changed from .cpp to .c.
+ * - Made argument to SHA1Update a const pointer, and enabled
+ * SHA1HANDSOFF. This incurs a speed penalty but prevents us from
+ * trashing the input.
+ * - Include <endian.h> to get endian info.
+ * - Split a small piece into a header file.
+ */
+
+/*
+sha1sum: inspired by md5sum.
+
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+-----------------
+Modified 7/98
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+bit machines
+Routine SHA1Update changed from
+ void SHA1Update(SHA1_CTX* context, unsigned char* data,
+ unsigned int len)
+to
+ void SHA1Update(SHA1_CTX* context, unsigned char* data,
+ unsigned long len)
+
+The 'len' parameter was declared an int which works fine on 32
+bit machines. However, on 16 bit machines an int is too small
+for the shifts being done against it. This caused the hash
+function to generate incorrect values if len was greater than
+8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or
+larger would be guaranteed to generate the wrong hash (e.g.
+Test Vector #3, a million "a"s).
+
+I also changed the declaration of variables i & j in SHA1Update
+to unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit
+implementations since an int and a long are the same size in
+those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland
+C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments
+containing 'JHB'
+
+-----------------
+Modified 13 August 2000
+By Michael Paul Johnson <mpj@cryptography.org>
+Still 100% Public Domain
+
+Changed command line syntax, added feature to automatically
+check files against their previous SHA-1 check values, kind of
+like md5sum does. Added functions hexval, verifyfile,
+and sha1file. Rewrote main().
+-----------------
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF /*Copies data before messing with it.*/
+
+/*#define CMDLINE * include main() and file processing */
+
+#include "sha1.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef __BORLANDC__
+#include <dir.h>
+#include <dos.h>
+#include <process.h> /* prototype for exit() - JHB
+ needed for Win32, but chokes Linux - MPJ */
+#define X_LITTLE_ENDIAN /* This should be #define'd if true.*/
+#else
+# include <unistd.h>
+# include <stdlib.h>
+//# include <endian.h>
+//# if __BYTE_ORDER == __LITTLE_ENDIAN
+# define X_LITTLE_ENDIAN
+//# endif
+#endif
+#include <ctype.h>
+
+#define LINESIZE 2048
+
+static void SHA1Transform(unsigned long state[5],
+ const unsigned char buffer[64]);
+
+#define rol(value,bits) \
+ (((value)<<(bits))|((value)>>(32-(bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from
+ SSLeay */
+#ifdef X_LITTLE_ENDIAN
+#define blk0(i) (block->l[i]=(rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[(i)&15] = rol(block->l[((i)+13)&15]^block->l[((i)+8)&15] \
+ ^block->l[((i)+2)&15]^block->l[(i)&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=(((w)&((x)^(y)))^(y))+blk0(i)+0x5A827999+rol(v,5);(w)=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=(((w)&((x)^(y)))^(y))+blk(i)+0x5A827999+rol(v,5);(w)=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=((w)^(x)^(y))+blk(i)+0x6ED9EBA1+rol(v,5);(w)=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=((((w)|(x))&(y))|((w)&(x)))+blk(i)+0x8F1BBCDC+rol(v,5);(w)=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=((w)^(x)^(y))+blk(i)+0xCA62C1D6+rol(v,5);(w)=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1Transform(unsigned long state[5],
+ const unsigned char buffer[64])
+{
+unsigned long a, b, c, d, e;
+union CHAR64LONG16 {
+ unsigned char c[64];
+ unsigned long l[16];
+};
+CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+static unsigned char workspace[64];
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+#else
+ block = (CHAR64LONG16*)buffer;
+#endif
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2);
+ R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5);
+ R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8);
+ R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14);
+ R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17);
+ R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20);
+ R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26);
+ R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29);
+ R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32);
+ R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38);
+ R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41);
+ R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44);
+ R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50);
+ R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53);
+ R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56);
+ R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62);
+ R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65);
+ R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68);
+ R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74);
+ R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77);
+ R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+/* a = b = c = d = e = 0; Nice try, but the compiler
+optimizes this out, anyway, and it produces an annoying
+warning. */
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+ unsigned long len) /* JHB */
+{
+ unsigned long i, j; /* JHB */
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3))
+ context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63)
+ {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else
+ i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[HASHSIZE], SHA1_CTX*
+context)
+{
+unsigned long i; /* JHB */
+unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++)
+ {
+ finalcount[i] = (unsigned char)((context->count[(i>=4?
+ 0:1)]>>((3-(i&3))*8))&255);
+ /* Endian independent */
+ }
+ SHA1Update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1Update(context, (unsigned char *)"\0", 1);
+ }
+ SHA1Update(context, finalcount, 8);
+ /* Should cause a SHA1Transform() */
+ for (i = 0; i < HASHSIZE; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, HASHSIZE);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF
+ /* make SHA1Transform overwrite it's own static vars */
+ SHA1Transform(context->state, context->buffer);
+#endif
+}
+
+
+
+#ifdef CMDLINE
+
+/* sha1file computes the SHA-1 hash of the named file and puts
+ it in the 20-byte array digest. If fname is NULL, stdin is
+ assumed.
+*/
+void sha1file(char *fname, unsigned char* digest)
+{
+ int bytesread;
+ SHA1_CTX context;
+ unsigned char buffer[16384];
+ FILE* f;
+
+ if (fname)
+ {
+ f = fopen(fname, "rb");
+ if (!f)
+ {
+ fprintf(stderr, "Can't open %s\n", fname);
+ memset(digest, 0, HASHSIZE);
+ return;
+ }
+ }
+ else
+ {
+ f = stdin;
+ }
+ SHA1Init(&context);
+ while (!feof(f))
+ {
+ bytesread = fread(buffer, 1, 16384, f);
+ SHA1Update(&context, buffer, bytesread);
+ }
+ SHA1Final(digest, &context);
+ if (fname)
+ fclose(f);
+}
+
+/* Convert ASCII hexidecimal digit to 4-bit value. */
+unsigned char hexval(char c)
+{
+ unsigned char h;
+
+ c = toupper(c);
+ if (c >= 'A')
+ h = c - 'A' + 10;
+ else
+ h = c - '0';
+ return h;
+}
+
+/* Verify a file created with sha1sum by redirecting output
+ to a file. */
+int verifyfile(char *fname)
+{
+ int j, k;
+ int found = 0;
+ unsigned char digest[HASHSIZE];
+ unsigned char expected_digest[HASHSIZE];
+ FILE *checkfile;
+ char checkline[LINESIZE];
+ char *s;
+ unsigned char err;
+
+ checkfile = fopen(fname, "rt");
+ if (!checkfile)
+ {
+ fprintf(stderr, "Can't open %s\n", fname);
+ return(0);
+ }
+ do
+ {
+ s = fgets(checkline, LINESIZE, checkfile);
+ if (s)
+ {
+ if ((strlen(checkline)>26)&&
+ 1 /*(!strncmp(checkline,"SHA1=", 5))*/)
+ {
+ /* Overwrite newline. */
+ checkline[strlen(checkline)-1]=0;
+ found = 1;
+
+ /* Read expected check value. */
+ for (k=0, j=5; k < HASHSIZE; k++)
+ {
+ expected_digest[k]=hexval(checkline[j++]);
+ expected_digest[k]=(expected_digest[k]<<4)
+ +hexval(checkline[j++]);
+ }
+
+ /* Compute fingerprints */
+ s = checkline+46;
+ sha1file(s, digest);
+
+ /* Compare fingerprints */
+ err = 0;
+ for (k=0; k<HASHSIZE; k++)
+ err |= digest[k]-
+ expected_digest[k];
+ if (err)
+ {
+ fprintf(stderr, "FAILED: %s\n"
+ " EXPECTED: ", s);
+ for (k=0; k<HASHSIZE; k++)
+ fprintf(stderr, "%02X",
+ expected_digest[k]);
+ fprintf(stderr,"\n FOUND: ");
+ for (k=0; k<HASHSIZE; k++)
+ fprintf(stderr, "%02X", digest[k]);
+ fprintf(stderr, "\n");
+ }
+ else
+ {
+ printf("OK: %s\n", s);
+ }
+ }
+ }
+ } while (s);
+ fclose(checkfile);
+ return found;
+}
+
+
+
+void syntax(char *progname)
+{
+ printf("\nsyntax:\n"
+ "%s [-c|-h][-q] file name[s]\n"
+ " -c = check files against previous check values\n"
+ " -g = generate SHA-1 check values (default action)\n"
+ " -h = display this help\n"
+ "For example,\n"
+ "sha1sum test.txt > check.txt\n"
+ "generates check value for test.txt in check.txt, and\n"
+ "sha1sum -c check.txt\n"
+ "checks test.txt against the check value in check.txt\n",
+ progname);
+ exit(1);
+}
+
+
+/**********************************************************/
+
+int main(int argc, char** argv)
+{
+ int i, j, k;
+ int check = 0;
+ int found = 0;
+ unsigned char digest[HASHSIZE];
+ unsigned char expected_digest[HASHSIZE];
+ FILE *checkfile;
+ char checkline[LINESIZE];
+ char *s;
+#ifdef __BORLANDC__
+ struct ffblk f;
+ int done;
+ char path[MAXPATH];
+ char drive[MAXDRIVE];
+ char dir[MAXDIR];
+ char name[MAXFILE];
+ char ext[MAXEXT];
+#endif
+ unsigned char err;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (argv[i][1])
+ {
+ case 'c':
+ case 'C':
+ check = 1;
+ break;
+ case 'g':
+ case 'G':
+ check = 0;
+ break;
+ default:
+ syntax(argv[0]);
+ }
+ }
+ }
+
+ for (i=1; i<argc; i++)
+ {
+ if (argv[i][0] != '-')
+ {
+#ifdef __BORLANDC__
+ fnsplit(argv[i], drive, dir, name, ext);
+ done = findfirst(argv[i], &f, FA_RDONLY |
+ FA_HIDDEN|FA_SYSTEM|FA_ARCH);
+ while (!done)
+ {
+ sprintf(path, "%s%s%s", drive, dir, f.ff_name);
+ s = path;
+#else
+ s = argv[i];
+#endif
+
+ if (check)
+ { /* Check fingerprint file. */
+ found |= verifyfile(s);
+ }
+ else
+ { /* Generate fingerprints & write to
+ stdout. */
+ sha1file(s, digest);
+ //printf("SHA1=");
+ for (j=0; j<HASHSIZE; j++)
+ printf("%02x", digest[j]);
+ printf(" %s\n", s);
+ found = 1;
+ }
+
+#ifdef __BORLANDC__
+ done = findnext(&f);
+ }
+#endif
+
+ }
+ }
+ if (!found)
+ {
+ if (check)
+ {
+ fprintf(stderr,
+ "No SHA1 lines found in %s\n",
+ argv[i]);
+ }
+ else
+ {
+ fprintf(stderr, "No files checked.\n");
+ syntax(argv[0]);
+ }
+ }
+ return(0); /* JHB */
+}
+
+#endif /*CMDLINE*/
diff --git a/libdex/sha1.h b/libdex/sha1.h
new file mode 100644
index 000000000..28907dedc
--- /dev/null
+++ b/libdex/sha1.h
@@ -0,0 +1,20 @@
+/*
+ * See "sha1.cpp" for author info.
+ */
+#ifndef LIBDEX_SHA1_H_
+#define LIBDEX_SHA1_H_
+
+struct SHA1_CTX {
+ unsigned long state[5];
+ unsigned long count[2];
+ unsigned char buffer[64];
+};
+
+#define HASHSIZE 20
+
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data,
+ unsigned long len);
+void SHA1Final(unsigned char digest[HASHSIZE], SHA1_CTX* context);
+
+#endif // LIBDEX_SHA1_H_
diff --git a/tools/dexdeps/src/com/android/dexdeps/DexData.java b/tools/dexdeps/src/com/android/dexdeps/DexData.java
index 1b6cc98ed..cc1978c86 100644
--- a/tools/dexdeps/src/com/android/dexdeps/DexData.java
+++ b/tools/dexdeps/src/com/android/dexdeps/DexData.java
@@ -18,8 +18,6 @@ package com.android.dexdeps;
import java.io.IOException;
import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@@ -37,7 +35,7 @@ public class DexData {
private ClassDefItem[] mClassDefs;
private byte tmpBuf[] = new byte[4];
- private ByteOrder mByteOrder = ByteOrder.LITTLE_ENDIAN;
+ private boolean isBigEndian = false;
/**
* Constructs a new DexData for this file.
@@ -101,7 +99,7 @@ public class DexData {
/* do nothing */
} else if (mHeaderItem.endianTag == HeaderItem.REVERSE_ENDIAN_CONSTANT){
/* file is big-endian (!), reverse future reads */
- mByteOrder = ByteOrder.BIG_ENDIAN;
+ isBigEndian = true;
} else {
System.err.println("Endian constant has unexpected value " +
Integer.toHexString(mHeaderItem.endianTag));
@@ -109,27 +107,26 @@ public class DexData {
}
seek(8+4+20); // magic, checksum, signature
- ByteBuffer buffer = readByteBuffer(Integer.BYTES * 20);
- mHeaderItem.fileSize = buffer.getInt();
- mHeaderItem.headerSize = buffer.getInt();
- /*mHeaderItem.endianTag =*/ buffer.getInt();
- /*mHeaderItem.linkSize =*/ buffer.getInt();
- /*mHeaderItem.linkOff =*/ buffer.getInt();
- /*mHeaderItem.mapOff =*/ buffer.getInt();
- mHeaderItem.stringIdsSize = buffer.getInt();
- mHeaderItem.stringIdsOff = buffer.getInt();
- mHeaderItem.typeIdsSize = buffer.getInt();
- mHeaderItem.typeIdsOff = buffer.getInt();
- mHeaderItem.protoIdsSize = buffer.getInt();
- mHeaderItem.protoIdsOff = buffer.getInt();
- mHeaderItem.fieldIdsSize = buffer.getInt();
- mHeaderItem.fieldIdsOff = buffer.getInt();
- mHeaderItem.methodIdsSize = buffer.getInt();
- mHeaderItem.methodIdsOff = buffer.getInt();
- mHeaderItem.classDefsSize = buffer.getInt();
- mHeaderItem.classDefsOff = buffer.getInt();
- /*mHeaderItem.dataSize =*/ buffer.getInt();
- /*mHeaderItem.dataOff =*/ buffer.getInt();
+ mHeaderItem.fileSize = readInt();
+ mHeaderItem.headerSize = readInt();
+ /*mHeaderItem.endianTag =*/ readInt();
+ /*mHeaderItem.linkSize =*/ readInt();
+ /*mHeaderItem.linkOff =*/ readInt();
+ /*mHeaderItem.mapOff =*/ readInt();
+ mHeaderItem.stringIdsSize = readInt();
+ mHeaderItem.stringIdsOff = readInt();
+ mHeaderItem.typeIdsSize = readInt();
+ mHeaderItem.typeIdsOff = readInt();
+ mHeaderItem.protoIdsSize = readInt();
+ mHeaderItem.protoIdsOff = readInt();
+ mHeaderItem.fieldIdsSize = readInt();
+ mHeaderItem.fieldIdsOff = readInt();
+ mHeaderItem.methodIdsSize = readInt();
+ mHeaderItem.methodIdsOff = readInt();
+ mHeaderItem.classDefsSize = readInt();
+ mHeaderItem.classDefsOff = readInt();
+ /*mHeaderItem.dataSize =*/ readInt();
+ /*mHeaderItem.dataOff =*/ readInt();
}
/**
@@ -146,7 +143,9 @@ public class DexData {
//System.out.println("reading " + count + " strings");
seek(mHeaderItem.stringIdsOff);
- readByteBuffer(Integer.BYTES * count).asIntBuffer().get(stringOffsets);
+ for (int i = 0; i < count; i++) {
+ stringOffsets[i] = readInt();
+ }
mStrings = new String[count];
@@ -167,10 +166,9 @@ public class DexData {
//System.out.println("reading " + count + " typeIds");
seek(mHeaderItem.typeIdsOff);
- ByteBuffer buffer = readByteBuffer(Integer.BYTES * count);
for (int i = 0; i < count; i++) {
mTypeIds[i] = new TypeIdItem();
- mTypeIds[i].descriptorIdx = buffer.getInt();
+ mTypeIds[i].descriptorIdx = readInt();
//System.out.println(i + ": " + mTypeIds[i].descriptorIdx +
// " " + mStrings[mTypeIds[i].descriptorIdx]);
@@ -186,16 +184,15 @@ public class DexData {
//System.out.println("reading " + count + " protoIds");
seek(mHeaderItem.protoIdsOff);
- ByteBuffer buffer = readByteBuffer(Integer.BYTES * 3 * count);
/*
* Read the proto ID items.
*/
for (int i = 0; i < count; i++) {
mProtoIds[i] = new ProtoIdItem();
- mProtoIds[i].shortyIdx = buffer.getInt();
- mProtoIds[i].returnTypeIdx = buffer.getInt();
- mProtoIds[i].parametersOff = buffer.getInt();
+ mProtoIds[i].shortyIdx = readInt();
+ mProtoIds[i].returnTypeIdx = readInt();
+ mProtoIds[i].parametersOff = readInt();
//System.out.println(i + ": " + mProtoIds[i].shortyIdx +
// " " + mStrings[mProtoIds[i].shortyIdx]);
@@ -215,11 +212,10 @@ public class DexData {
} else {
seek(offset);
int size = readInt(); // #of entries in list
- buffer = readByteBuffer(Short.BYTES * size);
protoId.types = new int[size];
for (int j = 0; j < size; j++) {
- protoId.types[j] = buffer.getShort() & 0xffff;
+ protoId.types[j] = readShort() & 0xffff;
}
}
}
@@ -234,12 +230,11 @@ public class DexData {
//System.out.println("reading " + count + " fieldIds");
seek(mHeaderItem.fieldIdsOff);
- ByteBuffer buffer = readByteBuffer((Integer.BYTES + Short.BYTES * 2) * count);
for (int i = 0; i < count; i++) {
mFieldIds[i] = new FieldIdItem();
- mFieldIds[i].classIdx = buffer.getShort() & 0xffff;
- mFieldIds[i].typeIdx = buffer.getShort() & 0xffff;
- mFieldIds[i].nameIdx = buffer.getInt();
+ mFieldIds[i].classIdx = readShort() & 0xffff;
+ mFieldIds[i].typeIdx = readShort() & 0xffff;
+ mFieldIds[i].nameIdx = readInt();
//System.out.println(i + ": " + mFieldIds[i].nameIdx +
// " " + mStrings[mFieldIds[i].nameIdx]);
@@ -255,12 +250,11 @@ public class DexData {
//System.out.println("reading " + count + " methodIds");
seek(mHeaderItem.methodIdsOff);
- ByteBuffer buffer = readByteBuffer((Integer.BYTES + Short.BYTES * 2) * count);
for (int i = 0; i < count; i++) {
mMethodIds[i] = new MethodIdItem();
- mMethodIds[i].classIdx = buffer.getShort() & 0xffff;
- mMethodIds[i].protoIdx = buffer.getShort() & 0xffff;
- mMethodIds[i].nameIdx = buffer.getInt();
+ mMethodIds[i].classIdx = readShort() & 0xffff;
+ mMethodIds[i].protoIdx = readShort() & 0xffff;
+ mMethodIds[i].nameIdx = readInt();
//System.out.println(i + ": " + mMethodIds[i].nameIdx +
// " " + mStrings[mMethodIds[i].nameIdx]);
@@ -276,18 +270,17 @@ public class DexData {
//System.out.println("reading " + count + " classDefs");
seek(mHeaderItem.classDefsOff);
- ByteBuffer buffer = readByteBuffer(Integer.BYTES * 8 * count);
for (int i = 0; i < count; i++) {
mClassDefs[i] = new ClassDefItem();
- mClassDefs[i].classIdx = buffer.getInt();
+ mClassDefs[i].classIdx = readInt();
- /* access_flags = */ buffer.getInt();
- /* superclass_idx = */ buffer.getInt();
- /* interfaces_off = */ buffer.getInt();
- /* source_file_idx = */ buffer.getInt();
- /* annotations_off = */ buffer.getInt();
- /* class_data_off = */ buffer.getInt();
- /* static_values_off = */ buffer.getInt();
+ /* access_flags = */ readInt();
+ /* superclass_idx = */ readInt();
+ /* interfaces_off = */ readInt();
+ /* source_file_idx = */ readInt();
+ /* annotations_off = */ readInt();
+ /* class_data_off = */ readInt();
+ /* static_values_off = */ readInt();
//System.out.println(i + ": " + mClassDefs[i].classIdx + " " +
// mStrings[mTypeIds[mClassDefs[i].classIdx].descriptorIdx]);
@@ -459,12 +452,24 @@ public class DexData {
}
/**
+ * Reads a signed 16-bit integer, byte-swapping if necessary.
+ */
+ short readShort() throws IOException {
+ mDexFile.readFully(tmpBuf, 0, 2);
+ if (isBigEndian) {
+ return (short) ((tmpBuf[1] & 0xff) | ((tmpBuf[0] & 0xff) << 8));
+ } else {
+ return (short) ((tmpBuf[0] & 0xff) | ((tmpBuf[1] & 0xff) << 8));
+ }
+ }
+
+ /**
* Reads a signed 32-bit integer, byte-swapping if necessary.
*/
int readInt() throws IOException {
mDexFile.readFully(tmpBuf, 0, 4);
- if (mByteOrder == ByteOrder.BIG_ENDIAN) {
+ if (isBigEndian) {
return (tmpBuf[3] & 0xff) | ((tmpBuf[2] & 0xff) << 8) |
((tmpBuf[1] & 0xff) << 16) | ((tmpBuf[0] & 0xff) << 24);
} else {
@@ -492,38 +497,29 @@ public class DexData {
}
/**
- * Reads bytes and transforms them into a ByteBuffer with the desired byte order set, from which
- * primitive values can be read.
- */
- ByteBuffer readByteBuffer(int size) throws IOException {
- byte bytes[] = new byte[size];
- mDexFile.read(bytes);
- return ByteBuffer.wrap(bytes).order(mByteOrder);
- }
-
- /**
* Reads a UTF-8 string.
*
- * We don't know how long the UTF-8 string is, so we try to read the worst case amount of bytes.
- *
- * Note that the dex file pointer will likely be at a wrong location after this operation, which
- * means it can't be used in the middle of sequential reads.
+ * We don't know how long the UTF-8 string is, so we have to read one
+ * byte at a time. We could make an educated guess based on the
+ * utf16_size and seek back if we get it wrong, but seeking backward
+ * may cause the underlying implementation to reload I/O buffers.
*/
String readString() throws IOException {
int utf16len = readUnsignedLeb128();
byte inBuf[] = new byte[utf16len * 3]; // worst case
+ int idx;
- int bytesRead = mDexFile.read(inBuf);
- for (int i = 0; i < bytesRead; i++) {
- if (inBuf[i] == 0) {
- bytesRead = i;
+ for (idx = 0; idx < inBuf.length; idx++) {
+ byte val = readByte();
+ if (val == 0)
break;
- }
+ inBuf[idx] = val;
}
- return new String(inBuf, 0, bytesRead, "UTF-8");
+ return new String(inBuf, 0, idx, "UTF-8");
}
+
/*
* =======================================================================
* Internal "structure" declarations