Compare commits

...
1 Commits
Author SHA1 Message Date
Rui Ueyama 0c57eca7b0 Support 32-bit PA-RISC 2023-01-05 14:50:32 +08:00
14 changed files with 1038 additions and 75 deletions

No files matched your search

+3 -2
View File
@@ -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
+506
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+162
View File
@@ -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
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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)
+8 -1
View File
@@ -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;
+3
View File
@@ -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;