1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 * Open an unoptimized DEX file. 19 */ 20 21 #include "Dalvik.h" 22 #include "libdex/OptInvocation.h" 23 24 #include <fcntl.h> 25 #include <sys/stat.h> 26 #include <sys/types.h> 27 #include <unistd.h> 28 29 /* 30 * Copy the given number of bytes from one fd to another, first 31 * seeking the source fd to the start of the file. 32 */ 33 static int copyFileToFile(int destFd, int srcFd, size_t size) 34 { 35 if (lseek(srcFd, 0, SEEK_SET) != 0) { 36 LOGE("lseek failure: %s", strerror(errno)); 37 return -1; 38 } 39 40 return sysCopyFileToFile(destFd, srcFd, size); 41 } 42 43 /* 44 * Get the modification time and size in bytes for the given fd. 45 */ 46 static int getModTimeAndSize(int fd, u4* modTime, size_t* size) 47 { 48 struct stat buf; 49 int result = fstat(fd, &buf); 50 51 if (result < 0) { 52 LOGE("Unable to determine mod time: %s", strerror(errno)); 53 return -1; 54 } 55 56 *modTime = (u4) buf.st_mtime; 57 *size = (size_t) buf.st_size; 58 assert((size_t) buf.st_size == buf.st_size); 59 60 return 0; 61 } 62 63 /* 64 * Verify the dex file magic number, and get the adler32 checksum out 65 * of the given fd, which is presumed to be a reference to a dex file 66 * with the cursor at the start of the file. The fd's cursor is 67 * modified by this operation. 68 */ 69 static int verifyMagicAndGetAdler32(int fd, u4 *adler32) 70 { 71 /* 72 * The start of a dex file is eight bytes of magic followed by 73 * four bytes of checksum. 74 */ 75 u1 headerStart[12]; 76 ssize_t amt = read(fd, headerStart, sizeof(headerStart)); 77 78 if (amt < 0) { 79 LOGE("Unable to read header: %s", strerror(errno)); 80 return -1; 81 } 82 83 if (amt != sizeof(headerStart)) { 84 LOGE("Unable to read full header (only got %d bytes)", (int) amt); 85 return -1; 86 } 87 88 if (!dexHasValidMagic((DexHeader*) (void*) headerStart)) { 89 return -1; 90 } 91 92 /* 93 * We can't just cast the data to a u4 and read it, since the 94 * platform might be big-endian (also, because that would make the 95 * compiler complain about type-punned pointers). We assume here 96 * that the dex file is in the standard little-endian format; if 97 * that assumption turns out to be invalid, code that runs later 98 * will notice and complain. 99 */ 100 *adler32 = (u4) headerStart[8] 101 | (((u4) headerStart[9]) << 8) 102 | (((u4) headerStart[10]) << 16) 103 | (((u4) headerStart[11]) << 24); 104 105 return 0; 106 } 107 108 /* See documentation comment in header. */ 109 int dvmRawDexFileOpen(const char* fileName, const char* odexOutputName, 110 RawDexFile** ppRawDexFile, bool isBootstrap) 111 { 112 /* 113 * TODO: This duplicates a lot of code from dvmJarFileOpen() in 114 * JarFile.c. This should be refactored. 115 */ 116 117 DvmDex* pDvmDex = NULL; 118 char* cachedName = NULL; 119 int result = -1; 120 int dexFd = -1; 121 int optFd = -1; 122 u4 modTime = 0; 123 u4 adler32 = 0; 124 size_t fileSize = 0; 125 bool newFile = false; 126 bool locked = false; 127 128 dexFd = open(fileName, O_RDONLY); 129 if (dexFd < 0) goto bail; 130 131 /* If we fork/exec into dexopt, don't let it inherit the open fd. */ 132 dvmSetCloseOnExec(dexFd); 133 134 if (verifyMagicAndGetAdler32(dexFd, &adler32) < 0) { 135 LOGE("Error with header for %s", fileName); 136 goto bail; 137 } 138 139 if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) { 140 LOGE("Error with stat for %s", fileName); 141 goto bail; 142 } 143 144 /* 145 * See if the cached file matches. If so, optFd will become a reference 146 * to the cached file and will have been seeked to just past the "opt" 147 * header. 148 */ 149 150 if (odexOutputName == NULL) { 151 cachedName = dexOptGenerateCacheFileName(fileName, NULL); 152 if (cachedName == NULL) 153 goto bail; 154 } else { 155 cachedName = strdup(odexOutputName); 156 } 157 158 LOGV("dvmRawDexFileOpen: Checking cache for %s (%s)", 159 fileName, cachedName); 160 161 optFd = dvmOpenCachedDexFile(fileName, cachedName, modTime, 162 adler32, isBootstrap, &newFile, /*createIfMissing=*/true); 163 164 if (optFd < 0) { 165 LOGI("Unable to open or create cache for %s (%s)", 166 fileName, cachedName); 167 goto bail; 168 } 169 locked = true; 170 171 /* 172 * If optFd points to a new file (because there was no cached 173 * version, or the cached version was stale), generate the 174 * optimized DEX. The file descriptor returned is still locked, 175 * and is positioned just past the optimization header. 176 */ 177 if (newFile) { 178 u8 startWhen, copyWhen, endWhen; 179 bool result; 180 off_t dexOffset; 181 182 dexOffset = lseek(optFd, 0, SEEK_CUR); 183 result = (dexOffset > 0); 184 185 if (result) { 186 startWhen = dvmGetRelativeTimeUsec(); 187 result = copyFileToFile(optFd, dexFd, fileSize) == 0; 188 copyWhen = dvmGetRelativeTimeUsec(); 189 } 190 191 if (result) { 192 result = dvmOptimizeDexFile(optFd, dexOffset, fileSize, 193 fileName, modTime, adler32, isBootstrap); 194 } 195 196 if (!result) { 197 LOGE("Unable to extract+optimize DEX from '%s'", fileName); 198 goto bail; 199 } 200 201 endWhen = dvmGetRelativeTimeUsec(); 202 LOGD("DEX prep '%s': copy in %dms, rewrite %dms", 203 fileName, 204 (int) (copyWhen - startWhen) / 1000, 205 (int) (endWhen - copyWhen) / 1000); 206 } 207 208 /* 209 * Map the cached version. This immediately rewinds the fd, so it 210 * doesn't have to be seeked anywhere in particular. 211 */ 212 if (dvmDexFileOpenFromFd(optFd, &pDvmDex) != 0) { 213 LOGI("Unable to map cached %s", fileName); 214 goto bail; 215 } 216 217 if (locked) { 218 /* unlock the fd */ 219 if (!dvmUnlockCachedDexFile(optFd)) { 220 /* uh oh -- this process needs to exit or we'll wedge the system */ 221 LOGE("Unable to unlock DEX file"); 222 goto bail; 223 } 224 locked = false; 225 } 226 227 LOGV("Successfully opened '%s'", fileName); 228 229 *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile)); 230 (*ppRawDexFile)->cacheFileName = cachedName; 231 (*ppRawDexFile)->pDvmDex = pDvmDex; 232 cachedName = NULL; // don't free it below 233 result = 0; 234 235 bail: 236 free(cachedName); 237 if (dexFd >= 0) { 238 close(dexFd); 239 } 240 if (optFd >= 0) { 241 if (locked) 242 (void) dvmUnlockCachedDexFile(optFd); 243 close(optFd); 244 } 245 return result; 246 } 247 248 /* See documentation comment in header. */ 249 int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile) 250 { 251 DvmDex* pDvmDex = NULL; 252 253 if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) { 254 LOGD("Unable to open raw DEX from array"); 255 return -1; 256 } 257 assert(pDvmDex != NULL); 258 259 *ppRawDexFile = (RawDexFile*) calloc(1, sizeof(RawDexFile)); 260 (*ppRawDexFile)->pDvmDex = pDvmDex; 261 262 return 0; 263 } 264 265 /* 266 * Close a RawDexFile and free the struct. 267 */ 268 void dvmRawDexFileFree(RawDexFile* pRawDexFile) 269 { 270 if (pRawDexFile == NULL) 271 return; 272 273 dvmDexFileFree(pRawDexFile->pDvmDex); 274 free(pRawDexFile->cacheFileName); 275 free(pRawDexFile); 276 } 277