| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_RELOCATION_H_ |
| #define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_RELOCATION_H_ |
| |
| #include <lib/stdcompat/bit.h> |
| #include <lib/stdcompat/span.h> |
| |
| #include <algorithm> |
| #include <type_traits> |
| #include <variant> |
| |
| #include "machine.h" |
| |
| namespace elfldltl { |
| |
| // This represents the ELF metadata in an ELF file that directs what dynamic |
| // relocation it requires. It holds spans of the various raw relocation record |
| // types and provides a uniform visitor pattern for iterating over them. |
| // |
| // Both REL and RELA formats are tracked here. Within each format, "relative" |
| // (simple fixup) relocation and "symbolic" (general) relocations are provided |
| // as separate subspans. The RELR format is held as a raw span of words. PLT |
| // (JMPREL) relocations use either REL or RELA format (but can't have both in |
| // parallel as general relocations do) and so are represented using a |
| // std::variant type across the REL and RELA container formats. |
| // |
| // The VisitRelative and VisitSymbolic methods can be used like std::visit to |
| // call a callback function (a generic lambda or other polymorphic callable) |
| // with each record, stopping early if the callback returns false. |
| // VisitSymbolic passes either an Elf::Rela or an Elf::Rel that uses an |
| // in-place addend. VisitRelative passes either an Elf::Rela with separate |
| // address and addend, or an Elf::size_type address using an in-place addend. |
| |
| template <class Elf> |
| class RelocationInfo { |
| public: |
| using size_type = typename Elf::size_type; |
| using Addr = typename Elf::Addr; |
| using Rel = typename Elf::Rel; |
| using Rela = typename Elf::Rela; |
| |
| // These span types hold the various relocation tables in their raw forms. |
| // The JMPREL table is in either REL or RELA format, so a variant is used. |
| using RelTable = cpp20::span<const Rel>; |
| using RelaTable = cpp20::span<const Rela>; |
| using RelrTable = cpp20::span<const Addr>; |
| using JmprelTable = std::variant<RelTable, RelaTable>; |
| |
| // Fetch the various relocation tables. The REL and RELA tables have |
| // relative and symbolic subsets. The RELR table needs further decoding. |
| // Enumeration should use the VisitRelative and VisitSymbolic methods, below. |
| |
| constexpr RelTable rel_relative() const { return rel_.subspan(0, relcount_); } |
| |
| constexpr RelTable rel_symbolic() const { return rel_.subspan(relcount_); } |
| |
| constexpr RelaTable rela_relative() const { return rela_.subspan(0, relacount_); } |
| |
| constexpr RelaTable rela_symbolic() const { return rela_.subspan(relacount_); } |
| |
| constexpr RelrTable relr() const { return relr_; } |
| |
| constexpr JmprelTable jmprel() const { return jmprel_; } |
| |
| // Install data for the various relocation tables. These return *this so |
| // they can be called in fluent style, e.g. in a constexpr initializer. |
| |
| constexpr RelocationInfo& set_rel(RelTable relocs, size_type relcount) { |
| rel_ = relocs; |
| relcount_ = std::min(relcount, static_cast<size_type>(rel_.size())); |
| return *this; |
| } |
| |
| constexpr RelocationInfo& set_rela(RelaTable relocs, size_type relacount) { |
| rela_ = relocs; |
| relacount_ = std::min(relacount, static_cast<size_type>(rela_.size())); |
| return *this; |
| } |
| |
| constexpr RelocationInfo& set_relr(RelrTable table) { |
| relr_ = table; |
| return *this; |
| } |
| |
| constexpr RelocationInfo& set_jmprel(JmprelTable table) { |
| jmprel_ = table; |
| return *this; |
| } |
| |
| // Return the number of valid entries in the table, from rel_relative(), |
| // rela_relative(), or relr(). Hence returns relocs.size() if all entries |
| // are valid, or else the index of the first invalid entry. |
| |
| template <ElfMachine Machine, class Reloc> // DT_REL or DT_RELA |
| static size_t ValidateRelative(cpp20::span<const Reloc> relocs) { |
| constexpr auto valid = [](const auto& reloc) -> bool { |
| constexpr uint32_t relative_type = |
| static_cast<uint32_t>(RelocationTraits<Machine>::Type::kRelative); |
| return reloc.type() == relative_type; |
| }; |
| return std::count_if(relocs.begin(), relocs.end(), valid); |
| } |
| |
| static size_t ValidateRelative(cpp20::span<const Addr> relocs) { // DT_RELR |
| // The first entry must be a fresh address (low bit clear), and all |
| // possible bit patterns are valid for all subsequent entries. |
| return (relocs.empty() || (relocs.front() & 1) != 0) ? 0 : relocs.size(); |
| } |
| |
| // Call visit(Elf::Rela reloc) -> bool or visit(Elf::size_type addr) on every |
| // location needing simple fixup. The Elf::size_type signature indicates the |
| // addend is to be read from the relocated address itself. Returns false the |
| // first time visit returns false, otherwise true. |
| template <typename Visitor> |
| constexpr bool VisitRelative(Visitor&& visit) const { |
| static_assert(std::is_invocable_r_v<bool, Visitor, size_type>); |
| |
| auto visit_all_rel = VisitAll([&visit](const Rel& reloc) -> bool { |
| size_type location = reloc.offset; |
| return visit(location); |
| }); |
| |
| auto visit_all_rela = VisitAll(visit); |
| |
| auto visit_all_relr = [&visit](RelrTable relr) -> bool { |
| size_type r_offset = 0; // Implied r_offset value for the last entry. |
| for (size_type entry : relr) { |
| // If the low bit is clear, this is a new address. |
| // This is like an Elf::Rel record with offset = entry. |
| if ((entry & 1) == 0) { |
| r_offset = entry; |
| if (!visit(r_offset)) { |
| return false; |
| } |
| } else { |
| // This is a bitmap representing the next Elf::kAddressBits - 1 |
| // address-size words after r_offset. |
| size_type bitmap = entry >> 1; |
| |
| // The low bit corresponds to the word after the r_offset value left |
| // from the last entry, so advance r_offset a word for every bit. |
| size_type bitmap_offset = r_offset; |
| r_offset += (Elf::kAddressBits - 1) * sizeof(size_type); |
| |
| // Now visit the address corresponding to each one bit, as if there |
| // were an Elf::Rel record with the r_offset implied by this bit |
| // position incrementing the running address by address-size per bit. |
| while (bitmap != 0) { |
| int skip = cpp20::countr_zero(bitmap) + 1; |
| bitmap >>= skip; |
| bitmap_offset += skip * sizeof(size_type); |
| if (!visit(bitmap_offset)) { |
| return false; |
| } |
| } |
| } |
| } |
| return true; |
| }; |
| |
| return visit_all_rel(rel_relative()) && visit_all_rela(rela_relative()) && |
| visit_all_relr(relr()); |
| } |
| |
| // Call visit(Elf::Rel) -> bool or visit(Elf::Rela) -> bool on every symbolic |
| // relocation record. Returns false the first time visit returns false, |
| // otherwise true. |
| template <typename Visitor> |
| constexpr bool VisitSymbolic(Visitor&& visit) const { |
| static_assert(std::is_invocable_r_v<bool, Visitor, const Rel&>); |
| static_assert(std::is_invocable_r_v<bool, Visitor, const Rela&>); |
| |
| auto visit_all = VisitAll(std::forward<Visitor>(visit)); |
| |
| return visit_all(rel_symbolic()) && visit_all(rela_symbolic()) && |
| std::visit(visit_all, jmprel_); |
| } |
| |
| private: |
| // Returns lambda visit_all(span<T>) -> bool that calls visit(T) -> bool. |
| static constexpr auto VisitAll = [](auto&& visit) { |
| return [visit = std::forward<decltype(visit)>(visit)](auto table) -> bool { |
| return std::all_of(table.begin(), table.end(), visit); |
| }; |
| }; |
| |
| RelTable rel_; |
| size_type relcount_ = 0; |
| RelaTable rela_; |
| size_type relacount_ = 0; |
| RelrTable relr_; |
| JmprelTable jmprel_; |
| }; |
| |
| } // namespace elfldltl |
| |
| #endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_RELOCATION_H_ |