Index: ELF/Arch/Mips.cpp =================================================================== --- ELF/Arch/Mips.cpp +++ ELF/Arch/Mips.cpp @@ -463,10 +463,85 @@ 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 void 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; + + 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/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -679,10 +679,16 @@ return; } - if (Sym.isTls() && !Out::TlsPhdr) - Target->relocateOne(BufLoc, Type, 0); - else - Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend))); + uint64_t Val = (Sym.isTls() && !Out::TlsPhdr) + ? 0 + : SignExtend64(Sym.getVA(Addend)); + + // Set the less-significant bit for microMIPS symbols + // to keep this information for a reader of this value. + if (Config->EMachine == EM_MIPS && (Sym.StOther & STO_MIPS_MICROMIPS)) + Val |= 1; + + Target->relocateOne(BufLoc, Type, Val); } } @@ -714,6 +720,30 @@ uint64_t TargetVA = SignExtend64( getRelocTargetVA(Type, Rel.Addend, AddrLoc, *Rel.Sym, Expr), Bits); + if (Config->EMachine == EM_MIPS) { + // We have to set the less-significant bit for microMIPS symbols + // or for addresses of PLT records. + bool SetMicroMipsBit = false; + switch (Expr) { + case R_ABS: + case R_PC: + SetMicroMipsBit = (Rel.Sym->StOther & STO_MIPS_MICROMIPS) || + (isMicroMips() && Rel.Sym->NeedsPltAddr); + break; + case R_MIPS_GOTREL: + SetMicroMipsBit = Rel.Sym->StOther & STO_MIPS_MICROMIPS; + break; + case R_PLT: + case R_PLT_PC: + SetMicroMipsBit = isMicroMips(); + break; + default: + break; + } + if (SetMicroMipsBit) + TargetVA |= 1; + } + switch (Expr) { case R_RELAX_GOT_PC: case R_RELAX_GOT_PC_NOPIC: Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1021,7 +1021,15 @@ template void DynamicSection::addSym(int32_t Tag, Symbol *Sym) { - Entries.push_back({Tag, [=] { return Sym->getVA(); }}); + Entries.push_back({Tag, [=] { + if (Config->EMachine == EM_MIPS && + (Sym->StOther & STO_MIPS_MICROMIPS)) + // Set the less-significant bit for microMIPS + // symbols to keep this information for a reader + // of this value. + return Sym->getVA() | 1; + return Sym->getVA(); + }}); } // Add remaining entries to complete .dynamic contents. Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1880,8 +1880,13 @@ // 6. the address 0. template uint64_t Writer::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 if the entry is a microMIPS symbol. + // That allows loader to handle such symbol properly. + 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 @@ -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 Index: test/ELF/mips-micro-relocs.s =================================================================== --- test/ELF/mips-micro-relocs.s +++ test/ELF/mips-micro-relocs.s @@ -5,7 +5,7 @@ # 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-mc -filetype=obj -triple=mipsel-unknown-linux \ @@ -13,14 +13,14 @@ # 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 # REQUIRES: mips # 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 @@ -29,9 +29,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 @@ -40,7 +46,13 @@ # 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 @@ -57,3 +69,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