[lld] r354311 - [MIPS] Handle cross-mode (regular <-> microMIPS) jumps

Simon Atanasyan via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 19 02:36:58 PST 2019


Author: atanasyan
Date: Tue Feb 19 02:36:58 2019
New Revision: 354311

URL: http://llvm.org/viewvc/llvm-project?rev=354311&view=rev
Log:
[MIPS] Handle cross-mode (regular <-> microMIPS) jumps

The patch solves two tasks:

1. MIPS ABI allows to mix regular and microMIPS code and perform
cross-mode jumps. Linker needs to detect such cases and replace
jump/branch instructions by their cross-mode equivalents.

2. Other tools like dunamic linkers need to recognize cases when dynamic
table entries, e_entry field of an ELF header etc point to microMIPS
symbol. Linker should provide such information.

The first task is implemented in the `MIPS<ELFT>::relocateOne()` method.
New routine `fixupCrossModeJump` detects ISA mode change, checks and
replaces an instruction.

The main problem is how to recognize that relocation target is microMIPS
symbol. For absolute and section symbols compiler or assembler set the
less-significant bit of the symbol's value or sum of the symbol's value
and addend. And this bit signals to linker about microMIPS code. For
global symbols compiler cannot do the same trick because other tools like,
for example, disassembler wants to know an actual position of the symbol.
So compiler sets STO_MIPS_MICROMIPS flag in the `st_other` field.

In `MIPS<ELFT>::relocateOne()` method we have a symbol's value only and
cannot access any symbol's attributes. 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) we set when necessary a less-significant bit in the `getSymVA`
function.

Differential revision: https://reviews.llvm.org/D40147

Added:
    lld/trunk/test/ELF/mips-micro-bad-cross-calls.s
    lld/trunk/test/ELF/mips-micro-cross-calls.s
Modified:
    lld/trunk/ELF/Arch/Mips.cpp
    lld/trunk/ELF/Symbols.cpp
    lld/trunk/ELF/SyntheticSections.cpp
    lld/trunk/test/ELF/mips-micro-plt.s
    lld/trunk/test/ELF/mips-micro-relocs.s

Modified: lld/trunk/ELF/Arch/Mips.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Arch/Mips.cpp?rev=354311&r1=354310&r2=354311&view=diff
==============================================================================
--- lld/trunk/ELF/Arch/Mips.cpp (original)
+++ lld/trunk/ELF/Arch/Mips.cpp Tue Feb 19 02:36:58 2019
@@ -460,6 +460,65 @@ calculateMipsRelChain(uint8_t *Loc, RelT
   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 *Lo
   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 ||

Modified: lld/trunk/ELF/Symbols.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Symbols.cpp?rev=354311&r1=354310&r2=354311&view=diff
==============================================================================
--- lld/trunk/ELF/Symbols.cpp (original)
+++ lld/trunk/ELF/Symbols.cpp Tue Feb 19 02:36:58 2019
@@ -89,6 +89,19 @@ static uint64_t getSymVA(const Symbol &S
     // 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::getPPC64LongBranchOffse
 
 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 {

Modified: lld/trunk/ELF/SyntheticSections.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/SyntheticSections.cpp?rev=354311&r1=354310&r2=354311&view=diff
==============================================================================
--- lld/trunk/ELF/SyntheticSections.cpp (original)
+++ lld/trunk/ELF/SyntheticSections.cpp Tue Feb 19 02:36:58 2019
@@ -1038,11 +1038,8 @@ void MipsGotSection::writeTo(uint8_t *Bu
   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 SymbolTableSe
       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;
         }
       }

Added: lld/trunk/test/ELF/mips-micro-bad-cross-calls.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-micro-bad-cross-calls.s?rev=354311&view=auto
==============================================================================
--- lld/trunk/test/ELF/mips-micro-bad-cross-calls.s (added)
+++ lld/trunk/test/ELF/mips-micro-bad-cross-calls.s Tue Feb 19 02:36:58 2019
@@ -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

Added: lld/trunk/test/ELF/mips-micro-cross-calls.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-micro-cross-calls.s?rev=354311&view=auto
==============================================================================
--- lld/trunk/test/ELF/mips-micro-cross-calls.s (added)
+++ lld/trunk/test/ELF/mips-micro-cross-calls.s Tue Feb 19 02:36:58 2019
@@ -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

Modified: lld/trunk/test/ELF/mips-micro-plt.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-micro-plt.s?rev=354311&r1=354310&r2=354311&view=diff
==============================================================================
--- lld/trunk/test/ELF/mips-micro-plt.s (original)
+++ lld/trunk/test/ELF/mips-micro-plt.s Tue Feb 19 02:36:58 2019
@@ -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

Modified: lld/trunk/test/ELF/mips-micro-relocs.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/mips-micro-relocs.s?rev=354311&r1=354310&r2=354311&view=diff
==============================================================================
--- lld/trunk/test/ELF/mips-micro-relocs.s (original)
+++ lld/trunk/test/ELF/mips-micro-relocs.s Tue Feb 19 02:36:58 2019
@@ -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




More information about the llvm-commits mailing list