summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDimitry Ivanov <dimitry@google.com>2015-03-18 17:25:32 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-03-18 17:25:33 +0000
commitc0eaa732844a8397d40a7ec82641f709e4b47a61 (patch)
treeb5fa6d3f36f7520c12f31e6bb75c3fe9ba6331a5
parentab12dc70f3ef551c490dcfede76033e57ceee64d (diff)
parentaef719510a57274e73ca02ab5ecdc5bf17d3985c (diff)
downloadbionic-c0eaa732844a8397d40a7ec82641f709e4b47a61.zip
bionic-c0eaa732844a8397d40a7ec82641f709e4b47a61.tar.gz
bionic-c0eaa732844a8397d40a7ec82641f709e4b47a61.tar.bz2
Merge "Support loading shared libraries from zip files"
-rw-r--r--linker/Android.mk2
-rw-r--r--linker/linker.cpp111
-rw-r--r--tests/dlfcn_test.cpp48
-rw-r--r--tests/libs/Android.build.dlext_testzip.mk6
4 files changed, 149 insertions, 18 deletions
diff --git a/linker/Android.mk b/linker/Android.mk
index f78a025..81c007f 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -57,7 +57,7 @@ LOCAL_ASFLAGS := $(LOCAL_CFLAGS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_STATIC_LIBRARIES := libc_nomalloc
+LOCAL_STATIC_LIBRARIES := libc_nomalloc libziparchive libutils libz liblog
LOCAL_FORCE_STATIC_EXECUTABLE := true
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 18f8cdc..15ea423 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -57,6 +57,7 @@
#include "linker_phdr.h"
#include "linker_relocs.h"
#include "linker_reloc_iterators.h"
+#include "ziparchive/zip_archive.h"
/* >>> IMPORTANT NOTE - READ ME BEFORE MODIFYING <<<
*
@@ -838,29 +839,109 @@ ElfW(Sym)* soinfo::elf_addr_lookup(const void* addr) {
return nullptr;
}
-static int open_library_on_path(const char* name, const char* const paths[]) {
+static int open_library_in_zipfile(const char* const path,
+ off64_t* file_offset) {
+ TRACE("Trying zip file open from path '%s'", path);
+
+ // Treat an '!' character inside a path as the separator between the name
+ // of the zip file on disk and the subdirectory to search within it.
+ // For example, if path is "foo.zip!bar/bas/x.so", then we search for
+ // "bar/bas/x.so" within "foo.zip".
+ const char* separator = strchr(path, '!');
+ if (separator == nullptr) {
+ return -1;
+ }
+
+ char buf[512];
+ if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) {
+ PRINT("Warning: ignoring very long library path: %s", path);
+ return -1;
+ }
+
+ buf[separator - path] = '\0';
+
+ const char* zip_path = buf;
+ const char* file_path = &buf[separator - path + 1];
+ int fd = TEMP_FAILURE_RETRY(open(zip_path, O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ return -1;
+ }
+
+ ZipArchiveHandle handle;
+ if (OpenArchiveFd(fd, "", &handle, false) != 0) {
+ // invalid zip-file (?)
+ close(fd);
+ return -1;
+ }
+
+ auto archive_guard = make_scope_guard([&]() {
+ CloseArchive(handle);
+ });
+
+ ZipEntry entry;
+
+ if (FindEntry(handle, ZipEntryName(file_path), &entry) != 0) {
+ // Entry was not found.
+ close(fd);
+ return -1;
+ }
+
+ // Check if it is properly stored
+ if (entry.method != kCompressStored || (entry.offset % PAGE_SIZE) != 0) {
+ close(fd);
+ return -1;
+ }
+
+ *file_offset = entry.offset;
+ return fd;
+}
+
+static int open_library_on_path(const char* name,
+ const char* const paths[],
+ off64_t* file_offset) {
char buf[512];
- for (size_t i = 0; paths[i] != nullptr; ++i) {
- int n = __libc_format_buffer(buf, sizeof(buf), "%s/%s", paths[i], name);
+ int fd = -1;
+
+ for (size_t i = 0; paths[i] != nullptr && fd == -1; ++i) {
+ const char* const path = paths[i];
+ int n = __libc_format_buffer(buf, sizeof(buf), "%s/%s", path, name);
if (n < 0 || n >= static_cast<int>(sizeof(buf))) {
- PRINT("Warning: ignoring very long library path: %s/%s", paths[i], name);
- continue;
+ PRINT("Warning: ignoring very long library path: %s/%s", path, name);
+ return -1;
}
- int fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC));
- if (fd != -1) {
- return fd;
+
+ const char* separator = strchr(path, '!');
+
+ if (separator != nullptr) {
+ fd = open_library_in_zipfile(buf, file_offset);
+ }
+
+ if (fd == -1) {
+ fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC));
+ if (fd != -1) {
+ *file_offset = 0;
+ }
}
}
- return -1;
+
+ return fd;
}
-static int open_library(const char* name) {
+static int open_library(const char* name, off64_t* file_offset) {
TRACE("[ opening %s ]", name);
// If the name contains a slash, we should attempt to open it directly and not search the paths.
if (strchr(name, '/') != nullptr) {
+ if (strchr(name, '!') != nullptr) {
+ int fd = open_library_in_zipfile(name, file_offset);
+ if (fd != -1) {
+ return fd;
+ }
+ }
+
int fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC));
if (fd != -1) {
+ *file_offset = 0;
return fd;
}
// ...but nvidia binary blobs (at least) rely on this behavior, so fall through for now.
@@ -870,9 +951,9 @@ static int open_library(const char* name) {
}
// Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths.
- int fd = open_library_on_path(name, g_ld_library_paths);
+ int fd = open_library_on_path(name, g_ld_library_paths, file_offset);
if (fd == -1) {
- fd = open_library_on_path(name, kDefaultLdPaths);
+ fd = open_library_on_path(name, kDefaultLdPaths, file_offset);
}
return fd;
}
@@ -886,7 +967,9 @@ static void for_each_dt_needed(const soinfo* si, F action) {
}
}
-static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int rtld_flags, const android_dlextinfo* extinfo) {
+static soinfo* load_library(LoadTaskList& load_tasks,
+ const char* name, int rtld_flags,
+ const android_dlextinfo* extinfo) {
int fd = -1;
off64_t file_offset = 0;
ScopedFd file_guard(-1);
@@ -898,7 +981,7 @@ static soinfo* load_library(LoadTaskList& load_tasks, const char* name, int rtld
}
} else {
// Open the file.
- fd = open_library(name);
+ fd = open_library(name, &file_offset);
if (fd == -1) {
DL_ERR("library \"%s\" not found", name);
return nullptr;
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index dfb5e54..ef4ff20 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -29,6 +29,14 @@
#define ASSERT_SUBSTR(needle, haystack) \
ASSERT_PRED_FORMAT2(::testing::IsSubstring, needle, haystack)
+#if defined(__LP64__)
+#define LIBPATH_PREFIX "/nativetest64/libdlext_test_fd/"
+#else
+#define LIBPATH_PREFIX "/nativetest/libdlext_test_fd/"
+#endif
+
+#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_fd_zipaligned.zip"
+
static bool g_called = false;
extern "C" void DlSymTestFunction() {
g_called = true;
@@ -844,6 +852,46 @@ TEST(dlfcn, dlopen_symlink) {
dlclose(handle2);
}
+TEST(dlfcn, dlopen_from_zip_absolute_path) {
+ const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH;
+
+ void* handle = dlopen((lib_path + "!libdir/libdlext_test_fd.so").c_str(), RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+
+ int (*fn)(void);
+ fn = reinterpret_cast<int (*)(void)>(dlsym(handle, "getRandomNumber"));
+ ASSERT_TRUE(fn != nullptr);
+ EXPECT_EQ(4, fn());
+
+ dlclose(handle);
+}
+
+TEST(dlfcn, dlopen_from_zip_ld_library_path) {
+ const std::string lib_path = std::string(getenv("ANDROID_DATA")) + LIBZIPPATH + "!libdir";
+
+ typedef void (*fn_t)(const char*);
+ fn_t android_update_LD_LIBRARY_PATH =
+ reinterpret_cast<fn_t>(dlsym(RTLD_DEFAULT, "android_update_LD_LIBRARY_PATH"));
+
+ ASSERT_TRUE(android_update_LD_LIBRARY_PATH != nullptr) << dlerror();
+
+ void* handle = dlopen("libdlext_test_fd.so", RTLD_NOW);
+ ASSERT_TRUE(handle == nullptr);
+
+ android_update_LD_LIBRARY_PATH(lib_path.c_str());
+
+ handle = dlopen("libdlext_test_fd.so", RTLD_NOW);
+ ASSERT_TRUE(handle != nullptr) << dlerror();
+
+ int (*fn)(void);
+ fn = reinterpret_cast<int (*)(void)>(dlsym(handle, "getRandomNumber"));
+ ASSERT_TRUE(fn != nullptr);
+ EXPECT_EQ(4, fn());
+
+ dlclose(handle);
+}
+
+
// libtest_dlopen_from_ctor_main.so depends on
// libtest_dlopen_from_ctor.so which has a constructor
// that calls dlopen(libc...). This is to test the situation
diff --git a/tests/libs/Android.build.dlext_testzip.mk b/tests/libs/Android.build.dlext_testzip.mk
index d05927e..7cc0dae 100644
--- a/tests/libs/Android.build.dlext_testzip.mk
+++ b/tests/libs/Android.build.dlext_testzip.mk
@@ -35,7 +35,7 @@ my_shared_libs := \
$(LOCAL_BUILT_MODULE): PRIVATE_ALIGNMENT := 4096 # PAGE_SIZE
$(LOCAL_BUILT_MODULE) : $(my_shared_libs) | $(ZIPALIGN)
@echo "Zipalign $(PRIVATE_ALIGNMENT): $@"
- $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)
- $(hide) cp $^ $(dir $@)
- $(hide) (cd $(dir $@) && touch empty_file.txt && zip -rD0 $(notdir $@).unaligned empty_file.txt *.so)
+ $(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)/libdir
+ $(hide) cp $^ $(dir $@)/libdir
+ $(hide) (cd $(dir $@) && touch empty_file.txt && zip -rD0 $(notdir $@).unaligned empty_file.txt libdir/*.so)
$(hide) $(ZIPALIGN) $(PRIVATE_ALIGNMENT) $@.unaligned $@