/* * Create and initialize a hash table. */ HashTable* dvmHashTableCreate(size_t initialSize, HashFreeFunc freeFunc) { HashTable* pHashTable; assert(initialSize > 0); pHashTable = (HashTable*) malloc(sizeof(*pHashTable)); if (pHashTable == NULL) return NULL; dvmInitMutex(&pHashTable->lock); pHashTable->tableSize = dexRoundUpPower2(initialSize); pHashTable->numEntries = pHashTable->numDeadEntries = 0; pHashTable->freeFunc = freeFunc; pHashTable->pEntries = (HashEntry*) malloc(pHashTable->tableSize * sizeof(HashEntry)); if (pHashTable->pEntries == NULL) { free(pHashTable); return NULL; } memset(pHashTable->pEntries, 0, pHashTable->tableSize * sizeof(HashEntry)); return pHashTable; }
/* * 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; }
/* * 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 }
/* * Parses the Zip archive's Central Directory. Allocates and populates the * hash table. * * Returns 0 on success. */ static int parseZipArchive(ZipArchive* pArchive) { int result = -1; const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr; size_t cdLength = pArchive->mDirectoryMap.length; int numEntries = pArchive->mNumEntries; /* * 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->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 and verifying values. */ const u1* ptr = cdPtr; int i; for (i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { LOGW("Zip: missed a central dir sig (at %d)\n", i); goto bail; } if (ptr + kCDELen > cdPtr + cdLength) { LOGW("Zip: ran off the end (at %d)\n", i); goto bail; } long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); if (localHdrOffset >= pArchive->mDirectoryOffset) { LOGW("Zip: bad LFH offset %ld at entry %d\n", localHdrOffset, i); goto bail; } unsigned int fileNameLen, extraLen, commentLen, hash; fileNameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); /* add the CDE filename to the hash table */ hash = computeHash((const char*)ptr + kCDELen, fileNameLen); addToHash(pArchive, (const char*)ptr + kCDELen, fileNameLen, hash); ptr += kCDELen + fileNameLen + extraLen + commentLen; if ((size_t)(ptr - cdPtr) > cdLength) { LOGW("Zip: bad CD advance (%d vs %zd) at entry %d\n", (int) (ptr - cdPtr), cdLength, i); goto bail; } } LOGV("+++ zip good scan %d entries\n", numEntries); result = 0; bail: return result; }
/* * Parses the Zip archive's Central Directory. Allocates and populates the * hash table. * * Returns 0 on success. */ static int parseZipArchive(ZipArchive* pArchive) { int result = -1; const u1* cdPtr = (const u1*)pArchive->mDirectoryMap.addr; size_t cdLength = pArchive->mDirectoryMap.length; int numEntries = pArchive->mNumEntries; /* * 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->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 and verifying values. */ const u1* ptr = cdPtr; int i; for (i = 0; i < numEntries; i++) { if (get4LE(ptr) != kCDESignature) { ALOGW("Zip: missed a central dir sig (at %d)", i); goto bail; } if (ptr + kCDELen > cdPtr + cdLength) { ALOGW("Zip: ran off the end (at %d)", i); goto bail; } long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset); if (localHdrOffset >= pArchive->mDirectoryOffset) { ALOGW("Zip: bad LFH offset %ld at entry %d", localHdrOffset, i); goto bail; } unsigned int gpbf = get2LE(ptr + kCDEGPBFlags); if ((gpbf & kGPFUnsupportedMask) != 0) { ALOGW("Invalid General Purpose Bit Flag: %d", gpbf); goto bail; } unsigned int nameLen, extraLen, commentLen, hash; nameLen = get2LE(ptr + kCDENameLen); extraLen = get2LE(ptr + kCDEExtraLen); commentLen = get2LE(ptr + kCDECommentLen); const char *name = (const char *) ptr + kCDELen; /* Check name for NULL characters */ if (memchr(name, 0, nameLen) != NULL) { ALOGW("Filename contains NUL byte"); goto bail; } /* add the CDE filename to the hash table */ hash = computeHash(name, nameLen); addToHash(pArchive, name, nameLen, hash); /* We don't care about the comment or extra data. */ ptr += kCDELen + nameLen + extraLen + commentLen; if ((size_t)(ptr - cdPtr) > cdLength) { ALOGW("Zip: bad CD advance (%d vs %zd) at entry %d", (int) (ptr - cdPtr), cdLength, i); goto bail; } } ALOGV("+++ zip good scan %d entries", numEntries); result = 0; bail: return result; }