diff options
author | Dimitry Ivanov <dimitry@google.com> | 2015-03-18 17:25:32 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2015-03-18 17:25:33 +0000 |
commit | c0eaa732844a8397d40a7ec82641f709e4b47a61 (patch) | |
tree | b5fa6d3f36f7520c12f31e6bb75c3fe9ba6331a5 | |
parent | ab12dc70f3ef551c490dcfede76033e57ceee64d (diff) | |
parent | aef719510a57274e73ca02ab5ecdc5bf17d3985c (diff) | |
download | bionic-c0eaa732844a8397d40a7ec82641f709e4b47a61.zip bionic-c0eaa732844a8397d40a7ec82641f709e4b47a61.tar.gz bionic-c0eaa732844a8397d40a7ec82641f709e4b47a61.tar.bz2 |
Merge "Support loading shared libraries from zip files"
-rw-r--r-- | linker/Android.mk | 2 | ||||
-rw-r--r-- | linker/linker.cpp | 111 | ||||
-rw-r--r-- | tests/dlfcn_test.cpp | 48 | ||||
-rw-r--r-- | tests/libs/Android.build.dlext_testzip.mk | 6 |
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 $@ |