Skip to content

Commit

Permalink
Emit DT_AARCH64_VARIANT_PCS if required
Browse files Browse the repository at this point in the history
Fixes #1115
  • Loading branch information
rui314 committed Sep 28, 2023
1 parent b8bcb8a commit 2e3b56e
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
30 changes: 30 additions & 0 deletions elf/elf.h
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ enum : u32 {
DT_VERNEEDNUM = 0x6fffffff,
DT_PPC_GOT = 0x70000000,
DT_PPC64_GLINK = 0x70000000,
DT_AARCH64_VARIANT_PCS = 0x70000005,
DT_AUXILIARY = 0x7ffffffd,
DT_FILTER = 0x7fffffff,
};
Expand Down Expand Up @@ -1708,6 +1709,35 @@ struct ElfNhdr {
// Target-specific ELF data types
//

template <>
struct ElfSym<ARM64> {
bool is_undef() const { return st_shndx == SHN_UNDEF; }
bool is_abs() const { return st_shndx == SHN_ABS; }
bool is_common() const { return st_shndx == SHN_COMMON; }
bool is_weak() const { return st_bind == STB_WEAK; }
bool is_undef_weak() const { return is_undef() && is_weak(); }

ul32 st_name;

#ifdef __LITTLE_ENDIAN__
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u8 : 5;
u8 arm64_variant_pcs : 1; // ARM64-specific
#else
u8 st_bind : 4;
u8 st_type : 4;
u8 arm64_variant_pcs : 1;
u8 : 6;
u8 st_visibility : 2;
#endif

ul16 st_shndx;
ul64 st_value;
ul64 st_size;
};

template <>
struct ElfSym<PPC64V2> {
bool is_undef() const { return st_shndx == SHN_UNDEF; }
Expand Down
28 changes: 28 additions & 0 deletions elf/output-chunks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -632,6 +632,27 @@ void SymtabSection<E>::copy_buf(Context<E> &ctx) {
});
}

// An ARM64 function with a non-standard calling convention is marked with
// STO_AARCH64_VARIANT_PCS bit in the symbol table.
//
// A function with that bit is not safe to be called through a lazy PLT
// stub because the PLT resolver may clobber registers that should be
// preserved in a non-standard calling convention.
//
// To solve the problem, the dynamic linker scans the dynamic symbol table
// at process startup time and resolve symbols with STO_AARCH64_VARIANT_PCS
// bit eagerly, so that the PLT resolver won't be called for that symbol
// lazily. As an optimization, it does so only when DT_AARCH64_VARIANT_PCS
// is set in the dynamic section.
//
// This function returns true if DT_AARCH64_VARIANT_PCS needs to be set.
static bool contains_variant_pcs(Context<ARM64> &ctx) {
for (Symbol<ARM64> *sym : ctx.plt->symbols)
if (sym->esym().arm64_variant_pcs)
return true;
return false;
}

template <typename E>
static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
std::vector<Word<E>> vec;
Expand Down Expand Up @@ -781,6 +802,10 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
if (flags1)
define(DT_FLAGS_1, flags1);

if constexpr (is_arm64<E>)
if (contains_variant_pcs(ctx))
define(DT_AARCH64_VARIANT_PCS, 1);

if constexpr (is_ppc32<E>)
define(DT_PPC_GOT, ctx.gotplt->shdr.sh_addr);

Expand Down Expand Up @@ -1561,6 +1586,9 @@ ElfSym<E> to_output_esym(Context<E> &ctx, Symbol<E> &sym, u32 st_name,
else
esym.st_bind = sym.esym().st_bind;

if constexpr (is_arm64<E>)
esym.arm64_variant_pcs = sym.esym().arm64_variant_pcs;

if constexpr (is_ppc64v2<E>)
esym.ppc_local_entry = sym.esym().ppc_local_entry;

Expand Down
23 changes: 23 additions & 0 deletions test/elf/aarch64_variant-pcs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
. $(dirname $0)/common.inc

[ $MACHINE = aarch64 ] || skip

cat <<EOF | $CC -c -o $t/a.o -fPIC -xassembler -
.global foo
.type foo, %function
.variant_pcs foo
foo:
ret
EOF

$CC -B. -shared -o $t/b.so $t/a.o
readelf -W --dyn-syms $t/b.so | grep -Eq '\[VARIANT_PCS\].* foo$'

cat <<EOF | $CC -c -o $t/c.o -xc -
void foo();
int main() { foo(); }
EOF

$CC -B. -o $t/exe $t/c.o $t/b.so
readelf -W --dynamic $t/exe | grep -q AARCH64_VARIANT_PCS

0 comments on commit 2e3b56e

Please sign in to comment.