mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-19 06:38:28 +00:00
[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 llvm-svn: 354311
This commit is contained in:
parent
19dbc6245f
commit
fae2a509fa
@ -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 ||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
15
lld/test/ELF/mips-micro-bad-cross-calls.s
Normal file
15
lld/test/ELF/mips-micro-bad-cross-calls.s
Normal file
@ -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
|
44
lld/test/ELF/mips-micro-cross-calls.s
Normal file
44
lld/test/ELF/mips-micro-cross-calls.s
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user