summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Arch/Mips.cpp62
-rw-r--r--lld/ELF/Symbols.cpp22
-rw-r--r--lld/ELF/SyntheticSections.cpp20
-rw-r--r--lld/test/ELF/mips-micro-bad-cross-calls.s15
-rw-r--r--lld/test/ELF/mips-micro-cross-calls.s44
-rw-r--r--lld/test/ELF/mips-micro-plt.s4
-rw-r--r--lld/test/ELF/mips-micro-relocs.s33
7 files changed, 182 insertions, 18 deletions
diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp
index 4bce29d02e1..8a35737ac3d 100644
--- a/lld/ELF/Arch/Mips.cpp
+++ b/lld/ELF/Arch/Mips.cpp
@@ -460,6 +460,65 @@ calculateMipsRelChain(uint8_t *Loc, RelType Type, uint64_t Val) {
return std::make_pair(Type & 0xff, Val);
}
+static bool isBranchReloc(RelType Type) {
+ return Type == R_MIPS_26 || Type == R_MIPS_PC26_S2 ||
+ Type == R_MIPS_PC21_S2 || Type == R_MIPS_PC16;
+}
+
+static bool isMicroBranchReloc(RelType Type) {
+ return Type == R_MICROMIPS_26_S1 || Type == R_MICROMIPS_PC16_S1 ||
+ Type == R_MICROMIPS_PC10_S1 || Type == R_MICROMIPS_PC7_S1;
+}
+
+template <class ELFT>
+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<E>(Loc) >> 26;
+ if (Inst == 0x3 || Inst == 0x1d) { // JAL or JALX
+ writeValue<E>(Loc, 0x1d << 26, 32, 0);
+ return Val;
+ }
+ break;
+ }
+ case R_MICROMIPS_26_S1: {
+ uint32_t Inst = readShuffle<E>(Loc) >> 26;
+ if (Inst == 0x3d || Inst == 0x3c) { // JAL32 or JALX32
+ Val >>= 1;
+ writeShuffleValue<E>(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 <class ELFT>
void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
const endianness E = ELFT::TargetEndianness;
@@ -467,6 +526,9 @@ void MIPS<ELFT>::relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const {
if (ELFT::Is64Bits || Config->MipsN32Abi)
std::tie(Type, Val) = calculateMipsRelChain(Loc, Type, Val);
+ // Detect cross-mode jump/branch and fix instruction.
+ Val = fixupCrossModeJump<ELFT>(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 ||
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 96e23b9d2a5..a1986d8df55 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -89,6 +89,19 @@ static uint64_t getSymVA(const Symbol &Sym, int64_t &Addend) {
// 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
@@ -149,7 +162,14 @@ uint64_t Symbol::getPPC64LongBranchOffset() const {
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 {
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 487a5718c9e..98894bf44d0 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -1038,11 +1038,8 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
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.
@@ -2052,14 +2049,17 @@ template <class ELFT> void SymbolTableSection<ELFT>::writeTo(uint8_t *Buf) {
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<ELFT>::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;
}
}
diff --git a/lld/test/ELF/mips-micro-bad-cross-calls.s b/lld/test/ELF/mips-micro-bad-cross-calls.s
new file mode 100644
index 00000000000..63f7e407ed8
--- /dev/null
+++ b/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
diff --git a/lld/test/ELF/mips-micro-cross-calls.s b/lld/test/ELF/mips-micro-cross-calls.s
new file mode 100644
index 00000000000..fc9dd0aeb8e
--- /dev/null
+++ b/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 <micro>
+# 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 <bar>
+
+# 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
diff --git a/lld/test/ELF/mips-micro-plt.s b/lld/test/ELF/mips-micro-plt.s
index 24e90ae49a8..63f50fc74e0 100644
--- a/lld/test/ELF/mips-micro-plt.s
+++ b/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
diff --git a/lld/test/ELF/mips-micro-relocs.s b/lld/test/ELF/mips-micro-relocs.s
index b539aa94676..c6850fcca96 100644
--- a/lld/test/ELF/mips-micro-relocs.s
+++ b/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 @@ __start:
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
OpenPOWER on IntegriCloud