Merge "Improve detection of already loaded libraries"

This commit is contained in:
Dmitriy Ivanov 2014-05-15 21:55:23 +00:00 committed by Gerrit Code Review
commit 86bc436e20
9 changed files with 459 additions and 158 deletions

View file

@ -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 }; static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
#endif #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. // This is used by the dynamic linker. Every process gets these symbols for free.
soinfo libdl_info = { soinfo* get_libdl_info() {
"libdl.so", 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, return &__libdl_info;
.phnum = 0, }
.entry = 0,
.base = 0,
.size = 0,
#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,
};

75
linker/linked_list.h Normal file
View file

@ -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<typename T>
struct LinkedListEntry {
LinkedListEntry<T>* next;
T* element;
};
/*
* Represents linked list of objects of type T
*/
template<typename T, typename Allocator>
class LinkedList {
public:
LinkedList() : head_(nullptr) {}
void push_front(T* const element) {
LinkedListEntry<T>* new_entry = Allocator::alloc();
new_entry->next = head_;
new_entry->element = element;
head_ = new_entry;
}
void clear() {
while (head_ != nullptr) {
LinkedListEntry<T>* p = head_;
head_ = head_->next;
Allocator::free(p);
}
}
template<typename F>
void for_each(F&& action) {
for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
if (e->element != nullptr) {
action(e->element);
}
}
}
template<typename F>
void remove_if(F&& predicate) {
LinkedListEntry<T>* e = head_;
for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) {
if (e->element != nullptr && predicate(e->element)) {
e->element = nullptr;
}
}
}
private:
LinkedListEntry<T>* head_;
DISALLOW_COPY_AND_ASSIGN(LinkedList);
};
#endif // __LINKED_LIST_H

View file

@ -68,13 +68,11 @@
static bool soinfo_link_image(soinfo* si, const android_dlextinfo* extinfo); static bool soinfo_link_image(soinfo* si, const android_dlextinfo* extinfo);
static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf); 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<soinfo> g_soinfo_allocator; static LinkerAllocator<soinfo> g_soinfo_allocator;
static LinkerAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator;
static soinfo* solist = &libdl_info; static soinfo* solist;
static soinfo* sonext = &libdl_info; static soinfo* sonext;
static soinfo* somain; /* main process, always the one after libdl_info */ static soinfo* somain; /* main process, always the one after libdl_info */
static const char* const kDefaultLdPaths[] = { static const char* const kDefaultLdPaths[] = {
@ -263,7 +261,20 @@ void notify_gdb_of_libraries() {
rtld_db_dlactivity(); rtld_db_dlactivity();
} }
static soinfo* soinfo_alloc(const char* name) { LinkedListEntry<soinfo>* SoinfoListAllocator::alloc() {
return g_soinfo_links_allocator.alloc();
}
void SoinfoListAllocator::free(LinkedListEntry<soinfo>* 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) { if (strlen(name) >= SOINFO_NAME_LEN) {
DL_ERR("library name \"%s\" too long", name); DL_ERR("library name \"%s\" too long", name);
return NULL; return NULL;
@ -274,6 +285,13 @@ static soinfo* soinfo_alloc(const char* name) {
// Initialize the new element. // Initialize the new element.
memset(si, 0, sizeof(soinfo)); memset(si, 0, sizeof(soinfo));
strlcpy(si->name, name, sizeof(si->name)); 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->next = si;
sonext = si; sonext = si;
@ -286,6 +304,10 @@ static void soinfo_free(soinfo* si) {
return; return;
} }
if (si->base != 0 && si->size != 0) {
munmap(reinterpret_cast<void*>(si->base), si->size);
}
soinfo *prev = NULL, *trav; soinfo *prev = NULL, *trav;
TRACE("name %s: freeing soinfo @ %p", si->name, si); TRACE("name %s: freeing soinfo @ %p", si->name, si);
@ -301,6 +323,9 @@ static void soinfo_free(soinfo* si) {
return; return;
} }
// clear links to/from si
si->remove_all_links();
/* prev will never be NULL, because the first entry in solist is /* prev will never be NULL, because the first entry in solist is
always the static libdl_info. always the static libdl_info.
*/ */
@ -651,25 +676,52 @@ static soinfo* load_library(const char* name, const android_dlextinfo* extinfo)
return NULL; return NULL;
} }
// Read the ELF header and load the segments.
ElfReader elf_reader(name, fd); 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)) { if (!elf_reader.Load(extinfo)) {
return NULL; return NULL;
} }
const char* bname = strrchr(name, '/'); 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) { if (si == NULL) {
return NULL; return NULL;
} }
si->base = elf_reader.load_start(); si->base = elf_reader.load_start();
si->size = elf_reader.load_size(); si->size = elf_reader.load_size();
si->load_bias = elf_reader.load_bias(); si->load_bias = elf_reader.load_bias();
si->flags = 0;
si->entry = 0;
si->dynamic = NULL;
si->phnum = elf_reader.phdr_count(); si->phnum = elf_reader.phdr_count();
si->phdr = elf_reader.loaded_phdr(); 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<void*>(si->base), si->size, si->name);
if (!soinfo_link_image(si, extinfo)) {
soinfo_free(si);
return NULL;
}
return si; 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); TRACE("[ '%s' has not been loaded yet. Locating...]", name);
si = load_library(name, extinfo); return 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<void*>(si->base), si->size, si->name);
if (!soinfo_link_image(si, extinfo)) {
munmap(reinterpret_cast<void*>(si->base), si->size);
soinfo_free(si);
return NULL;
}
return si;
} }
static soinfo* find_library(const char* name, const android_dlextinfo* 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); TRACE("unloading '%s'", si->name);
si->CallDestructors(); si->CallDestructors();
for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { if ((si->flags | FLAG_NEW_SOINFO) != 0) {
if (d->d_tag == DT_NEEDED) { si->get_children().for_each([&] (soinfo* child) {
const char* library_name = si->strtab + d->d_un.d_val; TRACE("%s needs to unload %s", si->name, child->name);
TRACE("%s needs to unload %s", si->name, library_name); soinfo_unload(child);
soinfo_unload(find_loaded_library(library_name)); });
} 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<void*>(si->base), si->size);
notify_gdb_of_unload(si); notify_gdb_of_unload(si);
si->ref_count = 0; si->ref_count = 0;
soinfo_free(si); 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); DL_ERR("invalid extended flags to android_dlopen_ext: %x", extinfo->flags);
return NULL; return NULL;
} }
g_soinfo_allocator.protect_all(PROT_READ | PROT_WRITE); protect_data(PROT_READ | PROT_WRITE);
soinfo* si = find_library(name, extinfo); soinfo* si = find_library(name, extinfo);
if (si != NULL) { if (si != NULL) {
si->CallConstructors(); si->CallConstructors();
somain->add_child(si);
} }
g_soinfo_allocator.protect_all(PROT_READ); protect_data(PROT_READ);
return si; return si;
} }
int do_dlclose(soinfo* 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); int result = soinfo_unload(si);
g_soinfo_allocator.protect_all(PROT_READ); protect_data(PROT_READ);
return result; 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 // 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). // 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() { void soinfo::CallPreInitConstructors() {
@ -1365,15 +1408,9 @@ void soinfo::CallConstructors() {
name, preinit_array_count); name, preinit_array_count);
} }
if (dynamic != NULL) { get_children().for_each([] (soinfo* si) {
for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { si->CallConstructors();
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();
}
}
}
TRACE("\"%s\": calling constructors", name); TRACE("\"%s\": calling constructors", name);
@ -1392,6 +1429,82 @@ void soinfo::CallDestructors() {
CallFunction("DT_FINI", fini_func); 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 /* Force any of the closed stdin, stdout and stderr to be associated with
/dev/null. */ /dev/null. */
static int nullify_closed_stdio() { 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); library_name, si->name, tmp_err_buf);
return false; return false;
} }
si->add_child(lsi);
*pneeded++ = lsi; *pneeded++ = lsi;
} }
} }
@ -1824,19 +1939,52 @@ static void add_vdso(KernelArgumentBlock& args __unused) {
return; return;
} }
soinfo* si = soinfo_alloc("[vdso]"); soinfo* si = soinfo_alloc("[vdso]", NULL);
si->phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff); si->phdr = reinterpret_cast<ElfW(Phdr)*>(reinterpret_cast<char*>(ehdr_vdso) + ehdr_vdso->e_phoff);
si->phnum = ehdr_vdso->e_phnum; si->phnum = ehdr_vdso->e_phnum;
si->base = reinterpret_cast<ElfW(Addr)>(ehdr_vdso); si->base = reinterpret_cast<ElfW(Addr)>(ehdr_vdso);
si->size = phdr_table_get_load_size(si->phdr, si->phnum); si->size = phdr_table_get_load_size(si->phdr, si->phnum);
si->flags = 0;
si->load_bias = get_elf_exec_load_bias(ehdr_vdso); si->load_bias = get_elf_exec_load_bias(ehdr_vdso);
soinfo_link_image(si, NULL); soinfo_link_image(si, NULL);
#endif #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<ElfW(Ehdr)*>(linker_base);
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(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 * This code is called after the linker has linked itself and
* fixed it's own GOT. It is safe to make references to externs * 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 // Linker does not call constructors for its own
// global variables so we need to initialize // global variables so we need to initialize
// the allocator explicitly. // the allocators explicitly.
g_soinfo_allocator.init(); g_soinfo_allocator.init();
g_soinfo_links_allocator.init();
INFO("[ android linker & debugger ]"); INFO("[ android linker & debugger ]");
soinfo* si = soinfo_alloc(args.argv[0]); soinfo* si = soinfo_alloc(args.argv[0], NULL);
if (si == NULL) { if (si == NULL) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@ -1908,35 +2057,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
_r_debug.r_map = map; _r_debug.r_map = map;
r_debug_tail = map; r_debug_tail = map;
/* gdb expects the linker to be in the debug shared object list. init_linker_info_for_gdb(linker_base);
* 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<ElfW(Ehdr)*>(linker_base);
ElfW(Phdr)* phdr = reinterpret_cast<ElfW(Phdr)*>(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);
}
// Extract information passed from the kernel. // Extract information passed from the kernel.
si->phdr = reinterpret_cast<ElfW(Phdr)*>(args.getauxval(AT_PHDR)); si->phdr = reinterpret_cast<ElfW(Phdr)*>(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. * function, or other GOT reference will generate a segfault.
*/ */
extern "C" ElfW(Addr) __linker_init(void* raw_args) { extern "C" ElfW(Addr) __linker_init(void* raw_args) {
// Initialize static variables.
solist = get_libdl_info();
sonext = get_libdl_info();
KernelArgumentBlock args(raw_args); KernelArgumentBlock args(raw_args);
ElfW(Addr) linker_addr = args.getauxval(AT_BASE); 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; args.abort_message_ptr = &g_abort_message;
ElfW(Addr) start_address = __linker_init_post_relocation(args, linker_addr); 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 the address that the calling assembly stub should jump to.
return start_address; return start_address;

View file

@ -33,8 +33,10 @@
#include <link.h> #include <link.h>
#include <unistd.h> #include <unistd.h>
#include <android/dlext.h> #include <android/dlext.h>
#include <sys/stat.h>
#include "private/libc_logging.h" #include "private/libc_logging.h"
#include "linked_list.h"
#define DL_ERR(fmt, x...) \ #define DL_ERR(fmt, x...) \
do { \ do { \
@ -84,6 +86,7 @@
#define FLAG_LINKED 0x00000001 #define FLAG_LINKED 0x00000001
#define FLAG_EXE 0x00000004 // The main executable #define FLAG_EXE 0x00000004 // The main executable
#define FLAG_LINKER 0x00000010 // The linker itself #define FLAG_LINKER 0x00000010 // The linker itself
#define FLAG_NEW_SOINFO 0x40000000 // new soinfo format
#define SOINFO_NAME_LEN 128 #define SOINFO_NAME_LEN 128
@ -94,7 +97,20 @@ typedef void (*linker_function_t)();
#define USE_RELA 1 #define USE_RELA 1
#endif #endif
struct soinfo;
class SoinfoListAllocator {
public:
static LinkedListEntry<soinfo>* alloc();
static void free(LinkedListEntry<soinfo>* entry);
private:
// unconstructable
DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator);
};
struct soinfo { struct soinfo {
public:
typedef LinkedList<soinfo, SoinfoListAllocator> soinfo_list_t;
public: public:
char name[SOINFO_NAME_LEN]; char name[SOINFO_NAME_LEN];
const ElfW(Phdr)* phdr; const ElfW(Phdr)* phdr;
@ -179,17 +195,39 @@ struct soinfo {
bool has_text_relocations; bool has_text_relocations;
#endif #endif
bool has_DT_SYMBOLIC; bool has_DT_SYMBOLIC;
void CallConstructors(); void CallConstructors();
void CallDestructors(); void CallDestructors();
void CallPreInitConstructors(); 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: private:
void CallArray(const char* array_name, linker_function_t* functions, size_t count, bool reverse); 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); 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_get_LD_LIBRARY_PATH(char*, size_t);
void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path);

View file

@ -55,8 +55,7 @@ void* LinkerBlockAllocator::alloc() {
free_block_list_ = block_info->next_block; free_block_list_ = block_info->next_block;
} }
block_info->next_block = nullptr; memset(block_info, 0, block_size_);
block_info->num_free_blocks = 0;
return block_info; return block_info;
} }
@ -78,6 +77,8 @@ void LinkerBlockAllocator::free(void* block) {
abort(); abort();
} }
memset(block, 0, block_size_);
FreeBlockInfo* block_info = reinterpret_cast<FreeBlockInfo*>(block); FreeBlockInfo* block_info = reinterpret_cast<FreeBlockInfo*>(block);
block_info->next_block = free_block_list_; block_info->next_block = free_block_list_;
@ -100,6 +101,7 @@ void LinkerBlockAllocator::create_new_page() {
if (page == MAP_FAILED) { if (page == MAP_FAILED) {
abort(); // oom abort(); // oom
} }
memset(page, 0, PAGE_SIZE);
FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes); FreeBlockInfo* first_block = reinterpret_cast<FreeBlockInfo*>(page->bytes);
first_block->next_block = free_block_list_; first_block->next_block = free_block_list_;

View file

@ -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 * A simple allocator for the dynamic linker. An allocator allocates instances
* of a single fixed-size type. Allocations are backed by page-sized private * of a single fixed-size type. Allocations are backed by page-sized private
* anonymous mmaps. * anonymous mmaps.

View file

@ -30,6 +30,7 @@ LOCAL_CFLAGS += -g -Wall -Wextra -Werror -std=gnu++11
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../libc/ LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../libc/
LOCAL_SRC_FILES := \ LOCAL_SRC_FILES := \
linked_list_test.cpp \
linker_allocator_test.cpp \ linker_allocator_test.cpp \
../linker_allocator.cpp ../linker_allocator.cpp

View file

@ -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 <stdlib.h>
#include <string>
#include <sstream>
#include <gtest/gtest.h>
#include "../linked_list.h"
namespace {
bool alloc_called = false;
bool free_called = false;
class LinkedListTestAllocator {
public:
typedef LinkedListEntry<const char> entry_t;
static entry_t* alloc() {
alloc_called = true;
return reinterpret_cast<entry_t*>(::malloc(sizeof(entry_t)));
}
static void free(entry_t* p) {
free_called = true;
::free(p);
}
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(LinkedListTestAllocator);
};
typedef LinkedList<const char, LinkedListTestAllocator> 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));
}

View file

@ -227,6 +227,19 @@ $(libdlext_sym): $(libdlext_origin)
ALL_MODULES := \ ALL_MODULES := \
$(ALL_MODULES) $(libdlext_sym) $(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 := \ libdlext_test_norelro_src_files := \
dlext_test_library.cpp \ dlext_test_library.cpp \