From 42d5fcb9f494eb45de3b6bf759f4a18076e84728 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Thu, 29 Oct 2015 17:01:24 -0700 Subject: [PATCH] Introducing linker namespaces Bug: http://b/22548808 Change-Id: Ia3af3c0a167f1d16447a3d83bb045d143319b1e1 --- libc/include/android/dlext.h | 40 +- libdl/libdl.arm.map | 8 +- libdl/libdl.arm64.map | 8 +- libdl/libdl.c | 18 +- libdl/libdl.map.txt | 8 +- libdl/libdl.mips.map | 8 +- libdl/libdl.mips64.map | 8 +- libdl/libdl.x86.map | 8 +- libdl/libdl.x86_64.map | 8 +- linker/Android.mk | 2 +- linker/dlfcn.cpp | 53 +- linker/linked_list.h | 56 ++ linker/linker.cpp | 531 ++++++++++++++---- linker/linker.h | 17 +- linker/linker_utils.cpp | 46 +- linker/linker_utils.h | 4 + linker/tests/linker_utils_test.cpp | 26 + tests/Android.build.mk | 12 +- tests/dlext_test.cpp | 199 ++++++- tests/libs/Android.build.linker_namespaces.mk | 84 +++ tests/libs/Android.build.target.testlib.mk | 20 + tests/libs/Android.mk | 6 + tests/libs/namespaces_dlopened.cpp | 19 + tests/libs/namespaces_private.cpp | 18 + tests/libs/namespaces_public.cpp | 18 + tests/libs/namespaces_root.cpp | 49 ++ 26 files changed, 1122 insertions(+), 152 deletions(-) create mode 100644 tests/libs/Android.build.linker_namespaces.mk create mode 100644 tests/libs/Android.build.target.testlib.mk create mode 100644 tests/libs/namespaces_dlopened.cpp create mode 100644 tests/libs/namespaces_private.cpp create mode 100644 tests/libs/namespaces_public.cpp create mode 100644 tests/libs/namespaces_root.cpp diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h index 4af5de311..d59de2938 100644 --- a/libc/include/android/dlext.h +++ b/libc/include/android/dlext.h @@ -98,6 +98,11 @@ enum { */ ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS = 0x100, + /* This flag used to load library in a different namespace. The namespace is + * specified in library_namespace. + */ + ANDROID_DLEXT_USE_NAMESPACE = 0x200, + /* Mask of valid bits */ ANDROID_DLEXT_VALID_FLAG_BITS = ANDROID_DLEXT_RESERVED_ADDRESS | ANDROID_DLEXT_RESERVED_ADDRESS_HINT | @@ -107,9 +112,12 @@ enum { ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET | ANDROID_DLEXT_FORCE_LOAD | ANDROID_DLEXT_FORCE_FIXED_VADDR | - ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS, + ANDROID_DLEXT_LOAD_AT_FIXED_ADDRESS | + ANDROID_DLEXT_USE_NAMESPACE, }; +struct android_namespace_t; + typedef struct { uint64_t flags; void* reserved_addr; @@ -117,10 +125,40 @@ typedef struct { int relro_fd; int library_fd; off64_t library_fd_offset; + struct android_namespace_t* library_namespace; } android_dlextinfo; extern void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo); +/* + * Initializes public namespace. The path is the list of sonames + * separated by colon. Example: "libc.so:libm.so:libdl.so". + * + * The libraries in this list should be loaded prior to this call. + */ +extern bool android_init_public_namespace(const char* path); + +/* + * Creates new linker namespace. + * ld_library_path and default_library_path represent the search path + * for the libraries in the namespace. + * + * The libraries in the namespace are searched by folowing order: + * 1. ld_library_path (Think of this as namespace-local LD_LIBRARY_PATH) + * 2. In directories specified by DT_RUNPATH of the "needed by" binary. + * 3. deault_library_path (This of this as namespace-local default library path) + * + * When is_isolated is true the resulted namespace requires all of the libraries + * to be on the search path; the search_path is ld_library_path:default_library_path. + * + * If a library or any of its dependencies are outside of the search path and not + * part of the public namespace dlopen will fail. + */ +extern struct android_namespace_t* android_create_namespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + bool is_isolated); + __END_DECLS #endif /* __ANDROID_DLEXT_H__ */ diff --git a/libdl/libdl.arm.map b/libdl/libdl.arm.map index 9e5870416..3417f99a8 100644 --- a/libdl/libdl.arm.map +++ b/libdl/libdl.arm.map @@ -14,10 +14,16 @@ LIBC { *; }; +LIBC_N { + global: + android_init_public_namespace; + android_create_namespace; +} LIBC; + LIBC_PRIVATE { global: android_get_application_target_sdk_version; android_set_application_target_sdk_version; android_get_LD_LIBRARY_PATH; android_update_LD_LIBRARY_PATH; -} LIBC; +} LIBC_N; diff --git a/libdl/libdl.arm64.map b/libdl/libdl.arm64.map index e83b501f1..b7e9aec64 100644 --- a/libdl/libdl.arm64.map +++ b/libdl/libdl.arm64.map @@ -13,10 +13,16 @@ LIBC { *; }; +LIBC_N { + global: + android_init_public_namespace; + android_create_namespace; +} LIBC; + LIBC_PRIVATE { global: android_get_application_target_sdk_version; android_set_application_target_sdk_version; android_get_LD_LIBRARY_PATH; android_update_LD_LIBRARY_PATH; -} LIBC; +} LIBC_N; diff --git a/libdl/libdl.c b/libdl/libdl.c index 9a858a36e..3cde5ebe4 100644 --- a/libdl/libdl.c +++ b/libdl/libdl.c @@ -33,12 +33,26 @@ int dlclose(void* handle __unused) { return 0; } _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr pc __unused, int* pcount __unused) { return 0; } #endif -int dl_iterate_phdr(int (*cb)(struct dl_phdr_info* info, size_t size, void* data) __unused, void* data __unused) { return 0; } +int dl_iterate_phdr(int (*cb)(struct dl_phdr_info* info, size_t size, void* data) __unused, + void* data __unused) { + return 0; +} void android_get_LD_LIBRARY_PATH(char* buffer __unused, size_t buffer_size __unused) { } void android_update_LD_LIBRARY_PATH(const char* ld_library_path __unused) { } -void* android_dlopen_ext(const char* filename __unused, int flag __unused, const android_dlextinfo* extinfo __unused) { return 0; } +void* android_dlopen_ext(const char* filename __unused, int flag __unused, + const android_dlextinfo* extinfo __unused) { + return 0; +} void android_set_application_target_sdk_version(uint32_t target __unused) { } uint32_t android_get_application_target_sdk_version() { return 0; } + +bool android_init_public_namespace(const char* paths __unused) { return false; } +struct android_namespace_t* android_create_namespace(const char* name __unused, + const char* ld_library_path __unused, + const char* default_library_path __unused, + bool isolated __unused) { + return 0; +} diff --git a/libdl/libdl.map.txt b/libdl/libdl.map.txt index f71f40b77..421d825cc 100644 --- a/libdl/libdl.map.txt +++ b/libdl/libdl.map.txt @@ -28,10 +28,16 @@ LIBC { *; }; +LIBC_N { + global: + android_init_public_namespace; + android_create_namespace; +} LIBC; + LIBC_PRIVATE { global: android_get_application_target_sdk_version; android_set_application_target_sdk_version; android_get_LD_LIBRARY_PATH; android_update_LD_LIBRARY_PATH; -} LIBC; +} LIBC_N; diff --git a/libdl/libdl.mips.map b/libdl/libdl.mips.map index e83b501f1..b7e9aec64 100644 --- a/libdl/libdl.mips.map +++ b/libdl/libdl.mips.map @@ -13,10 +13,16 @@ LIBC { *; }; +LIBC_N { + global: + android_init_public_namespace; + android_create_namespace; +} LIBC; + LIBC_PRIVATE { global: android_get_application_target_sdk_version; android_set_application_target_sdk_version; android_get_LD_LIBRARY_PATH; android_update_LD_LIBRARY_PATH; -} LIBC; +} LIBC_N; diff --git a/libdl/libdl.mips64.map b/libdl/libdl.mips64.map index e83b501f1..b7e9aec64 100644 --- a/libdl/libdl.mips64.map +++ b/libdl/libdl.mips64.map @@ -13,10 +13,16 @@ LIBC { *; }; +LIBC_N { + global: + android_init_public_namespace; + android_create_namespace; +} LIBC; + LIBC_PRIVATE { global: android_get_application_target_sdk_version; android_set_application_target_sdk_version; android_get_LD_LIBRARY_PATH; android_update_LD_LIBRARY_PATH; -} LIBC; +} LIBC_N; diff --git a/libdl/libdl.x86.map b/libdl/libdl.x86.map index e83b501f1..b7e9aec64 100644 --- a/libdl/libdl.x86.map +++ b/libdl/libdl.x86.map @@ -13,10 +13,16 @@ LIBC { *; }; +LIBC_N { + global: + android_init_public_namespace; + android_create_namespace; +} LIBC; + LIBC_PRIVATE { global: android_get_application_target_sdk_version; android_set_application_target_sdk_version; android_get_LD_LIBRARY_PATH; android_update_LD_LIBRARY_PATH; -} LIBC; +} LIBC_N; diff --git a/libdl/libdl.x86_64.map b/libdl/libdl.x86_64.map index e83b501f1..b7e9aec64 100644 --- a/libdl/libdl.x86_64.map +++ b/libdl/libdl.x86_64.map @@ -13,10 +13,16 @@ LIBC { *; }; +LIBC_N { + global: + android_init_public_namespace; + android_create_namespace; +} LIBC; + LIBC_PRIVATE { global: android_get_application_target_sdk_version; android_set_application_target_sdk_version; android_get_LD_LIBRARY_PATH; android_update_LD_LIBRARY_PATH; -} LIBC; +} LIBC_N; diff --git a/linker/Android.mk b/linker/Android.mk index 0188a4922..85ac0caf4 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -9,12 +9,12 @@ LOCAL_SRC_FILES := \ dlfcn.cpp \ linker.cpp \ linker_allocator.cpp \ - linker_sdk_versions.cpp \ linker_block_allocator.cpp \ linker_libc_support.c \ linker_mapped_file_fragment.cpp \ linker_memory.cpp \ linker_phdr.cpp \ + linker_sdk_versions.cpp \ linker_utils.cpp \ rt.cpp \ diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 93189df85..7957921ed 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -108,9 +108,16 @@ void* dlsym(void* handle, const char* symbol) { const ElfW(Sym)* sym = nullptr; void* caller_addr = __builtin_return_address(0); soinfo* caller = find_containing_library(caller_addr); + if (caller == nullptr) { + char buf[256]; + __libc_format_buffer(buf, sizeof(buf), "dlsym couldn't locate its caller address=%p; " + "the caller was code not loaded by the dynamic linker", caller_addr); + __bionic_format_dlerror(buf, nullptr); + return nullptr; + } if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) { - sym = dlsym_linear_lookup(symbol, &found, caller, handle); + sym = dlsym_linear_lookup(caller->get_namespace(), symbol, &found, caller, handle); } else { sym = dlsym_handle_lookup(reinterpret_cast(handle), &found, symbol); } @@ -177,6 +184,30 @@ uint32_t android_get_application_target_sdk_version() { return get_application_target_sdk_version(); } +bool android_init_public_namespace(const char* path) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + bool success = init_public_namespace(path); + if (!success) { + __bionic_format_dlerror("android_init_public_namespace failed", linker_get_error_buffer()); + } + + return success; +} + +android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, + const char* default_library_path, bool is_isolated) { + ScopedPthreadMutexLocker locker(&g_dl_mutex); + + android_namespace_t* result = create_namespace(name, ld_library_path, + default_library_path, is_isolated); + + if (result == nullptr) { + __bionic_format_dlerror("android_create_namespace failed", linker_get_error_buffer()); + } + + return result; +} + // name_offset: starting index of the name in libdl_info.strtab #define ELF32_SYM_INITIALIZER(name_offset, value, shndx) \ { name_offset, \ @@ -203,11 +234,11 @@ static const char ANDROID_LIBDL_STRTAB[] = // 00000000001 1111111112222222222 3333333333444444444455555555556666666666777 777777788888888889999999999 // 01234567890 1234567890123456789 0123456789012345678901234567890123456789012 345678901234567890123456789 "erate_phdr\0android_dlopen_ext\0android_set_application_target_sdk_version\0android_get_application_tar" - // 0000000000111111 - // 0123456789012345 - "get_sdk_version\0" + // 0000000000111111 111122222222223333333333444444 4444555555555566666666667 + // 0123456789012345 678901234567890123456789012345 6789012345678901234567890 + "get_sdk_version\0android_init_public_namespace\0android_create_namespace\0" #if defined(__arm__) - // 216 + // 271 "dl_unwind_find_exidx\0" #endif ; @@ -229,8 +260,10 @@ static ElfW(Sym) g_libdl_symtab[] = { ELFW(SYM_INITIALIZER)(111, &android_dlopen_ext, 1), ELFW(SYM_INITIALIZER)(130, &android_set_application_target_sdk_version, 1), ELFW(SYM_INITIALIZER)(173, &android_get_application_target_sdk_version, 1), + ELFW(SYM_INITIALIZER)(216, &android_init_public_namespace, 1), + ELFW(SYM_INITIALIZER)(246, &android_create_namespace, 1), #if defined(__arm__) - ELFW(SYM_INITIALIZER)(216, &dl_unwind_find_exidx, 1), + ELFW(SYM_INITIALIZER)(271, &dl_unwind_find_exidx, 1), #endif }; @@ -247,18 +280,20 @@ static ElfW(Sym) g_libdl_symtab[] = { // Note that adding any new symbols here requires stubbing them out in libdl. static unsigned g_libdl_buckets[1] = { 1 }; #if defined(__arm__) -static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0 }; +static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0 }; #else -static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0 }; +static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0 }; #endif static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8))); static soinfo* __libdl_info = nullptr; +extern android_namespace_t g_default_namespace; + // This is used by the dynamic linker. Every process gets these symbols for free. soinfo* get_libdl_info() { if (__libdl_info == nullptr) { - __libdl_info = new (__libdl_info_buf) soinfo("libdl.so", nullptr, 0, RTLD_GLOBAL); + __libdl_info = new (__libdl_info_buf) soinfo(&g_default_namespace, "libdl.so", nullptr, 0, RTLD_GLOBAL); __libdl_info->flags_ |= FLAG_LINKED; __libdl_info->strtab_ = ANDROID_LIBDL_STRTAB; __libdl_info->symtab_ = g_libdl_symtab; diff --git a/linker/linked_list.h b/linker/linked_list.h index eb3ecd413..88386b0f9 100644 --- a/linker/linked_list.h +++ b/linker/linked_list.h @@ -25,12 +25,49 @@ struct LinkedListEntry { T* element; }; +// ForwardInputIterator +template +class LinkedListIterator { + public: + LinkedListIterator() : entry_(nullptr) {} + LinkedListIterator(const LinkedListIterator& that) : entry_(that.entry_) {} + explicit LinkedListIterator(LinkedListEntry* entry) : entry_(entry) {} + + LinkedListIterator& operator=(const LinkedListIterator& that) { + entry_ = that.entry_; + return *this; + } + + LinkedListIterator& operator++() { + entry_ = entry_->next; + return *this; + } + + T* operator*() { + return entry_->element; + } + + bool operator==(const LinkedListIterator& that) const { + return entry_ == that.entry_; + } + + bool operator!=(const LinkedListIterator& that) const { + return entry_ != that.entry_; + } + + private: + LinkedListEntry *entry_; +}; + /* * Represents linked list of objects of type T */ template class LinkedList { public: + typedef LinkedListIterator iterator; + typedef T* value_type; + LinkedList() : head_(nullptr), tail_(nullptr) {} ~LinkedList() { clear(); @@ -133,6 +170,7 @@ class LinkedList { } Allocator::free(e); + e = next; } else { p = e; @@ -152,6 +190,24 @@ class LinkedList { return nullptr; } + iterator begin() { + return iterator(head_); + } + + iterator end() { + return iterator(nullptr); + } + + iterator find(T* value) { + for (LinkedListEntry* e = head_; e != nullptr; e = e->next) { + if (e->element == value) { + return iterator(e); + } + } + + return end(); + } + size_t copy_to_array(T* array[], size_t array_length) const { size_t sz = 0; for (LinkedListEntry* e = head_; sz < array_length && e != nullptr; e = e->next) { diff --git a/linker/linker.cpp b/linker/linker.cpp index 31ed1ec19..32e3063fd 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -70,11 +70,55 @@ extern void __libc_init_AT_SECURE(KernelArgumentBlock&); #undef ELF_ST_TYPE #define ELF_ST_TYPE(x) (static_cast(x) & 0xf) +struct android_namespace_t { + public: + android_namespace_t() : name_(nullptr), is_isolated_(false) {} + + const char* get_name() const { return name_; } + void set_name(const char* name) { name_ = name; } + + bool is_isolated() const { return is_isolated_; } + void set_isolated(bool isolated) { is_isolated_ = isolated; } + + const std::vector& get_ld_library_paths() const { + return ld_library_paths_; + } + void set_ld_library_paths(std::vector&& library_paths) { + ld_library_paths_ = library_paths; + } + + const std::vector& get_default_library_paths() const { + return default_library_paths_; + } + void set_default_library_paths(std::vector&& library_paths) { + default_library_paths_ = library_paths; + } + + soinfo::soinfo_list_t& soinfo_list() { return soinfo_list_; } + + // For isolated namespaces - checks if the file is on the search path; + // always returns true for not isolated namespace. + bool is_accessible(const std::string& path); + + private: + const char* name_; + bool is_isolated_; + std::vector ld_library_paths_; + std::vector default_library_paths_; + soinfo::soinfo_list_t soinfo_list_; + + DISALLOW_COPY_AND_ASSIGN(android_namespace_t); +}; + +android_namespace_t g_default_namespace; + static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf); static LinkerTypeAllocator g_soinfo_allocator; static LinkerTypeAllocator> g_soinfo_links_allocator; +static LinkerTypeAllocator g_namespace_allocator; + static soinfo* solist; static soinfo* sonext; static soinfo* somain; // main process, always the one after libdl_info @@ -107,14 +151,15 @@ static const char* const kAsanDefaultLdPaths[] = { static const ElfW(Versym) kVersymNotNeeded = 0; static const ElfW(Versym) kVersymGlobal = 1; -static const char* const kZipFileSeparator = "!/"; static const char* const* g_default_ld_paths; -static std::vector g_ld_library_paths; static std::vector g_ld_preload_names; static std::vector g_ld_preloads; +static bool g_public_namespace_initialized; +static soinfo::soinfo_list_t g_public_namespace; + __LIBC_HIDDEN__ int g_ld_debug_verbosity; __LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd. @@ -247,6 +292,26 @@ void notify_gdb_of_libraries() { rtld_db_dlactivity(); } +bool android_namespace_t::is_accessible(const std::string& file) { + if (!is_isolated_) { + return true; + } + + for (const auto& dir : ld_library_paths_) { + if (file_is_in_dir(file, dir)) { + return true; + } + } + + for (const auto& dir : default_library_paths_) { + if (file_is_in_dir(file, dir)) { + return true; + } + } + + return false; +} + LinkedListEntry* SoinfoListAllocator::alloc() { return g_soinfo_links_allocator.alloc(); } @@ -255,18 +320,22 @@ void SoinfoListAllocator::free(LinkedListEntry* entry) { g_soinfo_links_allocator.free(entry); } -static soinfo* soinfo_alloc(const char* name, struct stat* file_stat, - off64_t file_offset, uint32_t rtld_flags) { +static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name, + struct stat* file_stat, off64_t file_offset, + uint32_t rtld_flags) { if (strlen(name) >= PATH_MAX) { DL_ERR("library name \"%s\" too long", name); return nullptr; } - soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(name, file_stat, file_offset, rtld_flags); + soinfo* si = new (g_soinfo_allocator.alloc()) soinfo(ns, name, file_stat, + file_offset, rtld_flags); sonext->next = si; sonext = si; + ns->soinfo_list().push_back(si); + TRACE("name %s: allocated soinfo @ %p", name, si); return si; } @@ -307,33 +376,130 @@ static void soinfo_free(soinfo* si) { sonext = prev; } + // remove from the namespace + si->get_namespace()->soinfo_list().remove_if([&](soinfo* candidate) { + return si == candidate; + }); + si->~soinfo(); g_soinfo_allocator.free(si); } -static void parse_path(const char* path, const char* delimiters, +// For every path element this function checks of it exists, and is a directory, +// and normalizes it: +// 1. For regular path it converts it to realpath() +// 2. For path in a zip file it uses realpath on the zipfile +// normalizes entry name by calling normalize_path function. +static void resolve_paths(std::vector& paths, + std::vector* resolved_paths) { + resolved_paths->clear(); + for (const auto& path : paths) { + char resolved_path[PATH_MAX]; + const char* original_path = path.c_str(); + if (realpath(original_path, resolved_path) != nullptr) { + struct stat s; + if (stat(resolved_path, &s) == 0) { + if (S_ISDIR(s.st_mode)) { + resolved_paths->push_back(resolved_path); + } else { + DL_WARN("Warning: \"%s\" is not a directory (excluding from path)", resolved_path); + continue; + } + } else { + DL_WARN("Warning: cannot stat file \"%s\": %s", resolved_path, strerror(errno)); + continue; + } + } else { + std::string zip_path; + std::string entry_path; + + std::string normalized_path; + + if (!normalize_path(original_path, &normalized_path)) { + DL_WARN("Warning: unable to normalize \"%s\"", original_path); + continue; + } + + if (parse_zip_path(normalized_path.c_str(), &zip_path, &entry_path)) { + if (realpath(zip_path.c_str(), resolved_path) == nullptr) { + DL_WARN("Warning: unable to resolve \"%s\": %s", zip_path.c_str(), strerror(errno)); + continue; + } + + ZipArchiveHandle handle = nullptr; + if (OpenArchive(resolved_path, &handle) != 0) { + DL_WARN("Warning: unable to open zip archive: %s", resolved_path); + continue; + } + + // Check if zip-file has a dir with entry_path name + void* cookie = nullptr; + std::string prefix_str = entry_path + "/"; + ZipString prefix(prefix_str.c_str()); + + ZipEntry out_data; + ZipString out_name; + + int32_t error_code; + + if ((error_code = StartIteration(handle, &cookie, &prefix, nullptr)) != 0) { + DL_WARN("Unable to iterate over zip-archive entries \"%s\";" + " error code: %d", zip_path.c_str(), error_code); + continue; + } + + if (Next(cookie, &out_data, &out_name) != 0) { + DL_WARN("Unable to find entries starting with \"%s\" in \"%s\"", + prefix_str.c_str(), zip_path.c_str()); + continue; + } + + auto zip_guard = make_scope_guard([&]() { + if (cookie != nullptr) { + EndIteration(cookie); + } + CloseArchive(handle); + }); + + resolved_paths->push_back(std::string(resolved_path) + kZipFileSeparator + entry_path); + } + } + } +} + +static void split_path(const char* path, const char* delimiters, std::vector* paths) { - paths->clear(); if (path != nullptr) { *paths = android::base::Split(path, delimiters); } } +static void parse_path(const char* path, const char* delimiters, + std::vector* resolved_paths) { + std::vector paths; + split_path(path, delimiters, &paths); + resolve_paths(paths, resolved_paths); +} + static void parse_LD_LIBRARY_PATH(const char* path) { - parse_path(path, ":", &g_ld_library_paths); + std::vector ld_libary_paths; + parse_path(path, ":", &ld_libary_paths); + g_default_namespace.set_ld_library_paths(std::move(ld_libary_paths)); } void soinfo::set_dt_runpath(const char* path) { - if (!has_min_version(2)) { + if (!has_min_version(3)) { return; } - parse_path(path, ":", &dt_runpath_); + std::vector runpaths; + + split_path(path, ":", &runpaths); std::string origin = dirname(get_realpath()); // FIXME: add $LIB and $PLATFORM. std::pair substs[] = {{"ORIGIN", origin}}; - for (auto&& s : dt_runpath_) { + for (auto&& s : runpaths) { size_t pos = 0; while (pos < s.size()) { pos = s.find("$", pos); @@ -356,11 +522,16 @@ void soinfo::set_dt_runpath(const char* path) { ++pos; } } + + resolve_paths(runpaths, &dt_runpath_); } static void parse_LD_PRELOAD(const char* path) { - // We have historically supported ':' as well as ' ' in LD_PRELOAD. - parse_path(path, " :", &g_ld_preload_names); + g_ld_preload_names.clear(); + if (path != nullptr) { + // We have historically supported ':' as well as ' ' in LD_PRELOAD. + g_ld_preload_names = android::base::Split(path, " :"); + } } static bool realpath_fd(int fd, std::string* realpath) { @@ -688,8 +859,9 @@ bool soinfo::elf_lookup(SymbolName& symbol_name, return true; } -soinfo::soinfo(const char* realpath, const struct stat* file_stat, - off64_t file_offset, int rtld_flags) { +soinfo::soinfo(android_namespace_t* ns, const char* realpath, + const struct stat* file_stat, off64_t file_offset, + int rtld_flags) { memset(this, 0, sizeof(*this)); if (realpath != nullptr) { @@ -706,6 +878,7 @@ soinfo::soinfo(const char* realpath, const struct stat* file_stat, } this->rtld_flags_ = rtld_flags; + this->namespace_ = ns; } @@ -857,6 +1030,7 @@ class ProtectedDataGuard { void protect_data(int protection) { g_soinfo_allocator.protect_all(protection); g_soinfo_links_allocator.protect_all(protection); + g_namespace_allocator.protect_all(protection); } static size_t ref_count_; @@ -1096,7 +1270,7 @@ const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* nam // libraries and they are loaded in breath-first (correct) order we can just execute // dlsym(RTLD_DEFAULT, ...); instead of doing two stage lookup. if (si == somain) { - return dlsym_linear_lookup(name, found, nullptr, RTLD_DEFAULT); + return dlsym_linear_lookup(&g_default_namespace, name, found, nullptr, RTLD_DEFAULT); } SymbolName symbol_name(name); @@ -1108,24 +1282,29 @@ const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* nam beginning of the global solist. Otherwise the search starts at the specified soinfo (for RTLD_NEXT). */ -const ElfW(Sym)* dlsym_linear_lookup(const char* name, +const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, + const char* name, soinfo** found, soinfo* caller, void* handle) { SymbolName symbol_name(name); - soinfo* start = solist; + soinfo::soinfo_list_t& soinfo_list = ns->soinfo_list(); + soinfo::soinfo_list_t::iterator start = soinfo_list.begin(); if (handle == RTLD_NEXT) { if (caller == nullptr) { return nullptr; } else { - start = caller->next; + soinfo::soinfo_list_t::iterator it = soinfo_list.find(caller); + CHECK (it != soinfo_list.end()); + start = ++it; } } const ElfW(Sym)* s = nullptr; - for (soinfo* si = start; si != nullptr; si = si->next) { + for (soinfo::soinfo_list_t::iterator it = start, end = soinfo_list.end(); it != end; ++it) { + soinfo* si = *it; // Do not skip RTLD_LOCAL libraries in dlsym(RTLD_DEFAULT, ...) // if the library is opened by application with target api level <= 22 // See http://b/21565766 @@ -1337,35 +1516,13 @@ static bool format_path(char* buf, size_t buf_size, const char* path, const char return true; } -static int open_library_on_default_path(const char* name, off64_t* file_offset, std::string* realpath) { - for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) { - char buf[512]; - if (!format_path(buf, sizeof(buf), g_default_ld_paths[i], name)) { - continue; - } - - int fd = TEMP_FAILURE_RETRY(open(buf, O_RDONLY | O_CLOEXEC)); - if (fd != -1) { - *file_offset = 0; - if (!realpath_fd(fd, realpath)) { - PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", buf); - *realpath = buf; - } - return fd; - } - } - - return -1; -} - static int open_library_on_paths(ZipArchiveCache* zip_archive_cache, const char* name, off64_t* file_offset, const std::vector& paths, std::string* realpath) { - for (const auto& path_str : paths) { + for (const auto& path : paths) { char buf[512]; - const char* const path = path_str.c_str(); - if (!format_path(buf, sizeof(buf), path, name)) { + if (!format_path(buf, sizeof(buf), path.c_str(), name)) { continue; } @@ -1393,39 +1550,49 @@ static int open_library_on_paths(ZipArchiveCache* zip_archive_cache, return -1; } -static int open_library(ZipArchiveCache* zip_archive_cache, +static int open_library(android_namespace_t* ns, + ZipArchiveCache* zip_archive_cache, const char* name, soinfo *needed_by, off64_t* file_offset, std::string* realpath) { 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) { + int fd = -1; + if (strstr(name, kZipFileSeparator) != nullptr) { - int fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath); + fd = open_library_in_zipfile(zip_archive_cache, name, file_offset, realpath); + } + + if (fd == -1) { + fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC)); if (fd != -1) { - return fd; + *file_offset = 0; + if (!realpath_fd(fd, realpath)) { + PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", name); + *realpath = name; + } } } - int fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_CLOEXEC)); - if (fd != -1) { - *file_offset = 0; - if (!realpath_fd(fd, realpath)) { - PRINT("warning: unable to get realpath for the library \"%s\". Will use given path.", name); - *realpath = name; - } + if (fd != -1 && !ns->is_accessible(*realpath)) { + fd = -1; } return fd; } - // Otherwise we try LD_LIBRARY_PATH first, and fall back to the built-in well known paths. - int fd = open_library_on_paths(zip_archive_cache, name, file_offset, g_ld_library_paths, realpath); + // Otherwise we try LD_LIBRARY_PATH first, and fall back to the default library path + int fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_ld_library_paths(), realpath); if (fd == -1 && needed_by != nullptr) { fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath); + // Check if the library is accessible + if (fd != -1 && !ns->is_accessible(*realpath)) { + fd = -1; + } } if (fd == -1) { - fd = open_library_on_default_path(name, file_offset, realpath); + fd = open_library_on_paths(zip_archive_cache, name, file_offset, ns->get_default_library_paths(), realpath); } return fd; @@ -1464,7 +1631,8 @@ static void for_each_dt_needed(const ElfReader& elf_reader, F action) { } } -static bool load_library(LoadTask* task, +static bool load_library(android_namespace_t* ns, + LoadTask* task, LoadTaskList* load_tasks, int rtld_flags, const std::string& realpath) { @@ -1495,18 +1663,30 @@ static bool load_library(LoadTask* task, // Check for symlink and other situations where // 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->get_realpath()); - task->set_soinfo(si); - return true; + auto predicate = [&](soinfo* si) { + return 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; + }; + + soinfo* si = ns->soinfo_list().find_if(predicate); + + // check public namespace + if (si == nullptr) { + si = g_public_namespace.find_if(predicate); + if (si != nullptr) { + ns->soinfo_list().push_back(si); } } + + if (si != nullptr) { + TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " + "will return existing soinfo", name, si->get_realpath()); + task->set_soinfo(si); + return true; + } } if ((rtld_flags & RTLD_NOLOAD) != 0) { @@ -1514,7 +1694,7 @@ static bool load_library(LoadTask* task, return false; } - soinfo* si = soinfo_alloc(realpath.c_str(), &file_stat, file_offset, rtld_flags); + soinfo* si = soinfo_alloc(ns, realpath.c_str(), &file_stat, file_offset, rtld_flags); if (si == nullptr) { return false; } @@ -1550,7 +1730,8 @@ static bool load_library(LoadTask* task, } -static bool load_library(LoadTask* task, +static bool load_library(android_namespace_t* ns, + LoadTask* task, ZipArchiveCache* zip_archive_cache, LoadTaskList* load_tasks, int rtld_flags) { @@ -1574,11 +1755,11 @@ static bool load_library(LoadTask* task, task->set_fd(extinfo->library_fd, false); task->set_file_offset(file_offset); - return load_library(task, load_tasks, rtld_flags, realpath); + return load_library(ns, task, load_tasks, rtld_flags, realpath); } // Open the file. - int fd = open_library(zip_archive_cache, name, needed_by, &file_offset, &realpath); + int fd = open_library(ns, zip_archive_cache, name, needed_by, &file_offset, &realpath); if (fd == -1) { DL_ERR("library \"%s\" not found", name); return false; @@ -1587,14 +1768,15 @@ static bool load_library(LoadTask* task, task->set_fd(fd, true); task->set_file_offset(file_offset); - return load_library(task, load_tasks, rtld_flags, realpath); + return load_library(ns, task, load_tasks, rtld_flags, realpath); } // Returns true if library was found and false in 2 cases -// 1. The library was found but loaded under different target_sdk_version -// (*candidate != nullptr) +// 1. (for default namespace only) The library was found but loaded under different +// target_sdk_version (*candidate != nullptr) // 2. The library was not found by soname (*candidate is nullptr) -static bool find_loaded_library_by_soname(const char* name, soinfo** candidate) { +static bool find_loaded_library_by_soname(android_namespace_t* ns, + const char* name, soinfo** candidate) { *candidate = nullptr; // Ignore filename with path. @@ -1604,7 +1786,7 @@ static bool find_loaded_library_by_soname(const char* name, soinfo** candidate) uint32_t target_sdk_version = get_application_target_sdk_version(); - for (soinfo* si = solist; si != nullptr; si = si->next) { + return !ns->soinfo_list().visit([&](soinfo* si) { const char* soname = si->get_soname(); if (soname != nullptr && (strcmp(name, soname) == 0)) { // If the library was opened under different target sdk version @@ -1614,36 +1796,52 @@ static bool find_loaded_library_by_soname(const char* name, soinfo** candidate) // in any case. bool is_libdl = si == solist; if (is_libdl || (si->get_dt_flags_1() & DF_1_GLOBAL) != 0 || - !si->is_linked() || si->get_target_sdk_version() == target_sdk_version) { + !si->is_linked() || si->get_target_sdk_version() == target_sdk_version || + ns != &g_default_namespace) { *candidate = si; - return true; + return false; } else if (*candidate == nullptr) { - // for the different sdk version - remember the first library. + // for the different sdk version in the default namespace + // remember the first library. *candidate = si; } } - } - return false; + return true; + }); } -static bool find_library_internal(LoadTask* task, +static bool find_library_internal(android_namespace_t* ns, + LoadTask* task, ZipArchiveCache* zip_archive_cache, LoadTaskList* load_tasks, int rtld_flags) { soinfo* candidate; - if (find_loaded_library_by_soname(task->get_name(), &candidate)) { + if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) { task->set_soinfo(candidate); return true; } + if (ns != &g_default_namespace) { + // check public namespace + candidate = g_public_namespace.find_if([&](soinfo* si) { + return strcmp(task->get_name(), si->get_soname()) == 0; + }); + + if (candidate != nullptr) { + ns->soinfo_list().push_back(candidate); + task->set_soinfo(candidate); + return true; + } + } + // Library might still be loaded, the accurate detection // of this fact is done by load_library. TRACE("[ '%s' find_loaded_library_by_soname returned false (*candidate=%s@%p). Trying harder...]", task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate); - if (load_library(task, zip_archive_cache, load_tasks, rtld_flags)) { + if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags)) { return true; } else { // In case we were unable to load the library but there @@ -1667,13 +1865,13 @@ static void soinfo_unload(soinfo* si); // // This group consists of the main executable, LD_PRELOADs // and libraries with the DF_1_GLOBAL flag set. -static soinfo::soinfo_list_t make_global_group() { +static soinfo::soinfo_list_t make_global_group(android_namespace_t* ns) { soinfo::soinfo_list_t global_group; - for (soinfo* si = somain; si != nullptr; si = si->next) { + ns->soinfo_list().for_each([&](soinfo* si) { if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { global_group.push_back(si); } - } + }); return global_group; } @@ -1690,7 +1888,8 @@ static void shuffle(std::vector* v) { // not their transitive dependencies) as children of the start_with library. // This is false when find_libraries is called for dlopen(), when newly loaded // libraries must form a disjoint tree. -static bool find_libraries(soinfo* start_with, +static bool find_libraries(android_namespace_t* ns, + soinfo* start_with, const char* const library_names[], size_t library_names_count, soinfo* soinfos[], std::vector* ld_preloads, @@ -1707,7 +1906,7 @@ static bool find_libraries(soinfo* start_with, } // Construct global_group. - soinfo::soinfo_list_t global_group = make_global_group(); + soinfo::soinfo_list_t global_group = make_global_group(ns); // If soinfos array is null allocate one on stack. // The array is needed in case of failure; for example @@ -1749,7 +1948,7 @@ static bool find_libraries(soinfo* start_with, bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children); task->set_extinfo(is_dt_needed ? nullptr : extinfo); - if(!find_library_internal(task, &zip_archive_cache, &load_tasks, rtld_flags)) { + if(!find_library_internal(ns, task, &zip_archive_cache, &load_tasks, rtld_flags)) { return false; } @@ -1850,14 +2049,15 @@ static bool find_libraries(soinfo* start_with, return linked; } -static soinfo* find_library(const char* name, int rtld_flags, +static soinfo* find_library(android_namespace_t* ns, + const char* name, int rtld_flags, const android_dlextinfo* extinfo, soinfo* needed_by) { soinfo* si; if (name == nullptr) { si = somain; - } else if (!find_libraries(needed_by, &name, 1, &si, nullptr, 0, rtld_flags, + } else if (!find_libraries(ns, needed_by, &name, 1, &si, nullptr, 0, rtld_flags, extinfo, /* add_as_children */ false)) { return nullptr; } @@ -1920,7 +2120,9 @@ static void soinfo_unload(soinfo* root) { TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->get_realpath(), library_name); - soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr, nullptr); + soinfo* needed = find_library(si->get_namespace(), + library_name, RTLD_NOLOAD, nullptr, nullptr); + if (needed != nullptr) { // Not found: for example if symlink was deleted between dlopen and dlclose // Since we cannot really handle errors at this point - print and continue. @@ -1992,6 +2194,9 @@ soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, DL_ERR("invalid flags to dlopen: %x", flags); return nullptr; } + + android_namespace_t* ns = caller->get_namespace(); + if (extinfo != nullptr) { if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) { DL_ERR("invalid extended flags to android_dlopen_ext: 0x%" PRIx64, extinfo->flags); @@ -2011,13 +2216,22 @@ soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, "compatible with ANDROID_DLEXT_RESERVED_ADDRESS/ANDROID_DLEXT_RESERVED_ADDRESS_HINT"); return nullptr; } + + if ((extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE) != 0) { + if (extinfo->library_namespace == nullptr) { + DL_ERR("ANDROID_DLEXT_USE_NAMESPACE is set but extinfo->library_namespace is null"); + return nullptr; + } + ns = extinfo->library_namespace; + } } ProtectedDataGuard guard; - soinfo* si = find_library(name, flags, extinfo, caller); + soinfo* si = find_library(ns, name, flags, extinfo, caller); if (si != nullptr) { si->call_constructors(); } + return si; } @@ -2026,6 +2240,67 @@ void do_dlclose(soinfo* si) { soinfo_unload(si); } +bool init_public_namespace(const char* libs) { + CHECK(libs != nullptr); + if (g_public_namespace_initialized) { + DL_ERR("Public namespace has already been initialized."); + return false; + } + + std::vector sonames = android::base::Split(libs, ":"); + + ProtectedDataGuard guard; + + auto failure_guard = make_scope_guard([&]() { + g_public_namespace.clear(); + }); + + soinfo* candidate; + for (const auto& soname : sonames) { + if (!find_loaded_library_by_soname(&g_default_namespace, soname.c_str(), &candidate)) { + DL_ERR("Error initializing public namespace: \"%s\" was not found" + " in the default namespace", soname.c_str()); + return false; + } + + candidate->set_nodelete(); + g_public_namespace.push_back(candidate); + } + + failure_guard.disable(); + g_public_namespace_initialized = true; + return true; +} + +android_namespace_t* create_namespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + bool is_isolated) { + if (!g_public_namespace_initialized) { + DL_ERR("Cannot create namespace: public namespace is not initialized."); + return nullptr; + } + + ProtectedDataGuard guard; + std::vector ld_library_paths; + std::vector default_library_paths; + + parse_path(ld_library_path, ":", &ld_library_paths); + parse_path(default_library_path, ":", &default_library_paths); + + android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t(); + ns->set_name(name); + ns->set_isolated(is_isolated); + ns->set_ld_library_paths(std::move(ld_library_paths)); + ns->set_default_library_paths(std::move(default_library_paths)); + + // TODO(dimtiry): Should this be global group of caller's namespace? + auto global_group = make_global_group(&g_default_namespace); + std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list())); + + return ns; +} + static ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) { typedef ElfW(Addr) (*ifunc_resolver_t)(void); ifunc_resolver_t ifunc_resolver = reinterpret_cast(resolver_addr); @@ -2694,6 +2969,10 @@ void soinfo::set_dt_flags_1(uint32_t dt_flags_1) { } } +void soinfo::set_nodelete() { + rtld_flags_ |= RTLD_NODELETE; +} + const char* soinfo::get_realpath() const { #if defined(__work_around_b_24465209__) if (has_min_version(2)) { @@ -2760,13 +3039,21 @@ soinfo::soinfo_list_t& soinfo::get_parents() { static std::vector g_empty_runpath; const std::vector& soinfo::get_dt_runpath() const { - if (has_min_version(2)) { + if (has_min_version(3)) { return dt_runpath_; } return g_empty_runpath; } +android_namespace_t* soinfo::get_namespace() { + if (has_min_version(3)) { + return namespace_; + } + + return &g_default_namespace; +} + ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const { if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { return call_ifunc_resolver(s->st_value + load_bias); @@ -3464,7 +3751,8 @@ static soinfo* linker_soinfo_for_gdb = nullptr; * be on the soinfo list. */ static void init_linker_info_for_gdb(ElfW(Addr) linker_base) { - linker_soinfo_for_gdb = new (linker_soinfo_for_gdb_buf) soinfo(LINKER_PATH, nullptr, 0, 0); + linker_soinfo_for_gdb = new (linker_soinfo_for_gdb_buf) soinfo(nullptr, LINKER_PATH, + nullptr, 0, 0); linker_soinfo_for_gdb->load_bias = linker_base; @@ -3481,14 +3769,25 @@ static void init_linker_info_for_gdb(ElfW(Addr) linker_base) { insert_soinfo_into_debug_map(linker_soinfo_for_gdb); } -static void init_default_ld_library_path() { +static void init_default_namespace() { + g_default_namespace.set_name("(default)"); + g_default_namespace.set_isolated(false); + const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum, somain->load_bias); const char* bname = basename(interp); - if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0)) + if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0)) { g_default_ld_paths = kAsanDefaultLdPaths; - else + } else { g_default_ld_paths = kDefaultLdPaths; + } + + std::vector ld_default_paths; + for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) { + ld_default_paths.push_back(g_default_ld_paths[i]); + } + + g_default_namespace.set_default_library_paths(std::move(ld_default_paths)); }; extern "C" int __system_properties_init(void); @@ -3529,7 +3828,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( INFO("[ android linker & debugger ]"); - soinfo* si = soinfo_alloc(args.argv[0], nullptr, 0, RTLD_GLOBAL); + soinfo* si = soinfo_alloc(&g_default_namespace, args.argv[0], nullptr, 0, RTLD_GLOBAL); if (si == nullptr) { exit(EXIT_FAILURE); } @@ -3581,11 +3880,10 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( somain = si; - init_default_ld_library_path(); + init_default_namespace(); if (!si->prelink_image()) { - __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer()); - exit(EXIT_FAILURE); + __libc_fatal("CANNOT LINK EXECUTABLE: %s", linker_get_error_buffer()); } // add somain to global group @@ -3613,15 +3911,13 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( needed_library_name_list.copy_to_array(needed_library_names, needed_libraries_count); if (needed_libraries_count > 0 && - !find_libraries(si, needed_library_names, needed_libraries_count, nullptr, - &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr, + !find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count, + nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr, /* add_as_children */ true)) { - __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer()); - exit(EXIT_FAILURE); + __libc_fatal("CANNOT LINK EXECUTABLE: %s", linker_get_error_buffer()); } else if (needed_libraries_count == 0) { if (!si->link_image(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr)) { - __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer()); - exit(EXIT_FAILURE); + __libc_fatal("CANNOT LINK EXECUTABLE: %s", linker_get_error_buffer()); } si->increment_ref_count(); } @@ -3730,7 +4026,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { ElfW(Ehdr)* elf_hdr = reinterpret_cast(linker_addr); ElfW(Phdr)* phdr = reinterpret_cast(linker_addr + elf_hdr->e_phoff); - soinfo linker_so(nullptr, nullptr, 0, 0); + soinfo linker_so(nullptr, nullptr, nullptr, 0, 0); // If the linker is not acting as PT_INTERP entry_point is equal to // _start. Which means that the linker is running as an executable and @@ -3739,7 +4035,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { // This happens when user tries to run 'adb shell /system/bin/linker' // see also https://code.google.com/p/android/issues/detail?id=63174 if (reinterpret_cast(&_start) == entry_point) { - __libc_fatal("This is %s, the helper program for shared library executables.\n", args.argv[0]); + __libc_fatal("This is %s, the helper program for shared library executables.", args.argv[0]); } linker_so.base = linker_addr; @@ -3757,15 +4053,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { // are not yet initialized, and therefore we cannot use linked_list.push_* // functions at this point. if (!(linker_so.prelink_image() && linker_so.link_image(g_empty_list, g_empty_list, nullptr))) { - // It would be nice to print an error message, but if the linker - // can't link itself, there's no guarantee that we'll be able to - // call write() (because it involves a GOT reference). We may as - // well try though... - const char* msg = "CANNOT LINK EXECUTABLE: "; - write(2, msg, strlen(msg)); - write(2, __linker_dl_err_buf, strlen(__linker_dl_err_buf)); - write(2, "\n", 1); - _exit(EXIT_FAILURE); + __libc_fatal("CANNOT LINK EXECUTABLE: %s", linker_get_error_buffer()); } __libc_init_main_thread(args); @@ -3781,6 +4069,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { // before get_libdl_info(). solist = get_libdl_info(); sonext = get_libdl_info(); + g_default_namespace.soinfo_list().push_back(get_libdl_info()); // We have successfully fixed our own relocations. It's safe to run // the main part of the linker now. diff --git a/linker/linker.h b/linker/linker.h index 2c988692f..c46b4e162 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -86,7 +86,7 @@ #define SUPPORTED_DT_FLAGS_1 (DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE) -#define SOINFO_VERSION 2 +#define SOINFO_VERSION 3 #if defined(__work_around_b_24465209__) #define SOINFO_NAME_LEN 128 @@ -261,7 +261,8 @@ struct soinfo { bool has_DT_SYMBOLIC; public: - soinfo(const char* name, const struct stat* file_stat, off64_t file_offset, int rtld_flags); + soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat, + off64_t file_offset, int rtld_flags); void call_constructors(); void call_destructors(); @@ -311,6 +312,7 @@ struct soinfo { void set_linked(); void set_linker_flag(); void set_main_executable(); + void set_nodelete(); void increment_ref_count(); size_t decrement_ref_count(); @@ -332,6 +334,7 @@ struct soinfo { void set_dt_runpath(const char *); const std::vector& get_dt_runpath() const; + android_namespace_t* get_namespace(); private: bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; @@ -394,7 +397,9 @@ struct soinfo { uint32_t target_sdk_version_; + // version >= 3 std::vector dt_runpath_; + android_namespace_t* namespace_; friend soinfo* get_libdl_info(); }; @@ -422,7 +427,9 @@ void do_dlclose(soinfo* si); int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data); -const ElfW(Sym)* dlsym_linear_lookup(const char* name, soinfo** found, soinfo* caller, void* handle); +const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, const char* name, soinfo** found, + soinfo* caller, void* handle); + soinfo* find_containing_library(const void* addr); const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name); @@ -437,4 +444,8 @@ size_t linker_get_error_buffer_size(); void set_application_target_sdk_version(uint32_t target); uint32_t get_application_target_sdk_version(); +bool init_public_namespace(const char* path); +android_namespace_t* create_namespace(const char* name, const char* ld_library_path, + const char* default_library_path, bool is_isolated); + #endif diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp index 5d39d835f..f81b77b23 100644 --- a/linker/linker_utils.cpp +++ b/linker/linker_utils.cpp @@ -20,7 +20,7 @@ bool normalize_path(const char* path, std::string* normalized_path) { // Input should be an absolute path if (path[0] != '/') { - PRINT("canonize_path - invalid input: '%s', the input path should be absolute", path); + PRINT("normalize_path - invalid input: '%s', the input path should be absolute", path); return false; } @@ -61,3 +61,47 @@ bool normalize_path(const char* path, std::string* normalized_path) { return true; } +bool file_is_in_dir(const std::string& file, const std::string& dir) { + const char* needle = dir.c_str(); + const char* haystack = file.c_str(); + size_t needle_len = strlen(needle); + + return (strncmp(haystack, needle, needle_len) == 0 && + haystack[needle_len] == '/' && + strchr(haystack + needle_len + 1, '/') == nullptr); +} + +const char* const kZipFileSeparator = "!/"; + +bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path) { + std::string normalized_path; + if (!normalize_path(input_path, &normalized_path)) { + return false; + } + + const char* const path = normalized_path.c_str(); + TRACE("Trying zip file open from path '%s' -> normalized '%s'", input_path, path); + + // Treat an '!/' separator 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* const separator = strstr(path, kZipFileSeparator); + if (separator == nullptr) { + return false; + } + + char buf[512]; + if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) { + PRINT("Warning: ignoring very long library path: %s", path); + return false; + } + + buf[separator - path] = '\0'; + + *zip_path = buf; + *entry_path = &buf[separator - path + 2]; + + return true; +} + diff --git a/linker/linker_utils.h b/linker/linker_utils.h index fc79fd193..b998fb5df 100644 --- a/linker/linker_utils.h +++ b/linker/linker_utils.h @@ -18,6 +18,10 @@ #include +extern const char* const kZipFileSeparator; + bool normalize_path(const char* path, std::string* normalized_path); +bool file_is_in_dir(const std::string& file, const std::string& dir); +bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path); #endif diff --git a/linker/tests/linker_utils_test.cpp b/linker/tests/linker_utils_test.cpp index 458474eed..d9b290cd6 100644 --- a/linker/tests/linker_utils_test.cpp +++ b/linker/tests/linker_utils_test.cpp @@ -43,3 +43,29 @@ TEST(linker_utils, normalize_path_smoke) { ASSERT_FALSE(normalize_path("root///dir/.///dir2/somedir/../zipfile!/dir/dir9//..///afile", &output)); ASSERT_EQ("unchanged", output); } + +TEST(linker_utils, file_is_in_dir_smoke) { + ASSERT_TRUE(file_is_in_dir("/foo/bar/file", "/foo/bar")); + ASSERT_FALSE(file_is_in_dir("/foo/bar/file", "/foo")); + + ASSERT_FALSE(file_is_in_dir("/foo/bar/file", "/bar/foo")); + + ASSERT_TRUE(file_is_in_dir("/file", "")); + ASSERT_FALSE(file_is_in_dir("/file", "/")); +} + +TEST(linker_utils, parse_zip_path_smoke) { + std::string zip_path; + std::string entry_path; + + ASSERT_FALSE(parse_zip_path("/not/a/zip/path/file.zip", &zip_path, &entry_path)); + ASSERT_FALSE(parse_zip_path("/not/a/zip/path/file.zip!path/in/zip", &zip_path, &entry_path)); + ASSERT_TRUE(parse_zip_path("/zip/path/file.zip!/path/in/zip", &zip_path, &entry_path)); + ASSERT_EQ("/zip/path/file.zip", zip_path); + ASSERT_EQ("path/in/zip", entry_path); + + ASSERT_TRUE(parse_zip_path("/zip/path/file2.zip!/", &zip_path, &entry_path)); + ASSERT_EQ("/zip/path/file2.zip", zip_path); + ASSERT_EQ("", entry_path); +} + diff --git a/tests/Android.build.mk b/tests/Android.build.mk index 7cac34912..740c2f4cb 100644 --- a/tests/Android.build.mk +++ b/tests/Android.build.mk @@ -28,9 +28,17 @@ ifneq ($(findstring LIBRARY, $(build_target)),LIBRARY) LOCAL_MODULE_STEM_32 := $(module)32 LOCAL_MODULE_STEM_64 := $(module)64 else + +ifneq ($($(module)_install_to_out_data_dir),) + $(module)_install_to_out_data := true +endif + ifeq ($($(module)_install_to_out_data),true) - LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$(module) - LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$(module) + ifeq ($($(module)_install_to_out_data_dir),) + $(module)_install_to_out_data_dir := $(module) + endif + LOCAL_MODULE_PATH_32 := $($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_DATA_NATIVE_TESTS)/$($(module)_install_to_out_data_dir) + LOCAL_MODULE_PATH_64 := $(TARGET_OUT_DATA_NATIVE_TESTS)/$($(module)_install_to_out_data_dir) endif endif diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index c7d4d462f..83bd5cc06 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -52,14 +52,14 @@ typedef int (*fn)(void); #define LIBSIZE 1024*1024 // how much address space to reserve for it #if defined(__LP64__) -#define LIBPATH_PREFIX "/nativetest64/" +#define NATIVE_TESTS_PATH "/nativetest64" #else -#define LIBPATH_PREFIX "/nativetest/" +#define NATIVE_TESTS_PATH "/nativetest" #endif -#define LIBPATH LIBPATH_PREFIX "libdlext_test_fd/libdlext_test_fd.so" -#define LIBZIPPATH LIBPATH_PREFIX "libdlext_test_zip/libdlext_test_zip_zipaligned.zip" -#define LIBZIPPATH_WITH_RUNPATH LIBPATH_PREFIX "libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip" +#define LIBPATH NATIVE_TESTS_PATH "/libdlext_test_fd/libdlext_test_fd.so" +#define LIBZIPPATH NATIVE_TESTS_PATH "/libdlext_test_zip/libdlext_test_zip_zipaligned.zip" +#define LIBZIPPATH_WITH_RUNPATH NATIVE_TESTS_PATH "/libdlext_test_runpath_zip/libdlext_test_runpath_zip_zipaligned.zip" #define LIBZIP_OFFSET PAGE_SIZE @@ -602,3 +602,192 @@ void DlExtRelroSharingTest::SpawnChildrenAndMeasurePss(const char* lib, bool sha ASSERT_EQ(0, WEXITSTATUS(status)); } } + +// Testing namespaces +static const char* g_public_lib = "libnstest_public.so"; + +TEST(dlext, ns_smoke) { + static const char* root_lib = "libnstest_root.so"; + std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib; + + ASSERT_FALSE(android_init_public_namespace(path.c_str())); + ASSERT_STREQ("android_init_public_namespace failed: Error initializing public namespace: " + "\"libnstest_public.so\" was not found in the default namespace", dlerror()); + + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH; + + void* handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW); + ASSERT_TRUE(handle_public != nullptr) << dlerror(); + + ASSERT_TRUE(android_init_public_namespace(path.c_str())) << dlerror(); + + // Check that libraries added to public namespace are NODELETE + dlclose(handle_public); + handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle_public != nullptr) << dlerror(); + + android_namespace_t* ns1 = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false); + ASSERT_TRUE(ns1 != nullptr) << dlerror(); + + android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true); + ASSERT_TRUE(ns2 != nullptr) << dlerror(); + + // This should not have affect search path for default namespace: + ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr); + void* handle = dlopen(g_public_lib, RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + dlclose(handle); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns1; + + void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle1 != nullptr) << dlerror(); + + extinfo.library_namespace = ns2; + void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle2 != nullptr) << dlerror(); + + ASSERT_TRUE(handle1 != handle2); + + typedef const char* (*fn_t)(); + + fn_t ns_get_local_string1 = reinterpret_cast(dlsym(handle1, "ns_get_local_string")); + ASSERT_TRUE(ns_get_local_string1 != nullptr) << dlerror(); + fn_t ns_get_local_string2 = reinterpret_cast(dlsym(handle2, "ns_get_local_string")); + ASSERT_TRUE(ns_get_local_string2 != nullptr) << dlerror(); + + EXPECT_STREQ("This string is local to root library", ns_get_local_string1()); + EXPECT_STREQ("This string is local to root library", ns_get_local_string2()); + + ASSERT_TRUE(ns_get_local_string1() != ns_get_local_string2()); + + fn_t ns_get_private_extern_string1 = + reinterpret_cast(dlsym(handle1, "ns_get_private_extern_string")); + ASSERT_TRUE(ns_get_private_extern_string1 != nullptr) << dlerror(); + fn_t ns_get_private_extern_string2 = + reinterpret_cast(dlsym(handle2, "ns_get_private_extern_string")); + ASSERT_TRUE(ns_get_private_extern_string2 != nullptr) << dlerror(); + + EXPECT_STREQ("This string is from private namespace", ns_get_private_extern_string1()); + EXPECT_STREQ("This string is from private namespace", ns_get_private_extern_string2()); + + ASSERT_TRUE(ns_get_private_extern_string1() != ns_get_private_extern_string2()); + + fn_t ns_get_public_extern_string1 = + reinterpret_cast(dlsym(handle1, "ns_get_public_extern_string")); + ASSERT_TRUE(ns_get_public_extern_string1 != nullptr) << dlerror(); + fn_t ns_get_public_extern_string2 = + reinterpret_cast(dlsym(handle2, "ns_get_public_extern_string")); + ASSERT_TRUE(ns_get_public_extern_string2 != nullptr) << dlerror(); + + EXPECT_STREQ("This string is from public namespace", ns_get_public_extern_string1()); + ASSERT_TRUE(ns_get_public_extern_string1() == ns_get_public_extern_string2()); + + // and now check that dlopen() does the right thing in terms of preserving namespace + fn_t ns_get_dlopened_string1 = reinterpret_cast(dlsym(handle1, "ns_get_dlopened_string")); + ASSERT_TRUE(ns_get_dlopened_string1 != nullptr) << dlerror(); + fn_t ns_get_dlopened_string2 = reinterpret_cast(dlsym(handle2, "ns_get_dlopened_string")); + ASSERT_TRUE(ns_get_dlopened_string2 != nullptr) << dlerror(); + + EXPECT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string1()); + EXPECT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string2()); + + ASSERT_TRUE(ns_get_dlopened_string1() != ns_get_dlopened_string2()); + + dlclose(handle1); + + // Check if handle2 is still alive (and well) + ASSERT_STREQ("This string is local to root library", ns_get_local_string2()); + ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string2()); + ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string2()); + ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string2()); + + dlclose(handle2); +} + +TEST(dlext, ns_isolated) { + static const char* root_lib = "libnstest_root_not_isolated.so"; + std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib; + + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH; + void* handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW); + ASSERT_TRUE(handle_public != nullptr) << dlerror(); + + ASSERT_TRUE(android_init_public_namespace(path.c_str())) << dlerror(); + + android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false); + ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror(); + + android_namespace_t* ns_isolated = android_create_namespace("private_isolated1", nullptr, (lib_path + "/private_namespace_libs").c_str(), true); + ASSERT_TRUE(ns_isolated != nullptr) << dlerror(); + + android_namespace_t* ns_isolated2 = android_create_namespace("private_isolated2", (lib_path + "/private_namespace_libs").c_str(), nullptr, true); + ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror(); + + ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr); + ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror()); + + std::string lib_private_external_path = + lib_path + "/private_namespace_libs_external/libnstest_private_external.so"; + + // Load lib_private_external_path to default namespace + // (it should remain invisible for the isolated namespaces after this) + void* handle = dlopen(lib_private_external_path.c_str(), RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns_not_isolated; + + void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle1 != nullptr) << dlerror(); + + extinfo.library_namespace = ns_isolated; + + void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle2 == nullptr); + ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror()); + + // Check dlopen by absolute path + handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo); + ASSERT_TRUE(handle2 == nullptr); + ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" not found", dlerror()); + + extinfo.library_namespace = ns_isolated2; + + handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle2 == nullptr); + ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror()); + + // Check dlopen by absolute path + handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo); + ASSERT_TRUE(handle2 == nullptr); + ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" not found", dlerror()); + + typedef const char* (*fn_t)(); + fn_t ns_get_local_string = reinterpret_cast(dlsym(handle1, "ns_get_local_string")); + ASSERT_TRUE(ns_get_local_string != nullptr) << dlerror(); + + ASSERT_STREQ("This string is local to root library", ns_get_local_string()); + + fn_t ns_get_private_extern_string = + reinterpret_cast(dlsym(handle1, "ns_get_private_extern_string")); + ASSERT_TRUE(ns_get_private_extern_string != nullptr) << dlerror(); + + ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string()); + + fn_t ns_get_public_extern_string = + reinterpret_cast(dlsym(handle1, "ns_get_public_extern_string")); + ASSERT_TRUE(ns_get_public_extern_string != nullptr) << dlerror(); + + ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string()); + + fn_t ns_get_dlopened_string = reinterpret_cast(dlsym(handle1, "ns_get_dlopened_string")); + ASSERT_TRUE(ns_get_dlopened_string != nullptr) << dlerror(); + + ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string()); + + dlclose(handle1); +} diff --git a/tests/libs/Android.build.linker_namespaces.mk b/tests/libs/Android.build.linker_namespaces.mk new file mode 100644 index 000000000..f91378057 --- /dev/null +++ b/tests/libs/Android.build.linker_namespaces.mk @@ -0,0 +1,84 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ----------------------------------------------------------------------------- +# This set of libraries are used to verify linker namespaces. +# ----------------------------------------------------------------------------- + +# ----------------------------------------------------------------------------- +# Test cases +# 1. Check that private libraries loaded in different namespaces are +# different. Check that dlsym does not confuse them. +# 2. Check that public libraries loaded in different namespaces are shared +# between them. +# 3. Check that namespace sticks on dlopen +# +# Dependency tree (visibility) +# libnstest_root.so (this should be local to the namespace) +# +-> libnstest_public.so +# +-> libnstest_private.so +# +# libnstest_dlopened.so (library in private namespace dlopened from libnstest_root.so) +# ----------------------------------------------------------------------------- +libnstest_root_src_files := namespaces_root.cpp +libnstest_root_shared_libraries := libnstest_public libnstest_private +libnstest_root_install_to_out_data_dir := private_namespace_libs +module := libnstest_root +include $(LOCAL_PATH)/Android.build.target.testlib.mk + +libnstest_public_src_files := namespaces_public.cpp +module := libnstest_public +libnstest_public_install_to_out_data_dir := public_namespace_libs +include $(LOCAL_PATH)/Android.build.target.testlib.mk + +libnstest_private_src_files := namespaces_private.cpp +libnstest_private_install_to_out_data_dir := private_namespace_libs +module := libnstest_private +include $(LOCAL_PATH)/Android.build.target.testlib.mk + +libnstest_dlopened_src_files := namespaces_dlopened.cpp +libnstest_dlopened_install_to_out_data_dir := private_namespace_libs +module := libnstest_dlopened +include $(LOCAL_PATH)/Android.build.target.testlib.mk + +# ----------------------------------------------------------------------------- +# This set of libraries is to test isolated namespaces +# +# Isolated namespaces do not allow loading of the library outside of +# the search paths. +# +# This library cannot be loaded in isolated namespace because one of DT_NEEDED +# libraries is outside of the search paths. +# +# libnstest_root_not_isolated.so (DT_RUNPATH = $ORIGIN/../private_namespace_libs_external/) +# +-> libnstest_public.so +# +-> libnstest_private_external.so (located in $ORIGIN/../private_namespace_libs_external/) +# +# Search path: $NATIVE_TESTS/private_namespace_libs/ +# ----------------------------------------------------------------------------- +libnstest_root_not_isolated_src_files := namespaces_root.cpp +libnstest_root_not_isolated_shared_libraries := libnstest_public libnstest_private_external +libnstest_root_not_isolated_install_to_out_data_dir := private_namespace_libs +libnstest_root_not_isolated_ldflags := -Wl,--rpath,\$$ORIGIN/../private_namespace_libs_external \ + -Wl,--enable-new-dtags + +module := libnstest_root_not_isolated +include $(LOCAL_PATH)/Android.build.target.testlib.mk + +libnstest_private_external_src_files := namespaces_private.cpp +libnstest_private_external_install_to_out_data_dir := private_namespace_libs_external +module := libnstest_private_external +include $(LOCAL_PATH)/Android.build.target.testlib.mk diff --git a/tests/libs/Android.build.target.testlib.mk b/tests/libs/Android.build.target.testlib.mk new file mode 100644 index 000000000..1e767c2c1 --- /dev/null +++ b/tests/libs/Android.build.target.testlib.mk @@ -0,0 +1,20 @@ +# +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +build_target := SHARED_LIBRARY +build_type := target +include $(TEST_PATH)/Android.build.mk + diff --git a/tests/libs/Android.mk b/tests/libs/Android.mk index 3391d79fe..93d95ee32 100644 --- a/tests/libs/Android.mk +++ b/tests/libs/Android.mk @@ -26,6 +26,7 @@ common_additional_dependencies := \ $(LOCAL_PATH)/Android.build.dlopen_check_order_dlsym.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_siblings.mk \ $(LOCAL_PATH)/Android.build.dlopen_check_order_reloc_main_executable.mk \ + $(LOCAL_PATH)/Android.build.linker_namespaces.mk \ $(LOCAL_PATH)/Android.build.pthread_atfork.mk \ $(LOCAL_PATH)/Android.build.testlib.mk \ $(LOCAL_PATH)/Android.build.versioned_lib.mk \ @@ -212,6 +213,11 @@ libtest_nodelete_dt_flags_1_ldflags := -Wl,-z,nodelete module := libtest_nodelete_dt_flags_1 include $(LOCAL_PATH)/Android.build.testlib.mk +# ----------------------------------------------------------------------------- +# Build test helper libraries for linker namespaces +# ----------------------------------------------------------------------------- +include $(LOCAL_PATH)/Android.build.linker_namespaces.mk + # ----------------------------------------------------------------------------- # Build DT_RUNPATH test helper libraries # ----------------------------------------------------------------------------- diff --git a/tests/libs/namespaces_dlopened.cpp b/tests/libs/namespaces_dlopened.cpp new file mode 100644 index 000000000..9d11689c4 --- /dev/null +++ b/tests/libs/namespaces_dlopened.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const char* g_private_dlopened_string = "This string is from private namespace " + "(dlopened library)"; + diff --git a/tests/libs/namespaces_private.cpp b/tests/libs/namespaces_private.cpp new file mode 100644 index 000000000..07cab70ce --- /dev/null +++ b/tests/libs/namespaces_private.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const char* g_private_extern_string = "This string is from private namespace"; + diff --git a/tests/libs/namespaces_public.cpp b/tests/libs/namespaces_public.cpp new file mode 100644 index 000000000..bb2a8de68 --- /dev/null +++ b/tests/libs/namespaces_public.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const char* g_public_extern_string = "This string is from public namespace"; + diff --git a/tests/libs/namespaces_root.cpp b/tests/libs/namespaces_root.cpp new file mode 100644 index 000000000..0bb4611e7 --- /dev/null +++ b/tests/libs/namespaces_root.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +static const char* g_local_string = "This string is local to root library"; +extern "C" const char* g_private_extern_string; +extern "C" const char* g_public_extern_string; + +bool g_dlopened = false; + +extern "C" const char* ns_get_local_string() { + return g_local_string; +} + +extern "C" const char* ns_get_private_extern_string() { + return g_private_extern_string; +} + +extern "C" const char* ns_get_public_extern_string() { + return g_public_extern_string; +} + +extern "C" const char* ns_get_dlopened_string() { + void* handle = dlopen("libnstest_dlopened.so", RTLD_NOW | RTLD_GLOBAL); + if (handle == nullptr) { + return nullptr; + } + + const char* result = *static_cast(dlsym(handle, "g_private_dlopened_string")); + if (result != nullptr) { + g_dlopened = true; + } + + return result; +}