Load ArtMethod* from .data.bimg.rel.ro entries.
Introduce a new .data.bimg.rel.ro section in oat files where
we store offsets of boot image objects from the beginning of
the boot image. At runtime we relocate these entries using
the actual boot image address to turn offsets to pointers.
Use the .data.bimg.rel.ro to prepare the boot image methods
used by HInvokeStaticOrDirect for PIC AOT app compilation.
Loading the ArtMethod* from .data.bimg.rel.ro instead of the
.bss avoids the initial call to the resolution trampoline.
Test: Additional test in 522-checker-sharpening
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing --pictest --npictest
Test: Pixel 2 XL boots.
Test: testrunner.py --target --optimizing --pictest --npictest
Bug: 71526895
Change-Id: Ie5f5b1f622704877b36730377146e59092e46c0c
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 0c20556..2e625a7 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -3360,9 +3360,10 @@
CHECK_EQ(dex_cache_location, dex_file_suffix);
const OatFile* oat_file =
(dex_file.GetOatDexFile() != nullptr) ? dex_file.GetOatDexFile()->GetOatFile() : nullptr;
- // Clean up pass to remove null dex caches. Also check if we need to initialize OatFile .bss.
- // Null dex caches can occur due to class unloading and we are lazily removing null entries.
- bool initialize_oat_file_bss = (oat_file != nullptr);
+ // Clean up pass to remove null dex caches; null dex caches can occur due to class unloading
+ // and we are lazily removing null entries. Also check if we need to initialize OatFile data
+ // (.data.bimg.rel.ro and .bss sections) needed for code execution.
+ bool initialize_oat_file_data = (oat_file != nullptr) && oat_file->IsExecutable();
JavaVMExt* const vm = self->GetJniEnv()->GetVm();
for (auto it = dex_caches_.begin(); it != dex_caches_.end(); ) {
DexCacheData data = *it;
@@ -3370,15 +3371,36 @@
vm->DeleteWeakGlobalRef(self, data.weak_root);
it = dex_caches_.erase(it);
} else {
- if (initialize_oat_file_bss &&
+ if (initialize_oat_file_data &&
it->dex_file->GetOatDexFile() != nullptr &&
it->dex_file->GetOatDexFile()->GetOatFile() == oat_file) {
- initialize_oat_file_bss = false; // Already initialized.
+ initialize_oat_file_data = false; // Already initialized.
}
++it;
}
}
- if (initialize_oat_file_bss) {
+ if (initialize_oat_file_data) {
+ // Initialize the .data.bimg.rel.ro section.
+ if (!oat_file->GetBootImageRelocations().empty()) {
+ uint8_t* reloc_begin = const_cast<uint8_t*>(oat_file->DataBimgRelRoBegin());
+ CheckedCall(mprotect,
+ "un-protect boot image relocations",
+ reloc_begin,
+ oat_file->DataBimgRelRoSize(),
+ PROT_READ | PROT_WRITE);
+ uint32_t boot_image_begin = dchecked_integral_cast<uint32_t>(reinterpret_cast<uintptr_t>(
+ Runtime::Current()->GetHeap()->GetBootImageSpaces().front()->Begin()));
+ for (const uint32_t& relocation : oat_file->GetBootImageRelocations()) {
+ const_cast<uint32_t&>(relocation) += boot_image_begin;
+ }
+ CheckedCall(mprotect,
+ "protect boot image relocations",
+ reloc_begin,
+ oat_file->DataBimgRelRoSize(),
+ PROT_READ);
+ }
+
+ // Initialize the .bss section.
// TODO: Pre-initialize from boot/app image?
ArtMethod* resolution_method = Runtime::Current()->GetResolutionMethod();
for (ArtMethod*& entry : oat_file->GetBssMethods()) {
diff --git a/runtime/oat.h b/runtime/oat.h
index 292c9d6..0fa1d4b 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
class PACKED(4) OatHeader {
public:
static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
- // Last oat version changed reason: Math.pow() intrinsic.
- static constexpr uint8_t kOatVersion[] = { '1', '3', '8', '\0' };
+ // Last oat version changed reason: Retrieve ArtMethod* from .data.bimg.rel.ro .
+ static constexpr uint8_t kOatVersion[] = { '1', '3', '9', '\0' };
static constexpr const char* kImageLocationKey = "image-location";
static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 3576683..20297e0 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -343,6 +343,19 @@
// Readjust to be non-inclusive upper bound.
end_ += sizeof(uint32_t);
+ data_bimg_rel_ro_begin_ = FindDynamicSymbolAddress("oatdatabimgrelro", &symbol_error_msg);
+ if (data_bimg_rel_ro_begin_ != nullptr) {
+ data_bimg_rel_ro_end_ =
+ FindDynamicSymbolAddress("oatdatabimgrelrolastword", &symbol_error_msg);
+ if (data_bimg_rel_ro_end_ == nullptr) {
+ *error_msg =
+ StringPrintf("Failed to find oatdatabimgrelrolastword symbol in '%s'", file_path.c_str());
+ return false;
+ }
+ // Readjust to be non-inclusive upper bound.
+ data_bimg_rel_ro_end_ += sizeof(uint32_t);
+ }
+
bss_begin_ = const_cast<uint8_t*>(FindDynamicSymbolAddress("oatbss", &symbol_error_msg));
if (bss_begin_ == nullptr) {
// No .bss section.
@@ -536,6 +549,17 @@
}
const uint8_t* oat = Begin() + oat_dex_files_offset; // Jump to the OatDexFile records.
+ if (!IsAligned<sizeof(uint32_t)>(data_bimg_rel_ro_begin_) ||
+ !IsAligned<sizeof(uint32_t)>(data_bimg_rel_ro_end_) ||
+ data_bimg_rel_ro_begin_ > data_bimg_rel_ro_end_) {
+ *error_msg = StringPrintf("In oat file '%s' found unaligned or unordered databimgrelro "
+ "symbol(s): begin = %p, end = %p",
+ GetLocation().c_str(),
+ data_bimg_rel_ro_begin_,
+ data_bimg_rel_ro_end_);
+ return false;
+ }
+
DCHECK_GE(static_cast<size_t>(pointer_size), alignof(GcRoot<mirror::Object>));
if (!IsAligned<kPageSize>(bss_begin_) ||
!IsAlignedParam(bss_methods_, static_cast<size_t>(pointer_size)) ||
@@ -849,8 +873,29 @@
}
}
+ Runtime* runtime = Runtime::Current();
+
+ if (DataBimgRelRoBegin() != nullptr) {
+ // Make .data.bimg.rel.ro read only. ClassLinker shall make it writable for relocation.
+ uint8_t* reloc_begin = const_cast<uint8_t*>(DataBimgRelRoBegin());
+ CheckedCall(mprotect, "protect relocations", reloc_begin, DataBimgRelRoSize(), PROT_READ);
+ if (UNLIKELY(runtime == nullptr)) {
+ // This must be oatdump without boot image.
+ } else if (!IsExecutable()) {
+ // Do not check whether we have a boot image if the oat file is not executable.
+ } else if (UNLIKELY(runtime->GetHeap()->GetBootImageSpaces().empty())) {
+ *error_msg = StringPrintf("Cannot load oat file '%s' with .data.bimg.rel.ro as executable "
+ "without boot image.",
+ GetLocation().c_str());
+ return false;
+ } else {
+ // ClassLinker shall perform the relocation when we register a dex file from
+ // this oat file. We do not do the relocation here to avoid dirtying the pages
+ // if the code is never actually ready to be executed.
+ }
+ }
+
if (boot_image_tables != nullptr) {
- Runtime* runtime = Runtime::Current();
if (UNLIKELY(runtime == nullptr)) {
// This must be oatdump without boot image. Make sure the .bss is inaccessible.
CheckedCall(mprotect, "protect bss", const_cast<uint8_t*>(BssBegin()), BssSize(), PROT_NONE);
@@ -1513,6 +1558,8 @@
vdex_(nullptr),
begin_(nullptr),
end_(nullptr),
+ data_bimg_rel_ro_begin_(nullptr),
+ data_bimg_rel_ro_end_(nullptr),
bss_begin_(nullptr),
bss_end_(nullptr),
bss_methods_(nullptr),
@@ -1542,22 +1589,6 @@
return end_;
}
-const uint8_t* OatFile::BssBegin() const {
- return bss_begin_;
-}
-
-const uint8_t* OatFile::BssEnd() const {
- 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();
}
@@ -1566,6 +1597,16 @@
return vdex_->End();
}
+ArrayRef<const uint32_t> OatFile::GetBootImageRelocations() const {
+ if (data_bimg_rel_ro_begin_ != nullptr) {
+ const uint32_t* relocations = reinterpret_cast<const uint32_t*>(data_bimg_rel_ro_begin_);
+ const uint32_t* relocations_end = reinterpret_cast<const uint32_t*>(data_bimg_rel_ro_end_);
+ return ArrayRef<const uint32_t>(relocations, relocations_end - relocations);
+ } else {
+ return ArrayRef<const uint32_t>();
+ }
+}
+
ArrayRef<ArtMethod*> OatFile::GetBssMethods() const {
if (bss_methods_ != nullptr) {
ArtMethod** methods = reinterpret_cast<ArtMethod**>(bss_methods_);
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 255a31b..f32874a 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -275,6 +275,10 @@
return p >= Begin() && p < End();
}
+ size_t DataBimgRelRoSize() const {
+ return DataBimgRelRoEnd() - DataBimgRelRoBegin();
+ }
+
size_t BssSize() const {
return BssEnd() - BssBegin();
}
@@ -300,15 +304,19 @@
const uint8_t* Begin() const;
const uint8_t* End() const;
- const uint8_t* BssBegin() const;
- const uint8_t* BssEnd() const;
+ const uint8_t* DataBimgRelRoBegin() const { return data_bimg_rel_ro_begin_; }
+ const uint8_t* DataBimgRelRoEnd() const { return data_bimg_rel_ro_end_; }
- const uint8_t* VdexBegin() const;
- const uint8_t* VdexEnd() const;
+ const uint8_t* BssBegin() const { return bss_begin_; }
+ const uint8_t* BssEnd() const { return bss_end_; }
+
+ const uint8_t* VdexBegin() const { return vdex_begin_; }
+ const uint8_t* VdexEnd() const { return vdex_end_; }
const uint8_t* DexBegin() const;
const uint8_t* DexEnd() const;
+ ArrayRef<const uint32_t> GetBootImageRelocations() const;
ArrayRef<ArtMethod*> GetBssMethods() const;
ArrayRef<GcRoot<mirror::Object>> GetBssGcRoots() const;
@@ -355,6 +363,12 @@
// Pointer to end of oat region for bounds checking.
const uint8_t* end_;
+ // Pointer to the .data.bimg.rel.ro section, if present, otherwise null.
+ const uint8_t* data_bimg_rel_ro_begin_;
+
+ // Pointer to the end of the .data.bimg.rel.ro section, if present, otherwise null.
+ const uint8_t* data_bimg_rel_ro_end_;
+
// Pointer to the .bss section, if present, otherwise null.
uint8_t* bss_begin_;