From 114ff69f1753c7fe4d749f8fb0c082b80e0d43f4 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Wed, 14 Jan 2015 11:36:38 -0800 Subject: [PATCH] Refactoring: move mips reloc to separate method Change-Id: I712614853e3f0e515f5c2bdd8f0aaa5feeae8e55 --- linker/Android.mk | 4 +- linker/linker.cpp | 139 ++++++--------------------------------- linker/linker.h | 15 ++++- linker/linker_debug.h | 19 ++++++ linker/linker_mips.cpp | 143 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 123 deletions(-) create mode 100644 linker/linker_mips.cpp diff --git a/linker/Android.mk b/linker/Android.mk index d6e009575..720389f0e 100644 --- a/linker/Android.mk +++ b/linker/Android.mk @@ -16,8 +16,8 @@ LOCAL_SRC_FILES_arm := arch/arm/begin.S LOCAL_SRC_FILES_arm64 := arch/arm64/begin.S LOCAL_SRC_FILES_x86 := arch/x86/begin.c LOCAL_SRC_FILES_x86_64 := arch/x86_64/begin.S -LOCAL_SRC_FILES_mips := arch/mips/begin.S -LOCAL_SRC_FILES_mips64 := arch/mips64/begin.S +LOCAL_SRC_FILES_mips := arch/mips/begin.S linker_mips.cpp +LOCAL_SRC_FILES_mips64 := arch/mips64/begin.S linker_mips.cpp LOCAL_LDFLAGS := \ -shared \ diff --git a/linker/linker.cpp b/linker/linker.cpp index a438cfc3b..0b0afc32a 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -122,14 +122,6 @@ __LIBC_HIDDEN__ int g_ld_debug_verbosity; __LIBC_HIDDEN__ abort_msg_t* g_abort_message = nullptr; // For debuggerd. -enum RelocationKind { - kRelocAbsolute = 0, - kRelocRelative, - kRelocCopy, - kRelocSymbol, - kRelocMax -}; - #if STATS struct linker_stats_t { int count[kRelocMax]; @@ -137,30 +129,16 @@ struct linker_stats_t { static linker_stats_t linker_stats; -static void count_relocation(RelocationKind kind) { +void count_relocation(RelocationKind kind) { ++linker_stats.count[kind]; } #else -static void count_relocation(RelocationKind) { +void count_relocation(RelocationKind) { } #endif #if COUNT_PAGES -static unsigned bitmask[4096]; -#if defined(__LP64__) -#define MARK(offset) \ - do { \ - if ((((offset) >> 12) >> 5) < 4096) \ - bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \ - } while (0) -#else -#define MARK(offset) \ - do { \ - bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \ - } while (0) -#endif -#else -#define MARK(x) do {} while (0) +uint32_t bitmask[4096]; #endif // You shouldn't try to call memory-allocating functions in the dynamic linker. @@ -539,7 +517,7 @@ uint32_t SymbolName::gnu_hash() { return gnu_hash_; } -static ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, +ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group) { SymbolName symbol_name(name); ElfW(Sym)* s = nullptr; @@ -1292,10 +1270,9 @@ static ElfW(Addr) get_addend(ElfW(Rel)* rel, ElfW(Addr) reloc_addr) { return 0; } #endif -#endif template -int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { +bool soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { for (size_t idx = 0; idx < count; ++idx, ++rel) { ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); @@ -1303,11 +1280,9 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g ElfW(Addr) reloc = static_cast(rel->r_offset + load_bias); ElfW(Addr) sym_addr = 0; const char* sym_name = nullptr; -#if !defined(__mips__) ElfW(Addr) addend = get_addend(rel, reloc); -#endif - DEBUG("Processing '%s' relocation at index %zd", name, idx); + DEBUG("Processing '%s' relocation at index %zd", this->name, idx); if (type == R_GENERIC_NONE) { continue; } @@ -1323,7 +1298,7 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g s = &symtab_[sym]; if (ELF_ST_BIND(s->st_info) != STB_WEAK) { DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name); - return -1; + return false; } /* IHI0044C AAELF 4.5.1.1: @@ -1339,7 +1314,6 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g */ switch (type) { -#if !defined(__mips__) case R_GENERIC_JUMP_SLOT: case R_GENERIC_GLOB_DAT: case R_GENERIC_RELATIVE: @@ -1370,11 +1344,10 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g case R_386_PC32: sym_addr = reloc; break; -#endif #endif default: DL_ERR("unknown weak reloc type %d @ %p (%zu)", type, rel, idx); - return -1; + return false; } } else { // We got a definition. @@ -1384,7 +1357,6 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g } switch (type) { -#if !defined(__mips__) case R_GENERIC_JUMP_SLOT: count_relocation(kRelocAbsolute); MARK(rel->r_offset); @@ -1418,7 +1390,6 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g reinterpret_cast(base + addend)); *reinterpret_cast(reloc) = call_ifunc_resolver(base + addend); break; -#endif #if defined(__aarch64__) case R_AARCH64_ABS64: @@ -1441,7 +1412,7 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g (*reinterpret_cast(reloc) + (sym_addr + addend)), static_cast(INT32_MIN), static_cast(UINT32_MAX)); - return -1; + return false; } break; case R_AARCH64_ABS16: @@ -1457,7 +1428,7 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g (*reinterpret_cast(reloc) + (sym_addr + addend)), static_cast(INT16_MIN), static_cast(UINT16_MAX)); - return -1; + return false; } break; case R_AARCH64_PREL64: @@ -1480,7 +1451,7 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g (*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset)), static_cast(INT32_MIN), static_cast(UINT32_MAX)); - return -1; + return false; } break; case R_AARCH64_PREL16: @@ -1496,7 +1467,7 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g (*reinterpret_cast(reloc) + ((sym_addr + addend) - rel->r_offset)), static_cast(INT16_MIN), static_cast(UINT16_MAX)); - return -1; + return false; } break; @@ -1511,7 +1482,7 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g * set to ET_EXEC. */ DL_ERR("%s R_AARCH64_COPY relocations are not supported", name); - return -1; + return false; case R_AARCH64_TLS_TPREL64: TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16llx <- %16llx - %16llx\n", reloc, (sym_addr + addend), rel->r_offset); @@ -1568,7 +1539,7 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g * set to ET_EXEC. */ DL_ERR("%s R_ARM_COPY relocations are not supported", name); - return -1; + return false; #elif defined(__i386__) case R_386_32: count_relocation(kRelocRelative); @@ -1583,87 +1554,15 @@ int soinfo::relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_g reloc, (sym_addr - reloc), sym_addr, reloc, sym_name); *reinterpret_cast(reloc) += (sym_addr - reloc); break; -#elif defined(__mips__) - case R_MIPS_REL32: -#if defined(__LP64__) - // MIPS Elf64_Rel entries contain compound relocations - // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case - if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 || - ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) { - DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)", - type, (unsigned)ELF64_R_TYPE2(rel->r_info), - (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx); - return -1; - } -#endif - count_relocation(kRelocAbsolute); - MARK(rel->r_offset); - TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast(reloc), - static_cast(sym_addr), sym_name ? sym_name : "*SECTIONHDR*"); - if (s) { - *reinterpret_cast(reloc) += sym_addr; - } else { - *reinterpret_cast(reloc) += base; - } - break; #endif default: DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx); - return -1; - } - } - return 0; -} - -#if defined(__mips__) -bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) { - ElfW(Addr)** got = plt_got_; - if (got == nullptr) { - return true; - } - - // got[0] is the address of the lazy resolver function. - // got[1] may be used for a GNU extension. - // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start). - // FIXME: maybe this should be in a separate routine? - if ((flags_ & FLAG_LINKER) == 0) { - size_t g = 0; - got[g++] = reinterpret_cast(0xdeadbeef); - if (reinterpret_cast(got[g]) < 0) { - got[g++] = reinterpret_cast(0xdeadfeed); - } - // Relocate the local GOT entries. - for (; g < mips_local_gotno_; g++) { - got[g] = reinterpret_cast(reinterpret_cast(got[g]) + load_bias); - } - } - - // Now for the global GOT entries... - ElfW(Sym)* sym = symtab_ + mips_gotsym_; - got = plt_got_ + mips_local_gotno_; - for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) { - // This is an undefined reference... try to locate it. - const char* sym_name = get_string(sym->st_name); - soinfo* lsi = nullptr; - ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group); - if (s == nullptr) { - // We only allow an undefined symbol if this is a weak reference. - s = &symtab_[g]; - if (ELF_ST_BIND(s->st_info) != STB_WEAK) { - DL_ERR("cannot locate \"%s\"...", sym_name); return false; - } - *got = 0; - } else { - // FIXME: is this sufficient? - // For reference see NetBSD link loader - // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup - *got = reinterpret_cast(lsi->resolve_symbol_address(s)); } } return true; } -#endif +#endif // !defined(__mips__) void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, size_t count, bool reverse) { if (functions == nullptr) { @@ -2352,26 +2251,26 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& #if defined(USE_RELA) if (rela_ != nullptr) { DEBUG("[ relocating %s ]", name); - if (relocate(rela_, rela_count_, global_group, local_group)) { + if (!relocate(rela_, rela_count_, global_group, local_group)) { return false; } } if (plt_rela_ != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (relocate(plt_rela_, plt_rela_count_, global_group, local_group)) { + if (!relocate(plt_rela_, plt_rela_count_, global_group, local_group)) { return false; } } #else if (rel_ != nullptr) { DEBUG("[ relocating %s ]", name); - if (relocate(rel_, rel_count_, global_group, local_group)) { + if (!relocate(rel_, rel_count_, global_group, local_group)) { return false; } } if (plt_rel_ != nullptr) { DEBUG("[ relocating %s plt ]", name); - if (relocate(plt_rel_, plt_rel_count_, global_group, local_group)) { + if (!relocate(plt_rel_, plt_rel_count_, global_group, local_group)) { return false; } } diff --git a/linker/linker.h b/linker/linker.h index 4f6b585ee..2afbaf62f 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -287,7 +287,7 @@ struct soinfo { void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse); void call_function(const char* function_name, linker_function_t function); template - int relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group); + bool relocate(ElfRelT* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group); private: // This part of the structure is only available @@ -318,6 +318,19 @@ struct soinfo { friend soinfo* get_libdl_info(); }; +ElfW(Sym)* soinfo_do_lookup(soinfo* si_from, const char* name, soinfo** si_found_in, + const soinfo::soinfo_list_t& global_group, const soinfo::soinfo_list_t& local_group); + +enum RelocationKind { + kRelocAbsolute = 0, + kRelocRelative, + kRelocCopy, + kRelocSymbol, + kRelocMax +}; + +void count_relocation(RelocationKind kind); + soinfo* get_libdl_info(); void do_android_get_LD_LIBRARY_PATH(char*, size_t); diff --git a/linker/linker_debug.h b/linker/linker_debug.h index 0c7a78418..5ded5abc7 100644 --- a/linker/linker_debug.h +++ b/linker/linker_debug.h @@ -82,4 +82,23 @@ __LIBC_HIDDEN__ extern int g_ld_debug_verbosity; #define TRACE_TYPE(t, x...) do { if (DO_TRACE_##t) { TRACE(x); } } while (0) +#if COUNT_PAGES +extern uint32_t bitmask[]; +#if defined(__LP64__) +#define MARK(offset) \ + do { \ + if ((((offset) >> 12) >> 5) < 4096) \ + bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \ + } while (0) +#else +#define MARK(offset) \ + do { \ + bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \ + } while (0) +#endif +#else +#define MARK(x) do {} while (0) + +#endif + #endif /* _LINKER_DEBUG_H_ */ diff --git a/linker/linker_mips.cpp b/linker/linker_mips.cpp new file mode 100644 index 000000000..eb2ae8448 --- /dev/null +++ b/linker/linker_mips.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "linker.h" +#include "linker_debug.h" +#include "linker_relocs.h" + +template<> +bool soinfo::relocate(ElfW(Rel)* rel, unsigned count, const soinfo_list_t& global_group, const soinfo_list_t& local_group) { + for (size_t idx = 0; idx < count; ++idx, ++rel) { + ElfW(Word) type = ELFW(R_TYPE)(rel->r_info); + ElfW(Word) sym = ELFW(R_SYM)(rel->r_info); + + ElfW(Addr) reloc = static_cast(rel->r_offset + load_bias); + ElfW(Addr) sym_addr = 0; + const char* sym_name = nullptr; + + DEBUG("Processing '%s' relocation at index %zd", this->name, idx); + if (type == R_GENERIC_NONE) { + continue; + } + + ElfW(Sym)* s = nullptr; + soinfo* lsi = nullptr; + + if (sym != 0) { + sym_name = get_string(symtab_[sym].st_name); + s = soinfo_do_lookup(this, sym_name, &lsi, global_group,local_group); + if (s == nullptr) { + // mips does not support relocation with weak-undefined symbols + DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, name); + return false; + } else { + // We got a definition. + sym_addr = lsi->resolve_symbol_address(s); + } + count_relocation(kRelocSymbol); + } + + switch (type) { + case R_MIPS_REL32: +#if defined(__LP64__) + // MIPS Elf64_Rel entries contain compound relocations + // We only handle the R_MIPS_NONE|R_MIPS_64|R_MIPS_REL32 case + if (ELF64_R_TYPE2(rel->r_info) != R_MIPS_64 || + ELF64_R_TYPE3(rel->r_info) != R_MIPS_NONE) { + DL_ERR("Unexpected compound relocation type:%d type2:%d type3:%d @ %p (%zu)", + type, (unsigned)ELF64_R_TYPE2(rel->r_info), + (unsigned)ELF64_R_TYPE3(rel->r_info), rel, idx); + return false; + } +#endif + count_relocation(s == nullptr ? kRelocAbsolute : kRelocRelative); + MARK(rel->r_offset); + TRACE_TYPE(RELO, "RELO REL32 %08zx <- %08zx %s", static_cast(reloc), + static_cast(sym_addr), sym_name ? sym_name : "*SECTIONHDR*"); + if (s != nullptr) { + *reinterpret_cast(reloc) += sym_addr; + } else { + *reinterpret_cast(reloc) += base; + } + break; + default: + DL_ERR("unknown reloc type %d @ %p (%zu)", type, rel, idx); + return false; + } + } + return true; +} + +bool soinfo::mips_relocate_got(const soinfo_list_t& global_group, const soinfo_list_t& local_group) { + ElfW(Addr)** got = plt_got_; + if (got == nullptr) { + return true; + } + + // got[0] is the address of the lazy resolver function. + // got[1] may be used for a GNU extension. + // Set it to a recognizable address in case someone calls it (should be _rtld_bind_start). + // FIXME: maybe this should be in a separate routine? + if ((flags_ & FLAG_LINKER) == 0) { + size_t g = 0; + got[g++] = reinterpret_cast(0xdeadbeef); + if (reinterpret_cast(got[g]) < 0) { + got[g++] = reinterpret_cast(0xdeadfeed); + } + // Relocate the local GOT entries. + for (; g < mips_local_gotno_; g++) { + got[g] = reinterpret_cast(reinterpret_cast(got[g]) + load_bias); + } + } + + // Now for the global GOT entries... + ElfW(Sym)* sym = symtab_ + mips_gotsym_; + got = plt_got_ + mips_local_gotno_; + for (size_t g = mips_gotsym_; g < mips_symtabno_; g++, sym++, got++) { + // This is an undefined reference... try to locate it. + const char* sym_name = get_string(sym->st_name); + soinfo* lsi = nullptr; + ElfW(Sym)* s = soinfo_do_lookup(this, sym_name, &lsi, global_group, local_group); + if (s == nullptr) { + // We only allow an undefined symbol if this is a weak reference. + s = &symtab_[g]; + if (ELF_ST_BIND(s->st_info) != STB_WEAK) { + DL_ERR("cannot locate \"%s\"...", sym_name); + return false; + } + *got = 0; + } else { + // FIXME: is this sufficient? + // For reference see NetBSD link loader + // http://cvsweb.netbsd.org/bsdweb.cgi/src/libexec/ld.elf_so/arch/mips/mips_reloc.c?rev=1.53&content-type=text/x-cvsweb-markup + *got = reinterpret_cast(lsi->resolve_symbol_address(s)); + } + } + return true; +} +