Remove OatWriter buffering to memory for ElfWriterQuick
This allows the oat contents to be directly written to the file.
Change-Id: Ibc7ddf57477b152f07784b52f7334be73fd22833
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index da05c49..5eb837b 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -35,20 +35,6 @@
namespace art {
-bool OatWriter::Create(OutputStream& output_stream,
- const std::vector<const DexFile*>& dex_files,
- uint32_t image_file_location_oat_checksum,
- uint32_t image_file_location_oat_begin,
- const std::string& image_file_location,
- const CompilerDriver& driver) {
- OatWriter oat_writer(dex_files,
- image_file_location_oat_checksum,
- image_file_location_oat_begin,
- image_file_location,
- &driver);
- return oat_writer.Write(output_stream);
-}
-
OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
uint32_t image_file_location_oat_checksum,
uint32_t image_file_location_oat_begin,
@@ -89,6 +75,7 @@
offset = InitOatClasses(offset);
offset = InitOatCode(offset);
offset = InitOatCodeDexFiles(offset);
+ size_ = offset;
CHECK_EQ(dex_files_->size(), oat_dex_files_.size());
CHECK(image_file_location.empty() == compiler->IsImage());
@@ -190,7 +177,8 @@
if (compiler_driver_->IsImage()) {
InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
oat_header_->SetInterpreterToInterpreterEntryOffset(offset);
- interpreter_to_interpreter_entry_.reset(compiler_driver_->CreateInterpreterToInterpreterEntry());
+ interpreter_to_interpreter_entry_.reset(
+ compiler_driver_->CreateInterpreterToInterpreterEntry());
offset += interpreter_to_interpreter_entry_->size();
offset = CompiledCode::AlignCode(offset, instruction_set);
@@ -336,7 +324,8 @@
mapping_table_offset = (mapping_table_size == 0) ? 0 : offset;
// Deduplicate mapping tables
- SafeMap<const std::vector<uint32_t>*, uint32_t>::iterator mapping_iter = mapping_table_offsets_.find(&mapping_table);
+ SafeMap<const std::vector<uint32_t>*, uint32_t>::iterator mapping_iter =
+ mapping_table_offsets_.find(&mapping_table);
if (mapping_iter != mapping_table_offsets_.end()) {
mapping_table_offset = mapping_iter->second;
} else {
@@ -350,7 +339,8 @@
vmap_table_offset = (vmap_table_size == 0) ? 0 : offset;
// Deduplicate vmap tables
- SafeMap<const std::vector<uint16_t>*, uint32_t>::iterator vmap_iter = vmap_table_offsets_.find(&vmap_table);
+ SafeMap<const std::vector<uint16_t>*, uint32_t>::iterator vmap_iter =
+ vmap_table_offsets_.find(&vmap_table);
if (vmap_iter != vmap_table_offsets_.end()) {
vmap_table_offset = vmap_iter->second;
} else {
@@ -382,7 +372,8 @@
#endif
// Deduplicate GC maps
- SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter = gc_map_offsets_.find(&gc_map);
+ SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter =
+ gc_map_offsets_.find(&gc_map);
if (gc_map_iter != gc_map_offsets_.end()) {
gc_map_offset = gc_map_iter->second;
} else {
@@ -392,14 +383,14 @@
}
}
- oat_class->method_offsets_[class_def_method_index]
- = OatMethodOffsets(code_offset,
- frame_size_in_bytes,
- core_spill_mask,
- fp_spill_mask,
- mapping_table_offset,
- vmap_table_offset,
- gc_map_offset);
+ oat_class->method_offsets_[class_def_method_index] =
+ OatMethodOffsets(code_offset,
+ frame_size_in_bytes,
+ core_spill_mask,
+ fp_spill_mask,
+ mapping_table_offset,
+ vmap_table_offset,
+ gc_map_offset);
if (compiler_driver_->IsImage()) {
ClassLinker* linker = Runtime::Current()->GetClassLinker();
@@ -428,12 +419,16 @@
}
#define DCHECK_OFFSET() \
- DCHECK_EQ(static_cast<off_t>(offset), out.Seek(0, kSeekCurrent))
+ DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out.Seek(0, kSeekCurrent)) \
+ << "file_offset=" << file_offset << " relative_offset=" << relative_offset
#define DCHECK_OFFSET_() \
- DCHECK_EQ(static_cast<off_t>(offset_), out.Seek(0, kSeekCurrent))
+ DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out.Seek(0, kSeekCurrent)) \
+ << "file_offset=" << file_offset << " offset_=" << offset_
bool OatWriter::Write(OutputStream& out) {
+ const size_t file_offset = out.Seek(0, kSeekCurrent);
+
if (!out.WriteFully(oat_header_, sizeof(*oat_header_))) {
PLOG(ERROR) << "Failed to write oat header to " << out.GetLocation();
return false;
@@ -446,19 +441,19 @@
}
size_oat_header_image_file_location_ += image_file_location_.size();
- if (!WriteTables(out)) {
+ if (!WriteTables(out, file_offset)) {
LOG(ERROR) << "Failed to write oat tables to " << out.GetLocation();
return false;
}
- size_t code_offset = WriteCode(out);
- if (code_offset == 0) {
+ size_t relative_offset = WriteCode(out, file_offset);
+ if (relative_offset == 0) {
LOG(ERROR) << "Failed to write oat code to " << out.GetLocation();
return false;
}
- code_offset = WriteCodeDexFiles(out, code_offset);
- if (code_offset == 0) {
+ relative_offset = WriteCodeDexFiles(out, file_offset, relative_offset);
+ if (relative_offset == 0) {
LOG(ERROR) << "Failed to write oat code for dex files to " << out.GetLocation();
return false;
}
@@ -495,21 +490,25 @@
#undef DO_STAT
LOG(INFO) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \
- CHECK_EQ(size_total, static_cast<uint32_t>(out.Seek(0, kSeekCurrent)));
+ CHECK_EQ(file_offset + size_total, static_cast<uint32_t>(out.Seek(0, kSeekCurrent)));
+ CHECK_EQ(size_, size_total);
}
+ CHECK_EQ(file_offset + size_, static_cast<uint32_t>(out.Seek(0, kSeekCurrent)));
+ CHECK_EQ(size_, relative_offset);
+
return true;
}
-bool OatWriter::WriteTables(OutputStream& out) {
+bool OatWriter::WriteTables(OutputStream& out, const size_t file_offset) {
for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
- if (!oat_dex_files_[i]->Write(this, out)) {
+ if (!oat_dex_files_[i]->Write(this, out, file_offset)) {
PLOG(ERROR) << "Failed to write oat dex information to " << out.GetLocation();
return false;
}
}
for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
- uint32_t expected_offset = oat_dex_files_[i]->dex_file_offset_;
+ uint32_t expected_offset = file_offset + oat_dex_files_[i]->dex_file_offset_;
off_t actual_offset = out.Seek(expected_offset, kSeekSet);
if (static_cast<uint32_t>(actual_offset) != expected_offset) {
const DexFile* dex_file = (*dex_files_)[i];
@@ -519,13 +518,14 @@
}
const DexFile* dex_file = (*dex_files_)[i];
if (!out.WriteFully(&dex_file->GetHeader(), dex_file->GetHeader().file_size_)) {
- PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation() << " to " << out.GetLocation();
+ PLOG(ERROR) << "Failed to write dex file " << dex_file->GetLocation()
+ << " to " << out.GetLocation();
return false;
}
size_dex_file_ += dex_file->GetHeader().file_size_;
}
for (size_t i = 0; i != oat_classes_.size(); ++i) {
- if (!oat_classes_[i]->Write(this, out)) {
+ if (!oat_classes_[i]->Write(this, out, file_offset)) {
PLOG(ERROR) << "Failed to write oat methods information to " << out.GetLocation();
return false;
}
@@ -533,27 +533,29 @@
return true;
}
-size_t OatWriter::WriteCode(OutputStream& out) {
- uint32_t offset = oat_header_->GetExecutableOffset();
+size_t OatWriter::WriteCode(OutputStream& out, const size_t file_offset) {
+ size_t relative_offset = oat_header_->GetExecutableOffset();
off_t new_offset = out.Seek(size_executable_offset_alignment_, kSeekCurrent);
- if (static_cast<uint32_t>(new_offset) != offset) {
+ size_t expected_file_offset = file_offset + relative_offset;
+ if (static_cast<uint32_t>(new_offset) != expected_file_offset) {
PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
- << " Expected: " << offset << " File: " << out.GetLocation();
+ << " Expected: " << expected_file_offset << " File: " << out.GetLocation();
return 0;
}
DCHECK_OFFSET();
if (compiler_driver_->IsImage()) {
InstructionSet instruction_set = compiler_driver_->GetInstructionSet();
- if (!out.WriteFully(&(*interpreter_to_interpreter_entry_)[0], interpreter_to_interpreter_entry_->size())) {
+ if (!out.WriteFully(&(*interpreter_to_interpreter_entry_)[0],
+ interpreter_to_interpreter_entry_->size())) {
PLOG(ERROR) << "Failed to write interpreter to interpreter entry to " << out.GetLocation();
return false;
}
size_interpreter_to_interpreter_entry_ += interpreter_to_interpreter_entry_->size();
- offset += interpreter_to_interpreter_entry_->size();
+ relative_offset += interpreter_to_interpreter_entry_->size();
DCHECK_OFFSET();
- uint32_t aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
- uint32_t alignment_padding = aligned_offset - offset;
+ uint32_t aligned_offset = CompiledCode::AlignCode(relative_offset, instruction_set);
+ uint32_t alignment_padding = aligned_offset - relative_offset;
out.Seek(alignment_padding, kSeekCurrent);
size_stubs_alignment_ += alignment_padding;
if (!out.WriteFully(&(*interpreter_to_quick_entry_)[0], interpreter_to_quick_entry_->size())) {
@@ -561,60 +563,67 @@
return false;
}
size_interpreter_to_quick_entry_ += interpreter_to_quick_entry_->size();
- offset += alignment_padding + interpreter_to_quick_entry_->size();
+ relative_offset += alignment_padding + interpreter_to_quick_entry_->size();
DCHECK_OFFSET();
- aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
- alignment_padding = aligned_offset - offset;
+ aligned_offset = CompiledCode::AlignCode(relative_offset, instruction_set);
+ alignment_padding = aligned_offset - relative_offset;
out.Seek(alignment_padding, kSeekCurrent);
size_stubs_alignment_ += alignment_padding;
- if (!out.WriteFully(&(*portable_resolution_trampoline_)[0], portable_resolution_trampoline_->size())) {
+ if (!out.WriteFully(&(*portable_resolution_trampoline_)[0],
+ portable_resolution_trampoline_->size())) {
PLOG(ERROR) << "Failed to write portable resolution trampoline to " << out.GetLocation();
return false;
}
size_portable_resolution_trampoline_ += portable_resolution_trampoline_->size();
- offset += alignment_padding + portable_resolution_trampoline_->size();
+ relative_offset += alignment_padding + portable_resolution_trampoline_->size();
DCHECK_OFFSET();
- aligned_offset = CompiledCode::AlignCode(offset, instruction_set);
- alignment_padding = aligned_offset - offset;
+ aligned_offset = CompiledCode::AlignCode(relative_offset, instruction_set);
+ alignment_padding = aligned_offset - relative_offset;
out.Seek(alignment_padding, kSeekCurrent);
size_stubs_alignment_ += alignment_padding;
- if (!out.WriteFully(&(*quick_resolution_trampoline_)[0], quick_resolution_trampoline_->size())) {
+ if (!out.WriteFully(&(*quick_resolution_trampoline_)[0],
+ quick_resolution_trampoline_->size())) {
PLOG(ERROR) << "Failed to write quick resolution trampoline to " << out.GetLocation();
return false;
}
size_quick_resolution_trampoline_ += quick_resolution_trampoline_->size();
- offset += alignment_padding + quick_resolution_trampoline_->size();
+ relative_offset += alignment_padding + quick_resolution_trampoline_->size();
DCHECK_OFFSET();
}
- return offset;
+ return relative_offset;
}
-size_t OatWriter::WriteCodeDexFiles(OutputStream& out, size_t code_offset) {
+size_t OatWriter::WriteCodeDexFiles(OutputStream& out,
+ const size_t file_offset,
+ size_t relative_offset) {
size_t oat_class_index = 0;
for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
const DexFile* dex_file = (*dex_files_)[i];
CHECK(dex_file != NULL);
- code_offset = WriteCodeDexFile(out, code_offset, oat_class_index, *dex_file);
- if (code_offset == 0) {
+ relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, oat_class_index,
+ *dex_file);
+ if (relative_offset == 0) {
return 0;
}
}
- return code_offset;
+ return relative_offset;
}
-size_t OatWriter::WriteCodeDexFile(OutputStream& out, size_t code_offset, size_t& oat_class_index,
+size_t OatWriter::WriteCodeDexFile(OutputStream& out, const size_t file_offset,
+ size_t relative_offset, size_t& oat_class_index,
const DexFile& dex_file) {
for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs();
class_def_index++, oat_class_index++) {
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
- code_offset = WriteCodeClassDef(out, code_offset, oat_class_index, dex_file, class_def);
- if (code_offset == 0) {
+ relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, oat_class_index,
+ dex_file, class_def);
+ if (relative_offset == 0) {
return 0;
}
}
- return code_offset;
+ return relative_offset;
}
void OatWriter::ReportWriteFailure(const char* what, uint32_t method_idx,
@@ -624,13 +633,15 @@
}
size_t OatWriter::WriteCodeClassDef(OutputStream& out,
- size_t code_offset, size_t oat_class_index,
+ const size_t file_offset,
+ size_t relative_offset,
+ size_t oat_class_index,
const DexFile& dex_file,
const DexFile::ClassDef& class_def) {
const byte* class_data = dex_file.GetClassData(class_def);
if (class_data == NULL) {
// ie. an empty class such as a marker interface
- return code_offset;
+ return relative_offset;
}
ClassDataItemIterator it(dex_file, class_data);
// Skip fields
@@ -644,27 +655,29 @@
size_t class_def_method_index = 0;
while (it.HasNextDirectMethod()) {
bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0;
- code_offset = WriteCodeMethod(out, code_offset, oat_class_index, class_def_method_index,
- is_static, it.GetMemberIndex(), dex_file);
- if (code_offset == 0) {
+ relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
+ class_def_method_index, is_static, it.GetMemberIndex(),
+ dex_file);
+ if (relative_offset == 0) {
return 0;
}
class_def_method_index++;
it.Next();
}
while (it.HasNextVirtualMethod()) {
- code_offset = WriteCodeMethod(out, code_offset, oat_class_index, class_def_method_index,
- false, it.GetMemberIndex(), dex_file);
- if (code_offset == 0) {
+ relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
+ class_def_method_index, false, it.GetMemberIndex(), dex_file);
+ if (relative_offset == 0) {
return 0;
}
class_def_method_index++;
it.Next();
}
- return code_offset;
+ return relative_offset;
}
-size_t OatWriter::WriteCodeMethod(OutputStream& out, size_t offset, size_t oat_class_index,
+size_t OatWriter::WriteCodeMethod(OutputStream& out, const size_t file_offset,
+ size_t relative_offset, size_t oat_class_index,
size_t class_def_method_index, bool is_static,
uint32_t method_idx, const DexFile& dex_file) {
const CompiledMethod* compiled_method =
@@ -676,26 +689,27 @@
if (compiled_method != NULL) { // ie. not an abstract method
#if !defined(ART_USE_PORTABLE_COMPILER)
- uint32_t aligned_offset = compiled_method->AlignCode(offset);
- uint32_t aligned_code_delta = aligned_offset - offset;
+ uint32_t aligned_offset = compiled_method->AlignCode(relative_offset);
+ uint32_t aligned_code_delta = aligned_offset - relative_offset;
if (aligned_code_delta != 0) {
off_t new_offset = out.Seek(aligned_code_delta, kSeekCurrent);
size_code_alignment_ += aligned_code_delta;
- if (static_cast<uint32_t>(new_offset) != aligned_offset) {
+ uint32_t expected_offset = file_offset + aligned_offset;
+ if (static_cast<uint32_t>(new_offset) != expected_offset) {
PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset
- << " Expected: " << aligned_offset << " File: " << out.GetLocation();
+ << " Expected: " << expected_offset << " File: " << out.GetLocation();
return 0;
}
- offset += aligned_code_delta;
+ relative_offset += aligned_code_delta;
DCHECK_OFFSET();
}
- DCHECK_ALIGNED(offset, kArmAlignment);
+ DCHECK_ALIGNED(relative_offset, kArmAlignment);
const std::vector<uint8_t>& code = compiled_method->GetCode();
uint32_t code_size = code.size() * sizeof(code[0]);
CHECK_NE(code_size, 0U);
// Deduplicate code arrays
- size_t code_offset = offset + sizeof(code_size) + compiled_method->CodeDelta();
+ size_t code_offset = relative_offset + sizeof(code_size) + compiled_method->CodeDelta();
SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter = code_offsets_.find(&code);
if (code_iter != code_offsets_.end() && code_offset != method_offsets.code_offset_) {
DCHECK(code_iter->second == method_offsets.code_offset_)
@@ -707,14 +721,14 @@
return 0;
}
size_code_size_ += sizeof(code_size);
- offset += sizeof(code_size);
+ relative_offset += sizeof(code_size);
DCHECK_OFFSET();
if (!out.WriteFully(&code[0], code_size)) {
ReportWriteFailure("method code", method_idx, dex_file, out);
return 0;
}
size_code_ += code_size;
- offset += code_size;
+ relative_offset += code_size;
}
DCHECK_OFFSET();
#endif
@@ -726,20 +740,20 @@
SafeMap<const std::vector<uint32_t>*, uint32_t>::iterator mapping_iter =
mapping_table_offsets_.find(&mapping_table);
if (mapping_iter != mapping_table_offsets_.end() &&
- offset != method_offsets.mapping_table_offset_) {
+ relative_offset != method_offsets.mapping_table_offset_) {
DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0)
|| mapping_iter->second == method_offsets.mapping_table_offset_)
<< PrettyMethod(method_idx, dex_file);
} else {
DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0)
- || offset == method_offsets.mapping_table_offset_)
+ || relative_offset == method_offsets.mapping_table_offset_)
<< PrettyMethod(method_idx, dex_file);
if (!out.WriteFully(&mapping_table[0], mapping_table_size)) {
ReportWriteFailure("mapping table", method_idx, dex_file, out);
return 0;
}
size_mapping_table_ += mapping_table_size;
- offset += mapping_table_size;
+ relative_offset += mapping_table_size;
}
DCHECK_OFFSET();
@@ -750,20 +764,20 @@
SafeMap<const std::vector<uint16_t>*, uint32_t>::iterator vmap_iter =
vmap_table_offsets_.find(&vmap_table);
if (vmap_iter != vmap_table_offsets_.end() &&
- offset != method_offsets.vmap_table_offset_) {
+ relative_offset != method_offsets.vmap_table_offset_) {
DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0)
|| vmap_iter->second == method_offsets.vmap_table_offset_)
<< PrettyMethod(method_idx, dex_file);
} else {
DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0)
- || offset == method_offsets.vmap_table_offset_)
+ || relative_offset == method_offsets.vmap_table_offset_)
<< PrettyMethod(method_idx, dex_file);
if (!out.WriteFully(&vmap_table[0], vmap_table_size)) {
ReportWriteFailure("vmap table", method_idx, dex_file, out);
return 0;
}
size_vmap_table_ += vmap_table_size;
- offset += vmap_table_size;
+ relative_offset += vmap_table_size;
}
DCHECK_OFFSET();
@@ -774,25 +788,25 @@
SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter =
gc_map_offsets_.find(&gc_map);
if (gc_map_iter != gc_map_offsets_.end() &&
- offset != method_offsets.gc_map_offset_) {
+ relative_offset != method_offsets.gc_map_offset_) {
DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0)
|| gc_map_iter->second == method_offsets.gc_map_offset_)
<< PrettyMethod(method_idx, dex_file);
} else {
DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0)
- || offset == method_offsets.gc_map_offset_)
+ || relative_offset == method_offsets.gc_map_offset_)
<< PrettyMethod(method_idx, dex_file);
if (!out.WriteFully(&gc_map[0], gc_map_size)) {
ReportWriteFailure("GC map", method_idx, dex_file, out);
return 0;
}
size_gc_map_ += gc_map_size;
- offset += gc_map_size;
+ relative_offset += gc_map_size;
}
DCHECK_OFFSET();
}
- return offset;
+ return relative_offset;
}
OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) {
@@ -822,7 +836,9 @@
sizeof(methods_offsets_[0]) * methods_offsets_.size());
}
-bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, OutputStream& out) const {
+bool OatWriter::OatDexFile::Write(OatWriter* oat_writer,
+ OutputStream& out,
+ const size_t file_offset) const {
DCHECK_OFFSET_();
if (!out.WriteFully(&dex_file_location_size_, sizeof(dex_file_location_size_))) {
PLOG(ERROR) << "Failed to write dex file location length to " << out.GetLocation();
@@ -881,14 +897,16 @@
sizeof(method_offsets_[0]) * method_offsets_.size());
}
-bool OatWriter::OatClass::Write(OatWriter* oat_writer, OutputStream& out) const {
+bool OatWriter::OatClass::Write(OatWriter* oat_writer,
+ OutputStream& out,
+ const size_t file_offset) const {
DCHECK_OFFSET_();
if (!out.WriteFully(&status_, sizeof(status_))) {
PLOG(ERROR) << "Failed to write class status to " << out.GetLocation();
return false;
}
oat_writer->size_oat_class_status_ += sizeof(status_);
- DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(0)),
+ DCHECK_EQ(static_cast<off_t>(file_offset + GetOatMethodOffsetsOffsetFromOatHeader(0)),
out.Seek(0, kSeekCurrent));
if (!out.WriteFully(&method_offsets_[0],
sizeof(method_offsets_[0]) * method_offsets_.size())) {
@@ -896,7 +914,8 @@
return false;
}
oat_writer->size_oat_class_method_offsets_ += sizeof(method_offsets_[0]) * method_offsets_.size();
- DCHECK_EQ(static_cast<off_t>(GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())),
+ DCHECK_EQ(static_cast<off_t>(file_offset +
+ GetOatMethodOffsetsOffsetFromOatHeader(method_offsets_.size())),
out.Seek(0, kSeekCurrent));
return true;
}