diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index a3cad118d..85e91c394 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -225,78 +225,26 @@ static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 }; static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; #endif +// Defined as global because we do not yet have access +// to synchronization functions __cxa_guard_* needed +// to define statics inside functions. +static soinfo __libdl_info; + // This is used by the dynamic linker. Every process gets these symbols for free. -soinfo libdl_info = { - "libdl.so", +soinfo* get_libdl_info() { + if (__libdl_info.name[0] == '\0') { + // initialize + strncpy(__libdl_info.name, "libdl.so", sizeof(__libdl_info.name)); + __libdl_info.flags = FLAG_LINKED | FLAG_NEW_SOINFO; + __libdl_info.strtab = ANDROID_LIBDL_STRTAB; + __libdl_info.symtab = g_libdl_symtab; + __libdl_info.nbucket = sizeof(g_libdl_buckets)/sizeof(unsigned); + __libdl_info.nchain = sizeof(g_libdl_chains)/sizeof(unsigned); + __libdl_info.bucket = g_libdl_buckets; + __libdl_info.chain = g_libdl_chains; + __libdl_info.has_DT_SYMBOLIC = true; + } - .phdr = 0, - .phnum = 0, - .entry = 0, - .base = 0, - .size = 0, + return &__libdl_info; +} -#if !defined(__LP64__) - .unused1 = 0, -#endif - - .dynamic = 0, - -#if !defined(__LP64__) - .unused2 = 0, .unused3 = 0, -#endif - - .next = 0, - - .flags = FLAG_LINKED, - - .strtab = ANDROID_LIBDL_STRTAB, - .symtab = g_libdl_symtab, - - .nbucket = sizeof(g_libdl_buckets)/sizeof(unsigned), - .nchain = sizeof(g_libdl_chains)/sizeof(unsigned), - .bucket = g_libdl_buckets, - .chain = g_libdl_chains, - -#if defined(USE_RELA) - .plt_rela = 0, - .plt_rela_count = 0, - .rela = 0, - .rela_count = 0, -#else - .plt_got = 0, - .plt_rel = 0, - .plt_rel_count = 0, - .rel = 0, - .rel_count = 0, -#endif - - .preinit_array = 0, - .preinit_array_count = 0, - - .init_array = 0, - .init_array_count = 0, - - .fini_array = 0, - .fini_array_count = 0, - - .init_func = 0, - .fini_func = 0, - -#if defined(__arm__) - .ARM_exidx = 0, - .ARM_exidx_count = 0, -#elif defined(__mips__) - .mips_symtabno = 0, - .mips_local_gotno = 0, - .mips_gotsym = 0, -#endif - - .ref_count = 0, - { .l_addr = 0, .l_name = 0, .l_ld = 0, .l_next = 0, .l_prev = 0, }, - .constructors_called = false, - .load_bias = 0, -#if !defined(__LP64__) - .has_text_relocations = false, -#endif - .has_DT_SYMBOLIC = true, -}; diff --git a/linker/linked_list.h b/linker/linked_list.h new file mode 100644 index 000000000..a6bea6c8f --- /dev/null +++ b/linker/linked_list.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef __LINKED_LIST_H +#define __LINKED_LIST_H + +#include "private/bionic_macros.h" + +template +struct LinkedListEntry { + LinkedListEntry* next; + T* element; +}; + +/* + * Represents linked list of objects of type T + */ +template +class LinkedList { + public: + LinkedList() : head_(nullptr) {} + + void push_front(T* const element) { + LinkedListEntry* new_entry = Allocator::alloc(); + new_entry->next = head_; + new_entry->element = element; + head_ = new_entry; + } + + void clear() { + while (head_ != nullptr) { + LinkedListEntry* p = head_; + head_ = head_->next; + Allocator::free(p); + } + } + + template + void for_each(F&& action) { + for (LinkedListEntry* e = head_; e != nullptr; e = e->next) { + if (e->element != nullptr) { + action(e->element); + } + } + } + + template + void remove_if(F&& predicate) { + LinkedListEntry* e = head_; + for (LinkedListEntry* e = head_; e != nullptr; e = e->next) { + if (e->element != nullptr && predicate(e->element)) { + e->element = nullptr; + } + } + } + + private: + LinkedListEntry* head_; + DISALLOW_COPY_AND_ASSIGN(LinkedList); +}; + +#endif // __LINKED_LIST_H diff --git a/linker/linker.cpp b/linker/linker.cpp index 86204de5d..c5006e05d 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -68,13 +68,11 @@ static bool soinfo_link_image(soinfo* si, const android_dlextinfo* extinfo); static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf); -// We can't use malloc(3) in the dynamic linker. We use a linked list of anonymous -// maps, each a single page in size. The pages are broken up into as many struct soinfo -// objects as will fit. static LinkerAllocator g_soinfo_allocator; +static LinkerAllocator> g_soinfo_links_allocator; -static soinfo* solist = &libdl_info; -static soinfo* sonext = &libdl_info; +static soinfo* solist; +static soinfo* sonext; static soinfo* somain; /* main process, always the one after libdl_info */ static const char* const kDefaultLdPaths[] = { @@ -263,7 +261,20 @@ void notify_gdb_of_libraries() { rtld_db_dlactivity(); } -static soinfo* soinfo_alloc(const char* name) { +LinkedListEntry* SoinfoListAllocator::alloc() { + return g_soinfo_links_allocator.alloc(); +} + +void SoinfoListAllocator::free(LinkedListEntry* entry) { + g_soinfo_links_allocator.free(entry); +} + +static void protect_data(int protection) { + g_soinfo_allocator.protect_all(protection); + g_soinfo_links_allocator.protect_all(protection); +} + +static soinfo* soinfo_alloc(const char* name, struct stat* file_stat) { if (strlen(name) >= SOINFO_NAME_LEN) { DL_ERR("library name \"%s\" too long", name); return NULL; @@ -274,6 +285,13 @@ static soinfo* soinfo_alloc(const char* name) { // Initialize the new element. memset(si, 0, sizeof(soinfo)); strlcpy(si->name, name, sizeof(si->name)); + si->flags = FLAG_NEW_SOINFO; + + if (file_stat != NULL) { + si->set_st_dev(file_stat->st_dev); + si->set_st_ino(file_stat->st_ino); + } + sonext->next = si; sonext = si; @@ -286,6 +304,10 @@ static void soinfo_free(soinfo* si) { return; } + if (si->base != 0 && si->size != 0) { + munmap(reinterpret_cast(si->base), si->size); + } + soinfo *prev = NULL, *trav; TRACE("name %s: freeing soinfo @ %p", si->name, si); @@ -301,6 +323,9 @@ static void soinfo_free(soinfo* si) { return; } + // clear links to/from si + si->remove_all_links(); + /* prev will never be NULL, because the first entry in solist is always the static libdl_info. */ @@ -651,25 +676,52 @@ static soinfo* load_library(const char* name, const android_dlextinfo* extinfo) return NULL; } - // Read the ELF header and load the segments. ElfReader elf_reader(name, fd); + + struct stat file_stat; + if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) { + DL_ERR("unable to stat file for the library %s: %s", name, strerror(errno)); + return NULL; + } + + // Check for symlink and other situations where + // file can have different names. + for (soinfo* si = solist; si != NULL; 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) { + TRACE("library \"%s\" is already loaded under different name/path \"%s\" - will return existing soinfo", name, si->name); + return si; + } + } + + // Read the ELF header and load the segments. if (!elf_reader.Load(extinfo)) { return NULL; } const char* bname = strrchr(name, '/'); - soinfo* si = soinfo_alloc(bname ? bname + 1 : name); + soinfo* si = soinfo_alloc(bname ? bname + 1 : name, &file_stat); if (si == NULL) { return NULL; } si->base = elf_reader.load_start(); si->size = elf_reader.load_size(); si->load_bias = elf_reader.load_bias(); - si->flags = 0; - si->entry = 0; - si->dynamic = NULL; si->phnum = elf_reader.phdr_count(); si->phdr = elf_reader.loaded_phdr(); + + // At this point we know that whatever is loaded @ base is a valid ELF + // shared library whose segments are properly mapped in. + TRACE("[ find_library_internal base=%p size=%zu name='%s' ]", + reinterpret_cast(si->base), si->size, si->name); + + if (!soinfo_link_image(si, extinfo)) { + soinfo_free(si); + return NULL; + } + return si; } @@ -703,23 +755,7 @@ static soinfo* find_library_internal(const char* name, const android_dlextinfo* } TRACE("[ '%s' has not been loaded yet. Locating...]", name); - si = load_library(name, extinfo); - if (si == NULL) { - return NULL; - } - - // At this point we know that whatever is loaded @ base is a valid ELF - // shared library whose segments are properly mapped in. - TRACE("[ find_library_internal base=%p size=%zu name='%s' ]", - reinterpret_cast(si->base), si->size, si->name); - - if (!soinfo_link_image(si, extinfo)) { - munmap(reinterpret_cast(si->base), si->size); - soinfo_free(si); - return NULL; - } - - return si; + return load_library(name, extinfo); } static soinfo* find_library(const char* name, const android_dlextinfo* extinfo) { @@ -735,15 +771,21 @@ static int soinfo_unload(soinfo* si) { TRACE("unloading '%s'", si->name); si->CallDestructors(); - for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { - if (d->d_tag == DT_NEEDED) { - const char* library_name = si->strtab + d->d_un.d_val; - TRACE("%s needs to unload %s", si->name, library_name); - soinfo_unload(find_loaded_library(library_name)); + if ((si->flags | FLAG_NEW_SOINFO) != 0) { + si->get_children().for_each([&] (soinfo* child) { + TRACE("%s needs to unload %s", si->name, child->name); + soinfo_unload(child); + }); + } else { + for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_NEEDED) { + const char* library_name = si->strtab + d->d_un.d_val; + TRACE("%s needs to unload %s", si->name, library_name); + soinfo_unload(find_loaded_library(library_name)); + } } } - munmap(reinterpret_cast(si->base), si->size); notify_gdb_of_unload(si); si->ref_count = 0; soinfo_free(si); @@ -773,19 +815,20 @@ soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo) DL_ERR("invalid extended flags to android_dlopen_ext: %x", extinfo->flags); return NULL; } - g_soinfo_allocator.protect_all(PROT_READ | PROT_WRITE); + protect_data(PROT_READ | PROT_WRITE); soinfo* si = find_library(name, extinfo); if (si != NULL) { si->CallConstructors(); + somain->add_child(si); } - g_soinfo_allocator.protect_all(PROT_READ); + protect_data(PROT_READ); return si; } int do_dlclose(soinfo* si) { - g_soinfo_allocator.protect_all(PROT_READ | PROT_WRITE); + protect_data(PROT_READ | PROT_WRITE); int result = soinfo_unload(si); - g_soinfo_allocator.protect_all(PROT_READ); + protect_data(PROT_READ); return result; } @@ -1333,7 +1376,7 @@ void soinfo::CallFunction(const char* function_name __unused, linker_function_t // The function may have called dlopen(3) or dlclose(3), so we need to ensure our data structures // are still writable. This happens with our debug malloc (see http://b/7941716). - g_soinfo_allocator.protect_all(PROT_READ | PROT_WRITE); + protect_data(PROT_READ | PROT_WRITE); } void soinfo::CallPreInitConstructors() { @@ -1365,15 +1408,9 @@ void soinfo::CallConstructors() { name, preinit_array_count); } - if (dynamic != NULL) { - for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { - if (d->d_tag == DT_NEEDED) { - const char* library_name = strtab + d->d_un.d_val; - TRACE("\"%s\": calling constructors in DT_NEEDED \"%s\"", name, library_name); - find_loaded_library(library_name)->CallConstructors(); - } - } - } + get_children().for_each([] (soinfo* si) { + si->CallConstructors(); + }); TRACE("\"%s\": calling constructors", name); @@ -1392,6 +1429,82 @@ void soinfo::CallDestructors() { CallFunction("DT_FINI", fini_func); } +void soinfo::add_child(soinfo* child) { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return; + } + + this->children.push_front(child); + child->parents.push_front(this); +} + +void soinfo::remove_all_links() { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return; + } + + // 1. Untie connected soinfos from 'this'. + children.for_each([&] (soinfo* child) { + child->parents.remove_if([&] (const soinfo* parent) { + return parent == this; + }); + }); + + parents.for_each([&] (soinfo* parent) { + parent->children.for_each([&] (const soinfo* child) { + return child == this; + }); + }); + + // 2. Once everything untied - clear local lists. + parents.clear(); + children.clear(); +} + +void soinfo::set_st_dev(dev_t dev) { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return; + } + + st_dev = dev; +} + +void soinfo::set_st_ino(ino_t ino) { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return; + } + + st_ino = ino; +} + +dev_t soinfo::get_st_dev() { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return 0; + } + + return st_dev; +}; + +ino_t soinfo::get_st_ino() { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return 0; + } + + return st_ino; +} + +// This is a return on get_children() in case +// 'this->flags' does not have FLAG_NEW_SOINFO set. +static soinfo::soinfo_list_t g_empty_list; + +soinfo::soinfo_list_t& soinfo::get_children() { + if ((this->flags & FLAG_NEW_SOINFO) == 0) { + return g_empty_list; + } + + return this->children; +} + /* Force any of the closed stdin, stdout and stderr to be associated with /dev/null. */ static int nullify_closed_stdio() { @@ -1715,6 +1828,8 @@ static bool soinfo_link_image(soinfo* si, const android_dlextinfo* extinfo) { library_name, si->name, tmp_err_buf); return false; } + + si->add_child(lsi); *pneeded++ = lsi; } } @@ -1824,19 +1939,52 @@ static void add_vdso(KernelArgumentBlock& args __unused) { return; } - soinfo* si = soinfo_alloc("[vdso]"); + soinfo* si = soinfo_alloc("[vdso]", NULL); si->phdr = reinterpret_cast(reinterpret_cast(ehdr_vdso) + ehdr_vdso->e_phoff); si->phnum = ehdr_vdso->e_phnum; si->base = reinterpret_cast(ehdr_vdso); si->size = phdr_table_get_load_size(si->phdr, si->phnum); - si->flags = 0; si->load_bias = get_elf_exec_load_bias(ehdr_vdso); soinfo_link_image(si, NULL); #endif } +/* + * This is linker soinfo for GDB. See details below. + */ +static soinfo linker_soinfo_for_gdb; + +/* gdb expects the linker to be in the debug shared object list. + * Without this, gdb has trouble locating the linker's ".text" + * and ".plt" sections. Gdb could also potentially use this to + * relocate the offset of our exported 'rtld_db_dlactivity' symbol. + * Don't use soinfo_alloc(), because the linker shouldn't + * be on the soinfo list. + */ +static void init_linker_info_for_gdb(ElfW(Addr) linker_base) { +#if defined(__LP64__) + strlcpy(linker_soinfo_for_gdb.name, "/system/bin/linker64", sizeof(linker_soinfo_for_gdb.name)); +#else + strlcpy(linker_soinfo_for_gdb.name, "/system/bin/linker", sizeof(linker_soinfo_for_gdb.name)); +#endif + linker_soinfo_for_gdb.flags = FLAG_NEW_SOINFO; + linker_soinfo_for_gdb.base = linker_base; + + /* + * Set the dynamic field in the link map otherwise gdb will complain with + * the following: + * warning: .dynamic section for "/system/bin/linker" is not at the + * expected address (wrong library or version mismatch?) + */ + ElfW(Ehdr)* elf_hdr = reinterpret_cast(linker_base); + ElfW(Phdr)* phdr = reinterpret_cast(linker_base + elf_hdr->e_phoff); + phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base, + &linker_soinfo_for_gdb.dynamic, NULL, NULL); + insert_soinfo_into_debug_map(&linker_soinfo_for_gdb); +} + /* * This code is called after the linker has linked itself and * fixed it's own GOT. It is safe to make references to externs @@ -1886,12 +2034,13 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( // Linker does not call constructors for its own // global variables so we need to initialize - // the allocator explicitly. + // the allocators explicitly. g_soinfo_allocator.init(); + g_soinfo_links_allocator.init(); INFO("[ android linker & debugger ]"); - soinfo* si = soinfo_alloc(args.argv[0]); + soinfo* si = soinfo_alloc(args.argv[0], NULL); if (si == NULL) { exit(EXIT_FAILURE); } @@ -1908,35 +2057,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( _r_debug.r_map = map; r_debug_tail = map; - /* gdb expects the linker to be in the debug shared object list. - * Without this, gdb has trouble locating the linker's ".text" - * and ".plt" sections. Gdb could also potentially use this to - * relocate the offset of our exported 'rtld_db_dlactivity' symbol. - * Don't use soinfo_alloc(), because the linker shouldn't - * be on the soinfo list. - */ - { - static soinfo linker_soinfo; -#if defined(__LP64__) - strlcpy(linker_soinfo.name, "/system/bin/linker64", sizeof(linker_soinfo.name)); -#else - strlcpy(linker_soinfo.name, "/system/bin/linker", sizeof(linker_soinfo.name)); -#endif - linker_soinfo.flags = 0; - linker_soinfo.base = linker_base; - - /* - * Set the dynamic field in the link map otherwise gdb will complain with - * the following: - * warning: .dynamic section for "/system/bin/linker" is not at the - * expected address (wrong library or version mismatch?) - */ - ElfW(Ehdr)* elf_hdr = reinterpret_cast(linker_base); - ElfW(Phdr)* phdr = reinterpret_cast(linker_base + elf_hdr->e_phoff); - phdr_table_get_dynamic_section(phdr, elf_hdr->e_phnum, linker_base, - &linker_soinfo.dynamic, NULL, NULL); - insert_soinfo_into_debug_map(&linker_soinfo); - } + init_linker_info_for_gdb(linker_base); // Extract information passed from the kernel. si->phdr = reinterpret_cast(args.getauxval(AT_PHDR)); @@ -2071,6 +2192,10 @@ static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf) { * function, or other GOT reference will generate a segfault. */ extern "C" ElfW(Addr) __linker_init(void* raw_args) { + // Initialize static variables. + solist = get_libdl_info(); + sonext = get_libdl_info(); + KernelArgumentBlock args(raw_args); ElfW(Addr) linker_addr = args.getauxval(AT_BASE); @@ -2106,7 +2231,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { args.abort_message_ptr = &g_abort_message; ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr); - g_soinfo_allocator.protect_all(PROT_READ); + protect_data(PROT_READ); // Return the address that the calling assembly stub should jump to. return start_address; diff --git a/linker/linker.h b/linker/linker.h index 645498a84..e5aca6e50 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -33,8 +33,10 @@ #include #include #include +#include #include "private/libc_logging.h" +#include "linked_list.h" #define DL_ERR(fmt, x...) \ do { \ @@ -84,6 +86,7 @@ #define FLAG_LINKED 0x00000001 #define FLAG_EXE 0x00000004 // The main executable #define FLAG_LINKER 0x00000010 // The linker itself +#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format #define SOINFO_NAME_LEN 128 @@ -94,7 +97,20 @@ typedef void (*linker_function_t)(); #define USE_RELA 1 #endif +struct soinfo; + +class SoinfoListAllocator { +public: + static LinkedListEntry* alloc(); + static void free(LinkedListEntry* entry); +private: + // unconstructable + DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator); +}; + struct soinfo { + public: + typedef LinkedList soinfo_list_t; public: char name[SOINFO_NAME_LEN]; const ElfW(Phdr)* phdr; @@ -179,17 +195,39 @@ struct soinfo { bool has_text_relocations; #endif bool has_DT_SYMBOLIC; - void CallConstructors(); void CallDestructors(); void CallPreInitConstructors(); + void add_child(soinfo* child); + void remove_all_links(); + + void set_st_dev(dev_t st_dev); + void set_st_ino(ino_t st_ino); + ino_t get_st_ino(); + dev_t get_st_dev(); + + soinfo_list_t& get_children(); + private: void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); void CallFunction(const char* function_name, linker_function_t function); + + private: + // This part of the structure is only available + // when FLAG_NEW_SOINFO is set in this->flags. + unsigned int version; + + dev_t st_dev; + ino_t st_ino; + + // dependency graph + soinfo_list_t children; + soinfo_list_t parents; + }; -extern soinfo libdl_info; +extern soinfo* get_libdl_info(); void do_android_get_LD_LIBRARY_PATH(char*, size_t); void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); diff --git a/linker/linker_allocator.cpp b/linker/linker_allocator.cpp index 573809075..c8b97b121 100644 --- a/linker/linker_allocator.cpp +++ b/linker/linker_allocator.cpp @@ -55,8 +55,7 @@ void* LinkerBlockAllocator::alloc() { free_block_list_ = block_info->next_block; } - block_info->next_block = nullptr; - block_info->num_free_blocks = 0; + memset(block_info, 0, block_size_); return block_info; } @@ -78,6 +77,8 @@ void LinkerBlockAllocator::free(void* block) { abort(); } + memset(block, 0, block_size_); + FreeBlockInfo* block_info = reinterpret_cast(block); block_info->next_block = free_block_list_; @@ -100,6 +101,7 @@ void LinkerBlockAllocator::create_new_page() { if (page == MAP_FAILED) { abort(); // oom } + memset(page, 0, PAGE_SIZE); FreeBlockInfo* first_block = reinterpret_cast(page->bytes); first_block->next_block = free_block_list_; diff --git a/linker/linker_allocator.h b/linker/linker_allocator.h index 3af99da76..fbf58fec3 100644 --- a/linker/linker_allocator.h +++ b/linker/linker_allocator.h @@ -51,6 +51,8 @@ class LinkerBlockAllocator { }; /* + * We can't use malloc(3) in the dynamic linker. + * * A simple allocator for the dynamic linker. An allocator allocates instances * of a single fixed-size type. Allocations are backed by page-sized private * anonymous mmaps. diff --git a/linker/tests/Android.mk b/linker/tests/Android.mk index 600fe6910..831cfcb96 100644 --- a/linker/tests/Android.mk +++ b/linker/tests/Android.mk @@ -30,6 +30,7 @@ LOCAL_CFLAGS += -g -Wall -Wextra -Werror -std=gnu++11 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../libc/ LOCAL_SRC_FILES := \ + linked_list_test.cpp \ linker_allocator_test.cpp \ ../linker_allocator.cpp diff --git a/linker/tests/linked_list_test.cpp b/linker/tests/linked_list_test.cpp new file mode 100644 index 000000000..31ec7d505 --- /dev/null +++ b/linker/tests/linked_list_test.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2013 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 +#include +#include + +#include + +#include "../linked_list.h" + +namespace { + +bool alloc_called = false; +bool free_called = false; + +class LinkedListTestAllocator { + public: + typedef LinkedListEntry entry_t; + + static entry_t* alloc() { + alloc_called = true; + return reinterpret_cast(::malloc(sizeof(entry_t))); + } + + static void free(entry_t* p) { + free_called = true; + ::free(p); + } + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(LinkedListTestAllocator); +}; + +typedef LinkedList test_list_t; + +std::string test_list_to_string(test_list_t& list) { + std::stringstream ss; + list.for_each([&] (const char* c) { + ss << c; + }); + + return ss.str(); +} + +}; + +TEST(linked_list, simple) { + alloc_called = free_called = false; + test_list_t list; + ASSERT_EQ("", test_list_to_string(list)); + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(!free_called); + list.push_front("a"); + ASSERT_TRUE(alloc_called); + ASSERT_TRUE(!free_called); + ASSERT_EQ("a", test_list_to_string(list)); + list.push_front("b"); + ASSERT_EQ("ba", test_list_to_string(list)); + list.push_front("c"); + list.push_front("d"); + ASSERT_EQ("dcba", test_list_to_string(list)); + ASSERT_TRUE(alloc_called); + ASSERT_TRUE(!free_called); + alloc_called = free_called = false; + list.remove_if([] (const char* c) { + return *c == 'c'; + }); + + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(!free_called); + + ASSERT_EQ("dba", test_list_to_string(list)); + alloc_called = free_called = false; + list.remove_if([] (const char* c) { + return *c == '2'; + }); + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(!free_called); + ASSERT_EQ("dba", test_list_to_string(list)); + list.clear(); + ASSERT_TRUE(!alloc_called); + ASSERT_TRUE(free_called); + ASSERT_EQ("", test_list_to_string(list)); +} diff --git a/tests/Android.mk b/tests/Android.mk index 4ad07ba64..1e4900f08 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -227,6 +227,19 @@ $(libdlext_sym): $(libdlext_origin) ALL_MODULES := \ $(ALL_MODULES) $(libdlext_sym) +ifneq ($(TARGET_2ND_ARCH),) +# link 64 bit .so +libdlext_origin := $(TARGET_OUT)/lib64/libdlext_test.so +libdlext_sym := $(subst libdlext_test,libdlext_test_v2,$(libdlext_origin)) +$(libdlext_sym): $(libdlext_origin) + @echo "Symlink: $@ -> $(notdir $<)" + @mkdir -p $(dir $@) + $(hide) ln -sf $(notdir $<) $@ + +ALL_MODULES := \ + $(ALL_MODULES) $(libdlext_sym) +endif + libdlext_test_norelro_src_files := \ dlext_test_library.cpp \