diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp index 5ac5532705dc4..b3798f15a6cc9 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp @@ -990,6 +990,18 @@ uint8_t *RuntimeDyldImpl::createStubFunction(uint8_t *Addr, // and stubs for branches Thumb - ARM and ARM - Thumb. writeBytesUnaligned(0xe51ff004, Addr, 4); // ldr pc, [pc, #-4] return Addr + 4; + } else if (Arch == Triple::loongarch64) { + // lu12i.w $t0, %abs_hi20(addr) + // ori $t0, $t0, %abs_lo12(addr) + // lu32i.d $t0, %abs64_lo20(addr) + // lu52i.d $t0, $t0, %abs64_lo12(addr) + // jr $t0 + writeBytesUnaligned(0x1400000c, Addr, 4); + writeBytesUnaligned(0x0380018c, Addr + 4, 4); + writeBytesUnaligned(0x1600000c, Addr + 8, 4); + writeBytesUnaligned(0x0300018c, Addr + 12, 4); + writeBytesUnaligned(0x4c000180, Addr + 16, 4); + return Addr; } else if (IsMipsO32ABI || IsMipsN32ABI) { // 0: 3c190000 lui t9,%hi(addr). // 4: 27390000 addiu t9,t9,%lo(addr). diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp index 25b76c7668350..5f8dc41433564 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp @@ -645,6 +645,206 @@ void RuntimeDyldELF::resolveARMRelocation(const SectionEntry &Section, } } +bool RuntimeDyldELF::resolveLoongArch64ShortBranch( + unsigned SectionID, relocation_iterator RelI, + const RelocationValueRef &Value) { + uint64_t Address; + if (Value.SymbolName) { + auto Loc = GlobalSymbolTable.find(Value.SymbolName); + // Don't create direct branch for external symbols. + if (Loc == GlobalSymbolTable.end()) + return false; + const auto &SymInfo = Loc->second; + Address = + uint64_t(Sections[SymInfo.getSectionID()].getLoadAddressWithOffset( + SymInfo.getOffset())); + } else { + Address = uint64_t(Sections[Value.SectionID].getLoadAddress()); + } + uint64_t Offset = RelI->getOffset(); + uint64_t SourceAddress = Sections[SectionID].getLoadAddressWithOffset(Offset); + if (!isInt<28>(Address + Value.Addend - SourceAddress)) + return false; + resolveRelocation(Sections[SectionID], Offset, Address, RelI->getType(), + Value.Addend); + return true; +} + +void RuntimeDyldELF::resolveLoongArch64Branch(unsigned SectionID, + const RelocationValueRef &Value, + relocation_iterator RelI, + StubMap &Stubs) { + LLVM_DEBUG(dbgs() << "\t\tThis is an LoongArch64 branch relocation.\n"); + + if (resolveLoongArch64ShortBranch(SectionID, RelI, Value)) + return; + + SectionEntry &Section = Sections[SectionID]; + uint64_t Offset = RelI->getOffset(); + unsigned RelType = RelI->getType(); + // Look for an existing stub. + StubMap::const_iterator i = Stubs.find(Value); + if (i != Stubs.end()) { + resolveRelocation(Section, Offset, + (uint64_t)Section.getAddressWithOffset(i->second), + RelType, 0); + LLVM_DEBUG(dbgs() << " Stub function found\n"); + return; + } + // Create a new stub function. + LLVM_DEBUG(dbgs() << " Create a new stub function\n"); + Stubs[Value] = Section.getStubOffset(); + uint8_t *StubTargetAddr = + createStubFunction(Section.getAddressWithOffset(Section.getStubOffset())); + RelocationEntry LU12I_W(SectionID, StubTargetAddr - Section.getAddress(), + ELF::R_LARCH_ABS_HI20, Value.Addend); + RelocationEntry ORI(SectionID, StubTargetAddr - Section.getAddress() + 4, + ELF::R_LARCH_ABS_LO12, Value.Addend); + RelocationEntry LU32I_D(SectionID, StubTargetAddr - Section.getAddress() + 8, + ELF::R_LARCH_ABS64_LO20, Value.Addend); + RelocationEntry LU52I_D(SectionID, StubTargetAddr - Section.getAddress() + 12, + ELF::R_LARCH_ABS64_HI12, Value.Addend); + if (Value.SymbolName) { + addRelocationForSymbol(LU12I_W, Value.SymbolName); + addRelocationForSymbol(ORI, Value.SymbolName); + addRelocationForSymbol(LU32I_D, Value.SymbolName); + addRelocationForSymbol(LU52I_D, Value.SymbolName); + } else { + addRelocationForSection(LU12I_W, Value.SectionID); + addRelocationForSection(ORI, Value.SectionID); + addRelocationForSection(LU32I_D, Value.SectionID); + + addRelocationForSection(LU52I_D, Value.SectionID); + } + resolveRelocation(Section, Offset, + reinterpret_cast( + Section.getAddressWithOffset(Section.getStubOffset())), + RelType, 0); + Section.advanceStubOffset(getMaxStubSize()); +} + +// Returns extract bits Val[Hi:Lo]. +static inline uint32_t extractBits(uint64_t Val, uint32_t Hi, uint32_t Lo) { + return Hi == 63 ? Val >> Lo : (Val & (((1ULL << (Hi + 1)) - 1))) >> Lo; +} + +void RuntimeDyldELF::resolveLoongArch64Relocation(const SectionEntry &Section, + uint64_t Offset, + uint64_t Value, uint32_t Type, + int64_t Addend) { + auto *TargetPtr = Section.getAddressWithOffset(Offset); + uint64_t FinalAddress = Section.getLoadAddressWithOffset(Offset); + + LLVM_DEBUG(dbgs() << "resolveLoongArch64Relocation, LocalAddress: 0x" + << format("%llx", Section.getAddressWithOffset(Offset)) + << " FinalAddress: 0x" << format("%llx", FinalAddress) + << " Value: 0x" << format("%llx", Value) << " Type: 0x" + << format("%x", Type) << " Addend: 0x" + << format("%llx", Addend) << "\n"); + + switch (Type) { + default: + report_fatal_error("Relocation type not implemented yet!"); + break; + case ELF::R_LARCH_32: + support::ulittle32_t::ref{TargetPtr} = + static_cast(Value + Addend); + break; + case ELF::R_LARCH_64: + support::ulittle64_t::ref{TargetPtr} = Value + Addend; + break; + case ELF::R_LARCH_32_PCREL: + support::ulittle32_t::ref{TargetPtr} = + static_cast(Value + Addend - FinalAddress); + break; + case ELF::R_LARCH_B26: { + uint64_t B26 = (Value + Addend - FinalAddress) >> 2; + auto Instr = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm15_0 = extractBits(B26, /*Hi=*/15, /*Lo=*/0) << 10; + uint32_t Imm25_16 = extractBits(B26, /*Hi=*/25, /*Lo=*/16); + Instr = (Instr & 0xfc000000) | Imm15_0 | Imm25_16; + break; + } + case ELF::R_LARCH_CALL36: { + uint64_t Call36 = (Value + Addend - FinalAddress) >> 2; + auto Pcaddu18i = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm35_16 = + extractBits((Call36 + (1UL << 15)), /*Hi=*/35, /*Lo=*/16) << 5; + Pcaddu18i = (Pcaddu18i & 0xfe00001f) | Imm35_16; + auto Jirl = support::ulittle32_t::ref(TargetPtr + 4); + uint32_t Imm15_0 = extractBits(Call36, /*Hi=*/15, /*Lo=*/0) << 10; + Jirl = (Jirl & 0xfc0003ff) | Imm15_0; + break; + } + case ELF::R_LARCH_GOT_PC_HI20: + case ELF::R_LARCH_PCALA_HI20: { + uint64_t Target = Value + Addend; + uint64_t TargetPage = + (Target + (Target & 0x800)) & ~static_cast(0xfff); + uint64_t PCPage = FinalAddress & ~static_cast(0xfff); + int64_t PageDelta = TargetPage - PCPage; + auto Instr = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm31_12 = extractBits(PageDelta, /*Hi=*/31, /*Lo=*/12) << 5; + Instr = (Instr & 0xfe00001f) | Imm31_12; + break; + } + case ELF::R_LARCH_GOT_PC_LO12: + case ELF::R_LARCH_PCALA_LO12: { + uint64_t TargetOffset = (Value + Addend) & 0xfff; + auto Instr = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm11_0 = TargetOffset << 10; + Instr = (Instr & 0xffc003ff) | Imm11_0; + break; + } + case ELF::R_LARCH_ABS_HI20: { + uint64_t Target = Value + Addend; + auto Instr = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm31_12 = extractBits(Target, /*Hi=*/31, /*Lo=*/12) << 5; + Instr = (Instr & 0xfe00001f) | Imm31_12; + break; + } + case ELF::R_LARCH_ABS_LO12: { + uint64_t Target = Value + Addend; + auto Instr = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm11_0 = extractBits(Target, /*Hi=*/11, /*Lo=*/0) << 10; + Instr = (Instr & 0xffc003ff) | Imm11_0; + break; + } + case ELF::R_LARCH_ABS64_LO20: { + uint64_t Target = Value + Addend; + auto Instr = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm51_32 = extractBits(Target, /*Hi=*/51, /*Lo=*/32) << 5; + Instr = (Instr & 0xfe00001f) | Imm51_32; + break; + } + case ELF::R_LARCH_ABS64_HI12: { + uint64_t Target = Value + Addend; + auto Instr = support::ulittle32_t::ref(TargetPtr); + uint32_t Imm63_52 = extractBits(Target, /*Hi=*/63, /*Lo=*/52) << 10; + Instr = (Instr & 0xffc003ff) | Imm63_52; + break; + } + case ELF::R_LARCH_ADD32: + support::ulittle32_t::ref{TargetPtr} = + (support::ulittle32_t::ref{TargetPtr} + + static_cast(Value + Addend)); + break; + case ELF::R_LARCH_SUB32: + support::ulittle32_t::ref{TargetPtr} = + (support::ulittle32_t::ref{TargetPtr} - + static_cast(Value + Addend)); + break; + case ELF::R_LARCH_ADD64: + support::ulittle64_t::ref{TargetPtr} = + (support::ulittle64_t::ref{TargetPtr} + Value + Addend); + break; + case ELF::R_LARCH_SUB64: + support::ulittle64_t::ref{TargetPtr} = + (support::ulittle64_t::ref{TargetPtr} - Value - Addend); + break; + } +} + void RuntimeDyldELF::setMipsABI(const ObjectFile &Obj) { if (Arch == Triple::UnknownArch || Triple::getArchTypePrefix(Arch) != "mips") { @@ -1190,6 +1390,9 @@ void RuntimeDyldELF::resolveRelocation(const SectionEntry &Section, resolveARMRelocation(Section, Offset, (uint32_t)(Value & 0xffffffffL), Type, (uint32_t)(Addend & 0xffffffffL)); break; + case Triple::loongarch64: + resolveLoongArch64Relocation(Section, Offset, Value, Type, Addend); + break; case Triple::ppc: // Fall through. case Triple::ppcle: resolvePPC32Relocation(Section, Offset, Value, Type, Addend); @@ -1515,6 +1718,17 @@ RuntimeDyldELF::processRelocationRef( } processSimpleRelocation(SectionID, Offset, RelType, Value); } + } else if (Arch == Triple::loongarch64) { + if (RelType == ELF::R_LARCH_B26 && MemMgr.allowStubAllocation()) { + resolveLoongArch64Branch(SectionID, Value, RelI, Stubs); + } else if (RelType == ELF::R_LARCH_GOT_PC_HI20 || + RelType == ELF::R_LARCH_GOT_PC_LO12) { + uint64_t GOTOffset = findOrAllocGOTEntry(Value, ELF::R_LARCH_64); + resolveGOTOffsetRelocation(SectionID, Offset, GOTOffset + Addend, + RelType); + } else { + processSimpleRelocation(SectionID, Offset, RelType, Value); + } } else if (IsMipsO32ABI) { uint8_t *Placeholder = reinterpret_cast( computePlaceholderAddress(SectionID, Offset)); @@ -2371,6 +2585,7 @@ size_t RuntimeDyldELF::getGOTEntrySize() { case Triple::x86_64: case Triple::aarch64: case Triple::aarch64_be: + case Triple::loongarch64: case Triple::ppc64: case Triple::ppc64le: case Triple::systemz: @@ -2683,6 +2898,10 @@ bool RuntimeDyldELF::relocationNeedsGot(const RelocationRef &R) const { return RelTy == ELF::R_AARCH64_ADR_GOT_PAGE || RelTy == ELF::R_AARCH64_LD64_GOT_LO12_NC; + if (Arch == Triple::loongarch64) + return RelTy == ELF::R_LARCH_GOT_PC_HI20 || + RelTy == ELF::R_LARCH_GOT_PC_LO12; + if (Arch == Triple::x86_64) return RelTy == ELF::R_X86_64_GOTPCREL || RelTy == ELF::R_X86_64_GOTPCRELX || diff --git a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h index 97517884654bc..deb623b1a4bef 100644 --- a/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h +++ b/llvm/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h @@ -46,6 +46,18 @@ class RuntimeDyldELF : public RuntimeDyldImpl { void resolveARMRelocation(const SectionEntry &Section, uint64_t Offset, uint32_t Value, uint32_t Type, int32_t Addend); + void resolveLoongArch64Relocation(const SectionEntry &Section, + uint64_t Offset, uint64_t Value, + uint32_t Type, int64_t Addend); + + bool resolveLoongArch64ShortBranch(unsigned SectionID, + relocation_iterator RelI, + const RelocationValueRef &Value); + + void resolveLoongArch64Branch(unsigned SectionID, + const RelocationValueRef &Value, + relocation_iterator RelI, StubMap &Stubs); + void resolvePPC32Relocation(const SectionEntry &Section, uint64_t Offset, uint64_t Value, uint32_t Type, int64_t Addend); @@ -71,6 +83,8 @@ class RuntimeDyldELF : public RuntimeDyldImpl { return 16; else if (IsMipsN64ABI) return 32; + if (Arch == Triple::loongarch64) + return 20; // lu12i.w; ori; lu32i.d; lu52i.d; jr else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le) return 44; else if (Arch == Triple::x86_64) diff --git a/llvm/test/ExecutionEngine/RuntimeDyld/LoongArch/ELF_LoongArch_relocations.s b/llvm/test/ExecutionEngine/RuntimeDyld/LoongArch/ELF_LoongArch_relocations.s new file mode 100644 index 0000000000000..0fca88b6e9ba2 --- /dev/null +++ b/llvm/test/ExecutionEngine/RuntimeDyld/LoongArch/ELF_LoongArch_relocations.s @@ -0,0 +1,102 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc --triple=loongarch64 --filetype=obj -o %t/reloc.o %s +# RUN: llvm-rtdyld --triple=loongarch64 --verify --check=%s %t/reloc.o \ +# RUN: --map-section reloc.o,.got=0x21f00 \ +# RUN: --dummy-extern abs=0x0123456789abcdef \ +# RUN: --dummy-extern external_data=0x1234 + + .text + .globl main + .p2align 2 + .type main,@function +main: +## Check R_LARCH_ABS_HI20 +# rtdyld-check: *{4}(main) = 0x1513578c + lu12i.w $t0, %abs_hi20(abs) +## Check R_LARCH_ABS_LO12 +# rtdyld-check: *{4}(main + 4) = 0x03b7bd8c + ori $t0, $t0, %abs_lo12(abs) +## Check R_LARCH_ABS64_LO20 +# rtdyld-check: *{4}(main + 8) = 0x1668acec + lu32i.d $t0, %abs64_lo20(abs) +## Check R_LARCH_ABS64_HI12 +# rtdyld-check: *{4}(main + 12) = 0x0300498c + lu52i.d $t0, $t0, %abs64_hi12(abs) + ret + .size main, .-main + + .globl local_func + .p2align 2 + .type local_func,@function +local_func: + ret + .size local_func, .-local_func + + .globl local_func_call26 + .p2align 2 +local_func_call26: +## Check R_LARCH_B26 +# rtdyld-check: decode_operand(local_func_call26, 0)[27:0] = \ +# rtdyld-check: (local_func - local_func_call26)[27:0] + bl local_func + .size local_func_call26, .-local_func_call26 + + .globl local_func_call36 + .p2align 2 +local_func_call36: +## Check R_LARCH_CALL36 +# rtdyld-check: decode_operand(local_func_call36, 1)[19:0] = \ +# rtdyld-check: ((local_func - local_func_call36) + \ +# rtdyld-check: (((local_func - local_func_call36)[17:17]) << 17))[37:18] +# rtdyld-check: decode_operand(local_func_call36 + 4, 2)[17:0] = \ +# rtdyld-check: (local_func - local_func_call36)[17:0] + pcaddu18i $ra, %call36(local_func) + jirl $ra, $ra, 0 + .size local_func_call36, .-local_func_call36 + + .globl test_pc_hi20 + .p2align 2 +test_pc_hi20: +## Check R_LARCH_PCALA_HI20 +# rtdyld-check: decode_operand(test_pc_hi20, 1)[19:0] = \ +# rtdyld-check: (named_data - test_pc_hi20)[31:12] + \ +# rtdyld-check: named_data[11:11] + pcalau12i $a0, %pc_hi20(named_data) + .size test_pc_hi20, .-test_pc_hi20 + + .globl test_pc_lo12 + .p2align 2 +test_pc_lo12: +## Check R_LARCH_PCALA_LO12 +# rtdyld-check: decode_operand(test_pc_lo12, 2)[11:0] = \ +# rtdyld-check: (named_data)[11:0] + addi.d $a0, $a0, %pc_lo12(named_data) + .size test_pc_lo12, .-test_pc_lo12 + + .globl test_got_pc_hi20 + .p2align 2 +test_got_pc_hi20: +## Check R_LARCH_GOT_PC_HI20 +# rtdyld-check: decode_operand(test_got_pc_hi20, 1)[19:0] = \ +# rtdyld-check: (section_addr(reloc.o, .got)[31:12] - \ +# rtdyld-check: test_got_pc_hi20[31:12] + \ +# rtdyld-check: section_addr(reloc.o, .got)[11:11]) + pcalau12i $a0, %got_pc_hi20(external_data) + .size test_got_pc_hi20, .-test_got_pc_hi20 + + .globl test_got_pc_lo12 + .p2align 2 +test_got_pc_lo12: +## Check R_LARCH_GOT_PC_LO12 +# rtdyld-check: decode_operand(test_got_pc_lo12, 2)[11:0] = \ +# rtdyld-check: (section_addr(reloc.o, .got)[11:0]) + ld.d $a0, $a0, %got_pc_lo12(external_data) + .size test_gotoffset12_external, .-test_gotoffset12_external + + .globl named_data + .p2align 4 + .type named_data,@object +named_data: + .quad 0x2222222222222222 + .quad 0x3333333333333333 + .size named_data, .-named_data diff --git a/llvm/test/ExecutionEngine/RuntimeDyld/LoongArch/lit.local.cfg b/llvm/test/ExecutionEngine/RuntimeDyld/LoongArch/lit.local.cfg new file mode 100644 index 0000000000000..cc24278acbb41 --- /dev/null +++ b/llvm/test/ExecutionEngine/RuntimeDyld/LoongArch/lit.local.cfg @@ -0,0 +1,2 @@ +if not "LoongArch" in config.root.targets: + config.unsupported = True