blob: b8232d9d41e692a752c1aee6324ebae29dcf677b [file] [log] [blame]
Brian Carlstrome24fa612011-09-29 00:53:55 -07001// Copyright 2011 Google Inc. All Rights Reserved.
2
3#include "oat_writer.h"
4
5#include "class_linker.h"
6#include "class_loader.h"
7#include "file.h"
8#include "os.h"
9#include "stl_util.h"
10
11namespace art {
12
13bool OatWriter::Create(const std::string& filename, const ClassLoader* class_loader) {
14 const std::vector<const DexFile*>& dex_files = ClassLoader::GetClassPath(class_loader);
15 OatWriter oat_writer(dex_files, class_loader);
16 return oat_writer.Write(filename);
17}
18
19OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, const ClassLoader* class_loader) {
20 class_loader_ = class_loader;
21 dex_files_ = &dex_files;
22
23 size_t offset = InitOatHeader();
24 offset = InitOatDexFiles(offset);
25 offset = InitOatClasses(offset);
26 offset = InitOatMethods(offset);
27 offset = InitOatCode(offset);
28 offset = InitOatCodeDexFiles(offset);
29
30 CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
31 CHECK_EQ(dex_files_->size(), oat_classes_.size());
32}
33
34size_t OatWriter::InitOatHeader() {
35 // create the OatHeader
36 oat_header_ = new OatHeader(dex_files_);
37 size_t offset = sizeof(*oat_header_);
38 return offset;
39}
40
41size_t OatWriter::InitOatDexFiles(size_t offset) {
42 // create the OatDexFiles
43 for (size_t i = 0; i != dex_files_->size(); ++i) {
44 const DexFile* dex_file = (*dex_files_)[i];
45 CHECK(dex_file != NULL);
46 OatDexFile* oat_dex_file = new OatDexFile(*dex_file);
47 oat_dex_files_.push_back(oat_dex_file);
48 offset += oat_dex_file->SizeOf();
49 }
50 return offset;
51}
52
53size_t OatWriter::InitOatClasses(size_t offset) {
54 // create the OatClasses
55 // calculate the offsets within OatDexFiles to OatClasses
56 for (size_t i = 0; i != dex_files_->size(); ++i) {
57 // set offset in OatDexFile to OatClasses
58 oat_dex_files_[i]->classes_offset_ = offset;
59 oat_dex_files_[i]->UpdateChecksum(*oat_header_);
60
61 const DexFile* dex_file = (*dex_files_)[i];
62 OatClasses* oat_classes = new OatClasses(*dex_file);
63 oat_classes_.push_back(oat_classes);
64 offset += oat_classes->SizeOf();
65 }
66 return offset;
67}
68
69size_t OatWriter::InitOatMethods(size_t offset) {
70 // create the OatMethods
71 // calculate the offsets within OatClasses to OatMethods
72 size_t class_index = 0;
73 for (size_t i = 0; i != dex_files_->size(); ++i) {
74 const DexFile* dex_file = (*dex_files_)[i];
75 for (size_t class_def_index = 0;
76 class_def_index < dex_file->NumClassDefs();
77 class_def_index++, class_index++) {
78 oat_classes_[i]->methods_offsets_[class_def_index] = offset;
79 const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
80 const byte* class_data = dex_file->GetClassData(class_def);
81 DexFile::ClassDataHeader header = dex_file->ReadClassDataHeader(&class_data);
82 size_t num_direct_methods = header.direct_methods_size_;
83 size_t num_virtual_methods = header.virtual_methods_size_;
84 uint32_t num_methods = num_direct_methods + num_virtual_methods;
85 OatMethods* oat_methods = new OatMethods(num_methods);
86 oat_methods_.push_back(oat_methods);
87 offset += oat_methods->SizeOf();
88 }
89 oat_classes_[i]->UpdateChecksum(*oat_header_);
90 }
91 return offset;
92}
93
94size_t OatWriter::InitOatCode(size_t offset) {
95 // calculate the offsets within OatHeader to executable code
96 size_t old_offset = offset;
97 // required to be on a new page boundary
98 offset = RoundUp(offset, kPageSize);
99 oat_header_->SetExecutableOffset(offset);
100 executable_offset_padding_length_ = offset - old_offset;
101 return offset;
102}
103
104size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
105 // calculate the offsets within OatMethods
106 size_t oat_class_index = 0;
107 for (size_t i = 0; i != dex_files_->size(); ++i) {
108 const DexFile* dex_file = (*dex_files_)[i];
109 CHECK(dex_file != NULL);
110 offset = InitOatCodeDexFile(offset, oat_class_index, *dex_file);
111 }
112 return offset;
113}
114
115size_t OatWriter::InitOatCodeDexFile(size_t offset,
116 size_t& oat_class_index,
117 const DexFile& dex_file) {
118 for (size_t class_def_index = 0;
119 class_def_index < dex_file.NumClassDefs();
120 class_def_index++, oat_class_index++) {
121 const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
122 offset = InitOatCodeClassDef(offset, oat_class_index, dex_file, class_def);
123 oat_methods_[oat_class_index]->UpdateChecksum(*oat_header_);
124 }
125 return offset;
126}
127
128size_t OatWriter::InitOatCodeClassDef(size_t offset,
129 size_t oat_class_index,
130 const DexFile& dex_file,
131 const DexFile::ClassDef& class_def) {
132 const byte* class_data = dex_file.GetClassData(class_def);
133 DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data);
134 size_t num_virtual_methods = header.virtual_methods_size_;
135 const char* descriptor = dex_file.GetClassDescriptor(class_def);
136
137 // TODO: remove code ByteArrays from Class/Method (and therefore ClassLoader)
138 // TODO: don't write code for shared stubs
139 Class* klass = Runtime::Current()->GetClassLinker()->FindClass(descriptor, class_loader_);
140 CHECK(klass != NULL) << descriptor;
141 CHECK_EQ(klass->GetClassLoader(), class_loader_);
142 CHECK_EQ(oat_methods_[oat_class_index]->method_offsets_.size(),
143 klass->NumDirectMethods() + num_virtual_methods);
144 // Note that we leave the offset to the code in Method::code_
145 size_t class_def_method_index = 0;
146 for (size_t i = 0; i < klass->NumDirectMethods(); i++, class_def_method_index++) {
147 Method* method = klass->GetDirectMethod(i);
148 CHECK(method != NULL) << descriptor << " direct " << i;
149 offset = InitOatCodeMethod(offset, oat_class_index, class_def_method_index, method);
150 }
151 // note that num_virtual_methods != klass->NumVirtualMethods() because of miranda methods
152 for (size_t i = 0; i < num_virtual_methods; i++, class_def_method_index++) {
153 Method* method = klass->GetVirtualMethod(i);
154 CHECK(method != NULL) << descriptor << " virtual " << i;
155 offset = InitOatCodeMethod(offset, oat_class_index, class_def_method_index, method);
156 }
157 return offset;
158}
159
160size_t OatWriter::InitOatCodeMethod(size_t offset,
161 size_t oat_class_index,
162 size_t class_def_method_index,
163 Method* method) {
164 Runtime* runtime = Runtime::Current();
165 ByteArray* jni_stub_array = runtime->GetJniStubArray();
166 ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray();
167
168 const ByteArray* code_array = method->GetCodeArray();
169 if (code_array == NULL || code_array == jni_stub_array || code_array == ame_stub_array) {
170 oat_methods_[oat_class_index]->method_offsets_[class_def_method_index] = 0;
171 method->SetOatCodeOffset(0);
172 } else {
173 offset = RoundUp(offset, kArmAlignment);
174 uint32_t thumb_offset = (reinterpret_cast<const int8_t*>(method->GetCode())
175 - code_array->GetData());
176 uint32_t code_offset = offset + thumb_offset;
177 oat_methods_[oat_class_index]->method_offsets_[class_def_method_index] = code_offset;
178 method->SetOatCodeOffset(code_offset);
179 offset += code_array->GetLength();
180 oat_header_->UpdateChecksum(code_array->GetData(), code_array->GetLength());
181 }
182 return offset;
183}
184
185bool OatWriter::Write(const std::string& filename) {
186
187 UniquePtr<File> file(OS::OpenFile(filename.c_str(), true));
188 if (file.get() == NULL) {
189 return false;
190 }
191
192 if (!file->WriteFully(oat_header_, sizeof(*oat_header_))) {
193 PLOG(ERROR) << "Failed to write oat header to " << filename;
194 return false;
195 }
196
197 if (!WriteTables(file.get())) {
198 LOG(ERROR) << "Failed to write oat tables to " << filename;
199 return false;
200 }
201
202 size_t code_offset = WriteCode(file.get());
203 if (code_offset == 0) {
204 LOG(ERROR) << "Failed to write oat code to " << filename;
205 return false;
206 }
207
208 code_offset = WriteCodeDexFiles(file.get(), code_offset);
209 if (code_offset == 0) {
210 LOG(ERROR) << "Failed to write oat code for dex files to " << filename;
211 return false;
212 }
213
214 return true;
215}
216
217bool OatWriter::WriteTables(File* file) {
218 for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
219 if (!oat_dex_files_[i]->Write(file)) {
220 PLOG(ERROR) << "Failed to write oat dex information";
221 return false;
222 }
223 }
224 for (size_t i = 0; i != oat_classes_.size(); ++i) {
225 if (!oat_classes_[i]->Write(file)) {
226 PLOG(ERROR) << "Failed to write oat classes information";
227 return false;
228 }
229 }
230 for (size_t i = 0; i != oat_methods_.size(); ++i) {
231 if (!oat_methods_[i]->Write(file)) {
232 PLOG(ERROR) << "Failed to write oat methods information";
233 return false;
234 }
235 }
236 return true;
237}
238
239size_t OatWriter::WriteCode(File* file) {
240 uint32_t code_offset = oat_header_->GetExecutableOffset();
241 off_t new_offset = lseek(file->Fd(), executable_offset_padding_length_, SEEK_CUR);
242 if (static_cast<uint32_t>(new_offset) != code_offset) {
243 PLOG(ERROR) << "Failed to seek to oat code section";
244 return 0;
245 }
246 return code_offset;
247}
248
249size_t OatWriter::WriteCodeDexFiles(File* file, size_t code_offset) {
250 for (size_t i = 0; i != oat_classes_.size(); ++i) {
251 const DexFile* dex_file = (*dex_files_)[i];
252 CHECK(dex_file != NULL);
253 code_offset = WriteCodeDexFile(file, code_offset, *dex_file);
254 if (code_offset == 0) {
255 return 0;
256 }
257 }
258 return code_offset;
259}
260
261size_t OatWriter::WriteCodeDexFile(File* file,
262 size_t code_offset,
263 const DexFile& dex_file) {
264 for (size_t class_def_index = 0;
265 class_def_index < dex_file.NumClassDefs();
266 class_def_index++) {
267 const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
268 code_offset = WriteCodeClassDef(file, code_offset, dex_file, class_def);
269 if (code_offset == 0) {
270 return 0;
271 }
272 }
273 return code_offset;
274}
275
276size_t OatWriter::WriteCodeClassDef(File* file,
277 size_t code_offset,
278 const DexFile& dex_file,
279 const DexFile::ClassDef& class_def) {
280 const Runtime* runtime = Runtime::Current();
281 ClassLinker* class_linker = runtime->GetClassLinker();
282 ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray();
283
284 const byte* class_data = dex_file.GetClassData(class_def);
285 DexFile::ClassDataHeader header = dex_file.ReadClassDataHeader(&class_data);
286 size_t num_virtual_methods = header.virtual_methods_size_;
287 const char* descriptor = dex_file.GetClassDescriptor(class_def);
288 Class* klass = class_linker->FindClass(descriptor, class_loader_);
289
290 // TODO: deduplicate code arrays
291 // Note that we clear the code array here, image_writer will use GetCodeOffset to find it
292 for (size_t i = 0; i < klass->NumDirectMethods(); i++) {
293 Method* method = klass->GetDirectMethod(i);
294 code_offset = WriteCodeMethod(file, code_offset, method);
295 if (code_offset == 0) {
296 return 0;
297 }
298 }
299 // note that num_virtual_methods != klass->NumVirtualMethods() because of miranda methods
300 for (size_t i = 0; i < num_virtual_methods; i++) {
301 Method* method = klass->GetVirtualMethod(i);
302 code_offset = WriteCodeMethod(file, code_offset, method);
303 if (code_offset == 0) {
304 return 0;
305 }
306 }
307 for (size_t i = num_virtual_methods; i < klass->NumVirtualMethods(); i++) {
308 Method* method = klass->GetVirtualMethod(i);
309 const ByteArray* code_array = method->GetCodeArray();
310 CHECK(code_array == NULL // if compiler not run
311 || code_array == ame_stub_array) // otherwise
312 << PrettyMethod(method) << " " << code_array;
313 method->SetCodeArray(NULL, kNone);
314 }
315 return code_offset;
316}
317
318size_t OatWriter::WriteCodeMethod(File* file,
319 size_t code_offset,
320 Method* method) {
321 const Runtime* runtime = Runtime::Current();
322 ByteArray* jni_stub_array = runtime->GetJniStubArray();
323 ByteArray* ame_stub_array = runtime->GetAbstractMethodErrorStubArray();
324
325 const ByteArray* code_array = method->GetCodeArray();
326 if (code_array != NULL && code_array != jni_stub_array && code_array != ame_stub_array) {
327 uint32_t aligned_code_offset = RoundUp(code_offset, kArmAlignment);
328 uint32_t aligned_code_delta = aligned_code_offset - code_offset;
329 if (aligned_code_delta != 0) {
330 off_t new_offset = lseek(file->Fd(), aligned_code_delta, SEEK_CUR);
331 if (static_cast<uint32_t>(new_offset) != aligned_code_offset) {
332 PLOG(ERROR) << "Failed to seek to align oat code";
333 return false;
334 }
335 code_offset += aligned_code_delta;
336 }
337 if (!file->WriteFully(code_array->GetData(), code_array->GetLength())) {
338 PLOG(ERROR) << "Failed to write method code for " << PrettyMethod(method);
339 return false;
340 }
341 code_offset += code_array->GetLength();
342 }
343 // preserve code offset around code clearing
344 uint32_t offset = method->GetOatCodeOffset();
345 method->SetCodeArray(NULL, kNone);
346 method->SetOatCodeOffset(offset);
347 return code_offset;
348}
349
350OatWriter::~OatWriter() {
351 delete oat_header_;
352 STLDeleteElements(&oat_dex_files_);
353 STLDeleteElements(&oat_classes_);
354 STLDeleteElements(&oat_methods_);
355}
356
357OatWriter::OatDexFile::OatDexFile(const DexFile& dex_file) {
358 const std::string& location = dex_file.GetLocation();
359 dex_file_location_size_ = location.size();
360 dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data());
361 dex_file_checksum_ = dex_file.GetHeader().checksum_;
362}
363
364size_t OatWriter::OatDexFile::SizeOf() const {
365 return sizeof(dex_file_location_size_)
366 + dex_file_location_size_
367 + sizeof(dex_file_checksum_)
368 + sizeof(classes_offset_);
369}
370
371void OatWriter::OatDexFile::UpdateChecksum(OatHeader& oat_header) const {
372 oat_header.UpdateChecksum(&dex_file_location_size_, sizeof(dex_file_location_size_));
373 oat_header.UpdateChecksum(dex_file_location_data_, dex_file_location_size_);
374 oat_header.UpdateChecksum(&dex_file_checksum_, sizeof(dex_file_checksum_));
375 oat_header.UpdateChecksum(&classes_offset_, sizeof(classes_offset_));
376}
377
378bool OatWriter::OatDexFile::Write(File* file) const {
379 if (!file->WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
380 PLOG(ERROR) << "Failed to write dex file location length";
381 return false;
382 }
383 if (!file->WriteFully(dex_file_location_data_, dex_file_location_size_)) {
384 PLOG(ERROR) << "Failed to write dex file location data";
385 return false;
386 }
387 if (!file->WriteFully(&dex_file_checksum_, sizeof(dex_file_checksum_))) {
388 PLOG(ERROR) << "Failed to write dex file checksum";
389 return false;
390 }
391 if (!file->WriteFully(&classes_offset_, sizeof(classes_offset_))) {
392 PLOG(ERROR) << "Failed to write classes offset";
393 return false;
394 }
395 return true;
396}
397
398OatWriter::OatClasses::OatClasses(const DexFile& dex_file) {
399 methods_offsets_.resize(dex_file.NumClassDefs());
400}
401
402size_t OatWriter::OatClasses::SizeOf() const {
403 return (sizeof(methods_offsets_[0]) * methods_offsets_.size());
404}
405
406void OatWriter::OatClasses::UpdateChecksum(OatHeader& oat_header) const {
407 oat_header.UpdateChecksum(&methods_offsets_[0], SizeOf());
408}
409
410bool OatWriter::OatClasses::Write(File* file) const {
411 if (!file->WriteFully(&methods_offsets_[0], SizeOf())) {
412 PLOG(ERROR) << "Failed to methods offsets";
413 return false;
414 }
415 return true;
416}
417
418OatWriter::OatMethods::OatMethods(uint32_t methods_count) {
419 method_offsets_.resize(methods_count);
420}
421
422size_t OatWriter::OatMethods::SizeOf() const {
423 return (sizeof(method_offsets_[0]) * method_offsets_.size());
424}
425
426void OatWriter::OatMethods::UpdateChecksum(OatHeader& oat_header) const {
427 oat_header.UpdateChecksum(&method_offsets_[0], SizeOf());
428}
429
430bool OatWriter::OatMethods::Write(File* file) const {
431 if (!file->WriteFully(&method_offsets_[0], SizeOf())) {
432 PLOG(ERROR) << "Failed to method offsets";
433 return false;
434 }
435 return true;
436}
437
438} // namespace art