[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
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index 96e23b9..a1986d8 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/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 {