Try to mmap vdex file within the address range of the ELF file.
Add ELF section for the vdex file and mmap it there at runtime.
This ensures that the data is at predictable location, which
is needed to be able to reference it from native debug-info.
This does not change the amount of memory allocated, or the
location of the data on disk. However, it does change how
the memory is allocated - it replaces two allocations
(ELF and vdex) by just one (ELF which includes the vdex).
Bug: 71579677
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Change-Id: Ie2abd36c8b6617a527368e71f932998bbe5ad38c
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 0023cb9..60bcd63 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -170,6 +170,11 @@
$(TARGET_CORE_IMAGE_interpreter_32) \
patchoatd-target
+ART_GTEST_oat_file_test_HOST_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
+ART_GTEST_oat_file_test_TARGET_DEPS := \
+ $(ART_GTEST_dex2oat_environment_tests_TARGET_DEPS)
+
ART_GTEST_oat_file_assistant_test_HOST_DEPS := \
$(ART_GTEST_dex2oat_environment_tests_HOST_DEPS)
ART_GTEST_oat_file_assistant_test_TARGET_DEPS := \
diff --git a/compiler/linker/elf_builder.h b/compiler/linker/elf_builder.h
index aa3cd98..5262ab6 100644
--- a/compiler/linker/elf_builder.h
+++ b/compiler/linker/elf_builder.h
@@ -38,9 +38,10 @@
// Elf_Ehdr - The ELF header.
// Elf_Phdr[] - Program headers for the linker.
// .note.gnu.build-id - Optional build ID section (SHA-1 digest).
-// .rodata - DEX files and oat metadata.
+// .rodata - Oat metadata.
// .text - Compiled code.
// .bss - Zero-initialized writeable section.
+// .dex - Reserved NOBITS space for dex-related data.
// .MIPS.abiflags - MIPS specific section.
// .dynstr - Names for .dynsym.
// .dynsym - A few oat-specific dynamic symbols.
@@ -503,6 +504,7 @@
rodata_(this, ".rodata", SHT_PROGBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
text_(this, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, nullptr, 0, kPageSize, 0),
bss_(this, ".bss", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
+ dex_(this, ".dex", SHT_NOBITS, SHF_ALLOC, nullptr, 0, kPageSize, 0),
dynstr_(this, ".dynstr", SHF_ALLOC, kPageSize),
dynsym_(this, ".dynsym", SHT_DYNSYM, SHF_ALLOC, &dynstr_),
hash_(this, ".hash", SHT_HASH, SHF_ALLOC, &dynsym_, 0, sizeof(Elf_Word), sizeof(Elf_Word)),
@@ -525,6 +527,7 @@
virtual_address_(0) {
text_.phdr_flags_ = PF_R | PF_X;
bss_.phdr_flags_ = PF_R | PF_W;
+ dex_.phdr_flags_ = PF_R;
dynamic_.phdr_flags_ = PF_R | PF_W;
dynamic_.phdr_type_ = PT_DYNAMIC;
eh_frame_hdr_.phdr_type_ = PT_GNU_EH_FRAME;
@@ -538,6 +541,7 @@
Section* GetRoData() { return &rodata_; }
Section* GetText() { return &text_; }
Section* GetBss() { return &bss_; }
+ Section* GetDex() { return &dex_; }
StringSection* GetStrTab() { return &strtab_; }
SymbolSection* GetSymTab() { return &symtab_; }
Section* GetEhFrame() { return &eh_frame_; }
@@ -666,7 +670,8 @@
Elf_Word text_size,
Elf_Word bss_size,
Elf_Word bss_methods_offset,
- Elf_Word bss_roots_offset) {
+ Elf_Word bss_roots_offset,
+ Elf_Word dex_size) {
std::string soname(elf_file_path);
size_t directory_separator_pos = soname.rfind('/');
if (directory_separator_pos != std::string::npos) {
@@ -679,6 +684,9 @@
if (bss_size != 0) {
bss_.AllocateVirtualMemory(bss_size);
}
+ if (dex_size != 0) {
+ dex_.AllocateVirtualMemory(dex_size);
+ }
if (isa_ == InstructionSet::kMips || isa_ == InstructionSet::kMips64) {
abiflags_.AllocateVirtualMemory(abiflags_.GetSize());
}
@@ -725,6 +733,14 @@
Elf_Word bsslastword_address = bss_.GetAddress() + bss_size - 4;
dynsym_.Add(oatbsslastword, &bss_, bsslastword_address, 4, STB_GLOBAL, STT_OBJECT);
}
+ if (dex_size != 0u) {
+ Elf_Word oatdex = dynstr_.Add("oatdex");
+ dynsym_.Add(oatdex, &dex_, dex_.GetAddress(), dex_size, STB_GLOBAL, STT_OBJECT);
+ Elf_Word oatdexlastword = dynstr_.Add("oatdexlastword");
+ Elf_Word oatdexlastword_address = dex_.GetAddress() + dex_size - 4;
+ dynsym_.Add(oatdexlastword, &dex_, oatdexlastword_address, 4, STB_GLOBAL, STT_OBJECT);
+ }
+
Elf_Word soname_offset = dynstr_.Add(soname);
// We do not really need a hash-table since there is so few entries.
@@ -967,6 +983,7 @@
Section rodata_;
Section text_;
Section bss_;
+ Section dex_;
CachedStringSection dynstr_;
SymbolSection dynsym_;
CachedSection hash_;
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 4248b72..3b05f93 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -2020,8 +2020,8 @@
text_size,
oat_writer->GetBssSize(),
oat_writer->GetBssMethodsOffset(),
- oat_writer->GetBssRootsOffset());
-
+ oat_writer->GetBssRootsOffset(),
+ oat_writer->GetVdexSize());
if (IsImage()) {
// Update oat layout.
DCHECK(image_writer_ != nullptr);
diff --git a/dex2oat/linker/elf_writer.h b/dex2oat/linker/elf_writer.h
index 0eb36ed..a49112b 100644
--- a/dex2oat/linker/elf_writer.h
+++ b/dex2oat/linker/elf_writer.h
@@ -55,11 +55,17 @@
virtual ~ElfWriter() {}
virtual void Start() = 0;
+ // Prepares memory layout of the whole ELF file, and creates dynamic symbols
+ // which point to specific areas of interest (usually section begin and end).
+ // This is needed as multi-image needs to know the memory layout of all ELF
+ // files, before starting to write them.
+ // This method must be called before calling GetLoadedSize().
virtual void PrepareDynamicSection(size_t rodata_size,
size_t text_size,
size_t bss_size,
size_t bss_methods_offset,
- size_t bss_roots_offset) = 0;
+ size_t bss_roots_offset,
+ size_t dex_section_size) = 0;
virtual void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) = 0;
virtual OutputStream* StartRoData() = 0;
virtual void EndRoData(OutputStream* rodata) = 0;
diff --git a/dex2oat/linker/elf_writer_quick.cc b/dex2oat/linker/elf_writer_quick.cc
index aa64b7d..af11d58 100644
--- a/dex2oat/linker/elf_writer_quick.cc
+++ b/dex2oat/linker/elf_writer_quick.cc
@@ -99,7 +99,8 @@
size_t text_size,
size_t bss_size,
size_t bss_methods_offset,
- size_t bss_roots_offset) OVERRIDE;
+ size_t bss_roots_offset,
+ size_t dex_section_size) OVERRIDE;
void PrepareDebugInfo(const ArrayRef<const debug::MethodDebugInfo>& method_infos) OVERRIDE;
OutputStream* StartRoData() OVERRIDE;
void EndRoData(OutputStream* rodata) OVERRIDE;
@@ -183,7 +184,8 @@
size_t text_size,
size_t bss_size,
size_t bss_methods_offset,
- size_t bss_roots_offset) {
+ size_t bss_roots_offset,
+ size_t dex_section_size) {
DCHECK_EQ(rodata_size_, 0u);
rodata_size_ = rodata_size;
DCHECK_EQ(text_size_, 0u);
@@ -195,7 +197,8 @@
text_size_,
bss_size_,
bss_methods_offset,
- bss_roots_offset);
+ bss_roots_offset,
+ dex_section_size);
}
template <typename ElfTypes>
diff --git a/dex2oat/linker/image_test.h b/dex2oat/linker/image_test.h
index 2c24066..092f06c 100644
--- a/dex2oat/linker/image_test.h
+++ b/dex2oat/linker/image_test.h
@@ -318,7 +318,8 @@
text_size,
oat_writer->GetBssSize(),
oat_writer->GetBssMethodsOffset(),
- oat_writer->GetBssRootsOffset());
+ oat_writer->GetBssRootsOffset(),
+ oat_writer->GetVdexSize());
writer->UpdateOatFileLayout(i,
elf_writer->GetLoadedSize(),
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index 3e9f463..dda9153 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -230,6 +230,10 @@
return bss_roots_offset_;
}
+ size_t GetVdexSize() const {
+ return vdex_size_;
+ }
+
size_t GetOatDataOffset() const {
return oat_data_offset_;
}
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index 138306e..7e9ad90 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -219,7 +219,8 @@
text_size,
oat_writer.GetBssSize(),
oat_writer.GetBssMethodsOffset(),
- oat_writer.GetBssRootsOffset());
+ oat_writer.GetBssRootsOffset(),
+ oat_writer.GetVdexSize());
std::unique_ptr<BufferedOutputStream> vdex_out =
std::make_unique<BufferedOutputStream>(std::make_unique<FileOutputStream>(vdex_file));
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 1a1d8cc..d6b2ddd 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -171,7 +171,8 @@
text_size,
oat_file_->BssSize(),
oat_file_->BssMethodsOffset(),
- oat_file_->BssRootsOffset());
+ oat_file_->BssRootsOffset(),
+ oat_file_->VdexSize());
builder_->WriteDynamicSection();
const OatHeader& oat_header = oat_file_->GetOatHeader();
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index c20aa11..73820c1 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -194,10 +194,6 @@
ret->PreLoad();
- if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) {
- return nullptr;
- }
-
if (!ret->Load(elf_filename,
oat_file_begin,
writable,
@@ -211,6 +207,10 @@
return nullptr;
}
+ if (!ret->LoadVdex(vdex_filename, writable, low_4gb, error_msg)) {
+ return nullptr;
+ }
+
ret->PreSetup(elf_filename);
if (!ret->Setup(abs_dex_location, error_msg)) {
@@ -234,10 +234,6 @@
std::string* error_msg) {
std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(oat_location, executable));
- if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) {
- return nullptr;
- }
-
if (!ret->Load(oat_fd,
oat_file_begin,
writable,
@@ -251,6 +247,10 @@
return nullptr;
}
+ if (!ret->LoadVdex(vdex_fd, vdex_location, writable, low_4gb, error_msg)) {
+ return nullptr;
+ }
+
ret->PreSetup(oat_location);
if (!ret->Setup(abs_dex_location, error_msg)) {
@@ -264,7 +264,14 @@
bool writable,
bool low_4gb,
std::string* error_msg) {
- vdex_ = VdexFile::Open(vdex_filename, writable, low_4gb, /* unquicken*/ false, error_msg);
+ vdex_ = VdexFile::OpenAtAddress(vdex_begin_,
+ vdex_end_ - vdex_begin_,
+ true /* mmap_reuse */,
+ vdex_filename,
+ writable,
+ low_4gb,
+ /* unquicken*/ false,
+ error_msg);
if (vdex_.get() == nullptr) {
*error_msg = StringPrintf("Failed to load vdex file '%s' %s",
vdex_filename.c_str(),
@@ -285,13 +292,16 @@
if (rc == -1) {
PLOG(WARNING) << "Failed getting length of vdex file";
} else {
- vdex_ = VdexFile::Open(vdex_fd,
- s.st_size,
- vdex_filename,
- writable,
- low_4gb,
- false /* unquicken */,
- error_msg);
+ vdex_ = VdexFile::OpenAtAddress(vdex_begin_,
+ vdex_end_ - vdex_begin_,
+ true /* mmap_reuse */,
+ vdex_fd,
+ s.st_size,
+ vdex_filename,
+ writable,
+ low_4gb,
+ false /* unquicken */,
+ error_msg);
if (vdex_.get() == nullptr) {
*error_msg = "Failed opening vdex file.";
return false;
@@ -339,7 +349,7 @@
} else {
bss_end_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbsslastword", &symbol_error_msg));
if (bss_end_ == nullptr) {
- *error_msg = StringPrintf("Failed to find oatbasslastword symbol in '%s'", file_path.c_str());
+ *error_msg = StringPrintf("Failed to find oatbsslastword symbol in '%s'", file_path.c_str());
return false;
}
// Readjust to be non-inclusive upper bound.
@@ -351,6 +361,20 @@
bss_roots_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbssroots", &symbol_error_msg));
}
+ vdex_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatdex", &symbol_error_msg));
+ if (vdex_begin_ == nullptr) {
+ // No .vdex section.
+ vdex_end_ = nullptr;
+ } else {
+ vdex_end_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatdexlastword", &symbol_error_msg));
+ if (vdex_end_ == nullptr) {
+ *error_msg = StringPrintf("Failed to find oatdexlastword symbol in '%s'", file_path.c_str());
+ return false;
+ }
+ // Readjust to be non-inclusive upper bound.
+ vdex_end_ += sizeof(uint32_t);
+ }
+
return true;
}
@@ -1449,6 +1473,8 @@
bss_methods_(nullptr),
bss_roots_(nullptr),
is_executable_(is_executable),
+ vdex_begin_(nullptr),
+ vdex_end_(nullptr),
secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) {
CHECK(!location_.empty());
}
@@ -1479,6 +1505,14 @@
return bss_end_;
}
+const uint8_t* OatFile::VdexBegin() const {
+ return vdex_begin_;
+}
+
+const uint8_t* OatFile::VdexEnd() const {
+ return vdex_end_;
+}
+
const uint8_t* OatFile::DexBegin() const {
return vdex_->Begin();
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 61daa95..ba1c120 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -278,6 +278,10 @@
return BssEnd() - BssBegin();
}
+ size_t VdexSize() const {
+ return VdexEnd() - VdexBegin();
+ }
+
size_t BssMethodsOffset() const {
// Note: This is used only for symbolizer and needs to return a valid .bss offset.
return (bss_methods_ != nullptr) ? bss_methods_ - BssBegin() : BssRootsOffset();
@@ -298,6 +302,9 @@
const uint8_t* BssBegin() const;
const uint8_t* BssEnd() const;
+ const uint8_t* VdexBegin() const;
+ const uint8_t* VdexEnd() const;
+
const uint8_t* DexBegin() const;
const uint8_t* DexEnd() const;
@@ -362,6 +369,12 @@
// Was this oat_file loaded executable?
const bool is_executable_;
+ // Pointer to the .vdex section, if present, otherwise null.
+ uint8_t* vdex_begin_;
+
+ // Pointer to the end of the .vdex section, if present, otherwise null.
+ uint8_t* vdex_end_;
+
// Owning storage for the OatDexFile objects.
std::vector<const OatDexFile*> oat_dex_files_storage_;
diff --git a/runtime/oat_file_test.cc b/runtime/oat_file_test.cc
index 7bf0f84..8d86401 100644
--- a/runtime/oat_file_test.cc
+++ b/runtime/oat_file_test.cc
@@ -21,11 +21,13 @@
#include <gtest/gtest.h>
#include "common_runtime_test.h"
+#include "dexopt_test.h"
#include "scoped_thread_state_change-inl.h"
+#include "vdex_file.h"
namespace art {
-class OatFileTest : public CommonRuntimeTest {
+class OatFileTest : public DexoptTest {
};
TEST_F(OatFileTest, ResolveRelativeEncodedDexLocation) {
@@ -62,4 +64,28 @@
"/data/app/foo/base.apk", "o/base.apk"));
}
+TEST_F(OatFileTest, LoadOat) {
+ std::string dex_location = GetScratchDir() + "/LoadOat.jar";
+
+ Copy(GetDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ std::string oat_location;
+ std::string error_msg;
+ ASSERT_TRUE(OatFileAssistant::DexLocationToOatFilename(
+ dex_location, kRuntimeISA, &oat_location, &error_msg)) << error_msg;
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(oat_location.c_str(),
+ oat_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
+ ASSERT_TRUE(odex_file.get() != nullptr);
+
+ // Check that the vdex file was loaded in the reserved space of odex file.
+ EXPECT_EQ(odex_file->GetVdexFile()->Begin(), odex_file->VdexBegin());
+}
+
} // namespace art
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index fe768a1..47c8bd7 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -57,11 +57,14 @@
DCHECK(IsVersionValid());
}
-std::unique_ptr<VdexFile> VdexFile::Open(const std::string& vdex_filename,
- bool writable,
- bool low_4gb,
- bool unquicken,
- std::string* error_msg) {
+std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr,
+ size_t mmap_size,
+ bool mmap_reuse,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ bool unquicken,
+ std::string* error_msg) {
if (!OS::FileExists(vdex_filename.c_str())) {
*error_msg = "File " + vdex_filename + " does not exist.";
return nullptr;
@@ -85,23 +88,47 @@
return nullptr;
}
- return Open(vdex_file->Fd(), vdex_length, vdex_filename, writable, low_4gb, unquicken, error_msg);
+ return OpenAtAddress(mmap_addr,
+ mmap_size,
+ mmap_reuse,
+ vdex_file->Fd(),
+ vdex_length,
+ vdex_filename,
+ writable,
+ low_4gb,
+ unquicken,
+ error_msg);
}
-std::unique_ptr<VdexFile> VdexFile::Open(int file_fd,
- size_t vdex_length,
- const std::string& vdex_filename,
- bool writable,
- bool low_4gb,
- bool unquicken,
- std::string* error_msg) {
- std::unique_ptr<MemMap> mmap(MemMap::MapFile(
+std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr,
+ size_t mmap_size,
+ bool mmap_reuse,
+ int file_fd,
+ size_t vdex_length,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ bool unquicken,
+ std::string* error_msg) {
+ if (low_4gb) {
+ LOG(WARNING) << "Can not mmap vdex in low 4GB"; // TODO: Implement in MemMap.
+ mmap_addr = nullptr;
+ mmap_reuse = false;
+ }
+ if (mmap_addr != nullptr && mmap_size < vdex_length) {
+ LOG(WARNING) << "Insufficient pre-allocated space to mmap vdex.";
+ mmap_addr = nullptr;
+ mmap_reuse = false;
+ }
+ std::unique_ptr<MemMap> mmap(MemMap::MapFileAtAddress(
+ mmap_addr,
vdex_length,
(writable || unquicken) ? PROT_READ | PROT_WRITE : PROT_READ,
unquicken ? MAP_PRIVATE : MAP_SHARED,
file_fd,
0 /* start offset */,
low_4gb,
+ mmap_reuse,
vdex_filename.c_str(),
error_msg));
if (mmap == nullptr) {
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 2d9fcab..f78335d 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -102,11 +102,44 @@
explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
// Returns nullptr if the vdex file cannot be opened or is not valid.
+ // The mmap_* parameters can be left empty (nullptr/0/false) to allocate at random address.
+ static std::unique_ptr<VdexFile> OpenAtAddress(uint8_t* mmap_addr,
+ size_t mmap_size,
+ bool mmap_reuse,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ bool unquicken,
+ std::string* error_msg);
+
+ // Returns nullptr if the vdex file cannot be opened or is not valid.
+ // The mmap_* parameters can be left empty (nullptr/0/false) to allocate at random address.
+ static std::unique_ptr<VdexFile> OpenAtAddress(uint8_t* mmap_addr,
+ size_t mmap_size,
+ bool mmap_reuse,
+ int file_fd,
+ size_t vdex_length,
+ const std::string& vdex_filename,
+ bool writable,
+ bool low_4gb,
+ bool unquicken,
+ std::string* error_msg);
+
+ // Returns nullptr if the vdex file cannot be opened or is not valid.
static std::unique_ptr<VdexFile> Open(const std::string& vdex_filename,
bool writable,
bool low_4gb,
bool unquicken,
- std::string* error_msg);
+ std::string* error_msg) {
+ return OpenAtAddress(nullptr,
+ 0,
+ false,
+ vdex_filename,
+ writable,
+ low_4gb,
+ unquicken,
+ error_msg);
+ }
// Returns nullptr if the vdex file cannot be opened or is not valid.
static std::unique_ptr<VdexFile> Open(int file_fd,
@@ -115,7 +148,18 @@
bool writable,
bool low_4gb,
bool unquicken,
- std::string* error_msg);
+ std::string* error_msg) {
+ return OpenAtAddress(nullptr,
+ 0,
+ false,
+ file_fd,
+ vdex_length,
+ vdex_filename,
+ writable,
+ low_4gb,
+ unquicken,
+ error_msg);
+ }
const uint8_t* Begin() const { return mmap_->Begin(); }
const uint8_t* End() const { return mmap_->End(); }