Remove incomplete MIPS support

I made a fair amount of effort to try to support MIPS, but it turned out
that it is much harder than expected. I punted it instead of making
further efforts.

The problem is the MIPS ABI is hostile to the linker in the modern
environment. MIPS object files are still compiled for the small code
model in which GOT entries and data in the small data area are expected
to be accessible with a single machine instruction with a 16-bit
displacement. In other words, .got/.sdata/.sbss are expected to be
smaller than 64 KiB. This might have been a reasonable assumption in the
90s, but it's not suitable for modern applications that can be 1000x
larger than the binaries in the 90s.

MIPS requires the linker to implement tons of workarounds for its legacy
ABI assumptions. Our incomplete MIPS support can build binaries that
pass all our unit tests. However, it needed more effort to support
real-world programs that are larger than our test cases.

At this point, I don't think it is productive to implement workarounds
for the old ABI that is stuck in the 90s. It is honestly annoying to
think about workarounds for the code that is intentionally compiled to
be hostile to the linker. The situation is unfortunate, but if the
Open Source community is still serious about MIPS, they should improve
the ABI and the compiler instead of asking us to implement the legacy
ABI.

Closes https://github.com/rui314/mold/issues/1040
This commit is contained in:
Rui Ueyama 2023-09-03 21:47:34 +09:00
parent c03400303f
commit db5fa8a8cd
25 changed files with 20 additions and 1149 deletions

View File

@ -51,9 +51,9 @@ jobs:
# Install cross toolchains
dpkg --add-architecture i386
./install-build-deps.sh update
apt-get install -y sudo qemu-user gdb zstd dwarfdump xz-utils {gcc,g++}-10-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha,mips,mipsel}-linux-gnu {gcc,g++}-10-arm-linux-gnueabihf {gcc,g++}-10-{mips64,mips64el}-linux-gnuabi64
apt-get install -y sudo qemu-user gdb zstd dwarfdump xz-utils {gcc,g++}-10-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu {gcc,g++}-10-arm-linux-gnueabihf
for i in {i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha,mips,mipsel}-linux-gnu arm-linux-gnueabihf {mips64,mips64el}-linux-gnuabi64; do
for i in {i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu arm-linux-gnueabihf; do
ln -sf /usr/bin/$i-gcc-10 /usr/bin/$i-gcc
ln -sf /usr/bin/$i-g++-10 /usr/bin/$i-g++
done

View File

@ -295,11 +295,10 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
# 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 MIPS64LE MIPS64BE LOONGARCH32 LOONGARCH64)
S390X SPARC64 M68K SH4 ALPHA LOONGARCH32 LOONGARCH64)
list(APPEND MOLD_ELF_TEMPLATE_FILES
elf/arch-loongarch.cc
elf/arch-mips64.cc
elf/arch-riscv.cc
elf/cmdline.cc
elf/dwarf.cc
@ -376,7 +375,6 @@ target_sources(mold PRIVATE
elf/arch-arm64.cc
elf/arch-i386.cc
elf/arch-m68k.cc
elf/arch-mips64.cc
elf/arch-ppc32.cc
elf/arch-ppc64v1.cc
elf/arch-ppc64v2.cc

View File

@ -1,662 +0,0 @@
// MIPS is a RISC ISA developed in the '80s. The processor was once fairly
// popular; for examples, Silicon Graphics workstations and Nintendo 64
// game consoles are based on the processor. Even though it's no longer a
// popular choice when creating a new system, there are still many uses of
// the ISA especially in the network router segment.
//
// The MIPS psABIs are in a sad state due to the lack of ownership of the
// ABI. The last major Unix vendor in the MIPS market was Silicon
// Graphics, which effectively ceased its MIPS-based Unix workstation
// business in the '90s. Even at the time the MIPS ABIs looked peculiar.
// After that, various small vendors used MIPS to create appliances and
// notably routers, but no one tried to modernize or improve the ABIs. As
// a result, the MIPS ABIs left as probably the most diverged ABI compared
// to the other psABIs.
//
// Specifically, the MIPS ABIs has the following issues:
//
// 1. Since the ISA does not support PC-relative addressing, each function
// first materializes the address of GOT + 0x7ff0 in the GP register
// and access GOT entries relative to the GP's value. This GP-relative
// access is usually done with a single load instruction with a 16-bit
// offset. That means only GP ± 32 KiB is addressable. If GOT is larger
// than that, the linker is expected to create a GOT section for each
// input file and associate a different GP value for each GOT. This
// method is called "multi-GOT". Multi-GOT is not necessary for other
// ABIs because other processors either simply support PC-relative
// addressing or use two instructions to access GOT entries.
//
// 2. The MIPS ABIs require .dynsym entries to be sorted in a very
// specific manner to represent some dynamic relocations implicitly
// rather than explicitly in the .rela.dyn section. This feature is
// called "Quickstart" in the MIPS documentation.
//
// 3. Unlike other psABIs, a MIPS relocation record can have up to three
// types -- that is, each record has not only r_type but also r_type2
// and r_type3. A relocated value is computed as the combination of all
// the relocation types.
//
// In our MIPS support, we prioritize simplicity of implementation over
// marginal runtime efficiency. Specifically, we made the following
// decisions for simplification:
//
// 1. We do not sort .dynsym entries. Quickstart still kicks in at the
// load-time (there's no way to tell the loader to disable Quickstart),
// and the loader writes resolved addresses to our placeholder section
// `.mips_quickstart`. We just ignore these relocated values.
//
// 2. Instead of supporting arbitrary combinations of relocation types, we
// support only a limited set of them. This works because, in practice,
// the compiler emits only a limted set of relocation types.
#if MOLD_MIPS64LE || MOLD_MIPS64BE
#include "mold.h"
namespace mold::elf {
using E = MOLD_TARGET;
static constexpr i64 BIAS = 0x8000;
// We don't support lazy symbol resolution for MIPS. All dynamic symbols
// are resolved eagerly on process startup.
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_eh_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_MIPS_64:
// We relocate R_MIPS_64 in .eh_frame as a relative relocation.
// See the comment for mips_rewrite_cie() below.
*(U64<E> *)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);
// 0x7ff0 is added to maximize the GP-relative addressable range
// for load/store instructions with a signed 16-bit displacement.
u64 GP = file.extra.got->shdr.sh_addr + 0x7ff0;
u64 GP0 = file.extra.gp0;
MipsGotSection<E> *got = file.extra.got;
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 write_hi16 = [&](u64 val) {
check(val, -(1LL << 31), 1LL << 31);
*(U32<E> *)loc |= ((val + BIAS) >> 16) & 0xffff;
};
auto write_lo16 = [&](u64 val) {
check(val, -(1 << 15), 1 << 15);
*(U32<E> *)loc |= val & 0xffff;
};
auto write_lo16_nc = [&](u64 val) {
*(U32<E> *)loc |= val & 0xffff;
};
u64 S = sym.get_addr(ctx);
u64 A = rel.r_addend;
u64 P = get_addr() + rel.r_offset;
switch (rel.r_type) {
case R_MIPS_64:
apply_toc_rel(ctx, sym, rel, loc, S, A, P, &dynrel);
break;
case R_MIPS_GPREL16 | (R_MIPS_SUB << 8) | (R_MIPS_HI16 << 16): {
u64 val = sym.is_local(ctx) ? (S + A + GP0 - GP) : (S + A - GP);
write_hi16(-val);
break;
}
case R_MIPS_GPREL16 | (R_MIPS_SUB << 8) | (R_MIPS_LO16 << 16): {
u64 val = sym.is_local(ctx) ? (S + A + GP0 - GP) : (S + A - GP);
write_lo16_nc(-val);
break;
}
case R_MIPS_GPREL32 | (R_MIPS_64 << 8):
*(U64<E> *)loc = S + A + GP0 - GP;
break;
case R_MIPS_GOT_DISP:
case R_MIPS_CALL16:
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_LO16:
write_lo16(got->get_got_addr(ctx, sym, A) - GP);
break;
case R_MIPS_CALL_HI16:
case R_MIPS_GOT_HI16:
write_hi16(got->get_got_addr(ctx, sym, A) - GP);
break;
case R_MIPS_GOT_PAGE:
write_lo16(got->get_gotpage_addr(ctx, sym, A) - GP);
break;
case R_MIPS_GOT_OFST:
write_lo16(0);
break;
case R_MIPS_JALR:
break;
case R_MIPS_TLS_TPREL_HI16:
write_hi16(S + A - ctx.tp_addr);
break;
case R_MIPS_TLS_TPREL_LO16:
write_lo16_nc(S + A - ctx.tp_addr);
break;
case R_MIPS_TLS_GOTTPREL:
write_lo16(got->get_gottp_addr(ctx, sym) - GP);
break;
case R_MIPS_TLS_DTPREL_HI16:
write_hi16(S + A - ctx.dtp_addr);
break;
case R_MIPS_TLS_DTPREL_LO16:
write_lo16_nc(S + A - ctx.dtp_addr);
break;
case R_MIPS_TLS_GD:
write_lo16(got->get_tlsgd_addr(ctx, sym) - GP);
break;
case R_MIPS_TLS_LDM:
write_lo16(got->get_tlsld_addr(ctx) - GP);
break;
default:
unreachable();
}
}
}
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 || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
SectionFragment<E> *frag;
i64 frag_addend;
std::tie(frag, frag_addend) = get_fragment(ctx, rel);
u64 S = frag ? frag->get_addr(ctx) : sym.get_addr(ctx);
u64 A = frag ? frag_addend : get_addend(loc, rel);
switch (rel.r_type) {
case R_MIPS_64:
if (std::optional<u64> val = get_tombstone(sym, frag))
*(U64<E> *)loc = *val;
else
*(U64<E> *)loc = S + A;
break;
case R_MIPS_32:
*(U32<E> *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
}
}
}
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);
MipsGotSection<E> *got = file.extra.got;
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_NONE || record_undef_error(ctx, rel))
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
switch (rel.r_type) {
case R_MIPS_64:
scan_toc_rel(ctx, sym, rel);
break;
case R_MIPS_GOT_DISP:
case R_MIPS_CALL16:
case R_MIPS_CALL_HI16:
case R_MIPS_CALL_LO16:
case R_MIPS_GOT_HI16:
case R_MIPS_GOT_LO16:
got->got_syms.push_back({&sym, rel.r_addend});
break;
case R_MIPS_GOT_PAGE:
case R_MIPS_GOT_OFST:
got->gotpage_syms.push_back({&sym, rel.r_addend});
break;
case R_MIPS_TLS_GOTTPREL:
assert(rel.r_addend == 0);
got->gottp_syms.push_back(&sym);
break;
case R_MIPS_TLS_TPREL_HI16:
case R_MIPS_TLS_TPREL_LO16:
check_tlsle(ctx, sym, rel);
break;
case R_MIPS_TLS_GD:
assert(rel.r_addend == 0);
got->tlsgd_syms.push_back(&sym);
break;
case R_MIPS_TLS_LDM:
assert(rel.r_addend == 0);
got->has_tlsld = true;
break;
case R_MIPS_GPREL16 | (R_MIPS_SUB << 8) | (R_MIPS_HI16 << 16):
case R_MIPS_GPREL16 | (R_MIPS_SUB << 8) | (R_MIPS_LO16 << 16):
case R_MIPS_GPREL32 | (R_MIPS_64 << 8):
case R_MIPS_JALR:
case R_MIPS_TLS_DTPREL_HI16:
case R_MIPS_TLS_DTPREL_LO16:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
template <typename E>
bool MipsGotSection<E>::SymbolAddend::operator<(const SymbolAddend &other) const {
return std::tuple(sym->file->priority, sym->sym_idx, addend) <
std::tuple(other.sym->file->priority, other.sym->sym_idx, other.addend);
};
static bool compare(const Symbol<E> *a, const Symbol<E> *b) {
return std::tuple(a->file->priority, a->sym_idx) <
std::tuple(b->file->priority, b->sym_idx);
};
template <typename E>
u64 MipsGotSection<E>::SymbolAddend::get_addr(Context<E> &ctx, i64 flags) const {
return sym->get_addr(ctx, flags) + addend;
}
static inline i64 get_gotpage_offset(const MipsGotSection<E> &got) {
return got.got_syms.size();
}
static inline i64 get_tlsgd_offset(const MipsGotSection<E> &got) {
return get_gotpage_offset(got) + got.gotpage_syms.size();
}
static inline i64 get_gottp_offset(const MipsGotSection<E> &got) {
return get_tlsgd_offset(got) + got.tlsgd_syms.size() * 2;
}
static inline i64 get_tlsld_offset(const MipsGotSection<E> &got) {
return get_gottp_offset(got) + got.gottp_syms.size();
}
static inline i64 get_num_got_entries(const MipsGotSection<E> &got) {
return get_tlsld_offset(got) + got.has_tlsld * 2;
}
template <typename E>
u64 MipsGotSection<E>::get_got_addr(Context<E> &ctx, Symbol<E> &sym,
i64 addend) const {
auto it = std::lower_bound(got_syms.begin(), got_syms.end(),
SymbolAddend{&sym, addend});
i64 idx = it - got_syms.begin();
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_gotpage_addr(Context<E> &ctx, Symbol<E> &sym,
i64 addend) const {
auto it = std::lower_bound(gotpage_syms.begin(), gotpage_syms.end(),
SymbolAddend{&sym, addend});
i64 idx = get_gotpage_offset(*this) + (it - gotpage_syms.begin());
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_tlsgd_addr(Context<E> &ctx, Symbol<E> &sym) const {
auto it = std::lower_bound(tlsgd_syms.begin(), tlsgd_syms.end(), &sym, compare);
i64 idx = get_tlsgd_offset(*this) + (it - tlsgd_syms.begin()) * 2;
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_gottp_addr(Context<E> &ctx, Symbol<E> &sym) const {
auto it = std::lower_bound(gottp_syms.begin(), gottp_syms.end(), &sym, compare);
i64 idx = get_gottp_offset(*this) + (it - gottp_syms.begin());
return this->shdr.sh_addr + idx * sizeof(Word<E>);
}
template <typename E>
u64 MipsGotSection<E>::get_tlsld_addr(Context<E> &ctx) const {
assert(has_tlsld);
return this->shdr.sh_addr + get_tlsld_offset(*this) * sizeof(Word<E>);
}
namespace {
struct GotEntry {
u64 val = 0;
i64 r_type = R_NONE;
Symbol<E> *sym = nullptr;
};
}
std::vector<GotEntry>
get_got_entries(Context<E> &ctx, const MipsGotSection<E> &got) {
using SymbolAddend = typename MipsGotSection<E>::SymbolAddend;
std::vector<GotEntry> entries;
auto add = [&](GotEntry ent) { entries.push_back(ent); };
// Create GOT entries for ordinary symbols
for (const SymbolAddend &ent : got.got_syms) {
if (ent.sym->is_imported) {
// If a symbol is imported, let the dynamic linker to resolve it.
add({0, E::R_DYNAMIC, ent.sym});
} else if (ctx.arg.pic && ent.sym->is_relative()) {
// If we know an address at link-time, fill that GOT entry now.
// It may need a base relocation, though.
add({ent.get_addr(ctx, NO_PLT), E::R_RELATIVE});
} else {
add({ent.get_addr(ctx, NO_PLT)});
}
}
// Create GOT entries for GOT_PAGE and GOT_OFST relocs
for (const SymbolAddend &ent : got.gotpage_syms) {
if (ctx.arg.pic && ent.sym->is_relative())
add({ent.get_addr(ctx), E::R_RELATIVE});
else
add({ent.get_addr(ctx)});
}
// Create GOT entries for TLVs.
for (Symbol<E> *sym : got.tlsgd_syms) {
if (sym->is_imported) {
// If a symbol is imported, let the dynamic linker to resolve it.
add({0, E::R_DTPMOD, sym});
add({0, E::R_DTPOFF, sym});
} else if (ctx.arg.shared) {
// If we are creating a shared library, we know the TLV's offset
// within the current TLS block. We don't know the module ID though.
add({0, E::R_DTPMOD});
add({sym->get_addr(ctx) - ctx.dtp_addr});
} else {
// If we are creating an executable, we know both the module ID and
// the offset. Module ID 1 indicates the main executable.
add({1});
add({sym->get_addr(ctx) - ctx.dtp_addr});
}
}
for (Symbol<E> *sym : got.gottp_syms) {
if (sym->is_imported) {
// If we know nothing about the symbol, let the dynamic linker
// to fill the GOT entry.
add({0, E::R_TPOFF, sym});
} else if (ctx.arg.shared) {
// If we know the offset within the current thread vector,
// let the dynamic linker to adjust it.
add({sym->get_addr(ctx) - ctx.tls_begin, E::R_TPOFF});
} else {
// Otherwise, we know the offset from the thread pointer (TP) at
// link-time, so we can fill the GOT entry directly.
add({sym->get_addr(ctx) - ctx.tp_addr});
}
}
if (got.has_tlsld) {
if (ctx.arg.shared)
add({0, E::R_DTPMOD});
else
add({1}); // 1 means the main executable
add({0});
}
return entries;
}
template <typename E>
void MipsGotSection<E>::update_shdr(Context<E> &ctx) {
// Finalize got_syms
sort(got_syms);
remove_duplicates(got_syms);
// Finalize gotpage_syms
sort(gotpage_syms);
remove_duplicates(gotpage_syms);
// Finalize tlsgd_syms
sort(tlsgd_syms, compare);
remove_duplicates(tlsgd_syms);
// Finalize gottp_syms
sort(gottp_syms, compare);
remove_duplicates(gottp_syms);
this->shdr.sh_size = get_num_got_entries(*this) * sizeof(Word<E>);
}
template <typename E>
i64 MipsGotSection<E>::get_reldyn_size(Context<E> &ctx) const {
i64 n = 0;
for (GotEntry &ent : get_got_entries(ctx, *this))
if (ent.r_type != R_NONE)
n++;
return n;
}
template <typename E>
void MipsGotSection<E>::copy_buf(Context<E> &ctx) {
U64<E> *buf = (U64<E> *)(ctx.buf + this->shdr.sh_offset);
memset(buf, 0, this->shdr.sh_size);
ElfRel<E> *dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
this->reldyn_offset);
for (i64 i = 0; GotEntry &ent : get_got_entries(ctx, *this)) {
if (ent.r_type != R_NONE)
*dynrel++ = ElfRel<E>(this->shdr.sh_addr + i * sizeof(Word<E>),
ent.r_type,
ent.sym ? ent.sym->get_dynsym_idx(ctx) : 0,
ent.val);
buf[i++] = ent.val;
}
}
template <>
void MipsQuickstartSection<E>::update_shdr(Context<E> &ctx) {
this->shdr.sh_size = (NUM_RESERVED + ctx.dynsym->symbols.size()) *
sizeof(Word<E>);
}
template <>
void MipsQuickstartSection<E>::copy_buf(Context<E> &ctx) {
U64<E> *buf = (U64<E> *)(ctx.buf + this->shdr.sh_offset);
memset(buf, 0, this->shdr.sh_size);
// It is not clear how the runtime uses it, but all MIPS binaries
// have this value in GOT[1].
buf[1] = E::is_64 ? 0x8000'0000'0000'0000 : 0x8000'0000;
for (i64 i = 0; i < ctx.dynsym->symbols.size(); i++)
if (Symbol<E> *sym = ctx.dynsym->symbols[i])
if (!sym->file->is_dso && !sym->esym().is_undef())
buf[i + NUM_RESERVED] = sym->get_addr(ctx, NO_PLT);
}
template <>
void MipsABIFlagsSection<E>::update_shdr(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs) {
if (file->extra.abi_flags) {
contents = file->extra.abi_flags->contents;
break;
}
}
this->shdr.sh_size = contents.size();
}
// .MIPS.abiflags section contains ABI info such as ISA level.
// We need to merge input .MIPS.abiflags sections into a single
// .MIPS.abiflags section. But for now, we just pick the first one.
template <>
void MipsABIFlagsSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
memcpy(buf, contents.data(), contents.size());
}
// We merge consective .mips_got sections to reduce the total number of
// .mips_got entries. Note that each .mips_got should be equal or smaller
// than 64 KiB so that all of its entries are within its GP ± 32 KiB.
template <>
void mips_merge_got_sections(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
file->extra.got->update_shdr(ctx);
for (i64 i = 0; i < ctx.objs.size(); i++) {
MipsGotSection<E> &got = *ctx.objs[i]->extra.got;
for (i++; i < ctx.objs.size(); i++) {
MipsGotSection<E> &got2 = *ctx.objs[i]->extra.got;
if (got.shdr.sh_size + got2.shdr.sh_size >= 65536)
break;
append(got.got_syms, got2.got_syms);
append(got.gotpage_syms, got2.gotpage_syms);
append(got.tlsgd_syms, got2.tlsgd_syms);
append(got.gottp_syms, got2.gottp_syms);
got.has_tlsld = got.has_tlsld || got2.has_tlsld;
got2.got_syms.clear();
got2.gotpage_syms.clear();
got2.tlsgd_syms.clear();
got2.gottp_syms.clear();
got2.has_tlsld = false;
got.update_shdr(ctx);
ctx.objs[i]->extra.got = &got;
}
}
}
// Real MIPS e_flags computation is much more complicated.
// For now, we just copy the first object's e_flags to the output.
template <>
u64 get_eflags(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
if (file != ctx.internal_obj)
return file->get_ehdr().e_flags;
return 0;
}
// MIPS .eh_frame contains absolute addresses (i.e. R_MIPS_64 relocations)
// even if compiled with -fPIC. Instead of emitting base relocations, we
// rewrite CIEs so that we can write relative addresse instead of absolute
// ones to .eh_frame.
template <>
void mips_rewrite_cie(Context<E> &ctx, u8 *buf, CieRecord<E> &cie) {
u8 *aug = buf + 9; // Skip Length, CIE ID and Version fields
if (*aug != 'z')
return;
aug++;
// Skip Augmentation String
u8 *p = aug + strlen((char *)aug) + 1;
read_uleb(&p); // Skip Code Alignment Factor
read_uleb(&p); // Skip Data Alignment Factor
p++; // Skip Return Address Register
read_uleb(&p); // Skip Augmentation Data Length
auto rewrite = [&](u8 *ptr) {
i64 sz;
switch (*ptr & 0xf) {
case DW_EH_PE_absptr:
sz = sizeof(Word<E>);
break;
case DW_EH_PE_udata4:
case DW_EH_PE_sdata4:
sz = 4;
break;
case DW_EH_PE_udata8:
case DW_EH_PE_sdata8:
sz = 8;
break;
default:
Fatal(ctx) << cie.input_section << ": unknown pointer size";
}
if ((*ptr & 0x70) == DW_EH_PE_absptr) {
if (sz == 4)
*ptr = (*ptr & 0x80) | DW_EH_PE_pcrel | DW_EH_PE_sdata4;
else
*ptr = (*ptr & 0x80) | DW_EH_PE_pcrel | DW_EH_PE_sdata8;
}
return sz;
};
// Now p points to the beginning of Augmentation Data
for (; *aug; aug++) {
switch (*aug) {
case 'L':
case 'R':
rewrite(p);
p++;
break;
case 'P':
p += rewrite(p) + 1;
break;
case 'S':
case 'B':
break;
default:
Error(ctx) << cie.input_section
<< ": unknown argumentation string in CIE: '"
<< (char)*aug << "'";
}
}
}
template class MipsGotSection<E>;
} // namespace mold::elf
#endif

View File

@ -525,16 +525,8 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
<< " elf64briscv\n elf32lriscv\n elf32briscv\n"
<< " elf32ppc\n elf64ppc\n elf64lppc\n elf64_s390\n"
<< " elf64_sparc\n m68kelf\n shlelf_linux\n"
<< " elf64alpha\n elf64ltsmip\n elf64btsmip\n"
<< " elf64loongarch\n elf32loongarch";
<< " elf64alpha\n elf64loongarch\n elf32loongarch";
version_shown = true;
} else if (read_flag("mips32") || read_flag("mips32r2") ||
read_flag("mips32r3") || read_flag("mips32r4") ||
read_flag("mips32r5") || read_flag("mips32r6") ||
read_flag("mips64") || read_flag("mips64r2") ||
read_flag("mips64r3") || read_flag("mips64r4") ||
read_flag("mips64r5") || read_flag("mips64r6")) {
// Ignore useless MIPS-specific flags
} else if (read_arg("m")) {
if (arg == "elf_x86_64") {
ctx.arg.emulation = X86_64::target_name;
@ -568,10 +560,6 @@ 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 == "elf64ltsmip") {
ctx.arg.emulation = MIPS64LE::target_name;
} else if (arg == "elf64btsmip") {
ctx.arg.emulation = MIPS64BE::target_name;
} else if (arg == "elf64loongarch") {
ctx.arg.emulation = LOONGARCH64::target_name;
} else if (arg == "elf32loongarch") {
@ -1217,7 +1205,7 @@ std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
Fatal(ctx) << "-auxiliary may not be used without -shared";
}
if constexpr (!E::is_rela || is_mips<E>)
if constexpr (!E::is_rela)
if (!ctx.arg.apply_dynamic_relocs)
Fatal(ctx) << "--no-apply-dynamic-relocs may not be used on "
<< E::target_name;

View File

@ -926,94 +926,6 @@ std::string rel_to_string<ALPHA>(u32 r_type) {
return unknown_type(r_type);
}
template <>
std::string rel_to_string<MIPS64LE>(u32 r_type) {
switch (r_type) {
CASE(R_MIPS_NONE);
CASE(R_MIPS_16);
CASE(R_MIPS_32);
CASE(R_MIPS_REL32);
CASE(R_MIPS_26);
CASE(R_MIPS_HI16);
CASE(R_MIPS_LO16);
CASE(R_MIPS_GPREL16);
CASE(R_MIPS_LITERAL);
CASE(R_MIPS_GOT16);
CASE(R_MIPS_PC16);
CASE(R_MIPS_CALL16);
CASE(R_MIPS_GPREL32);
CASE(R_MIPS_UNUSED1);
CASE(R_MIPS_UNUSED2);
CASE(R_MIPS_UNUSED3);
CASE(R_MIPS_SHIFT5);
CASE(R_MIPS_SHIFT6);
CASE(R_MIPS_64);
CASE(R_MIPS_GOT_DISP);
CASE(R_MIPS_GOT_PAGE);
CASE(R_MIPS_GOT_OFST);
CASE(R_MIPS_GOT_HI16);
CASE(R_MIPS_GOT_LO16);
CASE(R_MIPS_SUB);
CASE(R_MIPS_INSERT_A);
CASE(R_MIPS_INSERT_B);
CASE(R_MIPS_DELETE);
CASE(R_MIPS_HIGHER);
CASE(R_MIPS_HIGHEST);
CASE(R_MIPS_CALL_HI16);
CASE(R_MIPS_CALL_LO16);
CASE(R_MIPS_SCN_DISP);
CASE(R_MIPS_REL16);
CASE(R_MIPS_ADD_IMMEDIATE);
CASE(R_MIPS_PJUMP);
CASE(R_MIPS_RELGOT);
CASE(R_MIPS_JALR);
CASE(R_MIPS_TLS_DTPMOD32);
CASE(R_MIPS_TLS_DTPREL32);
CASE(R_MIPS_TLS_DTPMOD64);
CASE(R_MIPS_TLS_DTPREL64);
CASE(R_MIPS_TLS_GD);
CASE(R_MIPS_TLS_LDM);
CASE(R_MIPS_TLS_DTPREL_HI16);
CASE(R_MIPS_TLS_DTPREL_LO16);
CASE(R_MIPS_TLS_GOTTPREL);
CASE(R_MIPS_TLS_TPREL32);
CASE(R_MIPS_TLS_TPREL64);
CASE(R_MIPS_TLS_TPREL_HI16);
CASE(R_MIPS_TLS_TPREL_LO16);
CASE(R_MIPS_GLOB_DAT);
CASE(R_MIPS_PC21_S2);
CASE(R_MIPS_PC26_S2);
CASE(R_MIPS_PC18_S3);
CASE(R_MIPS_PC19_S2);
CASE(R_MIPS_PCHI16);
CASE(R_MIPS_PCLO16);
CASE(R_MIPS16_26);
CASE(R_MIPS16_GPREL);
CASE(R_MIPS16_GOT16);
CASE(R_MIPS16_CALL16);
CASE(R_MIPS16_HI16);
CASE(R_MIPS16_LO16);
CASE(R_MIPS16_TLS_GD);
CASE(R_MIPS16_TLS_LDM);
CASE(R_MIPS16_TLS_DTPREL_HI16);
CASE(R_MIPS16_TLS_DTPREL_LO16);
CASE(R_MIPS16_TLS_GOTTPREL);
CASE(R_MIPS16_TLS_TPREL_HI16);
CASE(R_MIPS16_TLS_TPREL_LO16);
CASE(R_MIPS_COPY);
CASE(R_MIPS_JUMP_SLOT);
CASE(R_MIPS_NUM);
CASE(R_MIPS_PC32);
CASE(R_MIPS_EH);
}
return unknown_type(r_type);
}
template <>
std::string rel_to_string<MIPS64BE>(u32 r_type) {
return rel_to_string<MIPS64LE>(r_type);
}
template <>
std::string rel_to_string<LOONGARCH64>(u32 r_type) {
switch (r_type) {

161
elf/elf.h
View File

@ -25,8 +25,6 @@ struct SPARC64;
struct M68K;
struct SH4;
struct ALPHA;
struct MIPS64LE;
struct MIPS64BE;
struct LOONGARCH64;
struct LOONGARCH32;
@ -92,7 +90,6 @@ enum : u32 {
SHT_ARM_EXIDX = 0x70000001,
SHT_ARM_ATTRIBUTES = 0x70000003,
SHT_RISCV_ATTRIBUTES = 0x70000003,
SHT_MIPS_ABIFLAGS = 0x7000002a,
};
enum : u32 {
@ -107,7 +104,6 @@ enum : u32 {
SHF_TLS = 0x400,
SHF_COMPRESSED = 0x800,
SHF_GNU_RETAIN = 0x200000,
SHF_MIPS_GPREL = 0x10000000,
SHF_EXCLUDE = 0x80000000,
};
@ -192,7 +188,6 @@ enum : u32 {
PT_OPENBSD_RANDOMIZE = 0x65a3dbe6,
PT_ARM_EXIDX = 0x70000001,
PT_RISCV_ATTRIBUTES = 0x70000003,
PT_MIPS_ABIFLAGS = 0x70000003,
};
enum : u32 {
@ -227,7 +222,6 @@ enum : u32 {
EM_NONE = 0,
EM_386 = 3,
EM_68K = 4,
EM_MIPS = 8,
EM_PPC = 20,
EM_PPC64 = 21,
EM_S390X = 22,
@ -297,13 +291,6 @@ enum : u32 {
DT_VERNEEDNUM = 0x6fffffff,
DT_PPC_GOT = 0x70000000,
DT_PPC64_GLINK = 0x70000000,
DT_MIPS_RLD_VERSION = 0x70000001,
DT_MIPS_FLAGS = 0x70000005,
DT_MIPS_BASE_ADDRESS = 0x70000006,
DT_MIPS_LOCAL_GOTNO = 0x7000000a,
DT_MIPS_SYMTABNO = 0x70000011,
DT_MIPS_GOTSYM = 0x70000013,
DT_MIPS_OPTIONS = 0x70000029,
DT_AUXILIARY = 0x7ffffffd,
DT_FILTER = 0x7fffffff,
};
@ -1261,85 +1248,6 @@ enum : u32 {
R_ALPHA_TPREL16 = 41,
};
enum : u32 {
R_MIPS_NONE = 0,
R_MIPS_16 = 1,
R_MIPS_32 = 2,
R_MIPS_REL32 = 3,
R_MIPS_26 = 4,
R_MIPS_HI16 = 5,
R_MIPS_LO16 = 6,
R_MIPS_GPREL16 = 7,
R_MIPS_LITERAL = 8,
R_MIPS_GOT16 = 9,
R_MIPS_PC16 = 10,
R_MIPS_CALL16 = 11,
R_MIPS_GPREL32 = 12,
R_MIPS_UNUSED1 = 13,
R_MIPS_UNUSED2 = 14,
R_MIPS_UNUSED3 = 15,
R_MIPS_SHIFT5 = 16,
R_MIPS_SHIFT6 = 17,
R_MIPS_64 = 18,
R_MIPS_GOT_DISP = 19,
R_MIPS_GOT_PAGE = 20,
R_MIPS_GOT_OFST = 21,
R_MIPS_GOT_HI16 = 22,
R_MIPS_GOT_LO16 = 23,
R_MIPS_SUB = 24,
R_MIPS_INSERT_A = 25,
R_MIPS_INSERT_B = 26,
R_MIPS_DELETE = 27,
R_MIPS_HIGHER = 28,
R_MIPS_HIGHEST = 29,
R_MIPS_CALL_HI16 = 30,
R_MIPS_CALL_LO16 = 31,
R_MIPS_SCN_DISP = 32,
R_MIPS_REL16 = 33,
R_MIPS_ADD_IMMEDIATE = 34,
R_MIPS_PJUMP = 35,
R_MIPS_RELGOT = 36,
R_MIPS_JALR = 37,
R_MIPS_TLS_DTPMOD32 = 38,
R_MIPS_TLS_DTPREL32 = 39,
R_MIPS_TLS_DTPMOD64 = 40,
R_MIPS_TLS_DTPREL64 = 41,
R_MIPS_TLS_GD = 42,
R_MIPS_TLS_LDM = 43,
R_MIPS_TLS_DTPREL_HI16 = 44,
R_MIPS_TLS_DTPREL_LO16 = 45,
R_MIPS_TLS_GOTTPREL = 46,
R_MIPS_TLS_TPREL32 = 47,
R_MIPS_TLS_TPREL64 = 48,
R_MIPS_TLS_TPREL_HI16 = 49,
R_MIPS_TLS_TPREL_LO16 = 50,
R_MIPS_GLOB_DAT = 51,
R_MIPS_PC21_S2 = 60,
R_MIPS_PC26_S2 = 61,
R_MIPS_PC18_S3 = 62,
R_MIPS_PC19_S2 = 63,
R_MIPS_PCHI16 = 64,
R_MIPS_PCLO16 = 65,
R_MIPS16_26 = 100,
R_MIPS16_GPREL = 101,
R_MIPS16_GOT16 = 102,
R_MIPS16_CALL16 = 103,
R_MIPS16_HI16 = 104,
R_MIPS16_LO16 = 105,
R_MIPS16_TLS_GD = 106,
R_MIPS16_TLS_LDM = 107,
R_MIPS16_TLS_DTPREL_HI16 = 108,
R_MIPS16_TLS_DTPREL_LO16 = 109,
R_MIPS16_TLS_GOTTPREL = 110,
R_MIPS16_TLS_TPREL_HI16 = 111,
R_MIPS16_TLS_TPREL_LO16 = 112,
R_MIPS_COPY = 126,
R_MIPS_JUMP_SLOT = 127,
R_MIPS_NUM = 218,
R_MIPS_PC32 = 248,
R_MIPS_EH = 249,
};
enum : u32 {
R_LARCH_NONE = 0,
R_LARCH_32 = 1,
@ -1865,41 +1773,6 @@ struct ElfRel<SPARC64> {
ib64 r_addend;
};
template <>
struct ElfRel<MIPS64LE> {
ElfRel() = default;
ElfRel(u64 offset, u32 type, u32 sym, i64 addend)
: r_offset(offset), r_sym(sym), r_type(type), r_addend(addend) {}
// In the little-endian MIPS64, r_sym and r_type are reversed, with
// r_type being stored in big-endian order. It's extremely odd though.
ul64 r_offset;
ul32 r_sym;
ub32 r_type;
il64 r_addend;
};
// .MIPS.options section
template <typename E>
struct MipsOptions {
u8 kind;
u8 size;
U16<E> section;
U32<E> info;
};
template <typename E>
struct MipsRegInfo {
U32<E> ri_gprmask;
U32<E> ri_pad;
U32<E> ri_cprmask[4];
U64<E> ri_gp_value;
};
enum : u32 {
ODK_REGINFO = 1,
};
//
// Machine descriptions
//
@ -1924,8 +1797,6 @@ template <typename E> concept is_sparc64 = std::same_as<E, SPARC64>;
template <typename E> concept is_m68k = std::same_as<E, M68K>;
template <typename E> concept is_sh4 = std::same_as<E, SH4>;
template <typename E> concept is_alpha = std::same_as<E, ALPHA>;
template <typename E> concept is_mips64le = std::same_as<E, MIPS64LE>;
template <typename E> concept is_mips64be = std::same_as<E, MIPS64BE>;
template <typename E> concept is_loongarch64 = std::same_as<E, LOONGARCH64>;
template <typename E> concept is_loongarch32 = std::same_as<E, LOONGARCH32>;
@ -1937,8 +1808,6 @@ template <typename E> concept is_riscv = is_rv64<E> || is_rv32<E>;
template <typename E> concept is_ppc64 = is_ppc64v1<E> || is_ppc64v2<E>;
template <typename E> concept is_ppc = is_ppc64<E> || is_ppc32<E>;
template <typename E> concept is_sparc = is_sparc64<E>;
template <typename E> concept is_mips64 = is_mips64le<E> || is_mips64be<E>;
template <typename E> concept is_mips = is_mips64<E>;
template <typename E> concept is_loongarch = is_loongarch64<E> || is_loongarch32<E>;
struct X86_64 {
@ -2277,36 +2146,6 @@ struct ALPHA {
static constexpr u32 R_DTPMOD = R_ALPHA_DTPMOD64;
};
struct MIPS64 {
static constexpr bool is_64 = true;
static constexpr bool is_rela = true;
static constexpr u32 page_size = 4096;
static constexpr u32 e_machine = EM_MIPS;
static constexpr u32 plt_hdr_size = 0;
static constexpr u32 plt_size = 0;
static constexpr u32 pltgot_size = 0;
static constexpr u32 R_COPY = R_MIPS_COPY;
static constexpr u32 R_GLOB_DAT = R_MIPS_GLOB_DAT | (R_MIPS_64 << 8);
static constexpr u32 R_JUMP_SLOT = R_MIPS_JUMP_SLOT;
static constexpr u32 R_ABS = R_MIPS_64;
static constexpr u32 R_DYNAMIC = R_MIPS_REL32 | (R_MIPS_64 << 8);
static constexpr u32 R_RELATIVE = R_MIPS_REL32 | (R_MIPS_64 << 8);
static constexpr u32 R_DTPOFF = R_MIPS_TLS_DTPREL64;
static constexpr u32 R_TPOFF = R_MIPS_TLS_TPREL64;
static constexpr u32 R_DTPMOD = R_MIPS_TLS_DTPMOD64;
};
struct MIPS64LE : MIPS64 {
static constexpr std::string_view target_name = "mips64le";
static constexpr bool is_le = true;
};
struct MIPS64BE : MIPS64 {
static constexpr std::string_view target_name = "mips64be";
static constexpr bool is_le = false;
};
struct LOONGARCH64 {
static constexpr std::string_view target_name = "loongarch64";
static constexpr bool is_64 = true;

View File

@ -195,26 +195,6 @@ static void read_riscv_attributes(Context<E> &ctx, ObjectFile<E> &file,
}
}
template <typename E>
static u64 read_mips_gp0(Context<E> &ctx, InputSection<E> &isec) {
std::string_view data = isec.contents;
while (!data.empty()) {
if (data.size() < sizeof(MipsOptions<E>))
Fatal(ctx) << isec << ": corrupted .MIPS.options section";
MipsOptions<E> *opt = (MipsOptions<E> *)data.data();
if (opt->kind == ODK_REGINFO) {
if (data.size() < sizeof(MipsOptions<E>) + sizeof(MipsRegInfo<E>))
Fatal(ctx) << isec << ": corrupted .MIPS.options section";
MipsRegInfo<E> *info = (MipsRegInfo<E> *)(opt + 1);
return info->ri_gp_value;
}
data = data.substr(opt->size);
}
return 0;
}
template <typename E>
void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// Read sections
@ -294,11 +274,8 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// area in GNU linkers. We ignore that section because silently
// making the stack area executable is too dangerous. Tell our
// users about the difference if that matters.
//
// MIPS object files don't contain .note.GNU-stack for some reason,
// so ignore this error on MIPS.
if (name == ".note.GNU-stack" && !ctx.arg.relocatable) {
if ((shdr.sh_flags & SHF_EXECINSTR) && !is_mips<E>) {
if (shdr.sh_flags & SHF_EXECINSTR) {
if (!ctx.arg.z_execstack && !ctx.arg.z_execstack_if_needed)
Warn(ctx) << *this << ": this file may cause a segmentation"
" fault because it requires an executable stack. See"
@ -356,16 +333,6 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
if (name == ".got2")
extra.got2 = this->sections[i].get();
if constexpr (is_mips<E>) {
if (name == ".MIPS.abiflags") {
extra.abi_flags = std::move(this->sections[i]);
continue;
}
if (name == ".MIPS.options")
extra.gp0 = read_mips_gp0(ctx, *this->sections[i]);
}
// Save debug sections for --gdb-index.
if (ctx.arg.gdb_index) {
InputSection<E> *isec = this->sections[i].get();
@ -1105,11 +1072,10 @@ void ObjectFile<E>::scan_relocations(Context<E> &ctx) {
for (ElfRel<E> &rel : cie.get_rels()) {
Symbol<E> &sym = *this->symbols[rel.r_sym];
if constexpr (!is_mips<E>)
if (ctx.arg.pic && rel.r_type == E::R_ABS)
Error(ctx) << *this << ": relocation " << rel << " in .eh_frame can"
<< " not be used when making a position-independent output;"
<< " recompile with -fPIE or -fPIC";
if (ctx.arg.pic && rel.r_type == E::R_ABS)
Error(ctx) << *this << ": relocation " << rel << " in .eh_frame can"
<< " not be used when making a position-independent output;"
<< " recompile with -fPIE or -fPIC";
if (sym.is_imported) {
if (sym.get_type() != STT_FUNC)

View File

@ -70,10 +70,6 @@ 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_MIPS:
if (is_64)
return is_le ? MIPS64LE::target_name : MIPS64BE::target_name;
return "";
case EM_LOONGARCH:
return is_64 ? LOONGARCH64::target_name : LOONGARCH32::target_name;
default:
@ -376,10 +372,6 @@ 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 == MIPS64LE::target_name)
return elf_main<MIPS64LE>(argc, argv);
if (target == MIPS64BE::target_name)
return elf_main<MIPS64BE>(argc, argv);
if (target == LOONGARCH32::target_name)
return elf_main<LOONGARCH32>(argc, argv);
if (target == LOONGARCH64::target_name)
@ -771,8 +763,6 @@ 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<MIPS64LE>(int, char **);
extern template int elf_main<MIPS64BE>(int, char **);
extern template int elf_main<LOONGARCH32>(int, char **);
extern template int elf_main<LOONGARCH64>(int, char **);

View File

@ -48,7 +48,6 @@ template <typename E> struct CieRecord;
template <typename E> struct Context;
template <typename E> struct FdeRecord;
template <typename E> class RelocSection;
template <typename E> class MipsGotSection;
template <typename E>
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym);
@ -476,7 +475,7 @@ public:
this->shdr.sh_addralign = sizeof(Word<E>);
// We always create a .got so that _GLOBAL_OFFSET_TABLE_ has
// something to point to. s390x/MIPS psABIs define GOT[1] as a
// something to point to. s390x psABI define GOT[1] as a
// reserved slot, so we allocate one more for them.
this->shdr.sh_size = (is_s390x<E> ? 2 : 1) * sizeof(Word<E>);
}
@ -1195,13 +1194,6 @@ struct ObjectFileExtras<PPC32> {
InputSection<PPC32> *got2 = nullptr;
};
template <is_mips E>
struct ObjectFileExtras<E> {
std::unique_ptr<InputSection<E>> abi_flags;
MipsGotSection<E> *got = nullptr;
u64 gp0 = 0;
};
// ObjectFile represents an input .o file.
template <typename E>
class ObjectFile : public InputFile<E> {
@ -1567,89 +1559,6 @@ private:
std::mutex mu;
};
//
// arch-mips64.cc
//
template <typename E>
class MipsQuickstartSection : public Chunk<E> {
public:
MipsQuickstartSection() {
this->name = ".mips_quickstart";
this->is_relro = true;
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
this->shdr.sh_addralign = 8;
}
static constexpr i64 NUM_RESERVED = 2;
void update_shdr(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class MipsGotSection : public Chunk<E> {
public:
MipsGotSection(Context<E> &ctx, const ObjectFile<E> &file) {
this->name = save_string(ctx, ".mips_got." + std::to_string(file.priority));
this->is_relro = true;
this->shdr.sh_type = SHT_PROGBITS;
this->shdr.sh_flags = SHF_ALLOC | SHF_WRITE | SHF_MIPS_GPREL;
this->shdr.sh_addralign = 8;
}
u64 get_got_addr(Context<E> &ctx, Symbol<E> &sym, i64 addend) const;
u64 get_gotpage_addr(Context<E> &ctx, Symbol<E> &sym, i64 addend) const;
u64 get_tlsgd_addr(Context<E> &ctx, Symbol<E> &sym) const;
u64 get_gottp_addr(Context<E> &ctx, Symbol<E> &sym) const;
u64 get_tlsld_addr(Context<E> &ctx) const;
void update_shdr(Context<E> &ctx) override;
i64 get_reldyn_size(Context<E> &ctx) const override;
void copy_buf(Context<E> &ctx) override;
struct SymbolAddend {
bool operator==(const SymbolAddend &) const = default;
bool operator<(const SymbolAddend &) const;
u64 get_addr(Context<E> &ctx, i64 flags = 0) const;
Symbol<E> *sym;
i64 addend;
};
std::vector<SymbolAddend> got_syms;
std::vector<SymbolAddend> gotpage_syms;
std::vector<Symbol<E> *> tlsgd_syms;
std::vector<Symbol<E> *> gottp_syms;
bool has_tlsld = false;
};
template <typename E>
class MipsABIFlagsSection : public Chunk<E> {
public:
MipsABIFlagsSection() {
this->name = ".MIPS.abiflags";
this->shdr.sh_type = SHT_MIPS_ABIFLAGS;
this->shdr.sh_flags = SHF_ALLOC;
this->shdr.sh_addralign = 8;
}
std::string_view contents;
void update_shdr(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
};
template <is_mips E>
u64 get_eflags(Context<E> &ctx);
template <is_mips E>
void mips_merge_got_sections(Context<E> &ctx);
template <is_mips E>
void mips_rewrite_cie(Context<E> &ctx, u8 *buf, CieRecord<E> &cie);
//
// main.cc
//
@ -1739,12 +1648,6 @@ struct ContextExtras<ALPHA> {
AlphaGotSection *got = nullptr;
};
template <is_mips E>
struct ContextExtras<E> {
MipsQuickstartSection<E> *quickstart = nullptr;
MipsABIFlagsSection<E> *abi_flags = 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
@ -1787,7 +1690,7 @@ struct Context {
bool fork = true;
bool gc_sections = false;
bool gdb_index = false;
bool hash_style_gnu = !is_mips<E>;
bool hash_style_gnu = true;
bool hash_style_sysv = true;
bool icf = false;
bool icf_all = false;
@ -1854,7 +1757,7 @@ struct Context {
std::string dependency_file;
std::string directory;
std::string dynamic_linker;
std::string entry = is_mips<E> ? "__start" : "_start";
std::string entry = "_start";
std::string fini = "_fini";
std::string init = "_init";
std::string output = "a.out";
@ -2015,7 +1918,6 @@ struct Context {
Symbol<E> *_edata = nullptr;
Symbol<E> *_end = nullptr;
Symbol<E> *_etext = nullptr;
Symbol<E> *_gp = nullptr;
Symbol<E> *edata = nullptr;
Symbol<E> *end = nullptr;
Symbol<E> *etext = nullptr;

View File

@ -301,11 +301,6 @@ static std::vector<ElfPhdr<E>> create_phdr(Context<E> &ctx) {
if (ctx.extra.riscv_attributes->shdr.sh_size)
define(PT_RISCV_ATTRIBUTES, PF_R, 1, ctx.extra.riscv_attributes);
// Create a PT_MIPS_ABIFLAGS
if constexpr (is_mips<E>)
if (ctx.extra.abi_flags->shdr.sh_size)
define(PT_MIPS_ABIFLAGS, PF_R, 8, ctx.extra.abi_flags);
// Create a PT_OPENBSD_RANDOMIZE
for (Chunk<E> *chunk : ctx.chunks)
if (chunk->name == ".openbsd.randomdata")
@ -686,8 +681,6 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
} else if constexpr (is_ppc32<E>) {
if (ctx.gotplt->shdr.sh_size)
define(DT_PLTGOT, ctx.gotplt->shdr.sh_addr + GotPltSection<E>::HDR_SIZE);
} else if constexpr (is_mips<E>) {
define(DT_PLTGOT, ctx.extra.quickstart->shdr.sh_addr);
} else {
if (ctx.gotplt->shdr.sh_size)
define(DT_PLTGOT, ctx.gotplt->shdr.sh_addr);
@ -778,18 +771,7 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
if (ctx.arg.z_interpose)
flags1 |= DF_1_INTERPOSE;
auto has_gottp_syms = [&] {
if constexpr (is_mips<E>) {
for (ObjectFile<E> *file : ctx.objs)
if (!file->extra.got->gottp_syms.empty())
return true;
return false;
} else {
return !ctx.got->gottp_syms.empty();
}
};
if (has_gottp_syms())
if (!ctx.got->gottp_syms.empty())
flags |= DF_STATIC_TLS;
if (ctx.has_textrel)
flags |= DF_TEXTREL;
@ -809,16 +791,6 @@ static std::vector<Word<E>> create_dynamic_section(Context<E> &ctx) {
define(DT_PPC64_GLINK, ctx.plt->shdr.sh_addr + E::plt_hdr_size - 32);
}
if constexpr (is_mips<E>) {
define(DT_MIPS_RLD_VERSION, 1);
define(DT_MIPS_FLAGS, 0);
define(DT_MIPS_BASE_ADDRESS, ctx.arg.image_base);
define(DT_MIPS_LOCAL_GOTNO, 2);
define(DT_MIPS_SYMTABNO, ctx.dynsym->symbols.size());
define(DT_MIPS_GOTSYM, 0);
define(DT_MIPS_OPTIONS, 0);
}
// GDB needs a DT_DEBUG entry in an executable to store a word-size
// data for its own purpose. Its content is not important.
if (!ctx.arg.shared)
@ -2130,9 +2102,6 @@ void EhFrameSection<E>::copy_buf(Context<E> &ctx) {
if (ctx.arg.relocatable)
continue;
if constexpr (is_mips<E>)
mips_rewrite_cie(ctx, base + cie.output_offset, cie);
for (const ElfRel<E> &rel : cie.get_rels()) {
assert(rel.r_offset - cie.input_offset < contents.size());

View File

@ -133,14 +133,6 @@ void create_synthetic_sections(Context<E> &ctx) {
if constexpr (is_alpha<E>)
ctx.extra.got = push(new AlphaGotSection);
if constexpr (is_mips<E>) {
ctx.extra.quickstart = push(new MipsQuickstartSection<E>);
ctx.extra.abi_flags = push(new MipsABIFlagsSection<E>);
for (ObjectFile<E> *file : ctx.objs)
file->extra.got = push(new MipsGotSection<E>(ctx, *file));
}
}
template <typename E>
@ -752,9 +744,6 @@ void add_synthetic_symbols(Context<E> &ctx) {
if constexpr (is_ppc32<E>)
ctx.extra._SDA_BASE_ = add("_SDA_BASE_");
if constexpr (is_mips<E>)
ctx._gp = add("_gp");
for (Chunk<E> *chunk : ctx.chunks) {
if (std::optional<std::string> name = get_start_stop_name(ctx, *chunk)) {
add(save_string(ctx, "__start_" + *name));
@ -1394,9 +1383,6 @@ void scan_relocations(Context<E> &ctx) {
if constexpr (is_alpha<E>)
ctx.extra.got->finalize();
if constexpr (is_mips<E>)
mips_merge_got_sections(ctx);
if (ctx.has_textrel && ctx.arg.warn_textrel)
Warn(ctx) << "creating a DT_TEXTREL in an output file";
}
@ -2432,10 +2418,6 @@ void fix_synthetic_symbols(Context<E> &ctx) {
}
}
// MIPS' _gp symbol.
if constexpr (is_mips<E>)
start(ctx._gp, ctx.extra.quickstart, 0x7ff0);
// __start_ and __stop_ symbols
for (Chunk<E> *chunk : sections) {
if (std::optional<std::string> name = get_start_stop_name(ctx, *chunk)) {

View File

@ -160,8 +160,8 @@ u64 get_tp_addr(Context<E> &ctx) {
// of TLV template image when copying TLVs to the TLS block, so we need
// to offset it.
return align_down(phdr->p_vaddr - sizeof(Word<E>) * 2, phdr->p_align);
} else if constexpr (is_ppc<E> || is_m68k<E> || is_mips<E>) {
// On PowerPC, m68k and MIPS, TP is 0x7000 (28 KiB) past the beginning
} else if constexpr (is_ppc<E> || is_m68k<E>) {
// On PowerPC and m68k, TP is 0x7000 (28 KiB) past the beginning
// of the TLV block to maximize the addressable range of 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
@ -186,8 +186,8 @@ u64 get_dtp_addr(Context<E> &ctx) {
if (!phdr)
return 0;
if constexpr (is_ppc<E> || is_m68k<E> || is_mips<E>) {
// On PowerPC, m68k and MIPS, R_DTPOFF is resolved to the address 0x8000
if constexpr (is_ppc<E> || is_m68k<E>) {
// On PowerPC and m68k, R_DTPOFF is resolved to the address 0x8000
// (32 KiB) past the start of the TLS block. The bias maximizes the
// accessible range of load/store instructions with 16-bits signed
// immediates. That is, if the offset were right at the beginning of the

View File

@ -11,7 +11,7 @@ set -x
case "$ID-$VERSION_ID" in
ubuntu-* | pop-* | linuxmint-* | debian-* | raspbian-*)
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha,mips,mipsel}-linux-gnu {gcc,g++}-arm-linux-gnueabihf {gcc,g++}-{mips64,mips64el}-linux-gnuabi64
apt-get install -y qemu-user {gcc,g++}-{i686,aarch64,riscv64,powerpc,powerpc64,powerpc64le,s390x,sparc64,m68k,sh4,alpha}-linux-gnu {gcc,g++}-arm-linux-gnueabihf
;;
*)
echo "Error: don't know anything about build dependencies on $ID-$VERSION_ID"

View File

@ -62,8 +62,6 @@ add_target(sparc64-linux-gnu)
add_target(s390x-linux-gnu)
add_target(sh4-linux-gnu)
add_target(alpha-linux-gnu)
add_target(mips64-linux-gnuabi64)
add_target(mips64el-linux-gnuabi64)
option(MOLD_ENABLE_QEMU_TESTS_RV32 "Enable tests for RV32" OFF)
if(MOLD_ENABLE_QEMU_TESTS_RV32)

View File

@ -6,8 +6,6 @@
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = s390x ] && skip
[ $MACHINE = alpha ] && skip
[ $MACHINE = mips64el ] && skip
[ $MACHINE = mips64 ] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -fPIC -c -o $t/a.o -xassembler -

View File

@ -4,7 +4,6 @@
# arm-linux-gnueabihf-objcopy crashes on x86-64
[ $MACHINE = arm ] && skip
[ $MACHINE = riscv32 ] && skip
[[ $MACHINE = mips* ]] && skip
command -v zstdcat >& /dev/null || skip

View File

@ -4,7 +4,6 @@
[ $MACHINE = ppc64 ] && skip
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = alpha ] && skip
[[ $MACHINE = mips* ]] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -fPIC -shared -o $t/a.so -xc -

View File

@ -4,7 +4,6 @@
[ $MACHINE = ppc64 ] && skip
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = alpha ] && skip
[[ $MACHINE = mips* ]] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -

View File

@ -1,7 +1,6 @@
#!/bin/bash
. $(dirname $0)/common.inc
[[ $MACHINE = mips* ]] && skip
cat <<EOF | $CC -o $t/a.o -c -xc -fno-PIE -
#include <setjmp.h>

View File

@ -62,7 +62,7 @@ if [ $MACHINE = x86_64 -o $MACHINE = aarch64 ]; then
fi
# riscv64-linux-gnu-strip crashes for some reason
if [ $MACHINE != riscv32 ] && [[ $MACHINE != mips* ]]; then
if [ $MACHINE != riscv32 ]; then
$CXX -B. -o $t/exe11 $t/b.o -pie
$STRIP $t/exe11
$QEMU $t/exe11

View File

@ -2,7 +2,6 @@
. $(dirname $0)/common.inc
[ $MACHINE = alpha ] && skip
[[ $MACHINE = mips* ]] && skip
# glibc 2.22 or prior have a bug that ld-linux.so.2 crashes on dlopen()
# if .rela.dyn and .rela.plt are not contiguous in a given DSO.

View File

@ -2,7 +2,6 @@
. $(dirname $0)/common.inc
# MIPS ABIs are not compatible with .gnu.hash
[[ $MACHINE = mips* ]] && skip
cat <<EOF | $CC -o $t/a.o -c -xc -
void foo() {}

View File

@ -3,7 +3,6 @@
# ARM32's strip command crashes on the output of this test for some reason.
[ $MACHINE = arm ] && skip
[[ $MACHINE = mips* ]] && skip
strip=$STRIP
command -v $strip >& /dev/null || skip

View File

@ -8,7 +8,6 @@
[ $MACHINE = ppc64le ] && skip
[ $MACHINE = sh4 ] && skip
[ $MACHINE = alpha ] && skip
[[ $MACHINE = mips* ]] && skip
[[ $MACHINE = loongarch* ]] && skip
cat <<EOF | $CC -shared -o $t/a.so -xc -

View File

@ -2,7 +2,6 @@
. $(dirname $0)/common.inc
[ $MACHINE = ppc64 ] && skip
[[ $MACHINE = mips* ]] && skip
# BusyBox's grep can't handle capture groups (e.g. \1, \2 ...)
grep --version 2>&1 | grep BusyBox && skip