diff --git a/README.txt b/README.txt index 2dccb1347461af8106ad46ffa5c0fd7065b7d59b..2969180f3dd02b4ae69279d6f1f741812710183f 100644 --- a/README.txt +++ b/README.txt @@ -1,5 +1,5 @@ -This directory contains the Dalvik virtual machine and associated -class library. +This directory contains the Dalvik virtual machine and core class library, +as well as related tools, libraries, and tests. A note about the licenses and header comments --------------------------------------------- diff --git a/dexdump/DexDump.c b/dexdump/DexDump.c index 6440ad123e00b72d0d9b369146249fbfd4c8e78e..6fed7aaf4633950bbb663a59e56730c2dc07a49d 100644 --- a/dexdump/DexDump.c +++ b/dexdump/DexDump.c @@ -60,10 +60,12 @@ typedef enum OutputFormat { /* command-line options */ struct { + bool checksumOnly; bool disassemble; bool showFileHeaders; bool showSectionHeaders; bool ignoreBadChecksum; + bool dumpRegisterMaps; OutputFormat outputFormat; const char* tempFileName; bool exportsOnly; @@ -85,6 +87,14 @@ static inline u2 get2LE(unsigned char const* pSrc) return pSrc[0] | (pSrc[1] << 8); } +/* + * Get 4 little-endian bytes. + */ +static inline u4 get4LE(unsigned char const* pSrc) +{ + return pSrc[0] | (pSrc[1] << 8) | (pSrc[2] << 16) | (pSrc[3] << 24); +} + /* * Converts a single-character primitive type into its human-readable * equivalent. @@ -1193,6 +1203,8 @@ void dumpIField(const DexFile* pDexFile, const DexField* pIField, int i) /* * Dump the class. * + * Note "idx" is a DexClassDef index, not a DexTypeId index. + * * If "*pLastPackage" is NULL or does not match the current class' package, * the value will be replaced with a newly-allocated string. */ @@ -1365,6 +1377,208 @@ bail: free(accessStr); } + +/* + * Advance "ptr" to ensure 32-bit alignment. + */ +static inline const u1* align32(const u1* ptr) +{ + return (u1*) (((int) ptr + 3) & ~0x03); +} + + +/* + * Dump a map in the "differential" format. + * + * TODO: show a hex dump of the compressed data. (We can show the + * uncompressed data if we move the compression code to libdex; otherwise + * it's too complex to merit a fast & fragile implementation here.) + */ +void dumpDifferentialCompressedMap(const u1** pData) +{ + const u1* data = *pData; + const u1* dataStart = data -1; // format byte already removed + u1 regWidth; + u2 numEntries; + + /* standard header */ + regWidth = *data++; + numEntries = *data++; + numEntries |= (*data++) << 8; + + /* compressed data begins with the compressed data length */ + int compressedLen = readUnsignedLeb128(&data); + int addrWidth = 1; + if ((*data & 0x80) != 0) + addrWidth++; + + int origLen = 4 + (addrWidth + regWidth) * numEntries; + int compLen = (data - dataStart) + compressedLen; + + printf(" (differential compression %d -> %d [%d -> %d])\n", + origLen, compLen, + (addrWidth + regWidth) * numEntries, compressedLen); + + /* skip past end of entry */ + data += compressedLen; + + *pData = data; +} + +/* + * Dump register map contents of the current method. + * + * "*pData" should point to the start of the register map data. Advances + * "*pData" to the start of the next map. + */ +void dumpMethodMap(DexFile* pDexFile, const DexMethod* pDexMethod, int idx, + const u1** pData) +{ + const u1* data = *pData; + const DexMethodId* pMethodId; + const char* name; + int offset = data - (u1*) pDexFile->pOptHeader; + + pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); + name = dexStringById(pDexFile, pMethodId->nameIdx); + printf(" #%d: 0x%08x %s\n", idx, offset, name); + + u1 format; + int addrWidth; + + format = *data++; + if (format == 1) { /* kRegMapFormatNone */ + /* no map */ + printf(" (no map)\n"); + addrWidth = 0; + } else if (format == 2) { /* kRegMapFormatCompact8 */ + addrWidth = 1; + } else if (format == 3) { /* kRegMapFormatCompact16 */ + addrWidth = 2; + } else if (format == 4) { /* kRegMapFormatDifferential */ + dumpDifferentialCompressedMap(&data); + goto bail; + } else { + printf(" (unknown format %d!)\n", format); + /* don't know how to skip data; failure will cascade to end of class */ + goto bail; + } + + if (addrWidth > 0) { + u1 regWidth; + u2 numEntries; + int idx, addr, byte; + + regWidth = *data++; + numEntries = *data++; + numEntries |= (*data++) << 8; + + for (idx = 0; idx < numEntries; idx++) { + addr = *data++; + if (addrWidth > 1) + addr |= (*data++) << 8; + + printf(" %4x:", addr); + for (byte = 0; byte < regWidth; byte++) { + printf(" %02x", *data++); + } + printf("\n"); + } + } + +bail: + //if (addrWidth >= 0) + // *pData = align32(data); + *pData = data; +} + +/* + * Dump the contents of the register map area. + * + * These are only present in optimized DEX files, and the structure is + * not really exposed to other parts of the VM itself. We're going to + * dig through them here, but this is pretty fragile. DO NOT rely on + * this or derive other code from it. + */ +void dumpRegisterMaps(DexFile* pDexFile) +{ + const u1* pClassPool = pDexFile->pRegisterMapPool; + const u4* classOffsets; + const u1* ptr; + u4 numClasses; + int baseFileOffset = (u1*) pClassPool - (u1*) pDexFile->pOptHeader; + int idx; + + if (pClassPool == NULL) { + printf("No register maps found\n"); + return; + } + + ptr = pClassPool; + numClasses = get4LE(ptr); + ptr += sizeof(u4); + classOffsets = (const u4*) ptr; + + printf("RMAP begins at offset 0x%07x\n", baseFileOffset); + printf("Maps for %d classes\n", numClasses); + for (idx = 0; idx < (int) numClasses; idx++) { + const DexClassDef* pClassDef; + const char* classDescriptor; + + pClassDef = dexGetClassDef(pDexFile, idx); + classDescriptor = dexStringByTypeIdx(pDexFile, pClassDef->classIdx); + + printf("%4d: +%d (0x%08x) %s\n", idx, classOffsets[idx], + baseFileOffset + classOffsets[idx], classDescriptor); + + if (classOffsets[idx] == 0) + continue; + + /* + * What follows is a series of RegisterMap entries, one for every + * direct method, then one for every virtual method. + */ + DexClassData* pClassData; + const u1* pEncodedData; + const u1* data = (u1*) pClassPool + classOffsets[idx]; + u2 methodCount; + int i; + + pEncodedData = dexGetClassData(pDexFile, pClassDef); + pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL); + if (pClassData == NULL) { + fprintf(stderr, "Trouble reading class data\n"); + continue; + } + + methodCount = *data++; + methodCount |= (*data++) << 8; + data += 2; /* two pad bytes follow methodCount */ + if (methodCount != pClassData->header.directMethodsSize + + pClassData->header.virtualMethodsSize) + { + printf("NOTE: method count discrepancy (%d != %d + %d)\n", + methodCount, pClassData->header.directMethodsSize, + pClassData->header.virtualMethodsSize); + /* this is bad, but keep going anyway */ + } + + printf(" direct methods: %d\n", + pClassData->header.directMethodsSize); + for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) { + dumpMethodMap(pDexFile, &pClassData->directMethods[i], i, &data); + } + + printf(" virtual methods: %d\n", + pClassData->header.virtualMethodsSize); + for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) { + dumpMethodMap(pDexFile, &pClassData->virtualMethods[i], i, &data); + } + + free(pClassData); + } +} + /* * Dump the requested sections of the file. */ @@ -1378,6 +1592,11 @@ void processDexFile(const char* fileName, DexFile* pDexFile) pDexFile->pHeader->magic +4); } + if (gOptions.dumpRegisterMaps) { + dumpRegisterMaps(pDexFile); + return; + } + if (gOptions.showFileHeaders) dumpFileHeader(pDexFile); @@ -1429,7 +1648,11 @@ int process(const char* fileName) goto bail; } - processDexFile(fileName, pDexFile); + if (gOptions.checksumOnly) { + printf("Checksum verified\n"); + } else { + processDexFile(fileName, pDexFile); + } result = 0; @@ -1449,14 +1672,16 @@ void usage(void) { fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n"); fprintf(stderr, - "%s: [-d] [-f] [-h] [-i] [-l layout] [-t tempfile] dexfile...\n", + "%s: [-c] [-d] [-f] [-h] [-i] [-l layout] [-m] [-t tempfile] dexfile...\n", gProgName); fprintf(stderr, "\n"); + fprintf(stderr, " -c : verify checksum and exit\n"); fprintf(stderr, " -d : disassemble code sections\n"); fprintf(stderr, " -f : display summary information from file header\n"); fprintf(stderr, " -h : display file header details\n"); fprintf(stderr, " -i : ignore checksum failures\n"); fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n"); + fprintf(stderr, " -m : dump register maps (and nothing else)\n"); fprintf(stderr, " -t : temp file name (defaults to /sdcard/dex-temp-*)\n"); } @@ -1474,11 +1699,14 @@ int main(int argc, char* const argv[]) gOptions.verbose = true; while (1) { - ic = getopt(argc, argv, "dfhil:t:"); + ic = getopt(argc, argv, "cdfhil:mt:"); if (ic < 0) break; switch (ic) { + case 'c': // verify the checksum then exit + gOptions.checksumOnly = true; + break; case 'd': // disassemble Dalvik instructions gOptions.disassemble = true; break; @@ -1502,6 +1730,9 @@ int main(int argc, char* const argv[]) wantUsage = true; } break; + case 'm': // dump register maps only + gOptions.dumpRegisterMaps = true; + break; case 't': // temp file, used when opening compressed Jar gOptions.tempFileName = optarg; break; @@ -1516,6 +1747,11 @@ int main(int argc, char* const argv[]) wantUsage = true; } + if (gOptions.checksumOnly && gOptions.ignoreBadChecksum) { + fprintf(stderr, "Can't specify both -c and -i\n"); + wantUsage = true; + } + /* initialize some VM tables */ gInstrWidth = dexCreateInstrWidthTable(); gInstrFormat = dexCreateInstrFormatTable(); @@ -1525,12 +1761,14 @@ int main(int argc, char* const argv[]) return 2; } - while (optind < argc) - process(argv[optind++]); + int result = 0; + while (optind < argc) { + result |= process(argv[optind++]); + } free(gInstrWidth); free(gInstrFormat); - return 0; + return (result != 0); } diff --git a/dexopt/OptMain.c b/dexopt/OptMain.c index ef339cdd187aaba3648d3c52bbf3ec2fabda4ced..953db0b00e12d9b42005edf2536aa8f06552a517 100644 --- a/dexopt/OptMain.c +++ b/dexopt/OptMain.c @@ -337,7 +337,7 @@ static int fromDex(int argc, char* const argv[]) */ GET_ARG(vmBuildVersion, strtol, "bad vm build"); if (vmBuildVersion != DALVIK_VM_BUILD) { - LOGE("Inconsistent build rev: %d vs %d\n", + LOGE("DexOpt: build rev does not match VM: %d vs %d\n", vmBuildVersion, DALVIK_VM_BUILD); goto bail; } diff --git a/docs/debugger.html b/docs/debugger.html index 6e23f0df2b93c6a4e42f117d026af1f4f57f38ff..523c7123daef26b1a24f1e48e82049f09d8e06e5 100644 --- a/docs/debugger.html +++ b/docs/debugger.html @@ -41,11 +41,11 @@ only responsible for handling requests from the debugger; VM-initated communication, such as notifying the debugger when the VM has stopped at a breakpoint, are sent from the affected thread.
-When the VM is embedded in the Android framework,
-debugging is enabled in the VM unless the system property
-ro.secure is set to 1. On these
-"secure" devices, debugging is only enabled in app processes whose
-manifest contains android:debuggable="true" in the
+When the VM is started from the Android app framework, debugging is enabled
+for all applications when the system property ro.debuggable
+is set to 1 (use adb shell getprop ro.debuggable
+to check it). If it's zero, debugging can be enabled via the application's
+manifest, which must include android:debuggable="true" in the
<application> element.
@@ -194,7 +194,9 @@ The debugger and garbage collector are somewhat loosely integrated at present. The VM currently guarantees that any object the debugger is aware of will not be garbage collected until after the debugger disconnects. This can result in a build-up over time while the -debugger is connected. +debugger is connected. For example, if the debugger sees a running +thread, the associated Thread object will not be collected, even after +the thread terminates.
The situation is exacerbated by a flaw in the exception processing code, which results in nearly all exceptions being added to the "do not discard" @@ -202,6 +204,43 @@ list, even if the debugger never sees them. Having a debugger attached to a program that throws lots of exceptions can result in out-of-memory errors. This will be fixed in a future release.
+The only way to "unlock" the references is to detach and reattach the +debugger. +
+ +
+The translation from Java bytecode to Dalvik bytecode may result in +identical sequences of instructions being combined. This can make it +look like the wrong bit of code is being executed. For example: +
int test(int i) {
+ if (i == 1) {
+ return 0;
+ }
+ return 1;
+ }
+The Dalvik bytecode uses a common return instruction for both
+return statements, so when i is 1 the debugger
+will single-step through return 0 and then return 1.
++ +
+Dalvik handles synchronized methods differently from other VMs.
+Instead of marking a method as synchronized and expecting
+the VM to handle the locks, dx inserts a "lock"
+instruction at the top of the method and an "unlock" instruction in a
+synthetic finally block. As a result, when single-stepping
+a return statement, the "current line" cursor may jump to
+the last line in the method.
+
+This can also affect the way the debugger processes exceptions. The
+debugger may decide to break on an
+exception based on whether that exception is "caught" or "uncaught". To
+be considered uncaught, there must be no matching catch block
+or finally clause between the current point of execution and
+the top of the thread. An exception thrown within or below a synchronized
+method will always be considered "caught", so the debugger won't stop
+until the exception is re-thrown from the synthetic finally block.
+
Copyright © 2009 The Android Open Source Project diff --git a/docs/embedded-vm-control.html b/docs/embedded-vm-control.html index f90f0e5964fa5f06df60bbe958ec7fc74b7c7348..0b279e8125874be43c025b610df23fdc59499042 100644 --- a/docs/embedded-vm-control.html +++ b/docs/embedded-vm-control.html @@ -15,6 +15,7 @@
adb shell stop; adb shell start). This is because the
settings are processed in the "zygote" process, which starts early and stays
around "forever".
-You could also add a line to /data/local.prop that looks like:
+
You may not be able to set this as an unprivileged user. You can use
+adb root or run the su command from the device
+shell on "userdebug" builds to become root first. When in doubt,
+
adb shell getprop <name>+will tell you if the
setprop took.
+
+If you don't want the property to evaporate when the device reboots,
+add a line to /data/local.prop that looks like:
<name> = <value>-
Such changes will survive reboots, but will be removed by anything
-that wipes the data partition. (Hint: create a local.prop
-on your workstation, then adb push local.prop /data .)
+
Such changes will survive reboots, but will be lost if the data
+partition is wiped. (Hint: create a local.prop
+on your workstation, then adb push local.prop /data . Or,
+use one-liners like
+adb shell "echo name = value >> /data/local.prop" -- note
+the quotes are important.)
You can also pass JNI-checking options into the VM through a system
property. The value set for dalvik.vm.jniopts will
-be passed in as the -Xjniopts argument.
+be passed in as the -Xjniopts argument. For example:
+
adb shell setprop dalvik.vm.jniopts forcecopy
For more information about JNI checks, see JNI Tips. @@ -158,7 +170,12 @@ constrast, the "portable" interpreter is written in C and expected to run on a broad range of platforms. The "debug" interpreter is a variant of "portable" that includes support for profiling and single-stepping. -
The VM allows you to choose between "fast" and "portable" with an +
The VM may also support just-in-time compilation. While not strictly
+a different interpreter, the JIT compiler may be enabled or disabled
+with the same flag. (Check the output of dalvikvm -help to
+see if JIT compilation is enabled in your VM.)
+
+
The VM allows you to choose between "fast", "portable", and "jit" with an
extended form of the -Xint argument. The value of this
argument can be set through the dalvik.vm.execution-mode
system property.
@@ -235,6 +252,35 @@ is passed into the VM via the -Xstacktracefile argument.
If the property is not defined, the VM will write the stack traces to the Android log when the signal arrives. + +
For performance reasons, the checksum on "optimized" DEX files is +ignored. This is usually safe, because the files are generated on the +device, and have access permissions that prevent modification. + +
If the storage on a device becomes unreliable, however, data corruption
+can occur. This usually manifests itself as a repeatable virtual machine
+crash. To speed diagnosis of such failures, the VM provides the
+-Xcheckdexsum argument. When set, the checksums on all DEX
+files are verified before the contents are used.
+
+
The application framework will provide this argument during VM
+creation if the dalvik.vm.check-dex-sum property is enabled.
+
+
To enable extended DEX checksum verification: +
adb shell setprop dalvik.vm.check-dex-sum true+ +
Incorrect checksums will prevent the DEX data from being used, and will
+cause errors to be written to the log file. If a device has a history of
+problems it may be useful to add the property to
+/data/local.prop.
+
+
Note also that the
+dexdump tool always verifies DEX checksums, and can be used
+to check for corruption in a large set of files.
+
+