Index: lld/ELF/Arch/Mips.cpp =================================================================== --- lld/ELF/Arch/Mips.cpp +++ lld/ELF/Arch/Mips.cpp @@ -458,6 +458,79 @@ return std::make_pair(Type & 0xff, Val); } +static bool isBranchReloc(RelType Type) { + switch (Type) { + case R_MIPS_26: + case R_MIPS_PC26_S2: + case R_MIPS_PC21_S2: + case R_MIPS_PC16: + return true; + default: + return false; + } +} + +static bool isMicroBranchReloc(RelType Type) { + switch (Type) { + case R_MICROMIPS_26_S1: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + return true; + default: + return false; + } +} + +template +static uint64_t fixupCrossModeJump(uint8_t *Loc, RelType Type, uint64_t Val) { + // Here we need to detect jump/branch from regular MIPS code + // to a microMIPS target and vice versa. In that cases jump + // instructions need to be replaced by their "cross-mode" + // equivalents. + const endianness E = ELFT::TargetEndianness; + bool IsMicroTgt = Val & 0x1; + bool IsCrossJump = (IsMicroTgt && isBranchReloc(Type)) || + (!IsMicroTgt && isMicroBranchReloc(Type)); + if (!IsCrossJump) + return Val; + + switch (Type) { + case R_MIPS_26: { + uint32_t Inst = read32(Loc) >> 26; + if (Inst == 0x3 || Inst == 0x1d) { // JAL or JALX + writeValue(Loc, 0x1d << 26, 32, 0); + return Val; + } + break; + } + case R_MICROMIPS_26_S1: { + uint32_t Inst = readShuffle(Loc) >> 26; + if (Inst == 0x3d || Inst == 0x3c) { // JAL32 or JALX32 + Val >>= 1; + writeShuffleValue(Loc, 0x3c << 26, 32, 0); + return Val; + } + 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"); + return Val; +} + template void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { const endianness E = ELFT::TargetEndianness; @@ -465,6 +538,9 @@ if (ELFT::Is64Bits || Config->MipsN32Abi) std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); + // Detect cross-mode jump/branch and fix instruction. + Val = 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: lld/ELF/Symbols.cpp =================================================================== --- lld/ELF/Symbols.cpp +++ lld/ELF/Symbols.cpp @@ -89,6 +89,19 @@ // understanding of the linker. uint64_t VA = IS->getVA(Offset); + // MIPS relocatable files can mix regular and microMIPS code. + // Linker needs to distinguish such code. To do so microMIPS + // symbols has the `STO_MIPS_MICROMIPS` flag in the `st_other` + // field. Unfortunately, the `MIPS::relocateOne()` method has + // a symbol value only. To pass type of the 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) do the same trick as compiler uses to mark microMIPS + // for CPU - set the less-significant bit. + if (Config->EMachine == EM_MIPS && isMicroMips() && + ((Sym.StOther & STO_MIPS_MICROMIPS) || Sym.NeedsPltAddr)) + VA |= 1; + if (D.isTls() && !Config->Relocatable) { // Use the address of the TLS segment's first section rather than the // segment's address, because segment addresses aren't initialized until @@ -145,7 +158,14 @@ uint64_t Symbol::getPltVA() const { PltSection *Plt = IsInIplt ? In.Iplt : In.Plt; - return Plt->getVA() + Plt->HeaderSize + PltIndex * Target->PltEntrySize; + uint64_t OutVA = + Plt->getVA() + Plt->HeaderSize + PltIndex * Target->PltEntrySize; + // While linking microMIPS code PLT code are always microMIPS + // code. Set the less-significant bit to track that fact. + // See detailed comment in the `getSymVA` function. + if (Config->EMachine == EM_MIPS && isMicroMips()) + OutVA |= 1; + return OutVA; } uint64_t Symbol::getPPC64LongBranchTableVA() const { Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -1038,11 +1038,8 @@ for (const FileGot &G : Gots) { auto Write = [&](size_t I, const Symbol *S, int64_t A) { uint64_t VA = A; - if (S) { + if (S) VA = S->getVA(A); - if (S->StOther & STO_MIPS_MICROMIPS) - VA |= 1; - } writeUint(Buf + I * Config->Wordsize, VA); }; // Write 'page address' entries to the local part of the GOT. @@ -2048,14 +2045,17 @@ if (Sym->isInPlt() && Sym->NeedsPltAddr) ESym->st_other |= STO_MIPS_PLT; if (isMicroMips()) { - // Set STO_MIPS_MICROMIPS flag and less-significant bit for - // a defined microMIPS symbol and symbol should point to its - // PLT entry (in case of microMIPS, PLT entries always contain - // microMIPS code). + // We already set the less-significant bit for symbols + // marked by the `STO_MIPS_MICROMIPS` flag and for 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. if (Sym->isDefined() && ((Sym->StOther & STO_MIPS_MICROMIPS) || Sym->NeedsPltAddr)) { - if (StrTabSec.isDynamic()) - ESym->st_value |= 1; + if (!StrTabSec.isDynamic()) + ESym->st_value &= ~1; ESym->st_other |= STO_MIPS_MICROMIPS; } } Index: lld/test/ELF/mips-micro-bad-cross-calls.s =================================================================== --- /dev/null +++ lld/test/ELF/mips-micro-bad-cross-calls.s @@ -0,0 +1,15 @@ +# REQUIRES: mips +# 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 + +# 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: lld/test/ELF/mips-micro-cross-calls.s =================================================================== --- /dev/null +++ lld/test/ELF/mips-micro-cross-calls.s @@ -0,0 +1,44 @@ +# REQUIRES: mips +# 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 + +# 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: lld/test/ELF/mips-micro-plt.s =================================================================== --- lld/test/ELF/mips-micro-plt.s +++ lld/test/ELF/mips-micro-plt.s @@ -87,9 +87,9 @@ # ASM: __start: # ASM-NEXT: 20000: fd 1c 80 18 lw $8, -32744($gp) -# ASM-NEXT: 20004: 11 08 00 10 addi $8, $8, 16 +# 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 40 addi $8, $8, 64 +# ASM-NEXT: 2000c: 11 08 00 41 addi $8, $8, 65 # # ASM: foo: # ASM-NEXT: 20010: f4 01 00 20 jal 131136 Index: lld/test/ELF/mips-micro-relocs.s =================================================================== --- lld/test/ELF/mips-micro-relocs.s +++ lld/test/ELF/mips-micro-relocs.s @@ -6,20 +6,22 @@ # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ # RUN: -mattr=micromips %s -o %t2eb.o # RUN: ld.lld -o %teb.exe %t1eb.o %t2eb.o -# RUN: llvm-objdump -d -t -mattr=micromips %teb.exe \ +# RUN: llvm-objdump -d -t -s -mattr=micromips %teb.exe \ # RUN: | FileCheck --check-prefixes=EB,SYM %s +# RUN: llvm-readobj -h %teb.exe | FileCheck --check-prefix=ELF %s # RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \ # RUN: -mattr=micromips %S/Inputs/mips-micro.s -o %t1el.o # RUN: llvm-mc -filetype=obj -triple=mipsel-unknown-linux \ # RUN: -mattr=micromips %s -o %t2el.o # RUN: ld.lld -o %tel.exe %t1el.o %t2el.o -# RUN: llvm-objdump -d -t -mattr=micromips %tel.exe \ +# RUN: llvm-objdump -d -t -s -mattr=micromips %tel.exe \ # RUN: | FileCheck --check-prefixes=EL,SYM %s +# RUN: llvm-readobj -h %tel.exe | FileCheck --check-prefix=ELF %s # EB: __start: # EB-NEXT: 20010: 41 a3 00 01 lui $3, 1 -# EB-NEXT: 20014: 30 63 7f df addiu $3, $3, 32735 +# EB-NEXT: 20014: 30 63 7f ef addiu $3, $3, 32751 # EB-NEXT: 20018: fc 7c 80 18 lw $3, -32744($gp) # EB-NEXT: 2001c: fc 63 80 18 lw $3, -32744($3) # EB-NEXT: 20020: 8f 70 beqz16 $6, -32 @@ -28,9 +30,15 @@ # EB-NEXT: 20028: 00 00 00 00 nop # EB-NEXT: 2002c: 94 00 ff e8 b -44 +# EB: Contents of section .data: +# EB-NEXT: 30000 fffe8011 + +# EB: Contents of section .debug_info +# EB-NEXT: 0000 00020011 + # EL: __start: # EL-NEXT: 20010: a3 41 01 00 lui $3, 1 -# EL-NEXT: 20014: 63 30 df 7f addiu $3, $3, 32735 +# EL-NEXT: 20014: 63 30 ef 7f addiu $3, $3, 32751 # EL-NEXT: 20018: 7c fc 18 80 lw $3, -32744($gp) # EL-NEXT: 2001c: 63 fc 18 80 lw $3, -32744($3) # EL-NEXT: 20020: 70 8f beqz16 $6, -32 @@ -39,10 +47,19 @@ # EL-NEXT: 20028: 00 00 00 00 nop # EL-NEXT: 2002c: 00 94 e8 ff b -44 -# SYM: 00037ff0 .got 00000000 .hidden _gp +# EL: Contents of section .data: +# EL-NEXT: 30000 1180feff + +# EL: Contents of section .debug_info +# EL-NEXT: 0000 11000200 + +# SYM: 00038000 .got 00000000 .hidden _gp # SYM: 00020000 g F .text 00000000 foo # SYM: 00020010 .text 00000000 __start +# ELF: ElfHeader { +# ELF: Entry: 0x20011 + .text .set micromips .global __start @@ -56,3 +73,9 @@ beqz16 $6, foo # R_MICROMIPS_PC7_S1 b16 foo # R_MICROMIPS_PC10_S1 b foo # R_MICROMIPS_PC16_S1 + + .data + .gpword __start # R_MIPS_GPREL32 + + .section .debug_info + .word __start # R_MIPS_32