forked from OSchip/llvm-project
[ELF][MIPS] N32 ABI support
In short the patch introduces support for linking object file conform MIPS N32 ABI [1]. This ABI is similar to N64 ABI but uses 32-bit pointer size. The most non-trivial requirement of this ABI is one more relocation packing format. N64 ABI puts multiple relocation type into the single relocation record. The N32 ABI uses series of successive relocations with the same offset for this purpose. In this patch, new function `mergeMipsN32RelTypes` handle this case and "convert" N32 relocation to the N64 relocation so the rest of the code keep unchanged. For now, linker does not support series of relocations applied to sections without SHF_ALLOC bit. Probably later I will add the support or insert some sort of assert into the `relocateNonAlloc` routine to catch this case. [1] ftp://www.linux-mips.org/pub/linux/mips/doc/ABI/MIPS-N32-ABI-Handbook.pdf Differential revision: https://reviews.llvm.org/D26298 llvm-svn: 286052
This commit is contained in:
parent
8c6a5aaf15
commit
9e0297b8bc
|
@ -105,6 +105,7 @@ struct Configuration {
|
|||
bool GnuHash = false;
|
||||
bool ICF;
|
||||
bool Mips64EL = false;
|
||||
bool MipsN32Abi = false;
|
||||
bool NoGnuUnique;
|
||||
bool NoUndefinedVersion;
|
||||
bool Nostdlib;
|
||||
|
|
|
@ -59,7 +59,8 @@ bool elf::link(ArrayRef<const char *> Args, bool CanExitEarly,
|
|||
}
|
||||
|
||||
// Parses a linker -m option.
|
||||
static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
|
||||
static std::tuple<ELFKind, uint16_t, uint8_t, bool>
|
||||
parseEmulation(StringRef Emul) {
|
||||
uint8_t OSABI = 0;
|
||||
StringRef S = Emul;
|
||||
if (S.endswith("_fbsd")) {
|
||||
|
@ -74,6 +75,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
|
|||
.Case("elf32_x86_64", {ELF32LEKind, EM_X86_64})
|
||||
.Case("elf32btsmip", {ELF32BEKind, EM_MIPS})
|
||||
.Case("elf32ltsmip", {ELF32LEKind, EM_MIPS})
|
||||
.Case("elf32btsmipn32", {ELF32BEKind, EM_MIPS})
|
||||
.Case("elf32ltsmipn32", {ELF32LEKind, EM_MIPS})
|
||||
.Case("elf32ppc", {ELF32BEKind, EM_PPC})
|
||||
.Case("elf64btsmip", {ELF64BEKind, EM_MIPS})
|
||||
.Case("elf64ltsmip", {ELF64LEKind, EM_MIPS})
|
||||
|
@ -89,7 +92,8 @@ static std::tuple<ELFKind, uint16_t, uint8_t> parseEmulation(StringRef Emul) {
|
|||
else
|
||||
error("unknown emulation: " + Emul);
|
||||
}
|
||||
return std::make_tuple(Ret.first, Ret.second, OSABI);
|
||||
bool IsMipsN32ABI = S == "elf32btsmipn32" || S == "elf32ltsmipn32";
|
||||
return std::make_tuple(Ret.first, Ret.second, OSABI, IsMipsN32ABI);
|
||||
}
|
||||
|
||||
// Returns slices of MB by parsing MB as an archive file.
|
||||
|
@ -459,8 +463,8 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
|
|||
if (auto *Arg = Args.getLastArg(OPT_m)) {
|
||||
// Parse ELF{32,64}{LE,BE} and CPU type.
|
||||
StringRef S = Arg->getValue();
|
||||
std::tie(Config->EKind, Config->EMachine, Config->OSABI) =
|
||||
parseEmulation(S);
|
||||
std::tie(Config->EKind, Config->EMachine, Config->OSABI,
|
||||
Config->MipsN32Abi) = parseEmulation(S);
|
||||
Config->Emulation = S;
|
||||
}
|
||||
|
||||
|
@ -655,6 +659,7 @@ void LinkerDriver::inferMachineType() {
|
|||
Config->EKind = F->EKind;
|
||||
Config->EMachine = F->EMachine;
|
||||
Config->OSABI = F->OSABI;
|
||||
Config->MipsN32Abi = Config->EMachine == EM_MIPS && isMipsN32Abi(F);
|
||||
return;
|
||||
}
|
||||
error("target emulation unknown: -m or at least one .o file required");
|
||||
|
@ -691,7 +696,8 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
|
|||
LinkerScript<ELFT> LS;
|
||||
ScriptBase = Script<ELFT>::X = &LS;
|
||||
|
||||
Config->Rela = ELFT::Is64Bits || Config->EMachine == EM_X86_64;
|
||||
Config->Rela =
|
||||
ELFT::Is64Bits || Config->EMachine == EM_X86_64 || Config->MipsN32Abi;
|
||||
Config->Mips64EL =
|
||||
(Config->EMachine == EM_MIPS && Config->EKind == ELF64LEKind);
|
||||
Config->ImageBase = getImageBase(Args);
|
||||
|
|
|
@ -342,6 +342,27 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
|
|||
return OldFlag;
|
||||
}
|
||||
|
||||
template <class ELFT> static bool isN32Abi(const InputFile *F) {
|
||||
if (auto *EF = dyn_cast<ELFFileBase<ELFT>>(F))
|
||||
return EF->getObj().getHeader()->e_flags & EF_MIPS_ABI2;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool elf::isMipsN32Abi(const InputFile *F) {
|
||||
switch (Config->EKind) {
|
||||
case ELF32LEKind:
|
||||
return isN32Abi<ELF32LE>(F);
|
||||
case ELF32BEKind:
|
||||
return isN32Abi<ELF32BE>(F);
|
||||
case ELF64LEKind:
|
||||
return isN32Abi<ELF64LE>(F);
|
||||
case ELF64BEKind:
|
||||
return isN32Abi<ELF64BE>(F);
|
||||
default:
|
||||
llvm_unreachable("unknown Config->EKind");
|
||||
}
|
||||
}
|
||||
|
||||
template uint32_t elf::getMipsEFlags<ELF32LE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF32BE>();
|
||||
template uint32_t elf::getMipsEFlags<ELF64LE>();
|
||||
|
|
|
@ -585,6 +585,22 @@ static void reportUndefined(SymbolBody &Sym, InputSectionBase<ELFT> &S,
|
|||
error(Msg);
|
||||
}
|
||||
|
||||
template <class RelTy>
|
||||
static std::pair<uint32_t, uint32_t>
|
||||
mergeMipsN32RelTypes(uint32_t Type, uint32_t Offset, RelTy *I, RelTy *E) {
|
||||
// MIPS N32 ABI treats series of successive relocations with the same offset
|
||||
// as a single relocation. The similar approach used by N64 ABI, but this ABI
|
||||
// packs all relocations into the single relocation record. Here we emulate
|
||||
// this for the N32 ABI. Iterate over relocation with the same offset and put
|
||||
// theirs types into the single bit-set.
|
||||
uint32_t Processed = 0;
|
||||
for (; I != E && Offset == I->r_offset; ++I) {
|
||||
++Processed;
|
||||
Type |= I->getType(Config->Mips64EL) << (8 * Processed);
|
||||
}
|
||||
return std::make_pair(Type, Processed);
|
||||
}
|
||||
|
||||
// The reason we have to do this early scan is as follows
|
||||
// * To mmap the output file, we need to know the size
|
||||
// * For that, we need to know how many dynamic relocs we will have.
|
||||
|
@ -624,6 +640,13 @@ static void scanRelocs(InputSectionBase<ELFT> &C, ArrayRef<RelTy> Rels) {
|
|||
SymbolBody &Body = File.getRelocTargetSym(RI);
|
||||
uint32_t Type = RI.getType(Config->Mips64EL);
|
||||
|
||||
if (Config->MipsN32Abi) {
|
||||
uint32_t Processed;
|
||||
std::tie(Type, Processed) =
|
||||
mergeMipsN32RelTypes(Type, RI.r_offset, I + 1, E);
|
||||
I += Processed;
|
||||
}
|
||||
|
||||
// We only report undefined symbols if they are referenced somewhere in the
|
||||
// code.
|
||||
if (!Body.isLocal() && Body.isUndefined() && !Body.symbol()->isWeak())
|
||||
|
|
|
@ -36,8 +36,12 @@ using namespace lld::elf;
|
|||
template <class ELFT> static bool isCompatible(InputFile *F) {
|
||||
if (!isa<ELFFileBase<ELFT>>(F) && !isa<BitcodeFile>(F))
|
||||
return true;
|
||||
if (F->EKind == Config->EKind && F->EMachine == Config->EMachine)
|
||||
return true;
|
||||
if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) {
|
||||
if (Config->EMachine != EM_MIPS)
|
||||
return true;
|
||||
if (isMipsN32Abi(F) == Config->MipsN32Abi)
|
||||
return true;
|
||||
}
|
||||
StringRef A = F->getName();
|
||||
StringRef B = Config->Emulation;
|
||||
if (B.empty())
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "OutputSections.h"
|
||||
#include "Symbols.h"
|
||||
#include "Thunks.h"
|
||||
#include "Writer.h"
|
||||
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/Object/ELF.h"
|
||||
|
@ -1921,8 +1922,8 @@ template <class ELFT> MipsTargetInfo<ELFT>::MipsTargetInfo() {
|
|||
template <class ELFT>
|
||||
RelExpr MipsTargetInfo<ELFT>::getRelExpr(uint32_t Type,
|
||||
const SymbolBody &S) const {
|
||||
if (ELFT::Is64Bits)
|
||||
// See comment in the calculateMips64RelChain.
|
||||
// See comment in the calculateMipsRelChain.
|
||||
if (ELFT::Is64Bits || Config->MipsN32Abi)
|
||||
Type &= 0xff;
|
||||
switch (Type) {
|
||||
default:
|
||||
|
@ -2047,10 +2048,17 @@ template <class ELFT> static bool isMipsR6() {
|
|||
template <class ELFT>
|
||||
void MipsTargetInfo<ELFT>::writePltHeader(uint8_t *Buf) const {
|
||||
const endianness E = ELFT::TargetEndianness;
|
||||
write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
||||
write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
||||
write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||
write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||
if (Config->MipsN32Abi) {
|
||||
write32<E>(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0])
|
||||
write32<E>(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14)
|
||||
write32<E>(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0])
|
||||
write32<E>(Buf + 12, 0x030ec023); // subu $24, $24, $14
|
||||
} else {
|
||||
write32<E>(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0])
|
||||
write32<E>(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28)
|
||||
write32<E>(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0])
|
||||
write32<E>(Buf + 12, 0x031cc023); // subu $24, $24, $28
|
||||
}
|
||||
write32<E>(Buf + 16, 0x03e07825); // move $15, $31
|
||||
write32<E>(Buf + 20, 0x0018c082); // srl $24, $24, 2
|
||||
write32<E>(Buf + 24, 0x0320f809); // jalr $25
|
||||
|
@ -2137,8 +2145,8 @@ uint64_t MipsTargetInfo<ELFT>::getImplicitAddend(const uint8_t *Buf,
|
|||
}
|
||||
}
|
||||
|
||||
static std::pair<uint32_t, uint64_t> calculateMips64RelChain(uint32_t Type,
|
||||
uint64_t Val) {
|
||||
static std::pair<uint32_t, uint64_t> calculateMipsRelChain(uint32_t Type,
|
||||
uint64_t Val) {
|
||||
// MIPS N64 ABI packs multiple relocations into the single relocation
|
||||
// record. In general, all up to three relocations can have arbitrary
|
||||
// types. In fact, Clang and GCC uses only a few combinations. For now,
|
||||
|
@ -2175,8 +2183,8 @@ void MipsTargetInfo<ELFT>::relocateOne(uint8_t *Loc, uint32_t Type,
|
|||
else if (Type == R_MIPS_TLS_TPREL_HI16 || Type == R_MIPS_TLS_TPREL_LO16 ||
|
||||
Type == R_MIPS_TLS_TPREL32 || Type == R_MIPS_TLS_TPREL64)
|
||||
Val -= 0x7000;
|
||||
if (ELFT::Is64Bits)
|
||||
std::tie(Type, Val) = calculateMips64RelChain(Type, Val);
|
||||
if (ELFT::Is64Bits || Config->MipsN32Abi)
|
||||
std::tie(Type, Val) = calculateMipsRelChain(Type, Val);
|
||||
switch (Type) {
|
||||
case R_MIPS_32:
|
||||
case R_MIPS_GPREL32:
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
namespace lld {
|
||||
namespace elf {
|
||||
class InputFile;
|
||||
template <class ELFT> class OutputSectionBase;
|
||||
template <class ELFT> class InputSectionBase;
|
||||
template <class ELFT> class ObjectFile;
|
||||
|
@ -45,6 +46,8 @@ template <class ELFT> uint32_t getMipsEFlags();
|
|||
|
||||
uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
|
||||
llvm::StringRef FileName);
|
||||
|
||||
bool isMipsN32Abi(const InputFile *F);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -82,6 +82,6 @@ __start:
|
|||
# OCTEON-NEXT: EF_MIPS_PIC
|
||||
# OCTEON-NEXT: ]
|
||||
|
||||
# N32O32: target ABI 'n32' is incompatible with 'o32': {{.*}}mips-elf-flags-err.s.tmp2.o
|
||||
# N32O32: error: {{.*}}mips-elf-flags-err.s.tmp2.o is incompatible with {{.*}}mips-elf-flags-err.s.tmp1.o
|
||||
|
||||
# NAN: target -mnan=2008 is incompatible with -mnan=legacy: {{.*}}mips-elf-flags-err.s.tmp2.o
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# Check that LLD shows an error when N32 ABI emulation argument
|
||||
# is combined with non-N32 ABI object files.
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o
|
||||
# RUN: not ld.lld -m elf32btsmipn32 %t.o -o %t.exe 2>&1 | FileCheck %s
|
||||
|
||||
# REQUIRES: mips
|
||||
|
||||
.text
|
||||
.global __start
|
||||
__start:
|
||||
nop
|
||||
|
||||
# CHECK: error: {{.*}}mips-n32-emul.s.tmp.o is incompatible with elf32btsmipn32
|
|
@ -0,0 +1,71 @@
|
|||
# Check handling of N32 ABI relocation records.
|
||||
|
||||
# For now llvm-mc generates incorrect object files for N32 ABI.
|
||||
# We use the binary input file generated by GNU tool.
|
||||
# llvm-mc -filetype=obj -triple=mips64-unknown-linux \
|
||||
# -target-abi n32 %s -o %t.o
|
||||
# RUN: ld.lld %S/Inputs/mips-n32-rels.o -o %t.exe
|
||||
# RUN: llvm-objdump -t -d -s %t.exe | FileCheck %s
|
||||
# RUN: llvm-readobj -h %t.exe | FileCheck -check-prefix=ELF %s
|
||||
|
||||
# REQUIRES: mips
|
||||
|
||||
# .text
|
||||
# .type __start, @function
|
||||
# .global __start
|
||||
# __start:
|
||||
# lui $gp,%hi(%neg(%gp_rel(__start))) # R_MIPS_GPREL16
|
||||
# # R_MIPS_SUB
|
||||
# # R_MIPS_HI16
|
||||
# loc:
|
||||
# daddiu $gp,$gp,%lo(%neg(%gp_rel(__start))) # R_MIPS_GPREL16
|
||||
# # R_MIPS_SUB
|
||||
# # R_MIPS_LO16
|
||||
#
|
||||
# .section .rodata,"a",@progbits
|
||||
# .gpword(loc) # R_MIPS_32
|
||||
|
||||
# CHECK: Disassembly of section .text:
|
||||
# CHECK-NEXT: __start:
|
||||
# CHECK-NEXT: 20000: 3c 1c 00 01 lui $gp, 1
|
||||
# ^-- 0x20000 - 0x37ff0
|
||||
# ^-- 0 - 0xfffe8010
|
||||
# ^-- %hi(0x17ff0)
|
||||
# CHECK: loc:
|
||||
# CHECK-NEXT: 20004: 67 9c 7f f0 daddiu $gp, $gp, 32752
|
||||
# ^-- 0x20000 - 0x37ff0
|
||||
# ^-- 0 - 0xfffe8010
|
||||
# ^-- %lo(0x17ff0)
|
||||
|
||||
# CHECK: Contents of section .rodata:
|
||||
# CHECK-NEXT: 10128 00020004
|
||||
# ^-- loc
|
||||
|
||||
# CHECK: 00020004 .text 00000000 loc
|
||||
# CHECK: 00037ff0 .got 00000000 .hidden _gp
|
||||
# CHECK: 00020000 g F .text 00000000 __start
|
||||
|
||||
# ELF: Format: ELF32-mips
|
||||
# ELF-NEXT: Arch: mips
|
||||
# ELF-NEXT: AddressSize: 32bit
|
||||
# ELF-NEXT: LoadName:
|
||||
# ELF-NEXT: ElfHeader {
|
||||
# ELF-NEXT: Ident {
|
||||
# ELF-NEXT: Magic: (7F 45 4C 46)
|
||||
# ELF-NEXT: Class: 32-bit (0x1)
|
||||
# ELF-NEXT: DataEncoding: BigEndian (0x2)
|
||||
# ELF-NEXT: FileVersion: 1
|
||||
# ELF-NEXT: OS/ABI: SystemV (0x0)
|
||||
# ELF-NEXT: ABIVersion: 0
|
||||
# ELF-NEXT: Unused: (00 00 00 00 00 00 00)
|
||||
# ELF-NEXT: }
|
||||
# ELF-NEXT: Type: Executable (0x2)
|
||||
# ELF-NEXT: Machine: EM_MIPS (0x8)
|
||||
# ELF-NEXT: Version: 1
|
||||
# ELF-NEXT: Entry: 0x20000
|
||||
# ELF-NEXT: ProgramHeaderOffset:
|
||||
# ELF-NEXT: SectionHeaderOffset:
|
||||
# ELF-NEXT: Flags [
|
||||
# ELF-NEXT: EF_MIPS_ABI2
|
||||
# ELF-NEXT: EF_MIPS_ARCH_64R2
|
||||
# ELF-NEXT: ]
|
Loading…
Reference in New Issue