• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         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  */
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         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  */
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         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. */
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         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. */
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         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  */
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