blob: 22325384564c72965f0f71e5f99ebdec88a49b1e [file] [log] [blame]
// 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_STATIC_PIE_WITH_VDSO_H_
#define SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_STATIC_PIE_WITH_VDSO_H_
#include <lib/fit/result.h>
#include <atomic>
#include <tuple>
#include <utility>
#include "diagnostics.h"
#include "dynamic.h"
#include "link.h"
#include "memory.h"
#include "relocation.h"
#include "self.h"
#include "symbol.h"
// This file implements self-relocation for a static PIE that can have limited
// symbolic relocations against a single vDSO module.
//
// The instantiation of the templates here must be statically linked into the
// startup code of the PIE. It must be called before anything that uses any
// relocated data, including implicit GOT or PLT references--i.e. anything not
// explicitly declared with [[gnu::visibility("hidden")]]--or initialized data
// containing pointer values.
//
// This supports not only simple fixup but symbolic relocation too. However
// this uses the most trivial symbol resolution rules: all symbolic relocations
// are presumed to use undefined symbols that must be resolved in the vDSO
// symbol table.
namespace elfldltl {
// Do self-relocation against the vDSO so system calls can be made normally.
// This is the simplified all-in-one version that decodes the all vDSO details
// from memory itself. It returns the the program's own SymbolInfo data; see
// <lib/elfldltl/symbol.h> for details.
template <class Self, class DiagnosticsType>
inline SymbolInfo<typename Self::Elf> LinkStaticPieWithVdso( //
const Self& self, DiagnosticsType& diagnostics, const void* vdso_base);
// This version takes vDSO details already distilled separately.
template <class Self, class DiagnosticsType>
inline SymbolInfo<typename Self::Elf> LinkStaticPieWithVdso(
const Self& self, DiagnosticsType& diagnostics,
const SymbolInfo<typename Self::Elf>& vdso_symbols, typename Self::Elf::size_type vdso_bias) {
using namespace std::literals;
using Elf = typename Self::Elf;
using size_type = typename Elf::size_type;
using Sym = typename Elf::Sym;
auto memory = Self::Memory();
auto bias = static_cast<size_type>(Self::LoadBias());
// Collect our own information.
RelocationInfo<Elf> reloc_info;
SymbolInfo<Elf> symbol_info;
DecodeDynamic(diagnostics, memory, Self::Dynamic(), //
DynamicRelocationInfoObserver(reloc_info), //
DynamicSymbolInfoObserver(symbol_info));
// Apply simple fixups first, just in case anything else needs them done.
if (RelocateRelative(diagnostics, memory, reloc_info, bias)) {
std::atomic_signal_fence(std::memory_order_seq_cst);
} else [[unlikely]] {
__builtin_trap();
}
// This communicates the results of a symbol lookup back to RelocateSymbolic.
struct Definition {
constexpr bool undefined_weak() const { return false; }
constexpr const Sym& symbol() const { return symbol_; }
constexpr size_type bias() const { return bias_; }
// These will never actually be called.
constexpr size_type tls_module_id() const { return 0; }
constexpr size_type static_tls_bias() const { return 0; }
constexpr fit::result<bool, typename Elf::TlsDescGot> tls_desc(
DiagnosticsType& diagnostics) const {
return fit::error{false};
}
constexpr typename Elf::TlsDescGot tls_desc_undefined_weak() const { return {}; }
const Sym& symbol_;
size_type bias_;
};
// Symbol resolution is trivial: it's defined in the vDSO (or we crash).
auto resolve = [&](const auto& ref, RelocateTls tls_type) //
-> fit::result<bool, Definition> {
if (tls_type != RelocateTls::kNone) [[unlikely]] {
return fit::error{diagnostics.FormatError("TLS relocations not supported in vDSO"sv)};
}
SymbolName name(symbol_info, ref);
const Sym* vdso_sym = name.Lookup(vdso_symbols);
if (!vdso_sym) [[unlikely]] {
return fit::error{diagnostics.FormatError("reference to symbol not defined in vDSO"sv, name)};
}
return fit::ok(Definition{*vdso_sym, vdso_bias});
};
// Apply all the symbolic relocations, resolving each reference in the vDSO.
if (RelocateSymbolic(memory, diagnostics, reloc_info, symbol_info, bias, resolve)) {
std::atomic_signal_fence(std::memory_order_seq_cst);
} else [[unlikely]] {
__builtin_trap();
}
return symbol_info;
}
// This distills the vDSO symbols and load bias from the image in memory.
template <class Elf, class DiagnosticsType>
inline std::pair<SymbolInfo<Elf>, uintptr_t> GetVdsoSymbols(DiagnosticsType& diagnostics,
const void* vdso_base) {
using Ehdr = typename Elf::Ehdr;
using Phdr = typename Elf::Phdr;
using Dyn = typename Elf::Dyn;
SymbolInfo<Elf> vdso_symbols;
DirectMemory vdso_image(
{
static_cast<std::byte*>(const_cast<void*>(vdso_base)),
SIZE_MAX,
},
0);
const Ehdr& vdso_ehdr = *vdso_image.ReadFromFile<Ehdr>(0);
cpp20::span<const Phdr> vdso_phdrs = *vdso_image.ReadArrayFromFile<Phdr>(
vdso_ehdr.phoff, NoArrayFromFile<Phdr>(), vdso_ehdr.phnum);
constexpr uintptr_t kNoAddr = ~uintptr_t{};
uintptr_t vdso_image_vaddr = kNoAddr;
for (const auto& ph : vdso_phdrs) {
if (ph.type == ElfPhdrType::kDynamic) {
auto dyn = vdso_image.ReadArray<Dyn>(ph.vaddr(), ph.filesz() / sizeof(Dyn));
if (!dyn) [[unlikely]] {
diagnostics.FormatError("cannot read vDSO PT_DYNAMIC"sv, FileAddress{ph.vaddr()});
__builtin_trap();
}
DecodeDynamic(diagnostics, vdso_image, *dyn, DynamicSymbolInfoObserver(vdso_symbols));
} else if (ph.type == ElfPhdrType::kLoad && vdso_image_vaddr == kNoAddr) {
vdso_image_vaddr = ph.vaddr;
}
}
if (vdso_image_vaddr == kNoAddr) [[unlikely]] {
diagnostics.FormatError("no PT_LOAD found in vDSO"sv);
__builtin_trap();
}
auto vdso_bias = reinterpret_cast<uintptr_t>(vdso_base) - vdso_image_vaddr;
return {vdso_symbols, vdso_bias};
}
// This just combines the two functions above.
template <class Self, class DiagnosticsType>
inline SymbolInfo<typename Self::Elf> LinkStaticPieWithVdso( //
const Self& self, DiagnosticsType& diagnostics, const void* vdso_base) {
using Elf = typename Self::Elf;
// Fetch the vDSO symbol table.
auto [vdso_symbols, vdso_bias] = GetVdsoSymbols<Elf>(diagnostics, vdso_base);
// The main work is done in the overload defined above.
return LinkStaticPieWithVdso(self, diagnostics, vdso_symbols, vdso_bias);
}
} // namespace elfldltl
#endif // SRC_LIB_ELFLDLTL_INCLUDE_LIB_ELFLDLTL_STATIC_PIE_WITH_VDSO_H_