Skip to content

[lld][LoongArch] Handle extreme code model relocs according to psABI v2.30 #73387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 27 additions & 83 deletions lld/ELF/Arch/LoongArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,89 +82,33 @@ static uint64_t getLoongArchPage(uint64_t p) {
static uint32_t lo12(uint32_t val) { return val & 0xfff; }

// Calculate the adjusted page delta between dest and PC.
uint64_t elf::getLoongArchPageDelta(uint64_t dest, uint64_t pc) {
// Consider the large code model access pattern, of which the smaller code
// models' access patterns are a subset:
//
// pcalau12i U, %foo_hi20(sym) ; b in [-0x80000, 0x7ffff]
// addi.d T, zero, %foo_lo12(sym) ; a in [-0x800, 0x7ff]
// lu32i.d T, %foo64_lo20(sym) ; c in [-0x80000, 0x7ffff]
// lu52i.d T, T, %foo64_hi12(sym) ; d in [-0x800, 0x7ff]
// {ldx,stx,add}.* dest, U, T
//
// Let page(pc) = 0xRRR'QQQQQ'PPPPP'000 and dest = 0xZZZ'YYYYY'XXXXX'AAA,
// with RQ, P, ZY, X and A representing the respective bitfields as unsigned
// integers. We have:
//
// page(dest) = 0xZZZ'YYYYY'XXXXX'000
// - page(pc) = 0xRRR'QQQQQ'PPPPP'000
// ----------------------------------
// 0xddd'ccccc'bbbbb'000
//
// Now consider the above pattern's actual effects:
//
// page(pc) 0xRRR'QQQQQ'PPPPP'000
// pcalau12i + 0xiii'iiiii'bbbbb'000
// addi + 0xjjj'jjjjj'kkkkk'AAA
// lu32i.d & lu52i.d + 0xddd'ccccc'00000'000
// --------------------------------------------------
// dest = U + T
// = ((RQ<<32) + (P<<12) + i + (b<<12)) + (j + k + A + (cd<<32))
// = (((RQ+cd)<<32) + i + j) + (((P+b)<<12) + k) + A
// = (ZY<<32) + (X<<12) + A
//
// ZY<<32 = (RQ<<32)+(cd<<32)+i+j, X<<12 = (P<<12)+(b<<12)+k
// cd<<32 = (ZY<<32)-(RQ<<32)-i-j, b<<12 = (X<<12)-(P<<12)-k
//
// where i and k are terms representing the effect of b's and A's sign
// extension respectively.
//
// i = signed b < 0 ? -0x10000'0000 : 0
// k = signed A < 0 ? -0x1000 : 0
//
// The j term is a bit complex: it represents the higher half of
// sign-extended bits from A that are effectively lost if i == 0 but k != 0,
// due to overwriting by lu32i.d & lu52i.d.
//
// j = signed A < 0 && signed b >= 0 ? 0x10000'0000 : 0
//
// The actual effect of the instruction sequence before the final addition,
// i.e. our desired result value, is thus:
//
// result = (cd<<32) + (b<<12)
// = (ZY<<32)-(RQ<<32)-i-j + (X<<12)-(P<<12)-k
// = ((ZY<<32)+(X<<12)) - ((RQ<<32)+(P<<12)) - i - j - k
// = page(dest) - page(pc) - i - j - k
//
// when signed A >= 0 && signed b >= 0:
//
// i = j = k = 0
// result = page(dest) - page(pc)
//
// when signed A >= 0 && signed b < 0:
//
// i = -0x10000'0000, j = k = 0
// result = page(dest) - page(pc) + 0x10000'0000
//
// when signed A < 0 && signed b >= 0:
//
// i = 0, j = 0x10000'0000, k = -0x1000
// result = page(dest) - page(pc) - 0x10000'0000 + 0x1000
//
// when signed A < 0 && signed b < 0:
//
// i = -0x10000'0000, j = 0, k = -0x1000
// result = page(dest) - page(pc) + 0x1000
uint64_t result = getLoongArchPage(dest) - getLoongArchPage(pc);
bool negativeA = lo12(dest) > 0x7ff;
bool negativeB = (result & 0x8000'0000) != 0;

if (negativeA)
result += 0x1000;
if (negativeA && !negativeB)
result -= 0x10000'0000;
else if (!negativeA && negativeB)
result += 0x10000'0000;
uint64_t elf::getLoongArchPageDelta(uint64_t dest, uint64_t pc, RelType type) {
// Note that if the sequence being relocated is `pcalau12i + addi.d + lu32i.d
// + lu52i.d`, they must be adjancent so that we can infer the PC of
// `pcalau12i` when calculating the page delta for the other two instructions
// (lu32i.d and lu52i.d). Compensate all the sign-extensions is a bit
// complicated. Just use psABI recommended algorithm.
uint64_t pcalau12i_pc;
switch (type) {
case R_LARCH_PCALA64_LO20:
case R_LARCH_GOT64_PC_LO20:
case R_LARCH_TLS_IE64_PC_LO20:
pcalau12i_pc = pc - 8;
break;
case R_LARCH_PCALA64_HI12:
case R_LARCH_GOT64_PC_HI12:
case R_LARCH_TLS_IE64_PC_HI12:
pcalau12i_pc = pc - 12;
break;
default:
pcalau12i_pc = pc;
break;
}
uint64_t result = getLoongArchPage(dest) - getLoongArchPage(pcalau12i_pc);
if (dest & 0x800)
result += 0x1000 - 0x1'0000'0000;
if (result & 0x8000'0000)
result += 0x1'0000'0000;
return result;
}

Expand Down
10 changes: 5 additions & 5 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -714,8 +714,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
return sym.getGotVA() + a - p;
case R_LOONGARCH_GOT_PAGE_PC:
if (sym.hasFlag(NEEDS_TLSGD))
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p);
return getLoongArchPageDelta(sym.getGotVA() + a, p);
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p, type);
return getLoongArchPageDelta(sym.getGotVA() + a, p, type);
case R_MIPS_GOTREL:
return sym.getVA(a) - in.mipsGot->getGp(file);
case R_MIPS_GOT_GP:
Expand Down Expand Up @@ -765,7 +765,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
return 0;
}
case R_LOONGARCH_PAGE_PC:
return getLoongArchPageDelta(sym.getVA(a), p);
return getLoongArchPageDelta(sym.getVA(a), p, type);
case R_PC:
case R_ARM_PCA: {
uint64_t dest;
Expand Down Expand Up @@ -800,7 +800,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
case R_PPC64_CALL_PLT:
return sym.getPltVA() + a - p;
case R_LOONGARCH_PLT_PAGE_PC:
return getLoongArchPageDelta(sym.getPltVA() + a, p);
return getLoongArchPageDelta(sym.getPltVA() + a, p, type);
case R_PLT_GOTPLT:
return sym.getPltVA() + a - in.gotPlt->getVA();
case R_PPC32_PLTREL:
Expand Down Expand Up @@ -862,7 +862,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
case R_TLSGD_PC:
return in.got->getGlobalDynAddr(sym) + a - p;
case R_LOONGARCH_TLSGD_PAGE_PC:
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p);
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p, type);
case R_TLSLD_GOTPLT:
return in.got->getVA() + in.got->getTlsIndexOff() + a - in.gotPlt->getVA();
case R_TLSLD_GOT:
Expand Down
2 changes: 1 addition & 1 deletion lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ void addPPC64SaveRestore();
uint64_t getPPC64TocBase();
uint64_t getAArch64Page(uint64_t expr);
template <typename ELFT> void writeARMCmseImportLib();
uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc);
uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc, RelType type);
void riscvFinalizeRelax(int passes);
void mergeRISCVAttributesSections();
void addArmInputSectionMappingSymbols();
Expand Down
Loading