[RuntimeDyld] Add LoongArch support

This is necessary for supporting function calls in LLDB expressions for
LoongArch.
This patch is inspired by #99336 and simply extracts the parts related
to RuntimeDyld.

Reviewed By: lhames

Pull Request: https://github.com/llvm/llvm-project/pull/114741
This commit is contained in:
wanglei
2024-11-08 10:42:31 +08:00
committed by GitHub
parent 36d757f840
commit 4e668d5b27
5 changed files with 349 additions and 0 deletions

View File

@@ -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).

View File

@@ -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<uint64_t>(
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<uint32_t>(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<uint32_t>(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<uint64_t>(0xfff);
uint64_t PCPage = FinalAddress & ~static_cast<uint64_t>(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<uint32_t>(Value + Addend));
break;
case ELF::R_LARCH_SUB32:
support::ulittle32_t::ref{TargetPtr} =
(support::ulittle32_t::ref{TargetPtr} -
static_cast<uint32_t>(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<uint8_t *>(
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 ||

View File

@@ -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)

View File

@@ -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

View File

@@ -0,0 +1,2 @@
if not "LoongArch" in config.root.targets:
config.unsupported = True