Index: ELF/Target.cpp =================================================================== --- ELF/Target.cpp +++ ELF/Target.cpp @@ -195,6 +195,10 @@ public: MipsTargetInfo(); unsigned getDynRel(unsigned Type) const override; + void writeGotPlt(uint8_t *Buf, uint64_t Plt) const override; + void writePltZero(uint8_t *Buf) const override; + void writePlt(uint8_t *Buf, uint64_t GotEntryAddr, uint64_t PltEntryAddr, + int32_t Index, unsigned RelOff) const override; void writeGotHeader(uint8_t *Buf) const override; bool needsCopyRel(uint32_t Type, const SymbolBody &S) const override; bool needsGot(uint32_t Type, SymbolBody &S) const override; @@ -1386,8 +1390,13 @@ template MipsTargetInfo::MipsTargetInfo() { GotHeaderEntriesNum = 2; + GotPltHeaderEntriesNum = 2; PageSize = 65536; + PltEntrySize = 16; + PltZeroSize = 32; + UseLazyBinding = true; CopyRel = R_MIPS_COPY; + PltRel = R_MIPS_JUMP_SLOT; RelativeRel = R_MIPS_REL32; } @@ -1426,6 +1435,64 @@ } template +void MipsTargetInfo::writeGotPlt(uint8_t *Buf, uint64_t Plt) const { + write32(Buf, Out::Plt->getVA()); +} + +static uint16_t mipsHigh(uint64_t V) { return (V + 0x8000) >> 16; } + +template +static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t P, + uint64_t S) { + uint32_t Mask = 0xffffffff >> (32 - BSIZE); + uint32_t Instr = read32(Loc); + int64_t A = SignExtend64((Instr & Mask) << SHIFT); + if (SHIFT > 0) + checkAlignment<(1 << SHIFT)>(S + A, Type); + int64_t V = S + A - P; + checkInt(V, Type); + write32(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask)); +} + +template +static void applyMipsHi16Reloc(uint8_t *Loc, uint64_t S, int64_t A) { + uint32_t Instr = read32(Loc); + write32(Loc, (Instr & 0xffff0000) | mipsHigh(S + A)); +} + +template +void MipsTargetInfo::writePltZero(uint8_t *Buf) const { + const endianness E = ELFT::TargetEndianness; + write32(Buf, 0x3c1c0000); // lui $28, %hi(&GOTPLT[0]) + write32(Buf + 4, 0x8f990000); // lw $25, %lo(&GOTPLT[0])($28) + write32(Buf + 8, 0x279c0000); // addiu $28, $28, %lo(&GOTPLT[0]) + write32(Buf + 12, 0x031cc023); // subu $24, $24, $28 + write32(Buf + 16, 0x03e07825); // move $15, $31 + write32(Buf + 20, 0x0018c082); // srl $24, $24, 2 + write32(Buf + 24, 0x0320f809); // jalr $25 + write32(Buf + 28, 0x2718fffe); // subu $24, $24, 2 + uint64_t Got = Out::GotPlt->getVA(); + uint64_t Plt = Out::Plt->getVA(); + applyMipsHi16Reloc(Buf, Got, 0); + relocateOne(Buf + 4, Buf + 8, R_MIPS_LO16, Plt + 4, Got); + relocateOne(Buf + 8, Buf + 12, R_MIPS_LO16, Plt + 8, Got); +} + +template +void MipsTargetInfo::writePlt(uint8_t *Buf, uint64_t GotEntryAddr, + uint64_t PltEntryAddr, int32_t Index, + unsigned RelOff) const { + const endianness E = ELFT::TargetEndianness; + write32(Buf, 0x3c0f0000); // lui $15, %hi(.got.plt entry) + write32(Buf + 4, 0x8df90000); // l[wd] $25, %lo(.got.plt entry)($15) + write32(Buf + 8, 0x03200008); // jr $25 + write32(Buf + 12, 0x25f80000); // addiu $24, $15, %lo(.got.plt entry) + applyMipsHi16Reloc(Buf, GotEntryAddr, 0); + relocateOne(Buf + 4, Buf + 8, R_MIPS_LO16, PltEntryAddr + 4, GotEntryAddr); + relocateOne(Buf + 12, Buf + 16, R_MIPS_LO16, PltEntryAddr + 8, GotEntryAddr); +} + +template bool MipsTargetInfo::needsCopyRel(uint32_t Type, const SymbolBody &S) const { if (Config->Shared) @@ -1438,29 +1505,20 @@ template bool MipsTargetInfo::needsGot(uint32_t Type, SymbolBody &S) const { - return Type == R_MIPS_GOT16 || Type == R_MIPS_CALL16; + return needsPlt(Type, S) || Type == R_MIPS_GOT16 || Type == R_MIPS_CALL16; } template bool MipsTargetInfo::needsPlt(uint32_t Type, SymbolBody &S) const { + if (needsCopyRel(Type, S)) + return false; + if (Type == R_MIPS_26 && canBePreempted(&S, false)) + return true; + if (Type == R_MIPS_HI16 || Type == R_MIPS_LO16 || isRelRelative(Type)) + return S.isShared(); return false; } -static uint16_t mipsHigh(uint64_t V) { return (V + 0x8000) >> 16; } - -template -static void applyMipsPcReloc(uint8_t *Loc, uint32_t Type, uint64_t P, - uint64_t S) { - uint32_t Mask = 0xffffffff >> (32 - BSIZE); - uint32_t Instr = read32(Loc); - int64_t A = SignExtend64((Instr & Mask) << SHIFT); - if (SHIFT > 0) - checkAlignment<(1 << SHIFT)>(S + A, Type); - int64_t V = S + A - P; - checkInt(V, Type); - write32(Loc, (Instr & ~Mask) | ((V >> SHIFT) & Mask)); -} - template void MipsTargetInfo::relocateOne(uint8_t *Loc, uint8_t *BufEnd, uint32_t Type, uint64_t P, uint64_t S, @@ -1470,6 +1528,15 @@ case R_MIPS_32: add32(Loc, S); break; + case R_MIPS_26: { + uint32_t Instr = read32(Loc); + // FIXME (simon): If the relocation target symbol is not a PLT entry + // we should use another expression for calculation: + // ((A << 2) | (P & 0xf0000000)) >> 2 + S += SignExtend64<28>((Instr & 0x3ffffff) << 2); + write32(Loc, (Instr & ~0x3ffffff) | (S >> 2)); + break; + } case R_MIPS_CALL16: case R_MIPS_GOT16: { int64_t V = S - getMipsGpAddr(); @@ -1493,10 +1560,10 @@ if (PairedLoc) { uint64_t AHL = ((Instr & 0xffff) << 16) + SignExtend64<16>(read32(PairedLoc) & 0xffff); - write32(Loc, (Instr & 0xffff0000) | mipsHigh(S + AHL)); + applyMipsHi16Reloc(Loc, S, AHL); } else { warning("Can't find matching R_MIPS_LO16 relocation for R_MIPS_HI16"); - write32(Loc, (Instr & 0xffff0000) | mipsHigh(S)); + applyMipsHi16Reloc(Loc, S, 0); } break; } Index: test/ELF/Inputs/mips-dynamic.s =================================================================== --- test/ELF/Inputs/mips-dynamic.s +++ test/ELF/Inputs/mips-dynamic.s @@ -4,9 +4,14 @@ _foo: nop - .globl foo - .type foo, @function -foo: + .globl foo0 + .type foo0, @function +foo0: + nop + + .globl foo1 + .type foo1, @function +foo1: nop .data Index: test/ELF/mips-26.s =================================================================== --- /dev/null +++ test/ELF/mips-26.s @@ -0,0 +1,97 @@ +# Check R_MIPS_26 relocation handling. + +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t1.o +# RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ +# RUN: %S/Inputs/mips-dynamic.s -o %t2.o +# RUN: ld.lld %t2.o -shared -o %t.so +# RUN: ld.lld %t1.o %t.so -o %t.exe +# RUN: llvm-objdump -d %t.exe | FileCheck %s +# RUN: llvm-readobj -dynamic-table -s -r -mips-plt-got %t.exe \ +# RUN: | FileCheck -check-prefix=REL %s + +# REQUIRES: mips + +# CHECK: Disassembly of section .text: +# CHECK-NEXT: bar: +# CHECK-NEXT: 20000: 0c 00 80 06 jal 131096 +# ^-- 0x20018 loc +# CHECK-NEXT: 20004: 00 00 00 00 nop +# +# CHECK: __start: +# CHECK-NEXT: 20008: 0c 00 80 00 jal 131072 +# ^-- 0x20000 bar +# CHECK-NEXT: 2000c: 00 00 00 00 nop +# CHECK-NEXT: 20010: 0c 00 80 10 jal 131136 +# ^-- 0x20040 gotplt[foo0] +# CHECK-NEXT: 20014: 00 00 00 00 nop +# +# CHECK: loc: +# CHECK-NEXT: 20018: 00 00 00 00 nop +# CHECK-NEXT: Disassembly of section .plt: +# CHECK-NEXT: .plt: +# CHECK-NEXT: 20020: 3c 1c 00 04 lui $gp, 4 +# CHECK-NEXT: 20024: 8f 99 00 04 lw $25, 4($gp) +# CHECK-NEXT: 20028: 27 9c 00 04 addiu $gp, $gp, 4 +# CHECK-NEXT: 2002c: 03 1c c0 23 subu $24, $24, $gp +# CHECK-NEXT: 20030: 03 e0 78 25 move $15, $ra +# CHECK-NEXT: 20034: 00 18 c0 82 srl $24, $24, 2 +# CHECK-NEXT: 20038: 03 20 f8 09 jalr $25 +# CHECK-NEXT: 2003c: 27 18 ff fe addiu $24, $24, -2 +# CHECK-NEXT: 20040: 3c 0f 00 04 lui $15, 4 +# CHECK-NEXT: 20044: 8d f9 00 0c lw $25, 12($15) +# CHECK-NEXT: 20048: 03 20 00 08 jr $25 +# CHECK-NEXT: 2004c: 25 f8 00 0c addiu $24, $15, 12 + +# REL: Name: .plt +# REL-NEXT: Type: SHT_PROGBITS +# REL-NEXT: Flags [ (0x6) +# REL-NEXT: SHF_ALLOC +# REL-NEXT: SHF_EXECINSTR +# REL-NEXT: ] +# REL-NEXT: Address: 0x[[PLTADDR:[0-9A-F]+]] + +# REL: Name: .got.plt +# REL-NEXT: Type: SHT_PROGBITS +# REL-NEXT: Flags [ (0x3) +# REL-NEXT: SHF_ALLOC +# REL-NEXT: SHF_WRITE +# REL-NEXT: ] +# REL-NEXT: Address: 0x[[GOTPLTADDR:[0-9A-F]+]] + +# REL: Relocations [ +# REL-NEXT: Section (7) .rel.plt { +# REL-NEXT: 0x[[PLTSLOT:[0-9A-F]+]] R_MIPS_JUMP_SLOT foo0 0x0 +# REL-NEXT: } +# REL-NEXT: ] + +# REL: 0x70000032 MIPS_PLTGOT 0x[[GOTPLTADDR]] + +# REL: Primary GOT { +# REL: Local entries [ +# REL-NEXT: ] +# REL-NEXT: Global entries [ +# REL-NEXT: ] +# REL: PLT GOT { +# REL: Entries [ +# REL-NEXT: Entry { +# REL-NEXT: Address: 0x[[PLTSLOT]] +# REL-NEXT: Initial: 0x[[PLTADDR]] +# REL-NEXT: Value: 0x0 +# REL-NEXT: Type: Function +# REL-NEXT: Section: Undefined +# REL-NEXT: Name: foo0 +# REL-NEXT: } +# REL-NEXT: ] + + .text + .globl bar +bar: + jal loc # R_MIPS_26 against .text + offset + + .globl __start +__start: + jal bar # R_MIPS_26 against global 'bar' from object file + jal foo0 # R_MIPS_26 against 'foo0' from DSO + +loc: + nop Index: test/ELF/mips-plt-copy.s =================================================================== --- test/ELF/mips-plt-copy.s +++ test/ELF/mips-plt-copy.s @@ -1,4 +1,5 @@ -# Check creating of R_MIPS_COPY dynamic relocation. +# Check creating of R_MIPS_COPY and R_MIPS_JUMP_SLOT dynamic relocations +# and corresponding PLT entries. # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux %s -o %t.o # RUN: llvm-mc -filetype=obj -triple=mips-unknown-linux \ @@ -14,6 +15,10 @@ # CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_COPY data0 0x0 # CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_COPY data1 0x0 # CHECK-NEXT: } +# CHECK-NEXT: Section (8) .rel.plt { +# CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_JUMP_SLOT foo0 0x0 +# CHECK-NEXT: 0x{{[0-9A-F]+}} R_MIPS_JUMP_SLOT foo1 0x0 +# CHECK-NEXT: } # CHECK-NEXT: ] # CHECK: Primary GOT { @@ -24,9 +29,38 @@ # CHECK-NEXT: Number of TLS and multi-GOT entries: 0 # CHECK-NEXT: } +# CHECK: PLT GOT { +# CHECK: Entries [ +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x{{[0-9A-F]+}} +# CHECK-NEXT: Initial: 0x{{[0-9A-F]+}} +# CHECK-NEXT: Value: 0x{{[0-9A-F]+}} +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: Name: foo0 +# CHECK-NEXT: } +# CHECK-NEXT: Entry { +# CHECK-NEXT: Address: 0x{{[0-9A-F]+}} +# CHECK-NEXT: Initial: 0x{{[0-9A-F]+}} +# CHECK-NEXT: Value: 0x{{[0-9A-F]+}} +# CHECK-NEXT: Type: Function +# CHECK-NEXT: Section: Undefined +# CHECK-NEXT: Name: foo1 +# CHECK-NEXT: } +# CHECK-NEXT: ] +# CHECK-NEXT: } + .text .globl __start __start: + lui $t0,%hi(foo0) # R_MIPS_HI16 requires JUMP_SLOT/PLT entry + # for DSO defined func. + addi $t0,$t0,%lo(foo0) + lui $t0,%hi(bar) # Does not require PLT for locally defined func. + addi $t0,$t0,%lo(bar) + lui $t0,%hi(loc) # Does not require PLT for local func. + addi $t0,$t0,%lo(loc) + lui $t0,%hi(data0) # R_MIPS_HI16 requires COPY rel for DSO defined data. addi $t0,$t0,%lo(data0) lui $t0,%hi(gd) # Does not require COPY rel for locally defined data. @@ -34,9 +68,18 @@ lui $t0,%hi(ld) # Does not require COPY rel for local data. addi $t0,$t0,%lo(ld) + .globl bar + .type bar, @function +bar: + nop +loc: + nop + .data .globl gd gd: .word 0 ld: .word data1+8-. # R_MIPS_PC32 requires COPY rel for DSO defined data. + .word foo1+8-. # R_MIPS_PC32 requires JUMP_SLOT/PLT entry + # for DSO defined func.