Add ANDROID_DLEXT_FORCE_LOAD flag
This flag allows to force loading of the library
in the case when for some reason multiple ELF files
share the same filename (because the already-loaded
library has been removed and overwritten, for example).
Change-Id: I798d44409ee13d63eaa75d685e99c4d028d2b0c1
diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h
index 90daf30..f10a8a2 100644
--- a/libc/include/android/dlext.h
+++ b/libc/include/android/dlext.h
@@ -59,16 +59,28 @@
/* If opening a library using library_fd read it starting at library_fd_offset.
* This flag is only valid when ANDROID_DLEXT_USE_LIBRARY_FD is set.
*/
-
ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET = 0x20,
+ /* When set, do not check if the library has already been loaded by file stat(2)s.
+ *
+ * This flag allows forced loading of the library in the case when for some
+ * reason multiple ELF files share the same filename (because the already-loaded
+ * library has been removed and overwritten, for example).
+ *
+ * Note that if the library has the same dt_soname as an old one and some other
+ * library has the soname in DT_NEEDED list, the first one will be used to resolve any
+ * dependencies.
+ */
+ ANDROID_DLEXT_FORCE_LOAD = 0x40,
+
/* Mask of valid bits */
ANDROID_DLEXT_VALID_FLAG_BITS = ANDROID_DLEXT_RESERVED_ADDRESS |
ANDROID_DLEXT_RESERVED_ADDRESS_HINT |
ANDROID_DLEXT_WRITE_RELRO |
ANDROID_DLEXT_USE_RELRO |
ANDROID_DLEXT_USE_LIBRARY_FD |
- ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET,
+ ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET |
+ ANDROID_DLEXT_FORCE_LOAD,
};
typedef struct {
diff --git a/linker/linker.cpp b/linker/linker.cpp
index ebf125e..8703e4f 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1014,16 +1014,18 @@
}
// Check for symlink and other situations where
- // file can have different names.
- for (soinfo* si = solist; si != nullptr; si = si->next) {
- if (si->get_st_dev() != 0 &&
- si->get_st_ino() != 0 &&
- si->get_st_dev() == file_stat.st_dev &&
- si->get_st_ino() == file_stat.st_ino &&
- si->get_file_offset() == file_offset) {
- TRACE("library \"%s\" is already loaded under different name/path \"%s\" - "
- "will return existing soinfo", name, si->name);
- return si;
+ // file can have different names, unless ANDROID_DLEXT_FORCE_LOAD is set
+ if (extinfo == nullptr || (extinfo->flags & ANDROID_DLEXT_FORCE_LOAD) == 0) {
+ for (soinfo* si = solist; si != nullptr; si = si->next) {
+ if (si->get_st_dev() != 0 &&
+ si->get_st_ino() != 0 &&
+ si->get_st_dev() == file_stat.st_dev &&
+ si->get_st_ino() == file_stat.st_ino &&
+ si->get_file_offset() == file_offset) {
+ TRACE("library \"%s\" is already loaded under different name/path \"%s\" - "
+ "will return existing soinfo", name, si->name);
+ return si;
+ }
}
}
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index ca6a75a..700abff 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -173,6 +173,40 @@
ASSERT_STREQ("dlopen failed: invalid extended flag combination (ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET without ANDROID_DLEXT_USE_LIBRARY_FD): 0x20", dlerror());
}
+TEST(dlext, android_dlopen_ext_force_load_smoke) {
+ // 1. Open actual file
+ void* handle = dlopen("libdlext_test.so", RTLD_NOW);
+ ASSERT_DL_NOTNULL(handle);
+ // 2. Open link with force_load flag set
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_FORCE_LOAD;
+ void* handle2 = android_dlopen_ext("libdlext_test_v2.so", RTLD_NOW, &extinfo);
+ ASSERT_DL_NOTNULL(handle2);
+ ASSERT_TRUE(handle != handle2);
+
+ dlclose(handle2);
+ dlclose(handle);
+}
+
+TEST(dlext, android_dlopen_ext_force_load_soname_exception) {
+ // Check if soname lookup still returns already loaded library
+ // when ANDROID_DLEXT_FORCE_LOAD flag is specified.
+ void* handle = dlopen("libdlext_test_v2.so", RTLD_NOW);
+ ASSERT_DL_NOTNULL(handle);
+
+ android_dlextinfo extinfo;
+ extinfo.flags = ANDROID_DLEXT_FORCE_LOAD;
+
+ // Note that 'libdlext_test.so' is dt_soname for libdlext_test_v2.so
+ void* handle2 = android_dlopen_ext("libdlext_test.so", RTLD_NOW, &extinfo);
+
+ ASSERT_DL_NOTNULL(handle2);
+ ASSERT_TRUE(handle == handle2);
+
+ dlclose(handle2);
+ dlclose(handle);
+}
+
TEST(dlfcn, dlopen_from_zip_absolute_path) {
const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH;