From 9e0297b8bc1eafbd1eb845c740727abab44afa2f Mon Sep 17 00:00:00 2001 From: Simon Atanasyan Date: Sat, 5 Nov 2016 22:58:01 +0000 Subject: [PATCH] [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 --- lld/ELF/Config.h | 1 + lld/ELF/Driver.cpp | 16 +++++--- lld/ELF/Mips.cpp | 21 +++++++++++ lld/ELF/Relocations.cpp | 23 ++++++++++++ lld/ELF/SymbolTable.cpp | 8 +++- lld/ELF/Target.cpp | 28 +++++++++----- lld/ELF/Writer.h | 3 ++ lld/test/ELF/Inputs/mips-n32-rels.o | Bin 0 -> 1092 bytes lld/test/ELF/mips-elf-flags-err.s | 2 +- lld/test/ELF/mips-n32-emul.s | 14 +++++++ lld/test/ELF/mips-n32-rels.s | 71 ++++++++++++++++++++++++++++++++++++ 11 files changed, 169 insertions(+), 18 deletions(-) create mode 100644 lld/test/ELF/Inputs/mips-n32-rels.o create mode 100644 lld/test/ELF/mips-n32-emul.s create mode 100644 lld/test/ELF/mips-n32-rels.s diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index 3b9f716..52b4efc 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -105,6 +105,7 @@ struct Configuration { bool GnuHash = false; bool ICF; bool Mips64EL = false; + bool MipsN32Abi = false; bool NoGnuUnique; bool NoUndefinedVersion; bool Nostdlib; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index 85006a2..5b2eb69 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -59,7 +59,8 @@ bool elf::link(ArrayRef Args, bool CanExitEarly, } // Parses a linker -m option. -static std::tuple parseEmulation(StringRef Emul) { +static std::tuple +parseEmulation(StringRef Emul) { uint8_t OSABI = 0; StringRef S = Emul; if (S.endswith("_fbsd")) { @@ -74,6 +75,8 @@ static std::tuple 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 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 void LinkerDriver::link(opt::InputArgList &Args) { LinkerScript LS; ScriptBase = Script::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); diff --git a/lld/ELF/Mips.cpp b/lld/ELF/Mips.cpp index 2e7753c..ac65672 100644 --- a/lld/ELF/Mips.cpp +++ b/lld/ELF/Mips.cpp @@ -342,6 +342,27 @@ uint8_t elf::getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, return OldFlag; } +template static bool isN32Abi(const InputFile *F) { + if (auto *EF = dyn_cast>(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(F); + case ELF32BEKind: + return isN32Abi(F); + case ELF64LEKind: + return isN32Abi(F); + case ELF64BEKind: + return isN32Abi(F); + default: + llvm_unreachable("unknown Config->EKind"); + } +} + template uint32_t elf::getMipsEFlags(); template uint32_t elf::getMipsEFlags(); template uint32_t elf::getMipsEFlags(); diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index bf9eac2..d111c75 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -585,6 +585,22 @@ static void reportUndefined(SymbolBody &Sym, InputSectionBase &S, error(Msg); } +template +static std::pair +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 &C, ArrayRef 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()) diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index 5c6c8e6..c3e9f2e 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -36,8 +36,12 @@ using namespace lld::elf; template static bool isCompatible(InputFile *F) { if (!isa>(F) && !isa(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()) diff --git a/lld/ELF/Target.cpp b/lld/ELF/Target.cpp index d2e26d8..12a3ab0 100644 --- a/lld/ELF/Target.cpp +++ b/lld/ELF/Target.cpp @@ -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 MipsTargetInfo::MipsTargetInfo() { template RelExpr MipsTargetInfo::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 static bool isMipsR6() { template void MipsTargetInfo::writePltHeader(uint8_t *Buf) const { const endianness E = ELFT::TargetEndianness; - write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) - write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) - write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) - write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 + if (Config->MipsN32Abi) { + write32(Buf, 0x3c0e0000); // lui $14, %hi(&GOTPLT[0]) + write32(Buf + 4, 0x8dd90000); // lw $25, %lo(&GOTPLT[0])($14) + write32(Buf + 8, 0x25ce0000); // addiu $14, $14, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x030ec023); // subu $24, $24, $14 + } else { + write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 + } write32(Buf + 16, 0x03e07825); // move $15, $31 write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 write32(Buf + 24, 0x0320f809); // jalr $25 @@ -2137,8 +2145,8 @@ uint64_t MipsTargetInfo::getImplicitAddend(const uint8_t *Buf, } } -static std::pair calculateMips64RelChain(uint32_t Type, - uint64_t Val) { +static std::pair 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::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: diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h index b0d60b4..3b6c6e5 100644 --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -16,6 +16,7 @@ namespace lld { namespace elf { +class InputFile; template class OutputSectionBase; template class InputSectionBase; template class ObjectFile; @@ -45,6 +46,8 @@ template uint32_t getMipsEFlags(); uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, llvm::StringRef FileName); + +bool isMipsN32Abi(const InputFile *F); } } diff --git a/lld/test/ELF/Inputs/mips-n32-rels.o b/lld/test/ELF/Inputs/mips-n32-rels.o new file mode 100644 index 0000000000000000000000000000000000000000..88cbce699e6df9a81048d22c4b6c901447115602 GIT binary patch literal 1092 zcmah{O-sW-6nu%*YW)@uf*yJj5eW#M#6vwO7E}s7dMK$)(_(EU*@B+z!JpvKAK_2% zPkF90XVmlf-c#YqFeq0nYYB!jD|p z$YBB1T-UYp=J*`W^>8>&lyb=tFMfG6ejBriW^+$b6byXN1kXmij&FSN+Hovi6oma@ zcO>3Tz0vgicE3CDL+DSPh>O?~jb?Tn z+Lyc0bovdh@dlv1pr$o#0&7pGzhgWQq@g~}S{q;@e*&yx@So$k%)f+T<#W^nIf-QB E4}Bdz8~^|S literal 0 HcmV?d00001 diff --git a/lld/test/ELF/mips-elf-flags-err.s b/lld/test/ELF/mips-elf-flags-err.s index 45188cc..eab8377 100644 --- a/lld/test/ELF/mips-elf-flags-err.s +++ b/lld/test/ELF/mips-elf-flags-err.s @@ -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 diff --git a/lld/test/ELF/mips-n32-emul.s b/lld/test/ELF/mips-n32-emul.s new file mode 100644 index 0000000..d0d81cc1 --- /dev/null +++ b/lld/test/ELF/mips-n32-emul.s @@ -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 diff --git a/lld/test/ELF/mips-n32-rels.s b/lld/test/ELF/mips-n32-rels.s new file mode 100644 index 0000000..6bd171b --- /dev/null +++ b/lld/test/ELF/mips-n32-rels.s @@ -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: ] -- 2.7.4