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 */
copyFileToFile(int destFd,int srcFd,size_t size)33 static int copyFileToFile(int destFd, int srcFd, size_t size)
34 {
35 if (lseek(srcFd, 0, SEEK_SET) != 0) {
36 ALOGE("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 */
getModTimeAndSize(int fd,u4 * modTime,size_t * size)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 ALOGE("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 */
verifyMagicAndGetAdler32(int fd,u4 * adler32)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 ALOGE("Unable to read header: %s", strerror(errno));
80 return -1;
81 }
82
83 if (amt != sizeof(headerStart)) {
84 ALOGE("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. */
dvmRawDexFileOpen(const char * fileName,const char * odexOutputName,RawDexFile ** ppRawDexFile,bool isBootstrap)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 ALOGE("Error with header for %s", fileName);
136 goto bail;
137 }
138
139 if (getModTimeAndSize(dexFd, &modTime, &fileSize) < 0) {
140 ALOGE("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 ALOGV("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 ALOGI("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 ALOGE("Unable to extract+optimize DEX from '%s'", fileName);
198 goto bail;
199 }
200
201 endWhen = dvmGetRelativeTimeUsec();
202 ALOGD("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 ALOGI("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 ALOGE("Unable to unlock DEX file");
222 goto bail;
223 }
224 locked = false;
225 }
226
227 ALOGV("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. */
dvmRawDexFileOpenArray(u1 * pBytes,u4 length,RawDexFile ** ppRawDexFile)249 int dvmRawDexFileOpenArray(u1* pBytes, u4 length, RawDexFile** ppRawDexFile)
250 {
251 DvmDex* pDvmDex = NULL;
252
253 if (!dvmPrepareDexInMemory(pBytes, length, &pDvmDex)) {
254 ALOGD("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 */
dvmRawDexFileFree(RawDexFile * pRawDexFile)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