/* * Write a block of data in "chunk" format. * * The chunk header fields are always in "native" byte order. If "size" * is not a multiple of 8 bytes, the data area is padded out. */ static bool writeChunk(int fd, u4 type, const void* data, size_t size) { union { /* save a syscall by grouping these together */ char raw[8]; struct { u4 type; u4 size; } ts; } header; assert(sizeof(header) == 8); LOGV("Writing chunk, type=%.4s size=%d\n", (char*) &type, size); header.ts.type = type; header.ts.size = (u4) size; if (sysWriteFully(fd, &header, sizeof(header), "DexOpt opt chunk header write") != 0) { return false; } if (size > 0) { if (sysWriteFully(fd, data, size, "DexOpt opt chunk write") != 0) return false; } /* if necessary, pad to 64-bit alignment */ if ((size & 7) != 0) { int padSize = 8 - (size & 7); LOGV("size was %d, inserting %d pad bytes\n", size, padSize); lseek(fd, padSize, SEEK_CUR); } assert( ((int)lseek(fd, 0, SEEK_CUR) & 7) == 0); return true; }
/* * Copy bytes from input to output. */ static int copyFileToFile(int inFd, int outFd, size_t uncompLen) { const size_t kBufSize = 32768; unsigned char buf[kBufSize]; while (uncompLen != 0) { size_t getSize = (uncompLen > kBufSize) ? kBufSize : uncompLen; ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize)); if (actual != (ssize_t) getSize) { LOGW("Zip: copy read failed (%d vs %zd)\n", (int)actual, getSize); return -1; } if (sysWriteFully(outFd, buf, getSize, "Zip copy") != 0) return -1; uncompLen -= getSize; } return 0; }
/* See documentation comment in header file. */ int sysCopyFileToFile(int outFd, int inFd, size_t count) { const size_t kBufSize = 32768; unsigned char buf[kBufSize]; while (count != 0) { size_t getSize = (count > kBufSize) ? kBufSize : count; ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, buf, getSize)); if (actual != (ssize_t) getSize) { ALOGW("sysCopyFileToFile: copy read failed (%d vs %zd)", (int) actual, getSize); return -1; } if (sysWriteFully(outFd, buf, getSize, "sysCopyFileToFile") != 0) return -1; count -= getSize; } return 0; }
/* * Uncompress "deflate" data from the archive's file to an open file * descriptor. */ static int inflateToFile(int inFd, int outFd, size_t uncompLen, size_t compLen) { int result = -1; const size_t kBufSize = 32768; unsigned char* readBuf = (unsigned char*) malloc(kBufSize); unsigned char* writeBuf = (unsigned char*) malloc(kBufSize); z_stream zstream; int zerr; if (readBuf == NULL || writeBuf == NULL) goto bail; /* * 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 = NULL; zstream.avail_in = 0; zstream.next_out = (Bytef*) writeBuf; zstream.avail_out = kBufSize; 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 { LOGW("Call to inflateInit2 failed (zerr=%d)\n", zerr); } goto bail; } /* * Loop while we have more to do. */ do { /* read as much as we can */ if (zstream.avail_in == 0) { size_t getSize = (compLen > kBufSize) ? kBufSize : compLen; ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize)); if (actual != (ssize_t) getSize) { LOGW("Zip: inflate read failed (%d vs %zd)\n", (int)actual, getSize); goto z_bail; } compLen -= getSize; zstream.next_in = readBuf; zstream.avail_in = getSize; } /* uncompress the data */ zerr = inflate(&zstream, Z_NO_FLUSH); if (zerr != Z_OK && zerr != Z_STREAM_END) { LOGW("Zip: 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 != kBufSize)) { size_t writeSize = zstream.next_out - writeBuf; if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0) goto z_bail; zstream.next_out = writeBuf; zstream.avail_out = kBufSize; } } while (zerr == Z_OK); assert(zerr == Z_STREAM_END); /* other errors should've been caught */ /* paranoia */ if (zstream.total_out != uncompLen) { LOGW("Zip: size mismatch on inflated file (%ld vs %zd)\n", zstream.total_out, uncompLen); goto z_bail; } result = 0; z_bail: inflateEnd(&zstream); /* free up any allocated structures */ bail: free(readBuf); free(writeBuf); return result; }
/* * Do the actual optimization. This is executed in the dexopt process. * * For best use of disk/memory, we want to extract once and perform * optimizations in place. If the file has to expand or contract * to match local structure padding/alignment expectations, we want * to do the rewrite as part of the extract, rather than extracting * into a temp file and slurping it back out. (The structure alignment * is currently correct for all platforms, and this isn't expected to * change, so we should be okay with having it already extracted.) * * Returns "true" on success. */ bool dvmContinueOptimization(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap) { DexClassLookup* pClassLookup = NULL; RegisterMapBuilder* pRegMapBuilder = NULL; u4 headerFlags = 0; assert(gDvm.optimizing); LOGV("Continuing optimization (%s, isb=%d)\n", fileName, isBootstrap); assert(dexOffset >= 0); /* quick test so we don't blow up on empty file */ if (dexLength < (int) sizeof(DexHeader)) { LOGE("too small to be DEX\n"); return false; } if (dexOffset < (int) sizeof(DexOptHeader)) { LOGE("not enough room for opt header\n"); return false; } bool result = false; /* * Drop this into a global so we don't have to pass it around. We could * also add a field to DexFile, but since it only pertains to DEX * creation that probably doesn't make sense. */ gDvm.optimizingBootstrapClass = isBootstrap; { /* * Map the entire file (so we don't have to worry about page * alignment). The expectation is that the output file contains * our DEX data plus room for a small header. */ bool success; void* mapAddr; mapAddr = mmap(NULL, dexOffset + dexLength, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (mapAddr == MAP_FAILED) { LOGE("unable to mmap DEX cache: %s\n", strerror(errno)); goto bail; } /* * Rewrite the file. Byte reordering, structure realigning, * class verification, and bytecode optimization are all performed * here. * * In theory the file could change size and bits could shift around. * In practice this would be annoying to deal with, so the file * layout is designed so that it can always be rewritten in place. * * This sets "headerFlags" and creates the class lookup table as * part of doing the processing. */ success = rewriteDex(((u1*) mapAddr) + dexOffset, dexLength, &headerFlags, &pClassLookup); if (success) { DvmDex* pDvmDex = NULL; u1* dexAddr = ((u1*) mapAddr) + dexOffset; if (dvmDexFileOpenPartial(dexAddr, dexLength, &pDvmDex) != 0) { LOGE("Unable to create DexFile\n"); success = false; } else { /* * If configured to do so, generate register map output * for all verified classes. The register maps were * generated during verification, and will now be serialized. */ if (gDvm.generateRegisterMaps) { pRegMapBuilder = dvmGenerateRegisterMaps(pDvmDex); if (pRegMapBuilder == NULL) { LOGE("Failed generating register maps\n"); success = false; } } DexHeader* pHeader = (DexHeader*)pDvmDex->pHeader; updateChecksum(dexAddr, dexLength, pHeader); dvmDexFileFree(pDvmDex); } } /* unmap the read-write version, forcing writes to disk */ if (msync(mapAddr, dexOffset + dexLength, MS_SYNC) != 0) { LOGW("msync failed: %s\n", strerror(errno)); // weird, but keep going } #if 1 /* * This causes clean shutdown to fail, because we have loaded classes * that point into it. For the optimizer this isn't a problem, * because it's more efficient for the process to simply exit. * Exclude this code when doing clean shutdown for valgrind. */ if (munmap(mapAddr, dexOffset + dexLength) != 0) { LOGE("munmap failed: %s\n", strerror(errno)); goto bail; } #endif if (!success) goto bail; } /* get start offset, and adjust deps start for 64-bit alignment */ off_t depsOffset, optOffset, endOffset, adjOffset; int depsLength, optLength; u4 optChecksum; depsOffset = lseek(fd, 0, SEEK_END); if (depsOffset < 0) { LOGE("lseek to EOF failed: %s\n", strerror(errno)); goto bail; } adjOffset = (depsOffset + 7) & ~(0x07); if (adjOffset != depsOffset) { LOGV("Adjusting deps start from %d to %d\n", (int) depsOffset, (int) adjOffset); depsOffset = adjOffset; lseek(fd, depsOffset, SEEK_SET); } /* * Append the dependency list. */ if (writeDependencies(fd, modWhen, crc) != 0) { LOGW("Failed writing dependencies\n"); goto bail; } /* compute deps length, then adjust opt start for 64-bit alignment */ optOffset = lseek(fd, 0, SEEK_END); depsLength = optOffset - depsOffset; adjOffset = (optOffset + 7) & ~(0x07); if (adjOffset != optOffset) { LOGV("Adjusting opt start from %d to %d\n", (int) optOffset, (int) adjOffset); optOffset = adjOffset; lseek(fd, optOffset, SEEK_SET); } /* * Append any optimized pre-computed data structures. */ if (!writeOptData(fd, pClassLookup, pRegMapBuilder)) { LOGW("Failed writing opt data\n"); goto bail; } endOffset = lseek(fd, 0, SEEK_END); optLength = endOffset - optOffset; /* compute checksum from start of deps to end of opt area */ if (!computeFileChecksum(fd, depsOffset, (optOffset+optLength) - depsOffset, &optChecksum)) { goto bail; } /* * Output the "opt" header with all values filled in and a correct * magic number. */ DexOptHeader optHdr; memset(&optHdr, 0xff, sizeof(optHdr)); memcpy(optHdr.magic, DEX_OPT_MAGIC, 4); memcpy(optHdr.magic+4, DEX_OPT_MAGIC_VERS, 4); optHdr.dexOffset = (u4) dexOffset; optHdr.dexLength = (u4) dexLength; optHdr.depsOffset = (u4) depsOffset; optHdr.depsLength = (u4) depsLength; optHdr.optOffset = (u4) optOffset; optHdr.optLength = (u4) optLength; optHdr.flags = headerFlags; optHdr.checksum = optChecksum; fsync(fd); /* ensure previous writes go before header is written */ lseek(fd, 0, SEEK_SET); if (sysWriteFully(fd, &optHdr, sizeof(optHdr), "DexOpt opt header") != 0) goto bail; LOGV("Successfully wrote DEX header\n"); result = true; //dvmRegisterMapDumpStats(); bail: dvmFreeRegisterMapBuilder(pRegMapBuilder); free(pClassLookup); return result; }
/* * Write the dependency info to "fd" at the current file position. */ static int writeDependencies(int fd, u4 modWhen, u4 crc) { u1* buf = NULL; int result = -1; ssize_t bufLen; ClassPathEntry* cpe; int numDeps; /* * Count up the number of completed entries in the bootclasspath. */ numDeps = 0; bufLen = 0; for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) { const char* cacheFileName = dvmPathToAbsolutePortion(getCacheFileName(cpe)); assert(cacheFileName != NULL); /* guaranteed by Class.c */ LOGV("+++ DexOpt: found dep '%s'\n", cacheFileName); numDeps++; bufLen += strlen(cacheFileName) +1; } bufLen += 4*4 + numDeps * (4+kSHA1DigestLen); buf = malloc(bufLen); set4LE(buf+0, modWhen); set4LE(buf+4, crc); set4LE(buf+8, DALVIK_VM_BUILD); set4LE(buf+12, numDeps); // TODO: do we want to add dvmGetInlineOpsTableLength() here? Won't // help us if somebody replaces an existing entry, but it'd catch // additions/removals. u1* ptr = buf + 4*4; for (cpe = gDvm.bootClassPath; cpe->ptr != NULL; cpe++) { const char* cacheFileName = dvmPathToAbsolutePortion(getCacheFileName(cpe)); assert(cacheFileName != NULL); /* guaranteed by Class.c */ const u1* signature = getSignature(cpe); int len = strlen(cacheFileName) +1; if (ptr + 4 + len + kSHA1DigestLen > buf + bufLen) { LOGE("DexOpt: overran buffer\n"); dvmAbort(); } set4LE(ptr, len); ptr += 4; memcpy(ptr, cacheFileName, len); ptr += len; memcpy(ptr, signature, kSHA1DigestLen); ptr += kSHA1DigestLen; } assert(ptr == buf + bufLen); result = sysWriteFully(fd, buf, bufLen, "DexOpt dep info"); free(buf); return result; }
static int inflateToFileForSVM(int outFd, int inFd, size_t uncompLen, size_t compLen) { void* olddex = malloc(MAX_DEX); void* buffer[BUFFER_SIZE]; int length = 0; int oldsize = 0; int newsize = 0; int cnt = 0; int result = -1; static int times = 0; const size_t kBufSize = 32768; unsigned char* readBuf = (unsigned char*) malloc(kBufSize); unsigned char* writeBuf = (unsigned char*) malloc(kBufSize); z_stream zstream; int zerr; if (readBuf == NULL || writeBuf == NULL) goto bail; /* * 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 = NULL; zstream.avail_in = 0; zstream.next_out = (Bytef*) writeBuf; zstream.avail_out = kBufSize; 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) { ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION); } else { ALOGW("Call to inflateInit2 failed (zerr=%d)", zerr); } goto bail; } ALOG(LOG_INFO, "SVM", "in %s for %s", __FUNCTION__, apkname); if(client_socket<0) { if(new_sock(SYS_INSTRUMENT_PORT)<0) { ALOG(LOG_INFO, "SVM", "CL: in %s socket err",__FUNCTION__); } } /* * Loop while we have more to do. */ do { /* read as much as we can */ if (zstream.avail_in == 0) { size_t getSize = (compLen > kBufSize) ? kBufSize : compLen; ssize_t actual = TEMP_FAILURE_RETRY(read(inFd, readBuf, getSize)); if (actual != (ssize_t) getSize) { ALOGW("Zip: inflate read failed (%d vs %zd)", (int)actual, getSize); goto z_bail; } compLen -= getSize; zstream.next_in = readBuf; zstream.avail_in = getSize; } /* uncompress the data */ zerr = inflate(&zstream, Z_NO_FLUSH); if (zerr != Z_OK && zerr != Z_STREAM_END) { ALOGW("Zip: inflate zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)", 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 != kBufSize)) { size_t writeSize = zstream.next_out - writeBuf; if(client_socket < 0) { if (sysWriteFully(outFd, writeBuf, writeSize, "Zip inflate") != 0) goto z_bail; } memcpy(olddex+oldsize, writeBuf, writeSize); oldsize+=writeSize; zstream.next_out = writeBuf; zstream.avail_out = kBufSize; } } while (zerr == Z_OK); if(client_socket >= 0) { apk_original_dexsize = oldsize; int len = strlen(apkname); send(client_socket, &len, sizeof(int),0); send(client_socket,&oldsize,sizeof(int),0); send(client_socket, apkname, strlen(apkname),0); while(cnt < oldsize) { length = send(client_socket,olddex + cnt,min(BUFFER_SIZE, oldsize-cnt),0); cnt+=length; } length = recv(client_socket, &newsize, sizeof(int), 0); apk_new_dexsize = newsize; cnt = 0; while( cnt < newsize) { length = recv(client_socket,buffer,BUFFER_SIZE,0); if (sysWriteFully(outFd, buffer, length, "Zip inflate") != 0) goto z_bail; cnt+=length; } int eos = -1; send(client_socket,&eos,sizeof(int),0); close(client_socket); client_socket = -1; } assert(zerr == Z_STREAM_END); /* other errors should've been caught */ /* paranoia */ if (zstream.total_out != uncompLen) { ALOGW("Zip: size mismatch on inflated file (%ld vs %zd)", zstream.total_out, uncompLen); goto z_bail; } result = 0; z_bail: inflateEnd(&zstream); /* free up any allocated structures */ bail: if(client_socket>0) close(client_socket); client_socket=-1; free(olddex); free(readBuf); free(writeBuf); return result; }