Index: ELF/Arch/Mips.cpp =================================================================== --- ELF/Arch/Mips.cpp +++ ELF/Arch/Mips.cpp @@ -459,6 +459,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; @@ -466,6 +539,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: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -764,6 +764,42 @@ } } +// Set the less-significant bit for a microMIPS symbol. This bit is used +// to mark a microMIPS symbol when there is no information about symbol +// table entry related to this symbol. The MIPS::relocateOne function gets +// a symbol's value only, but needs to adjust a jump/branch instruction +// if the jump crosses CPU mode boundaries, i.e. execution passes from +// regular to microMIPS code or vice versa. +static uint64_t setMicroMipsBit(RelExpr Expr, Symbol &Sym, uint64_t VA) { + switch (Expr) { + case R_ABS: + case R_MIPS_GOTREL: + case R_PC: + if ((Sym.StOther & STO_MIPS_MICROMIPS) || + (isMicroMips() && Sym.NeedsPltAddr)) + VA |= 1; + break; + case R_PLT: + case R_PLT_PC: + // The best solution is to create a regular PLT entries for regular code + // and mciroMIPS PLT entries for microMIPS code. If the same routine is + // called by both regular and microMIPS code, we have to create two PLT + // entries. That solution minimizes cross-mode jumps. Now LLD implements + // more simple solution. If the file uses microMIPS code, all PLT entries + // are microMIPS. Otherwise all entries are regular. That works fine for + // case when all code is either regular or microMIPS, but increase number + // of cross-mode jumps for mixed code. And that's why the less-significant + // bit is always raised for a R_PLTxxx relocation or a symbol requires + // PLT address. + if (isMicroMips()) + VA |= 1; + break; + default: + break; + } + return VA; +} + // This function applies relocations to sections without SHF_ALLOC bit. // Such sections are never mapped to memory at runtime. Debug sections are // an example. Relocations in non-alloc sections are much easier to @@ -820,8 +856,12 @@ if (Sym.isTls() && !Out::TlsPhdr) Target->relocateOne(BufLoc, Type, 0); - else - Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend))); + else { + uint64_t TargetVA = SignExtend64(Sym.getVA(Addend)); + if (Config->EMachine == EM_MIPS) + TargetVA = setMicroMipsBit(Expr, Sym, TargetVA); + Target->relocateOne(BufLoc, Type, TargetVA); + } } } @@ -919,6 +959,8 @@ Target->relocateOne(BufLoc, Type, TargetVA); break; default: + if (Config->EMachine == EM_MIPS) + TargetVA = setMicroMipsBit(Expr, *Rel.Sym, TargetVA); Target->relocateOne(BufLoc, Type, TargetVA); break; } Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1252,7 +1252,11 @@ template void DynamicSection::addSym(int32_t Tag, Symbol *Sym) { - Entries.push_back({Tag, [=] { return Sym->getVA(); }}); + if (Config->EMachine == EM_MIPS && (Sym->StOther & STO_MIPS_MICROMIPS)) + // Set the less-significant bit for a microMIPS symbol. + Entries.push_back({Tag, [=] { return Sym->getVA() | 1; }}); + else + Entries.push_back({Tag, [=] { return Sym->getVA(); }}); } // A Linker script may assign the RELA relocation sections to the same Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -2287,8 +2287,15 @@ // 6. the address 0. static uint64_t getEntryAddr() { // Case 1, 2 or 3 - if (Symbol *B = Symtab->find(Config->Entry)) + if (Symbol *B = Symtab->find(Config->Entry)) { + // Set the less-significant bit for a microMIPS symbol. + // When loader reads this entry, it will know that the symbol + // is microMIPS and adjust a jump instruction appropriately + // to handle possible cross-mode (regular-to-microMIPS) jump. + if (Config->EMachine == EM_MIPS && (B->StOther & STO_MIPS_MICROMIPS)) + return B->getVA() | 1; return B->getVA(); + } // Case 4 uint64_t Addr; 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 @@ -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: test/ELF/mips-micro-relocs.s =================================================================== --- test/ELF/mips-micro-relocs.s +++ 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