1
1
mirror of https://github.com/rui314/mold.git synced 2024-09-11 21:17:28 +03:00

[ELF][m68k] Add initial m68k support

This commit is contained in:
Rui Ueyama 2022-10-30 11:06:15 +08:00
parent e0ff005333
commit 5fe60e3b6d
10 changed files with 505 additions and 17 deletions

View File

@ -43,7 +43,7 @@ jobs:
# Install cross toolchains
dpkg --add-architecture i386
./install-build-deps.sh update
apt-get install -y sudo qemu-user gdb zstd dwarfdump gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf gcc-10-powerpc64-linux-gnu gcc-10-powerpc64le-linux-gnu gcc-10-s390x-linux-gnu gcc-10-sparc64-linux-gnu g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf g++-10-powerpc64-linux-gnu g++-10-powerpc64le-linux-gnu g++-10-s390x-linux-gnu g++-10-sparc64-linux-gnu
apt-get install -y sudo qemu-user gdb zstd dwarfdump gcc-10-i686-linux-gnu gcc-10-aarch64-linux-gnu gcc-10-riscv64-linux-gnu gcc-10-arm-linux-gnueabihf gcc-10-powerpc64-linux-gnu gcc-10-powerpc64le-linux-gnu gcc-10-s390x-linux-gnu gcc-10-sparc64-linux-gnu gcc-10-m68k-linux-gnu g++-10-i686-linux-gnu g++-10-aarch64-linux-gnu g++-10-riscv64-linux-gnu g++-10-arm-linux-gnueabihf g++-10-powerpc64-linux-gnu g++-10-powerpc64le-linux-gnu g++-10-s390x-linux-gnu g++-10-sparc64-linux-gnu g++-10-m68k-linux-gnu
ln -sf /usr/bin/i686-linux-gnu-gcc-10 /usr/bin/i686-linux-gnu-gcc
ln -sf /usr/bin/i686-linux-gnu-g++-10 /usr/bin/i686-linux-gnu-g++
ln -sf /usr/bin/aarch64-linux-gnu-gcc-10 /usr/bin/aarch64-linux-gnu-gcc
@ -60,6 +60,8 @@ jobs:
ln -sf /usr/bin/s390x-linux-gnu-g++-10 /usr/bin/s390x-linux-gnu-g++
ln -sf /usr/bin/sparc64-linux-gnu-gcc-10 /usr/bin/sparc64-linux-gnu-gcc
ln -sf /usr/bin/sparc64-linux-gnu-g++-10 /usr/bin/sparc64-linux-gnu-g++
ln -sf /usr/bin/m68k-linux-gnu-gcc-10 /usr/bin/m68k-linux-gnu-gcc
ln -sf /usr/bin/m68k-linux-gnu-g++-10 /usr/bin/m68k-linux-gnu-g++
# Install a RV32 toolchain from third party since it's not available
# as an Ubuntu package.

View File

@ -246,7 +246,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
# compiler instances. This is hacky but greatly reduces compile time.
list(APPEND MOLD_ELF_TARGETS
X86_64 I386 ARM64 ARM32 RV32LE RV32BE RV64LE RV64BE
PPC64V1 PPC64V2 SPARC64 S390X)
PPC64V1 PPC64V2 S390X SPARC64 M68K)
list(APPEND MOLD_ELF_TEMPLATE_FILES
elf/cmdline.cc
@ -308,6 +308,7 @@ target_sources(mold PRIVATE
elf/arch-arm32.cc
elf/arch-arm64.cc
elf/arch-i386.cc
elf/arch-m68k.cc
elf/arch-ppc64v1.cc
elf/arch-ppc64v2.cc
elf/arch-riscv.cc

338
elf/arch-m68k.cc Normal file
View File

@ -0,0 +1,338 @@
// This file contains code for the Motorola 68000 series microprocessors,
// which is often abbreviated as m68k. Running a Unix-like system on a
// m68k-based machine today is probably a retro-computing hobby activity,
// but the processor was a popular choice to build Unix computers during
// '80s. Early Sun workstations for example used m68k. Macintosh until
// 1994 were based on m68k as well until they switched to PowerPC (and
// then to x86 and to ARM.)
//
// From the linker's point of view, it is not hard to support m68k. It's
// just a 32-bit big-endian CISC ISA. Compared to comtemporary i386,
// m68k's psABI is actually simpler because m68k has PC-relative memory
// access instructions and therefore can support position-independent
// code without too much hassle.
#include "mold.h"
namespace mold::elf {
using E = M68K;
template <>
void write_plt_header(Context<E> &ctx, u8 *buf) {
static const u8 insn[] = {
0x2f, 0x00, // move.l %d0, -(%sp)
0x2f, 0x3b, 0x01, 0x70, 0, 0, 0, 0, // move.l (GOTPLT+4, %pc), -(%sp)
0x4e, 0xfb, 0x01, 0x71, 0, 0, 0, 0, // jmp ([GOTPLT+8, %pc])
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 6) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr;
*(ub32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 4;
}
template <>
void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const u8 insn[] = {
0x20, 0x3c, 0, 0, 0, 0, // move.l PLT_OFFSET, %d0
0x4e, 0xfb, 0x01, 0x71, 0, 0, 0, 0, // jmp ([GOTPLT_ENTRY, %pc])
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 2) = sym.get_plt_idx(ctx) * sizeof(ElfRel<E>);
*(ub32 *)(buf + 10) = sym.get_gotplt_addr(ctx) - sym.get_plt_addr(ctx) - 8;
}
template <>
void write_pltgot_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
static const u8 insn[] = {
0x4e, 0xfb, 0x01, 0x71, 0, 0, 0, 0, // jmp ([GOT_ENTRY, %pc])
};
memcpy(buf, insn, sizeof(insn));
*(ub32 *)(buf + 4) = sym.get_got_addr(ctx) - sym.get_plt_addr(ctx) - 2;
}
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_68K_32:
*(ub32 *)loc = val;
break;
case R_68K_PC32:
*(ub32 *)loc = val - this->shdr.sh_addr - offset;
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);
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;
auto check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write16 = [&](u64 val) {
check(val, 0, 1 << 16);
*(ub16 *)loc = val;
};
auto write16s = [&](u64 val) {
check(val, -(1 << 15), 1 << 15);
*(ub16 *)loc = val;
};
auto write8 = [&](u64 val) {
check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
check(val, -(1 << 7), 1 << 7);
*loc = val;
};
#define S sym.get_addr(ctx)
#define A rel.r_addend
#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_68K_32:
apply_dyn_absrel(ctx, sym, rel, loc, S, A, P, dynrel);
break;
case R_68K_16:
write16(S + A);
break;
case R_68K_8:
write8(S + A);
break;
case R_68K_PC32:
case R_68K_PLT32:
*(ub32 *)loc = S + A - P;
break;
case R_68K_PC16:
case R_68K_PLT16:
write16s(S + A - P);
break;
case R_68K_PC8:
case R_68K_PLT8:
write8s(S + A - P);
break;
case R_68K_GOTPCREL32:
*(ub32 *)loc = GOT + A - P;
break;
case R_68K_GOTPCREL16:
write16s(GOT + A - P);
break;
case R_68K_GOTPCREL8:
write8s(GOT + A - P);
break;
case R_68K_GOTOFF32:
*(ub32 *)loc = G + A;
break;
case R_68K_GOTOFF16:
write16(G + A);
break;
case R_68K_GOTOFF8:
write8(G + A);
break;
case R_68K_TLS_GD32:
*(ub32 *)loc = sym.get_tlsgd_addr(ctx) + A - GOT;
break;
case R_68K_TLS_GD16:
write16(sym.get_tlsgd_addr(ctx) + A - GOT);
break;
case R_68K_TLS_GD8:
write8(sym.get_tlsgd_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LDM32:
*(ub32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - GOT;
break;
case R_68K_TLS_LDM16:
write16(ctx.got->get_tlsld_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LDM8:
write8(ctx.got->get_tlsld_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LDO32:
*(ub32 *)loc = S + A - ctx.tls_begin - E::tls_dtp_offset;
break;
case R_68K_TLS_LDO16:
write16s(S + A - ctx.tls_begin - E::tls_dtp_offset);
break;
case R_68K_TLS_LDO8:
write8s(S + A - ctx.tls_begin - E::tls_dtp_offset);
break;
case R_68K_TLS_IE32:
*(ub32 *)loc = sym.get_gottp_addr(ctx) + A - GOT;
break;
case R_68K_TLS_IE16:
write16(sym.get_gottp_addr(ctx) + A - GOT);
break;
case R_68K_TLS_IE8:
write8(sym.get_gottp_addr(ctx) + A - GOT);
break;
case R_68K_TLS_LE32:
*(ub32 *)loc = S + A - ctx.tp_addr;
break;
case R_68K_TLS_LE16:
write16(S + A - ctx.tp_addr);
break;
case R_68K_TLS_LE8:
write8(S + A - ctx.tp_addr);
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 : (i64)rel.r_addend)
switch (rel.r_type) {
case R_68K_32:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(ub32 *)loc = *val;
else
*(ub32 *)loc = S + A;
break;
default:
Fatal(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 m68k";
switch (rel.r_type) {
case R_68K_32:
scan_rel(ctx, sym, rel, dyn_absrel_table);
break;
case R_68K_16:
case R_68K_8:
scan_rel(ctx, sym, rel, absrel_table);
break;
case R_68K_PC32:
case R_68K_PC16:
case R_68K_PC8:
scan_rel(ctx, sym, rel, pcrel_table);
break;
case R_68K_GOTPCREL32:
case R_68K_GOTPCREL16:
case R_68K_GOTPCREL8:
case R_68K_GOTOFF32:
case R_68K_GOTOFF16:
case R_68K_GOTOFF8:
sym.flags |= NEEDS_GOT;
break;
case R_68K_PLT32:
case R_68K_PLT16:
case R_68K_PLT8:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_68K_TLS_GD32:
case R_68K_TLS_GD16:
case R_68K_TLS_GD8:
sym.flags |= NEEDS_TLSGD;
break;
case R_68K_TLS_LDM32:
case R_68K_TLS_LDM16:
case R_68K_TLS_LDM8:
ctx.needs_tlsld = true;
break;
case R_68K_TLS_IE32:
case R_68K_TLS_IE16:
case R_68K_TLS_IE8:
sym.flags |= NEEDS_GOTTP;
break;
case R_68K_TLS_LDO32:
case R_68K_TLS_LDO16:
case R_68K_TLS_LDO8:
case R_68K_TLS_LE32:
case R_68K_TLS_LE16:
case R_68K_TLS_LE8:
break;
default:
Fatal(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
} // namespace mold::elf

View File

@ -194,8 +194,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 elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64linux aarch64elf elf32lriscv elf32briscv elf64lriscv elf64briscv elf64_s390 elf64_sparc)";
mold: supported targets: elf32-i386 elf64-x86-64 elf32-littlearm elf64-littleaarch64 elf32-littleriscv elf32-bigriscv elf64-littleriscv elf64-bigriscv elf64-powerpc elf64-powerpc elf64-powerpcle elf64-s390 elf64-sparc elf32-m68k
mold: supported emulations: elf_i386 elf_x86_64 armelf_linux_eabi aarch64linux aarch64elf elf32lriscv elf32briscv elf64lriscv elf64briscv elf64_s390 elf64_sparc m68kelf)";
static std::vector<std::string> add_dashes(std::string name) {
// Single-letter option
@ -507,7 +507,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
<< "\n Supported emulations:\n elf_x86_64\n elf_i386\n"
<< " aarch64linux\n armelf_linux_eabi\n elf64lriscv\n"
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
<< " elf64ppc\n elf64lppc\n elf64_s390\n elf64_sparc";
<< " elf64ppc\n elf64lppc\n elf64_s390\n elf64_sparc\n"
<< " m68kelf";
version_shown = true;
} else if (read_arg("m")) {
if (arg == "elf_x86_64") {
@ -534,6 +535,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
ctx.arg.emulation = MachineType::S390X;
} else if (arg == "elf64_sparc") {
ctx.arg.emulation = MachineType::SPARC64;
} else if (arg == "m68kelf") {
ctx.arg.emulation = MachineType::M68K;
} else {
Fatal(ctx) << "unknown -m argument: " << arg;
}

137
elf/elf.h
View File

@ -19,8 +19,9 @@ struct RV32LE;
struct RV32BE;
struct PPC64V1;
struct PPC64V2;
struct SPARC64;
struct S390X;
struct SPARC64;
struct M68K;
template <typename E> struct ElfSym;
template <typename E> struct ElfShdr;
@ -39,7 +40,7 @@ static constexpr u32 R_NONE = 0;
enum class MachineType {
NONE, X86_64, I386, ARM64, ARM32, RV64LE, RV64BE, RV32LE, RV32BE,
PPC64V1, PPC64V2, SPARC64, S390X
PPC64V1, PPC64V2, S390X, SPARC64, M68K
};
inline std::ostream &operator<<(std::ostream &out, MachineType mt) {
@ -55,8 +56,9 @@ inline std::ostream &operator<<(std::ostream &out, MachineType mt) {
case MachineType::RV32BE: out << "riscv32be"; break;
case MachineType::PPC64V1: out << "ppc64v1"; break;
case MachineType::PPC64V2: out << "ppc64v2"; break;
case MachineType::SPARC64: out << "sparc64"; break;
case MachineType::S390X: out << "s390x"; break;
case MachineType::SPARC64: out << "sparc64"; break;
case MachineType::M68K: out << "m68k"; break;
}
return out;
}
@ -179,6 +181,7 @@ static constexpr u32 EV_CURRENT = 1;
static constexpr u32 EM_NONE = 0;
static constexpr u32 EM_386 = 3;
static constexpr u32 EM_68K = 4;
static constexpr u32 EM_PPC64 = 21;
static constexpr u32 EM_S390X = 22;
static constexpr u32 EM_ARM = 40;
@ -1649,6 +1652,96 @@ inline std::string rel_to_string<S390X>(u32 r_type) {
return "unknown (" + std::to_string(r_type) + ")";
}
static constexpr u32 R_68K_NONE = 0;
static constexpr u32 R_68K_32 = 1;
static constexpr u32 R_68K_16 = 2;
static constexpr u32 R_68K_8 = 3;
static constexpr u32 R_68K_PC32 = 4;
static constexpr u32 R_68K_PC16 = 5;
static constexpr u32 R_68K_PC8 = 6;
static constexpr u32 R_68K_GOTPCREL32 = 7;
static constexpr u32 R_68K_GOTPCREL16 = 8;
static constexpr u32 R_68K_GOTPCREL8 = 9;
static constexpr u32 R_68K_GOTOFF32 = 10;
static constexpr u32 R_68K_GOTOFF16 = 11;
static constexpr u32 R_68K_GOTOFF8 = 12;
static constexpr u32 R_68K_PLT32 = 13;
static constexpr u32 R_68K_PLT16 = 14;
static constexpr u32 R_68K_PLT8 = 15;
static constexpr u32 R_68K_PLTOFF32 = 16;
static constexpr u32 R_68K_PLTOFF16 = 17;
static constexpr u32 R_68K_PLTOFF8 = 18;
static constexpr u32 R_68K_COPY = 19;
static constexpr u32 R_68K_GLOB_DAT = 20;
static constexpr u32 R_68K_JMP_SLOT = 21;
static constexpr u32 R_68K_RELATIVE = 22;
static constexpr u32 R_68K_TLS_GD32 = 25;
static constexpr u32 R_68K_TLS_GD16 = 26;
static constexpr u32 R_68K_TLS_GD8 = 27;
static constexpr u32 R_68K_TLS_LDM32 = 28;
static constexpr u32 R_68K_TLS_LDM16 = 29;
static constexpr u32 R_68K_TLS_LDM8 = 30;
static constexpr u32 R_68K_TLS_LDO32 = 31;
static constexpr u32 R_68K_TLS_LDO16 = 32;
static constexpr u32 R_68K_TLS_LDO8 = 33;
static constexpr u32 R_68K_TLS_IE32 = 34;
static constexpr u32 R_68K_TLS_IE16 = 35;
static constexpr u32 R_68K_TLS_IE8 = 36;
static constexpr u32 R_68K_TLS_LE32 = 37;
static constexpr u32 R_68K_TLS_LE16 = 38;
static constexpr u32 R_68K_TLS_LE8 = 39;
static constexpr u32 R_68K_TLS_DTPMOD32 = 40;
static constexpr u32 R_68K_TLS_DTPREL32 = 41;
static constexpr u32 R_68K_TLS_TPREL32 = 42;
template <>
inline std::string rel_to_string<M68K>(u32 r_type) {
switch (r_type) {
case R_68K_NONE: return "R_68K_NONE";
case R_68K_32: return "R_68K_32";
case R_68K_16: return "R_68K_16";
case R_68K_8: return "R_68K_8";
case R_68K_PC32: return "R_68K_PC32";
case R_68K_PC16: return "R_68K_PC16";
case R_68K_PC8: return "R_68K_PC8";
case R_68K_GOTPCREL32: return "R_68K_GOTPCREL32";
case R_68K_GOTPCREL16: return "R_68K_GOTPCREL16";
case R_68K_GOTPCREL8: return "R_68K_GOTPCREL8";
case R_68K_GOTOFF32: return "R_68K_GOTOFF32";
case R_68K_GOTOFF16: return "R_68K_GOTOFF16";
case R_68K_GOTOFF8: return "R_68K_GOTOFF8";
case R_68K_PLT32: return "R_68K_PLT32";
case R_68K_PLT16: return "R_68K_PLT16";
case R_68K_PLT8: return "R_68K_PLT8";
case R_68K_PLTOFF32: return "R_68K_PLTOFF32";
case R_68K_PLTOFF16: return "R_68K_PLTOFF16";
case R_68K_PLTOFF8: return "R_68K_PLTOFF8";
case R_68K_COPY: return "R_68K_COPY";
case R_68K_GLOB_DAT: return "R_68K_GLOB_DAT";
case R_68K_JMP_SLOT: return "R_68K_JMP_SLOT";
case R_68K_RELATIVE: return "R_68K_RELATIVE";
case R_68K_TLS_GD32: return "R_68K_TLS_GD32";
case R_68K_TLS_GD16: return "R_68K_TLS_GD16";
case R_68K_TLS_GD8: return "R_68K_TLS_GD8";
case R_68K_TLS_LDM32: return "R_68K_TLS_LDM32";
case R_68K_TLS_LDM16: return "R_68K_TLS_LDM16";
case R_68K_TLS_LDM8: return "R_68K_TLS_LDM8";
case R_68K_TLS_LDO32: return "R_68K_TLS_LDO32";
case R_68K_TLS_LDO16: return "R_68K_TLS_LDO16";
case R_68K_TLS_LDO8: return "R_68K_TLS_LDO8";
case R_68K_TLS_IE32: return "R_68K_TLS_IE32";
case R_68K_TLS_IE16: return "R_68K_TLS_IE16";
case R_68K_TLS_IE8: return "R_68K_TLS_IE8";
case R_68K_TLS_LE32: return "R_68K_TLS_LE32";
case R_68K_TLS_LE16: return "R_68K_TLS_LE16";
case R_68K_TLS_LE8: return "R_68K_TLS_LE8";
case R_68K_TLS_DTPMOD32: return "R_68K_TLS_DTPMOD32";
case R_68K_TLS_DTPREL32: return "R_68K_TLS_DTPREL32";
case R_68K_TLS_TPREL32: return "R_68K_TLS_TPREL32";
}
return "unknown (" + std::to_string(r_type) + ")";
}
//
// DWARF data types
//
@ -2341,6 +2434,9 @@ static constexpr bool is_sparc = std::is_same_v<E, SPARC64>;
template <typename E>
static constexpr bool is_s390x = std::is_same_v<E, S390X>;
template <typename E>
static constexpr bool is_m68k = std::is_same_v<E, M68K>;
struct X86_64 {
static constexpr u32 R_COPY = R_X86_64_COPY;
static constexpr u32 R_GLOB_DAT = R_X86_64_GLOB_DAT;
@ -2795,4 +2891,39 @@ template <> struct ElfVerdaux<SPARC64> : EBVerdaux {};
template <> struct ElfChdr<SPARC64> : EB64Chdr {};
template <> struct ElfNhdr<SPARC64> : EBNhdr {};
struct M68K {
static constexpr u32 R_COPY = R_68K_COPY;
static constexpr u32 R_GLOB_DAT = R_68K_GLOB_DAT;
static constexpr u32 R_JUMP_SLOT = R_68K_JMP_SLOT;
static constexpr u32 R_ABS = R_68K_32;
static constexpr u32 R_RELATIVE = R_68K_RELATIVE;
static constexpr u32 R_IRELATIVE = R_68K_NONE; // m68k does not support ifunc
static constexpr u32 R_DTPOFF = R_68K_TLS_DTPREL32;
static constexpr u32 R_TPOFF = R_68K_TLS_TPREL32;
static constexpr u32 R_DTPMOD = R_68K_TLS_DTPMOD32;
static constexpr MachineType machine_type = MachineType::M68K;
static constexpr bool is_64 = false;
static constexpr bool is_le = false;
static constexpr u32 page_size = 8192;
static constexpr u32 e_machine = EM_68K;
static constexpr u32 plt_hdr_size = 18;
static constexpr u32 plt_size = 14;
static constexpr u32 pltgot_size = 8;
static constexpr u32 tls_dtp_offset = 0x8000;
};
template <> struct ElfSym<M68K> : EB32Sym {};
template <> struct ElfShdr<M68K> : EB32Shdr {};
template <> struct ElfEhdr<M68K> : EB32Ehdr {};
template <> struct ElfPhdr<M68K> : EB32Phdr {};
template <> struct ElfRel<M68K> : EB32Rela { using EB32Rela::EB32Rela; };
template <> struct ElfDyn<M68K> : EB32Dyn {};
template <> struct ElfVerneed<M68K> : EBVerneed {};
template <> struct ElfVernaux<M68K> : EBVernaux {};
template <> struct ElfVerdef<M68K> : EBVerdef {};
template <> struct ElfVerdaux<M68K> : EBVerdaux {};
template <> struct ElfChdr<M68K> : EB32Chdr {};
template <> struct ElfNhdr<M68K> : EBNhdr {};
} // namespace mold::elf

View File

@ -62,6 +62,8 @@ static MachineType get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf)
return MachineType::S390X;
case EM_SPARC64:
return MachineType::SPARC64;
case EM_68K:
return MachineType::M68K;
default:
return MachineType::NONE;
}
@ -397,6 +399,8 @@ static int redo_main(int argc, char **argv, MachineType ty) {
return elf_main<S390X>(argc, argv);
case MachineType::SPARC64:
return elf_main<SPARC64>(argc, argv);
case MachineType::M68K:
return elf_main<M68K>(argc, argv);
default:
unreachable();
}
@ -792,6 +796,7 @@ extern template int elf_main<PPC64V1>(int, char **);
extern template int elf_main<PPC64V2>(int, char **);
extern template int elf_main<S390X>(int, char **);
extern template int elf_main<SPARC64>(int, char **);
extern template int elf_main<M68K>(int, char **);
int main(int argc, char **argv) {
return elf_main<X86_64>(argc, argv);

View File

@ -178,12 +178,12 @@ static void init_thread_pointers(Context<E> &ctx, ElfPhdr<E> phdr) {
// template image when copying TLVs to per-thread area, so we need
// to offset it.
//
// On PPC64, TP is 0x7000 (28 KiB) past the beginning of the TLV block
// to maximize the addressable range for load/store instructions with
// 16-bits signed immediates. It's not exactly 0x8000 (32 KiB) off
// because there's a small implementation-defined piece of data before
// the TLV block, and the runtime wants to access them efficiently
// too.
// On PPC64 and m68k, TP is 0x7000 (28 KiB) past the beginning of the
// TLV block to maximize the addressable range for load/store
// instructions with 16-bits signed immediates. It's not exactly 0x8000
// (32 KiB) off because there's a small implementation-defined piece of
// data before the TLV block, and the runtime wants to access them
// efficiently too.
//
// RISC-V just uses the beginning of the TLV block as TP. RISC-V
// load/store instructions usually take 12-bits signed immediates,
@ -193,7 +193,7 @@ static void init_thread_pointers(Context<E> &ctx, ElfPhdr<E> phdr) {
ctx.tp_addr = align_to(phdr.p_vaddr + phdr.p_memsz, phdr.p_align);
} else if constexpr (is_arm<E>) {
ctx.tp_addr = align_down(phdr.p_vaddr - sizeof(Word<E>) * 2, phdr.p_align);
} else if constexpr (is_ppc<E>) {
} else if constexpr (is_ppc<E> || is_m68k<E>) {
ctx.tp_addr = phdr.p_vaddr + 0x7000;
} else {
static_assert(is_riscv<E>);

View File

@ -97,8 +97,13 @@ FileType get_file_type(MappedFile<C> *mf) {
elf::EB32Ehdr &ehdr = *(elf::EB32Ehdr *)data.data();
if (ehdr.e_type == elf::ET_REL) {
if (is_gcc_lto_obj<elf::SPARC64>(mf))
return FileType::GCC_LTO_OBJ;
if (ehdr.e_ident[elf::EI_CLASS] == elf::ELFCLASS32) {
if (is_gcc_lto_obj<elf::M68K>(mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<elf::SPARC64>(mf))
return FileType::GCC_LTO_OBJ;
}
return FileType::ELF_OBJ;
}

View File

@ -53,6 +53,7 @@ add_target(powerpc64-linux-gnu)
add_target(powerpc64le-linux-gnu)
add_target(sparc64-linux-gnu)
add_target(s390x-linux-gnu)
add_target(m68k-linux-gnu)
if(MOLD_ENABLE_QEMU_TESTS_RV32)
add_target(riscv32-linux-gnu)

View File

@ -1,6 +1,8 @@
#!/bin/bash
. $(dirname $0)/common.inc
[ $MACHINE = m68k ] && skip
command -v llvm-readelf >& /dev/null || skip
cat <<EOF | $CC -o $t/a.o -fPIC -c -xc -