Resolves ifunc targets before `main()` runs in static libc This enables static binaries to use ifunc-based dispatch during early process startup, so optimized implementations can be selected based on CPU features. Without this relocation step in startup, those targets are not ready when program code begins executing. This change: - adds IRELATIVE relocation handling for x86_64, AArch64, ARMv7 and RISC-V, - reads `AT_HWCAP` / `AT_HWCAP2` from auxv and passes them to resolvers where required (notably AArch64), - runs IRELATIVE processing after base-address discovery and before TLS setup, - adds integration tests for both the ifunc path and the no-ifunc path, - Changed the load bias type for ptrdiff_t to intptr_t to align with IRELATIVE handling, which uses intptr_t for load bias calculations.
41 lines
1.5 KiB
C++
41 lines
1.5 KiB
C++
//===-- Implementation of apply_irelative_relocs (AArch64) ----------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "startup/linux/irelative.h"
|
|
#include "hdr/elf_macros.h"
|
|
#include "hdr/elf_proxy.h"
|
|
#include "hdr/link_macros.h"
|
|
#include "src/__support/macros/config.h"
|
|
|
|
namespace LIBC_NAMESPACE_DECL {
|
|
|
|
void apply_irelative_relocs(intptr_t base, unsigned long hwcap,
|
|
unsigned long hwcap2) {
|
|
for (const ElfW(Rela) *rela = __rela_iplt_start; rela != __rela_iplt_end;
|
|
++rela) {
|
|
if (ELF64_R_TYPE(rela->r_info) != R_AARCH64_IRELATIVE)
|
|
continue;
|
|
|
|
// AArch64 resolvers receive hwcap and hwcap2.
|
|
// Use unsigned arithmetic to avoid undefined behavior on signed overflow,
|
|
// which can occur with very large binaries or high load addresses.
|
|
uintptr_t resolver_addr =
|
|
static_cast<uintptr_t>(base) + static_cast<uintptr_t>(rela->r_addend);
|
|
auto resolver =
|
|
reinterpret_cast<uintptr_t (*)(unsigned long, unsigned long)>(
|
|
resolver_addr);
|
|
uintptr_t result = resolver(hwcap, hwcap2);
|
|
|
|
// Write the resolved function pointer to the target location.
|
|
uintptr_t target_addr = static_cast<uintptr_t>(base) + rela->r_offset;
|
|
*reinterpret_cast<uintptr_t *>(target_addr) = result;
|
|
}
|
|
}
|
|
|
|
} // namespace LIBC_NAMESPACE_DECL
|