mirror of
https://github.com/rui314/mold.git
synced 2026-11-09 04:49:33 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c57eca7b0 |
No files matched your search
+3
-2
@@ -275,8 +275,8 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
# compiler instances. This is hacky but greatly reduces compile time
|
||||
# on a multicore machine.
|
||||
list(APPEND MOLD_ELF_TARGETS
|
||||
X86_64 I386 ARM64 ARM32 RV32LE RV32BE RV64LE RV64BE
|
||||
PPC32 PPC64V1 PPC64V2 S390X SPARC64 M68K SH4 ALPHA)
|
||||
X86_64 I386 ARM64 ARM32 RV32LE RV32BE RV64LE RV64BE PPC32 PPC64V1 PPC64V2
|
||||
S390X SPARC64 M68K SH4 ALPHA HPPA32)
|
||||
|
||||
list(APPEND MOLD_ELF_TEMPLATE_FILES
|
||||
elf/cmdline.cc
|
||||
@@ -340,6 +340,7 @@ target_sources(mold PRIVATE
|
||||
elf/arch-alpha.cc
|
||||
elf/arch-arm32.cc
|
||||
elf/arch-arm64.cc
|
||||
elf/arch-hppa32.cc
|
||||
elf/arch-i386.cc
|
||||
elf/arch-m68k.cc
|
||||
elf/arch-ppc32.cc
|
||||
|
||||
@@ -0,0 +1,506 @@
|
||||
// HP/PA (Hewlett-Packard Precision Architecture), also known as PA-RISC,
|
||||
// is a RISC ISA developed by HP in the '80s. It was replaced by Itanium
|
||||
// which was a joint project between Intel and HP in the early 2000s. No
|
||||
// new HP/PA processors have been created after 2005.
|
||||
//
|
||||
// Thread pointer (TP) is stored to the control register cr27, which is an
|
||||
// alias for tr3.
|
||||
//
|
||||
// $ltp (linkage table pointer) is $r19
|
||||
// $dp is $r27
|
||||
|
||||
|
||||
#include "mold.h"
|
||||
|
||||
namespace mold::elf {
|
||||
|
||||
using E = HPPA32;
|
||||
|
||||
static u64 RND(u64 x) { return (x + 0x1000) & ~(u64)0x1fff; }
|
||||
static u64 L(u64 x) { return x & 0xffff'f800; }
|
||||
static u64 R(u64 x) { return x & 0x0000'07ff; }
|
||||
static u64 LR(u64 x, u64 a) { return L(x + RND(a)); }
|
||||
static u64 RR(u64 x, u64 a) { return R(x + RND(a)) + a - RND(a); }
|
||||
|
||||
static u64 get_gp(Context<E> &ctx) {
|
||||
if (ctx.arg.pic)
|
||||
return ctx.gotplt->shdr.sh_addr;
|
||||
return ctx.extra.global->value;
|
||||
}
|
||||
|
||||
static u32 dis_assemble_17(u32 val) {
|
||||
return ((val & 0x10000) >> 16) |
|
||||
((val & 0x0f800) << 5) |
|
||||
((val & 0x00400) >> 8) |
|
||||
((val & 0x003ff) << 3);
|
||||
}
|
||||
|
||||
static u32 dis_assemble_21(u32 val) {
|
||||
return (bits(val, 6, 2) << 16) | (bits(val, 8, 7) << 14) |
|
||||
(bits(val, 1, 0) << 12) | (bits(val, 19, 9) << 1) |
|
||||
bit(val, 20);
|
||||
}
|
||||
|
||||
static u32 dis_low_sign_ext(u32 val, u32 len) {
|
||||
return (bits(val, len - 2, 0) << 1) | bit(val, len - 1);
|
||||
}
|
||||
|
||||
template <>
|
||||
void write_plt_header(Context<E> &ctx, u8 *buf) {}
|
||||
|
||||
template <>
|
||||
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
|
||||
|
||||
template <>
|
||||
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {}
|
||||
|
||||
template <>
|
||||
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
|
||||
u64 offset, u64 val) {
|
||||
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_NONE:
|
||||
break;
|
||||
case R_PARISC_PCREL32:
|
||||
*(ub32 *)loc = val - this->shdr.sh_addr - offset;
|
||||
break;
|
||||
case R_PARISC_SEGREL32:
|
||||
*(ub32 *)loc = val;
|
||||
break;
|
||||
default:
|
||||
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
ElfRel<E> *dynrel = nullptr;
|
||||
if (ctx.reldyn)
|
||||
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
|
||||
file.reldyn_offset + this->reldyn_offset);
|
||||
|
||||
u64 SB = 0;
|
||||
u64 GP = get_gp(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
#define S sym.get_addr(ctx)
|
||||
#define A get_addend(loc, rel)
|
||||
#define P (get_addr() + rel.r_offset)
|
||||
#define G (sym.get_got_idx(ctx) * sizeof(Word<E>))
|
||||
#define GOT ctx.got->shdr.sh_addr
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PARISC_DIR32:
|
||||
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
|
||||
break;
|
||||
case R_PARISC_DIR21L:
|
||||
*(ub32 *)loc |= dis_assemble_21(bits(LR(S, A), 31, 11));
|
||||
break;
|
||||
case R_PARISC_DIR14R:
|
||||
*(ub32 *)loc |= dis_low_sign_ext(RR(S, A), 14);
|
||||
break;
|
||||
case R_PARISC_PCREL32: {
|
||||
u64 addr = has_thunk(i) ? get_thunk_addr(i) : S;
|
||||
*(ub32 *)loc = addr + A - P - 8;
|
||||
break;
|
||||
}
|
||||
case R_PARISC_PCREL21L: {
|
||||
u64 addr = has_thunk(i) ? get_thunk_addr(i) : S;
|
||||
*(ub32 *)loc |= dis_assemble_21(bits(addr + A - P - 8, 31, 11));
|
||||
break;
|
||||
}
|
||||
case R_PARISC_PCREL17F: {
|
||||
u64 addr = has_thunk(i) ? get_thunk_addr(i) : S;
|
||||
*(ub32 *)loc |= dis_assemble_17((addr + A - P - 8) >> 2);
|
||||
break;
|
||||
}
|
||||
case R_PARISC_PCREL14R: {
|
||||
u64 addr = has_thunk(i) ? get_thunk_addr(i) : S;
|
||||
*(ub32 *)loc |= dis_low_sign_ext(R(addr - P - 8 + A), 14);
|
||||
break;
|
||||
}
|
||||
case R_PARISC_DPREL21L:
|
||||
*(ub32 *)loc |= dis_assemble_21(bits(LR(S - GP, A), 31, 11));
|
||||
break;
|
||||
case R_PARISC_DPREL14R:
|
||||
*(ub32 *)loc |= dis_low_sign_ext(RR(S - GP, A), 14);
|
||||
break;
|
||||
case R_PARISC_DLTIND21L:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |= dis_assemble_21(bits(G + GOT - GP, 31, 11));
|
||||
break;
|
||||
case R_PARISC_DLTIND14R:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |= dis_low_sign_ext(R(G + GOT - GP), 14);
|
||||
break;
|
||||
case R_PARISC_SEGREL32:
|
||||
*(ub32 *)loc = S + A - SB;
|
||||
break;
|
||||
case R_PARISC_PLABEL32:
|
||||
if (sym.is_remaining_undef_weak()) {
|
||||
*(ub32 *)loc = 0;
|
||||
} else if (ctx.arg.pic) {
|
||||
if (sym.is_imported)
|
||||
*dynrel++ = ElfRel<E>(P, R_PARISC_PLABEL32, sym.get_dynsym_idx(ctx), 0);
|
||||
else
|
||||
*dynrel++ = ElfRel<E>(P, R_PARISC_PLABEL32, 0, sym.get_opd_addr(ctx) + 2);
|
||||
*(ub32 *)loc = 0;
|
||||
} else {
|
||||
*(ub32 *)loc = sym.get_opd_addr(ctx) + 2;
|
||||
}
|
||||
break;
|
||||
case R_PARISC_TPREL21L:
|
||||
*(ub32 *)loc |= dis_assemble_21(bits(LR(S - ctx.tp_addr, A), 31, 11));
|
||||
break;
|
||||
case R_PARISC_TPREL14R:
|
||||
*(ub32 *)loc |= dis_low_sign_ext(RR(S - ctx.tp_addr, A), 14);
|
||||
break;
|
||||
case R_PARISC_LTOFF_TP21L:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |=
|
||||
dis_assemble_21(bits(LR(sym.get_gottp_addr(ctx) - GP, A), 31, 11));
|
||||
break;
|
||||
case R_PARISC_LTOFF_TP14R:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |= dis_low_sign_ext(R(sym.get_gottp_addr(ctx) - GP), 14);
|
||||
break;
|
||||
case R_PARISC_TLS_GD21L:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |=
|
||||
dis_assemble_21(bits(LR(sym.get_tlsgd_addr(ctx) - GP, A), 31, 11));
|
||||
break;
|
||||
case R_PARISC_TLS_GD14R:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |= dis_low_sign_ext(R(sym.get_tlsgd_addr(ctx) - GP), 14);
|
||||
break;
|
||||
case R_PARISC_TLS_LDM21L:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |=
|
||||
dis_assemble_21(bits(LR(ctx.got->get_tlsld_addr(ctx) - GP, A), 31, 11));
|
||||
break;
|
||||
case R_PARISC_TLS_LDM14R:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |= dis_low_sign_ext(R(ctx.got->get_tlsld_addr(ctx) - GP), 14);
|
||||
break;
|
||||
case R_PARISC_TLS_LDO21L:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |= dis_assemble_21(bits(LR(S - ctx.dtp_addr, A), 31, 11));
|
||||
break;
|
||||
case R_PARISC_TLS_LDO14R:
|
||||
assert(A == 0);
|
||||
*(ub32 *)loc |= dis_low_sign_ext(R(S - ctx.dtp_addr), 14);
|
||||
break;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
|
||||
#undef S
|
||||
#undef A
|
||||
#undef P
|
||||
#undef G
|
||||
#undef GOT
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
u8 *loc = base + rel.r_offset;
|
||||
|
||||
if (!sym.file) {
|
||||
record_undef_error(ctx, rel);
|
||||
continue;
|
||||
}
|
||||
|
||||
SectionFragment<E> *frag;
|
||||
i64 frag_addend;
|
||||
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
|
||||
|
||||
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
|
||||
#define A (frag ? frag_addend : get_addend(loc, rel))
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PARISC_DIR32:
|
||||
case R_PARISC_SEGREL32:
|
||||
*(ub32 *)loc = S + A;
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": invalid relocation for non-allocated sections: "
|
||||
<< rel;
|
||||
}
|
||||
|
||||
#undef S
|
||||
#undef A
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
assert(shdr().sh_flags & SHF_ALLOC);
|
||||
|
||||
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
|
||||
std::span<const ElfRel<E>> rels = get_rels(ctx);
|
||||
|
||||
for (i64 i = 0; i < rels.size(); i++) {
|
||||
const ElfRel<E> &rel = rels[i];
|
||||
if (rel.r_type == R_NONE)
|
||||
continue;
|
||||
|
||||
Symbol<E> &sym = *file.symbols[rel.r_sym];
|
||||
|
||||
if (!sym.file) {
|
||||
record_undef_error(ctx, rel);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (sym.is_ifunc())
|
||||
Error(ctx) << sym << ": GNU ifunc symbol is not supported on PA-RISC";
|
||||
|
||||
if (sym.is_func()) {
|
||||
switch (rel.r_type) {
|
||||
case R_PARISC_PCREL32:
|
||||
case R_PARISC_PCREL21L:
|
||||
case R_PARISC_PCREL17F:
|
||||
case R_PARISC_PCREL14R:
|
||||
case R_PARISC_PLABEL32:
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": " << rel
|
||||
<< " may not refer to a function symbol " << sym;
|
||||
}
|
||||
}
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PARISC_DIR32:
|
||||
scan_dyn_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_PARISC_DIR21L:
|
||||
case R_PARISC_DIR14R:
|
||||
scan_absrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_PARISC_PCREL32:
|
||||
case R_PARISC_PCREL21L:
|
||||
case R_PARISC_PCREL17F:
|
||||
case R_PARISC_PCREL14R:
|
||||
case R_PARISC_DPREL21L:
|
||||
case R_PARISC_DPREL14R:
|
||||
if (sym.is_func() || sym.esym().is_undef_weak())
|
||||
sym.flags.fetch_or(NEEDS_OPD, std::memory_order_relaxed);
|
||||
else
|
||||
scan_pcrel(ctx, sym, rel);
|
||||
break;
|
||||
case R_PARISC_DLTIND21L:
|
||||
case R_PARISC_DLTIND14R:
|
||||
sym.flags.fetch_or(NEEDS_GOT, std::memory_order_relaxed);
|
||||
break;
|
||||
case R_PARISC_PLABEL32:
|
||||
if (!sym.is_remaining_undef_weak()) {
|
||||
if (ctx.arg.pic)
|
||||
file.num_dynrel++;
|
||||
sym.flags.fetch_or(NEEDS_OPD, std::memory_order_relaxed);
|
||||
}
|
||||
break;
|
||||
case R_PARISC_LTOFF_TP21L:
|
||||
case R_PARISC_LTOFF_TP14R:
|
||||
sym.flags.fetch_or(NEEDS_GOTTP, std::memory_order_relaxed);
|
||||
break;
|
||||
case R_PARISC_TLS_GD21L:
|
||||
case R_PARISC_TLS_GD14R:
|
||||
sym.flags.fetch_or(NEEDS_TLSGD, std::memory_order_relaxed);
|
||||
break;
|
||||
case R_PARISC_TLS_LDM21L:
|
||||
case R_PARISC_TLS_LDM14R:
|
||||
ctx.needs_tlsld.store(true, std::memory_order_relaxed);
|
||||
break;
|
||||
case R_PARISC_SEGREL32:
|
||||
case R_PARISC_TPREL21L:
|
||||
case R_PARISC_TPREL14R:
|
||||
case R_PARISC_TLS_LDO21L:
|
||||
case R_PARISC_TLS_LDO14R:
|
||||
break;
|
||||
default:
|
||||
Error(ctx) << *this << ": unknown relocation: " << rel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
|
||||
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
|
||||
u64 gp = get_gp(ctx);
|
||||
|
||||
static const ub32 opd_pic_entry[] = {
|
||||
0x2a60'0000, // addil LR'<OFFSET>, r19, r1
|
||||
0x3436'0000, // ldo RR'<OFFSET>(r1), r22
|
||||
0x0ec0'1095, // ldw 0(r22), r21
|
||||
0xeaa0'c000, // bv r0(r21)
|
||||
0x0ec8'1093, // ldw 4(r22), r19
|
||||
};
|
||||
|
||||
static const ub32 opd_nopic_entry[] = {
|
||||
0x2b60'0000, // addil LR'<OFFSET>, dp, r1
|
||||
0x3436'0000, // ldo RR'<OFFSET>(r1), r22
|
||||
0x0ec0'1095, // ldw 0(r22), r21
|
||||
0xeaa0'c000, // bv r0(r21)
|
||||
0x0ec8'1093, // ldw 4(r22), r19
|
||||
};
|
||||
|
||||
static const ub32 local_pic_entry[] = {
|
||||
0x2a60'0000, // addil L%0, r19, r1
|
||||
0x3435'0000, // ldo 0(r1), r21
|
||||
0xeaa0'c000, // bv r0(r21)
|
||||
0x0800'0240, // nop
|
||||
0x0800'0240, // nop
|
||||
};
|
||||
|
||||
static const ub32 local_nopic_entry[] = {
|
||||
0x2b60'0000, // addil L%0, dp, r1
|
||||
0x3435'0000, // ldo 0(r1), r21
|
||||
0xeaa0'c000, // bv r0(r21)
|
||||
0x0800'0240, // nop
|
||||
0x0800'0240, // nop
|
||||
};
|
||||
|
||||
static_assert(E::thunk_size == sizeof(opd_pic_entry));
|
||||
static_assert(E::thunk_size == sizeof(opd_nopic_entry));
|
||||
static_assert(E::thunk_size == sizeof(local_pic_entry));
|
||||
static_assert(E::thunk_size == sizeof(local_nopic_entry));
|
||||
|
||||
for (i64 i = 0; i < symbols.size(); i++) {
|
||||
ub32 *loc = (ub32 *)(buf + i * E::thunk_size);
|
||||
Symbol<E> &sym = *symbols[i];
|
||||
i64 val;
|
||||
|
||||
if (sym.has_opd(ctx)) {
|
||||
val = sym.get_opd_addr(ctx) - gp;
|
||||
if (ctx.arg.pic)
|
||||
memcpy(loc, opd_pic_entry, E::thunk_size);
|
||||
else
|
||||
memcpy(loc, opd_nopic_entry, E::thunk_size);
|
||||
} else {
|
||||
assert(!sym.is_imported);
|
||||
val = sym.get_addr(ctx) - gp;
|
||||
if (ctx.arg.pic)
|
||||
memcpy(loc, local_pic_entry, E::thunk_size);
|
||||
else
|
||||
memcpy(loc, local_nopic_entry, E::thunk_size);
|
||||
}
|
||||
|
||||
loc[0] |= dis_assemble_21(bits(LR(val, 0), 31, 11));
|
||||
loc[1] |= dis_low_sign_ext(RR(val, 0), 14);
|
||||
}
|
||||
}
|
||||
|
||||
void HppaOpdSection::add_symbol(Context<E> &ctx, Symbol<E> *sym) {
|
||||
sym->set_opd_idx(ctx, symbols.size());
|
||||
symbols.push_back(sym);
|
||||
}
|
||||
|
||||
void HppaOpdSection::update_shdr(Context<E> &ctx) {
|
||||
if (!symbols.empty())
|
||||
this->shdr.sh_size = symbols.size() * ENTRY_SIZE + TRAILER_SIZE;
|
||||
this->shdr.sh_link = ctx.extra.opd->shndx;
|
||||
}
|
||||
|
||||
void HppaOpdSection::copy_buf(Context<E> &ctx) {
|
||||
ub32 *buf = (ub32 *)(ctx.buf + this->shdr.sh_offset);
|
||||
u64 gp = get_gp(ctx);
|
||||
|
||||
memset(buf, 0, this->shdr.sh_size);
|
||||
|
||||
for (i64 i = 0; i < symbols.size(); i++) {
|
||||
Symbol<E> &sym = *symbols[i];
|
||||
if (!ctx.arg.pic && !sym.is_imported) {
|
||||
buf[i * 2] = sym.get_addr(ctx, NO_OPD);
|
||||
buf[i * 2 + 1] = gp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HppaRelOpdSection::update_shdr(Context<E> &ctx) {
|
||||
this->shdr.sh_link = ctx.extra.opd->shndx;
|
||||
|
||||
if (ctx.arg.pic) {
|
||||
this->shdr.sh_size = ctx.extra.opd->symbols.size() * sizeof(ElfRel<E>);
|
||||
} else {
|
||||
this->shdr.sh_size = 0;
|
||||
for (Symbol<E> *sym : ctx.extra.opd->symbols)
|
||||
if (sym->is_imported)
|
||||
this->shdr.sh_size += sizeof(ElfRel<E>);
|
||||
}
|
||||
}
|
||||
|
||||
void HppaRelOpdSection::copy_buf(Context<E> &ctx) {
|
||||
ElfRel<E> *rel = (ElfRel<E> *)(ctx.buf + this->shdr.sh_offset);
|
||||
|
||||
for (Symbol<E> *sym : ctx.extra.opd->symbols) {
|
||||
if (ctx.arg.pic || sym->is_imported) {
|
||||
memset(rel, 0, sizeof(*rel));
|
||||
rel->r_offset = sym->get_opd_addr(ctx);
|
||||
rel->r_type = R_PARISC_IPLT;
|
||||
if (sym->is_imported)
|
||||
rel->r_sym = sym->get_dynsym_idx(ctx);
|
||||
else
|
||||
rel->r_addend = sym->get_addr(ctx, NO_OPD);
|
||||
rel++;
|
||||
}
|
||||
}
|
||||
|
||||
// This code stub must immediately follow the OPD element that the last
|
||||
// .rela.plt relocation refers to, because that's how the runtime finds
|
||||
// this piece of code on process startup.
|
||||
//
|
||||
// The code stub loads the _dl_runtime_resolve address to $r20 call the
|
||||
// function with its GP in $r19. The magic bytes, 0xc0ffee and 0xdeadbeef,
|
||||
// are used by the runtime to identify this code stub, so they must be
|
||||
// exactly that bytes.
|
||||
//
|
||||
// HPPA's runtime assumes that this stub is immediately followed by .got.
|
||||
// The files created by mold don't actually satisfy that constraint
|
||||
// because we don't intermix executable code and data. So, instead, we
|
||||
// reserve three padding words after the code stub and set
|
||||
// _GLOBAL_OFFSET_TABLE_ there as if that particular place is the
|
||||
// beginning of a .got. This trick works because the runtime only uses
|
||||
// _GLOBAL_OFFSET_TABLE_ to access GOT[1],
|
||||
//
|
||||
// Due to the existence of this code stub, .opd must be readable,
|
||||
// writable and executable. Writable executable segment is bad from the
|
||||
// security standpoint, but we have no choice other than doing it.
|
||||
//
|
||||
// https://sourceware.org/git/?p=glibc.git;f=sysdeps/hppa/dl-machine.h;h=1d5194856601e025c#l223
|
||||
ub32 insn[] = {
|
||||
0x0e80'1095, // 1: ldw 0(r20), r21
|
||||
0xeaa0'c000, // bv r0(r21)
|
||||
0x0e88'1095, // ldw 4(r20), r21
|
||||
0xea9f'1fdd, // b,l 1b, r20
|
||||
0xd680'1c1e, // depwi 0, 31, 2, r20
|
||||
0x00c0'ffee, // (_dl_runtime_resolve's address)
|
||||
0xdead'beef, // (%r19 for _dl_runtime_resolve)
|
||||
0x0000'0000, // (_GLOBAL_OFFSET_TABLE_ is set to here)
|
||||
0x0000'0000, // (runtime uses this word to identify this ELF module)
|
||||
0x0000'0000, // (padding)
|
||||
};
|
||||
|
||||
memcpy(ctx.buf + this->shdr.sh_offset +
|
||||
ctx.extra.opd->symbols.size() * sizeof(ElfRel<E>),
|
||||
insn, sizeof(insn));
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
+6
-6
@@ -332,13 +332,13 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
|
||||
if (sym.is_ifunc())
|
||||
sym.flags.fetch_or(NEEDS_GOT | NEEDS_PLT | NEEDS_PPC_OPD,
|
||||
sym.flags.fetch_or(NEEDS_GOT | NEEDS_PLT | NEEDS_OPD,
|
||||
std::memory_order_relaxed);
|
||||
|
||||
// Any relocation except R_PPC64_REL24 is considered as an
|
||||
// address-taking relocation.
|
||||
if (rel.r_type != R_PPC64_REL24 && sym.get_type() == STT_FUNC)
|
||||
sym.flags.fetch_or(NEEDS_PPC_OPD, std::memory_order_relaxed);
|
||||
sym.flags.fetch_or(NEEDS_OPD, std::memory_order_relaxed);
|
||||
|
||||
switch (rel.r_type) {
|
||||
case R_PPC64_ADDR64:
|
||||
@@ -551,7 +551,7 @@ get_opd_sym_at(Context<E> &ctx, std::span<OpdSymbol> syms, u64 offset) {
|
||||
// section so that they refer function symbols instead. We then mark input
|
||||
// .opd sections as dead.
|
||||
//
|
||||
// After this function, we mark symbols with the NEEDS_PPC_OPD flag if the
|
||||
// After this function, we mark symbols with the NEEDS_OPD flag if the
|
||||
// symbol needs an .opd entry. We then create an output .opd just like we
|
||||
// do for .plt or .got.
|
||||
void ppc64v1_rewrite_opd(Context<E> &ctx) {
|
||||
@@ -613,20 +613,20 @@ void ppc64v1_rewrite_opd(Context<E> &ctx) {
|
||||
|
||||
// When a function is exported, the dynamic symbol for the function should
|
||||
// refers the function's .opd entry. This function marks such symbols with
|
||||
// NEEDS_PPC_OPD.
|
||||
// NEEDS_OPD.
|
||||
void ppc64v1_scan_symbols(Context<E> &ctx) {
|
||||
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
|
||||
for (Symbol<E> *sym : file->symbols)
|
||||
if (sym->file == file && sym->is_exported)
|
||||
if (u32 ty = sym->get_type(); ty == STT_FUNC || ty == STT_GNU_IFUNC)
|
||||
sym->flags |= NEEDS_PPC_OPD;
|
||||
sym->flags |= NEEDS_OPD;
|
||||
});
|
||||
|
||||
// Functions referenced by the ELF header also have to have .opd entries.
|
||||
auto mark = [&](std::string_view name) {
|
||||
if (!name.empty())
|
||||
if (Symbol<E> &sym = *get_symbol(ctx, name); !sym.is_imported)
|
||||
sym.flags |= NEEDS_PPC_OPD;
|
||||
sym.flags |= NEEDS_OPD;
|
||||
};
|
||||
|
||||
mark(ctx.arg.entry);
|
||||
|
||||
+6
-3
@@ -197,8 +197,8 @@ Options:
|
||||
-z notext
|
||||
-z textoff
|
||||
|
||||
mold: supported targets: elf32-i386 elf64-x86-64 elf32-littlearm elf64-littleaarch64 elf32-littleriscv elf32-bigriscv elf64-littleriscv elf64-bigriscv elf32-powerpc elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc elf32-m68k elf32-sh-linux elf64-alpha
|
||||
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64linux aarch64elf elf32lriscv elf32briscv elf64lriscv elf64briscv elf32ppc elf32ppclinux elf64ppc elf64lppc elf64_s390 elf64_sparc m68kelf shlelf_linux elf64alpha)";
|
||||
mold: supported targets: elf32-i386 elf64-x86-64 elf32-littlearm elf64-littleaarch64 elf32-littleriscv elf32-bigriscv elf64-littleriscv elf64-bigriscv elf32-powerpc elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc elf32-m68k elf32-sh-linux elf64-alpha elf32-hppa-linux
|
||||
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64linux aarch64elf elf32lriscv elf32briscv elf64lriscv elf64briscv elf32ppc elf32ppclinux elf64ppc elf64lppc elf64_s390 elf64_sparc m68kelf shlelf_linux elf64alpha hppalinux)";
|
||||
|
||||
static std::vector<std::string> add_dashes(std::string name) {
|
||||
// Single-letter option
|
||||
@@ -520,7 +520,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
<< " aarch64linux\n armelf_linux_eabi\n elf64lriscv\n"
|
||||
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
|
||||
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
|
||||
<< " elf64_sparc\n m68kelf\n shlelf_linux\n elf64alpha";
|
||||
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
|
||||
<< " elf64alpha\n hppalinux";
|
||||
version_shown = true;
|
||||
} else if (read_arg("m")) {
|
||||
if (arg == "elf_x86_64") {
|
||||
@@ -555,6 +556,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
|
||||
ctx.arg.emulation = SH4::target_name;
|
||||
} else if (arg == "elf64alpha") {
|
||||
ctx.arg.emulation = ALPHA::target_name;
|
||||
} else if (arg == "hppalinux") {
|
||||
ctx.arg.emulation = HPPA32::target_name;
|
||||
} else {
|
||||
Fatal(ctx) << "unknown -m argument: " << arg;
|
||||
}
|
||||
|
||||
+128
@@ -911,4 +911,132 @@ std::string rel_to_string<ALPHA>(u32 r_type) {
|
||||
return "unknown (" + std::to_string(r_type) + ")";
|
||||
}
|
||||
|
||||
template <>
|
||||
std::string rel_to_string<HPPA32>(u32 r_type) {
|
||||
switch (r_type) {
|
||||
case R_PARISC_NONE: return "R_PARISC_NONE";
|
||||
case R_PARISC_DIR32: return "R_PARISC_DIR32";
|
||||
case R_PARISC_DIR21L: return "R_PARISC_DIR21L";
|
||||
case R_PARISC_DIR17R: return "R_PARISC_DIR17R";
|
||||
case R_PARISC_DIR17F: return "R_PARISC_DIR17F";
|
||||
case R_PARISC_DIR14R: return "R_PARISC_DIR14R";
|
||||
case R_PARISC_DIR14F: return "R_PARISC_DIR14F";
|
||||
case R_PARISC_PCREL12F: return "R_PARISC_PCREL12F";
|
||||
case R_PARISC_PCREL32: return "R_PARISC_PCREL32";
|
||||
case R_PARISC_PCREL21L: return "R_PARISC_PCREL21L";
|
||||
case R_PARISC_PCREL17R: return "R_PARISC_PCREL17R";
|
||||
case R_PARISC_PCREL17F: return "R_PARISC_PCREL17F";
|
||||
case R_PARISC_PCREL17C: return "R_PARISC_PCREL17C";
|
||||
case R_PARISC_PCREL14R: return "R_PARISC_PCREL14R";
|
||||
case R_PARISC_PCREL14F: return "R_PARISC_PCREL14F";
|
||||
case R_PARISC_DPREL21L: return "R_PARISC_DPREL21L";
|
||||
case R_PARISC_DPREL14WR: return "R_PARISC_DPREL14WR";
|
||||
case R_PARISC_DPREL14DR: return "R_PARISC_DPREL14DR";
|
||||
case R_PARISC_DPREL14R: return "R_PARISC_DPREL14R";
|
||||
case R_PARISC_DPREL14F: return "R_PARISC_DPREL14F";
|
||||
case R_PARISC_DLTREL21L: return "R_PARISC_DLTREL21L";
|
||||
case R_PARISC_DLTREL14R: return "R_PARISC_DLTREL14R";
|
||||
case R_PARISC_DLTREL14F: return "R_PARISC_DLTREL14F";
|
||||
case R_PARISC_DLTIND21L: return "R_PARISC_DLTIND21L";
|
||||
case R_PARISC_DLTIND14R: return "R_PARISC_DLTIND14R";
|
||||
case R_PARISC_DLTIND14F: return "R_PARISC_DLTIND14F";
|
||||
case R_PARISC_SETBASE: return "R_PARISC_SETBASE";
|
||||
case R_PARISC_SECREL32: return "R_PARISC_SECREL32";
|
||||
case R_PARISC_BASEREL21L: return "R_PARISC_BASEREL21L";
|
||||
case R_PARISC_BASEREL17R: return "R_PARISC_BASEREL17R";
|
||||
case R_PARISC_BASEREL17F: return "R_PARISC_BASEREL17F";
|
||||
case R_PARISC_BASEREL14R: return "R_PARISC_BASEREL14R";
|
||||
case R_PARISC_BASEREL14F: return "R_PARISC_BASEREL14F";
|
||||
case R_PARISC_SEGBASE: return "R_PARISC_SEGBASE";
|
||||
case R_PARISC_SEGREL32: return "R_PARISC_SEGREL32";
|
||||
case R_PARISC_PLTOFF21L: return "R_PARISC_PLTOFF21L";
|
||||
case R_PARISC_PLTOFF14R: return "R_PARISC_PLTOFF14R";
|
||||
case R_PARISC_PLTOFF14F: return "R_PARISC_PLTOFF14F";
|
||||
case R_PARISC_LTOFF_FPTR32: return "R_PARISC_LTOFF_FPTR32";
|
||||
case R_PARISC_LTOFF_FPTR21L: return "R_PARISC_LTOFF_FPTR21L";
|
||||
case R_PARISC_LTOFF_FPTR14R: return "R_PARISC_LTOFF_FPTR14R";
|
||||
case R_PARISC_FPTR64: return "R_PARISC_FPTR64";
|
||||
case R_PARISC_PLABEL32: return "R_PARISC_PLABEL32";
|
||||
case R_PARISC_PLABEL21L: return "R_PARISC_PLABEL21L";
|
||||
case R_PARISC_PLABEL14R: return "R_PARISC_PLABEL14R";
|
||||
case R_PARISC_PCREL64: return "R_PARISC_PCREL64";
|
||||
case R_PARISC_PCREL22C: return "R_PARISC_PCREL22C";
|
||||
case R_PARISC_PCREL22F: return "R_PARISC_PCREL22F";
|
||||
case R_PARISC_PCREL14WR: return "R_PARISC_PCREL14WR";
|
||||
case R_PARISC_PCREL14DR: return "R_PARISC_PCREL14DR";
|
||||
case R_PARISC_PCREL16F: return "R_PARISC_PCREL16F";
|
||||
case R_PARISC_PCREL16WF: return "R_PARISC_PCREL16WF";
|
||||
case R_PARISC_PCREL16DF: return "R_PARISC_PCREL16DF";
|
||||
case R_PARISC_DIR64: return "R_PARISC_DIR64";
|
||||
case R_PARISC_DIR14WR: return "R_PARISC_DIR14WR";
|
||||
case R_PARISC_DIR14DR: return "R_PARISC_DIR14DR";
|
||||
case R_PARISC_DIR16F: return "R_PARISC_DIR16F";
|
||||
case R_PARISC_DIR16WF: return "R_PARISC_DIR16WF";
|
||||
case R_PARISC_DIR16DF: return "R_PARISC_DIR16DF";
|
||||
case R_PARISC_GPREL64: return "R_PARISC_GPREL64";
|
||||
case R_PARISC_DLTREL14WR: return "R_PARISC_DLTREL14WR";
|
||||
case R_PARISC_DLTREL14DR: return "R_PARISC_DLTREL14DR";
|
||||
case R_PARISC_GPREL16F: return "R_PARISC_GPREL16F";
|
||||
case R_PARISC_GPREL16WF: return "R_PARISC_GPREL16WF";
|
||||
case R_PARISC_GPREL16DF: return "R_PARISC_GPREL16DF";
|
||||
case R_PARISC_LTOFF64: return "R_PARISC_LTOFF64";
|
||||
case R_PARISC_DLTIND14WR: return "R_PARISC_DLTIND14WR";
|
||||
case R_PARISC_DLTIND14DR: return "R_PARISC_DLTIND14DR";
|
||||
case R_PARISC_LTOFF16F: return "R_PARISC_LTOFF16F";
|
||||
case R_PARISC_LTOFF16WF: return "R_PARISC_LTOFF16WF";
|
||||
case R_PARISC_LTOFF16DF: return "R_PARISC_LTOFF16DF";
|
||||
case R_PARISC_SECREL64: return "R_PARISC_SECREL64";
|
||||
case R_PARISC_BASEREL14WR: return "R_PARISC_BASEREL14WR";
|
||||
case R_PARISC_BASEREL14DR: return "R_PARISC_BASEREL14DR";
|
||||
case R_PARISC_SEGREL64: return "R_PARISC_SEGREL64";
|
||||
case R_PARISC_PLTOFF14WR: return "R_PARISC_PLTOFF14WR";
|
||||
case R_PARISC_PLTOFF14DR: return "R_PARISC_PLTOFF14DR";
|
||||
case R_PARISC_PLTOFF16F: return "R_PARISC_PLTOFF16F";
|
||||
case R_PARISC_PLTOFF16WF: return "R_PARISC_PLTOFF16WF";
|
||||
case R_PARISC_PLTOFF16DF: return "R_PARISC_PLTOFF16DF";
|
||||
case R_PARISC_LTOFF_FPTR64: return "R_PARISC_LTOFF_FPTR64";
|
||||
case R_PARISC_LTOFF_FPTR14WR: return "R_PARISC_LTOFF_FPTR14WR";
|
||||
case R_PARISC_LTOFF_FPTR14DR: return "R_PARISC_LTOFF_FPTR14DR";
|
||||
case R_PARISC_LTOFF_FPTR16F: return "R_PARISC_LTOFF_FPTR16F";
|
||||
case R_PARISC_LTOFF_FPTR16WF: return "R_PARISC_LTOFF_FPTR16WF";
|
||||
case R_PARISC_LTOFF_FPTR16DF: return "R_PARISC_LTOFF_FPTR16DF";
|
||||
case R_PARISC_COPY: return "R_PARISC_COPY";
|
||||
case R_PARISC_IPLT: return "R_PARISC_IPLT";
|
||||
case R_PARISC_EPLT: return "R_PARISC_EPLT";
|
||||
case R_PARISC_TPREL32: return "R_PARISC_TPREL32";
|
||||
case R_PARISC_TPREL21L: return "R_PARISC_TPREL21L";
|
||||
case R_PARISC_TPREL14R: return "R_PARISC_TPREL14R";
|
||||
case R_PARISC_LTOFF_TP21L: return "R_PARISC_LTOFF_TP21L";
|
||||
case R_PARISC_LTOFF_TP14R: return "R_PARISC_LTOFF_TP14R";
|
||||
case R_PARISC_LTOFF_TP14F: return "R_PARISC_LTOFF_TP14F";
|
||||
case R_PARISC_TPREL64: return "R_PARISC_TPREL64";
|
||||
case R_PARISC_TPREL14WR: return "R_PARISC_TPREL14WR";
|
||||
case R_PARISC_TPREL14DR: return "R_PARISC_TPREL14DR";
|
||||
case R_PARISC_TPREL16F: return "R_PARISC_TPREL16F";
|
||||
case R_PARISC_TPREL16WF: return "R_PARISC_TPREL16WF";
|
||||
case R_PARISC_TPREL16DF: return "R_PARISC_TPREL16DF";
|
||||
case R_PARISC_LTOFF_TP64: return "R_PARISC_LTOFF_TP64";
|
||||
case R_PARISC_LTOFF_TP14WR: return "R_PARISC_LTOFF_TP14WR";
|
||||
case R_PARISC_LTOFF_TP14DR: return "R_PARISC_LTOFF_TP14DR";
|
||||
case R_PARISC_LTOFF_TP16F: return "R_PARISC_LTOFF_TP16F";
|
||||
case R_PARISC_LTOFF_TP16WF: return "R_PARISC_LTOFF_TP16WF";
|
||||
case R_PARISC_LTOFF_TP16DF: return "R_PARISC_LTOFF_TP16DF";
|
||||
case R_PARISC_GNU_VTENTRY: return "R_PARISC_GNU_VTENTRY";
|
||||
case R_PARISC_GNU_VTINHERIT: return "R_PARISC_GNU_VTINHERIT";
|
||||
case R_PARISC_TLS_GD21L: return "R_PARISC_TLS_GD21L";
|
||||
case R_PARISC_TLS_GD14R: return "R_PARISC_TLS_GD14R";
|
||||
case R_PARISC_TLS_GDCALL: return "R_PARISC_TLS_GDCALL";
|
||||
case R_PARISC_TLS_LDM21L: return "R_PARISC_TLS_LDM21L";
|
||||
case R_PARISC_TLS_LDM14R: return "R_PARISC_TLS_LDM14R";
|
||||
case R_PARISC_TLS_LDMCALL: return "R_PARISC_TLS_LDMCALL";
|
||||
case R_PARISC_TLS_LDO21L: return "R_PARISC_TLS_LDO21L";
|
||||
case R_PARISC_TLS_LDO14R: return "R_PARISC_TLS_LDO14R";
|
||||
case R_PARISC_TLS_DTPMOD32: return "R_PARISC_TLS_DTPMOD32";
|
||||
case R_PARISC_TLS_DTPMOD64: return "R_PARISC_TLS_DTPMOD64";
|
||||
case R_PARISC_TLS_DTPOFF32: return "R_PARISC_TLS_DTPOFF32";
|
||||
case R_PARISC_TLS_DTPOFF64: return "R_PARISC_TLS_DTPOFF64";
|
||||
}
|
||||
return "unknown (" + std::to_string(r_type) + ")";
|
||||
}
|
||||
|
||||
} // namespace mold::elf
|
||||
@@ -25,6 +25,7 @@ struct SPARC64;
|
||||
struct M68K;
|
||||
struct SH4;
|
||||
struct ALPHA;
|
||||
struct HPPA32;
|
||||
|
||||
template <typename E> struct ElfSym;
|
||||
template <typename E> struct ElfShdr;
|
||||
@@ -118,6 +119,7 @@ enum : u32 {
|
||||
STT_TLS = 6,
|
||||
STT_GNU_IFUNC = 10,
|
||||
STT_SPARC_REGISTER = 13,
|
||||
STT_HPPA_MILLI = 13,
|
||||
};
|
||||
|
||||
template <typename E>
|
||||
@@ -137,6 +139,10 @@ inline std::string stt_to_string(u32 st_type) {
|
||||
if (st_type == STT_SPARC_REGISTER)
|
||||
return "STT_SPARC_REGISTER";
|
||||
|
||||
if constexpr (std::is_same_v<E, HPPA32>)
|
||||
if (st_type == STT_HPPA_MILLI)
|
||||
return "STT_HPPA_MILLI";
|
||||
|
||||
return "unknown st_type (" + std::to_string(st_type) + ")";
|
||||
}
|
||||
|
||||
@@ -217,6 +223,7 @@ enum : u32 {
|
||||
EM_NONE = 0,
|
||||
EM_386 = 3,
|
||||
EM_68K = 4,
|
||||
EM_PARISC = 15,
|
||||
EM_PPC = 20,
|
||||
EM_PPC64 = 21,
|
||||
EM_S390X = 22,
|
||||
@@ -237,6 +244,11 @@ enum : u32 {
|
||||
EI_ABIVERSION = 8,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
ELFOSABI_NONE = 0,
|
||||
ELFOSABI_GNU = 3,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
DT_NULL = 0,
|
||||
DT_NEEDED = 1,
|
||||
@@ -1217,6 +1229,130 @@ enum : u32 {
|
||||
R_ALPHA_TPREL16 = 41,
|
||||
};
|
||||
|
||||
enum : u32 {
|
||||
R_PARISC_NONE = 0,
|
||||
R_PARISC_DIR32 = 1,
|
||||
R_PARISC_DIR21L = 2,
|
||||
R_PARISC_DIR17R = 3,
|
||||
R_PARISC_DIR17F = 4,
|
||||
R_PARISC_DIR14R = 6,
|
||||
R_PARISC_DIR14F = 7,
|
||||
R_PARISC_PCREL12F = 8,
|
||||
R_PARISC_PCREL32 = 9,
|
||||
R_PARISC_PCREL21L = 10,
|
||||
R_PARISC_PCREL17R = 11,
|
||||
R_PARISC_PCREL17F = 12,
|
||||
R_PARISC_PCREL17C = 13,
|
||||
R_PARISC_PCREL14R = 14,
|
||||
R_PARISC_PCREL14F = 15,
|
||||
R_PARISC_DPREL21L = 18,
|
||||
R_PARISC_DPREL14WR = 19,
|
||||
R_PARISC_DPREL14DR = 20,
|
||||
R_PARISC_DPREL14R = 22,
|
||||
R_PARISC_DPREL14F = 23,
|
||||
R_PARISC_DLTREL21L = 26,
|
||||
R_PARISC_DLTREL14R = 30,
|
||||
R_PARISC_DLTREL14F = 31,
|
||||
R_PARISC_DLTIND21L = 34,
|
||||
R_PARISC_DLTIND14R = 38,
|
||||
R_PARISC_DLTIND14F = 39,
|
||||
R_PARISC_SETBASE = 40,
|
||||
R_PARISC_SECREL32 = 41,
|
||||
R_PARISC_BASEREL21L = 42,
|
||||
R_PARISC_BASEREL17R = 43,
|
||||
R_PARISC_BASEREL17F = 44,
|
||||
R_PARISC_BASEREL14R = 46,
|
||||
R_PARISC_BASEREL14F = 47,
|
||||
R_PARISC_SEGBASE = 48,
|
||||
R_PARISC_SEGREL32 = 49,
|
||||
R_PARISC_PLTOFF21L = 50,
|
||||
R_PARISC_PLTOFF14R = 54,
|
||||
R_PARISC_PLTOFF14F = 55,
|
||||
R_PARISC_LTOFF_FPTR32 = 57,
|
||||
R_PARISC_LTOFF_FPTR21L = 58,
|
||||
R_PARISC_LTOFF_FPTR14R = 62,
|
||||
R_PARISC_FPTR64 = 64,
|
||||
R_PARISC_PLABEL32 = 65,
|
||||
R_PARISC_PLABEL21L = 66,
|
||||
R_PARISC_PLABEL14R = 70,
|
||||
R_PARISC_PCREL64 = 72,
|
||||
R_PARISC_PCREL22C = 73,
|
||||
R_PARISC_PCREL22F = 74,
|
||||
R_PARISC_PCREL14WR = 75,
|
||||
R_PARISC_PCREL14DR = 76,
|
||||
R_PARISC_PCREL16F = 77,
|
||||
R_PARISC_PCREL16WF = 78,
|
||||
R_PARISC_PCREL16DF = 79,
|
||||
R_PARISC_DIR64 = 80,
|
||||
R_PARISC_DIR14WR = 83,
|
||||
R_PARISC_DIR14DR = 84,
|
||||
R_PARISC_DIR16F = 85,
|
||||
R_PARISC_DIR16WF = 86,
|
||||
R_PARISC_DIR16DF = 87,
|
||||
R_PARISC_GPREL64 = 88,
|
||||
R_PARISC_DLTREL14WR = 91,
|
||||
R_PARISC_DLTREL14DR = 92,
|
||||
R_PARISC_GPREL16F = 93,
|
||||
R_PARISC_GPREL16WF = 94,
|
||||
R_PARISC_GPREL16DF = 95,
|
||||
R_PARISC_LTOFF64 = 96,
|
||||
R_PARISC_DLTIND14WR = 99,
|
||||
R_PARISC_DLTIND14DR = 100,
|
||||
R_PARISC_LTOFF16F = 101,
|
||||
R_PARISC_LTOFF16WF = 102,
|
||||
R_PARISC_LTOFF16DF = 103,
|
||||
R_PARISC_SECREL64 = 104,
|
||||
R_PARISC_BASEREL14WR = 107,
|
||||
R_PARISC_BASEREL14DR = 108,
|
||||
R_PARISC_SEGREL64 = 112,
|
||||
R_PARISC_PLTOFF14WR = 115,
|
||||
R_PARISC_PLTOFF14DR = 116,
|
||||
R_PARISC_PLTOFF16F = 117,
|
||||
R_PARISC_PLTOFF16WF = 118,
|
||||
R_PARISC_PLTOFF16DF = 119,
|
||||
R_PARISC_LTOFF_FPTR64 = 120,
|
||||
R_PARISC_LTOFF_FPTR14WR = 123,
|
||||
R_PARISC_LTOFF_FPTR14DR = 124,
|
||||
R_PARISC_LTOFF_FPTR16F = 125,
|
||||
R_PARISC_LTOFF_FPTR16WF = 126,
|
||||
R_PARISC_LTOFF_FPTR16DF = 127,
|
||||
R_PARISC_COPY = 128,
|
||||
R_PARISC_IPLT = 129,
|
||||
R_PARISC_EPLT = 130,
|
||||
R_PARISC_TPREL32 = 153,
|
||||
R_PARISC_TPREL21L = 154,
|
||||
R_PARISC_TPREL14R = 158,
|
||||
R_PARISC_LTOFF_TP21L = 162,
|
||||
R_PARISC_LTOFF_TP14R = 166,
|
||||
R_PARISC_LTOFF_TP14F = 167,
|
||||
R_PARISC_TPREL64 = 216,
|
||||
R_PARISC_TPREL14WR = 219,
|
||||
R_PARISC_TPREL14DR = 220,
|
||||
R_PARISC_TPREL16F = 221,
|
||||
R_PARISC_TPREL16WF = 222,
|
||||
R_PARISC_TPREL16DF = 223,
|
||||
R_PARISC_LTOFF_TP64 = 224,
|
||||
R_PARISC_LTOFF_TP14WR = 227,
|
||||
R_PARISC_LTOFF_TP14DR = 228,
|
||||
R_PARISC_LTOFF_TP16F = 229,
|
||||
R_PARISC_LTOFF_TP16WF = 230,
|
||||
R_PARISC_LTOFF_TP16DF = 231,
|
||||
R_PARISC_GNU_VTENTRY = 232,
|
||||
R_PARISC_GNU_VTINHERIT = 233,
|
||||
R_PARISC_TLS_GD21L = 234,
|
||||
R_PARISC_TLS_GD14R = 235,
|
||||
R_PARISC_TLS_GDCALL = 236,
|
||||
R_PARISC_TLS_LDM21L = 237,
|
||||
R_PARISC_TLS_LDM14R = 238,
|
||||
R_PARISC_TLS_LDMCALL = 239,
|
||||
R_PARISC_TLS_LDO21L = 240,
|
||||
R_PARISC_TLS_LDO14R = 241,
|
||||
R_PARISC_TLS_DTPMOD32 = 242,
|
||||
R_PARISC_TLS_DTPMOD64 = 243,
|
||||
R_PARISC_TLS_DTPOFF32 = 244,
|
||||
R_PARISC_TLS_DTPOFF64 = 245,
|
||||
};
|
||||
|
||||
//
|
||||
// DWARF data types
|
||||
//
|
||||
@@ -1670,6 +1806,7 @@ template <typename E> static constexpr bool is_sparc64 = std::is_same_v<E, SPARC
|
||||
template <typename E> static constexpr bool is_m68k = std::is_same_v<E, M68K>;
|
||||
template <typename E> static constexpr bool is_sh4 = std::is_same_v<E, SH4>;
|
||||
template <typename E> static constexpr bool is_alpha = std::is_same_v<E, ALPHA>;
|
||||
template <typename E> static constexpr bool is_hppa32 = std::is_same_v<E, HPPA32>;
|
||||
|
||||
template <typename E> static constexpr bool is_x86 = is_x86_64<E> || is_i386<E>;
|
||||
template <typename E> static constexpr bool is_arm = is_arm64<E> || is_arm32<E>;
|
||||
@@ -1679,6 +1816,7 @@ template <typename E> static constexpr bool is_riscv = is_rv64<E> || is_rv32<E>;
|
||||
template <typename E> static constexpr bool is_ppc64 = is_ppc64v1<E> || is_ppc64v2<E>;
|
||||
template <typename E> static constexpr bool is_ppc = is_ppc64<E> || is_ppc32<E>;
|
||||
template <typename E> static constexpr bool is_sparc = is_sparc64<E>;
|
||||
template <typename E> static constexpr bool is_hppa = is_hppa32<E>;
|
||||
|
||||
struct X86_64 {
|
||||
static constexpr std::string_view target_name = "x86_64";
|
||||
@@ -2046,4 +2184,28 @@ struct ALPHA {
|
||||
static constexpr u32 R_DTPMOD = R_ALPHA_DTPMOD64;
|
||||
};
|
||||
|
||||
struct HPPA32 {
|
||||
static constexpr std::string_view target_name = "hppa32";
|
||||
static constexpr bool is_64 = false;
|
||||
static constexpr bool is_le = false;
|
||||
static constexpr bool is_rela = true;
|
||||
static constexpr u32 page_size = 4096;
|
||||
static constexpr u32 e_machine = EM_PARISC;
|
||||
static constexpr u32 plt_hdr_size = 0;
|
||||
static constexpr u32 plt_size = 0;
|
||||
static constexpr u32 pltgot_size = 0;
|
||||
static constexpr u32 thunk_hdr_size = 0;
|
||||
static constexpr u32 thunk_size = 20;
|
||||
|
||||
static constexpr u32 R_COPY = R_PARISC_COPY;
|
||||
static constexpr u32 R_GLOB_DAT = R_PARISC_DIR32;
|
||||
static constexpr u32 R_JUMP_SLOT = R_PARISC_IPLT;
|
||||
static constexpr u32 R_ABS = R_PARISC_DIR32;
|
||||
static constexpr u32 R_RELATIVE = R_PARISC_DIR32;
|
||||
static constexpr u32 R_IRELATIVE = R_NONE;
|
||||
static constexpr u32 R_DTPOFF = R_PARISC_TLS_DTPOFF32;
|
||||
static constexpr u32 R_TPOFF = R_PARISC_TPREL32;
|
||||
static constexpr u32 R_DTPMOD = R_PARISC_TLS_DTPMOD32;
|
||||
};
|
||||
|
||||
} // namespace mold::elf
|
||||
@@ -70,6 +70,8 @@ std::string_view get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
|
||||
return SH4::target_name;
|
||||
case EM_ALPHA:
|
||||
return ALPHA::target_name;
|
||||
case EM_PARISC:
|
||||
return HPPA32::target_name;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
@@ -371,6 +373,8 @@ static int redo_main(int argc, char **argv, std::string_view target) {
|
||||
return elf_main<SH4>(argc, argv);
|
||||
if (target == ALPHA::target_name)
|
||||
return elf_main<ALPHA>(argc, argv);
|
||||
if (target == HPPA32::target_name)
|
||||
return elf_main<HPPA32>(argc, argv);
|
||||
unreachable();
|
||||
}
|
||||
|
||||
@@ -759,6 +763,7 @@ extern template int elf_main<SPARC64>(int, char **);
|
||||
extern template int elf_main<M68K>(int, char **);
|
||||
extern template int elf_main<SH4>(int, char **);
|
||||
extern template int elf_main<ALPHA>(int, char **);
|
||||
extern template int elf_main<HPPA32>(int, char **);
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return elf_main<X86_64>(argc, argv);
|
||||
|
||||
+57
-5
@@ -90,8 +90,8 @@ struct SymbolAux {
|
||||
u32 djb_hash = 0;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SymbolAux<PPC64V1> : SymbolAux<X86_64> {
|
||||
template <typename E> requires is_ppc64v1<E> || is_hppa<E>
|
||||
struct SymbolAux<E> : SymbolAux<X86_64> {
|
||||
i32 opd_idx = -1;
|
||||
};
|
||||
|
||||
@@ -323,6 +323,7 @@ private:
|
||||
std::pair<SectionFragment<E> *, i64>
|
||||
get_fragment(Context<E> &ctx, const ElfRel<E> &rel);
|
||||
|
||||
u64 has_thunk(i64 idx);
|
||||
u64 get_thunk_addr(i64 idx);
|
||||
|
||||
std::optional<u64> get_tombstone(Symbol<E> &sym, SectionFragment<E> *frag);
|
||||
@@ -1545,6 +1546,43 @@ private:
|
||||
std::mutex mu;
|
||||
};
|
||||
|
||||
//
|
||||
// arch-hppa.cc
|
||||
//
|
||||
|
||||
class HppaOpdSection : public Chunk<HPPA32> {
|
||||
public:
|
||||
HppaOpdSection() {
|
||||
this->name = ".opd";
|
||||
this->shdr.sh_type = SHT_PROGBITS;
|
||||
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE | SHF_EXECINSTR;
|
||||
this->shdr.sh_addralign = 8;
|
||||
}
|
||||
|
||||
void add_symbol(Context<HPPA32> &ctx, Symbol<HPPA32> *sym);
|
||||
void update_shdr(Context<HPPA32> &ctx) override;
|
||||
void copy_buf(Context<HPPA32> &ctx) override;
|
||||
|
||||
static constexpr i64 ENTRY_SIZE = sizeof(Word<HPPA32>) * 2;
|
||||
static constexpr i64 TRAILER_SIZE = sizeof(Word<HPPA32>) * 9;
|
||||
|
||||
std::vector<Symbol<HPPA32> *> symbols;
|
||||
};
|
||||
|
||||
class HppaRelOpdSection : public Chunk<HPPA32> {
|
||||
public:
|
||||
HppaRelOpdSection() {
|
||||
this->name = ".rela.opd";
|
||||
this->shdr.sh_type = SHT_RELA;
|
||||
this->shdr.sh_flags = SHF_ALLOC;
|
||||
this->shdr.sh_entsize = sizeof(ElfRel<HPPA32>);
|
||||
this->shdr.sh_addralign = sizeof(Word<HPPA32>);
|
||||
}
|
||||
|
||||
void update_shdr(Context<HPPA32> &ctx) override;
|
||||
void copy_buf(Context<HPPA32> &ctx) override;
|
||||
};
|
||||
|
||||
//
|
||||
// main.cc
|
||||
//
|
||||
@@ -1625,6 +1663,12 @@ template <> struct ContextExtras<ALPHA> {
|
||||
AlphaGotSection *got = nullptr;
|
||||
};
|
||||
|
||||
template <> struct ContextExtras<HPPA32> {
|
||||
HppaOpdSection *opd = nullptr;
|
||||
HppaRelOpdSection *relopd = nullptr;
|
||||
Symbol<HPPA32> *global = nullptr;
|
||||
};
|
||||
|
||||
// Context represents a context object for each invocation of the linker.
|
||||
// It contains command line flags, pointers to singleton objects
|
||||
// (such as linker-synthesized output sections), unique_ptrs for
|
||||
@@ -1706,7 +1750,7 @@ struct Context {
|
||||
bool z_dlopen = true;
|
||||
bool z_dump = true;
|
||||
bool z_dynamic_undefined_weak = true;
|
||||
bool z_execstack = false;
|
||||
bool z_execstack = is_hppa<E>;
|
||||
bool z_execstack_if_needed = false;
|
||||
bool z_ibt = false;
|
||||
bool z_initfirst = false;
|
||||
@@ -1931,7 +1975,7 @@ enum {
|
||||
NEEDS_TLSGD = 1 << 4,
|
||||
NEEDS_COPYREL = 1 << 5,
|
||||
NEEDS_TLSDESC = 1 << 6,
|
||||
NEEDS_PPC_OPD = 1 << 7, // for PPCv1
|
||||
NEEDS_OPD = 1 << 7, // for PPCv1 or HPPA
|
||||
NEEDS_ALPHA_GOT = 1 << 7, // for Alpha
|
||||
};
|
||||
|
||||
@@ -2004,6 +2048,7 @@ public:
|
||||
bool is_absolute() const;
|
||||
bool is_relative() const { return !is_absolute(); }
|
||||
bool is_local(Context<E> &ctx) const;
|
||||
bool is_func() const { return get_type() == STT_FUNC; }
|
||||
bool is_ifunc() const { return get_type() == STT_GNU_IFUNC; }
|
||||
bool is_remaining_undef_weak() const;
|
||||
|
||||
@@ -2318,6 +2363,13 @@ InputSection<E>::get_fragment(Context<E> &ctx, const ElfRel<E> &rel) {
|
||||
return {nullptr, 0};
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
u64 InputSection<E>::has_thunk(i64 idx) {
|
||||
if constexpr (needs_thunk<E>)
|
||||
return extra.range_extn[idx].thunk_idx != -1;
|
||||
unreachable();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
u64 InputSection<E>::get_thunk_addr(i64 idx) {
|
||||
if constexpr (needs_thunk<E>) {
|
||||
@@ -2556,7 +2608,7 @@ template <typename E>
|
||||
inline u64 Symbol<E>::get_opd_addr(Context<E> &ctx) const {
|
||||
assert(get_opd_idx(ctx) != -1);
|
||||
return ctx.extra.opd->shdr.sh_addr +
|
||||
get_opd_idx(ctx) * PPC64OpdSection::ENTRY_SIZE;
|
||||
get_opd_idx(ctx) * ctx.extra.opd->ENTRY_SIZE;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
|
||||
+36
-5
@@ -36,6 +36,24 @@ static u32 djb_hash(std::string_view name) {
|
||||
return h;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static u64 get_osabi() {
|
||||
// The EI_OSABI field in the ELF header specified what ABI variant the
|
||||
// output file is using. In practice, it is either GNU or NONE.
|
||||
//
|
||||
// Technically, if we use GNU extensions such as GNU ifunc, we should
|
||||
// set it to GNU and otherwise NONE. That being said, GNU extensions are
|
||||
// so prevalent that no runtime bothers to check the value. Likewise, we
|
||||
// don't bother to change the value of EI_OSABI depending on the feature
|
||||
// that the output file depends on.
|
||||
//
|
||||
// HPPA's objdump refuses to disassemble an executable unless it is
|
||||
// ELFOSABI_GNU, so we unconditionally set it to GNU for HPPA.
|
||||
if constexpr (is_hppa<E>)
|
||||
return ELFOSABI_GNU;
|
||||
return ELFOSABI_NONE;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
u64 get_eflags(Context<E> &ctx) {
|
||||
if constexpr (is_arm32<E>)
|
||||
@@ -57,6 +75,10 @@ u64 get_eflags(Context<E> &ctx) {
|
||||
|
||||
if constexpr (is_ppc64v2<E>)
|
||||
return 2;
|
||||
|
||||
if constexpr (is_hppa<E>)
|
||||
return 0x210;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -84,6 +106,7 @@ void OutputEhdr<E>::copy_buf(Context<E> &ctx) {
|
||||
hdr.e_ident[EI_CLASS] = E::is_64 ? ELFCLASS64 : ELFCLASS32;
|
||||
hdr.e_ident[EI_DATA] = E::is_le ? ELFDATA2LSB : ELFDATA2MSB;
|
||||
hdr.e_ident[EI_VERSION] = EV_CURRENT;
|
||||
hdr.e_ident[EI_OSABI] = get_osabi<E>();
|
||||
hdr.e_machine = E::e_machine;
|
||||
hdr.e_version = EV_CURRENT;
|
||||
hdr.e_entry = get_entry_addr();
|
||||
@@ -225,7 +248,7 @@ static void init_thread_pointers(Context<E> &ctx, ElfPhdr<E> phdr) {
|
||||
// load/store instruction.
|
||||
if constexpr (is_x86<E> || is_sparc<E> || is_s390x<E>) {
|
||||
ctx.tp_addr = align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
|
||||
} else if constexpr (is_arm<E> || is_sh4<E> || is_alpha<E>) {
|
||||
} else if constexpr (is_arm<E> || is_sh4<E> || is_alpha<E> || is_hppa<E>) {
|
||||
ctx.tp_addr = align_down(phdr.p_vaddr - sizeof(Word<E>) * 2, phdr.p_align);
|
||||
} else if constexpr (is_ppc<E> || is_m68k<E>) {
|
||||
ctx.tp_addr = phdr.p_vaddr + 0x7000;
|
||||
@@ -801,10 +824,18 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
|
||||
define(DT_RELRENT, ctx.relrdyn->shdr.sh_entsize);
|
||||
}
|
||||
|
||||
if (ctx.relplt->shdr.sh_size) {
|
||||
define(DT_JMPREL, ctx.relplt->shdr.sh_addr);
|
||||
define(DT_PLTRELSZ, ctx.relplt->shdr.sh_size);
|
||||
define(DT_PLTREL, E::is_rela ? DT_RELA : DT_REL);
|
||||
if constexpr (is_hppa<E>) {
|
||||
if (ctx.extra.relopd->shdr.sh_size) {
|
||||
define(DT_JMPREL, ctx.extra.relopd->shdr.sh_addr);
|
||||
define(DT_PLTRELSZ, ctx.extra.relopd->shdr.sh_size);
|
||||
define(DT_PLTREL, DT_RELA);
|
||||
}
|
||||
} else {
|
||||
if (ctx.relplt->shdr.sh_size) {
|
||||
define(DT_JMPREL, ctx.relplt->shdr.sh_addr);
|
||||
define(DT_PLTRELSZ, ctx.relplt->shdr.sh_size);
|
||||
define(DT_PLTREL, E::is_rela ? DT_RELA : DT_REL);
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (is_sparc<E>) {
|
||||
|
||||
+91
-44
@@ -127,6 +127,11 @@ void create_synthetic_sections(Context<E> &ctx) {
|
||||
if constexpr (is_alpha<E>)
|
||||
ctx.extra.got = push(new AlphaGotSection);
|
||||
|
||||
if constexpr (is_hppa<E>) {
|
||||
ctx.extra.opd = push(new HppaOpdSection);
|
||||
ctx.extra.relopd = push(new HppaRelOpdSection);
|
||||
}
|
||||
|
||||
// If .dynamic exists, .dynsym and .dynstr must exist as well
|
||||
// since .dynamic refers them.
|
||||
if (ctx.dynamic) {
|
||||
@@ -378,21 +383,6 @@ static std::vector<std::span<T>> split(std::vector<T> &input, i64 unit) {
|
||||
return vec;
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
static u64 canonicalize_type(std::string_view name, u64 type) {
|
||||
if (type == SHT_PROGBITS) {
|
||||
if (name == ".init_array" || name.starts_with(".init_array."))
|
||||
return SHT_INIT_ARRAY;
|
||||
if (name == ".fini_array" || name.starts_with(".fini_array."))
|
||||
return SHT_FINI_ARRAY;
|
||||
}
|
||||
|
||||
if constexpr (is_x86_64<E>)
|
||||
if (type == SHT_X86_64_UNWIND)
|
||||
return SHT_PROGBITS;
|
||||
return type;
|
||||
}
|
||||
|
||||
struct OutputSectionKey {
|
||||
std::string_view name;
|
||||
u64 type;
|
||||
@@ -460,18 +450,41 @@ static OutputSectionKey
|
||||
get_output_section_key(Context<E> &ctx, InputSection<E> &isec) {
|
||||
const ElfShdr<E> &shdr = isec.shdr();
|
||||
std::string_view name = get_output_name(ctx, isec.name(), shdr.sh_flags);
|
||||
u64 type = canonicalize_type<E>(name, shdr.sh_type);
|
||||
u64 flags = shdr.sh_flags & ~(u64)SHF_COMPRESSED;
|
||||
|
||||
if (!ctx.arg.relocatable)
|
||||
flags &= ~(u64)SHF_GROUP & ~(u64)SHF_GNU_RETAIN;
|
||||
auto get_type = [&]() -> u32 {
|
||||
if (shdr.sh_type == SHT_PROGBITS) {
|
||||
if (name == ".init_array" || name.starts_with(".init_array."))
|
||||
return SHT_INIT_ARRAY;
|
||||
if (name == ".fini_array" || name.starts_with(".fini_array."))
|
||||
return SHT_FINI_ARRAY;
|
||||
}
|
||||
|
||||
// .init_array is usually writable. We don't want to create multiple
|
||||
// .init_array output sections, so make it always writable.
|
||||
// So is .fini_array.
|
||||
if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY)
|
||||
flags |= SHF_WRITE;
|
||||
return {name, type, flags};
|
||||
if constexpr (is_x86_64<E>)
|
||||
if (shdr.sh_type == SHT_X86_64_UNWIND)
|
||||
return SHT_PROGBITS;
|
||||
return shdr.sh_type;
|
||||
};
|
||||
|
||||
u32 type = get_type();
|
||||
|
||||
auto get_flags = [&]() -> u32 {
|
||||
if constexpr (is_hppa<E>)
|
||||
if (name == ".PARISC.unwind")
|
||||
return SHF_ALLOC | SHF_INFO_LINK;
|
||||
|
||||
u32 flags = shdr.sh_flags & ~(u64)SHF_COMPRESSED;
|
||||
if (!ctx.arg.relocatable)
|
||||
flags &= ~(u64)SHF_GROUP & ~(u64)SHF_GNU_RETAIN;
|
||||
|
||||
// .init_array is usually writable. We don't want to create multiple
|
||||
// .init_array output sections, so make it always writable.
|
||||
// So is .fini_array.
|
||||
if (type == SHT_INIT_ARRAY || type == SHT_FINI_ARRAY)
|
||||
flags |= SHF_WRITE;
|
||||
return flags;
|
||||
};
|
||||
|
||||
return {name, type, get_flags()};
|
||||
}
|
||||
|
||||
// Create output sections for input sections.
|
||||
@@ -562,7 +575,7 @@ void create_internal_file(Context<E> &ctx) {
|
||||
obj->is_alive = true;
|
||||
obj->priority = 1;
|
||||
|
||||
auto add = [&](Symbol<E> *sym) {
|
||||
auto add = [&](Symbol<E> *sym, u32 visibility = STV_DEFAULT) {
|
||||
obj->symbols.push_back(sym);
|
||||
|
||||
// An actual value will be set to a linker-synthesized symbol by
|
||||
@@ -576,7 +589,7 @@ void create_internal_file(Context<E> &ctx) {
|
||||
esym.st_type = STT_NOTYPE;
|
||||
esym.st_shndx = SHN_ABS;
|
||||
esym.st_bind = STB_GLOBAL;
|
||||
esym.st_visibility = STV_DEFAULT;
|
||||
esym.st_visibility = visibility;
|
||||
ctx.internal_esyms.push_back(esym);
|
||||
};
|
||||
|
||||
@@ -609,6 +622,26 @@ void create_internal_file(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// Add _GLOBAL_OFFSET_TABLE_
|
||||
//
|
||||
// We watn to do this before resolve_symbols() because on HP/PA, the
|
||||
// symbol may also be defined by shared libraries. We always want to
|
||||
// make the undefined symbol to it to refer to the synthesized one,
|
||||
// so the symbol needs to be available before resolve_symbols().
|
||||
{
|
||||
Symbol<E> *sym = get_symbol(ctx, "_GLOBAL_OFFSET_TABLE_");
|
||||
ctx._GLOBAL_OFFSET_TABLE_ = sym;
|
||||
|
||||
if constexpr (is_hppa<E>) {
|
||||
add(sym);
|
||||
sym->is_exported = true;
|
||||
if (ctx.arg.shared)
|
||||
sym->is_imported = true;
|
||||
} else {
|
||||
add(sym, STV_HIDDEN);
|
||||
}
|
||||
}
|
||||
|
||||
// Add --section-order symbols
|
||||
for (SectionOrder &ord : ctx.arg.section_order)
|
||||
if (ord.type == SectionOrder::SYMBOL)
|
||||
@@ -672,7 +705,6 @@ void add_synthetic_symbols(Context<E> &ctx) {
|
||||
ctx.__preinit_array_start = add("__preinit_array_start");
|
||||
ctx.__preinit_array_end = add("__preinit_array_end");
|
||||
ctx._DYNAMIC = add("_DYNAMIC");
|
||||
ctx._GLOBAL_OFFSET_TABLE_ = add("_GLOBAL_OFFSET_TABLE_");
|
||||
ctx._PROCEDURE_LINKAGE_TABLE_ = add("_PROCEDURE_LINKAGE_TABLE_");
|
||||
ctx.__bss_start = add("__bss_start");
|
||||
ctx._end = add("_end");
|
||||
@@ -715,6 +747,9 @@ void add_synthetic_symbols(Context<E> &ctx) {
|
||||
if constexpr (is_ppc32<E>)
|
||||
ctx.extra._SDA_BASE_ = add("_SDA_BASE_");
|
||||
|
||||
if constexpr (is_hppa<E>)
|
||||
ctx.extra.global = add("$global$");
|
||||
|
||||
for (Chunk<E> *chunk : ctx.chunks) {
|
||||
if (std::optional<std::string> name = get_start_stop_name(ctx, *chunk)) {
|
||||
add(save_string(ctx, "__start_" + *name));
|
||||
@@ -962,6 +997,17 @@ template <typename E>
|
||||
void check_symbol_types(Context<E> &ctx) {
|
||||
Timer t(ctx, "check_symbol_types");
|
||||
|
||||
auto canonicalize = [](u32 ty) -> u32 {
|
||||
if (ty == STT_GNU_IFUNC)
|
||||
return STT_FUNC;
|
||||
|
||||
if constexpr (is_hppa<E>)
|
||||
if (ty == STT_HPPA_MILLI)
|
||||
return STT_FUNC;
|
||||
|
||||
return ty;
|
||||
};
|
||||
|
||||
auto check = [&](InputFile<E> *file) {
|
||||
for (i64 i = file->first_global; i < file->elf_syms.size(); i++) {
|
||||
const ElfSym<E> &esym = file->elf_syms[i];
|
||||
@@ -970,13 +1016,8 @@ void check_symbol_types(Context<E> &ctx) {
|
||||
if (!sym.file)
|
||||
continue;
|
||||
|
||||
u32 x = sym.esym().st_type;
|
||||
if (x == STT_GNU_IFUNC)
|
||||
x = STT_FUNC;
|
||||
|
||||
u32 y = esym.st_type;
|
||||
if (y == STT_GNU_IFUNC)
|
||||
y = STT_FUNC;
|
||||
u32 x = canonicalize(sym.esym().st_type);
|
||||
u32 y = canonicalize(esym.st_type);
|
||||
|
||||
if (x != STT_NOTYPE && y != STT_NOTYPE && x != y)
|
||||
Warn(ctx) << "symbol type mismatch: " << sym << '\n'
|
||||
@@ -1319,8 +1360,8 @@ void scan_relocations(Context<E> &ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (is_ppc64v1<E>)
|
||||
if (sym->flags & NEEDS_PPC_OPD)
|
||||
if constexpr (is_ppc64v1<E> || is_hppa<E>)
|
||||
if (sym->flags & NEEDS_OPD)
|
||||
ctx.extra.opd->add_symbol(ctx, sym);
|
||||
|
||||
sym->flags = 0;
|
||||
@@ -2186,10 +2227,10 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
}
|
||||
};
|
||||
|
||||
auto stop = [](Symbol<E> *sym, auto &chunk) {
|
||||
auto stop = [](Symbol<E> *sym, auto &chunk, i64 bias = 0) {
|
||||
if (sym && chunk) {
|
||||
sym->set_output_section(chunk);
|
||||
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size;
|
||||
sym->value = chunk->shdr.sh_addr + chunk->shdr.sh_size + bias;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -2279,13 +2320,19 @@ void fix_synthetic_symbols(Context<E> &ctx) {
|
||||
// _DYNAMIC
|
||||
start(ctx._DYNAMIC, ctx.dynamic);
|
||||
|
||||
// _GLOBAL_OFFSET_TABLE_. I don't know why, but for the sake of
|
||||
// compatibility with existing code, it must be set to the beginning of
|
||||
// .got.plt instead of .got only on i386 and x86-64.
|
||||
if constexpr (is_x86<E>)
|
||||
// _GLOBAL_OFFSET_TABLE_
|
||||
if constexpr (is_x86<E>) {
|
||||
// I don't know why, but for the sake of compatibility with existing
|
||||
// code, it must be set to the beginning of .got.plt instead of .got
|
||||
// only on i386 and x86-64.
|
||||
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.gotplt);
|
||||
else
|
||||
} else if constexpr (is_hppa<E>) {
|
||||
i64 offset = ctx.extra.opd->shdr.sh_size - sizeof(Word<E>) * 2;
|
||||
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.extra.opd, offset);
|
||||
start(ctx.extra.global, ctx.extra.opd, offset);
|
||||
} else {
|
||||
start(ctx._GLOBAL_OFFSET_TABLE_, ctx.got);
|
||||
}
|
||||
|
||||
// _PROCEDURE_LINKAGE_TABLE_. We need this on SPARC.
|
||||
start(ctx._PROCEDURE_LINKAGE_TABLE_, ctx.plt);
|
||||
|
||||
+26
-9
@@ -47,13 +47,23 @@ namespace mold::elf {
|
||||
// ARM32: PC ± 16 MiB
|
||||
// PPC: PC ± 32 MiB
|
||||
template <typename E>
|
||||
static constexpr i64 max_distance =
|
||||
1LL << (is_arm64<E> ? 27 : is_arm32<E> ? 24 : 25);
|
||||
static consteval i64 max_distance() {
|
||||
if constexpr (is_arm64<E>) {
|
||||
return 1LL << 27;
|
||||
} else if constexpr (is_arm32<E>) {
|
||||
return 1LL << 24;
|
||||
} else if constexpr (is_ppc<E>) {
|
||||
return 1LL << 25;
|
||||
} else {
|
||||
static_assert(is_hppa<E>);
|
||||
return 1LL << 18;
|
||||
}
|
||||
}
|
||||
|
||||
// We create thunks for each 12.8/1.6/3.2 MiB code block for
|
||||
// ARM64/ARM32/PPC, respectively.
|
||||
template <typename E>
|
||||
static constexpr i64 batch_size = max_distance<E> / 10;
|
||||
static constexpr i64 batch_size = max_distance<E>() / 10;
|
||||
|
||||
// We assume that a single thunk group is smaller than 100 KiB.
|
||||
static constexpr i64 max_thunk_size = 102400;
|
||||
@@ -70,9 +80,11 @@ static bool needs_thunk_rel(const ElfRel<E> &r) {
|
||||
ty == R_ARM_CALL || ty == R_ARM_THM_CALL;
|
||||
} else if constexpr (is_ppc32<E>) {
|
||||
return ty == R_PPC_REL24 || ty == R_PPC_PLTREL24 || ty == R_PPC_LOCAL24PC;
|
||||
} else {
|
||||
static_assert(is_ppc64<E>);
|
||||
} else if constexpr (is_ppc64<E>) {
|
||||
return ty == R_PPC64_REL24;
|
||||
} else {
|
||||
static_assert(is_hppa<E>);
|
||||
return ty == R_PARISC_PCREL17F || ty == R_PARISC_PCREL32;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,13 +118,18 @@ static bool is_reachable(Context<E> &ctx, InputSection<E> &isec,
|
||||
return false;
|
||||
}
|
||||
|
||||
// (TODO)
|
||||
if constexpr (is_hppa<E>)
|
||||
if (sym.has_opd(ctx))
|
||||
return false;
|
||||
|
||||
// Compute a distance between the relocated place and the symbol
|
||||
// and check if they are within reach.
|
||||
i64 S = sym.get_addr(ctx, NO_OPD);
|
||||
i64 A = get_addend(isec, rel);
|
||||
i64 P = isec.get_addr() + rel.r_offset;
|
||||
i64 val = S + A - P;
|
||||
return -max_distance<E> <= val && val < max_distance<E>;
|
||||
return -max_distance<E>() <= val && val < max_distance<E>();
|
||||
}
|
||||
|
||||
template <typename E>
|
||||
@@ -194,7 +211,7 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
|
||||
// Move D foward as far as we can jump from B to anywhere in a thunk after D.
|
||||
while (d < m.size() &&
|
||||
align_to(offset, 1 << m[d]->p2align) + m[d]->sh_size + max_thunk_size <
|
||||
m[b]->offset + max_distance<E>) {
|
||||
m[b]->offset + max_distance<E>()) {
|
||||
offset = align_to(offset, 1 << m[d]->p2align);
|
||||
m[d]->offset = offset;
|
||||
offset += m[d]->sh_size;
|
||||
@@ -212,7 +229,7 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
|
||||
// Move A forward so that A is reachable from C.
|
||||
i64 c_offset = (c == m.size()) ? offset : m[c]->offset;
|
||||
while (a < osec.thunks.size() &&
|
||||
osec.thunks[a]->offset + max_distance<E> < c_offset)
|
||||
osec.thunks[a]->offset + max_distance<E>() < c_offset)
|
||||
reset_thunk(*osec.thunks[a++]);
|
||||
|
||||
// Create a thunk for input sections between B and C and place it at D.
|
||||
@@ -266,7 +283,7 @@ void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
|
||||
osec.shdr.sh_size = offset;
|
||||
}
|
||||
|
||||
#if MOLD_ARM32 || MOLD_ARM64 || MOLD_PPC32 || MOLD_PPC64V1 || MOLD_PPC64V2
|
||||
#if MOLD_ARM32 || MOLD_ARM64 || MOLD_PPC32 || MOLD_PPC64V1 || MOLD_PPC64V2 || MOLD_HPPA32
|
||||
using E = MOLD_TARGET;
|
||||
template void create_range_extension_thunks(Context<E> &, OutputSection<E> &);
|
||||
#endif
|
||||
|
||||
@@ -57,6 +57,7 @@ add_target(s390x-linux-gnu)
|
||||
add_target(m68k-linux-gnu)
|
||||
add_target(sh4-linux-gnu)
|
||||
add_target(alpha-linux-gnu)
|
||||
add_target(hppa-linux-gnu)
|
||||
|
||||
if(MOLD_ENABLE_QEMU_TESTS_RV32)
|
||||
add_target(riscv32-linux-gnu)
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
# https://sourceware.org/bugzilla/show_bug.cgi?id=29655
|
||||
[ $MACHINE = s390x ] && $CC -v 2>&1 | grep -E '^gcc version 1[0-3]\.' && skip
|
||||
|
||||
# This test does not pass even with GNU ld, which means function pointer
|
||||
# equality is not guaranteed on HP/PA. I'm not sure if this is an bug in
|
||||
# QEMU or this test really fails on a real HP/PA machine, but it's quite
|
||||
# surprising. Many programs could silently break if function pointer
|
||||
# equality is not guaranteed.
|
||||
[ $MACHINE = hppa ] && skip
|
||||
|
||||
cat <<EOF | $CC -c -fPIC -o$t/a.o -xc -
|
||||
int foo = 4;
|
||||
|
||||
@@ -19,7 +26,7 @@ EOF
|
||||
|
||||
$CC -B. -shared -fPIC -o $t/b.so $t/a.o -Wl,-Bsymbolic -Wl,-Bno-symbolic
|
||||
|
||||
cat <<EOF | $CC -c -o $t/c.o -xc - -fno-PIE
|
||||
cat <<EOF | $CC -B. -c -o $t/c.o -xc - -fno-PIE
|
||||
#include <stdio.h>
|
||||
|
||||
extern int foo;
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
# https://sourceware.org/bugzilla/show_bug.cgi?id=29655
|
||||
[ $MACHINE = s390x ] && $CC -v 2>&1 | grep -E '^gcc version 1[0-3]\.' && skip
|
||||
|
||||
# Pointer equality does not seem to be guaranteed on HP/PA
|
||||
[ $MACHINE = hppa ] && skip
|
||||
|
||||
cat <<EOF | $CC -o $t/a.so -fPIC -shared -xc -
|
||||
void *foo() {
|
||||
return foo;
|
||||
|
||||
Reference in new issue
Block a user