blob: 5acf112614abd2619b6fef054302f12b3d5af2df [file] [log] [blame] [edit]
// Copyright 2024 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.
#include "lib/ld/testing/startup-ld-abi.h"
// Avoid symbol conflicts between <ld/abi/abi.h> and <link.h>.
#pragma push_macro("_r_debug")
#undef _r_debug
#define _r_debug not_using_system_r_debug
#include <link.h>
#pragma pop_macro("_r_debug")
#include <lib/elfldltl/container.h>
#include <lib/elfldltl/diagnostics.h>
#include <lib/elfldltl/dynamic.h>
#include <lib/elfldltl/layout.h>
#include <lib/elfldltl/load.h>
#include <lib/elfldltl/note.h>
#include <lib/elfldltl/phdr.h>
#include <lib/ld/abi.h>
#include <lib/ld/load.h>
#include <lib/ld/memory.h>
#include <lib/ld/module.h>
#include <lib/ld/tls.h>
#include <unistd.h>
#include <zircon/assert.h>
#include <bit>
#include <vector>
namespace ld::testing {
namespace {
using Elf = elfldltl::Elf<>;
using AbiModule = abi::Abi<>::Module;
// A container of abi modules (i.e. startup modules) loaded with this test.
std::vector<AbiModule> gModuleStorage;
using TlsModule = abi::Abi<>::TlsModule;
std::vector<TlsModule> gTlsModuleStorage;
using Offset = abi::Abi<>::Addr;
std::vector<Offset> gTlsOffsetStorage;
struct DecodedModule {
AbiModule abi_module;
std::optional<TlsModule> tls_module;
std::optional<Offset> tls_offset;
};
// This class is a wrapper around the ModuleMemory object that handles
// addresses read from the .dynamic section in memory as modified by glibc.
//
// This "adaptive" memory object will attempt to perform a read on the memory
// object using the initial `ptr` value passed in: if that read fails, it will
// try to read again with `ptr - load_bias`, as is needed for linux.
class AdjustLoadBiasAdaptor : public ModuleMemory {
public:
using Base = ModuleMemory;
explicit AdjustLoadBiasAdaptor(const AbiModule& module, size_t load_bias)
: Base(module), load_bias_(load_bias) {}
template <typename T>
std::optional<std::span<const T>> ReadArray(uintptr_t ptr, size_t count) {
auto result = Base::ReadArray<T>(ptr, count);
if (!result) {
return Base::ReadArray<T>(ptr - load_bias_);
}
return result;
}
template <typename T>
std::optional<std::span<const T>> ReadArray(uintptr_t ptr) {
auto result = Base::ReadArray<T>(ptr);
if (!result) {
return Base::ReadArray<T>(ptr - load_bias_);
}
return result;
}
private:
Elf::Addr load_bias_ = 0;
};
// TODO(https://fxbug.dev/324136435): Share SetTLs implementation with
// //sdk/lib/ld/include/lib/ld/decoded-module.h
std::optional<TlsModule> SetTls(auto& diag, const Elf::Phdr& tls_phdr, auto& memory) {
using PhdrError = elfldltl::internal::PhdrError<elfldltl::ElfPhdrType::kTls>;
Elf::size_type alignment = std::max<Elf::size_type>(tls_phdr.align, 1);
if (!std::has_single_bit(alignment)) [[unlikely]] {
diag.FormatError(PhdrError::kBadAlignment);
return {};
}
if (tls_phdr.filesz > tls_phdr.memsz) [[unlikely]] {
diag.FormatError("PT_TLS header `p_filesz > p_memsz`");
return {};
}
auto initial_data = memory.template ReadArray<std::byte>(tls_phdr.vaddr, tls_phdr.filesz);
if (!initial_data) [[unlikely]] {
diag.FormatError("PT_TLS has invalid p_vaddr", elfldltl::FileAddress{tls_phdr.vaddr},
" or p_filesz ", tls_phdr.filesz());
return {};
}
return TlsModule{
.tls_initial_data = *initial_data,
.tls_bss_size = tls_phdr.memsz - tls_phdr.filesz,
.tls_alignment = alignment,
};
}
// Decode an AbiModule from the provided `dl_phdr_info`.
DecodedModule DecodeModule(const dl_phdr_info& phdr_info) {
static const size_t kPageSize = sysconf(_SC_PAGE_SIZE);
// Use panic diagnostics to abort and print to stderr in the event any of the
// following functions fail.
elfldltl::Diagnostics diag{
elfldltl::PrintfDiagnosticsReport(__zx_panic, phdr_info.dlpi_name, ": "),
elfldltl::DiagnosticsPanicFlags(),
};
std::optional<Elf::Phdr> dyn_phdr;
std::optional<Elf::Phdr> tls_phdr;
elfldltl::Elf<>::size_type vaddr_start, vaddr_size;
elfldltl::LoadInfo<Elf, elfldltl::StdContainer<std::vector>::Container> load_info;
std::span phdrs{reinterpret_cast<const Elf::Phdr*>(phdr_info.dlpi_phdr), phdr_info.dlpi_phnum};
elfldltl::DecodePhdrs(
diag, phdrs, elfldltl::PhdrDynamicObserver<Elf>(dyn_phdr),
elfldltl::PhdrTlsObserver<Elf>(tls_phdr),
elfldltl::PhdrLoadObserver<elfldltl::Elf<>>(kPageSize, vaddr_start, vaddr_size),
load_info.GetPhdrObserver(kPageSize));
vaddr_start += phdr_info.dlpi_addr;
AbiModule module{
.link_map =
{
.addr = phdr_info.dlpi_addr,
.name = elfldltl::AbiPtr<const char>(phdr_info.dlpi_name),
},
.vaddr_start = vaddr_start,
.vaddr_end = vaddr_start + vaddr_size,
.phdrs = phdrs,
.tls_modid = phdr_info.dlpi_tls_modid,
};
AdjustLoadBiasAdaptor memory(module, phdr_info.dlpi_addr);
elfldltl::DecodePhdrs(diag, phdrs, PhdrMemoryBuildIdObserver(memory, module));
auto count = dyn_phdr->filesz() / sizeof(Elf::Dyn);
std::span dyn = *memory.ReadArray<Elf::Dyn>(dyn_phdr->vaddr, count);
elfldltl::DecodeDynamic(diag, memory, dyn, elfldltl::DynamicSymbolInfoObserver(module.symbols));
module.link_map.ld = dyn.data();
module.soname = module.symbols.soname();
// All test abi modules that are loaded at startup will have global symbol
// visibility.
module.symbols_visible = true;
std::optional<TlsModule> tls_module;
std::optional<Offset> tls_offset;
if (module.tls_modid > 0) {
assert(tls_phdr);
tls_module = SetTls(diag, *tls_phdr, memory);
assert(phdr_info.dlpi_tls_data);
tls_offset = TpRelativeToOffset(phdr_info.dlpi_tls_data);
} else {
assert(!tls_phdr);
}
return {.abi_module = module, .tls_module = tls_module, .tls_offset = tls_offset};
}
int AddModule(dl_phdr_info* phdr_info, size_t size, void* data) {
assert(size >= sizeof(*phdr_info));
DecodedModule decoded = DecodeModule(*phdr_info);
gModuleStorage.push_back(decoded.abi_module);
if (decoded.tls_module) {
Elf::size_type distance = *decoded.tls_offset;
if constexpr (elfldltl::TlsTraits<>::kTlsNegative) {
distance = -distance; // The size is included in the negated offset.
} else {
distance += decoded.tls_module->tls_size();
}
auto& layout = *static_cast<elfldltl::TlsLayout<>*>(data);
layout = {std::max(layout.size_bytes(), distance),
std::max(layout.alignment(), decoded.tls_module->tls_alignment())};
assert(decoded.abi_module.tls_modid > 0);
ptrdiff_t idx = static_cast<ptrdiff_t>(decoded.abi_module.tls_modid) - 1;
gTlsModuleStorage.insert(gTlsModuleStorage.begin() + idx, *decoded.tls_module);
gTlsOffsetStorage.insert(gTlsOffsetStorage.begin() + idx, *decoded.tls_offset);
}
return 0;
}
// This function populates an abi::Abi<> object. It iterates over and
// decodes the modules that are loaded with this test program to fill out abi
// information and returns the finished abi object to the caller.
abi::Abi<> PopulateLdAbi() {
elfldltl::TlsLayout<> tls_layout;
ZX_ASSERT(!dl_iterate_phdr(AddModule, &tls_layout));
// Connect the link_map list pointers for each abi module and assign a
// symbolizer_modid.
uint32_t symbolizer_modid = 0;
auto prev = gModuleStorage.begin();
for (auto it = std::next(prev); it != gModuleStorage.end(); ++it) {
// The loop doesn't visit the first module (the application), which
// implicitly gets ID 0 (and nullptr in .prev) from default construction.
it->symbolizer_modid = ++symbolizer_modid;
auto& prev_link_map = prev->link_map;
auto& this_link_map = it->link_map;
prev_link_map.next = &this_link_map;
this_link_map.prev = &prev_link_map;
prev = it;
}
return {
.loaded_modules{&gModuleStorage.front()},
.loaded_modules_count{gModuleStorage.size()},
.static_tls_modules{gTlsModuleStorage},
.static_tls_offsets{gTlsOffsetStorage},
.static_tls_layout{tls_layout},
};
}
} // namespace
const abi::Abi<> gStartupLdAbi = PopulateLdAbi();
} // namespace ld::testing