diff --git a/linker/linker.cpp b/linker/linker.cpp index 487829b70..926fc9b91 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -50,7 +50,6 @@ #include "private/KernelArgumentBlock.h" #include "private/ScopedPthreadMutexLocker.h" #include "private/ScopeGuard.h" -#include "private/UniquePtr.h" #include "linker.h" #include "linker_block_allocator.h" @@ -325,7 +324,10 @@ static void parse_LD_LIBRARY_PATH(const char* path) { } void soinfo::set_dt_runpath(const char* path) { - if (!has_min_version(2)) return; + if (!has_min_version(2)) { + return; + } + parse_path(path, ":", &dt_runpath_); std::string origin = dirname(get_realpath()); @@ -897,17 +899,17 @@ class LoadTask { public: struct deleter_t { void operator()(LoadTask* t) { + t->~LoadTask(); TypeBasedAllocator::free(t); } }; - typedef UniquePtr unique_ptr; - static deleter_t deleter; - static LoadTask* create(const char* name, soinfo* needed_by) { + static LoadTask* create(const char* name, soinfo* needed_by, + std::unordered_map* readers_map) { LoadTask* ptr = TypeBasedAllocator::alloc(); - return new (ptr) LoadTask(name, needed_by); + return new (ptr) LoadTask(name, needed_by, readers_map); } const char* get_name() const { @@ -917,12 +919,94 @@ class LoadTask { soinfo* get_needed_by() const { return needed_by_; } + + soinfo* get_soinfo() const { + return si_; + } + + void set_soinfo(soinfo* si) { + si_ = si; + } + + off64_t get_file_offset() const { + return file_offset_; + } + + void set_file_offset(off64_t offset) { + file_offset_ = offset; + } + + int get_fd() const { + return fd_; + } + + void set_fd(int fd, bool assume_ownership) { + fd_ = fd; + close_fd_ = assume_ownership; + } + + const android_dlextinfo* get_extinfo() const { + return extinfo_; + } + + void set_extinfo(const android_dlextinfo* extinfo) { + extinfo_ = extinfo; + } + + const ElfReader& get_elf_reader() const { + CHECK(si_ != nullptr); + return (*elf_readers_map_)[si_]; + } + + ElfReader& get_elf_reader() { + CHECK(si_ != nullptr); + return (*elf_readers_map_)[si_]; + } + + std::unordered_map* get_readers_map() { + return elf_readers_map_; + } + + bool read(const char* realpath, off64_t file_size) { + ElfReader& elf_reader = get_elf_reader(); + return elf_reader.Read(realpath, fd_, file_offset_, file_size); + } + + bool load() { + ElfReader& elf_reader = get_elf_reader(); + if (!elf_reader.Load(extinfo_)) { + return false; + } + + si_->base = elf_reader.load_start(); + si_->size = elf_reader.load_size(); + si_->load_bias = elf_reader.load_bias(); + si_->phnum = elf_reader.phdr_count(); + si_->phdr = elf_reader.loaded_phdr(); + + return true; + } + private: - LoadTask(const char* name, soinfo* needed_by) - : name_(name), needed_by_(needed_by) {} + LoadTask(const char* name, soinfo* needed_by, + std::unordered_map* readers_map) + : name_(name), needed_by_(needed_by), si_(nullptr), + fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map) {} + + ~LoadTask() { + if (fd_ != -1 && close_fd_) { + close(fd_); + } + } const char* name_; soinfo* needed_by_; + soinfo* si_; + const android_dlextinfo* extinfo_; + int fd_; + bool close_fd_; + off64_t file_offset_; + std::unordered_map* elf_readers_map_; DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask); }; @@ -934,7 +1018,7 @@ using linked_list_t = LinkedList>>; typedef linked_list_t SoinfoLinkedList; typedef linked_list_t StringLinkedList; -typedef linked_list_t LoadTaskList; +typedef std::vector LoadTaskList; // This function walks down the tree of soinfo dependencies @@ -1336,7 +1420,7 @@ static int open_library(ZipArchiveCache* zip_archive_cache, // 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); - if (fd == -1 && needed_by) { + if (fd == -1 && needed_by != nullptr) { fd = open_library_on_paths(zip_archive_cache, name, file_offset, needed_by->get_dt_runpath(), realpath); } @@ -1364,36 +1448,48 @@ static const char* fix_dt_needed(const char* dt_needed, const char* sopath __unu template static void for_each_dt_needed(const soinfo* si, F action) { - for (ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { + for (const ElfW(Dyn)* d = si->dynamic; d->d_tag != DT_NULL; ++d) { if (d->d_tag == DT_NEEDED) { action(fix_dt_needed(si->get_string(d->d_un.d_val), si->get_realpath())); } } } -static soinfo* load_library(int fd, off64_t file_offset, - LoadTaskList& load_tasks, - const char* name, int rtld_flags, - const android_dlextinfo* extinfo, - const std::string& realpath) { +template +static void for_each_dt_needed(const ElfReader& elf_reader, F action) { + for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_NEEDED) { + action(fix_dt_needed(elf_reader.get_string(d->d_un.d_val), elf_reader.name())); + } + } +} + +static bool load_library(LoadTask* task, + LoadTaskList* load_tasks, + int rtld_flags, + const std::string& realpath) { + off64_t file_offset = task->get_file_offset(); + const char* name = task->get_name(); + const android_dlextinfo* extinfo = task->get_extinfo(); + if ((file_offset % PAGE_SIZE) != 0) { DL_ERR("file offset for the library \"%s\" is not page-aligned: %" PRId64, name, file_offset); - return nullptr; + return false; } if (file_offset < 0) { DL_ERR("file offset for the library \"%s\" is negative: %" PRId64, name, file_offset); - return nullptr; + return false; } struct stat file_stat; - if (TEMP_FAILURE_RETRY(fstat(fd, &file_stat)) != 0) { + if (TEMP_FAILURE_RETRY(fstat(task->get_fd(), &file_stat)) != 0) { DL_ERR("unable to stat file for the library \"%s\": %s", name, strerror(errno)); - return nullptr; + return false; } if (file_offset >= file_stat.st_size) { DL_ERR("file offset for the library \"%s\" >= file size: %" PRId64 " >= %" PRId64, name, file_offset, file_stat.st_size); - return nullptr; + return false; } // Check for symlink and other situations where @@ -1407,48 +1503,59 @@ static soinfo* load_library(int fd, off64_t file_offset, 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()); - return si; + task->set_soinfo(si); + return true; } } } if ((rtld_flags & RTLD_NOLOAD) != 0) { DL_ERR("library \"%s\" wasn't loaded and RTLD_NOLOAD prevented it", name); - return nullptr; - } - - // Read the ELF header and load the segments. - ElfReader elf_reader(realpath.c_str(), fd, file_offset, file_stat.st_size); - if (!elf_reader.Load(extinfo)) { - return nullptr; + return false; } soinfo* si = soinfo_alloc(realpath.c_str(), &file_stat, file_offset, rtld_flags); if (si == nullptr) { - return nullptr; - } - si->base = elf_reader.load_start(); - si->size = elf_reader.load_size(); - si->load_bias = elf_reader.load_bias(); - si->phnum = elf_reader.phdr_count(); - si->phdr = elf_reader.loaded_phdr(); - - if (!si->prelink_image()) { - soinfo_free(si); - return nullptr; + return false; } - for_each_dt_needed(si, [&] (const char* name) { - load_tasks.push_back(LoadTask::create(name, si)); + task->set_soinfo(si); + + // Read the ELF header and some of the segments. + if (!task->read(realpath.c_str(), file_stat.st_size)) { + return false; + } + + // find and set DT_RUNPATH and dt_soname + // Note that these field values are temporary and are + // going to be overwritten on soinfo::prelink_image + // with values from PT_LOAD segments. + const ElfReader& elf_reader = task->get_elf_reader(); + for (const ElfW(Dyn)* d = elf_reader.dynamic(); d->d_tag != DT_NULL; ++d) { + if (d->d_tag == DT_RUNPATH) { + si->set_dt_runpath(elf_reader.get_string(d->d_un.d_val)); + } + if (d->d_tag == DT_SONAME) { + si->set_soname(elf_reader.get_string(d->d_un.d_val)); + } + } + + for_each_dt_needed(task->get_elf_reader(), [&](const char* name) { + load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map())); }); - return si; + return true; + } -static soinfo* load_library(ZipArchiveCache* zip_archive_cache, - LoadTaskList& load_tasks, const char* name, - soinfo* needed_by, int rtld_flags, - const android_dlextinfo* extinfo) { +static bool load_library(LoadTask* task, + ZipArchiveCache* zip_archive_cache, + LoadTaskList* load_tasks, + int rtld_flags) { + const char* name = task->get_name(); + soinfo* needed_by = task->get_needed_by(); + const android_dlextinfo* extinfo = task->get_extinfo(); + off64_t file_offset; std::string realpath; if (extinfo != nullptr && (extinfo->flags & ANDROID_DLEXT_USE_LIBRARY_FD) != 0) { @@ -1462,18 +1569,23 @@ static soinfo* load_library(ZipArchiveCache* zip_archive_cache, "Will use given name.", name); realpath = name; } - return load_library(extinfo->library_fd, file_offset, load_tasks, name, rtld_flags, extinfo, realpath); + + task->set_fd(extinfo->library_fd, false); + task->set_file_offset(file_offset); + return load_library(task, load_tasks, rtld_flags, realpath); } // Open the file. int fd = open_library(zip_archive_cache, name, needed_by, &file_offset, &realpath); if (fd == -1) { DL_ERR("library \"%s\" not found", name); - return nullptr; + return false; } - soinfo* result = load_library(fd, file_offset, load_tasks, name, rtld_flags, extinfo, realpath); - close(fd); - return result; + + task->set_fd(fd, true); + task->set_file_offset(file_offset); + + return load_library(task, load_tasks, rtld_flags, realpath); } // Returns true if library was found and false in 2 cases @@ -1513,31 +1625,35 @@ static bool find_loaded_library_by_soname(const char* name, soinfo** candidate) return false; } -static soinfo* find_library_internal(ZipArchiveCache* zip_archive_cache, - LoadTaskList& load_tasks, const char* name, - soinfo* needed_by, int rtld_flags, - const android_dlextinfo* extinfo) { +static bool find_library_internal(LoadTask* task, + ZipArchiveCache* zip_archive_cache, + LoadTaskList* load_tasks, + int rtld_flags) { soinfo* candidate; - if (find_loaded_library_by_soname(name, &candidate)) { - return candidate; + if (find_loaded_library_by_soname(task->get_name(), &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...]", - name, candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate); + task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate); - soinfo* si = load_library(zip_archive_cache, load_tasks, name, needed_by, rtld_flags, extinfo); - - // In case we were unable to load the library but there - // is a candidate loaded under the same soname but different - // sdk level - return it anyways. - if (si == nullptr && candidate != nullptr) { - si = candidate; + if (load_library(task, zip_archive_cache, load_tasks, rtld_flags)) { + return true; + } else { + // In case we were unable to load the library but there + // is a candidate loaded under the same soname but different + // sdk level - return it anyways. + if (candidate != nullptr) { + task->set_soinfo(candidate); + return true; + } } - return si; + return false; } static void soinfo_unload(soinfo* si); @@ -1560,6 +1676,14 @@ static soinfo::soinfo_list_t make_global_group() { return global_group; } +static void shuffle(std::vector* v) { + for (size_t i = 0, size = v->size(); i < size; ++i) { + size_t n = size - i; + size_t r = arc4random_uniform(n); + std::swap((*v)[n-1], (*v)[r]); + } +} + // add_as_children - add first-level loaded libraries (i.e. library_names[], but // not their transitive dependencies) as children of the start_with library. // This is false when find_libraries is called for dlopen(), when newly loaded @@ -1573,9 +1697,11 @@ static bool find_libraries(soinfo* start_with, bool add_as_children) { // Step 0: prepare. LoadTaskList load_tasks; + std::unordered_map readers_map; + for (size_t i = 0; i < library_names_count; ++i) { const char* name = library_names[i]; - load_tasks.push_back(LoadTask::create(name, start_with)); + load_tasks.push_back(LoadTask::create(name, start_with, &readers_map)); } // Construct global_group. @@ -1597,12 +1723,14 @@ static bool find_libraries(soinfo* start_with, // list of libraries to link - see step 2. size_t soinfos_count = 0; + auto scope_guard = make_scope_guard([&]() { + for (LoadTask* t : load_tasks) { + LoadTask::deleter(t); + } + }); + auto failure_guard = make_scope_guard([&]() { // Housekeeping - load_tasks.for_each([] (LoadTask* t) { - LoadTask::deleter(t); - }); - for (size_t i = 0; iget_needed_by(); + bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children); + task->set_extinfo(is_dt_needed ? nullptr : extinfo); - soinfo* si = find_library_internal(&zip_archive_cache, load_tasks, - task->get_name(), needed_by, rtld_flags, - is_dt_needed ? nullptr : extinfo); - - if (si == nullptr) { + if(!find_library_internal(task, &zip_archive_cache, &load_tasks, rtld_flags)) { return false; } + soinfo* si = task->get_soinfo(); + if (is_dt_needed) { needed_by->add_child(si); } @@ -1635,11 +1764,6 @@ static bool find_libraries(soinfo* start_with, // When ld_preloads is not null, the first // ld_preloads_count libs are in fact ld_preloads. if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) { - // Add LD_PRELOADed libraries to the global group for future runs. - // There is no need to explicitly add them to the global group - // for this run because they are going to appear in the local - // group in the correct order. - si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); ld_preloads->push_back(si); } @@ -1648,7 +1772,47 @@ static bool find_libraries(soinfo* start_with, } } - // Step 2: link libraries. + // Step 2: Load libraries in random order (see b/24047022) + LoadTaskList load_list; + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + auto pred = [&](const LoadTask* t) { + return t->get_soinfo() == si; + }; + + if (!si->is_linked() && + std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) { + load_list.push_back(task); + } + } + shuffle(&load_list); + + for (auto&& task : load_list) { + if (!task->load()) { + return false; + } + } + + // Step 3: pre-link all DT_NEEDED libraries in breadth first order. + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + if (!si->is_linked() && !si->prelink_image()) { + return false; + } + } + + // Step 4: Add LD_PRELOADed libraries to the global group for + // future runs. There is no need to explicitly add them to + // the global group for this run because they are going to + // appear in the local group in the correct order. + if (ld_preloads != nullptr) { + for (auto&& si : *ld_preloads) { + si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); + } + } + + + // Step 5: link libraries. soinfo::soinfo_list_t local_group; walk_dependencies_tree( (start_with != nullptr && add_as_children) ? &start_with : soinfos, @@ -2532,6 +2696,17 @@ const char* soinfo::get_realpath() const { #endif } +void soinfo::set_soname(const char* soname) { +#if defined(__work_around_b_24465209__) + if (has_min_version(2)) { + soname_ = soname; + } + strlcpy(old_name_, soname_, sizeof(old_name_)); +#else + soname_ = soname; +#endif +} + const char* soinfo::get_soname() const { #if defined(__work_around_b_24465209__) if (has_min_version(2)) { @@ -3063,13 +3238,9 @@ bool soinfo::prelink_image() { for (ElfW(Dyn)* d = dynamic; d->d_tag != DT_NULL; ++d) { switch (d->d_tag) { case DT_SONAME: - soname_ = get_string(d->d_un.d_val); -#if defined(__work_around_b_24465209__) - strlcpy(old_name_, soname_, sizeof(old_name_)); -#endif + set_soname(get_string(d->d_un.d_val)); break; case DT_RUNPATH: - // FIXME: $LIB, $PLATFORM unsupported. set_dt_runpath(get_string(d->d_un.d_val)); break; } diff --git a/linker/linker.h b/linker/linker.h index 39d3ff1dc..2c988692f 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -317,6 +317,7 @@ struct soinfo { soinfo* get_local_group_root() const; + void set_soname(const char* soname); const char* get_soname() const; const char* get_realpath() const; const ElfW(Versym)* get_versym(size_t n) const; @@ -329,6 +330,7 @@ struct soinfo { uint32_t get_target_sdk_version() const; + void set_dt_runpath(const char *); const std::vector& get_dt_runpath() const; private: @@ -392,7 +394,6 @@ struct soinfo { uint32_t target_sdk_version_; - void set_dt_runpath(const char *); std::vector dt_runpath_; friend soinfo* get_libdl_info(); diff --git a/linker/linker_libc_support.c b/linker/linker_libc_support.c index 4c49384bf..77a025245 100644 --- a/linker/linker_libc_support.c +++ b/linker/linker_libc_support.c @@ -15,7 +15,9 @@ */ #include "../libc/arch-common/bionic/__dso_handle.h" +#include "../libc/arch-common/bionic/pthread_atfork.h" int atexit(void (*function)(void) __attribute__((__unused__))) { return -1; } + diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 6fe808421..a26187a4d 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -133,30 +133,59 @@ static int GetTargetElfMachine() { MAYBE_MAP_FLAG((x), PF_R, PROT_READ) | \ MAYBE_MAP_FLAG((x), PF_W, PROT_WRITE)) -ElfReader::ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size) - : name_(name), fd_(fd), file_offset_(file_offset), file_size_(file_size), phdr_num_(0), - phdr_table_(nullptr), load_start_(nullptr), load_size_(0), load_bias_(0), - loaded_phdr_(nullptr) { +ElfReader::ElfReader() + : did_read_(false), did_load_(false), fd_(-1), file_offset_(0), file_size_(0), phdr_num_(0), + phdr_table_(nullptr), shdr_table_(nullptr), shdr_num_(0), dynamic_(nullptr), strtab_(nullptr), + strtab_size_(0), load_start_(nullptr), load_size_(0), load_bias_(0), loaded_phdr_(nullptr) { +} + +bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) { + CHECK(!did_read_); + CHECK(!did_load_); + name_ = name; + fd_ = fd; + file_offset_ = file_offset; + file_size_ = file_size; + + if (ReadElfHeader() && + VerifyElfHeader() && + ReadProgramHeaders() && + ReadSectionHeaders() && + ReadDynamicSection()) { + did_read_ = true; + } + + return did_read_; } bool ElfReader::Load(const android_dlextinfo* extinfo) { - return ReadElfHeader() && - VerifyElfHeader() && - ReadProgramHeader() && - ReserveAddressSpace(extinfo) && - LoadSegments() && - FindPhdr(); + CHECK(did_read_); + CHECK(!did_load_); + if (ReserveAddressSpace(extinfo) && + LoadSegments() && + FindPhdr()) { + did_load_ = true; + } + + return did_load_; +} + +const char* ElfReader::get_string(ElfW(Word) index) const { + CHECK(strtab_ != nullptr); + CHECK(index < strtab_size_); + + return strtab_ + index; } bool ElfReader::ReadElfHeader() { ssize_t rc = TEMP_FAILURE_RETRY(pread64(fd_, &header_, sizeof(header_), file_offset_)); if (rc < 0) { - DL_ERR("can't read file \"%s\": %s", name_, strerror(errno)); + DL_ERR("can't read file \"%s\": %s", name_.c_str(), strerror(errno)); return false; } if (rc != sizeof(header_)) { - DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_, + DL_ERR("\"%s\" is too small to be an ELF executable: only found %zd bytes", name_.c_str(), static_cast(rc)); return false; } @@ -165,7 +194,7 @@ bool ElfReader::ReadElfHeader() { bool ElfReader::VerifyElfHeader() { if (memcmp(header_.e_ident, ELFMAG, SELFMAG) != 0) { - DL_ERR("\"%s\" has bad ELF magic", name_); + DL_ERR("\"%s\" has bad ELF magic", name_.c_str()); return false; } @@ -175,40 +204,40 @@ bool ElfReader::VerifyElfHeader() { #if defined(__LP64__) if (elf_class != ELFCLASS64) { if (elf_class == ELFCLASS32) { - DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_); + DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_.c_str()); } else { - DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class); + DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class); } return false; } #else if (elf_class != ELFCLASS32) { if (elf_class == ELFCLASS64) { - DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_); + DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_.c_str()); } else { - DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class); + DL_ERR("\"%s\" has unknown ELF class: %d", name_.c_str(), elf_class); } return false; } #endif if (header_.e_ident[EI_DATA] != ELFDATA2LSB) { - DL_ERR("\"%s\" not little-endian: %d", name_, header_.e_ident[EI_DATA]); + DL_ERR("\"%s\" not little-endian: %d", name_.c_str(), header_.e_ident[EI_DATA]); return false; } if (header_.e_type != ET_DYN) { - DL_ERR("\"%s\" has unexpected e_type: %d", name_, header_.e_type); + DL_ERR("\"%s\" has unexpected e_type: %d", name_.c_str(), header_.e_type); return false; } if (header_.e_version != EV_CURRENT) { - DL_ERR("\"%s\" has unexpected e_version: %d", name_, header_.e_version); + DL_ERR("\"%s\" has unexpected e_version: %d", name_.c_str(), header_.e_version); return false; } if (header_.e_machine != GetTargetElfMachine()) { - DL_ERR("\"%s\" has unexpected e_machine: %d", name_, header_.e_machine); + DL_ERR("\"%s\" has unexpected e_machine: %d", name_.c_str(), header_.e_machine); return false; } @@ -217,18 +246,18 @@ bool ElfReader::VerifyElfHeader() { // Loads the program header table from an ELF file into a read-only private // anonymous mmap-ed block. -bool ElfReader::ReadProgramHeader() { +bool ElfReader::ReadProgramHeaders() { phdr_num_ = header_.e_phnum; // Like the kernel, we only accept program header tables that // are smaller than 64KiB. if (phdr_num_ < 1 || phdr_num_ > 65536/sizeof(ElfW(Phdr))) { - DL_ERR("\"%s\" has invalid e_phnum: %zd", name_, phdr_num_); + DL_ERR("\"%s\" has invalid e_phnum: %zd", name_.c_str(), phdr_num_); return false; } if (!phdr_fragment_.Map(fd_, file_offset_, header_.e_phoff, phdr_num_ * sizeof(ElfW(Phdr)))) { - DL_ERR("\"%s\" phdr mmap failed: %s", name_, strerror(errno)); + DL_ERR("\"%s\" phdr mmap failed: %s", name_.c_str(), strerror(errno)); return false; } @@ -236,6 +265,63 @@ bool ElfReader::ReadProgramHeader() { return true; } +bool ElfReader::ReadSectionHeaders() { + shdr_num_ = header_.e_shnum; + + if (!shdr_fragment_.Map(fd_, file_offset_, header_.e_shoff, shdr_num_ * sizeof(ElfW(Shdr)))) { + DL_ERR("\"%s\" shdr mmap failed: %s", name_.c_str(), strerror(errno)); + return false; + } + + shdr_table_ = static_cast(shdr_fragment_.data()); + return true; +} + +bool ElfReader::ReadDynamicSection() { + // 1. Find .dynamic section (in section headers) + const ElfW(Shdr)* dynamic_shdr = nullptr; + for (size_t i = 0; i < shdr_num_; ++i) { + if (shdr_table_[i].sh_type == SHT_DYNAMIC) { + dynamic_shdr = &shdr_table_ [i]; + break; + } + } + + if (dynamic_shdr == nullptr) { + DL_ERR("\"%s\" .dynamic section was not found", name_.c_str()); + return false; + } + + if (dynamic_shdr->sh_link >= shdr_num_) { + DL_ERR("\"%s\" .dynamic section has invalid sh_link: %d", name_.c_str(), dynamic_shdr->sh_link); + return false; + } + + const ElfW(Shdr)* strtab_shdr = &shdr_table_[dynamic_shdr->sh_link]; + + if (strtab_shdr->sh_type != SHT_STRTAB) { + DL_ERR("\"%s\" .dynamic section has invalid link(%d) sh_type: %d (expected SHT_STRTAB)", + name_.c_str(), dynamic_shdr->sh_link, strtab_shdr->sh_type); + return false; + } + + if (!dynamic_fragment_.Map(fd_, file_offset_, dynamic_shdr->sh_offset, dynamic_shdr->sh_size)) { + DL_ERR("\"%s\" dynamic section mmap failed: %s", name_.c_str(), strerror(errno)); + return false; + } + + dynamic_ = static_cast(dynamic_fragment_.data()); + + if (!strtab_fragment_.Map(fd_, file_offset_, strtab_shdr->sh_offset, strtab_shdr->sh_size)) { + DL_ERR("\"%s\" strtab section mmap failed: %s", name_.c_str(), strerror(errno)); + return false; + } + + strtab_ = static_cast(strtab_fragment_.data()); + strtab_size_ = strtab_fragment_.size(); + return true; +} + /* Returns the size of the extent of all the possibly non-contiguous * loadable segments in an ELF program header table. This corresponds * to the page-aligned size in bytes that needs to be reserved in the @@ -292,7 +378,7 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { ElfW(Addr) min_vaddr; load_size_ = phdr_table_get_load_size(phdr_table_, phdr_num_, &min_vaddr); if (load_size_ == 0) { - DL_ERR("\"%s\" has no loadable segments", name_); + DL_ERR("\"%s\" has no loadable segments", name_.c_str()); return false; } @@ -319,13 +405,13 @@ bool ElfReader::ReserveAddressSpace(const android_dlextinfo* extinfo) { if (load_size_ > reserved_size) { if (!reserved_hint) { DL_ERR("reserved address space %zd smaller than %zd bytes needed for \"%s\"", - reserved_size - load_size_, load_size_, name_); + reserved_size - load_size_, load_size_, name_.c_str()); return false; } int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS; start = mmap(mmap_hint, load_size_, PROT_NONE, mmap_flags, -1, 0); if (start == MAP_FAILED) { - DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_); + DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_.c_str()); return false; } } else { @@ -362,14 +448,14 @@ bool ElfReader::LoadSegments() { ElfW(Addr) file_length = file_end - file_page_start; if (file_size_ <= 0) { - DL_ERR("\"%s\" invalid file size: %" PRId64, name_, file_size_); + DL_ERR("\"%s\" invalid file size: %" PRId64, name_.c_str(), file_size_); return false; } if (file_end > static_cast(file_size_)) { DL_ERR("invalid ELF file \"%s\" load segment[%zd]:" " p_offset (%p) + p_filesz (%p) ( = %p) past end of file (0x%" PRIx64 ")", - name_, i, reinterpret_cast(phdr->p_offset), + name_.c_str(), i, reinterpret_cast(phdr->p_offset), reinterpret_cast(phdr->p_filesz), reinterpret_cast(file_end), file_size_); return false; @@ -383,7 +469,7 @@ bool ElfReader::LoadSegments() { fd_, file_offset_ + file_page_start); if (seg_addr == MAP_FAILED) { - DL_ERR("couldn't map \"%s\" segment %zd: %s", name_, i, strerror(errno)); + DL_ERR("couldn't map \"%s\" segment %zd: %s", name_.c_str(), i, strerror(errno)); return false; } } @@ -408,7 +494,7 @@ bool ElfReader::LoadSegments() { -1, 0); if (zeromap == MAP_FAILED) { - DL_ERR("couldn't zero fill \"%s\" gap: %s", name_, strerror(errno)); + DL_ERR("couldn't zero fill \"%s\" gap: %s", name_.c_str(), strerror(errno)); return false; } } @@ -803,7 +889,7 @@ bool ElfReader::FindPhdr() { } } - DL_ERR("can't find loaded phdr for \"%s\"", name_); + DL_ERR("can't find loaded phdr for \"%s\"", name_.c_str()); return false; } @@ -824,6 +910,7 @@ bool ElfReader::CheckPhdr(ElfW(Addr) loaded) { return true; } } - DL_ERR("\"%s\" loaded phdr %p not in loadable segment", name_, reinterpret_cast(loaded)); + DL_ERR("\"%s\" loaded phdr %p not in loadable segment", + name_.c_str(), reinterpret_cast(loaded)); return false; } diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h index 4e0219701..f7b1caf89 100644 --- a/linker/linker_phdr.h +++ b/linker/linker_phdr.h @@ -40,26 +40,34 @@ class ElfReader { public: - ElfReader(const char* name, int fd, off64_t file_offset, off64_t file_size); + ElfReader(); + bool Read(const char* name, int fd, off64_t file_offset, off64_t file_size); bool Load(const android_dlextinfo* extinfo); - size_t phdr_count() { return phdr_num_; } - ElfW(Addr) load_start() { return reinterpret_cast(load_start_); } - size_t load_size() { return load_size_; } - ElfW(Addr) load_bias() { return load_bias_; } - const ElfW(Phdr)* loaded_phdr() { return loaded_phdr_; } + const char* name() const { return name_.c_str(); } + size_t phdr_count() const { return phdr_num_; } + ElfW(Addr) load_start() const { return reinterpret_cast(load_start_); } + size_t load_size() const { return load_size_; } + ElfW(Addr) load_bias() const { return load_bias_; } + const ElfW(Phdr)* loaded_phdr() const { return loaded_phdr_; } + const ElfW(Dyn)* dynamic() const { return dynamic_; } + const char* get_string(ElfW(Word) index) const; private: bool ReadElfHeader(); bool VerifyElfHeader(); - bool ReadProgramHeader(); + bool ReadProgramHeaders(); + bool ReadSectionHeaders(); + bool ReadDynamicSection(); bool ReserveAddressSpace(const android_dlextinfo* extinfo); bool LoadSegments(); bool FindPhdr(); bool CheckPhdr(ElfW(Addr)); - const char* name_; + bool did_read_; + bool did_load_; + std::string name_; int fd_; off64_t file_offset_; off64_t file_size_; @@ -70,6 +78,17 @@ class ElfReader { MappedFileFragment phdr_fragment_; const ElfW(Phdr)* phdr_table_; + MappedFileFragment shdr_fragment_; + const ElfW(Shdr)* shdr_table_; + size_t shdr_num_; + + MappedFileFragment dynamic_fragment_; + const ElfW(Dyn)* dynamic_; + + MappedFileFragment strtab_fragment_; + const char* strtab_; + size_t strtab_size_; + // First page of reserved address space. void* load_start_; // Size in bytes of reserved address space.