ART: Add support for patching and loading OAT files compiled with PIC
* Images (.art) compiled with pic now have a new field added.
* isDexOptNeeded will now skip patch-ing for apps compiled PIC
* First-boot patching now only copies boot.art, boot.oat is linked
As a result, all system preopted dex files (with --compile-pic) no
longer take up any space in /data/dalvik-cache/<isa>.
(cherry-picked from AOSP master
46774767fcf7780d1455e755729198648d08742e)
Conflicts (from aosp master):
compiler/image_test.cc
compiler/image_writer.cc
compiler/image_writer.h
compiler/oat_test.cc
dex2oat/dex2oat.cc
oatdump/oatdump.cc
runtime/elf_file.cc
runtime/elf_file.h
runtime/elf_file_impl.h
runtime/oat_file.cc
runtime/oat_file.h
Bug: 18035729
Change-Id: Ie1acad81a0fd8b2f24e1f3f07a06e6fdb548be62
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 079d958..c3c7369 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -965,7 +965,7 @@
uint32_t dex_location_checksum,
const char* oat_location,
std::string* error_msg) {
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
error_msg));
if (oat_file.get() == nullptr) {
@@ -1037,7 +1037,7 @@
error_msgs->push_back(error_msg);
return nullptr;
}
- std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr,
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
&error_msg));
if (oat_file.get() == nullptr) {
@@ -1291,6 +1291,7 @@
// There is a high probability that both these oat files map similar/the same address
// spaces so we must scope them like this so they each gets its turn.
std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
+ nullptr,
executable, &odex_error_msg));
if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
&odex_checksum_verified,
@@ -1314,6 +1315,7 @@
bool cache_checksum_verified = false;
if (have_dalvik_cache) {
std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
+ nullptr,
executable, &cache_error_msg));
if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
&cache_checksum_verified,
@@ -1391,7 +1393,7 @@
InstructionSet isa,
std::string* error_msg) {
// We open it non-executable
- std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, false, error_msg));
+ std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
if (output.get() == nullptr) {
return nullptr;
}
@@ -1448,7 +1450,7 @@
LOG(INFO) << "Relocate Oat File: " << command_line;
bool success = Exec(argv, error_msg);
if (success) {
- std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr,
+ std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
!runtime->IsCompiler(), error_msg));
bool checksum_verified = false;
if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
@@ -1549,7 +1551,7 @@
return oat_file;
}
- return OatFile::Open(oat_location, oat_location, nullptr, !Runtime::Current()->IsCompiler(),
+ return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(),
error_msg);
}
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 0ab6394..6ec0712 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -106,7 +106,7 @@
delete entry;
}
-ElfFile::ElfFile(File* file, bool writable, bool program_header_only)
+ElfFile::ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base)
: file_(file),
writable_(writable),
program_header_only_(program_header_only),
@@ -124,13 +124,15 @@
symtab_symbol_table_(nullptr),
dynsym_symbol_table_(nullptr),
jit_elf_image_(nullptr),
- jit_gdb_entry_(nullptr) {
+ jit_gdb_entry_(nullptr),
+ requested_base_(requested_base) {
CHECK(file != nullptr);
}
ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only,
- std::string* error_msg) {
- std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only));
+ std::string* error_msg, uint8_t* requested_base) {
+ std::unique_ptr<ElfFile> elf_file(new ElfFile(file, writable, program_header_only,
+ requested_base));
int prot;
int flags;
if (writable) {
@@ -147,7 +149,8 @@
}
ElfFile* ElfFile::Open(File* file, int prot, int flags, std::string* error_msg) {
- std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false));
+ std::unique_ptr<ElfFile> elf_file(new ElfFile(file, (prot & PROT_WRITE) == PROT_WRITE, false,
+ /*requested_base*/nullptr));
if (!elf_file->Setup(prot, flags, error_msg)) {
return nullptr;
}
@@ -757,6 +760,8 @@
}
const Elf32_Sym* sym = FindDynamicSymbol(symbol_name);
if (sym != nullptr) {
+ // TODO: we need to change this to calculate base_address_ in ::Open,
+ // otherwise it will be wrongly 0 if ::Load has not yet been called.
return base_address_ + sym->st_value;
} else {
return nullptr;
@@ -1094,12 +1099,16 @@
}
size_t file_length = static_cast<size_t>(temp_file_length);
if (!reserved) {
- byte* reserve_base = ((program_header->p_vaddr != 0) ?
- reinterpret_cast<byte*>(program_header->p_vaddr) : nullptr);
+ uint8_t* reserve_base = reinterpret_cast<uint8_t*>(program_header->p_vaddr);
+ uint8_t* reserve_base_override = reserve_base;
+ // Override the base (e.g. when compiling with --compile-pic)
+ if (requested_base_ != nullptr) {
+ reserve_base_override = requested_base_;
+ }
std::string reservation_name("ElfFile reservation for ");
reservation_name += file_->GetPath();
std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
- reserve_base,
+ reserve_base_override,
GetLoadedSize(), PROT_NONE, false,
error_msg));
if (reserve.get() == nullptr) {
@@ -1108,9 +1117,15 @@
return false;
}
reserved = true;
- if (reserve_base == nullptr) {
- base_address_ = reserve->Begin();
- }
+
+ // Base address is the difference of actual mapped location and the p_vaddr
+ base_address_ = reinterpret_cast<uint8_t*>(reinterpret_cast<uintptr_t>(reserve->Begin())
+ - reinterpret_cast<uintptr_t>(reserve_base));
+ // By adding the p_vaddr of a section/symbol to base_address_ we will always get the
+ // dynamic memory address of where that object is actually mapped
+ //
+ // TODO: base_address_ needs to be calculated in ::Open, otherwise
+ // FindDynamicSymbolAddress returns the wrong values until Load is called.
segments_.push_back(reserve.release());
}
// empty segment, nothing to map
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 985be76..9c0bc9a 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -40,7 +40,8 @@
// ELFObjectFile.
class ElfFile {
public:
- static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg);
+ static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+ uint8_t* requested_base = nullptr); // TODO: move arg to before error_msg.
// Open with specific mmap flags, Always maps in the whole file, not just the
// program header sections.
static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
@@ -52,10 +53,12 @@
return *file_;
}
+ // The start of the memory map address range for this ELF file.
byte* Begin() const {
return map_->Begin();
}
+ // The end of the memory map address range for this ELF file.
byte* End() const {
return map_->End();
}
@@ -109,7 +112,7 @@
bool Load(bool executable, std::string* error_msg);
private:
- ElfFile(File* file, bool writable, bool program_header_only);
+ ElfFile(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
bool Setup(int prot, int flags, std::string* error_msg);
@@ -200,6 +203,9 @@
JITCodeEntry* jit_gdb_entry_;
std::unique_ptr<ElfFile> gdb_file_mapping_;
void GdbJITSupport();
+
+ // When not-null, override the base vaddr we memory map LOAD segments into.
+ uint8_t* requested_base_;
};
} // namespace art
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index cf54991..e691518 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -668,7 +668,10 @@
const ImageHeader& image_header = GetImageHeader();
std::string oat_filename = ImageHeader::GetOatLocationFromImageLocation(image_path);
+ CHECK(image_header.GetOatDataBegin() != nullptr);
+
OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, image_header.GetOatDataBegin(),
+ image_header.GetOatFileBegin(),
!Runtime::Current()->IsCompiler(), error_msg);
if (oat_file == NULL) {
*error_msg = StringPrintf("Failed to open oat file '%s' referenced from image %s: %s",
@@ -684,7 +687,7 @@
}
int32_t image_patch_delta = image_header.GetPatchDelta();
int32_t oat_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
- if (oat_patch_delta != image_patch_delta) {
+ if (oat_patch_delta != image_patch_delta && !image_header.CompilePic()) {
// We should have already relocated by this point. Bail out.
*error_msg = StringPrintf("Failed to match oat file patch delta %d to expected patch delta %d "
"in image %s", oat_patch_delta, image_patch_delta, GetName());
diff --git a/runtime/image.cc b/runtime/image.cc
index f451df9..2a69128 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -35,7 +35,8 @@
uint32_t oat_file_begin,
uint32_t oat_data_begin,
uint32_t oat_data_end,
- uint32_t oat_file_end)
+ uint32_t oat_file_end,
+ bool compile_pic)
: image_begin_(image_begin),
image_size_(image_size),
image_bitmap_offset_(image_bitmap_offset),
@@ -46,7 +47,8 @@
oat_data_end_(oat_data_end),
oat_file_end_(oat_file_end),
patch_delta_(0),
- image_roots_(image_roots) {
+ image_roots_(image_roots),
+ compile_pic_(compile_pic) {
CHECK_EQ(image_begin, RoundUp(image_begin, kPageSize));
CHECK_EQ(oat_file_begin, RoundUp(oat_file_begin, kPageSize));
CHECK_EQ(oat_data_begin, RoundUp(oat_data_begin, kPageSize));
diff --git a/runtime/image.h b/runtime/image.h
index e7f5552..a77aec4 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -28,7 +28,7 @@
// header of image files written by ImageWriter, read and validated by Space.
class PACKED(4) ImageHeader {
public:
- ImageHeader() {}
+ ImageHeader() : compile_pic_(0) {}
ImageHeader(uint32_t image_begin,
uint32_t image_size_,
@@ -39,7 +39,8 @@
uint32_t oat_file_begin,
uint32_t oat_data_begin,
uint32_t oat_data_end,
- uint32_t oat_file_end);
+ uint32_t oat_file_end,
+ bool compile_pic_);
bool IsValid() const;
const char* GetMagic() const;
@@ -121,6 +122,10 @@
void RelocateImage(off_t delta);
+ bool CompilePic() const {
+ return compile_pic_ != 0;
+ }
+
private:
static const byte kImageMagic[4];
static const byte kImageVersion[4];
@@ -162,6 +167,9 @@
// Absolute address of an Object[] of objects needed to reinitialize from an image.
uint32_t image_roots_;
+ // Boolean (0 or 1) to denote if the image was compiled with --compile-pic option
+ const uint32_t compile_pic_;
+
friend class ImageWriter;
};
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 01c8978..2bb59e2 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -287,9 +287,11 @@
template <const bool kVerboseLogging, const bool kReasonLogging>
static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* filename,
- InstructionSet target_instruction_set) {
+ InstructionSet target_instruction_set,
+ bool* oat_is_pic) {
std::string error_msg;
std::unique_ptr<const OatFile> oat_file(OatFile::Open(oat_filename, oat_filename, nullptr,
+ nullptr,
false, &error_msg));
if (oat_file.get() == nullptr) {
if (kReasonLogging) {
@@ -299,6 +301,11 @@
error_msg.clear();
return kDexoptNeeded;
}
+
+ // Pass-up the information about if this is PIC.
+ // TODO: Refactor this function to be less complicated.
+ *oat_is_pic = oat_file->IsPic();
+
bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate();
uint32_t location_checksum = 0;
const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr,
@@ -526,10 +533,16 @@
// Lets try the cache first (since we want to load from there since thats where the relocated
// versions will be).
if (have_cache_filename && !force_system_only) {
+ bool oat_is_pic;
// We can use the dalvik-cache if we find a good file.
dalvik_cache_decision =
IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(cache_filename, filename,
- target_instruction_set);
+ target_instruction_set, &oat_is_pic);
+
+ // Apps that are compiled with --compile-pic never need to be patchoat-d
+ if (oat_is_pic && dalvik_cache_decision == kPatchoatNeeded) {
+ dalvik_cache_decision = kUpToDate;
+ }
// We will only return DexOptNeeded if both the cache and system return it.
if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) {
CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible))
@@ -539,12 +552,18 @@
// We couldn't find one thats easy. We should now try the system.
}
+ bool oat_is_pic;
jbyte system_decision =
IsDexOptNeededForFile<kVerboseLogging, kReasonLogging>(odex_filename, filename,
- target_instruction_set);
+ target_instruction_set, &oat_is_pic);
CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible))
<< "May not return PatchoatNeeded when patching is disabled.";
+ // Apps that are compiled with --compile-pic never need to be patchoat-d
+ if (oat_is_pic && system_decision == kPatchoatNeeded) {
+ system_decision = kUpToDate;
+ }
+
if (require_system_version && system_decision == kPatchoatNeeded
&& dalvik_cache_decision == kUpToDate) {
// We have a version from system relocated to the cache. Return it.
diff --git a/runtime/oat.cc b/runtime/oat.cc
index ede108c..9d11de8 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -470,6 +470,12 @@
return sizeof(OatHeader) + key_value_store_size_;
}
+bool OatHeader::IsPic() const {
+ const char* pic_string = GetStoreValueByKey(OatHeader::kPicKey);
+ static const char kTrue[] = "true";
+ return (pic_string != nullptr && strncmp(pic_string, kTrue, sizeof(kTrue)) == 0);
+}
+
void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
char* data_ptr = reinterpret_cast<char*>(&key_value_store_);
if (key_value_store != nullptr) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 139f1cc..a5cb4bc 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -104,6 +104,7 @@
bool GetStoreKeyValuePairByIndex(size_t index, const char** key, const char** value) const;
size_t GetHeaderSize() const;
+ bool IsPic() const;
private:
OatHeader(InstructionSet instruction_set,
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index be50199..9ba860c 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -67,6 +67,7 @@
OatFile* OatFile::Open(const std::string& filename,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool executable,
std::string* error_msg) {
CHECK(!filename.empty()) << location;
@@ -91,7 +92,8 @@
*error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno));
return nullptr;
}
- ret.reset(OpenElfFile(file.get(), location, requested_base, false, executable, error_msg));
+ ret.reset(OpenElfFile(file.get(), location, requested_base, oat_file_begin, false, executable,
+ error_msg));
// It would be nice to unlink here. But we might have opened the file created by the
// ScopedLock, which we better not delete to avoid races. TODO: Investigate how to fix the API
@@ -102,12 +104,12 @@
OatFile* OatFile::OpenWritable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, true, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, true, false, error_msg);
}
OatFile* OatFile::OpenReadable(File* file, const std::string& location, std::string* error_msg) {
CheckLocation(location);
- return OpenElfFile(file, location, NULL, false, false, error_msg);
+ return OpenElfFile(file, location, nullptr, nullptr, false, false, error_msg);
}
OatFile* OatFile::OpenDlopen(const std::string& elf_filename,
@@ -125,11 +127,13 @@
OatFile* OatFile::OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool writable,
bool executable,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(new OatFile(location, executable));
- bool success = oat_file->ElfFileOpen(file, requested_base, writable, executable, error_msg);
+ bool success = oat_file->ElfFileOpen(file, requested_base, oat_file_begin, writable, executable,
+ error_msg);
if (!success) {
CHECK(!error_msg->empty());
return nullptr;
@@ -188,9 +192,12 @@
return Setup(error_msg);
}
-bool OatFile::ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+bool OatFile::ElfFileOpen(File* file, byte* requested_base, uint8_t* oat_file_begin,
+ bool writable, bool executable,
std::string* error_msg) {
- elf_file_.reset(ElfFile::Open(file, writable, true, error_msg));
+ // TODO: rename requested_base to oat_data_begin
+ elf_file_.reset(ElfFile::Open(file, writable, /*program_header_only*/true, error_msg,
+ oat_file_begin));
if (elf_file_.get() == nullptr) {
DCHECK(!error_msg->empty());
return false;
@@ -600,8 +607,7 @@
}
bool OatFile::IsPic() const {
- const char* pic_string = GetOatHeader().GetStoreValueByKey(OatHeader::kPicKey);
- return (pic_string != nullptr && strncmp(pic_string, "true", 5) == 0);
+ return GetOatHeader().IsPic();
// TODO: Check against oat_patches. b/18144996
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index a4c8da9..488988e 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -49,6 +49,7 @@
static OatFile* Open(const std::string& filename,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin,
bool executable,
std::string* error_msg);
@@ -305,13 +306,16 @@
static OatFile* OpenElfFile(File* file,
const std::string& location,
byte* requested_base,
+ uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
std::string* error_msg);
explicit OatFile(const std::string& filename, bool executable);
bool Dlopen(const std::string& elf_filename, byte* requested_base, std::string* error_msg);
- bool ElfFileOpen(File* file, byte* requested_base, bool writable, bool executable,
+ bool ElfFileOpen(File* file, byte* requested_base,
+ uint8_t* oat_file_begin, // Override where the file is loaded to if not null
+ bool writable, bool executable,
std::string* error_msg);
bool Setup(std::string* error_msg);