Index: ELF/Arch/AArch64.cpp =================================================================== --- ELF/Arch/AArch64.cpp +++ ELF/Arch/AArch64.cpp @@ -43,7 +43,8 @@ uint64_t BranchAddr, const Symbol &S) const override; bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; bool usesOnlyLowPageBits(RelType Type) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; void relaxTlsGdToLe(uint8_t *Loc, RelType Type, uint64_t Val) const override; @@ -243,7 +244,8 @@ or32le(L, (Imm & 0xFFF) << 10); } -void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AArch64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_AARCH64_ABS16: case R_AARCH64_PREL16: Index: ELF/Arch/AMDGPU.cpp =================================================================== --- ELF/Arch/AMDGPU.cpp +++ ELF/Arch/AMDGPU.cpp @@ -26,7 +26,8 @@ public: AMDGPU(); uint32_t calcEFlags() const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; }; @@ -56,7 +57,8 @@ return Ret; } -void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AMDGPU::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_AMDGPU_ABS32: case R_AMDGPU_GOTPCREL: Index: ELF/Arch/ARM.cpp =================================================================== --- ELF/Arch/ARM.cpp +++ ELF/Arch/ARM.cpp @@ -41,7 +41,8 @@ bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; bool inBranchRange(RelType Type, uint64_t Src, uint64_t Dst) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; }; } // namespace @@ -358,7 +359,8 @@ return Distance <= Range; } -void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void ARM::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_ARM_ABS32: case R_ARM_BASE_PREL: Index: ELF/Arch/AVR.cpp =================================================================== --- ELF/Arch/AVR.cpp +++ ELF/Arch/AVR.cpp @@ -45,7 +45,8 @@ public: RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; }; } // namespace @@ -54,7 +55,8 @@ return R_ABS; } -void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void AVR::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_AVR_CALL: { uint16_t Hi = Val >> 17; Index: ELF/Arch/Mips.cpp =================================================================== --- ELF/Arch/Mips.cpp +++ ELF/Arch/Mips.cpp @@ -39,7 +39,8 @@ int32_t Index, unsigned RelOff) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; bool usesOnlyLowPageBits(RelType Type) const override; }; } // namespace @@ -465,13 +466,126 @@ 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_PC26_S1: + case R_MICROMIPS_PC16_S1: + case R_MICROMIPS_PC10_S1: + case R_MICROMIPS_PC7_S1: + return true; + default: + return false; + } +} + template -void MIPS::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +static uint64_t fixupCrossModeJump(uint8_t *Loc, RelType Type, uint64_t Val, + bool IsMicroTgt) { + // 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 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 Symbol *S) const { const endianness E = ELFT::TargetEndianness; if (ELFT::Is64Bits || Config->MipsN32Abi) std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val); + if (S) { + // MIPS processors use different instructions to jump / branch between + // between the same (regulat-to-regular or microMIPS-to_microMIPS) or + // mixed (regular-to-microMIPS or microMIPS-to_regular) code. When we + // write a symbols's value we need to keep information about type of + // the symbol: regular or microMIPS to allow a reader of this value + // (dynamic linker, loader, debugger etc) will be able to adjust a jump + // / branch instruction properly. To do so we set the less-significant + // bit for microMIPS symbols. + + // We consider a symbol as a microMIPS one if it has the STO_MIPS_MICROMIPS + // flag or if its value is an address of the corresponding PLT entry. + // The best solution is to create a regular PLT entry for a regular symbol + // and a mciroMIPS PLT entry for a microMIPS symbol. But in that case if + // the same routine is called by both regular and microMIPS code, we should + // 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. + bool IsMicroTgt = + (S->StOther & STO_MIPS_MICROMIPS) || (S->isInPlt() && isMicroMips()); + if (IsMicroTgt) { + switch (getRelExpr(Type, *S, Loc)) { + case R_ABS: + case R_MIPS_GOTREL: + case R_PC: + Val |= 1; + break; + default: + break; + } + } + + // Detect cross-mode jump/branch and fix instruction. + Val = fixupCrossModeJump(Loc, Type, Val, IsMicroTgt); + } + // 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/Arch/PPC.cpp =================================================================== --- ELF/Arch/PPC.cpp +++ ELF/Arch/PPC.cpp @@ -22,7 +22,8 @@ class PPC final : public TargetInfo { public: PPC(); - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; }; @@ -46,7 +47,8 @@ } } -void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_PPC_ADDR16_HA: write16be(Loc, (Val + 0x8000) >> 16); Index: ELF/Arch/PPC64.cpp =================================================================== --- ELF/Arch/PPC64.cpp +++ ELF/Arch/PPC64.cpp @@ -43,7 +43,8 @@ uint32_t calcEFlags() const override; RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; void writeGotHeader(uint8_t *Buf) const override; bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File, uint64_t BranchAddr, const Symbol &S) const override; @@ -190,7 +191,8 @@ } } -void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void PPC64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { // For a TOC-relative relocation, proceed in terms of the corresponding // ADDR16 relocation type. std::tie(Type, Val) = toAddr16Rel(Type, Val); Index: ELF/Arch/SPARCV9.cpp =================================================================== --- ELF/Arch/SPARCV9.cpp +++ ELF/Arch/SPARCV9.cpp @@ -28,7 +28,8 @@ const uint8_t *Loc) const override; void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; }; } // namespace @@ -72,7 +73,8 @@ } } -void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void SPARCV9::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_SPARC_32: case R_SPARC_UA32: Index: ELF/Arch/X86.cpp =================================================================== --- ELF/Arch/X86.cpp +++ ELF/Arch/X86.cpp @@ -34,7 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; @@ -249,7 +250,8 @@ } } -void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_386_8: // R_386_{PC,}{8,16} are not part of the i386 psABI, but they are Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -34,7 +34,8 @@ void writePltHeader(uint8_t *Buf) const override; void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr, int32_t Index, unsigned RelOff) const override; - void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override; + void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const override; RelExpr adjustRelaxExpr(RelType Type, const uint8_t *Data, RelExpr Expr) const override; @@ -283,7 +284,8 @@ } template -void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const { +void X86_64::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S) const { switch (Type) { case R_X86_64_8: checkUInt(Loc, Val, 8, Type); Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -685,7 +685,8 @@ if (Sym.isTls() && !Out::TlsPhdr) Target->relocateOne(BufLoc, Type, 0); else - Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend))); + Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend)), + &Sym); } } @@ -752,7 +753,7 @@ Target->relocateOne(BufLoc, Type, TargetVA); break; default: - Target->relocateOne(BufLoc, Type, TargetVA); + Target->relocateOne(BufLoc, Type, TargetVA, Rel.Sym); break; } } Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -1036,7 +1036,15 @@ 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. + // When loader / dynamic linker reads this tag, it will + // know that the symbol is microMIPS and adjust a jump + // instruction appropriately to handle possible cross-mode + // (regular-to-microMIPS) jump. + Entries.push_back({Tag, [=] { return Sym->getVA() | 1; }}); + else + Entries.push_back({Tag, [=] { return Sym->getVA(); }}); } // Add remaining entries to complete .dynamic contents. Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -65,7 +65,8 @@ virtual RelExpr getRelExpr(RelType Type, const Symbol &S, const uint8_t *Loc) const = 0; - virtual void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const = 0; + virtual void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val, + const Symbol *S = nullptr) const = 0; virtual ~TargetInfo(); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -2134,8 +2134,15 @@ // 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 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 @@ -88,9 +88,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 @@ -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