[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

git-svn-id: https://llvm.org/svn/llvm-project/lld/trunk@354311 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/ELF/Arch/Mips.cpp b/ELF/Arch/Mips.cpp
index 4bce29d..8a35737 100644
--- a/ELF/Arch/Mips.cpp
+++ b/ELF/Arch/Mips.cpp
@@ -460,6 +460,65 @@
   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 @@
   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/ELF/Symbols.cpp b/ELF/Symbols.cpp
index 96e23b9..a1986d8 100644
--- a/ELF/Symbols.cpp
+++ b/ELF/Symbols.cpp
@@ -89,6 +89,19 @@
     // 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::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/ELF/SyntheticSections.cpp b/ELF/SyntheticSections.cpp
index 487a571..98894bf 100644
--- a/ELF/SyntheticSections.cpp
+++ b/ELF/SyntheticSections.cpp
@@ -1038,11 +1038,8 @@
   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 @@
       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/test/ELF/mips-micro-bad-cross-calls.s b/test/ELF/mips-micro-bad-cross-calls.s
new file mode 100644
index 0000000..63f7e40
--- /dev/null
+++ b/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/test/ELF/mips-micro-cross-calls.s b/test/ELF/mips-micro-cross-calls.s
new file mode 100644
index 0000000..fc9dd0a
--- /dev/null
+++ b/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/test/ELF/mips-micro-plt.s b/test/ELF/mips-micro-plt.s
index 24e90ae..63f50fc 100644
--- a/test/ELF/mips-micro-plt.s
+++ b/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/test/ELF/mips-micro-relocs.s b/test/ELF/mips-micro-relocs.s
index b539aa9..c6850fc 100644
--- a/test/ELF/mips-micro-relocs.s
+++ b/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