1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
|
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_RUNTIME_OAT_FILE_ASSISTANT_H_
#define ART_RUNTIME_OAT_FILE_ASSISTANT_H_
#include <cstdint>
#include <memory>
#include <sstream>
#include <string>
#include "arch/instruction_set.h"
#include "base/os.h"
#include "base/scoped_flock.h"
#include "base/unix_file/fd_file.h"
#include "compiler_filter.h"
#include "class_loader_context.h"
#include "oat_file.h"
namespace art {
namespace gc {
namespace space {
class ImageSpace;
} // namespace space
} // namespace gc
// Class for assisting with oat file management.
//
// This class collects common utilities for determining the status of an oat
// file on the device, updating the oat file, and loading the oat file.
//
// The oat file assistant is intended to be used with dex locations not on the
// boot class path. See the IsInBootClassPath method for a way to check if the
// dex location is in the boot class path.
class OatFileAssistant {
public:
enum DexOptNeeded {
// No dexopt should (or can) be done to update the apk/jar.
// Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0
kNoDexOptNeeded = 0,
// dex2oat should be run to update the apk/jar from scratch.
// Matches Java: dalvik.system.DexFile.DEX2OAT_FROM_SCRATCH = 1
kDex2OatFromScratch = 1,
// dex2oat should be run to update the apk/jar because the existing code
// is out of date with respect to the boot image.
// Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_BOOT_IMAGE
kDex2OatForBootImage = 2,
// dex2oat should be run to update the apk/jar because the existing code
// is out of date with respect to the target compiler filter.
// Matches Java: dalvik.system.DexFile.DEX2OAT_FOR_FILTER
kDex2OatForFilter = 3,
};
enum OatStatus {
// kOatCannotOpen - The oat file cannot be opened, because it does not
// exist, is unreadable, or otherwise corrupted.
kOatCannotOpen,
// kOatDexOutOfDate - The oat file is out of date with respect to the dex file.
kOatDexOutOfDate,
// kOatBootImageOutOfDate - The oat file is up to date with respect to the
// dex file, but is out of date with respect to the boot image.
kOatBootImageOutOfDate,
// kOatUpToDate - The oat file is completely up to date with respect to
// the dex file and boot image.
kOatUpToDate,
};
// Constructs an OatFileAssistant object to assist the oat file
// corresponding to the given dex location with the target instruction set.
//
// The dex_location must not be null and should remain available and
// unchanged for the duration of the lifetime of the OatFileAssistant object.
// Typically the dex_location is the absolute path to the original,
// un-optimized dex file.
//
// Note: Currently the dex_location must have an extension.
// TODO: Relax this restriction?
//
// The isa should be either the 32 bit or 64 bit variant for the current
// device. For example, on an arm device, use arm or arm64. An oat file can
// be loaded executable only if the ISA matches the current runtime.
//
// load_executable should be true if the caller intends to try and load
// executable code for this dex location.
//
// only_load_system_executable should be true if the caller intends to have
// only oat files from /system loaded executable.
OatFileAssistant(const char* dex_location,
const InstructionSet isa,
bool load_executable,
bool only_load_system_executable = false);
// Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
// provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
// Otherwise, dex_location will be used to construct necessary filenames.
OatFileAssistant(const char* dex_location,
const InstructionSet isa,
bool load_executable,
bool only_load_system_executable,
int vdex_fd,
int oat_fd,
int zip_fd);
~OatFileAssistant();
// Returns true if the dex location refers to an element of the boot class
// path.
bool IsInBootClassPath();
// Return what action needs to be taken to produce up-to-date code for this
// dex location. If "downgrade" is set to false, it verifies if the current
// compiler filter is at least as good as an oat file generated with the
// given compiler filter otherwise, if its set to true, it checks whether
// the oat file generated with the target filter will be downgraded as
// compared to the current state. For example, if the current compiler filter is
// quicken, and target filter is verify, it will recommend to dexopt, while
// if the target filter is speed profile, it will recommend to keep it in its
// current state.
// profile_changed should be true to indicate the profile has recently changed
// for this dex location.
// If the purpose of the dexopt is to downgrade the compiler filter,
// set downgrade to true.
// Returns a positive status code if the status refers to the oat file in
// the oat location. Returns a negative status code if the status refers to
// the oat file in the odex location.
int GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed = false,
bool downgrade = false,
ClassLoaderContext* context = nullptr,
const std::vector<int>& context_fds = std::vector<int>());
// Returns true if there is up-to-date code for this dex location,
// irrespective of the compiler filter of the up-to-date code.
bool IsUpToDate();
// Returns an oat file that can be used for loading dex files.
// Returns null if no suitable oat file was found.
//
// After this call, no other methods of the OatFileAssistant should be
// called, because access to the loaded oat file has been taken away from
// the OatFileAssistant object.
std::unique_ptr<OatFile> GetBestOatFile();
// Returns a human readable description of the status of the code for the
// dex file. The returned description is for debugging purposes only.
std::string GetStatusDump();
// Computes the optimization status of the given dex file. The result is
// returned via the two output parameters.
// - out_compilation_filter: the level of optimizations (compiler filter)
// - out_compilation_reason: the optimization reason. The reason might
// be "unknown" if the compiler artifacts were not annotated during optimizations.
//
// This method will try to mimic the runtime effect of loading the dex file.
// For example, if there is no usable oat file, the compiler filter will be set
// to "run-from-apk".
static void GetOptimizationStatus(const std::string& filename,
InstructionSet isa,
std::string* out_compilation_filter,
std::string* out_compilation_reason);
// Open and returns an image space associated with the oat file.
static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
// Loads the dex files in the given oat file for the given dex location.
// The oat file should be up to date for the given dex location.
// This loads multiple dex files in the case of multidex.
// Returns an empty vector if no dex files for that location could be loaded
// from the oat file.
//
// The caller is responsible for freeing the dex_files returned, if any. The
// dex_files will only remain valid as long as the oat_file is valid.
static std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(
const OatFile& oat_file, const char* dex_location);
// Same as `std::vector<std::unique_ptr<const DexFile>> LoadDexFiles(...)` with the difference:
// - puts the dex files in the given vector
// - returns whether or not all dex files were successfully opened
static bool LoadDexFiles(const OatFile& oat_file,
const std::string& dex_location,
std::vector<std::unique_ptr<const DexFile>>* out_dex_files);
// Returns true if there are dex files in the original dex location that can
// be compiled with dex2oat for this dex location.
// Returns false if there is no original dex file, or if the original dex
// file is an apk/zip without a classes.dex entry.
bool HasOriginalDexFiles();
// If the dex file has been installed with a compiled oat file alongside
// it, the compiled oat file will have the extension .odex, and is referred
// to as the odex file. It is called odex for legacy reasons; the file is
// really an oat file. The odex file will often, but not always, have a
// patch delta of 0 and need to be relocated before use for the purposes of
// ASLR. The odex file is treated as if it were read-only.
//
// Returns the status of the odex file for the dex location.
OatStatus OdexFileStatus();
// When the dex files is compiled on the target device, the oat file is the
// result. The oat file will have been relocated to some
// (possibly-out-of-date) offset for ASLR.
//
// Returns the status of the oat file for the dex location.
OatStatus OatFileStatus();
// Constructs the odex file name for the given dex location.
// Returns true on success, in which case odex_filename is set to the odex
// file name.
// Returns false on error, in which case error_msg describes the error and
// odex_filename is not changed.
// Neither odex_filename nor error_msg may be null.
static bool DexLocationToOdexFilename(const std::string& location,
InstructionSet isa,
std::string* odex_filename,
std::string* error_msg);
// Constructs the oat file name for the given dex location.
// Returns true on success, in which case oat_filename is set to the oat
// file name.
// Returns false on error, in which case error_msg describes the error and
// oat_filename is not changed.
// Neither oat_filename nor error_msg may be null.
static bool DexLocationToOatFilename(const std::string& location,
InstructionSet isa,
std::string* oat_filename,
std::string* error_msg);
// Computes the location checksum, dex location and vdex filename by combining
// the checksums of the individual dex files. If the data directory of the process
// is known, creates an absolute path in that directory and tries to infer path
// of a corresponding vdex file. Otherwise only creates a basename dex_location
// from the combined checksums. Returns true if all out-arguments have been set.
static bool AnonymousDexVdexLocation(const std::vector<const DexFile::Header*>& dex_headers,
InstructionSet isa,
/* out */ uint32_t* location_checksum,
/* out */ std::string* dex_location,
/* out */ std::string* vdex_filename);
// Returns true if a filename (given as basename) is a name of a vdex for
// anonymous dex file(s) created by AnonymousDexVdexLocation.
static bool IsAnonymousVdexBasename(const std::string& basename);
private:
class OatFileInfo {
public:
// Initially the info is for no file in particular. It will treat the
// file as out of date until Reset is called with a real filename to use
// the cache for.
// Pass true for is_oat_location if the information associated with this
// OatFileInfo is for the oat location, as opposed to the odex location.
OatFileInfo(OatFileAssistant* oat_file_assistant, bool is_oat_location);
bool IsOatLocation();
const std::string* Filename();
// Returns true if this oat file can be used for running code. The oat
// file can be used for running code as long as it is not out of date with
// respect to the dex code or boot image. An oat file that is out of date
// with respect to relocation is considered useable, because it's possible
// to interpret the dex code rather than run the unrelocated compiled
// code.
bool IsUseable();
// Returns the status of this oat file.
OatStatus Status();
// Return the DexOptNeeded value for this oat file with respect to the
// given target_compilation_filter.
// profile_changed should be true to indicate the profile has recently
// changed for this dex location.
// downgrade should be true if the purpose of dexopt is to downgrade the
// compiler filter.
DexOptNeeded GetDexOptNeeded(CompilerFilter::Filter target_compiler_filter,
bool profile_changed,
bool downgrade,
ClassLoaderContext* context,
const std::vector<int>& context_fds);
// Returns the loaded file.
// Loads the file if needed. Returns null if the file failed to load.
// The caller shouldn't clean up or free the returned pointer.
const OatFile* GetFile();
// Returns true if the file is opened executable.
bool IsExecutable();
// Clear any cached information about the file that depends on the
// contents of the file. This does not reset the provided filename.
void Reset();
// Clear any cached information and switch to getting info about the oat
// file with the given filename.
void Reset(const std::string& filename,
bool use_fd,
int zip_fd = -1,
int vdex_fd = -1,
int oat_fd = -1);
// Release the loaded oat file for runtime use.
// Returns null if the oat file hasn't been loaded or is out of date.
// Ensures the returned file is not loaded executable if it has unuseable
// compiled code.
//
// After this call, no other methods of the OatFileInfo should be
// called, because access to the loaded oat file has been taken away from
// the OatFileInfo object.
std::unique_ptr<OatFile> ReleaseFileForUse();
private:
// Returns true if the compiler filter used to generate the file is at
// least as good as the given target filter. profile_changed should be
// true to indicate the profile has recently changed for this dex
// location.
// downgrade should be true if the purpose of dexopt is to downgrade the
// compiler filter.
bool CompilerFilterIsOkay(CompilerFilter::Filter target, bool profile_changed, bool downgrade);
bool ClassLoaderContextIsOkay(ClassLoaderContext* context, const std::vector<int>& context_fds);
// Release the loaded oat file.
// Returns null if the oat file hasn't been loaded.
//
// After this call, no other methods of the OatFileInfo should be
// called, because access to the loaded oat file has been taken away from
// the OatFileInfo object.
std::unique_ptr<OatFile> ReleaseFile();
OatFileAssistant* oat_file_assistant_;
const bool is_oat_location_;
bool filename_provided_ = false;
std::string filename_;
int zip_fd_ = -1;
int oat_fd_ = -1;
int vdex_fd_ = -1;
bool use_fd_ = false;
bool load_attempted_ = false;
std::unique_ptr<OatFile> file_;
bool status_attempted_ = false;
OatStatus status_ = OatStatus::kOatCannotOpen;
// For debugging only.
// If this flag is set, the file has been released to the user and the
// OatFileInfo object is in a bad state and should no longer be used.
bool file_released_ = false;
};
// Return info for the best oat file.
OatFileInfo& GetBestInfo();
// Returns true when vdex/oat/odex files should be read from file descriptors.
// The method checks the value of zip_fd_, and if the value is valid, returns
// true. This is required to have a deterministic behavior around how different
// files are being read.
bool UseFdToReadFiles();
// Returns true if the dex checksums in the given vdex file are up to date
// with respect to the dex location. If the dex checksums are not up to
// date, error_msg is updated with a message describing the problem.
bool DexChecksumUpToDate(const VdexFile& file, std::string* error_msg);
// Returns true if the dex checksums in the given oat file are up to date
// with respect to the dex location. If the dex checksums are not up to
// date, error_msg is updated with a message describing the problem.
bool DexChecksumUpToDate(const OatFile& file, std::string* error_msg);
// Return the status for a given opened oat file with respect to the dex
// location.
OatStatus GivenOatFileStatus(const OatFile& file);
// Gets the dex checksums required for an up-to-date oat file.
// Returns cached_required_dex_checksums if the required checksums were
// located. Returns null if the required checksums were not found. The
// caller shouldn't clean up or free the returned pointer. This sets the
// has_original_dex_files_ field to true if the checksums were found for the
// dex_location_ dex file.
const std::vector<uint32_t>* GetRequiredDexChecksums();
// Validates the boot class path checksum of an OatFile.
bool ValidateBootClassPathChecksums(const OatFile& oat_file);
// To implement Lock(), we lock a dummy file where the oat file would go
// (adding ".flock" to the target file name) and retain the lock for the
// remaining lifetime of the OatFileAssistant object.
ScopedFlock flock_;
std::string dex_location_;
// Whether or not the parent directory of the dex file is writable.
bool dex_parent_writable_ = false;
// In a properly constructed OatFileAssistant object, isa_ should be either
// the 32 or 64 bit variant for the current device.
const InstructionSet isa_ = InstructionSet::kNone;
// Whether we will attempt to load oat files executable.
bool load_executable_ = false;
// Whether only oat files on /system are loaded executable.
const bool only_load_system_executable_ = false;
// Whether the potential zip file only contains uncompressed dex.
// Will be set during GetRequiredDexChecksums.
bool zip_file_only_contains_uncompressed_dex_ = true;
// Cached value of the required dex checksums.
// This should be accessed only by the GetRequiredDexChecksums() method.
std::vector<uint32_t> cached_required_dex_checksums_;
bool required_dex_checksums_attempted_ = false;
bool required_dex_checksums_found_;
bool has_original_dex_files_;
OatFileInfo odex_;
OatFileInfo oat_;
// File descriptor corresponding to apk, dex file, or zip.
int zip_fd_;
size_t cached_boot_class_path_checksum_component_count_ = 0u;
std::string cached_boot_class_path_checksums_;
friend class OatFileAssistantTest;
DISALLOW_COPY_AND_ASSIGN(OatFileAssistant);
};
std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status);
} // namespace art
#endif // ART_RUNTIME_OAT_FILE_ASSISTANT_H_
|