[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:
@@ -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).
|
||||
|
||||
@@ -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 ||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
if not "LoongArch" in config.root.targets:
|
||||
config.unsupported = True
|
||||
Reference in New Issue
Block a user