Skip to content

Commit 38394a3

Browse files
authored
[lld][LoongArch] Handle extreme code model relocs according to psABI v2.30 (#73387)
psABI v2.30 requires the extreme code model instructions sequence (pcalau12i+addi.d+lu32i.d+lu52i.d) to be adjacent. See llvm/llvm-project#71907 and loongson-community/discussions#17 for details.
1 parent ccaf9e0 commit 38394a3

File tree

4 files changed

+93
-138
lines changed

4 files changed

+93
-138
lines changed

lld/ELF/Arch/LoongArch.cpp

Lines changed: 27 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -82,89 +82,33 @@ static uint64_t getLoongArchPage(uint64_t p) {
8282
static uint32_t lo12(uint32_t val) { return val & 0xfff; }
8383

8484
// Calculate the adjusted page delta between dest and PC.
85-
uint64_t elf::getLoongArchPageDelta(uint64_t dest, uint64_t pc) {
86-
// Consider the large code model access pattern, of which the smaller code
87-
// models' access patterns are a subset:
88-
//
89-
// pcalau12i U, %foo_hi20(sym) ; b in [-0x80000, 0x7ffff]
90-
// addi.d T, zero, %foo_lo12(sym) ; a in [-0x800, 0x7ff]
91-
// lu32i.d T, %foo64_lo20(sym) ; c in [-0x80000, 0x7ffff]
92-
// lu52i.d T, T, %foo64_hi12(sym) ; d in [-0x800, 0x7ff]
93-
// {ldx,stx,add}.* dest, U, T
94-
//
95-
// Let page(pc) = 0xRRR'QQQQQ'PPPPP'000 and dest = 0xZZZ'YYYYY'XXXXX'AAA,
96-
// with RQ, P, ZY, X and A representing the respective bitfields as unsigned
97-
// integers. We have:
98-
//
99-
// page(dest) = 0xZZZ'YYYYY'XXXXX'000
100-
// - page(pc) = 0xRRR'QQQQQ'PPPPP'000
101-
// ----------------------------------
102-
// 0xddd'ccccc'bbbbb'000
103-
//
104-
// Now consider the above pattern's actual effects:
105-
//
106-
// page(pc) 0xRRR'QQQQQ'PPPPP'000
107-
// pcalau12i + 0xiii'iiiii'bbbbb'000
108-
// addi + 0xjjj'jjjjj'kkkkk'AAA
109-
// lu32i.d & lu52i.d + 0xddd'ccccc'00000'000
110-
// --------------------------------------------------
111-
// dest = U + T
112-
// = ((RQ<<32) + (P<<12) + i + (b<<12)) + (j + k + A + (cd<<32))
113-
// = (((RQ+cd)<<32) + i + j) + (((P+b)<<12) + k) + A
114-
// = (ZY<<32) + (X<<12) + A
115-
//
116-
// ZY<<32 = (RQ<<32)+(cd<<32)+i+j, X<<12 = (P<<12)+(b<<12)+k
117-
// cd<<32 = (ZY<<32)-(RQ<<32)-i-j, b<<12 = (X<<12)-(P<<12)-k
118-
//
119-
// where i and k are terms representing the effect of b's and A's sign
120-
// extension respectively.
121-
//
122-
// i = signed b < 0 ? -0x10000'0000 : 0
123-
// k = signed A < 0 ? -0x1000 : 0
124-
//
125-
// The j term is a bit complex: it represents the higher half of
126-
// sign-extended bits from A that are effectively lost if i == 0 but k != 0,
127-
// due to overwriting by lu32i.d & lu52i.d.
128-
//
129-
// j = signed A < 0 && signed b >= 0 ? 0x10000'0000 : 0
130-
//
131-
// The actual effect of the instruction sequence before the final addition,
132-
// i.e. our desired result value, is thus:
133-
//
134-
// result = (cd<<32) + (b<<12)
135-
// = (ZY<<32)-(RQ<<32)-i-j + (X<<12)-(P<<12)-k
136-
// = ((ZY<<32)+(X<<12)) - ((RQ<<32)+(P<<12)) - i - j - k
137-
// = page(dest) - page(pc) - i - j - k
138-
//
139-
// when signed A >= 0 && signed b >= 0:
140-
//
141-
// i = j = k = 0
142-
// result = page(dest) - page(pc)
143-
//
144-
// when signed A >= 0 && signed b < 0:
145-
//
146-
// i = -0x10000'0000, j = k = 0
147-
// result = page(dest) - page(pc) + 0x10000'0000
148-
//
149-
// when signed A < 0 && signed b >= 0:
150-
//
151-
// i = 0, j = 0x10000'0000, k = -0x1000
152-
// result = page(dest) - page(pc) - 0x10000'0000 + 0x1000
153-
//
154-
// when signed A < 0 && signed b < 0:
155-
//
156-
// i = -0x10000'0000, j = 0, k = -0x1000
157-
// result = page(dest) - page(pc) + 0x1000
158-
uint64_t result = getLoongArchPage(dest) - getLoongArchPage(pc);
159-
bool negativeA = lo12(dest) > 0x7ff;
160-
bool negativeB = (result & 0x8000'0000) != 0;
161-
162-
if (negativeA)
163-
result += 0x1000;
164-
if (negativeA && !negativeB)
165-
result -= 0x10000'0000;
166-
else if (!negativeA && negativeB)
167-
result += 0x10000'0000;
85+
uint64_t elf::getLoongArchPageDelta(uint64_t dest, uint64_t pc, RelType type) {
86+
// Note that if the sequence being relocated is `pcalau12i + addi.d + lu32i.d
87+
// + lu52i.d`, they must be adjancent so that we can infer the PC of
88+
// `pcalau12i` when calculating the page delta for the other two instructions
89+
// (lu32i.d and lu52i.d). Compensate all the sign-extensions is a bit
90+
// complicated. Just use psABI recommended algorithm.
91+
uint64_t pcalau12i_pc;
92+
switch (type) {
93+
case R_LARCH_PCALA64_LO20:
94+
case R_LARCH_GOT64_PC_LO20:
95+
case R_LARCH_TLS_IE64_PC_LO20:
96+
pcalau12i_pc = pc - 8;
97+
break;
98+
case R_LARCH_PCALA64_HI12:
99+
case R_LARCH_GOT64_PC_HI12:
100+
case R_LARCH_TLS_IE64_PC_HI12:
101+
pcalau12i_pc = pc - 12;
102+
break;
103+
default:
104+
pcalau12i_pc = pc;
105+
break;
106+
}
107+
uint64_t result = getLoongArchPage(dest) - getLoongArchPage(pcalau12i_pc);
108+
if (dest & 0x800)
109+
result += 0x1000 - 0x1'0000'0000;
110+
if (result & 0x8000'0000)
111+
result += 0x1'0000'0000;
168112
return result;
169113
}
170114

lld/ELF/InputSection.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -716,8 +716,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
716716
return sym.getGotVA() + a - p;
717717
case R_LOONGARCH_GOT_PAGE_PC:
718718
if (sym.hasFlag(NEEDS_TLSGD))
719-
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p);
720-
return getLoongArchPageDelta(sym.getGotVA() + a, p);
719+
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p, type);
720+
return getLoongArchPageDelta(sym.getGotVA() + a, p, type);
721721
case R_MIPS_GOTREL:
722722
return sym.getVA(a) - in.mipsGot->getGp(file);
723723
case R_MIPS_GOT_GP:
@@ -767,7 +767,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
767767
return 0;
768768
}
769769
case R_LOONGARCH_PAGE_PC:
770-
return getLoongArchPageDelta(sym.getVA(a), p);
770+
return getLoongArchPageDelta(sym.getVA(a), p, type);
771771
case R_PC:
772772
case R_ARM_PCA: {
773773
uint64_t dest;
@@ -802,7 +802,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
802802
case R_PPC64_CALL_PLT:
803803
return sym.getPltVA() + a - p;
804804
case R_LOONGARCH_PLT_PAGE_PC:
805-
return getLoongArchPageDelta(sym.getPltVA() + a, p);
805+
return getLoongArchPageDelta(sym.getPltVA() + a, p, type);
806806
case R_PLT_GOTPLT:
807807
return sym.getPltVA() + a - in.gotPlt->getVA();
808808
case R_PPC32_PLTREL:
@@ -864,7 +864,7 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
864864
case R_TLSGD_PC:
865865
return in.got->getGlobalDynAddr(sym) + a - p;
866866
case R_LOONGARCH_TLSGD_PAGE_PC:
867-
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p);
867+
return getLoongArchPageDelta(in.got->getGlobalDynAddr(sym) + a, p, type);
868868
case R_TLSLD_GOTPLT:
869869
return in.got->getVA() + in.got->getTlsIndexOff() + a - in.gotPlt->getVA();
870870
case R_TLSLD_GOT:

lld/ELF/Target.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ void addPPC64SaveRestore();
228228
uint64_t getPPC64TocBase();
229229
uint64_t getAArch64Page(uint64_t expr);
230230
template <typename ELFT> void writeARMCmseImportLib();
231-
uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc);
231+
uint64_t getLoongArchPageDelta(uint64_t dest, uint64_t pc, RelType type);
232232
void riscvFinalizeRelax(int passes);
233233
void mergeRISCVAttributesSections();
234234
void addArmInputSectionMappingSymbols();

0 commit comments

Comments
 (0)