RawDexFile.cpp revision c1a4ab9c313d8a3d12007f2dbef7b5a6fa4ac2ef
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 */
33static 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 */
46static 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 */
69static 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. */
109int 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
235bail:
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. */
249int 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 */
268void dvmRawDexFileFree(RawDexFile* pRawDexFile)
269{
270    if (pRawDexFile == NULL)
271        return;
272
273    dvmDexFileFree(pRawDexFile->pDvmDex);
274    free(pRawDexFile->cacheFileName);
275    free(pRawDexFile);
276}
277