[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
This commit is contained in:
Simon Atanasyan 2017-10-02 14:56:41 +00:00
parent 01e5320c48
commit 649e4d328f
9 changed files with 85 additions and 26 deletions

View File

@ -238,37 +238,29 @@ static void writeMicroRelocation16(uint8_t *Loc, uint64_t V, uint8_t BitsSize,
write16<E>(Loc, Data);
}
template <class ELFT> 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<ELFFileBase<ELFT>>(*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 <class ELFT> static bool isMipsR6() {
const auto &FirstObj = cast<ELFFileBase<ELFT>>(*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 <class ELFT> void MIPS<ELFT>::writePltHeader(uint8_t *Buf) const {
const endianness E = ELFT::TargetEndianness;
if (isMicroMips<ELFT>()) {
if (isMicroMips()) {
uint64_t GotPlt = In<ELFT>::GotPlt->getVA();
uint64_t Plt = In<ELFT>::Plt->getVA();
// Overwrite trap instructions written by Writer::writeTrapInstr.
memset(Buf, 0, PltHeaderSize);
write16<E>(Buf, isMipsR6<ELFT>() ? 0x7860 : 0x7980);
// addiupc v1, (GOTPLT) - .
write16<E>(Buf, isMipsR6() ? 0x7860 : 0x7980); // addiupc v1, (GOTPLT) - .
write16<E>(Buf + 4, 0xff23); // lw $25, 0($3)
write16<E>(Buf + 8, 0x0535); // subu16 $2, $2, $3
write16<E>(Buf + 10, 0x2525); // srl16 $2, $2, 2
write16<E>(Buf + 12, 0x3302); // addiu $24, $2, -2
write16<E>(Buf + 14, 0xfffe);
write16<E>(Buf + 16, 0x0dff); // move $15, $31
if (isMipsR6<ELFT>()) {
if (isMipsR6()) {
write16<E>(Buf + 18, 0x0f83); // move $28, $3
write16<E>(Buf + 20, 0x472b); // jalrc $25
write16<E>(Buf + 22, 0x0c00); // nop
@ -310,11 +302,11 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
uint64_t PltEntryAddr, int32_t Index,
unsigned RelOff) const {
const endianness E = ELFT::TargetEndianness;
if (isMicroMips<ELFT>()) {
if (isMicroMips()) {
// Overwrite trap instructions written by Writer::writeTrapInstr.
memset(Buf, 0, PltEntrySize);
if (isMipsR6<ELFT>()) {
if (isMipsR6()) {
write16<E>(Buf, 0x7840); // addiupc $2, (GOTPLT) - .
write16<E>(Buf + 4, 0xff22); // lw $25, 0($2)
write16<E>(Buf + 8, 0x0f02); // move $24, $2
@ -332,8 +324,7 @@ void MIPS<ELFT>::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
write32<E>(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry)
write32<E>(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15)
// jr $25
write32<E>(Buf + 8, isMipsR6<ELFT>() ? 0x03200009 : 0x03200008);
write32<E>(Buf + 8, isMipsR6() ? 0x03200009 : 0x03200008); // jr $25
write32<E>(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry)
writeRelocation<E>(Buf, GotPltEntryAddr + 0x8000, 16, 16);
writeRelocation<E>(Buf + 4, GotPltEntryAddr, 16, 0);

View File

@ -281,7 +281,7 @@ static uint32_t getArchFlags(ArrayRef<FileFlags> Files) {
return Ret;
}
template <class ELFT> uint32_t elf::getMipsEFlags() {
template <class ELFT> uint32_t elf::calcMipsEFlags() {
std::vector<FileFlags> V;
for (InputFile *F : ObjectFiles)
V.push_back(
@ -364,7 +364,7 @@ bool elf::isMipsN32Abi(const InputFile *F) {
}
}
template uint32_t elf::getMipsEFlags<ELF32LE>();
template uint32_t elf::getMipsEFlags<ELF32BE>();
template uint32_t elf::getMipsEFlags<ELF64LE>();
template uint32_t elf::getMipsEFlags<ELF64BE>();
template uint32_t elf::calcMipsEFlags<ELF32LE>();
template uint32_t elf::calcMipsEFlags<ELF32BE>();
template uint32_t elf::calcMipsEFlags<ELF64LE>();
template uint32_t elf::calcMipsEFlags<ELF64BE>();

View File

@ -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

View File

@ -1080,6 +1080,9 @@ template <class ELFT> void LinkerDriver::link(opt::InputArgList &Args) {
for (InputSectionBase *S : F->getSections())
InputSections.push_back(cast<InputSection>(S));
if (Config->EMachine == EM_MIPS)
Config->MipsEFlags = calcMipsEFlags<ELFT>();
// This adds a .comment section containing a version string. We have to add it
// before decompressAndMergeSections because the .comment section is a
// mergeable section.

View File

@ -151,7 +151,7 @@ MipsAbiFlagsSection<ELFT> *MipsAbiFlagsSection<ELFT>::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);

View File

@ -1798,7 +1798,7 @@ template <class ELFT> void Writer<ELFT>::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<ELFT>();
EHdr->e_flags = Config->MipsEFlags;
if (!Config->Relocatable) {
EHdr->e_phoff = sizeof(Elf_Ehdr);

View File

@ -48,7 +48,7 @@ struct PhdrEntry {
llvm::StringRef getOutputSectionName(llvm::StringRef Name);
template <class ELFT> uint32_t getMipsEFlags();
template <class ELFT> uint32_t calcMipsEFlags();
uint8_t getMipsFpAbiFlag(uint8_t OldFlag, uint8_t NewFlag,
llvm::StringRef FileName);

View File

@ -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: }

View File

@ -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