/* * 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. */ /* * List all methods in all concrete classes in a DEX file. */ #include "libdex/DexFile.h" #include "libdex/DexClass.h" #include "libdex/DexProto.h" #include "libdex/SysUtil.h" #include "libdex/CmdUtils.h" #include <stdlib.h> #include <stdio.h> #include <fcntl.h> #include <stddef.h> #include <string.h> #include <unistd.h> #include <getopt.h> #include <errno.h> #include <assert.h> static const char* gProgName = "dexlist"; /* * Return a newly-allocated string for the "dot version" of the class * name for the given type descriptor. That is, The initial "L" and * final ";" (if any) have been removed and all occurrences of '/' * have been changed to '.'. */ static char* descriptorToDot(const char* str) { size_t at = strlen(str); char* newStr; if (str[0] == 'L') { assert(str[at - 1] == ';'); at -= 2; /* Two fewer chars to copy. */ str++; /* Skip the 'L'. */ } newStr = malloc(at + 1); /* Add one for the '\0'. */ newStr[at] = '\0'; while (at > 0) { at--; newStr[at] = (str[at] == '/') ? '.' : str[at]; } return newStr; } /* * Position table callback; we just want to catch the number of the * first line in the method, which *should* correspond to the first * entry from the table. (Could also use "min" here.) */ static int positionsCallback(void* cnxt, u4 address, u4 lineNum) { int* pFirstLine = (int*) cnxt; if (*pFirstLine == -1) *pFirstLine = lineNum; return 0; } /* * Dump a method. */ void dumpMethod(DexFile* pDexFile, const char* fileName, const DexMethod* pDexMethod, int i) { const DexMethodId* pMethodId; const DexCode* pCode; const char* classDescriptor; const char* methodName; int firstLine; /* abstract and native methods don't get listed */ if (pDexMethod->codeOff == 0) return; pMethodId = dexGetMethodId(pDexFile, pDexMethod->methodIdx); methodName = dexStringById(pDexFile, pMethodId->nameIdx); classDescriptor = dexStringByTypeIdx(pDexFile, pMethodId->classIdx); pCode = dexGetCode(pDexFile, pDexMethod); assert(pCode != NULL); /* * If the filename is empty, then set it to something printable * so that it is easier to parse. * * TODO: A method may override its class's default source file by * specifying a different one in its debug info. This possibility * should be handled here. */ if (fileName == NULL || fileName[0] == 0) { fileName = "(none)"; } firstLine = -1; dexDecodeDebugInfo(pDexFile, pCode, classDescriptor, pMethodId->protoIdx, pDexMethod->accessFlags, positionsCallback, NULL, &firstLine); char* className = descriptorToDot(classDescriptor); char* desc = dexCopyDescriptorFromMethodId(pDexFile, pMethodId); u4 insnsOff = pDexMethod->codeOff + offsetof(DexCode, insns); printf("0x%08x %d %s %s %s %s %d\n", insnsOff, pCode->insnsSize * 2, className, methodName, desc, fileName, firstLine); free(desc); free(className); } /* * Run through all direct and virtual methods in the class. */ void dumpClass(DexFile* pDexFile, int idx) { const DexClassDef* pClassDef; DexClassData* pClassData; const u1* pEncodedData; const char* fileName; int i; pClassDef = dexGetClassDef(pDexFile, idx); pEncodedData = dexGetClassData(pDexFile, pClassDef); pClassData = dexReadAndVerifyClassData(&pEncodedData, NULL); if (pClassData == NULL) { fprintf(stderr, "Trouble reading class data\n"); return; } if (pClassDef->sourceFileIdx == 0xffffffff) { fileName = NULL; } else { fileName = dexStringById(pDexFile, pClassDef->sourceFileIdx); } /* * TODO: Each class def points at a sourceFile, so maybe that * should be printed out. However, this needs to be coordinated * with the tools that parse this output. */ for (i = 0; i < (int) pClassData->header.directMethodsSize; i++) { dumpMethod(pDexFile, fileName, &pClassData->directMethods[i], i); } for (i = 0; i < (int) pClassData->header.virtualMethodsSize; i++) { dumpMethod(pDexFile, fileName, &pClassData->virtualMethods[i], i); } free(pClassData); } /* * Process a file. * * Returns 0 on success. */ int process(const char* fileName) { DexFile* pDexFile = NULL; MemMapping map; bool mapped = false; int result = -1; UnzipToFileResult utfr; utfr = dexOpenAndMap(fileName, NULL, &map, true); if (utfr != kUTFRSuccess) { if (utfr == kUTFRNoClassesDex) { /* no classes.dex in the APK; pretend we succeeded */ result = 0; goto bail; } fprintf(stderr, "Unable to process '%s'\n", fileName); goto bail; } mapped = true; pDexFile = dexFileParse(map.addr, map.length, kDexParseDefault); if (pDexFile == NULL) { fprintf(stderr, "Warning: DEX parse failed for '%s'\n", fileName); goto bail; } printf("#%s\n", fileName); int i; for (i = 0; i < (int) pDexFile->pHeader->classDefsSize; i++) { dumpClass(pDexFile, i); } result = 0; bail: if (mapped) sysReleaseShmem(&map); if (pDexFile != NULL) dexFileFree(pDexFile); return result; } /* * Show usage. */ void usage(void) { fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n"); fprintf(stderr, "%s: dexfile [dexfile2 ...]\n", gProgName); fprintf(stderr, "\n"); } /* * Parse args. */ int main(int argc, char* const argv[]) { int result = 0; int i; if (argc < 2) { fprintf(stderr, "%s: no file specified\n", gProgName); usage(); return 2; } /* * Run through the list of files. If one of them fails we contine on, * only returning a failure at the end. */ for (i = 1; i < argc; i++) result |= process(argv[i]); return result; }