From fa8429b967fe2260ece572337534c9dda6c50d8a Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 7 Apr 2015 18:34:42 -0700 Subject: ART: Re-add dlopen Re-add an oat-file path that uses dlopen to load oat files. This code-path will be necessary to support libunwind unwinding through properly CFI-annotated oat files, as libunwind consults the linker about loaded ELF files. We avoid the original dlopen issue, namely that the semantics of dlopen disallow loading the same soname twice, by using an extension of bionic that supersedes the specified behavior. Change-Id: I4a7e3cb00d1ea156dad84f76826def2a5967c9ca --- runtime/oat_file.cc | 136 +++++++++++++++++++++++++++++++++++++++++++++++----- runtime/oat_file.h | 8 ++++ 2 files changed, 131 insertions(+), 13 deletions(-) diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index d3c4b49..eddbd8a 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -23,6 +23,11 @@ #include #include +// dlopen_ext support from bionic. +#ifdef HAVE_ANDROID_OS +#include "android/dlext.h" +#endif + #include "base/bit_vector.h" #include "base/stl_util.h" #include "base/unix_file/fd_file.h" @@ -40,6 +45,18 @@ namespace art { +// Whether OatFile::Open will try DlOpen() first. Fallback is our own ELF loader. +static constexpr bool kUseDlopen = false; + +// Whether OatFile::Open will try DlOpen() on the host. On the host we're not linking against +// bionic, so cannot take advantage of the support for changed semantics (loading the same soname +// multiple times). However, if/when we switch the above, we likely want to switch this, too, +// to get test coverage of the code paths. +static constexpr bool kUseDlopenOnHost = false; + +// For debugging, Open will print DlOpen error message if set to true. +static constexpr bool kPrintDlOpenErrorMessage = false; + std::string OatFile::ResolveRelativeEncodedDexLocation( const char* abs_dex_location, const std::string& rel_dex_location) { if (abs_dex_location != nullptr && rel_dex_location[0] != '/') { @@ -89,6 +106,23 @@ OatFile* OatFile::Open(const std::string& filename, CHECK(!filename.empty()) << location; CheckLocation(location); std::unique_ptr ret; + + // Use dlopen only when flagged to do so, and when it's OK to load things executable. + // TODO: Also try when not executable? The issue here could be re-mapping as writable (as + // !executable is a sign that we may want to patch), which may not be allowed for + // various reasons. + if (kUseDlopen && (kIsTargetBuild || kUseDlopenOnHost) && executable) { + // Try to use dlopen. This may fail for various reasons, outlined below. We try dlopen, as + // this will register the oat file with the linker and allows libunwind to find our info. + ret.reset(OpenDlopen(filename, location, requested_base, abs_dex_location, error_msg)); + if (ret.get() != nullptr) { + return ret.release(); + } + if (kPrintDlOpenErrorMessage) { + LOG(ERROR) << "Failed to dlopen: " << *error_msg; + } + } + // If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons: // // On target, dlopen may fail when compiling due to selinux restrictions on installd. @@ -98,8 +132,12 @@ OatFile* OatFile::Open(const std::string& filename, // another generated dex file with the same name. http://b/10614658 // // On host, dlopen is expected to fail when cross compiling, so fall back to OpenElfFile. + // + // + // Another independent reason is the absolute placement of boot.oat. dlopen on the host usually + // does honor the virtual address encoded in the ELF file only for ET_EXEC files, not ET_DYN. std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); - if (file.get() == NULL) { + if (file == nullptr) { *error_msg = StringPrintf("Failed to open oat filename for reading: %s", strerror(errno)); return nullptr; } @@ -126,6 +164,19 @@ OatFile* OatFile::OpenReadable(File* file, const std::string& location, return OpenElfFile(file, location, nullptr, nullptr, false, false, abs_dex_location, error_msg); } +OatFile* OatFile::OpenDlopen(const std::string& elf_filename, + const std::string& location, + uint8_t* requested_base, + const char* abs_dex_location, + std::string* error_msg) { + std::unique_ptr oat_file(new OatFile(location, true)); + bool success = oat_file->Dlopen(elf_filename, requested_base, abs_dex_location, error_msg); + if (!success) { + return nullptr; + } + return oat_file.release(); +} + OatFile* OatFile::OpenElfFile(File* file, const std::string& location, uint8_t* requested_base, @@ -145,19 +196,78 @@ OatFile* OatFile::OpenElfFile(File* file, } OatFile::OatFile(const std::string& location, bool is_executable) - : location_(location), begin_(NULL), end_(NULL), bss_begin_(nullptr), bss_end_(nullptr), - is_executable_(is_executable), dlopen_handle_(NULL), + : location_(location), begin_(nullptr), end_(nullptr), bss_begin_(nullptr), bss_end_(nullptr), + is_executable_(is_executable), dlopen_handle_(nullptr), secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); } OatFile::~OatFile() { STLDeleteElements(&oat_dex_files_storage_); - if (dlopen_handle_ != NULL) { + if (dlopen_handle_ != nullptr) { dlclose(dlopen_handle_); } } +bool OatFile::Dlopen(const std::string& elf_filename, uint8_t* requested_base, + const char* abs_dex_location, std::string* error_msg) { + std::unique_ptr absolute_path(realpath(elf_filename.c_str(), nullptr)); + if (absolute_path == nullptr) { + *error_msg = StringPrintf("Failed to find absolute path for '%s'", elf_filename.c_str()); + return false; + } +#ifdef HAVE_ANDROID_OS + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_FORCE_LOAD; + dlopen_handle_ = android_dlopen_ext(absolute_path.get(), RTLD_NOW, &extinfo); +#else + dlopen_handle_ = dlopen(absolute_path.get(), RTLD_NOW); +#endif + if (dlopen_handle_ == nullptr) { + *error_msg = StringPrintf("Failed to dlopen '%s': %s", elf_filename.c_str(), dlerror()); + return false; + } + begin_ = reinterpret_cast(dlsym(dlopen_handle_, "oatdata")); + if (begin_ == nullptr) { + *error_msg = StringPrintf("Failed to find oatdata symbol in '%s': %s", elf_filename.c_str(), + dlerror()); + return false; + } + if (requested_base != nullptr && begin_ != requested_base) { + PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); + *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " + "oatdata=%p != expected=%p, %s. See process maps in the log.", + begin_, requested_base, elf_filename.c_str()); + return false; + } + end_ = reinterpret_cast(dlsym(dlopen_handle_, "oatlastword")); + if (end_ == nullptr) { + *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s': %s", elf_filename.c_str(), + dlerror()); + return false; + } + // Readjust to be non-inclusive upper bound. + end_ += sizeof(uint32_t); + + bss_begin_ = reinterpret_cast(dlsym(dlopen_handle_, "oatbss")); + if (bss_begin_ == nullptr) { + // No .bss section. Clear dlerror(). + bss_end_ = nullptr; + dlerror(); + } else { + bss_end_ = reinterpret_cast(dlsym(dlopen_handle_, "oatbsslastword")); + if (bss_end_ == nullptr) { + *error_msg = StringPrintf("Failed to find oatbasslastword symbol in '%s'", + elf_filename.c_str()); + return false; + } + // Readjust to be non-inclusive upper bound. + bss_end_ += sizeof(uint32_t); + } + + return Setup(abs_dex_location, error_msg); +} + bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin, bool writable, bool executable, const char* abs_dex_location, @@ -165,7 +275,7 @@ bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file // 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) { + if (elf_file_ == nullptr) { DCHECK(!error_msg->empty()); return false; } @@ -175,11 +285,11 @@ bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file return false; } begin_ = elf_file_->FindDynamicSymbolAddress("oatdata"); - if (begin_ == NULL) { + if (begin_ == nullptr) { *error_msg = StringPrintf("Failed to find oatdata symbol in '%s'", file->GetPath().c_str()); return false; } - if (requested_base != NULL && begin_ != requested_base) { + if (requested_base != nullptr && begin_ != requested_base) { PrintFileToLog("/proc/self/maps", LogSeverity::WARNING); *error_msg = StringPrintf("Failed to find oatdata symbol at expected address: " "oatdata=%p != expected=%p. See process maps in the log.", @@ -187,7 +297,7 @@ bool OatFile::ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file return false; } end_ = elf_file_->FindDynamicSymbolAddress("oatlastword"); - if (end_ == NULL) { + if (end_ == nullptr) { *error_msg = StringPrintf("Failed to find oatlastword symbol in '%s'", file->GetPath().c_str()); return false; } @@ -344,12 +454,12 @@ const OatHeader& OatFile::GetOatHeader() const { } const uint8_t* OatFile::Begin() const { - CHECK(begin_ != NULL); + CHECK(begin_ != nullptr); return begin_; } const uint8_t* OatFile::End() const { - CHECK(end_ != NULL); + CHECK(end_ != nullptr); return end_; } @@ -414,7 +524,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, if (warn_if_not_found) { std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location); std::string checksum(""); - if (dex_location_checksum != NULL) { + if (dex_location_checksum != nullptr) { checksum = StringPrintf("0x%08x", *dex_location_checksum); } LOG(WARNING) << "Failed to find OatDexFile for DexFile " << dex_location @@ -430,7 +540,7 @@ const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, } } - return NULL; + return nullptr; } OatFile::OatDexFile::OatDexFile(const OatFile* oat_file, @@ -584,7 +694,7 @@ const OatFile::OatMethod OatFile::OatClass::GetOatMethod(uint32_t method_index) } void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const { - CHECK(method != NULL); + CHECK(method != nullptr); method->SetEntryPointFromQuickCompiledCode(GetQuickCode()); } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index a5d5ae8..42c60dc 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -263,6 +263,12 @@ class OatFile FINAL { private: static void CheckLocation(const std::string& location); + static OatFile* OpenDlopen(const std::string& elf_filename, + const std::string& location, + uint8_t* requested_base, + const char* abs_dex_location, + std::string* error_msg); + static OatFile* OpenElfFile(File* file, const std::string& location, uint8_t* requested_base, @@ -273,6 +279,8 @@ class OatFile FINAL { std::string* error_msg); explicit OatFile(const std::string& filename, bool executable); + bool Dlopen(const std::string& elf_filename, uint8_t* requested_base, + const char* abs_dex_location, std::string* error_msg); bool ElfFileOpen(File* file, uint8_t* requested_base, uint8_t* oat_file_begin, // Override where the file is loaded to if not null bool writable, bool executable, -- cgit v1.1