From 649e4d328f1569f92b4dcec4f25437fbbf21816a Mon Sep 17 00:00:00 2001 From: Simon Atanasyan Date: Mon, 2 Oct 2017 14:56:41 +0000 Subject: [PATCH] [MIPS] Fix PLT entries generation in case of linking regular and microMIPS code Currently LLD calls the `isMicroMips` routine to determine type of PLT entries needs to be generated: regular or microMIPS. This routine checks ELF header flags in the `FirstObj` to retrieve type of linked object files. So if the first file does not contain microMIPS code, LLD will generate PLT entries with regular (non-microMIPS) code only. Ideally, if a PLT entry is referenced by microMIPS code only this entry should contain microMIPS code, if a PLT entry is referenced by regular code this entry should contain regular code. In a "mixed" case the PLT entry can be either microMIPS or regular, but each "cross-mode-call" has additional cost. It's rather difficult to implement this ideal solution. But we can assume that if there is an input object file with microMIPS code, the most part of the code is microMIPS too. So we need to deduce type of PLT entries based on finally calculated ELF header flags and do not check only the first input object file. This change implements this. - The `getMipsEFlags` renamed to the `calcMipsEFlags`. The function called from the `LinkerDriver::link`. Result is stored in the Configuration::MipsEFlags field. - The `isMicroMips` and `isMipsR6` routines access the `MipsEFlags` field to get and check calculated ELF flags. - New types of PLT records created when necessary. Differential revision: https://reviews.llvm.org/D37747 llvm-svn: 314675 --- lld/ELF/Arch/Mips.cpp | 27 +++++++++------------------ lld/ELF/Arch/MipsArchTree.cpp | 10 +++++----- lld/ELF/Config.h | 6 ++++++ lld/ELF/Driver.cpp | 3 +++ lld/ELF/SyntheticSections.cpp | 2 +- lld/ELF/Writer.cpp | 2 +- lld/ELF/Writer.h | 2 +- lld/test/ELF/mips-elf-flags.s | 29 +++++++++++++++++++++++++++++ lld/test/ELF/mips-micro-jal.s | 30 ++++++++++++++++++++++++++++++ 9 files changed, 85 insertions(+), 26 deletions(-) diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp index 06618ec..4665ca1 100644 --- a/lld/ELF/Arch/Mips.cpp +++ b/lld/ELF/Arch/Mips.cpp @@ -238,37 +238,29 @@ static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize, write16(Loc, Data); } -template static bool isMicroMips() { - // FIXME (simon): This code does not support the case when both - // microMIPS and MIPS object files are linked together. - const auto &FirstObj = cast>(*Config->FirstElf); - uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH_ASE; - return Arch == EF_MIPS_MICROMIPS; -} +static bool isMicroMips() { return Config->MipsEFlags & EF_MIPS_MICROMIPS; } -template static bool isMipsR6() { - const auto &FirstObj = cast>(*Config->FirstElf); - uint32_t Arch = FirstObj.getObj().getHeader()->e_flags & EF_MIPS_ARCH; +static bool isMipsR6() { + uint32_t Arch = Config->MipsEFlags & EF_MIPS_ARCH; return Arch == EF_MIPS_ARCH_32R6 || Arch == EF_MIPS_ARCH_64R6; } template void MIPS::writePltHeader(uint8_t *Buf) const { const endianness E = ELFT::TargetEndianness; - if (isMicroMips()) { + if (isMicroMips()) { uint64_t GotPlt = In::GotPlt->getVA(); uint64_t Plt = In::Plt->getVA(); // Overwrite trap instructions written by Writer::writeTrapInstr. memset(Buf, 0, PltHeaderSize); - write16(Buf, isMipsR6() ? 0x7860 : 0x7980); - // addiupc v1, (GOTPLT) - . + write16(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - . write16(Buf + 4, 0xff23); // lw $25, 0($3) write16(Buf + 8, 0x0535); // subu16 $2, $2, $3 write16(Buf + 10, 0x2525); // srl16 $2, $2, 2 write16(Buf + 12, 0x3302); // addiu $24, $2, -2 write16(Buf + 14, 0xfffe); write16(Buf + 16, 0x0dff); // move $15, $31 - if (isMipsR6()) { + if (isMipsR6()) { write16(Buf + 18, 0x0f83); // move $28, $3 write16(Buf + 20, 0x472b); // jalrc $25 write16(Buf + 22, 0x0c00); // nop @@ -310,11 +302,11 @@ void MIPS::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const { const endianness E = ELFT::TargetEndianness; - if (isMicroMips()) { + if (isMicroMips()) { // Overwrite trap instructions written by Writer::writeTrapInstr. memset(Buf, 0, PltEntrySize); - if (isMipsR6()) { + if (isMipsR6()) { write16(Buf, 0x7840); // addiupc $2, (GOTPLT) - . write16(Buf + 4, 0xff22); // lw $25, 0($2) write16(Buf + 8, 0x0f02); // move $24, $2 @@ -332,8 +324,7 @@ void MIPS::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, write32(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) write32(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) - // jr $25 - write32(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008); + write32(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008); // jr $25 write32(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) writeRelocation(Buf, GotPltEntryAddr + 0x8000, 16, 16); writeRelocation(Buf + 4, GotPltEntryAddr, 16, 0); diff --git a/lld/ELF/Arch/MipsArchTree.cpp b/lld/ELF/Arch/MipsArchTree.cpp index ff3aa9d..f78b6ba 100644 --- a/lld/ELF/Arch/MipsArchTree.cpp +++ b/lld/ELF/Arch/MipsArchTree.cpp @@ -281,7 +281,7 @@ static uint32_t getArchFlags(ArrayRef Files) { return Ret; } -template uint32_t elf::getMipsEFlags() { +template uint32_t elf::calcMipsEFlags() { std::vector V; for (InputFile *F : ObjectFiles) V.push_back( @@ -364,7 +364,7 @@ bool elf::isMipsN32Abi(const InputFile *F) { } } -template uint32_t elf::getMipsEFlags(); -template uint32_t elf::getMipsEFlags(); -template uint32_t elf::getMipsEFlags(); -template uint32_t elf::getMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); +template uint32_t elf::calcMipsEFlags(); diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h index ecc3399..83afb84 100644 --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -211,6 +211,12 @@ struct Configuration { // if that's true.) bool IsMips64EL; + // Holds set of ELF header flags for MIPS targets. The set calculated + // by the `elf::calcMipsEFlags` function and cached in this field. For + // the calculation we iterate over all input object files and combine + // their ELF flags. + uint32_t MipsEFlags = 0; + // The ELF spec defines two types of relocation table entries, RELA and // REL. RELA is a triplet of (offset, info, addend) while REL is a // tuple of (offset, info). Addends for REL are implicit and read from diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp index e272f00..b53d88b 100644 --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1080,6 +1080,9 @@ template void LinkerDriver::link(opt::InputArgList &Args) { for (InputSectionBase *S : F->getSections()) InputSections.push_back(cast(S)); + if (Config->EMachine == EM_MIPS) + Config->MipsEFlags = calcMipsEFlags(); + // This adds a .comment section containing a version string. We have to add it // before decompressAndMergeSections because the .comment section is a // mergeable section. diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index 835b4ca..5910395 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -151,7 +151,7 @@ MipsAbiFlagsSection *MipsAbiFlagsSection::create() { return nullptr; } - // LLD checks ISA compatibility in getMipsEFlags(). Here we just + // LLD checks ISA compatibility in calcMipsEFlags(). Here we just // select the highest number of ISA/Rev/Ext. Flags.isa_level = std::max(Flags.isa_level, S->isa_level); Flags.isa_rev = std::max(Flags.isa_rev, S->isa_rev); diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 6274808..aee315e 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1798,7 +1798,7 @@ template void Writer::writeHeader() { // kernels (as of 2016) require an EABI version to be set. EHdr->e_flags = EF_ARM_EABI_VER5; else if (Config->EMachine == EM_MIPS) - EHdr->e_flags = getMipsEFlags(); + EHdr->e_flags = Config->MipsEFlags; if (!Config->Relocatable) { EHdr->e_phoff = sizeof(Elf_Ehdr); diff --git a/lld/ELF/Writer.h b/lld/ELF/Writer.h index 8307000..4db5fe8 100644 --- a/lld/ELF/Writer.h +++ b/lld/ELF/Writer.h @@ -48,7 +48,7 @@ struct PhdrEntry { llvm::StringRef getOutputSectionName(llvm::StringRef Name); -template uint32_t getMipsEFlags(); +template uint32_t calcMipsEFlags(); uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag, llvm::StringRef FileName); diff --git a/lld/test/ELF/mips-elf-flags.s b/lld/test/ELF/mips-elf-flags.s index f8f916c..d2b3d92 100644 --- a/lld/test/ELF/mips-elf-flags.s +++ b/lld/test/ELF/mips-elf-flags.s @@ -35,6 +35,12 @@ # RUN: llvm-readobj -h -mips-abi-flags %t.exe \ # RUN: | FileCheck -check-prefix=OCTEON %s +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mattr=micromips %S/Inputs/mips-fpic.s -o %t-mm.o +# RUN: ld.lld %t.o %t-mm.o -o %t.exe +# RUN: llvm-readobj -h -mips-abi-flags %t.exe | FileCheck -check-prefix=MICRO %s + # REQUIRES: mips .text @@ -170,3 +176,26 @@ __start: # OCTEON-NEXT: ] # OCTEON-NEXT: Flags 2: 0x0 # OCTEON-NEXT: } + +# MICRO: Flags [ +# MICRO-NEXT: EF_MIPS_ABI_O32 +# MICRO-NEXT: EF_MIPS_ARCH_32 +# MICRO-NEXT: EF_MIPS_CPIC +# MICRO-NEXT: EF_MIPS_MICROMIPS +# MICRO-NEXT: ] +# MICRO: MIPS ABI Flags { +# MICRO-NEXT: Version: 0 +# MICRO-NEXT: ISA: MIPS32 +# MICRO-NEXT: ISA Extension: None +# MICRO-NEXT: ASEs [ +# MICRO-NEXT: microMIPS +# MICRO-NEXT: ] +# MICRO-NEXT: FP ABI: Hard float (double precision) +# MICRO-NEXT: GPR size: 32 +# MICRO-NEXT: CPR1 size: 32 +# MICRO-NEXT: CPR2 size: 0 +# MICRO-NEXT: Flags 1 [ +# MICRO-NEXT: ODDSPREG +# MICRO-NEXT: ] +# MICRO-NEXT: Flags 2: 0x0 +# MICRO-NEXT: } diff --git a/lld/test/ELF/mips-micro-jal.s b/lld/test/ELF/mips-micro-jal.s index 39193ae..83826126 100644 --- a/lld/test/ELF/mips-micro-jal.s +++ b/lld/test/ELF/mips-micro-jal.s @@ -34,6 +34,17 @@ # RUN: ld.lld -o %tel.exe %t2el.o %tel.so # RUN: llvm-objdump -d -mattr=micromips %tel.exe | FileCheck --check-prefix=ELR6 %s +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mattr=micromips %S/Inputs/mips-micro.s -o %t1eb.o +# RUN: ld.lld -shared -o %teb.so %t1eb.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %S/Inputs/mips-fpic.s -o %t-reg.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mattr=micromips %s -o %t2eb.o +# RUN: ld.lld --no-threads -o %teb.exe %t-reg.o %t2eb.o %teb.so +# RUN: llvm-objdump -d -mattr=micromips %teb.exe \ +# RUN: | FileCheck --check-prefix=MIXED %s + # REQUIRES: mips # EB: Disassembly of section .plt: @@ -106,6 +117,25 @@ # ELR6-NEXT: 20038: 02 0f move16 $24, $2 # ELR6-NEXT: 2003a: 23 47 jrc16 $25 +# MIXED: Disassembly of section .plt: +# MIXED-NEXT: .plt: +# MIXED-NEXT: 20020: 79 80 3f f9 addiupc $3, 65508 +# MIXED-NEXT: 20024: ff 23 00 00 lw $25, 0($3) +# MIXED-NEXT: 20028: 05 35 subu16 $2, $2, $3 +# MIXED-NEXT: 2002a: 25 25 srl16 $2, $2, 2 +# MIXED-NEXT: 2002c: 33 02 ff fe addiu $24, $2, -2 +# MIXED-NEXT: 20030: 0d ff move $15, $ra +# MIXED-NEXT: 20032: 45 f9 jalrs16 $25 +# MIXED-NEXT: 20034: 0f 83 move $gp, $3 +# MIXED-NEXT: 20036: 0c 00 nop +# MIXED-NEXT: 20038: 00 00 00 00 nop +# MIXED-NEXT: 2003c: 00 00 00 00 nop + +# MIXED-NEXT: 20040: 79 00 3f f3 addiupc $2, 65484 +# MIXED-NEXT: 20044: ff 22 00 00 lw $25, 0($2) +# MIXED-NEXT: 20048: 45 99 jr16 $25 +# MIXED-NEXT: 2004a: 0f 02 move $24, $2 + # PLT: Entries [ # PLT-NEXT: Entry { # PLT-NEXT: Address: 0x3000C -- 2.7.4