diff options
author | Elliott Hughes <enh@google.com> | 2018-01-23 00:24:26 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2018-01-23 00:24:26 +0000 |
commit | d00d38b36be0fd25399256d0cebdd71625aea2b6 (patch) | |
tree | b32e0212754398814e0f825783eec448cc712249 /linker/linker.cpp | |
parent | 86a2fba164cb87375b81b6873f7399e92073ea32 (diff) | |
parent | b7feec74547f84559a1467aca02708ff61346d2a (diff) |
Merge "linker: add experimental support for SHT_RELR sections."
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r-- | linker/linker.cpp | 65 |
1 files changed, 60 insertions, 5 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index 63415d8d9..05efc5500 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -2595,6 +2595,35 @@ bool soinfo::lookup_version_info(const VersionTracker& version_tracker, ElfW(Wor return true; } +// Process relocations in SHT_RELR section (experimental). +// See the original proposal for details of the encoding: +// - https://groups.google.com/forum/#!topic/generic-abi/bX460iggiKg +bool soinfo::relocate_relr() { + ElfW(Relr)* begin = relr_; + ElfW(Relr)* end = relr_ + relr_count_; + + ElfW(Addr) offset = 0; + for (ElfW(Relr)* current = begin; current < end; ++current) { + ElfW(Addr) jump = ELFW(R_JUMP)(*current); + ElfW(Addr) bits = ELFW(R_BITS)(*current); + offset += jump * sizeof(ElfW(Addr)); + if (jump == 0) { + ++current; + offset = *current; + } + ElfW(Addr) r_offset = offset; + for (; bits != 0; bits >>= 1) { + if ((bits&1) != 0) { + ElfW(Addr) reloc = static_cast<ElfW(Addr)>(r_offset + load_bias); + ElfW(Addr) addend = *reinterpret_cast<ElfW(Addr)*>(reloc); + *reinterpret_cast<ElfW(Addr)*>(reloc) = (load_bias + addend); + } + r_offset += sizeof(ElfW(Addr)); + } + } + return true; +} + #if !defined(__mips__) #if defined(USE_RELA) static ElfW(Addr) get_addend(ElfW(Rela)* rela, ElfW(Addr) reloc_addr __unused) { @@ -3151,7 +3180,7 @@ bool soinfo::prelink_image() { } break; - // ignored (see DT_RELCOUNT comments for details) + // Ignored (see DT_RELCOUNT comments for details). case DT_RELACOUNT: break; @@ -3212,6 +3241,25 @@ bool soinfo::prelink_image() { return false; #endif + case DT_RELR: + relr_ = reinterpret_cast<ElfW(Relr)*>(load_bias + d->d_un.d_ptr); + break; + + case DT_RELRSZ: + relr_count_ = d->d_un.d_val / sizeof(ElfW(Relr)); + break; + + case DT_RELRENT: + if (d->d_un.d_val != sizeof(ElfW(Relr))) { + DL_ERR("invalid DT_RELRENT: %zd", static_cast<size_t>(d->d_un.d_val)); + return false; + } + break; + + // Ignored (see DT_RELCOUNT comments for details). + case DT_RELRCOUNT: + break; + case DT_INIT: init_func_ = reinterpret_cast<linker_ctor_function_t>(load_bias + d->d_un.d_ptr); DEBUG("%s constructors (DT_INIT) found at %p", get_realpath(), init_func_); @@ -3504,16 +3552,23 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& } } + if (relr_ != nullptr) { + DEBUG("[ relocating %s relr ]", get_realpath()); + if (!relocate_relr()) { + return false; + } + } + #if defined(USE_RELA) if (rela_ != nullptr) { - DEBUG("[ relocating %s ]", get_realpath()); + DEBUG("[ relocating %s rela ]", get_realpath()); if (!relocate(version_tracker, plain_reloc_iterator(rela_, rela_count_), global_group, local_group)) { return false; } } if (plt_rela_ != nullptr) { - DEBUG("[ relocating %s plt ]", get_realpath()); + DEBUG("[ relocating %s plt rela ]", get_realpath()); if (!relocate(version_tracker, plain_reloc_iterator(plt_rela_, plt_rela_count_), global_group, local_group)) { return false; @@ -3521,14 +3576,14 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& } #else if (rel_ != nullptr) { - DEBUG("[ relocating %s ]", get_realpath()); + DEBUG("[ relocating %s rel ]", get_realpath()); if (!relocate(version_tracker, plain_reloc_iterator(rel_, rel_count_), global_group, local_group)) { return false; } } if (plt_rel_ != nullptr) { - DEBUG("[ relocating %s plt ]", get_realpath()); + DEBUG("[ relocating %s plt rel ]", get_realpath()); if (!relocate(version_tracker, plain_reloc_iterator(plt_rel_, plt_rel_count_), global_group, local_group)) { return false; |