auto import from //depot/cupcake/@135843
diff --git a/libdex/Android.mk b/libdex/Android.mk
new file mode 100644
index 0000000..df45f04
--- /dev/null
+++ b/libdex/Android.mk
@@ -0,0 +1,64 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+dex_src_files := \
+ CmdUtils.c \
+ DexCatch.c \
+ DexClass.c \
+ DexDataMap.c \
+ DexFile.c \
+ DexInlines.c \
+ DexProto.c \
+ DexSwapVerify.c \
+ InstrUtils.c \
+ Leb128.c \
+ OptInvocation.c \
+ sha1.c \
+ SysUtil.c \
+ ZipArchive.c
+
+dex_include_files := \
+ dalvik \
+ $(JNI_H_INCLUDE) \
+ external/zlib \
+ external/safe-iop/include
+
+##
+##
+## Build the device version of libdex
+##
+##
+ifneq ($(SDK_ONLY),true) # SDK_only doesn't need device version
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(dex_src_files)
+LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_MODULE := libdex
+include $(BUILD_STATIC_LIBRARY)
+
+endif # !SDK_ONLY
+
+
+##
+##
+## Build the host version of libdex
+##
+##
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(dex_src_files)
+LOCAL_C_INCLUDES += $(dex_include_files)
+LOCAL_MODULE := libdex
+include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/libdex/CmdUtils.c b/libdex/CmdUtils.c
new file mode 100644
index 0000000..ca1054c
--- /dev/null
+++ b/libdex/CmdUtils.c
@@ -0,0 +1,184 @@
+/*
+ * 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 <fcntl.h>
+#include <errno.h>
+
+
+/*
+ * 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";
+ ZipArchive 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_WRONLY | O_CREAT | O_EXCL, 0600);
+ if (fd < 0) {
+ fprintf(stderr, "Unable to create output file '%s': %s\n",
+ outFileName, strerror(errno));
+ result = kUTFROutputFileProblem;
+ goto bail;
+ }
+
+ unlinkOnFailure = true;
+
+ entry = dexZipFindEntry(&archive, kFileToExtract);
+ if (entry == NULL) {
+ if (!quiet) {
+ fprintf(stderr, "Unable to find '%s' in '%s'\n",
+ kFileToExtract, zipFileName);
+ }
+ result = kUTFRNoClassesDex;
+ goto bail;
+ }
+
+ if (!dexZipExtractEntryToFile(&archive, entry, fd)) {
+ 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.
+ *
+ * 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 "quiet" is set, don't report common errors.
+ *
+ * Returns 0 on success.
+ */
+UnzipToFileResult dexOpenAndMap(const char* fileName, const char* tempFileName,
+ MemMapping* pMap, bool quiet)
+{
+ UnzipToFileResult result = kUTFRSuccess;
+ int len = strlen(fileName);
+ char tempName[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.
+ */
+ if (access("/tmp", W_OK) == 0)
+ sprintf(tempName, "/tmp/dex-temp-%d", getpid());
+ else
+ sprintf(tempName, "/sdcard/dex-temp-%d", getpid());
+
+ tempFileName = tempName;
+ }
+
+ result = dexUnzipToFile(fileName, tempName, quiet);
+
+ if (result == kUTFRSuccess) {
+ //printf("+++ Good unzip to '%s'\n", tempName);
+ fileName = tempName;
+ 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;
+ }
+ }
+
+ /*
+ * Pop open the (presumed) DEX file.
+ */
+ fd = open(fileName, O_RDONLY);
+ if (fd < 0) {
+ if (!quiet) {
+ fprintf(stderr, "ERROR: unable to open '%s': %s\n",
+ fileName, strerror(errno));
+ }
+ goto bail;
+ }
+
+ if (sysMapFileInShmem(fd, pMap) != 0) {
+ fprintf(stderr, "ERROR: Unable to map %s\n", fileName);
+ close(fd);
+ goto bail;
+ }
+
+ /*
+ * Success! Close the file and return with the start/length in pMap.
+ */
+ result = 0;
+
+bail:
+ if (fd >= 0)
+ close(fd);
+ if (removeTemp) {
+ if (unlink(tempName) != 0)
+ fprintf(stderr, "Warning: unable to remove temp '%s'\n", tempName);
+ }
+ return result;
+}
diff --git a/libdex/CmdUtils.h b/libdex/CmdUtils.h
new file mode 100644
index 0000000..fa354a9
--- /dev/null
+++ b/libdex/CmdUtils.h
@@ -0,0 +1,66 @@
+/*
+ * 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
+#define _LIBDEX_CMDUTILS
+
+/* encode the result of unzipping to a file */
+typedef enum UnzipToFileResult {
+ kUTFRSuccess = 0,
+ kUTFRBadArgs,
+ kUTFRNotZip,
+ kUTFRNoClassesDex,
+ kUTFROutputFileProblem,
+ kUTFRBadZip,
+} UnzipToFileResult;
+
+/*
+ * Map the specified DEX file, possibly after expanding it into a temp file
+ * from a Jar. Pass in a MemMapping struct to hold the info.
+ *
+ * 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.
+ *
+ * Returns 0 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*/
diff --git a/libdex/DexCatch.c b/libdex/DexCatch.c
new file mode 100644
index 0000000..5eae17a
--- /dev/null
+++ b/libdex/DexCatch.c
@@ -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 0000000..f928144
--- /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
+#define _LIBDEX_DEXCATCH
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/*
+ * Catch handler entry, used while iterating over catch_handler_items.
+ */
+typedef struct DexCatchHandler {
+ u4 typeIdx; /* type index of the caught exception type */
+ u4 address; /* handler address */
+} DexCatchHandler;
+
+/* 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.
+ */
+typedef struct DexCatchIterator {
+ const u1* pEncodedData;
+ bool catchesAll;
+ u4 countRemaining;
+ DexCatchHandler handler;
+} DexCatchIterator;
+
+/* 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
diff --git a/libdex/DexClass.c b/libdex/DexClass.c
new file mode 100644
index 0000000..1268130
--- /dev/null
+++ b/libdex/DexClass.c
@@ -0,0 +1,192 @@
+/*
+ * 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;
+ u4 i;
+
+ 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 = 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 = 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 0000000..ce41e8b
--- /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
+#define _LIBDEX_DEXCLASS
+
+#include "DexFile.h"
+#include "Leb128.h"
+
+/* expanded form of a class_data_item header */
+typedef struct DexClassDataHeader {
+ u4 staticFieldsSize;
+ u4 instanceFieldsSize;
+ u4 directMethodsSize;
+ u4 virtualMethodsSize;
+} DexClassDataHeader;
+
+/* expanded form of encoded_field */
+typedef struct DexField {
+ u4 fieldIdx; /* index to a field_id_item */
+ u4 accessFlags;
+} DexField;
+
+/* expanded form of encoded_method */
+typedef struct DexMethod {
+ u4 methodIdx; /* index to a method_id_item */
+ u4 accessFlags;
+ u4 codeOff; /* file offset to a code_item */
+} DexMethod;
+
+/* 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. */
+typedef struct DexClassData {
+ DexClassDataHeader header;
+ DexField* staticFields;
+ DexField* instanceFields;
+ DexMethod* directMethods;
+ DexMethod* virtualMethods;
+} DexClassData;
+
+/* 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
diff --git a/libdex/DexDataMap.c b/libdex/DexDataMap.c
new file mode 100644
index 0000000..a9d429e
--- /dev/null
+++ b/libdex/DexDataMap.c
@@ -0,0 +1,141 @@
+/*
+ * 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 <safe_iop.h>
+#include <stdlib.h>
+
+/*
+ * Allocate and initialize a DexDataMap. Returns NULL on failure.
+ */
+DexDataMap* dexDataMapAlloc(u4 maxCount) {
+ /*
+ * Allocate a single chunk for the DexDataMap per se as well as the
+ * two arrays.
+ */
+ size_t size = 0;
+ DexDataMap* map = NULL;
+
+ /*
+ * Avoiding pulling in safe_iop for safe_iopf.
+ */
+ if (!safe_mul(&size, maxCount, sizeof(u4) + sizeof(u2)) ||
+ !safe_add(&size, size, sizeof(DexDataMap))) {
+ return NULL;
+ }
+
+ map = 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)) {
+ LOGE("Out-of-order data map offset: 0x%x then 0x%x\n",
+ 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) {
+ LOGE("No data map entry found @ 0x%x; expected %x\n",
+ offset, type);
+ } else {
+ LOGE("Unexpected data map entry @ 0x%x: expected %x, found %x\n",
+ offset, type, found);
+ }
+
+ return false;
+}
diff --git a/libdex/DexDataMap.h b/libdex/DexDataMap.h
new file mode 100644
index 0000000..fa556d5
--- /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
+#define _LIBDEX_DEXDATAMAP
+
+#include "DexFile.h"
+
+typedef 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 */
+} DexDataMap;
+
+/*
+ * 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*/
diff --git a/libdex/DexFile.c b/libdex/DexFile.c
new file mode 100644
index 0000000..2639d7b
--- /dev/null
+++ b/libdex/DexFile.c
@@ -0,0 +1,1183 @@
+/*
+ * 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 "DexProto.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 kVerifyChecksum = false;
+static const bool kVerifySignature = false;
+
+
+/* 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).
+ */
+
+ 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;
+ }
+ }
+}
+
+/* Return whether the given string is a valid type descriptor. */
+bool dexIsValidTypeDescriptor(const char* s) {
+ int arrayCount = 0;
+
+ while (*s == '[') {
+ arrayCount++;
+ s++;
+ }
+
+ if (arrayCount > 255) {
+ // Arrays may have no more than 255 dimensions.
+ return false;
+ }
+
+ 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': {
+ // You can't have an array of void.
+ return (arrayCount == 0) && (*s == '\0');
+ }
+ case 'L': {
+ // Break out and continue below.
+ break;
+ }
+ default: {
+ // Oddball descriptor character.
+ return false;
+ }
+ }
+
+ // We just consumed the 'L' that introduces a class name.
+
+ bool slashOrFirst = true; // first character or just encountered a slash
+ for (;;) {
+ u1 c = (u1) *s;
+ switch (c) {
+ case '\0': {
+ // Premature end.
+ return false;
+ }
+ 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 (s[1] == '\0') && !slashOrFirst;
+ }
+ case '/': {
+ if (slashOrFirst) {
+ // Slash at start or two slashes in a row.
+ return false;
+ }
+ slashOrFirst = true;
+ s++;
+ break;
+ }
+ default: {
+ if (!dexIsValidMemberNameUtf8(&s)) {
+ return false;
+ }
+ slashOrFirst = false;
+ break;
+ }
+ }
+ }
+}
+
+/* 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';
+}
+
+/* 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);
+ const DexClassDef* pClassDef =
+ (const DexClassDef*) (pDexFile->baseAddr + classDefOff);
+ 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)
+ // LOGW("classLookupAdd: probes=%d\n", probes);
+
+ pLookup->table[idx].classDescriptorHash = hash;
+ pLookup->table[idx].classDescriptorOffset = stringOff;
+ pLookup->table[idx].classDefOffset = classDefOff;
+ *pNumProbes = probes;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+ }
+
+ LOGV("Class lookup: classes=%d slots=%d (%d%% occ) alloc=%d"
+ " total=%d max=%d\n",
+ 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 out an index map entry, advancing "*pData" and reducing "*pSize".
+ */
+static bool parseIndexMapEntry(const u1** pData, u4* pSize, bool expanding,
+ u4* pFullCount, u4* pReducedCount, const u2** pMap)
+{
+ const u4* wordPtr = (const u4*) *pData;
+ u4 size = *pSize;
+ u4 mapCount;
+
+ if (expanding) {
+ if (size < 4)
+ return false;
+ mapCount = *pReducedCount = *wordPtr++;
+ *pFullCount = (u4) -1;
+ size -= sizeof(u4);
+ } else {
+ if (size < 8)
+ return false;
+ mapCount = *pFullCount = *wordPtr++;
+ *pReducedCount = *wordPtr++;
+ size -= sizeof(u4) * 2;
+ }
+
+ u4 mapSize = mapCount * sizeof(u2);
+
+ if (size < mapSize)
+ return false;
+ *pMap = (const u2*) wordPtr;
+ size -= mapSize;
+
+ /* advance the pointer */
+ const u1* ptr = (const u1*) wordPtr;
+ ptr += (mapSize + 3) & ~0x3;
+
+ /* update pass-by-reference values */
+ *pData = (const u1*) ptr;
+ *pSize = size;
+
+ return true;
+}
+
+/*
+ * Set up some pointers into the mapped data.
+ *
+ * See analysis/ReduceConstants.c for the data layout description.
+ */
+static bool parseIndexMap(DexFile* pDexFile, const u1* data, u4 size,
+ bool expanding)
+{
+ if (!parseIndexMapEntry(&data, &size, expanding,
+ &pDexFile->indexMap.classFullCount,
+ &pDexFile->indexMap.classReducedCount,
+ &pDexFile->indexMap.classMap))
+ {
+ return false;
+ }
+
+ if (!parseIndexMapEntry(&data, &size, expanding,
+ &pDexFile->indexMap.methodFullCount,
+ &pDexFile->indexMap.methodReducedCount,
+ &pDexFile->indexMap.methodMap))
+ {
+ return false;
+ }
+
+ if (!parseIndexMapEntry(&data, &size, expanding,
+ &pDexFile->indexMap.fieldFullCount,
+ &pDexFile->indexMap.fieldReducedCount,
+ &pDexFile->indexMap.fieldMap))
+ {
+ return false;
+ }
+
+ if (!parseIndexMapEntry(&data, &size, expanding,
+ &pDexFile->indexMap.stringFullCount,
+ &pDexFile->indexMap.stringReducedCount,
+ &pDexFile->indexMap.stringMap))
+ {
+ return false;
+ }
+
+ if (expanding) {
+ /*
+ * The map includes the "reduced" counts; pull the original counts
+ * out of the DexFile so that code has a consistent source.
+ */
+ assert(pDexFile->indexMap.classFullCount == (u4) -1);
+ assert(pDexFile->indexMap.methodFullCount == (u4) -1);
+ assert(pDexFile->indexMap.fieldFullCount == (u4) -1);
+ assert(pDexFile->indexMap.stringFullCount == (u4) -1);
+
+#if 0 // TODO: not available yet -- do later or just skip this
+ pDexFile->indexMap.classFullCount =
+ pDexFile->pHeader->typeIdsSize;
+ pDexFile->indexMap.methodFullCount =
+ pDexFile->pHeader->methodIdsSize;
+ pDexFile->indexMap.fieldFullCount =
+ pDexFile->pHeader->fieldIdsSize;
+ pDexFile->indexMap.stringFullCount =
+ pDexFile->pHeader->stringIdsSize;
+#endif
+ }
+
+ LOGI("Class : %u %u %u\n",
+ pDexFile->indexMap.classFullCount,
+ pDexFile->indexMap.classReducedCount,
+ pDexFile->indexMap.classMap[0]);
+ LOGI("Method: %u %u %u\n",
+ pDexFile->indexMap.methodFullCount,
+ pDexFile->indexMap.methodReducedCount,
+ pDexFile->indexMap.methodMap[0]);
+ LOGI("Field : %u %u %u\n",
+ pDexFile->indexMap.fieldFullCount,
+ pDexFile->indexMap.fieldReducedCount,
+ pDexFile->indexMap.fieldMap[0]);
+ LOGI("String: %u %u %u\n",
+ pDexFile->indexMap.stringFullCount,
+ pDexFile->indexMap.stringReducedCount,
+ pDexFile->indexMap.stringMap[0]);
+
+ return true;
+}
+
+/*
+ * Parse some auxillary data tables.
+ *
+ * v1.0 wrote a zero in the first 32 bits, followed by the DexClassLookup
+ * table. Subsequent versions switched to the "chunk" format.
+ */
+static bool parseAuxData(const u1* data, DexFile* pDexFile)
+{
+ const u4* pAux = (const u4*) (data + pDexFile->pOptHeader->auxOffset);
+ u4 indexMapType = 0;
+
+ /* v1.0 format? */
+ if (*pAux == 0) {
+ LOGV("+++ found OLD dex format\n");
+ pDexFile->pClassLookup = (const DexClassLookup*) (pAux+1);
+ return true;
+ }
+ LOGV("+++ found NEW dex format\n");
+
+ /* process chunks until we see the end marker */
+ while (*pAux != kDexChunkEnd) {
+ u4 size = *(pAux+1);
+ u1* data = (u1*) (pAux + 2);
+
+ switch (*pAux) {
+ case kDexChunkClassLookup:
+ pDexFile->pClassLookup = (const DexClassLookup*) data;
+ break;
+ case kDexChunkReducingIndexMap:
+ LOGI("+++ found reducing index map, size=%u\n", size);
+ if (!parseIndexMap(pDexFile, data, size, false)) {
+ LOGE("Failed parsing reducing index map\n");
+ return false;
+ }
+ indexMapType = *pAux;
+ break;
+ case kDexChunkExpandingIndexMap:
+ LOGI("+++ found expanding index map, size=%u\n", size);
+ if (!parseIndexMap(pDexFile, data, size, true)) {
+ LOGE("Failed parsing expanding index map\n");
+ return false;
+ }
+ indexMapType = *pAux;
+ break;
+ default:
+ LOGI("Unknown chunk 0x%08x (%c%c%c%c), size=%d in aux data area\n",
+ *pAux,
+ (char) ((*pAux) >> 24), (char) ((*pAux) >> 16),
+ (char) ((*pAux) >> 8), (char) (*pAux),
+ size);
+ break;
+ }
+
+ /*
+ * Advance pointer, padding to 64-bit boundary. The extra "+8" is
+ * for the type/size header.
+ */
+ size = (size + 8 + 7) & ~7;
+ pAux += size / sizeof(u4);
+ }
+
+#if 0 // TODO: propagate expected map type from the VM through the API
+ /*
+ * If we're configured to expect an index map, and we don't find one,
+ * reject this DEX so we'll regenerate it. Also, if we found an
+ * "expanding" map but we're not configured to use it, we have to fail
+ * because the constants aren't usable without translation.
+ */
+ if (indexMapType != expectedIndexMapType) {
+ LOGW("Incompatible index map configuration: found 0x%04x, need %d\n",
+ indexMapType, DVM_REDUCE_CONSTANTS);
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+/*
+ * 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)) {
+ LOGE("too short to be a valid .dex\n");
+ 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) {
+ LOGE("bad opt version (0x%02x %02x %02x %02x)\n",
+ magic[4], magic[5], magic[6], magic[7]);
+ goto bail;
+ }
+
+ pDexFile->pOptHeader = (const DexOptHeader*) data;
+ LOGV("Good opt header, DEX offset is %d, flags=0x%02x\n",
+ pDexFile->pOptHeader->dexOffset, pDexFile->pOptHeader->flags);
+
+ /* locate some auxillary data tables */
+ if (!parseAuxData(data, 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) {
+ LOGE("File truncated? stored len=%d, rem len=%d\n",
+ pDexFile->pOptHeader->dexLength, (int) length);
+ goto bail;
+ }
+ length = pDexFile->pOptHeader->dexLength;
+ }
+
+ dexFileSetupBasicPointers(pDexFile, data);
+ pHeader = pDexFile->pHeader;
+
+ magic = pHeader->magic;
+ if (memcmp(magic, DEX_MAGIC, 4) != 0) {
+ /* not expected */
+ LOGE("bad magic number (0x%02x %02x %02x %02x)\n",
+ magic[0], magic[1], magic[2], magic[3]);
+ goto bail;
+ }
+ if (memcmp(magic+4, DEX_MAGIC_VERS, 4) != 0) {
+ LOGE("bad dex version (0x%02x %02x %02x %02x)\n",
+ magic[4], magic[5], magic[6], magic[7]);
+ goto bail;
+ }
+
+ /*
+ * Verify the checksum. This is reasonably quick, but does require
+ * touching every byte in the DEX file. The checksum changes after
+ * byte-swapping and DEX optimization.
+ */
+ if (flags & kDexParseVerifyChecksum) {
+ u4 adler = dexComputeChecksum(pHeader);
+ if (adler != pHeader->checksum) {
+ LOGE("ERROR: bad checksum (%08x vs %08x)\n",
+ adler, pHeader->checksum);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ LOGV("+++ adler32 checksum (%08x) verified\n", adler);
+ }
+ }
+
+ /*
+ * Verify the SHA-1 digest. (Normally we don't want to do this --
+ * the digest is used to uniquely identify a DEX file, and can't be
+ * computed post-optimization.)
+ *
+ * The digest will be invalid after byte swapping and DEX optimization.
+ */
+ 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];
+ LOGE("ERROR: bad SHA1 digest (%s vs %s)\n",
+ dexSHA1DigestToStr(sha1Digest, tmpBuf1),
+ dexSHA1DigestToStr(pHeader->signature, tmpBuf2));
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ } else {
+ LOGV("+++ sha1 digest verified\n");
+ }
+ }
+
+ if (pHeader->fileSize != length) {
+ LOGE("ERROR: stored file size (%d) != expected (%d)\n",
+ (int) pHeader->fileSize, (int) length);
+ if (!(flags & kDexParseContinueOnError))
+ goto bail;
+ }
+
+ if (pHeader->classDefsSize == 0) {
+ LOGE("ERROR: DEX file has no classes in it, failing\n");
+ 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);
+}
+
+
+/*
+ * ===========================================================================
+ * Debug info
+ * ===========================================================================
+ */
+
+/*
+ * Decode the arguments in a method signature, which looks something
+ * like "(ID[Ljava/lang/String;)V".
+ *
+ * Returns the type signature letter for the next argument, or ')' if
+ * there are no more args. Advances "pSig" to point to the character
+ * after the one returned.
+ */
+static char decodeSignature(const char** pSig)
+{
+ const char* sig = *pSig;
+
+ if (*sig == '(')
+ sig++;
+
+ if (*sig == 'L') {
+ /* object ref */
+ while (*++sig != ';')
+ ;
+ *pSig = sig+1;
+ return 'L';
+ }
+ if (*sig == '[') {
+ /* array; advance past array type */
+ while (*++sig == '[')
+ ;
+ if (*sig == 'L') {
+ while (*++sig != ';')
+ ;
+ }
+ *pSig = sig+1;
+ return '[';
+ }
+ if (*sig == '\0')
+ return *sig; /* don't advance further */
+
+ *pSig = sig+1;
+ return *sig;
+}
+
+/*
+ * returns the length of a type string, given the start of the
+ * type string. Used for the case where the debug info format
+ * references types that are inside a method type signature.
+ */
+static int typeLength (const char *type) {
+ // Assumes any leading '(' has already been gobbled
+ const char *end = type;
+ decodeSignature(&end);
+ return end - type;
+}
+
+/*
+ * 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);
+ }
+}
+
+/* access_flag value indicating that a method is static */
+#define ACC_STATIC 0x0008
+
+typedef struct LocalInfo {
+ const char *name;
+ const char *descriptor;
+ const char *signature;
+ u2 startAddress;
+ bool live;
+} LocalInfo;
+
+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 );
+ }
+}
+
+// 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);
+ u4 line;
+ u4 parametersSize;
+ u4 address = 0;
+ LocalInfo localInReg[pCode->registersSize];
+ u4 insnsSize = pCode->insnsSize;
+ DexProto proto = { pDexFile, protoIdx };
+
+ memset(localInReg, 0, sizeof(LocalInfo) * pCode->registersSize);
+
+ if (stream == NULL) {
+ goto end;
+ }
+
+ line = readUnsignedLeb128(&stream);
+ parametersSize = readUnsignedLeb128(&stream);
+
+ u2 argReg = pCode->registersSize - pCode->insSize;
+
+ 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)) {
+ goto invalid_stream;
+ }
+
+ 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:
+ goto end;
+
+ 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) goto invalid_stream;
+
+ // 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) goto invalid_stream;
+
+ emitLocalCbIfLive (cnxt, reg, address, localInReg, localCb);
+ localInReg[reg].live = false;
+ break;
+
+ case DBG_RESTART_LOCAL:
+ reg = readUnsignedLeb128(&stream);
+ if (reg > pCode->registersSize) goto invalid_stream;
+
+ if (localInReg[reg].name == NULL
+ || localInReg[reg].descriptor == NULL) {
+ goto invalid_stream;
+ }
+
+ /*
+ * 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
+ goto end;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+end:
+ {
+ int reg;
+ for (reg = 0; reg < pCode->registersSize; reg++) {
+ emitLocalCbIfLive (cnxt, reg, insnsSize, localInReg, localCb);
+ }
+ }
+ return;
+
+invalid_stream:
+ IF_LOGE() {
+ char* methodDescriptor = dexProtoCopyMethodDescriptor(&proto);
+ LOGE("Invalid debug info stream. class %s; proto %s",
+ classDescriptor, methodDescriptor);
+ free(methodDescriptor);
+ }
+}
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
new file mode 100644
index 0000000..d1ea5eb
--- /dev/null
+++ b/libdex/DexFile.h
@@ -0,0 +1,1054 @@
+/*
+ * 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
+#define _LIBDEX_DEXFILE
+
+#include "vm/Common.h" // basic type defs, e.g. u1/u2/u4/u8, and LOG
+#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"
+/* version, encoded in 4 bytes of ASCII */
+#define DEX_MAGIC_VERS "035\0"
+
+/* same, but for optimized DEX header */
+#define DEX_OPT_MAGIC "dey\n"
+#define DEX_OPT_MAGIC_VERS "035\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 */
+};
+
+/*
+ * 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,
+ 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,
+ 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 */
+
+ kDexChunkReducingIndexMap = 0x5249584d, /* RIXM */
+ kDexChunkExpandingIndexMap = 0x4549584d, /* EIXM */
+
+ 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.
+ */
+typedef 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;
+} DexHeader;
+
+/*
+ * Direct-mapped "map_item".
+ */
+typedef 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 */
+} DexMapItem;
+
+/*
+ * Direct-mapped "map_list".
+ */
+typedef struct DexMapList {
+ u4 size; /* #of entries in list */
+ DexMapItem list[1]; /* entries */
+} DexMapList;
+
+/*
+ * Direct-mapped "string_id_item".
+ */
+typedef struct DexStringId {
+ u4 stringDataOff; /* file offset to string_data_item */
+} DexStringId;
+
+/*
+ * Direct-mapped "type_id_item".
+ */
+typedef struct DexTypeId {
+ u4 descriptorIdx; /* index into stringIds list for type descriptor */
+} DexTypeId;
+
+/*
+ * Direct-mapped "field_id_item".
+ */
+typedef 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 */
+} DexFieldId;
+
+/*
+ * Direct-mapped "method_id_item".
+ */
+typedef 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 */
+} DexMethodId;
+
+/*
+ * Direct-mapped "proto_id_item".
+ */
+typedef 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 */
+} DexProtoId;
+
+/*
+ * Direct-mapped "class_def_item".
+ */
+typedef 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 */
+} DexClassDef;
+
+/*
+ * Direct-mapped "type_item".
+ */
+typedef struct DexTypeItem {
+ u2 typeIdx; /* index into typeIds */
+} DexTypeItem;
+
+/*
+ * Direct-mapped "type_list".
+ */
+typedef struct DexTypeList {
+ u4 size; /* #of entries in list */
+ DexTypeItem list[1]; /* entries */
+} DexTypeList;
+
+/*
+ * 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.
+ */
+typedef 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] */
+} DexCode;
+
+/*
+ * Direct-mapped "try_item".
+ */
+typedef 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 */
+} DexTry;
+
+/*
+ * Link table. Currently undefined.
+ */
+typedef struct DexLink {
+ u1 bleargh;
+} DexLink;
+
+
+/*
+ * Direct-mapped "annotations_directory_item".
+ */
+typedef 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] */
+} DexAnnotationsDirectoryItem;
+
+/*
+ * Direct-mapped "field_annotations_item".
+ */
+typedef struct DexFieldAnnotationsItem {
+ u4 fieldIdx;
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+} DexFieldAnnotationsItem;
+
+/*
+ * Direct-mapped "method_annotations_item".
+ */
+typedef struct DexMethodAnnotationsItem {
+ u4 methodIdx;
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+} DexMethodAnnotationsItem;
+
+/*
+ * Direct-mapped "parameter_annotations_item".
+ */
+typedef struct DexParameterAnnotationsItem {
+ u4 methodIdx;
+ u4 annotationsOff; /* offset to DexAnotationSetRefList */
+} DexParameterAnnotationsItem;
+
+/*
+ * Direct-mapped "annotation_set_ref_item".
+ */
+typedef struct DexAnnotationSetRefItem {
+ u4 annotationsOff; /* offset to DexAnnotationSetItem */
+} DexAnnotationSetRefItem;
+
+/*
+ * Direct-mapped "annotation_set_ref_list".
+ */
+typedef struct DexAnnotationSetRefList {
+ u4 size;
+ DexAnnotationSetRefItem list[1];
+} DexAnnotationSetRefList;
+
+/*
+ * Direct-mapped "anotation_set_item".
+ */
+typedef struct DexAnnotationSetItem {
+ u4 size;
+ u4 entries[1]; /* offset to DexAnnotationItem */
+} DexAnnotationSetItem;
+
+/*
+ * Direct-mapped "annotation_item".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+typedef struct DexAnnotationItem {
+ u1 visibility;
+ u1 annotation[1]; /* data in encoded_annotation format */
+} DexAnnotationItem;
+
+/*
+ * Direct-mapped "encoded_array".
+ *
+ * NOTE: this structure is byte-aligned.
+ */
+typedef struct DexEncodedArray {
+ u1 array[1]; /* data in encoded_array format */
+} DexEncodedArray;
+
+/*
+ * 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.
+ */
+typedef 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];
+} DexClassLookup;
+
+/*
+ * Map constant pool indices from one form to another. Some or all of these
+ * may be NULL.
+ *
+ * The map values are 16-bit unsigned values. If the values we map to
+ * require a larger range, we omit the mapping for that category (which
+ * requires that the lookup code recognize that the data will not be
+ * there for all DEX files in all categories.)
+ */
+typedef struct DexIndexMap {
+ const u2* classMap; /* map, either expanding or reducing */
+ u4 classFullCount; /* same as typeIdsSize */
+ u4 classReducedCount; /* post-reduction count */
+ const u2* methodMap;
+ u4 methodFullCount;
+ u4 methodReducedCount;
+ const u2* fieldMap;
+ u4 fieldFullCount;
+ u4 fieldReducedCount;
+ const u2* stringMap;
+ u4 stringFullCount;
+ u4 stringReducedCount;
+} DexIndexMap;
+
+/*
+ * 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.
+ */
+typedef 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 auxOffset; /* file offset of pre-calc auxillary data */
+ u4 auxLength;
+
+ u4 flags; /* some info flags */
+
+ u4 padding; /* induce 64-bit alignment */
+} DexOptHeader;
+
+#define DEX_FLAG_VERIFIED (1) /* tried to verify all classes */
+#define DEX_OPT_FLAG_BIG (1<<1) /* swapped to big-endian */
+#define DEX_OPT_FLAG_FIELDS (1<<2) /* field access optimized */
+#define DEX_OPT_FLAG_INVOCATIONS (1<<3) /* method calls optimized */
+
+#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.
+ */
+typedef 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;
+
+ /* mapped in "auxillary" section */
+ const DexClassLookup* pClassLookup;
+
+ /* mapped in "auxillary" section */
+ DexIndexMap indexMap;
+
+ /* 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;
+} DexFile;
+
+/*
+ * 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),
+};
+
+/*
+ * Correct the byte ordering in a memory-mapped DEX file. This is only
+ * required for code that opens "raw" DEX files, such as the DEX optimizer.
+ *
+ * Return 0 on success.
+ */
+int dexFixByteOrdering(u1* addr, int len);
+
+/*
+ * 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];
+}
+
+/* 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 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 ((((u4) 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];
+}
+
+DEX_INLINE const u1* dexGetDebugInfoStream(const DexFile* pDexFile,
+ const DexCode* pCode)
+{
+ if (pCode->debugInfoOff == 0) {
+ return NULL;
+ } else {
+ return pDexFile->baseAddr + pCode->debugInfoOff;
+ }
+}
+
+/*
+ * 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);
+
+/* 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)
+{
+ return (const DexAnnotationSetItem*) (pDexFile->baseAddr + offset);
+}
+/* get the class' annotation set */
+DEX_INLINE const DexAnnotationSetItem* dexGetClassAnnotationSet(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ if (pAnnoDir->classAnnotationsOff == 0)
+ return NULL;
+ return dexGetAnnotationSetItem(pDexFile, pAnnoDir->classAnnotationsOff);
+}
+
+/* get the class' field annotation list */
+DEX_INLINE const DexFieldAnnotationsItem* dexGetFieldAnnotations(
+ const DexFile* pDexFile, const DexAnnotationsDirectoryItem* pAnnoDir)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ 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)
+{
+ return pAnnoDir->parametersSize;
+}
+
+/* return the parameter annotation ref list */
+DEX_INLINE const DexAnnotationSetRefList* dexGetParameterAnnotationSetRefList(
+ const DexFile* pDexFile, const DexParameterAnnotationsItem* pItem)
+{
+ 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)
+{
+ return (const DexAnnotationItem*)
+ (pDexFile->baseAddr + dexGetAnnotationOff(pAnnoSet, idx));
+}
+
+
+/*
+ * ===========================================================================
+ * Utility Functions
+ * ===========================================================================
+ */
+
+/*
+ * 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 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_DEXFILE*/
diff --git a/libdex/DexInlines.c b/libdex/DexInlines.c
new file mode 100644
index 0000000..ac0262f
--- /dev/null
+++ b/libdex/DexInlines.c
@@ -0,0 +1,31 @@
+/*
+ * 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 "DexProto.h"
+#include "InstrUtils.h"
+#include "Leb128.h"
+#include "ZipArchive.h"
+
diff --git a/libdex/DexProto.c b/libdex/DexProto.c
new file mode 100644
index 0000000..c8f1b3e
--- /dev/null
+++ b/libdex/DexProto.c
@@ -0,0 +1,534 @@
+/*
+ * 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.
+ */
+static 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 = 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);
+}
+
+/*
+ * Get the short-form method descriptor for the given prototype. The
+ * prototype must be protoIdx-based.
+ */
+const char* dexProtoGetShorty(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+
+ return dexStringById(pProto->dexFile, protoId->shortyIdx);
+}
+
+/*
+ * Get the full method descriptor for the given prototype.
+ */
+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;
+}
+
+/*
+ * 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) {
+ DexStringCache cache;
+
+ dexStringCacheInit(&cache);
+ return dexStringCacheAbandon(&cache,
+ dexProtoGetMethodDescriptor(pProto, &cache));
+}
+
+/*
+ * 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) {
+ 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;
+}
+
+/*
+ * Get the type descriptor for the return type of the given prototype.
+ */
+const char* dexProtoGetReturnType(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+ return dexStringByTypeIdx(pProto->dexFile, protoId->returnTypeIdx);
+}
+
+/*
+ * Get the parameter count of the given prototype.
+ */
+size_t dexProtoGetParameterCount(const DexProto* pProto) {
+ const DexProtoId* protoId = getProtoId(pProto);
+ const DexTypeList* typeList =
+ dexGetProtoParameters(pProto->dexFile, protoId);
+ return (typeList == NULL) ? 0 : typeList->size;
+}
+
+/*
+ * 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) {
+ 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;
+ }
+ }
+}
+
+/*
+ * 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) {
+ return protoCompare(pProto1, pProto2, true);
+}
+
+/*
+ * Compare the two prototypes. 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){
+ 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;
+}
+
+/*
+ * 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) {
+ // First compare the return types.
+
+ int result = strcmp(dexProtoGetReturnType(proto),
+ methodDescriptorReturnType(descriptor));
+
+ if (result != 0) {
+ return result;
+ }
+
+ // The return types match, so we have to check arguments.
+
+ DexParameterIterator iterator;
+ dexParameterIteratorInit(&iterator, proto);
+
+ // Skip the '('.
+ assert (*descriptor == '(');
+ descriptor++;
+
+ for (;;) {
+ const char* protoDesc = dexParameterIteratorNextDescriptor(&iterator);
+
+ if (*descriptor == ')') {
+ // 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);
+
+ 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.
+ */
+ }
+}
+
+
+/*
+ * ===========================================================================
+ * 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 0000000..1ef577b
--- /dev/null
+++ b/libdex/DexProto.h
@@ -0,0 +1,216 @@
+/*
+ * 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
+#define _LIBDEX_DEXPROTO
+
+#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.
+ */
+typedef 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 */
+} DexStringCache;
+
+/*
+ * 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.
+ */
+typedef struct DexProto {
+ const DexFile* dexFile; /* file the idx refers to */
+ u4 protoIdx; /* index into proto_ids table of dexFile */
+} DexProto;
+
+/*
+ * 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. 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);
+
+/*
+ * Single-thread prototype parameter iterator. This structure holds a
+ * pointer to a prototype and its parts, along with a cursor.
+ */
+typedef struct DexParameterIterator {
+ const DexProto* proto;
+ const DexTypeList* parameters;
+ int parameterCount;
+ int cursor;
+} DexParameterIterator;
+
+/*
+ * 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*/
diff --git a/libdex/DexSwapVerify.c b/libdex/DexSwapVerify.c
new file mode 100644
index 0000000..5ecda9f
--- /dev/null
+++ b/libdex/DexSwapVerify.c
@@ -0,0 +1,2851 @@
+/*
+ * 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 "Leb128.h"
+
+#include <safe_iop.h>
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef __BYTE_ORDER
+# error "byte ordering not defined"
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+# define SWAP2(_value) (_value)
+# define SWAP4(_value) (_value)
+# define SWAP8(_value) (_value)
+#else
+# define SWAP2(_value) endianSwapU2((_value))
+# define SWAP4(_value) endianSwapU4((_value))
+# define SWAP8(_value) endianSwapU8((_value))
+static u2 endianSwapU2(u2 value) {
+ return (value >> 8) | (value << 8);
+}
+static u4 endianSwapU4(u4 value) {
+ /* ABCD --> CDAB --> DCBA */
+ value = (value >> 16) | (value << 16);
+ return ((value & 0xff00ff00) >> 8) | ((value << 8) & 0xff00ff00);
+}
+static u8 endianSwapU8(u8 value) {
+ /* ABCDEFGH --> EFGHABCD --> GHEFCDAB --> HGFEDCBA */
+ value = (value >> 32) | (value << 32);
+ value = ((value & 0xffff0000ffff0000ULL) >> 16) |
+ ((value << 16) & 0xffff0000ffff0000ULL);
+ return ((value & 0xff00ff00ff00ff00ULL) >> 8) |
+ ((value << 8) & 0xff00ff00ff00ff00ULL);
+}
+#endif
+
+#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.
+ */
+typedef 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 void* previousItem; // set during section iteration
+} CheckState;
+
+/*
+ * 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)) {
+ LOGW("Bad offset range for %s: 0x%x..0x%x\n", 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 = filePointer(state, (_start)); \
+ const u1* _endPtr = 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)); \
+ if (!safe_mul(NULL, (_count), (_elemSize)) || \
+ !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)) { \
+ LOGW("Bad index: %s(%u) > %s(%u)\n", \
+ #_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)) { \
+ LOGW("Bad index: %s(%u) > %s(%u)\n", \
+ #_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;
+}
+
+/*
+ * 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) {
+ LOGE("Unexpected endian_tag: 0x%x\n", 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) {
+ LOGE("Multiple header items\n");
+ return false;
+ }
+
+ if (sectionOffset != 0) {
+ LOGE("Header at 0x%x; not at start of file\n", sectionOffset);
+ return false;
+ }
+
+ const DexHeader* pHeader = 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;
+ default: {
+ LOGE("Unknown map item type %04x\n", 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 = pMap->size;
+ 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;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexMapItem));
+
+ 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) {
+ LOGE("Out-of-order map item: 0x%x then 0x%x\n",
+ lastOffset, item->offset);
+ return false;
+ }
+
+ if (item->offset >= state->pHeader->fileSize) {
+ LOGE("Map item after end of file: %x, size 0x%x\n",
+ item->offset, state->pHeader->fileSize);
+ return false;
+ }
+
+ if (isDataSectionType(item->type)) {
+ u4 count = 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 (count > dataItemsLeft) {
+ LOGE("Unrealistically many items in the data section: "
+ "at least %d\n", dataItemCount + count);
+ return false;
+ }
+
+ dataItemsLeft -= count;
+ dataItemCount += count;
+ }
+
+ u4 bit = mapTypeToBitMask(item->type);
+
+ if (bit == 0) {
+ return false;
+ }
+
+ if ((usedBits & bit) != 0) {
+ LOGE("Duplicate map section of type 0x%x\n", item->type);
+ return false;
+ }
+
+ usedBits |= bit;
+ lastOffset = item->offset;
+ item++;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeHeaderItem)) == 0) {
+ LOGE("Map is missing header entry\n");
+ return false;
+ }
+
+ if ((usedBits & mapTypeToBitMask(kDexTypeMapList)) == 0) {
+ LOGE("Map is missing map_list entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeStringIdItem)) == 0)
+ && ((state->pHeader->stringIdsOff != 0)
+ || (state->pHeader->stringIdsSize != 0))) {
+ LOGE("Map is missing string_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem)) == 0)
+ && ((state->pHeader->typeIdsOff != 0)
+ || (state->pHeader->typeIdsSize != 0))) {
+ LOGE("Map is missing type_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem)) == 0)
+ && ((state->pHeader->protoIdsOff != 0)
+ || (state->pHeader->protoIdsSize != 0))) {
+ LOGE("Map is missing proto_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem)) == 0)
+ && ((state->pHeader->fieldIdsOff != 0)
+ || (state->pHeader->fieldIdsSize != 0))) {
+ LOGE("Map is missing field_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem)) == 0)
+ && ((state->pHeader->methodIdsOff != 0)
+ || (state->pHeader->methodIdsSize != 0))) {
+ LOGE("Map is missing method_ids entry\n");
+ return false;
+ }
+
+ if (((usedBits & mapTypeToBitMask(kDexTypeClassDefItem)) == 0)
+ && ((state->pHeader->classDefsOff != 0)
+ || (state->pHeader->classDefsSize != 0))) {
+ LOGE("Map is missing class_defs entry\n");
+ return false;
+ }
+
+ state->pDataMap = dexDataMapAlloc(dataItemCount);
+ if (state->pDataMap == NULL) {
+ LOGE("Unable to allocate data map (size 0x%x)\n", 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) {
+ LOGE("Multiple map list items");
+ return false;
+ }
+
+ if (sectionOffset != state->pHeader->mapOff) {
+ LOGE("Map not at header-defined offset: 0x%x, expected 0x%x\n",
+ sectionOffset, state->pHeader->mapOff);
+ return false;
+ }
+
+ const DexMapList* pMap = 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 = 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 = ptr;
+
+ if (!dexDataMapVerify(state->pDataMap,
+ item->stringDataOff, kDexTypeStringDataItem)) {
+ return NULL;
+ }
+
+ const DexStringId* item0 = 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) {
+ LOGE("Out-of-order string_ids: '%s' then '%s'\n", 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 = 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 = ptr;
+ const char* descriptor =
+ dexStringById(state->pDexFile, item->descriptorIdx);
+
+ if (!dexIsValidTypeDescriptor(descriptor)) {
+ LOGE("Invalid type descriptor: '%s'\n", descriptor);
+ return NULL;
+ }
+
+ const DexTypeId* item0 = state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on string_ids being in order.
+ if (item0->descriptorIdx >= item->descriptorIdx) {
+ LOGE("Out-of-order type_ids: 0x%x then 0x%x\n",
+ 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 = 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) {
+ LOGE("Invalid use of void\n");
+ return false;
+ }
+ // Fall through.
+ }
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'F':
+ case 'I':
+ case 'J':
+ case 'S':
+ case 'Z': {
+ if ((descriptor[0] != shorty) || (descriptor[1] != '\0')) {
+ LOGE("Shorty vs. primitive type mismatch: '%c', '%s'\n",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ case 'L': {
+ if ((descriptor[0] != 'L') && (descriptor[0] != '[')) {
+ LOGE("Shorty vs. type mismatch: '%c', '%s'\n",
+ shorty, descriptor);
+ return false;
+ }
+ break;
+ }
+ default: {
+ LOGE("Bogus shorty: '%c'\n", shorty);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* Perform cross-item verification of proto_id_item. */
+static void* crossVerifyProtoIdItem(const CheckState* state, void* ptr) {
+ const DexProtoId* item = 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') {
+ LOGE("Shorty is too short\n");
+ return NULL;
+ }
+
+ if (!shortyDescMatch(*shorty, desc, false)) {
+ return NULL;
+ }
+
+ shorty++;
+ }
+
+ if (*shorty != '\0') {
+ LOGE("Shorty is too long\n");
+ return NULL;
+ }
+
+ const DexProtoId* item0 = state->previousItem;
+ if (item0 != NULL) {
+ // Check ordering. This relies on type_ids being in order.
+ if (item0->returnTypeIdx > item->returnTypeIdx) {
+ LOGE("Out-of-order proto_id return types\n");
+ 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) {
+ LOGE("Out-of-order proto_id arguments\n");
+ 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 = 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 = ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsClassDescriptor(s)) {
+ LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringByTypeIdx(state->pDexFile, item->typeIdx);
+ if (!dexIsFieldDescriptor(s)) {
+ LOGE("Invalid descriptor for type_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ LOGE("Invalid name: '%s'\n", s);
+ return NULL;
+ }
+
+ const DexFieldId* item0 = 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) {
+ LOGE("Out-of-order field_ids\n");
+ 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 = 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 = ptr;
+ const char* s;
+
+ s = dexStringByTypeIdx(state->pDexFile, item->classIdx);
+ if (!dexIsReferenceDescriptor(s)) {
+ LOGE("Invalid descriptor for class_idx: '%s'\n", s);
+ return NULL;
+ }
+
+ s = dexStringById(state->pDexFile, item->nameIdx);
+ if (!dexIsValidMemberName(s)) {
+ LOGE("Invalid name: '%s'\n", s);
+ return NULL;
+ }
+
+ const DexMethodId* item0 = 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) {
+ LOGE("Out-of-order method_ids\n");
+ 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 = 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);
+
+ 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 = 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 = 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 = ptr;
+ const char* descriptor =
+ dexStringByTypeIdx(state->pDexFile, item->classIdx);
+
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Invalid class: '%s'\n", 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)) {
+ LOGE("Invalid superclass: '%s'\n", 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)) {
+ LOGE("Invalid interface: '%s'\n", 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) {
+ LOGE("Duplicate interface: '%s'\n",
+ dexStringByTypeIdx(state->pDexFile, idx1));
+ return NULL;
+ }
+ }
+ }
+ }
+
+ if (!verifyClassDataIsForDef(state, item->classDataOff, item->classIdx)) {
+ LOGE("Invalid class_data_item\n");
+ return NULL;
+ }
+
+ if (!verifyAnnotationsDirectoryIsForDef(state, item->annotationsOff,
+ item->classIdx)) {
+ LOGE("Invalid annotations_directory_item\n");
+ return NULL;
+ }
+
+ 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;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexFieldAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->fieldIdx, state->pHeader->fieldIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->fieldIdx) {
+ LOGE("Out-of-order field_idx: 0x%x then 0x%x\n", 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;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexMethodAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", 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;
+
+ CHECK_LIST_SIZE(item, count, sizeof(DexParameterAnnotationsItem));
+
+ while (count--) {
+ SWAP_INDEX4(item->methodIdx, state->pHeader->methodIdsSize);
+ SWAP_OFFSET4(item->annotationsOff);
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= item->methodIdx) {
+ LOGE("Out-of-order method_idx: 0x%x then 0x%x\n", 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 = 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;
+}
+
+/* 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 = 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 = ptr;
+ DexTypeItem* pType;
+ u4 count;
+
+ CHECK_PTR_RANGE(pTypeList, pTypeList + 1);
+ SWAP_FIELD4(pTypeList->size);
+ count = pTypeList->size;
+ pType = pTypeList->list;
+ CHECK_LIST_SIZE(pType, count, sizeof(DexTypeItem));
+
+ 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 = ptr;
+ DexAnnotationSetRefItem* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(list, list + 1);
+ SWAP_FIELD4(list->size);
+ count = list->size;
+ item = list->list;
+ CHECK_LIST_SIZE(item, count, sizeof(DexAnnotationSetRefItem));
+
+ 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 = 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 = ptr;
+ u4* item;
+ u4 count;
+
+ CHECK_PTR_RANGE(set, set + 1);
+ SWAP_FIELD4(set->size);
+ count = set->size;
+ item = set->entries;
+ CHECK_LIST_SIZE(item, count, sizeof(u4));
+
+ 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 = 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) {
+ LOGE("Out-of-order entry types: 0x%x then 0x%x\n",
+ 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) {
+ LOGE("Field in wrong list @ %d\n", i);
+ return false;
+ }
+
+ if ((accessFlags & ~ACC_FIELD_MASK) != 0) {
+ LOGE("Bogus field access flags %x @ %d\n", accessFlags, i);
+ return false;
+ }
+ }
+
+ 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) {
+ LOGE("Method in wrong list @ %d\n", i);
+ return false;
+ }
+
+ if (((accessFlags & ~ACC_METHOD_MASK) != 0)
+ || (isSynchronized && !allowSynchronized)) {
+ LOGE("Bogus method access flags %x @ %d\n", accessFlags, i);
+ return false;
+ }
+
+ if (expectCode) {
+ if (method->codeOff == 0) {
+ LOGE("Unexpected zero code_off for access_flags %x\n",
+ accessFlags);
+ return false;
+ }
+ } else if (method->codeOff != 0) {
+ LOGE("Unexpected non-zero code_off 0x%x for access_flags %x\n",
+ 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) {
+ LOGE("Trouble with static fields\n");
+ return false;
+ }
+
+ verifyFields(state, classData->header.instanceFieldsSize,
+ classData->instanceFields, false);
+
+ if (!okay) {
+ LOGE("Trouble with instance fields\n");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.directMethodsSize,
+ classData->directMethods, true);
+
+ if (!okay) {
+ LOGE("Trouble with direct methods\n");
+ return false;
+ }
+
+ okay = verifyMethods(state, classData->header.virtualMethodsSize,
+ classData->virtualMethods, false);
+
+ if (!okay) {
+ LOGE("Trouble with virtual methods\n");
+ return false;
+ }
+
+ return true;
+}
+
+/* Perform intra-item verification on class_data_item. */
+static void* intraVerifyClassDataItem(const CheckState* state, void* ptr) {
+ const u1* data = ptr;
+ DexClassData* classData = dexReadAndVerifyClassData(&data, state->fileEnd);
+
+ if (classData == NULL) {
+ LOGE("Unable to parse class_data_item\n");
+ 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 = 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) {
+ LOGE("Bogus size\n");
+ return 0;
+ }
+
+ if ((size < -65536) || (size > 65536)) {
+ LOGE("Invalid size: %d\n", 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) {
+ LOGE("Bogus type_idx");
+ return 0;
+ }
+
+ CHECK_INDEX(typeIdx, state->pHeader->typeIdsSize);
+
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ LOGE("Invalid addr: 0x%x", addr);
+ return 0;
+ }
+ }
+
+ if (catchAll) {
+ u4 addr = readAndVerifyUnsignedLeb128(&ptr, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus catch_all_addr");
+ return 0;
+ }
+
+ if (addr >= code->insnsSize) {
+ LOGE("Invalid catch_all_addr: 0x%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) {
+ LOGE("Bogus handlers_size\n");
+ return NULL;
+ }
+
+ if ((handlersSize == 0) || (handlersSize >= 65536)) {
+ LOGE("Invalid handlers_size: %d\n", 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;
+
+ CHECK_LIST_SIZE(tries, count, sizeof(DexTry));
+
+ while (count--) {
+ u4 i;
+
+ SWAP_FIELD4(tries->startAddr);
+ SWAP_FIELD2(tries->insnCount);
+ SWAP_FIELD2(tries->handlerOff);
+
+ if (tries->startAddr < lastEnd) {
+ LOGE("Out-of-order try\n");
+ return NULL;
+ }
+
+ if (tries->startAddr >= code->insnsSize) {
+ LOGE("Invalid start_addr: 0x%x\n", tries->startAddr);
+ return NULL;
+ }
+
+ for (i = 0; i < handlersSize; i++) {
+ if (tries->handlerOff == handlerOffs[i]) {
+ break;
+ }
+ }
+
+ if (i == handlersSize) {
+ LOGE("Bogus handler offset: 0x%x\n", tries->handlerOff);
+ return NULL;
+ }
+
+ lastEnd = tries->startAddr + tries->insnCount;
+
+ if (lastEnd > code->insnsSize) {
+ LOGE("Invalid insn_count: 0x%x (end addr 0x%x)\n",
+ 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 = 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);
+
+ count = item->insnsSize;
+ insns = item->insns;
+ CHECK_LIST_SIZE(insns, count, sizeof(u2));
+
+ while (count--) {
+ *insns = SWAP2(*insns);
+ insns++;
+ }
+
+ if (item->triesSize == 0) {
+ ptr = insns;
+ } else {
+ if ((((u4) insns) & 3) != 0) {
+ // Four-byte alignment for the tries. Verify the spacer is a 0.
+ if (*insns != 0) {
+ LOGE("Non-zero padding: 0x%x\n", (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 = ptr;
+ bool okay = true;
+ u4 utf16Size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 i;
+
+ if (!okay) {
+ LOGE("Bogus utf16_size\n");
+ return NULL;
+ }
+
+ for (i = 0; i < utf16Size; i++) {
+ if (data >= fileEnd) {
+ LOGE("String data would go beyond end-of-file\n");
+ 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) {
+ LOGE("String shorter than indicated utf16_size 0x%x\n",
+ 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.
+ */
+ LOGE("Illegal start byte 0x%x\n", byte1);
+ return NULL;
+ }
+ case 0x0e: {
+ // Bit pattern 1110, so there are two additional bytes.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte2);
+ return NULL;
+ }
+ u1 byte3 = *(data++);
+ if ((byte3 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte3);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x0f) << 12) | ((byte2 & 0x3f) << 6)
+ | (byte3 & 0x3f);
+ if (value < 0x800) {
+ LOGE("Illegal representation for value %x\n", value);
+ return NULL;
+ }
+ break;
+ }
+ case 0x0c:
+ case 0x0d: {
+ // Bit pattern 110x, so there is one additional byte.
+ u1 byte2 = *(data++);
+ if ((byte2 & 0xc0) != 0x80) {
+ LOGE("Illegal continuation byte 0x%x\n", byte2);
+ return NULL;
+ }
+ u2 value = ((byte1 & 0x1f) << 6) | (byte2 & 0x3f);
+ if ((value != 0) && (value < 0x80)) {
+ LOGE("Illegal representation for value %x\n", value);
+ return NULL;
+ }
+ break;
+ }
+ }
+ }
+
+ if (*(data++) != '\0') {
+ LOGE("String longer than indicated utf16_size 0x%x\n", 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 = ptr;
+ bool okay = true;
+ u4 i;
+
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus line_start\n");
+ return NULL;
+ }
+
+ u4 parametersSize =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus parameters_size\n");
+ return NULL;
+ }
+
+ if (parametersSize > 65536) {
+ LOGE("Invalid parameters_size: 0x%x\n", parametersSize);
+ return NULL;
+ }
+
+ for (i = 0; i < parametersSize; i++) {
+ u4 parameterName =
+ readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus parameter_name\n");
+ 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) {
+ LOGE("Bogus syntax for opcode %02x\n", 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) {
+ LOGE("Bogus encoded_array size\n");
+ return NULL;
+ }
+
+ while (size--) {
+ data = verifyEncodedValue(state, data, crossVerify);
+ if (data == NULL) {
+ return NULL;
+ }
+ }
+
+ return data;
+}
+
+/* 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) {
+ LOGE("Bogus byte size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data++;
+ break;
+ }
+ case kDexAnnotationShort:
+ case kDexAnnotationChar: {
+ if (valueArg > 1) {
+ LOGE("Bogus char/short size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationInt:
+ case kDexAnnotationFloat: {
+ if (valueArg > 3) {
+ LOGE("Bogus int/float size 0x%x\n", valueArg);
+ return NULL;
+ }
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationLong:
+ case kDexAnnotationDouble: {
+ data += valueArg + 1;
+ break;
+ }
+ case kDexAnnotationString: {
+ if (valueArg > 3) {
+ LOGE("Bogus string size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+ break;
+ }
+ case kDexAnnotationType: {
+ if (valueArg > 3) {
+ LOGE("Bogus type size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+ break;
+ }
+ case kDexAnnotationField:
+ case kDexAnnotationEnum: {
+ if (valueArg > 3) {
+ LOGE("Bogus field/enum size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->fieldIdsSize);
+ break;
+ }
+ case kDexAnnotationMethod: {
+ if (valueArg > 3) {
+ LOGE("Bogus method size 0x%x\n", valueArg);
+ return NULL;
+ }
+ u4 idx = readUnsignedLittleEndian(state, &data, valueArg + 1);
+ CHECK_INDEX(idx, state->pHeader->methodIdsSize);
+ break;
+ }
+ case kDexAnnotationArray: {
+ if (valueArg != 0) {
+ LOGE("Bogus array value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedArray(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationAnnotation: {
+ if (valueArg != 0) {
+ LOGE("Bogus annotation value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ data = verifyEncodedAnnotation(state, data, crossVerify);
+ break;
+ }
+ case kDexAnnotationNull: {
+ if (valueArg != 0) {
+ LOGE("Bogus null value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ case kDexAnnotationBoolean: {
+ if (valueArg > 1) {
+ LOGE("Bogus boolean value_arg 0x%x\n", valueArg);
+ return NULL;
+ }
+ // Nothing else to do for this type.
+ break;
+ }
+ default: {
+ LOGE("Bogus value_type 0x%x\n", 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) {
+ LOGE("Bogus encoded_annotation type_idx\n");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->typeIdsSize);
+
+ if (crossVerify) {
+ const char* descriptor = dexStringByTypeIdx(state->pDexFile, idx);
+ if (!dexIsClassDescriptor(descriptor)) {
+ LOGE("Bogus annotation type: '%s'\n", descriptor);
+ return NULL;
+ }
+ }
+
+ u4 size = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+ u4 lastIdx = 0;
+ bool first = true;
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation size\n");
+ return NULL;
+ }
+
+ while (size--) {
+ idx = readAndVerifyUnsignedLeb128(&data, fileEnd, &okay);
+
+ if (!okay) {
+ LOGE("Bogus encoded_annotation name_idx\n");
+ return NULL;
+ }
+
+ CHECK_INDEX(idx, state->pHeader->stringIdsSize);
+
+ if (crossVerify) {
+ const char* name = dexStringById(state->pDexFile, idx);
+ if (!dexIsValidMemberName(name)) {
+ LOGE("Bogus annotation member name: '%s'\n", name);
+ return NULL;
+ }
+ }
+
+ if (first) {
+ first = false;
+ } else if (lastIdx >= idx) {
+ LOGE("Out-of-order encoded_annotation name_idx: 0x%x then 0x%x\n",
+ 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 = ptr;
+
+ CHECK_PTR_RANGE(data, data + 1);
+
+ switch (*(data++)) {
+ case kDexVisibilityBuild:
+ case kDexVisibilityRuntime:
+ case kDexVisibilitySystem: {
+ break;
+ }
+ default: {
+ LOGE("Bogus annotation visibility: 0x%x\n", *data);
+ return NULL;
+ }
+ }
+
+ return (void*) verifyEncodedAnnotation(state, data, false);
+}
+
+/* Perform cross-item verification on annotation_item. */
+static void* crossVerifyAnnotationItem(const CheckState* state, void* ptr) {
+ const u1* data = ptr;
+
+ // Skip the visibility byte.
+ data++;
+
+ return (void*) verifyEncodedAnnotation(state, data, true);
+}
+
+
+
+
+/*
+ * 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 = filePointer(state, newOffset);
+
+ if (offset < newOffset) {
+ ptr = filePointer(state, offset);
+ if (offset < newOffset) {
+ CHECK_OFFSET_RANGE(offset, newOffset);
+ while (offset < newOffset) {
+ if (*ptr != '\0') {
+ LOGE("Non-zero padding 0x%02x @ %x\n", *ptr, offset);
+ return false;
+ }
+ ptr++;
+ offset++;
+ }
+ }
+ }
+
+ u1* newPtr = (u1*) func(state, ptr);
+ newOffset = fileOffset(state, newPtr);
+
+ if (newPtr == NULL) {
+ LOGE("Trouble with item %d @ offset 0x%x\n", i, offset);
+ return false;
+ }
+
+ if (newOffset > state->fileLen) {
+ LOGE("Item %d @ offset 0x%x ends out of bounds\n", 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) {
+ LOGE("Bogus offset for section: got 0x%x; expected 0x%x\n",
+ offset, expectedOffset);
+ return false;
+ }
+
+ if (count != expectedCount) {
+ LOGE("Bogus size for section: got 0x%x; expected 0x%x\n",
+ 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)) {
+ LOGE("Bogus offset for data subsection: 0x%x\n", offset);
+ return false;
+ }
+
+ if (!iterateSectionWithOptionalUpdate(state, offset, count, func,
+ alignment, nextOffset, mapType)) {
+ return false;
+ }
+
+ if (*nextOffset > dataEnd) {
+ LOGE("Out-of-bounds end of data subsection: 0x%x\n", *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 = filePointer(state, lastOffset);
+ while (lastOffset < sectionOffset) {
+ if (*ptr != '\0') {
+ LOGE("Non-zero padding 0x%02x before section start @ %x\n",
+ *ptr, lastOffset);
+ okay = false;
+ break;
+ }
+ ptr++;
+ lastOffset++;
+ }
+ } else if (lastOffset > sectionOffset) {
+ LOGE("Section overlap or out-of-order map: %x, %x\n",
+ 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 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: {
+ LOGE("Unknown map item type %04x\n", type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("Swap of section type %04x failed\n", 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: {
+ okay = iterateSection(state, sectionOffset, sectionCount,
+ crossVerifyClassDefItem, 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: {
+ LOGE("Unknown map item type %04x\n", item->type);
+ return false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("Cross-item verify of section type %04x failed\n",
+ item->type);
+ }
+
+ item++;
+ }
+
+ return okay;
+}
+
+/*
+ * Fix the byte ordering of all fields in the DEX file, and do structural
+ * verification.
+ *
+ * While we're at it, make sure that the file offsets all refer to locations
+ * within the file.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+int dexFixByteOrdering(u1* addr, int len)
+{
+ DexHeader* pHeader;
+ CheckState state;
+ bool okay = true;
+
+ memset(&state, 0, sizeof(state));
+ LOGV("+++ swapping and verifying\n");
+
+ /*
+ * Start by verifying the magic number. The caller verified that "len"
+ * says we have at least a header's worth of data.
+ */
+ pHeader = (DexHeader*) addr;
+ if (memcmp(pHeader->magic, DEX_MAGIC, 4) != 0) {
+ /* really shouldn't be here -- this is weird */
+ LOGE("ERROR: Can't byte swap: bad magic number "
+ "(0x%02x %02x %02x %02x)\n",
+ pHeader->magic[0], pHeader->magic[1],
+ pHeader->magic[2], pHeader->magic[3]);
+ okay = false;
+ }
+
+ if (okay && memcmp(pHeader->magic+4, DEX_MAGIC_VERS, 4) != 0) {
+ /* older or newer version we don't know how to read */
+ LOGE("ERROR: Can't byte swap: bad dex version "
+ "(0x%02x %02x %02x %02x)\n",
+ pHeader->magic[4], pHeader->magic[5],
+ pHeader->magic[6], pHeader->magic[7]);
+ okay = false;
+ }
+
+ if (okay) {
+ int expectedLen = (int) SWAP4(pHeader->fileSize);
+ if (len < expectedLen) {
+ LOGE("ERROR: Bad length: expected %d, got %d\n", expectedLen, len);
+ okay = false;
+ } else if (len != expectedLen) {
+ LOGW("WARNING: Odd length: expected %d, got %d\n", expectedLen,
+ len);
+ // keep going
+ }
+ }
+
+ 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) {
+ LOGE("ERROR: bad checksum (%08lx, expected %08x)\n",
+ adler, expectedChecksum);
+ okay = false;
+ }
+ }
+
+ if (okay) {
+ state.fileStart = addr;
+ state.fileEnd = addr + len;
+ state.fileLen = len;
+ state.pDexFile = NULL;
+ state.pDataMap = 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)) {
+ LOGE("ERROR: Small header size %d, struct %d\n",
+ pHeader->headerSize, (int) sizeof(DexHeader));
+ okay = false;
+ } else if (pHeader->headerSize > sizeof(DexHeader)) {
+ LOGW("WARNING: Large header size %d, struct %d\n",
+ 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 {
+ LOGE("ERROR: No map found; impossible to byte-swap and verify");
+ okay = false;
+ }
+ }
+
+ if (!okay) {
+ LOGE("ERROR: Byte swap + verify failed\n");
+ }
+
+ if (state.pDataMap != NULL) {
+ dexDataMapFree(state.pDataMap);
+ }
+
+ return !okay; // 0 == success
+}
diff --git a/libdex/InstrUtils.c b/libdex/InstrUtils.c
new file mode 100644
index 0000000..b0718f3
--- /dev/null
+++ b/libdex/InstrUtils.c
@@ -0,0 +1,1234 @@
+/*
+ * 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.
+ */
+#include "InstrUtils.h"
+
+#include <stdlib.h>
+
+
+/*
+ * Generate a table that holds the width of all instructions.
+ *
+ * Standard instructions have positive values, optimizer instructions
+ * have negative values, unimplemented instructions have a width of zero.
+ *
+ * I'm doing it with a giant switch statement because it's easier to
+ * maintain and update than a static table with 256 unadorned integers,
+ * and if we're missing a case gcc emits a "warning: enumeration value not
+ * handled" message.
+ *
+ * (To save space in the binary we could generate a static table with a
+ * command-line utility.)
+ */
+InstructionWidth* dexCreateInstrWidthTable(void)
+{
+ InstructionWidth* instrWidth;
+ int i;
+
+ instrWidth = malloc(sizeof(InstructionWidth) * kNumDalvikInstructions);
+ if (instrWidth == NULL)
+ return NULL;
+
+ for (i = 0; i < kNumDalvikInstructions; i++) {
+ OpCode opc = (OpCode) i;
+ int width = 0;
+
+ switch (opc) {
+ case OP_NOP: /* switch-statement data is a special case of NOP */
+ case OP_MOVE:
+ case OP_MOVE_WIDE:
+ case OP_MOVE_OBJECT:
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_WIDE:
+ case OP_MOVE_RESULT_OBJECT:
+ case OP_MOVE_EXCEPTION:
+ case OP_RETURN_VOID:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ case OP_CONST_4:
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ case OP_ARRAY_LENGTH:
+ case OP_THROW:
+ case OP_GOTO:
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ case OP_NEG_FLOAT:
+ case OP_NEG_DOUBLE:
+ case OP_INT_TO_LONG:
+ case OP_INT_TO_FLOAT:
+ case OP_INT_TO_DOUBLE:
+ case OP_LONG_TO_INT:
+ case OP_LONG_TO_FLOAT:
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_LONG:
+ case OP_FLOAT_TO_DOUBLE:
+ case OP_DOUBLE_TO_INT:
+ case OP_DOUBLE_TO_LONG:
+ case OP_DOUBLE_TO_FLOAT:
+ case OP_INT_TO_BYTE:
+ case OP_INT_TO_CHAR:
+ case OP_INT_TO_SHORT:
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_DIV_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ width = 1;
+ break;
+
+ case OP_MOVE_FROM16:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_OBJECT_FROM16:
+ case OP_CONST_16:
+ case OP_CONST_HIGH16:
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_HIGH16:
+ case OP_CONST_STRING:
+ case OP_CONST_CLASS:
+ case OP_CHECK_CAST:
+ case OP_INSTANCE_OF:
+ case OP_NEW_INSTANCE:
+ case OP_NEW_ARRAY:
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ case OP_CMP_LONG:
+ case OP_GOTO_16:
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ case OP_AGET:
+ case OP_AGET_WIDE:
+ case OP_AGET_OBJECT:
+ case OP_AGET_BOOLEAN:
+ case OP_AGET_BYTE:
+ case OP_AGET_CHAR:
+ case OP_AGET_SHORT:
+ case OP_APUT:
+ case OP_APUT_WIDE:
+ case OP_APUT_OBJECT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_IGET:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IPUT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_SGET:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SPUT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_DIV_INT:
+ case OP_REM_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE:
+ case OP_REM_DOUBLE:
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ width = 2;
+ break;
+
+ case OP_MOVE_16:
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_OBJECT_16:
+ case OP_CONST:
+ case OP_CONST_WIDE_32:
+ case OP_CONST_STRING_JUMBO:
+ case OP_GOTO_32:
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ case OP_FILL_ARRAY_DATA:
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ width = 3;
+ break;
+
+ case OP_CONST_WIDE:
+ width = 5;
+ break;
+
+ /*
+ * Optimized instructions. We return negative size values for these
+ * to distinguish them.
+ */
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ width = -2;
+ break;
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ case OP_EXECUTE_INLINE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ width = -3;
+ break;
+
+ /* these should never appear */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_UNUSED_E3:
+ case OP_UNUSED_E4:
+ case OP_UNUSED_E5:
+ case OP_UNUSED_E6:
+ case OP_UNUSED_E7:
+ case OP_UNUSED_E8:
+ case OP_UNUSED_E9:
+ case OP_UNUSED_EA:
+ case OP_UNUSED_EB:
+ case OP_UNUSED_EC:
+ case OP_UNUSED_ED:
+ case OP_UNUSED_EF:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FC:
+ case OP_UNUSED_FD:
+ case OP_UNUSED_FE:
+ case OP_UNUSED_FF:
+ assert(width == 0);
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ instrWidth[opc] = width;
+ }
+
+ return instrWidth;
+}
+
+/*
+ * Generate a table that holds instruction flags.
+ */
+InstructionFlags* dexCreateInstrFlagsTable(void)
+{
+ InstructionFlags* instrFlags;
+ int i;
+
+ instrFlags = malloc(sizeof(InstructionFlags) * kNumDalvikInstructions);
+ if (instrFlags == NULL)
+ return NULL;
+
+ for (i = 0; i < kNumDalvikInstructions; i++) {
+ OpCode opc = (OpCode) i;
+ InstructionFlags flags = 0;
+
+ switch (opc) {
+ /* these don't affect the PC and can't cause an exception */
+ case OP_NOP:
+ case OP_MOVE:
+ case OP_MOVE_FROM16:
+ case OP_MOVE_16:
+ case OP_MOVE_WIDE:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_OBJECT:
+ case OP_MOVE_OBJECT_FROM16:
+ case OP_MOVE_OBJECT_16:
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_WIDE:
+ case OP_MOVE_RESULT_OBJECT:
+ case OP_MOVE_EXCEPTION:
+ case OP_CONST_4:
+ case OP_CONST_16:
+ case OP_CONST:
+ case OP_CONST_HIGH16:
+ case OP_CONST_WIDE_16:
+ case OP_CONST_WIDE_32:
+ case OP_CONST_WIDE:
+ case OP_CONST_WIDE_HIGH16:
+ case OP_FILL_ARRAY_DATA:
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ case OP_CMP_LONG:
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ case OP_NEG_FLOAT:
+ case OP_NEG_DOUBLE:
+ case OP_INT_TO_LONG:
+ case OP_INT_TO_FLOAT:
+ case OP_INT_TO_DOUBLE:
+ case OP_LONG_TO_INT:
+ case OP_LONG_TO_FLOAT:
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_LONG:
+ case OP_FLOAT_TO_DOUBLE:
+ case OP_DOUBLE_TO_INT:
+ case OP_DOUBLE_TO_LONG:
+ case OP_DOUBLE_TO_FLOAT:
+ case OP_INT_TO_BYTE:
+ case OP_INT_TO_CHAR:
+ case OP_INT_TO_SHORT:
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE: // div by zero just returns NaN
+ case OP_REM_DOUBLE:
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ flags = kInstrCanContinue;
+ break;
+
+ /* these don't affect the PC, but can cause exceptions */
+ case OP_CONST_STRING:
+ case OP_CONST_STRING_JUMBO:
+ case OP_CONST_CLASS:
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ case OP_CHECK_CAST:
+ case OP_INSTANCE_OF:
+ case OP_ARRAY_LENGTH:
+ case OP_NEW_INSTANCE:
+ case OP_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY:
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ case OP_AGET:
+ case OP_AGET_BOOLEAN:
+ case OP_AGET_BYTE:
+ case OP_AGET_CHAR:
+ case OP_AGET_SHORT:
+ case OP_AGET_WIDE:
+ case OP_AGET_OBJECT:
+ case OP_APUT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_APUT_WIDE:
+ case OP_APUT_OBJECT:
+ case OP_IGET:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IPUT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_SGET:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SPUT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ case OP_DIV_INT:
+ case OP_REM_INT:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_DIV_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ flags = kInstrCanContinue | kInstrCanThrow;
+ break;
+
+ case OP_RETURN_VOID:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ flags = kInstrCanReturn;
+ break;
+
+ case OP_THROW:
+ flags = kInstrCanThrow;
+ break;
+
+ /* unconditional branches */
+ case OP_GOTO:
+ case OP_GOTO_16:
+ case OP_GOTO_32:
+ flags = kInstrCanBranch;
+ break;
+
+ /* conditional branches */
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ flags = kInstrCanBranch | kInstrCanContinue;
+ break;
+
+ /* switch statements; if value not in switch, it continues */
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ flags = kInstrCanSwitch | kInstrCanContinue;
+ break;
+
+ /* optimizer-generated instructions */
+ case OP_EXECUTE_INLINE:
+ flags = kInstrCanContinue;
+ break;
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ case OP_INVOKE_DIRECT_EMPTY:
+ flags = kInstrCanContinue | kInstrCanThrow;
+ break;
+
+ /* these should never appear */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_UNUSED_E3:
+ case OP_UNUSED_E4:
+ case OP_UNUSED_E5:
+ case OP_UNUSED_E6:
+ case OP_UNUSED_E7:
+ case OP_UNUSED_E8:
+ case OP_UNUSED_E9:
+ case OP_UNUSED_EA:
+ case OP_UNUSED_EB:
+ case OP_UNUSED_EC:
+ case OP_UNUSED_ED:
+ case OP_UNUSED_EF:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FC:
+ case OP_UNUSED_FD:
+ case OP_UNUSED_FE:
+ case OP_UNUSED_FF:
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ instrFlags[opc] = flags;
+ }
+
+ return instrFlags;
+}
+
+/*
+ * Allocate and populate a 256-element array with instruction formats.
+ * Used in conjunction with dexDecodeInstruction.
+ */
+InstructionFormat* dexCreateInstrFormatTable(void)
+{
+ InstructionFormat* instFmt;
+ int i;
+
+ instFmt = malloc(sizeof(InstructionFormat) * kNumDalvikInstructions);
+ if (instFmt == NULL)
+ return NULL;
+
+ for (i = 0; i < kNumDalvikInstructions; i++) {
+ OpCode opc = (OpCode) i;
+ InstructionFormat fmt = kFmtUnknown;
+
+ switch (opc) {
+ case OP_GOTO:
+ fmt = kFmt10t;
+ break;
+ case OP_NOP:
+ case OP_RETURN_VOID:
+ fmt = kFmt10x;
+ break;
+ case OP_CONST_4:
+ fmt = kFmt11n;
+ break;
+ case OP_CONST_HIGH16:
+ case OP_CONST_WIDE_HIGH16:
+ fmt = kFmt21h;
+ break;
+ case OP_MOVE_RESULT:
+ case OP_MOVE_RESULT_WIDE:
+ case OP_MOVE_RESULT_OBJECT:
+ case OP_MOVE_EXCEPTION:
+ case OP_RETURN:
+ case OP_RETURN_WIDE:
+ case OP_RETURN_OBJECT:
+ case OP_MONITOR_ENTER:
+ case OP_MONITOR_EXIT:
+ case OP_THROW:
+ fmt = kFmt11x;
+ break;
+ case OP_MOVE:
+ case OP_MOVE_WIDE:
+ case OP_MOVE_OBJECT:
+ case OP_ARRAY_LENGTH:
+ case OP_NEG_INT:
+ case OP_NOT_INT:
+ case OP_NEG_LONG:
+ case OP_NOT_LONG:
+ case OP_NEG_FLOAT:
+ case OP_NEG_DOUBLE:
+ case OP_INT_TO_LONG:
+ case OP_INT_TO_FLOAT:
+ case OP_INT_TO_DOUBLE:
+ case OP_LONG_TO_INT:
+ case OP_LONG_TO_FLOAT:
+ case OP_LONG_TO_DOUBLE:
+ case OP_FLOAT_TO_INT:
+ case OP_FLOAT_TO_LONG:
+ case OP_FLOAT_TO_DOUBLE:
+ case OP_DOUBLE_TO_INT:
+ case OP_DOUBLE_TO_LONG:
+ case OP_DOUBLE_TO_FLOAT:
+ case OP_INT_TO_BYTE:
+ case OP_INT_TO_CHAR:
+ case OP_INT_TO_SHORT:
+ case OP_ADD_INT_2ADDR:
+ case OP_SUB_INT_2ADDR:
+ case OP_MUL_INT_2ADDR:
+ case OP_DIV_INT_2ADDR:
+ case OP_REM_INT_2ADDR:
+ case OP_AND_INT_2ADDR:
+ case OP_OR_INT_2ADDR:
+ case OP_XOR_INT_2ADDR:
+ case OP_SHL_INT_2ADDR:
+ case OP_SHR_INT_2ADDR:
+ case OP_USHR_INT_2ADDR:
+ case OP_ADD_LONG_2ADDR:
+ case OP_SUB_LONG_2ADDR:
+ case OP_MUL_LONG_2ADDR:
+ case OP_DIV_LONG_2ADDR:
+ case OP_REM_LONG_2ADDR:
+ case OP_AND_LONG_2ADDR:
+ case OP_OR_LONG_2ADDR:
+ case OP_XOR_LONG_2ADDR:
+ case OP_SHL_LONG_2ADDR:
+ case OP_SHR_LONG_2ADDR:
+ case OP_USHR_LONG_2ADDR:
+ case OP_ADD_FLOAT_2ADDR:
+ case OP_SUB_FLOAT_2ADDR:
+ case OP_MUL_FLOAT_2ADDR:
+ case OP_DIV_FLOAT_2ADDR:
+ case OP_REM_FLOAT_2ADDR:
+ case OP_ADD_DOUBLE_2ADDR:
+ case OP_SUB_DOUBLE_2ADDR:
+ case OP_MUL_DOUBLE_2ADDR:
+ case OP_DIV_DOUBLE_2ADDR:
+ case OP_REM_DOUBLE_2ADDR:
+ fmt = kFmt12x;
+ break;
+ case OP_GOTO_16:
+ fmt = kFmt20t;
+ break;
+ case OP_GOTO_32:
+ fmt = kFmt30t;
+ break;
+ case OP_CONST_STRING:
+ case OP_CONST_CLASS:
+ case OP_CHECK_CAST:
+ case OP_NEW_INSTANCE:
+ case OP_SGET:
+ case OP_SGET_WIDE:
+ case OP_SGET_OBJECT:
+ case OP_SGET_BOOLEAN:
+ case OP_SGET_BYTE:
+ case OP_SGET_CHAR:
+ case OP_SGET_SHORT:
+ case OP_SPUT:
+ case OP_SPUT_WIDE:
+ case OP_SPUT_OBJECT:
+ case OP_SPUT_BOOLEAN:
+ case OP_SPUT_BYTE:
+ case OP_SPUT_CHAR:
+ case OP_SPUT_SHORT:
+ fmt = kFmt21c;
+ break;
+ case OP_CONST_16:
+ case OP_CONST_WIDE_16:
+ fmt = kFmt21s;
+ break;
+ case OP_IF_EQZ:
+ case OP_IF_NEZ:
+ case OP_IF_LTZ:
+ case OP_IF_GEZ:
+ case OP_IF_GTZ:
+ case OP_IF_LEZ:
+ fmt = kFmt21t;
+ break;
+ case OP_FILL_ARRAY_DATA:
+ case OP_PACKED_SWITCH:
+ case OP_SPARSE_SWITCH:
+ fmt = kFmt31t;
+ break;
+ case OP_ADD_INT_LIT8:
+ case OP_RSUB_INT_LIT8:
+ case OP_MUL_INT_LIT8:
+ case OP_DIV_INT_LIT8:
+ case OP_REM_INT_LIT8:
+ case OP_AND_INT_LIT8:
+ case OP_OR_INT_LIT8:
+ case OP_XOR_INT_LIT8:
+ case OP_SHL_INT_LIT8:
+ case OP_SHR_INT_LIT8:
+ case OP_USHR_INT_LIT8:
+ fmt = kFmt22b;
+ break;
+ case OP_INSTANCE_OF:
+ case OP_NEW_ARRAY:
+ case OP_IGET:
+ case OP_IGET_WIDE:
+ case OP_IGET_OBJECT:
+ case OP_IGET_BOOLEAN:
+ case OP_IGET_BYTE:
+ case OP_IGET_CHAR:
+ case OP_IGET_SHORT:
+ case OP_IPUT:
+ case OP_IPUT_WIDE:
+ case OP_IPUT_OBJECT:
+ case OP_IPUT_BOOLEAN:
+ case OP_IPUT_BYTE:
+ case OP_IPUT_CHAR:
+ case OP_IPUT_SHORT:
+ fmt = kFmt22c;
+ break;
+ case OP_ADD_INT_LIT16:
+ case OP_RSUB_INT:
+ case OP_MUL_INT_LIT16:
+ case OP_DIV_INT_LIT16:
+ case OP_REM_INT_LIT16:
+ case OP_AND_INT_LIT16:
+ case OP_OR_INT_LIT16:
+ case OP_XOR_INT_LIT16:
+ fmt = kFmt22s;
+ break;
+ case OP_IF_EQ:
+ case OP_IF_NE:
+ case OP_IF_LT:
+ case OP_IF_GE:
+ case OP_IF_GT:
+ case OP_IF_LE:
+ fmt = kFmt22t;
+ break;
+ case OP_MOVE_FROM16:
+ case OP_MOVE_WIDE_FROM16:
+ case OP_MOVE_OBJECT_FROM16:
+ fmt = kFmt22x;
+ break;
+ case OP_CMPL_FLOAT:
+ case OP_CMPG_FLOAT:
+ case OP_CMPL_DOUBLE:
+ case OP_CMPG_DOUBLE:
+ case OP_CMP_LONG:
+ case OP_AGET:
+ case OP_AGET_WIDE:
+ case OP_AGET_OBJECT:
+ case OP_AGET_BOOLEAN:
+ case OP_AGET_BYTE:
+ case OP_AGET_CHAR:
+ case OP_AGET_SHORT:
+ case OP_APUT:
+ case OP_APUT_WIDE:
+ case OP_APUT_OBJECT:
+ case OP_APUT_BOOLEAN:
+ case OP_APUT_BYTE:
+ case OP_APUT_CHAR:
+ case OP_APUT_SHORT:
+ case OP_ADD_INT:
+ case OP_SUB_INT:
+ case OP_MUL_INT:
+ case OP_DIV_INT:
+ case OP_REM_INT:
+ case OP_AND_INT:
+ case OP_OR_INT:
+ case OP_XOR_INT:
+ case OP_SHL_INT:
+ case OP_SHR_INT:
+ case OP_USHR_INT:
+ case OP_ADD_LONG:
+ case OP_SUB_LONG:
+ case OP_MUL_LONG:
+ case OP_DIV_LONG:
+ case OP_REM_LONG:
+ case OP_AND_LONG:
+ case OP_OR_LONG:
+ case OP_XOR_LONG:
+ case OP_SHL_LONG:
+ case OP_SHR_LONG:
+ case OP_USHR_LONG:
+ case OP_ADD_FLOAT:
+ case OP_SUB_FLOAT:
+ case OP_MUL_FLOAT:
+ case OP_DIV_FLOAT:
+ case OP_REM_FLOAT:
+ case OP_ADD_DOUBLE:
+ case OP_SUB_DOUBLE:
+ case OP_MUL_DOUBLE:
+ case OP_DIV_DOUBLE:
+ case OP_REM_DOUBLE:
+ fmt = kFmt23x;
+ break;
+ case OP_CONST:
+ case OP_CONST_WIDE_32:
+ fmt = kFmt31i;
+ break;
+ case OP_CONST_STRING_JUMBO:
+ fmt = kFmt31c;
+ break;
+ case OP_MOVE_16:
+ case OP_MOVE_WIDE_16:
+ case OP_MOVE_OBJECT_16:
+ fmt = kFmt32x;
+ break;
+ case OP_FILLED_NEW_ARRAY:
+ case OP_INVOKE_VIRTUAL:
+ case OP_INVOKE_SUPER:
+ case OP_INVOKE_DIRECT:
+ case OP_INVOKE_STATIC:
+ case OP_INVOKE_INTERFACE:
+ fmt = kFmt35c;
+ break;
+ case OP_FILLED_NEW_ARRAY_RANGE:
+ case OP_INVOKE_VIRTUAL_RANGE:
+ case OP_INVOKE_SUPER_RANGE:
+ case OP_INVOKE_DIRECT_RANGE:
+ case OP_INVOKE_STATIC_RANGE:
+ case OP_INVOKE_INTERFACE_RANGE:
+ fmt = kFmt3rc;
+ break;
+ case OP_CONST_WIDE:
+ fmt = kFmt51l;
+ break;
+
+ /*
+ * Optimized instructions.
+ */
+ case OP_IGET_QUICK:
+ case OP_IGET_WIDE_QUICK:
+ case OP_IGET_OBJECT_QUICK:
+ case OP_IPUT_QUICK:
+ case OP_IPUT_WIDE_QUICK:
+ case OP_IPUT_OBJECT_QUICK:
+ fmt = kFmt22cs;
+ break;
+ case OP_INVOKE_VIRTUAL_QUICK:
+ case OP_INVOKE_SUPER_QUICK:
+ fmt = kFmt35ms;
+ break;
+ case OP_INVOKE_VIRTUAL_QUICK_RANGE:
+ case OP_INVOKE_SUPER_QUICK_RANGE:
+ fmt = kFmt3rms;
+ break;
+ case OP_EXECUTE_INLINE:
+ fmt = kFmt3inline;
+ break;
+ case OP_INVOKE_DIRECT_EMPTY:
+ fmt = kFmt35c;
+ break;
+
+ /* these should never appear */
+ case OP_UNUSED_3E:
+ case OP_UNUSED_3F:
+ case OP_UNUSED_40:
+ case OP_UNUSED_41:
+ case OP_UNUSED_42:
+ case OP_UNUSED_43:
+ case OP_UNUSED_73:
+ case OP_UNUSED_79:
+ case OP_UNUSED_7A:
+ case OP_UNUSED_E3:
+ case OP_UNUSED_E4:
+ case OP_UNUSED_E5:
+ case OP_UNUSED_E6:
+ case OP_UNUSED_E7:
+ case OP_UNUSED_E8:
+ case OP_UNUSED_E9:
+ case OP_UNUSED_EA:
+ case OP_UNUSED_EB:
+ case OP_UNUSED_EC:
+ case OP_UNUSED_ED:
+ case OP_UNUSED_EF:
+ case OP_UNUSED_F1:
+ case OP_UNUSED_FC:
+ case OP_UNUSED_FD:
+ case OP_UNUSED_FE:
+ case OP_UNUSED_FF:
+ fmt = kFmtUnknown;
+ break;
+
+ /*
+ * DO NOT add a "default" clause here. Without it the compiler will
+ * complain if an instruction is missing (which is desirable).
+ */
+ }
+
+ instFmt[opc] = fmt;
+ }
+
+ return instFmt;
+}
+
+/*
+ * Copied from InterpCore.h. Used for instruction decoding.
+ */
+#define FETCH(_offset) (insns[(_offset)])
+#define INST_INST(_inst) ((_inst) & 0xff)
+#define INST_A(_inst) (((u2)(_inst) >> 8) & 0x0f)
+#define INST_B(_inst) ((u2)(_inst) >> 12)
+#define INST_AA(_inst) ((_inst) >> 8)
+
+/*
+ * 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 InstructionFormat* fmts, const u2* insns,
+ DecodedInstruction* pDec)
+{
+ u2 inst = *insns;
+
+ pDec->opCode = (OpCode) INST_INST(inst);
+
+ switch (dexGetInstrFormat(fmts, pDec->opCode)) {
+ 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 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(1) | ((u4) FETCH(2) << 16); // signed 32-bit value
+ break;
+ case kFmt31t: // op vAA, +BBBBBBBB
+ case kFmt31c: // op vAA, thing@BBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1) | ((u4) FETCH(2) << 16); // 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(1) | ((u4) FETCH(2) << 16);
+ break;
+ case kFmt35c: // op vB, {vD..vG,vA}, thing@CCCC
+ case kFmt35ms: // [opt] invoke-virtual+super
+ {
+ /*
+ * The lettering changes that came about when we went from 4 args
+ * to 5 made the "range" versions of the calls different from
+ * the non-range versions. We have the choice between decoding
+ * them the way the spec shows and having lots of conditionals
+ * in the verifier, or mapping the values onto their original
+ * registers and leaving the verifier intact.
+ *
+ * Current plan is to leave the verifier alone. We can fix it
+ * later if it's architecturally unbearable.
+ *
+ * Bottom line: method constant is always in vB.
+ */
+ u2 regList;
+ int i, count;
+
+ pDec->vA = INST_B(inst);
+ pDec->vB = FETCH(1);
+ regList = FETCH(2);
+
+ if (pDec->vA > 5) {
+ LOGW("Invalid arg count in 35c/35ms (%d)\n", pDec->vA);
+ goto bail;
+ }
+ count = pDec->vA;
+ if (count == 5) {
+ /* 5th arg comes from A field in instruction */
+ pDec->arg[4] = INST_A(inst);
+ count--;
+ }
+ for (i = 0; i < count; i++) {
+ pDec->arg[i] = regList & 0x0f;
+ regList >>= 4;
+ }
+ /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */
+ if (pDec->vA > 0)
+ pDec->vC = pDec->arg[0];
+ }
+ break;
+ case kFmt3inline: // [opt] inline invoke
+ {
+ u2 regList;
+ int i;
+
+ pDec->vA = INST_B(inst);
+ pDec->vB = FETCH(1);
+ regList = FETCH(2);
+
+ if (pDec->vA > 4) {
+ LOGW("Invalid arg count in 3inline (%d)\n", pDec->vA);
+ goto bail;
+ }
+ for (i = 0; i < (int) pDec->vA; i++) {
+ pDec->arg[i] = regList & 0x0f;
+ regList >>= 4;
+ }
+ /* copy arg[0] to vC; we don't have vD/vE/vF, so ignore those */
+ if (pDec->vA > 0)
+ pDec->vC = pDec->arg[0];
+ }
+ break;
+ case kFmt35fs: // [opt] invoke-interface
+ assert(false); // TODO
+ break;
+ case kFmt3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+ case kFmt3rms: // [opt] invoke-virtual+super/range
+ pDec->vA = INST_AA(inst);
+ pDec->vB = FETCH(1);
+ pDec->vC = FETCH(2);
+ break;
+ case kFmt3rfs: // [opt] invoke-interface/range
+ assert(false); // TODO
+ break;
+ case kFmt51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ pDec->vA = INST_AA(inst);
+ pDec->vB_wide = FETCH(1);
+ pDec->vB_wide |= (u8)FETCH(2) << 16;
+ pDec->vB_wide |= (u8)FETCH(3) << 32;
+ pDec->vB_wide |= (u8)FETCH(4) << 48;
+ break;
+ default:
+ LOGW("Can't decode unexpected format %d (op=%d)\n",
+ dexGetInstrFormat(fmts, pDec->opCode), pDec->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.
+ */
+int dexGetInstrOrTableWidthAbs(const InstructionWidth* widths, const u2* insns)
+{
+ int 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);
+ width = 4 + (elemWidth * len + 1) / 2;
+ } else {
+ width = dexGetInstrWidthAbs(widths, INST_INST(insns[0]));
+ }
+ return width;
+}
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
new file mode 100644
index 0000000..9d8e5c3
--- /dev/null
+++ b/libdex/InstrUtils.h
@@ -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.
+ */
+/*
+ * Dalvik instruction utility functions.
+ */
+#ifndef _LIBDEX_INSTRUTILS
+#define _LIBDEX_INSTRUTILS
+
+#include "DexFile.h"
+#include "OpCode.h"
+
+/*
+ * Dalvik-defined instruction formats.
+ *
+ * (This defines InstructionFormat as an unsigned char to reduce the size
+ * of the table. This isn't necessary with some compilers, which use an
+ * integer width appropriate for the number of enum values.)
+ *
+ * If you add or delete a format, you have to change some or all of:
+ * - this enum
+ * - the switch inside dexDecodeInstruction() in InstrUtils.c
+ * - the switch inside dumpInstruction() in DexDump.c
+ */
+typedef unsigned char InstructionFormat;
+enum InstructionFormat {
+ kFmtUnknown = 0,
+ kFmt10x, // op
+ kFmt12x, // op vA, vB
+ kFmt11n, // op vA, #+B
+ kFmt11x, // op vAA
+ kFmt10t, // op +AA
+ 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
+ kFmt32x, // op vAAAA, vBBBB
+ kFmt30t, // op +AAAAAAAA
+ kFmt31t, // op vAA, +BBBBBBBB
+ kFmt31i, // op vAA, #+BBBBBBBB
+ kFmt31c, // op vAA, thing@BBBBBBBB
+ kFmt35c, // op {vC, vD, vE, vF, vG}, thing@BBBB (B: count, A: vG)
+ kFmt35ms, // [opt] invoke-virtual+super
+ kFmt35fs, // [opt] invoke-interface
+ kFmt3rc, // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+ kFmt3rms, // [opt] invoke-virtual+super/range
+ kFmt3rfs, // [opt] invoke-interface/range
+ kFmt3inline, // [opt] inline invoke
+ kFmt51l, // op vAA, #+BBBBBBBBBBBBBBBB
+};
+
+/*
+ * Holds the contents of a decoded instruction.
+ */
+typedef 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;
+} DecodedInstruction;
+
+/*
+ * Instruction width, a value in the range -3 to 5.
+ */
+typedef signed char InstructionWidth;
+
+/*
+ * Instruction flags, used by the verifier to determine where control
+ * can flow to next.
+ */
+typedef unsigned char InstructionFlags;
+enum InstructionFlags {
+ 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
+};
+
+
+/*
+ * Allocate and populate a 256-element array with instruction widths. A
+ * width of zero means the entry does not exist.
+ */
+InstructionWidth* dexCreateInstrWidthTable(void);
+
+/*
+ * Returns the width of the specified instruction, or 0 if not defined.
+ * Optimized instructions use negative values.
+ */
+DEX_INLINE int dexGetInstrWidth(const InstructionWidth* widths, OpCode opCode)
+{
+ // assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+ return widths[opCode];
+}
+
+/*
+ * Return the width of the specified instruction, or 0 if not defined.
+ */
+DEX_INLINE int dexGetInstrWidthAbs(const InstructionWidth* widths,OpCode opCode)
+{
+ //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+
+ int val = dexGetInstrWidth(widths, opCode);
+ if (val < 0)
+ val = -val;
+ /* XXX - the no-compare trick may be a cycle slower on ARM */
+ return val;
+}
+
+/*
+ * 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.
+ */
+int dexGetInstrOrTableWidthAbs(const InstructionWidth* widths, const u2* insns);
+
+
+/*
+ * Allocate and populate a 256-element array with instruction flags.
+ */
+InstructionFlags* dexCreateInstrFlagsTable(void);
+
+/*
+ * Returns the flags for the specified opcode.
+ */
+DEX_INLINE int dexGetInstrFlags(const InstructionFlags* flags, OpCode opCode)
+{
+ //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+ return flags[opCode];
+}
+
+
+/*
+ * Allocate and populate a 256-element array with instruction formats.
+ */
+InstructionFormat* dexCreateInstrFormatTable(void);
+
+/*
+ * Return the instruction format for the specified opcode.
+ */
+DEX_INLINE InstructionFormat dexGetInstrFormat(const InstructionFormat* fmts,
+ OpCode opCode)
+{
+ //assert(/*opCode >= 0 &&*/ opCode < kNumDalvikInstructions);
+ return fmts[opCode];
+}
+
+/*
+ * Decode the instruction pointed to by "insns".
+ */
+void dexDecodeInstruction(const InstructionFormat* fmts, const u2* insns,
+ DecodedInstruction* pDec);
+
+#endif /*_LIBDEX_INSTRUTILS*/
diff --git a/libdex/Leb128.c b/libdex/Leb128.c
new file mode 100644
index 0000000..ed09e19
--- /dev/null
+++ b/libdex/Leb128.c
@@ -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 0000000..215ae30
--- /dev/null
+++ b/libdex/Leb128.h
@@ -0,0 +1,127 @@
+/*
+ * 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
+#define _LIBDEX_LEB128
+
+#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);
+
+#endif
diff --git a/libdex/OpCode.h b/libdex/OpCode.h
new file mode 100644
index 0000000..d389472
--- /dev/null
+++ b/libdex/OpCode.h
@@ -0,0 +1,653 @@
+/*
+ * 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 enumeration.
+ */
+#ifndef _LIBDEX_OPCODE
+#define _LIBDEX_OPCODE
+
+/*
+ * If you add, delete, or renumber instructions, you need to change things
+ * in various places. Renumbering really only affects the "unused" opcodes,
+ * which are given explicit enumeration values to make it easier to find
+ * the places in the code that need to be updated when making changes --
+ * if you replace "OP_UNUSED_2D" and neglect to update a switch statement,
+ * the compiler will complain about an unknown value.
+ *
+ * Opcode definitions and attributes:
+ * - update the OpCode enum below
+ * - update the "goto table" definition macro, DEFINE_GOTO_TABLE(), below
+ * - update the instruction info table generators and (if you changed an
+ * instruction format) instruction decoder in InstrUtils.c
+ * - update the instruction format list in InstrUtils.h, if necessary
+ * - update the parallel definitions in the class dalvik.bytecode.Opcodes
+ *
+ * Interpreter:
+ * - implement/update the instruction in C in mterp/c/...
+ * - verify new code by running with "dalvik.vm.execution-mode =
+ * int:portable" or "-Xint:portable"
+ * - implement/update the instruction in ARM in mterp/armv5/...
+ * - verify by enabling ARM handler for that instruction in mterp config
+ * and running int:fast as above
+ * - repeat for other platforms (x86, ...)
+ * (see notes in mterp/ReadMe.txt for rebuilding instructions)
+ *
+ * Verifier / optimizer:
+ * - update some stuff in analysis/DexOptimize.c, analysis/DexVerify.c,
+ * and/or analysis/CodeVerify.c as needed
+ * - verify by running with verifier enabled (it's on by default)
+ *
+ * Tools:
+ * - update the OpCodeNames table in dexdump/OpCodeNames.c
+ * - update dexdump/DexDump.c if an instruction format has changed
+ *
+ * Note: The Dalvik VM tests (in the tests subdirectory) provide a convenient
+ * way to test most of the above without doing any rebuilds. In particular,
+ * test 003-omnibus-opcodes will exercise most of the opcodes.
+ */
+
+/*
+ * Dalvik opcode list.
+ */
+typedef enum OpCode {
+ 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, /* no _LIT16 suffix for this */
+ 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_UNUSED_E3 = 0xe3,
+ OP_UNUSED_E4 = 0xe4,
+ OP_UNUSED_E5 = 0xe5,
+ OP_UNUSED_E6 = 0xe6,
+ OP_UNUSED_E7 = 0xe7,
+ OP_UNUSED_E8 = 0xe8,
+ OP_UNUSED_E9 = 0xe9,
+ OP_UNUSED_EA = 0xea,
+ OP_UNUSED_EB = 0xeb,
+ OP_UNUSED_EC = 0xec,
+ OP_UNUSED_ED = 0xed,
+
+ /* optimizer output -- these are never generated by "dx" */
+ OP_EXECUTE_INLINE = 0xee,
+ OP_UNUSED_EF = 0xef, /* OP_EXECUTE_INLINE_RANGE? */
+
+ OP_INVOKE_DIRECT_EMPTY = 0xf0,
+ OP_UNUSED_F1 = 0xf1, /* OP_INVOKE_DIRECT_EMPTY_RANGE? */
+ OP_IGET_QUICK = 0xf2,
+ OP_IGET_WIDE_QUICK = 0xf3,
+ OP_IGET_OBJECT_QUICK = 0xf4,
+ OP_IPUT_QUICK = 0xf5,
+ OP_IPUT_WIDE_QUICK = 0xf6,
+ OP_IPUT_OBJECT_QUICK = 0xf7,
+
+ OP_INVOKE_VIRTUAL_QUICK = 0xf8,
+ OP_INVOKE_VIRTUAL_QUICK_RANGE = 0xf9,
+ OP_INVOKE_SUPER_QUICK = 0xfa,
+ OP_INVOKE_SUPER_QUICK_RANGE = 0xfb,
+ OP_UNUSED_FC = 0xfc, /* OP_INVOKE_DIRECT_QUICK? */
+ OP_UNUSED_FD = 0xfd, /* OP_INVOKE_DIRECT_QUICK_RANGE? */
+ OP_UNUSED_FE = 0xfe, /* OP_INVOKE_INTERFACE_QUICK? */
+ OP_UNUSED_FF = 0xff, /* OP_INVOKE_INTERFACE_QUICK_RANGE*/
+} OpCode;
+
+#define kNumDalvikInstructions 256
+
+/*
+ * Switch-statement signatures are a "NOP" followed by a code. (A true NOP
+ * is 0x0000.)
+ */
+#define kPackedSwitchSignature 0x0100
+#define kSparseSwitchSignature 0x0200
+#define kArrayDataSignature 0x0300
+
+/*
+ * Macro used to generate computed goto tables for the C interpreter.
+ *
+ * The labels here must match up with the labels in the interpreter
+ * implementation. There is no direct connection between these and the
+ * numeric definitions above, but if the two get out of sync strange things
+ * will happen.
+ */
+#define DEFINE_GOTO_TABLE(_name) \
+ static const void* _name[kNumDalvikInstructions] = { \
+ /* 00..0f */ \
+ 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), \
+ /* 10..1f */ \
+ 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), \
+ /* 20..2f */ \
+ 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), \
+ /* 30..3f */ \
+ 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), \
+ /* 40..4f */ \
+ 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), \
+ /* 50..5f */ \
+ 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), \
+ /* 60..6f */ \
+ 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), \
+ /* 70..7f */ \
+ 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), \
+ /* 80..8f */ \
+ 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), \
+ /* 90..9f */ \
+ 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), \
+ /* a0..af */ \
+ 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), \
+ /* b0..bf */ \
+ 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), \
+ /* c0..cf */ \
+ 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), \
+ /* d0..df */ \
+ 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), \
+ /* e0..ef */ \
+ H(OP_SHL_INT_LIT8), \
+ H(OP_SHR_INT_LIT8), \
+ H(OP_USHR_INT_LIT8), \
+ H(OP_UNUSED_E3), \
+ H(OP_UNUSED_E4), \
+ H(OP_UNUSED_E5), \
+ H(OP_UNUSED_E6), \
+ H(OP_UNUSED_E7), \
+ H(OP_UNUSED_E8), \
+ H(OP_UNUSED_E9), \
+ H(OP_UNUSED_EA), \
+ H(OP_UNUSED_EB), \
+ H(OP_UNUSED_EC), \
+ H(OP_UNUSED_ED), \
+ H(OP_EXECUTE_INLINE), \
+ H(OP_UNUSED_EF), \
+ /* f0..ff */ \
+ H(OP_INVOKE_DIRECT_EMPTY), \
+ H(OP_UNUSED_F1), \
+ H(OP_IGET_QUICK), \
+ H(OP_IGET_WIDE_QUICK), \
+ H(OP_IGET_OBJECT_QUICK), \
+ H(OP_IPUT_QUICK), \
+ H(OP_IPUT_WIDE_QUICK), \
+ H(OP_IPUT_OBJECT_QUICK), \
+ H(OP_INVOKE_VIRTUAL_QUICK), \
+ H(OP_INVOKE_VIRTUAL_QUICK_RANGE), \
+ H(OP_INVOKE_SUPER_QUICK), \
+ H(OP_INVOKE_SUPER_QUICK_RANGE), \
+ H(OP_UNUSED_FC), \
+ H(OP_UNUSED_FD), \
+ H(OP_UNUSED_FE), \
+ H(OP_UNUSED_FF), \
+ };
+
+#endif /*_LIBDEX_OPCODE*/
diff --git a/libdex/OptInvocation.c b/libdex/OptInvocation.c
new file mode 100644
index 0000000..8ce918b
--- /dev/null
+++ b/libdex/OptInvocation.c
@@ -0,0 +1,143 @@
+/*
+ * 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 managing an invocation of "dexopt".
+ */
+#include "vm/DalvikVersion.h"
+
+#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* kClassesDex = "classes.dex";
+
+
+/*
+ * 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];
+ static const char kDexCachePath[] = "dalvik-cache";
+ 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) {
+ LOGE("Can't get CWD while opening jar file\n");
+ return NULL;
+ }
+ strncat(absoluteFile, "/", kBufLen);
+ }
+ strncat(absoluteFile, fileName, kBufLen);
+
+ /*
+ * 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);
+ strncat(absoluteFile, subFileName, kBufLen);
+ }
+
+ /* 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, kDexCachePath);
+
+ /* Tack on the file name for the actual cache file path.
+ */
+ strncat(nameBuf, absoluteFile, kBufLen);
+
+ LOGV("Cache file for '%s' '%s' is '%s'\n", 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)) {
+ int err = errno ? errno : -1;
+ LOGE("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 0000000..d9708ca
--- /dev/null
+++ b/libdex/OptInvocation.h
@@ -0,0 +1,50 @@
+/*
+ * 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
+#define _LIBDEX_OPTINVOCATION
+
+#include <stdint.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * Utility routines, used by the VM.
+ */
+char* dexOptGenerateCacheFileName(const char* fileName,
+ const char* subFileName);
+int dexOptCreateEmptyHeader(int fd);
+
+/* some flags that get passed through to "dexopt" command */
+#define DEXOPT_OPT_ENABLED (1)
+#define DEXOPT_OPT_ALL (1 << 1)
+#define DEXOPT_VERIFY_ENABLED (1 << 2)
+#define DEXOPT_VERIFY_ALL (1 << 3)
+#define DEXOPT_IS_BOOTSTRAP (1 << 4)
+#define DEXOPT_GEN_REGISTER_MAP (1 << 5)
+
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif /*_LIBDEX_OPTINVOCATION*/
diff --git a/libdex/SysUtil.c b/libdex/SysUtil.c
new file mode 100644
index 0000000..530ac2e
--- /dev/null
+++ b/libdex/SysUtil.c
@@ -0,0 +1,286 @@
+/*
+ * 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>
+
+#ifdef HAVE_POSIX_FILEMAP
+#include <sys/mman.h>
+#endif
+
+#include <limits.h>
+#include <errno.h>
+
+/*
+ * Having trouble finding a portable way to get this. sysconf(_SC_PAGE_SIZE)
+ * seems appropriate, but we don't have that on the device. Some systems
+ * have getpagesize(2), though the linux man page has some odd cautions.
+ */
+#define DEFAULT_PAGE_SIZE 4096
+
+
+/*
+ * 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)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ void* ptr;
+
+ ptr = mmap(NULL, length, PROT_READ | PROT_WRITE,
+ MAP_SHARED | MAP_ANON, -1, 0);
+ if (ptr == MAP_FAILED) {
+ LOGW("mmap(%d, RW, SHARED|ANON) failed: %s\n", (int) length,
+ strerror(errno));
+ return NULL;
+ }
+
+ return ptr;
+#else
+ LOGE("sysCreateAnonShmem not implemented.\n");
+ return NULL;
+#endif
+}
+
+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) {
+ LOGE("could not determine length of file\n");
+ return -1;
+ }
+
+ length = end - start;
+ if (length == 0) {
+ LOGE("file is empty\n");
+ return -1;
+ }
+
+ *start_ = start;
+ *length_ = length;
+
+ return 0;
+}
+
+/*
+ * Pull the contents of a file into an new shared memory segment. We grab
+ * everything from fd's current offset on.
+ *
+ * We need to know the length ahead of time so we can allocate a segment
+ * of sufficient size.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ off_t start;
+ size_t length, actual;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &start, &length) < 0)
+ return -1;
+
+ memPtr = sysCreateAnonShmem(length);
+ if (memPtr == NULL)
+ return -1;
+
+ actual = read(fd, memPtr, length);
+ if (actual != length) {
+ LOGE("only read %d of %d bytes\n", (int) actual, (int) length);
+ sysReleaseShmem(pMap);
+ return -1;
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#else
+ LOGE("sysLoadFileInShmem not implemented.\n");
+ return -1;
+#endif
+}
+
+/*
+ * Map a file (from fd's current offset) into a shared, read-only memory
+ * segment. The file offset must be a multiple of the page size.
+ *
+ * On success, returns 0 and fills out "pMap". On failure, returns a nonzero
+ * value and does not disturb "pMap".
+ */
+int sysMapFileInShmem(int fd, MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ 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, MAP_FILE | MAP_SHARED, fd, start);
+ if (memPtr == MAP_FAILED) {
+ LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n", (int) length,
+ fd, (int) start, strerror(errno));
+ return -1;
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#else
+ /* 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) {
+ LOGW("read(fd=%d, start=%d, length=%d) failed: %s\n", (int) length,
+ fd, (int) start, strerror(errno));
+ return -1;
+ }
+
+ pMap->baseAddr = pMap->addr = memPtr;
+ pMap->baseLength = pMap->length = length;
+
+ return 0;
+#endif
+}
+
+/*
+ * Map part of a file (from fd's current offset) into a shared, read-only
+ * memory segment.
+ *
+ * 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, long length,
+ MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ off_t dummy;
+ size_t fileLength, actualLength;
+ off_t actualStart;
+ int adjust;
+ void* memPtr;
+
+ assert(pMap != NULL);
+
+ if (getFileStartAndLength(fd, &dummy, &fileLength) < 0)
+ return -1;
+
+ if (start + length > (long)fileLength) {
+ LOGW("bad segment: st=%d len=%ld flen=%d\n",
+ (int) start, length, (int) fileLength);
+ return -1;
+ }
+
+ /* adjust to be page-aligned */
+ adjust = start % DEFAULT_PAGE_SIZE;
+ actualStart = start - adjust;
+ actualLength = length + adjust;
+
+ memPtr = mmap(NULL, actualLength, PROT_READ, MAP_FILE | MAP_SHARED,
+ fd, actualStart);
+ if (memPtr == MAP_FAILED) {
+ LOGW("mmap(%d, R, FILE|SHARED, %d, %d) failed: %s\n",
+ (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\n",
+ (int) start, (int) length,
+ pMap->baseAddr, (int) pMap->baseLength,
+ pMap->addr, (int) pMap->length);
+
+ return 0;
+#else
+ LOGE("sysMapFileSegmentInShmem not implemented.\n");
+ return -1;
+#endif
+}
+
+/*
+ * Release a memory mapping.
+ */
+void sysReleaseShmem(MemMapping* pMap)
+{
+#ifdef HAVE_POSIX_FILEMAP
+ if (pMap->baseAddr == NULL && pMap->baseLength == 0)
+ return;
+
+ if (munmap(pMap->baseAddr, pMap->baseLength) < 0) {
+ LOGW("munmap(%p, %d) failed: %s\n",
+ pMap->baseAddr, (int)pMap->baseLength, strerror(errno));
+ } else {
+ LOGV("munmap(%p, %d) succeeded\n", 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));
+}
+
diff --git a/libdex/SysUtil.h b/libdex/SysUtil.h
new file mode 100644
index 0000000..8d85efa
--- /dev/null
+++ b/libdex/SysUtil.h
@@ -0,0 +1,72 @@
+/*
+ * 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
+#define _LIBDEX_SYSUTIL
+
+#include <sys/types.h>
+
+/*
+ * Use this to keep track of mapped segments.
+ */
+typedef 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 */
+} MemMapping;
+
+/*
+ * Copy a map.
+ */
+void sysCopyMap(MemMapping* dst, const MemMapping* src);
+
+/*
+ * Load a file into a new shared memory segment. All data from the current
+ * offset to the end of the file is pulled in.
+ *
+ * The segment is read-write, allowing VM fixups. (It should be modified
+ * to support .gz/.zip compressed data.)
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysLoadFileInShmem(int fd, MemMapping* pMap);
+
+/*
+ * Map a file (from fd's current offset) into a shared,
+ * read-only memory segment.
+ *
+ * On success, "pMap" is filled in, and zero is returned.
+ */
+int sysMapFileInShmem(int fd, MemMapping* pMap);
+
+/*
+ * Like sysMapFileInShmem, but on only part of a file.
+ */
+int sysMapFileSegmentInShmem(int fd, off_t start, long length,
+ 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);
+
+#endif /*_DALVIK_SYSUTIL*/
diff --git a/libdex/ZipArchive.c b/libdex/ZipArchive.c
new file mode 100644
index 0000000..a75a85b
--- /dev/null
+++ b/libdex/ZipArchive.c
@@ -0,0 +1,643 @@
+/*
+ * 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.
+ */
+#include "ZipArchive.h"
+
+#include <zlib.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+
+/*
+ * Zip file constants.
+ */
+#define kEOCDSignature 0x06054b50
+#define kEOCDLen 22
+#define kEOCDNumEntries 8 // offset to #of entries in file
+#define kEOCDFileOffset 16 // offset to central directory
+
+#define kMaxCommentLen 65535 // longest possible in ushort
+#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen)
+
+#define kLFHSignature 0x04034b50
+#define kLFHLen 30 // excluding variable-len fields
+#define kLFHNameLen 26 // offset to filename length
+#define kLFHExtraLen 28 // offset to extra length
+
+#define kCDESignature 0x02014b50
+#define kCDELen 46 // excluding variable-len fields
+#define kCDEMethod 10 // offset to compression method
+#define kCDEModWhen 12 // offset to modification timestamp
+#define kCDECRC 16 // offset to entry CRC
+#define kCDECompLen 20 // offset to compressed length
+#define kCDEUncompLen 24 // offset to uncompressed length
+#define kCDENameLen 28 // offset to filename length
+#define kCDEExtraLen 30 // offset to extra length
+#define kCDECommentLen 32 // offset to comment length
+#define kCDELocalOffset 42 // offset to local hdr
+
+/*
+ * The values we return for ZipEntry use 0 as an invalid value, so we
+ * want to adjust the hash table index by a fixed amount. Using a large
+ * value helps insure that people don't mix & match arguments, e.g. with
+ * entry indices.
+ */
+#define kZipEntryAdj 10000
+
+/*
+ * Convert a ZipEntry to a hash table index, verifying that it's in a
+ * valid range.
+ */
+static int entryToIndex(const ZipArchive* pArchive, const ZipEntry entry)
+{
+ long ent = ((long) entry) - kZipEntryAdj;
+ if (ent < 0 || ent >= pArchive->mHashTableSize ||
+ pArchive->mHashTable[ent].name == NULL)
+ {
+ LOGW("Invalid ZipEntry %p (%ld)\n", entry, ent);
+ return -1;
+ }
+ return ent;
+}
+
+/*
+ * Simple string hash function for non-null-terminated strings.
+ */
+static unsigned int computeHash(const char* str, int len)
+{
+ unsigned int hash = 0;
+
+ while (len--)
+ hash = hash * 31 + *str++;
+
+ return hash;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+static void addToHash(ZipArchive* pArchive, const char* str, int strLen,
+ unsigned int hash)
+{
+ const int hashTableSize = pArchive->mHashTableSize;
+ int ent = hash & (hashTableSize - 1);
+
+ /*
+ * We over-allocated the table, so we're guaranteed to find an empty slot.
+ */
+ while (pArchive->mHashTable[ent].name != NULL)
+ ent = (ent + 1) & (hashTableSize-1);
+
+ pArchive->mHashTable[ent].name = str;
+ pArchive->mHashTable[ent].nameLen = strLen;
+}
+
+/*
+ * Get 2 little-endian bytes.
+ */
+static u2 get2LE(unsigned char const* pSrc)
+{
+ return pSrc[0] | (pSrc[1] << 8);
+}
+
+/*
+ * Get 4 little-endian bytes.
+ */
+static u4 get4LE(unsigned char const* pSrc)
+{
+ u4 result;
+
+ result = pSrc[0];
+ result |= pSrc[1] << 8;
+ result |= pSrc[2] << 16;
+ result |= pSrc[3] << 24;
+
+ return result;
+}
+
+/*
+ * Parse the Zip archive, verifying its contents and initializing internal
+ * data structures.
+ */
+static bool parseZipArchive(ZipArchive* pArchive, const MemMapping* pMap)
+{
+#define CHECK_OFFSET(_off) { \
+ if ((unsigned int) (_off) >= maxOffset) { \
+ LOGE("ERROR: bad offset %u (max %d): %s\n", \
+ (unsigned int) (_off), maxOffset, #_off); \
+ goto bail; \
+ } \
+ }
+ bool result = false;
+ const unsigned char* basePtr = (const unsigned char*)pMap->addr;
+ const unsigned char* ptr;
+ size_t length = pMap->length;
+ unsigned int i, numEntries, cdOffset;
+ unsigned int val;
+
+ /*
+ * The first 4 bytes of the file will either be the local header
+ * signature for the first file (kLFHSignature) or, if the archive doesn't
+ * have any files in it, the end-of-central-directory signature
+ * (kEOCDSignature).
+ */
+ val = get4LE(basePtr);
+ if (val == kEOCDSignature) {
+ LOGI("Found Zip archive, but it looks empty\n");
+ goto bail;
+ } else if (val != kLFHSignature) {
+ LOGV("Not a Zip archive (found 0x%08x)\n", val);
+ goto bail;
+ }
+
+ /*
+ * Find the EOCD. We'll find it immediately unless they have a file
+ * comment.
+ */
+ ptr = basePtr + length - kEOCDLen;
+
+ while (ptr >= basePtr) {
+ if (*ptr == (kEOCDSignature & 0xff) && get4LE(ptr) == kEOCDSignature)
+ break;
+ ptr--;
+ }
+ if (ptr < basePtr) {
+ LOGI("Could not find end-of-central-directory in Zip\n");
+ goto bail;
+ }
+
+ /*
+ * There are two interesting items in the EOCD block: the number of
+ * entries in the file, and the file offset of the start of the
+ * central directory.
+ *
+ * (There's actually a count of the #of entries in this file, and for
+ * all files which comprise a spanned archive, but for our purposes
+ * we're only interested in the current file. Besides, we expect the
+ * two to be equivalent for our stuff.)
+ */
+ numEntries = get2LE(ptr + kEOCDNumEntries);
+ cdOffset = get4LE(ptr + kEOCDFileOffset);
+
+ /* valid offsets are [0,EOCD] */
+ unsigned int maxOffset;
+ maxOffset = (ptr - basePtr) +1;
+
+ LOGV("+++ numEntries=%d cdOffset=%d\n", numEntries, cdOffset);
+ if (numEntries == 0 || cdOffset >= length) {
+ LOGW("Invalid entries=%d offset=%d (len=%zd)\n",
+ numEntries, cdOffset, length);
+ goto bail;
+ }
+
+ /*
+ * Create hash table. We have a minimum 75% load factor, possibly as
+ * low as 50% after we round off to a power of 2. There must be at
+ * least one unused entry to avoid an infinite loop during creation.
+ */
+ pArchive->mNumEntries = numEntries;
+ pArchive->mHashTableSize = dexRoundUpPower2(1 + (numEntries * 4) / 3);
+ pArchive->mHashTable = (ZipHashEntry*)
+ calloc(pArchive->mHashTableSize, sizeof(ZipHashEntry));
+
+ /*
+ * Walk through the central directory, adding entries to the hash
+ * table.
+ */
+ ptr = basePtr + cdOffset;
+ for (i = 0; i < numEntries; i++) {
+ unsigned int fileNameLen, extraLen, commentLen, localHdrOffset;
+ const unsigned char* localHdr;
+ unsigned int hash;
+
+ if (get4LE(ptr) != kCDESignature) {
+ LOGW("Missed a central dir sig (at %d)\n", i);
+ goto bail;
+ }
+ if (ptr + kCDELen > basePtr + length) {
+ LOGW("Ran off the end (at %d)\n", i);
+ goto bail;
+ }
+
+ localHdrOffset = get4LE(ptr + kCDELocalOffset);
+ CHECK_OFFSET(localHdrOffset);
+ fileNameLen = get2LE(ptr + kCDENameLen);
+ extraLen = get2LE(ptr + kCDEExtraLen);
+ commentLen = get2LE(ptr + kCDECommentLen);
+
+ //LOGV("+++ %d: localHdr=%d fnl=%d el=%d cl=%d\n",
+ // i, localHdrOffset, fileNameLen, extraLen, commentLen);
+ //LOGV(" '%.*s'\n", fileNameLen, ptr + kCDELen);
+
+ /* add the CDE filename to the hash table */
+ hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
+ addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash);
+
+ localHdr = basePtr + localHdrOffset;
+ if (get4LE(localHdr) != kLFHSignature) {
+ LOGW("Bad offset to local header: %d (at %d)\n",
+ localHdrOffset, i);
+ goto bail;
+ }
+
+ ptr += kCDELen + fileNameLen + extraLen + commentLen;
+ CHECK_OFFSET(ptr - basePtr);
+ }
+
+ result = true;
+
+bail:
+ return result;
+#undef CHECK_OFFSET
+}
+
+/*
+ * Open the specified file read-only. We memory-map the entire thing and
+ * parse the contents.
+ *
+ * This will be called on non-Zip files, especially during VM startup, so
+ * we don't want to be too noisy about certain types of failure. (Do
+ * we want a "quiet" flag?)
+ *
+ * On success, we fill out the contents of "pArchive" and return 0.
+ */
+int dexZipOpenArchive(const char* fileName, ZipArchive* pArchive)
+{
+ int fd, err;
+
+ LOGV("Opening archive '%s' %p\n", fileName, pArchive);
+
+ fd = open(fileName, O_RDONLY, 0);
+ if (fd < 0) {
+ err = errno ? errno : -1;
+ LOGV("Unable to open '%s': %s\n", fileName, strerror(err));
+ return err;
+ }
+
+ return dexZipPrepArchive(fd, fileName, pArchive);
+}
+
+/*
+ * Prepare to access a ZipArchive in an open file descriptor.
+ */
+int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* pArchive)
+{
+ MemMapping map;
+ int err;
+
+ map.addr = NULL;
+ memset(pArchive, 0, sizeof(*pArchive));
+
+ pArchive->mFd = fd;
+
+ if (sysMapFileInShmem(pArchive->mFd, &map) != 0) {
+ err = -1;
+ LOGW("Map of '%s' failed\n", debugFileName);
+ goto bail;
+ }
+
+ if (map.length < kEOCDLen) {
+ err = -1;
+ LOGV("File '%s' too small to be zip (%zd)\n", debugFileName,map.length);
+ goto bail;
+ }
+
+ if (!parseZipArchive(pArchive, &map)) {
+ err = -1;
+ LOGV("Parsing '%s' failed\n", debugFileName);
+ goto bail;
+ }
+
+ /* success */
+ err = 0;
+ sysCopyMap(&pArchive->mMap, &map);
+ map.addr = NULL;
+
+bail:
+ if (err != 0)
+ dexZipCloseArchive(pArchive);
+ if (map.addr != NULL)
+ sysReleaseShmem(&map);
+ return err;
+}
+
+
+/*
+ * Close a ZipArchive, closing the file and freeing the contents.
+ *
+ * NOTE: the ZipArchive may not have been fully created.
+ */
+void dexZipCloseArchive(ZipArchive* pArchive)
+{
+ LOGV("Closing archive %p\n", pArchive);
+
+ if (pArchive->mFd >= 0)
+ close(pArchive->mFd);
+
+ sysReleaseShmem(&pArchive->mMap);
+
+ free(pArchive->mHashTable);
+
+ pArchive->mFd = -1;
+ pArchive->mNumEntries = -1;
+ pArchive->mHashTableSize = -1;
+ pArchive->mHashTable = NULL;
+}
+
+
+/*
+ * Find a matching entry.
+ *
+ * Returns 0 if not found.
+ */
+ZipEntry dexZipFindEntry(const ZipArchive* pArchive, const char* entryName)
+{
+ int nameLen = strlen(entryName);
+ unsigned int hash = computeHash(entryName, nameLen);
+ const int hashTableSize = pArchive->mHashTableSize;
+ int ent = hash & (hashTableSize-1);
+
+ while (pArchive->mHashTable[ent].name != NULL) {
+ if (pArchive->mHashTable[ent].nameLen == nameLen &&
+ memcmp(pArchive->mHashTable[ent].name, entryName, nameLen) == 0)
+ {
+ /* match */
+ return (ZipEntry) (ent + kZipEntryAdj);
+ }
+
+ ent = (ent + 1) & (hashTableSize-1);
+ }
+
+ return NULL;
+}
+
+#if 0
+/*
+ * Find the Nth entry.
+ *
+ * This currently involves walking through the sparse hash table, counting
+ * non-empty entries. If we need to speed this up we can either allocate
+ * a parallel lookup table or (perhaps better) provide an iterator interface.
+ */
+ZipEntry findEntryByIndex(ZipArchive* pArchive, int idx)
+{
+ if (idx < 0 || idx >= pArchive->mNumEntries) {
+ LOGW("Invalid index %d\n", idx);
+ return NULL;
+ }
+
+ int ent;
+ for (ent = 0; ent < pArchive->mHashTableSize; ent++) {
+ if (pArchive->mHashTable[ent].name != NULL) {
+ if (idx-- == 0)
+ return (ZipEntry) (ent + kZipEntryAdj);
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+/*
+ * Get the useful fields from the zip entry.
+ *
+ * Returns "false" if the offsets to the fields or the contents of the fields
+ * appear to be bogus.
+ */
+bool dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
+ int* pMethod, long* pUncompLen, long* pCompLen, off_t* pOffset,
+ long* pModWhen, long* pCrc32)
+{
+ int ent = entryToIndex(pArchive, entry);
+ if (ent < 0)
+ return false;
+
+ /*
+ * Recover the start of the central directory entry from the filename
+ * pointer.
+ */
+ const unsigned char* basePtr = (const unsigned char*)
+ pArchive->mMap.addr;
+ const unsigned char* ptr = (const unsigned char*)
+ pArchive->mHashTable[ent].name;
+ size_t zipLength =
+ pArchive->mMap.length;
+
+ ptr -= kCDELen;
+
+ int method = get2LE(ptr + kCDEMethod);
+ if (pMethod != NULL)
+ *pMethod = method;
+
+ if (pModWhen != NULL)
+ *pModWhen = get4LE(ptr + kCDEModWhen);
+ if (pCrc32 != NULL)
+ *pCrc32 = get4LE(ptr + kCDECRC);
+
+ /*
+ * We need to make sure that the lengths are not so large that somebody
+ * trying to map the compressed or uncompressed data runs off the end
+ * of the mapped region.
+ */
+ unsigned long localHdrOffset = get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset + kLFHLen >= zipLength) {
+ LOGE("ERROR: bad local hdr offset in zip\n");
+ return false;
+ }
+ const unsigned char* localHdr = basePtr + localHdrOffset;
+ off_t dataOffset = localHdrOffset + kLFHLen
+ + get2LE(localHdr + kLFHNameLen) + get2LE(localHdr + kLFHExtraLen);
+ if ((unsigned long) dataOffset >= zipLength) {
+ LOGE("ERROR: bad data offset in zip\n");
+ return false;
+ }
+
+ if (pCompLen != NULL) {
+ *pCompLen = get4LE(ptr + kCDECompLen);
+ if (*pCompLen < 0 || (size_t)(dataOffset + *pCompLen) >= zipLength) {
+ LOGE("ERROR: bad compressed length in zip\n");
+ return false;
+ }
+ }
+ if (pUncompLen != NULL) {
+ *pUncompLen = get4LE(ptr + kCDEUncompLen);
+ if (*pUncompLen < 0) {
+ LOGE("ERROR: negative uncompressed length in zip\n");
+ return false;
+ }
+ if (method == kCompressStored &&
+ (size_t)(dataOffset + *pUncompLen) >= zipLength)
+ {
+ LOGE("ERROR: bad uncompressed length in zip\n");
+ return false;
+ }
+ }
+
+ if (pOffset != NULL) {
+ *pOffset = dataOffset;
+ }
+ return true;
+}
+
+/*
+ * Uncompress "deflate" data from one buffer to an open file descriptor.
+ */
+static bool inflateToFile(int fd, const void* inBuf, long uncompLen,
+ long compLen)
+{
+ bool result = false;
+ const int kWriteBufSize = 32768;
+ unsigned char writeBuf[kWriteBufSize];
+ z_stream zstream;
+ int zerr;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = (Bytef*)inBuf;
+ zstream.avail_in = compLen;
+ zstream.next_out = (Bytef*) writeBuf;
+ zstream.avail_out = sizeof(writeBuf);
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ LOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ LOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have more to do.
+ */
+ do {
+ /*
+ * Expand data.
+ */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ LOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
+ zerr, zstream.next_in, zstream.avail_in,
+ zstream.next_out, zstream.avail_out);
+ goto z_bail;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 ||
+ (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf)))
+ {
+ long writeSize = zstream.next_out - writeBuf;
+ int cc = write(fd, writeBuf, writeSize);
+ if (cc != (int) writeSize) {
+ if (cc < 0) {
+ LOGW("write failed in inflate: %s\n", strerror(errno));
+ } else {
+ LOGW("partial write in inflate (%d vs %ld)\n",
+ cc, writeSize);
+ }
+ goto z_bail;
+ }
+
+ zstream.next_out = writeBuf;
+ zstream.avail_out = sizeof(writeBuf);
+ }
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ /* paranoia */
+ if ((long) zstream.total_out != uncompLen) {
+ LOGW("Size mismatch on inflated file (%ld vs %ld)\n",
+ zstream.total_out, uncompLen);
+ goto z_bail;
+ }
+
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ return result;
+}
+
+/*
+ * Uncompress an entry, in its entirety, to an open file descriptor.
+ *
+ * TODO: this doesn't verify the data's CRC, but probably should (especially
+ * for uncompressed data).
+ */
+bool dexZipExtractEntryToFile(const ZipArchive* pArchive,
+ const ZipEntry entry, int fd)
+{
+ bool result = false;
+ int ent = entryToIndex(pArchive, entry);
+ if (ent < 0)
+ return -1;
+
+ const unsigned char* basePtr = (const unsigned char*)pArchive->mMap.addr;
+ int method;
+ long uncompLen, compLen;
+ off_t offset;
+
+ if (!dexZipGetEntryInfo(pArchive, entry, &method, &uncompLen, &compLen,
+ &offset, NULL, NULL))
+ {
+ goto bail;
+ }
+
+ if (method == kCompressStored) {
+ ssize_t actual;
+
+ actual = write(fd, basePtr + offset, uncompLen);
+ if (actual < 0) {
+ LOGE("Write failed: %s\n", strerror(errno));
+ goto bail;
+ } else if (actual != uncompLen) {
+ LOGE("Partial write during uncompress (%d of %ld)\n",
+ (int) actual, uncompLen);
+ goto bail;
+ } else {
+ LOGI("+++ successful write\n");
+ }
+ } else {
+ if (!inflateToFile(fd, basePtr+offset, uncompLen, compLen))
+ goto bail;
+ }
+
+ result = true;
+
+bail:
+ return result;
+}
+
diff --git a/libdex/ZipArchive.h b/libdex/ZipArchive.h
new file mode 100644
index 0000000..0cd98b2
--- /dev/null
+++ b/libdex/ZipArchive.h
@@ -0,0 +1,173 @@
+/*
+ * 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
+#define _LIBDEX_ZIPARCHIVE
+
+#include "SysUtil.h"
+#include "DexFile.h" // need DEX_INLINE
+
+
+/*
+ * Trivial typedef to ensure that ZipEntry is not treated as a simple
+ * integer. We use NULL to indicate an invalid value.
+ */
+typedef void* ZipEntry;
+
+/*
+ * One entry in the hash table.
+ */
+typedef struct ZipHashEntry {
+ const char* name;
+ unsigned short nameLen;
+ //unsigned int hash;
+} ZipHashEntry;
+
+/*
+ * Read-only Zip archive.
+ *
+ * We want "open" and "find entry by name" to be fast operations, and we
+ * want to use as little memory as possible. We memory-map the file,
+ * and load a hash table with pointers to the filenames (which aren't
+ * null-terminated). The other fields are at a fixed offset from the
+ * filename, so we don't need to extract those (but we do need to byte-read
+ * and endian-swap them every time we want them).
+ *
+ * To speed comparisons when doing a lookup by name, we could make the mapping
+ * "private" (copy-on-write) and null-terminate the filenames after verifying
+ * the record structure. However, this requires a private mapping of
+ * every page that the Central Directory touches. Easier to tuck a copy
+ * of the string length into the hash table entry.
+ */
+typedef struct ZipArchive {
+ /* open Zip archive */
+ int mFd;
+
+ /* mapped file */
+ MemMapping mMap;
+
+ /* number of entries in the Zip archive */
+ int mNumEntries;
+
+ /*
+ * We know how many entries are in the Zip archive, so we can have a
+ * fixed-size hash table. We probe on collisions.
+ */
+ int mHashTableSize;
+ ZipHashEntry* mHashTable;
+} ZipArchive;
+
+/* Zip compression methods we support */
+enum {
+ kCompressStored = 0, // no compression
+ kCompressDeflated = 8, // standard deflate
+};
+
+
+/*
+ * Open a Zip archive.
+ *
+ * On success, returns 0 and populates "pArchive". Returns nonzero errno
+ * value on failure.
+ */
+int dexZipOpenArchive(const char* fileName, ZipArchive* 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.
+ */
+int dexZipPrepArchive(int fd, const char* debugFileName, ZipArchive* 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.
+ */
+void dexZipCloseArchive(ZipArchive* pArchive);
+
+/*
+ * Return the archive's file descriptor.
+ */
+DEX_INLINE int dexZipGetArchiveFd(const ZipArchive* pArchive) {
+ return pArchive->mFd;
+}
+
+/*
+ * Find an entry in the Zip archive, by name. Returns NULL if the entry
+ * was not found.
+ */
+ZipEntry dexZipFindEntry(const ZipArchive* pArchive,
+ const char* entryName);
+
+/*
+ * Retrieve one or more of the "interesting" fields. Non-NULL pointers
+ * are filled in.
+ */
+bool dexZipGetEntryInfo(const ZipArchive* pArchive, ZipEntry entry,
+ int* pMethod, long* pUncompLen, long* pCompLen, off_t* pOffset,
+ long* pModWhen, long* pCrc32);
+
+/*
+ * Simple accessors.
+ */
+DEX_INLINE long dexGetZipEntryOffset(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ off_t val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, &val, NULL, NULL);
+ return (long) val;
+}
+DEX_INLINE long dexGetZipEntryUncompLen(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ long val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, &val, NULL, NULL, NULL, NULL);
+ return val;
+}
+DEX_INLINE long dexGetZipEntryModTime(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ long val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, &val, NULL);
+ return val;
+}
+DEX_INLINE long dexGetZipEntryCrc32(const ZipArchive* pArchive,
+ const ZipEntry entry)
+{
+ long val = 0;
+ dexZipGetEntryInfo(pArchive, entry, NULL, NULL, NULL, NULL, NULL, &val);
+ return val;
+}
+
+/*
+ * Uncompress and write an entry to a file descriptor.
+ */
+bool dexZipExtractEntryToFile(const ZipArchive* pArchive,
+ const ZipEntry entry, int fd);
+
+/*
+ * Utility function to compute a CRC-32.
+ */
+u4 dexInitCrc32(void);
+u4 dexComputeCrc32(u4 crc, const void* buf, size_t len);
+
+#endif /*_LIBDEX_ZIPARCHIVE*/
diff --git a/libdex/sha1.c b/libdex/sha1.c
new file mode 100644
index 0000000..6289248
--- /dev/null
+++ b/libdex/sha1.c
@@ -0,0 +1,514 @@
+/*
+ * 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>
+
+#include "DexFile.h" // want common byte ordering def
+
+# 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;
+typedef union {
+ unsigned char c[64];
+ unsigned long l[16];
+} CHAR64LONG16;
+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);
+ 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 0000000..65cf667
--- /dev/null
+++ b/libdex/sha1.h
@@ -0,0 +1,20 @@
+/*
+ * See "sha1.c" for author info.
+ */
+#ifndef _DALVIK_SHA1
+#define _DALVIK_SHA1
+
+typedef struct {
+ unsigned long state[5];
+ unsigned long count[2];
+ unsigned char buffer[64];
+} SHA1_CTX;
+
+#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 /*_DALVIK_SHA1*/