Index: ELF/Arch/Mips.cpp =================================================================== --- ELF/Arch/Mips.cpp +++ ELF/Arch/Mips.cpp @@ -454,10 +454,81 @@ return std::make_pair(Type & 0xff, Val); } +static bool isBranchReloc(RelType Type) { + switch (Type) { + default: + return false; + case R_MIPS_26: + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + return true; + } +} + +static bool isMicroBranchReloc(RelType Type) { + switch (Type) { + default: + return false; + case R_MICROMIPS_26_S1: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + return true; + } +} + +template +static void fixupCrossModeJump(uint8_t *Loc, RelType Type, uint64_t *Val) { + const endianness E = ELFT::TargetEndianness; + bool IsMicroTgt = *Val & 0x1; + bool IsCrossJump = (IsMicroTgt && isBranchReloc(Type)) || + (!IsMicroTgt && isMicroBranchReloc(Type)); + if (!IsCrossJump) + return; + + switch (Type) { + case R_MIPS_26: { + uint32_t Inst = read32(Loc) >> 26; + if (Inst == 0x3 || Inst == 0x1d) { // JAL or JALX + writeRelocation(Loc, 0x1d << 26, 32, 0); + return; + } + break; + } + case R_MICROMIPS_26_S1: { + uint32_t Inst = readShuffle(Loc) >> 26; + if (Inst == 0x3d || Inst == 0x3c) { // JAL32 or JALX32 + *Val >>= 1; + writeMicroRelocation32(Loc, 0x3c << 26, 32, 0); + return; + } + break; + } + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + // FIXME (simon): Support valid branch relocations. + break; + default: + llvm_unreachable("unexpected jump/branch relocation"); + } + + error(getErrorLocation(Loc) + + "unsupported jump/branch instruction between ISA modes referenced by " + + toString(Type) + " relocation"); +} + template void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { const endianness E = ELFT::TargetEndianness; + // Detect cross-mode jump/branch and fix instruction. + fixupCrossModeJump(Loc, Type, &Val); + // Thread pointer and DRP offsets from the start of TLS data area. // https://www.linux-mips.org/wiki/NPTL if (Type == R_MIPS_TLS_DTPREL_HI16 || Type == R_MIPS_TLS_DTPREL_LO16 || Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -91,6 +91,22 @@ // understanding of the linker. uint64_t VA = (OutSec ? OutSec->Addr : 0) + IS->getOffset(Offset); + // MIPS relocatable files can mix regular and microMIPS code. + // Linker needs to distinguish such code. For microMIPS absolute + // and section symbols compiler sets the less-significant bit for + // symbol's value or symbol's value plus addend. For global symbols + // it cannot do the same trick because other tools like, for example, + // disassembler wants to know an actual position of the symbol. + // Global symbols get the STO_MIPS_MICROMIPS flag in the `st_other` + // field. + // In the `MIPS::relocateOne()` method we have a symbol value only. + // To pass type of the global symbol (regular/microMIPS) to that + // routine as well as other places where we write a symbol value + // as-is (.dynamic section, `Elf_Ehdr::e_entry` field etc) let's + // set the less-significant bit here. + if (Config->EMachine == EM_MIPS && (Sym.StOther & STO_MIPS_MICROMIPS)) + VA |= 1; + if (D.isTls() && !Config->Relocatable) { if (!Out::TlsPhdr) fatal(toString(D.getFile()) + @@ -175,8 +191,15 @@ uint64_t Symbol::getPltVA() const { if (this->IsInIplt) return InX::Iplt->getVA() + PltIndex * Target->PltEntrySize; - return InX::Plt->getVA() + Target->PltHeaderSize + - PltIndex * Target->PltEntrySize; + uint64_t OutVA = InX::Plt->getVA() + Target->PltHeaderSize + + PltIndex * Target->PltEntrySize; + // We need to catch the case when a regular branch/jump + // instruction has a microMIPS PLT record as a target. + // To pass this information to the MIPS::relocateOne() + // routine set the less-significant bit. + if (Config->EMachine == EM_MIPS && isMicroMips()) + OutVA |= 1; + return OutVA; } uint64_t Symbol::getSize() const { Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -853,8 +853,6 @@ Buf += Config->Wordsize; const Symbol *Sym = SA.first; uint64_t VA = Sym->getVA(SA.second); - if (Sym->StOther & STO_MIPS_MICROMIPS) - VA |= 1; writeUint(Entry, VA); }; std::for_each(std::begin(LocalEntries), std::end(LocalEntries), AddEntry); @@ -1622,12 +1620,17 @@ if (Sym->isInPlt() && Sym->NeedsPltAddr) ESym->st_other |= STO_MIPS_PLT; if (isMicroMips()) { - // Set STO_MIPS_MICROMIPS flag and less-significant bit for - // defined microMIPS symbols and shared symbols with PLT record. if ((Sym->isDefined() && (Sym->StOther & STO_MIPS_MICROMIPS)) || (Sym->isShared() && Sym->NeedsPltAddr)) { - if (StrTabSec.isDynamic()) - ESym->st_value |= 1; + if (!StrTabSec.isDynamic()) + // We already set the less-significant bit for symbols + // marked by the STO_MIPS_MICROMIPS and microMIPS PLT + // records. That allows us to distinguish such symbols + // in the MIPS::relocateOne() routine. Now we + // should clear that bit for non-dynamic symbol table, + // so tools like `objdump` will be able to deal with + // a correct symbol position. + ESym->st_value &= ~1; ESym->st_other |= STO_MIPS_MICROMIPS; } } Index: ELF/Thunks.cpp =================================================================== --- ELF/Thunks.cpp +++ ELF/Thunks.cpp @@ -258,7 +258,7 @@ // Write microMIPS R2-R5 LA25 thunk code // to call PIC function from the non-PIC one. void MicroMipsThunk::writeTo(uint8_t *Buf, ThunkSection &) const { - uint64_t S = Destination.getVA() | 1; + uint64_t S = Destination.getVA(); write16(Buf, 0x41b9, Config->Endianness); // lui $25, %hi(func) write16(Buf + 4, 0xd400, Config->Endianness); // j func write16(Buf + 8, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func) @@ -283,7 +283,7 @@ // Write microMIPS R6 LA25 thunk code // to call PIC function from the non-PIC one. void MicroMipsR6Thunk::writeTo(uint8_t *Buf, ThunkSection &) const { - uint64_t S = Destination.getVA() | 1; + uint64_t S = Destination.getVA(); uint64_t P = ThunkSym->getVA(); write16(Buf, 0x1320, Config->Endianness); // lui $25, %hi(func) write16(Buf + 4, 0x3339, Config->Endianness); // addiu $25, $25, %lo(func) Index: test/ELF/mips-micro-bad-cross-calls.s =================================================================== --- /dev/null +++ test/ELF/mips-micro-bad-cross-calls.s @@ -0,0 +1,16 @@ +# Check error message for invalid cross-mode branch instructions. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %S/Inputs/mips-dynamic.s -o %t2.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t1.o +# RUN: not ld.lld -o %t.exe %t1.o %t2.o 2>&1 | FileCheck %s + +# REQUIRES: mips + +# CHECK: (.text+0x0): unsupported jump/branch instruction between ISA modes referenced by R_MICROMIPS_PC10_S1 relocation + + .text + .set micromips + .global __start +__start: + b16 foo0 Index: test/ELF/mips-micro-cross-calls.s =================================================================== --- /dev/null +++ test/ELF/mips-micro-cross-calls.s @@ -0,0 +1,45 @@ +# Check various cases of microMIPS - regular code cross-calls. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -mattr=micromips %s -o %t-eb.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: -position-independent -mattr=micromips \ +# RUN: %S/Inputs/mips-micro.s -o %t-eb-pic.o +# RUN: ld.lld -o %t-eb.exe %t-eb.o %t-eb-pic.o +# RUN: llvm-objdump -d -mattr=-micromips %t-eb.exe \ +# RUN: | FileCheck --check-prefix=REG %s +# RUN: llvm-objdump -d -mattr=+micromips %t-eb.exe \ +# RUN: | FileCheck --check-prefix=MICRO %s + +# REQUIRES: mips + +# REG: __start: +# REG-NEXT: 20000: 74 00 80 04 jalx 131088 +# REG-NEXT: 20004: 00 00 00 00 nop +# REG-NEXT: 20008: 74 00 80 08 jalx 131104 <__microLA25Thunk_foo> + +# REG: __LA25Thunk_bar: +# REG-NEXT: 20030: 3c 19 00 02 lui $25, 2 +# REG-NEXT: 20034: 08 00 80 11 j 131140 + +# MICRO: micro: +# MICRO-NEXT: 20010: f0 00 80 00 jalx 65536 +# MICRO-NEXT: 20014: 00 00 00 00 nop +# MICRO-NEXT: 20018: f0 00 80 0c jalx 65560 + +# MICRO: __microLA25Thunk_foo: +# MICRO-NEXT: 20020: 41 b9 00 02 lui $25, 2 +# MICRO-NEXT: 20024: d4 01 00 20 j 131136 + + .text + .set nomicromips + .global __start +__start: + jal micro + jal foo + + .set micromips + .global micro +micro: + jal __start + jal bar Index: test/ELF/mips-micro-plt.s =================================================================== --- test/ELF/mips-micro-plt.s +++ test/ELF/mips-micro-plt.s @@ -7,13 +7,14 @@ # RUN: -mattr=micromips %s -o %t-exe.o # RUN: ld.lld %t-exe.o %t.so -o %t.exe # RUN: llvm-readobj -t -dt -mips-plt-got %t.exe | FileCheck %s +# RUN: llvm-objdump -d -mattr=micromips %t.exe | FileCheck --check-prefix=ASM %s # REQUIRES: mips # CHECK: Symbols [ # CHECK: Symbol { # CHECK: Name: foo -# CHECK-NEXT: Value: 0x20008 +# CHECK-NEXT: Value: 0x20010 # CHECK-NEXT: Size: # CHECK-NEXT: Binding: Local # CHECK-NEXT: Type: None @@ -36,22 +37,28 @@ # CHECK-NEXT: } # CHECK: Symbol { # CHECK: Name: foo0 -# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Value: 0x20040 # CHECK-NEXT: Size: # CHECK-NEXT: Binding: Global # CHECK-NEXT: Type: Function -# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Other [ +# CHECK-NEXT: STO_MIPS_MICROMIPS +# CHECK-NEXT: STO_MIPS_PLT +# CHECK-NEXT: ] # CHECK-NEXT: Section: Undefined # CHECK-NEXT: } # CHECK-NEXT: ] # CHECK: DynamicSymbols [ # CHECK: Symbol { # CHECK: Name: foo0 -# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Value: 0x20041 # CHECK-NEXT: Size: # CHECK-NEXT: Binding: Global # CHECK-NEXT: Type: Function -# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Other [ +# CHECK-NEXT: STO_MIPS_MICROMIPS +# CHECK-NEXT: STO_MIPS_PLT +# CHECK-NEXT: ] # CHECK-NEXT: Section: Undefined # CHECK-NEXT: } # CHECK-NEXT: ] @@ -61,7 +68,7 @@ # CHECK-NEXT: Entry { # CHECK-NEXT: Address: # CHECK-NEXT: Access: -# CHECK-NEXT: Initial: 0x20009 +# CHECK-NEXT: Initial: 0x20011 # CHECK-NEXT: } # CHECK: ] # CHECK: } @@ -70,8 +77,8 @@ # CHECK: Entries [ # CHECK-NEXT: Entry { # CHECK-NEXT: Address: -# CHECK-NEXT: Initial: 0x20011 -# CHECK-NEXT: Value: 0x0 +# CHECK-NEXT: Initial: 0x20021 +# CHECK-NEXT: Value: 0x20041 # CHECK-NEXT: Type: Function # CHECK-NEXT: Section: Undefined # CHECK-NEXT: Name: foo0@ @@ -79,6 +86,15 @@ # CHECK-NEXT: ] # CHECK-NEXT: } +# ASM: __start: +# ASM-NEXT: 20000: fd 1c 80 18 lw $8, -32744($gp) +# ASM-NEXT: 20004: 11 08 00 11 addi $8, $8, 17 +# ASM-NEXT: 20008: 41 a8 00 02 lui $8, 2 +# ASM-NEXT: 2000c: 11 08 00 41 addi $8, $8, 65 +# +# ASM: foo: +# ASM-NEXT: 20010: f4 01 00 20 jal 131136 + .text .set micromips .global foo @@ -87,5 +103,7 @@ __start: lw $t0,%got(foo)($gp) addi $t0,$t0,%lo(foo) + lui $t0,%hi(foo0) + addi $t0,$t0,%lo(foo0) foo: jal foo0