Skip to content

Commit bda5e6c

Browse files
committed
cmd/internal/obj/riscv,cmd/link: rework riscv64 call relocations
The riscv64 assembler and linker generate three types of calls. Most calls are made via a single JAL instruction, however this is limited to +/-1MB of text. In the case where a call target is unreachable (or unknown), the JAL targets an AUIPC+JALR trampoline. All other cases use AUIPC+JALR pairs, including the case where a single function exceeds 1MB in text size, potentially making it impossible to reach trampolines. Currently, the single instruction JAL call is marked with R_RISCV_CALL and the two instruction AUIPC+JALR call is marked with R_RISCV_PCREL_ITYPE, which is also used for memory load instructions. This means that we have no way to identify that the latter is a call. Switch to using R_RISCV_CALL to mark the AUIPC+JALR pair (aligning somewhat with the elf.R_RISCV_CALL, which is deprecated in favour of elf.R_RISCV_CALL_PLT). Add R_RISCV_JAL and use this to mark the single instruction JAL direct calls. This is clearer and allows us to map elf.R_RISCV_CALL_PLT to Go's R_RISCV_CALL. Add all three types to IsDirectCall, so that direct calls are correctly identified when a function exceeds 1MB of text. Fixes #62465 Change-Id: Id3eea09688a2b7d6e481eae9ed0aa0d1f9a3a48f Reviewed-on: https://go-review.googlesource.com/c/go/+/520095 TryBot-Result: Gopher Robot <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Run-TryBot: Joel Sing <[email protected]> Reviewed-by: Than McIntosh <[email protected]>
1 parent 3c4f12a commit bda5e6c

File tree

6 files changed

+90
-74
lines changed

6 files changed

+90
-74
lines changed

src/cmd/asm/internal/asm/testdata/riscv64.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,7 @@ start:
381381
JMP 4(X5) // 67804200
382382

383383
// CALL and JMP to symbol are encoded as JAL (using LR or ZERO
384-
// respectively), with a R_RISCV_CALL relocation. The linker resolves
384+
// respectively), with a R_RISCV_JAL relocation. The linker resolves
385385
// the real address and updates the immediate, using a trampoline in
386386
// the case where the address is not directly reachable.
387387
CALL asmtest(SB) // ef000000

src/cmd/internal/obj/riscv/cpu.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,13 @@ const (
260260
// corresponding *obj.Prog uses the temporary register.
261261
USES_REG_TMP = 1 << iota
262262

263-
// NEED_CALL_RELOC is set on JAL instructions to indicate that a
264-
// R_RISCV_CALL relocation is needed.
263+
// NEED_JAL_RELOC is set on JAL instructions to indicate that a
264+
// R_RISCV_JAL relocation is needed.
265+
NEED_JAL_RELOC
266+
267+
// NEED_CALL_RELOC is set on an AUIPC instruction to indicate that it
268+
// is the first instruction in an AUIPC + JAL pair that needs a
269+
// R_RISCV_CALL relocation.
265270
NEED_CALL_RELOC
266271

267272
// NEED_PCREL_ITYPE_RELOC is set on AUIPC instructions to indicate that

src/cmd/internal/obj/riscv/obj.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func jalToSym(ctxt *obj.Link, p *obj.Prog, lr int16) {
4141
}
4242

4343
p.As = AJAL
44-
p.Mark |= NEED_CALL_RELOC
44+
p.Mark |= NEED_JAL_RELOC
4545
p.From.Type = obj.TYPE_REG
4646
p.From.Reg = lr
4747
p.Reg = obj.REG_NONE
@@ -610,7 +610,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
610610
var callCount int
611611
for p := cursym.Func().Text; p != nil; p = p.Link {
612612
markRelocs(p)
613-
if p.Mark&NEED_CALL_RELOC == NEED_CALL_RELOC {
613+
if p.Mark&NEED_JAL_RELOC == NEED_JAL_RELOC {
614614
callCount++
615615
}
616616
}
@@ -664,7 +664,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
664664
jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
665665

666666
p.As = AAUIPC
667-
p.Mark = (p.Mark &^ NEED_CALL_RELOC) | NEED_PCREL_ITYPE_RELOC
667+
p.Mark = (p.Mark &^ NEED_JAL_RELOC) | NEED_CALL_RELOC
668668
p.AddRestSource(obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym})
669669
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
670670
p.Reg = obj.REG_NONE
@@ -2345,13 +2345,13 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
23452345
for p := cursym.Func().Text; p != nil; p = p.Link {
23462346
switch p.As {
23472347
case AJAL:
2348-
if p.Mark&NEED_CALL_RELOC == NEED_CALL_RELOC {
2348+
if p.Mark&NEED_JAL_RELOC == NEED_JAL_RELOC {
23492349
rel := obj.Addrel(cursym)
23502350
rel.Off = int32(p.Pc)
23512351
rel.Siz = 4
23522352
rel.Sym = p.To.Sym
23532353
rel.Add = p.To.Offset
2354-
rel.Type = objabi.R_RISCV_CALL
2354+
rel.Type = objabi.R_RISCV_JAL
23552355
}
23562356
case AJALR:
23572357
if p.To.Sym != nil {
@@ -2361,7 +2361,10 @@ func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
23612361
case AAUIPC, AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
23622362
var addr *obj.Addr
23632363
var rt objabi.RelocType
2364-
if p.Mark&NEED_PCREL_ITYPE_RELOC == NEED_PCREL_ITYPE_RELOC {
2364+
if p.Mark&NEED_CALL_RELOC == NEED_CALL_RELOC {
2365+
rt = objabi.R_RISCV_CALL
2366+
addr = &p.From
2367+
} else if p.Mark&NEED_PCREL_ITYPE_RELOC == NEED_PCREL_ITYPE_RELOC {
23652368
rt = objabi.R_RISCV_PCREL_ITYPE
23662369
addr = &p.From
23672370
} else if p.Mark&NEED_PCREL_STYPE_RELOC == NEED_PCREL_STYPE_RELOC {

src/cmd/internal/objabi/reloctype.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -259,28 +259,31 @@ const (
259259

260260
// RISC-V.
261261

262-
// R_RISCV_CALL relocates a J-type instruction with a 21 bit PC-relative
263-
// address.
264-
R_RISCV_CALL
262+
// R_RISCV_JAL resolves a 20 bit offset for a J-type instruction.
263+
R_RISCV_JAL
265264

266-
// R_RISCV_CALL_TRAMP is the same as R_RISCV_CALL but denotes the use of a
265+
// R_RISCV_JAL_TRAMP is the same as R_RISCV_JAL but denotes the use of a
267266
// trampoline, which we may be able to avoid during relocation. These are
268267
// only used by the linker and are not emitted by the compiler or assembler.
269-
R_RISCV_CALL_TRAMP
268+
R_RISCV_JAL_TRAMP
270269

271-
// R_RISCV_PCREL_ITYPE resolves a 32 bit PC-relative address using an
270+
// R_RISCV_CALL resolves a 32 bit PC-relative address for an AUIPC + JALR
271+
// instruction pair.
272+
R_RISCV_CALL
273+
274+
// R_RISCV_PCREL_ITYPE resolves a 32 bit PC-relative address for an
272275
// AUIPC + I-type instruction pair.
273276
R_RISCV_PCREL_ITYPE
274277

275-
// R_RISCV_PCREL_STYPE resolves a 32 bit PC-relative address using an
278+
// R_RISCV_PCREL_STYPE resolves a 32 bit PC-relative address for an
276279
// AUIPC + S-type instruction pair.
277280
R_RISCV_PCREL_STYPE
278281

279-
// R_RISCV_TLS_IE resolves a 32 bit TLS initial-exec address using an
282+
// R_RISCV_TLS_IE resolves a 32 bit TLS initial-exec address for an
280283
// AUIPC + I-type instruction pair.
281284
R_RISCV_TLS_IE
282285

283-
// R_RISCV_TLS_LE resolves a 32 bit TLS local-exec address using an
286+
// R_RISCV_TLS_LE resolves a 32 bit TLS local-exec address for a
284287
// LUI + I-type instruction sequence.
285288
R_RISCV_TLS_LE
286289

@@ -387,12 +390,13 @@ const (
387390

388391
// IsDirectCall reports whether r is a relocation for a direct call.
389392
// A direct call is a CALL instruction that takes the target address
390-
// as an immediate. The address is embedded into the instruction, possibly
393+
// as an immediate. The address is embedded into the instruction(s), possibly
391394
// with limited width. An indirect call is a CALL instruction that takes
392395
// the target address in register or memory.
393396
func (r RelocType) IsDirectCall() bool {
394397
switch r {
395-
case R_CALL, R_CALLARM, R_CALLARM64, R_CALLLOONG64, R_CALLMIPS, R_CALLPOWER, R_RISCV_CALL, R_RISCV_CALL_TRAMP:
398+
case R_CALL, R_CALLARM, R_CALLARM64, R_CALLLOONG64, R_CALLMIPS, R_CALLPOWER,
399+
R_RISCV_CALL, R_RISCV_JAL, R_RISCV_JAL_TRAMP:
396400
return true
397401
}
398402
return false

src/cmd/internal/objabi/reloctype_string.go

Lines changed: 32 additions & 31 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/link/internal/riscv64/asm.go

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
6363
ldr.Errorf(s, "unknown symbol %s in RISCV call", ldr.SymName(targ))
6464
}
6565
su := ldr.MakeSymbolUpdater(s)
66-
su.SetRelocType(rIdx, objabi.R_RISCV_PCREL_ITYPE)
66+
su.SetRelocType(rIdx, objabi.R_RISCV_CALL)
6767
return true
6868

6969
case objabi.ElfRelocOffset + objabi.RelocType(elf.R_RISCV_GOT_HI20):
@@ -130,7 +130,7 @@ func adddynrel(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, s loade
130130
r = relocs.At(rIdx)
131131

132132
switch r.Type() {
133-
case objabi.R_RISCV_PCREL_ITYPE:
133+
case objabi.R_RISCV_CALL:
134134
if targType != sym.SDYNIMPORT {
135135
// nothing to do, the relocation will be laid out in reloc
136136
return true
@@ -228,12 +228,12 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
228228
}
229229
out.Write64(uint64(r.Xadd))
230230

231-
case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
231+
case objabi.R_RISCV_JAL, objabi.R_RISCV_JAL_TRAMP:
232232
out.Write64(uint64(sectoff))
233233
out.Write64(uint64(elf.R_RISCV_JAL) | uint64(elfsym)<<32)
234234
out.Write64(uint64(r.Xadd))
235235

236-
case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE:
236+
case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE:
237237
// Find the text symbol for the AUIPC instruction targeted
238238
// by this relocation.
239239
relocs := ldr.Relocs(s)
@@ -256,7 +256,7 @@ func elfreloc1(ctxt *ld.Link, out *ld.OutBuf, ldr *loader.Loader, s loader.Sym,
256256
//
257257
var hiRel, loRel elf.R_RISCV
258258
switch r.Type {
259-
case objabi.R_RISCV_PCREL_ITYPE:
259+
case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE:
260260
hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_I
261261
case objabi.R_RISCV_PCREL_STYPE:
262262
hiRel, loRel = elf.R_RISCV_PCREL_HI20, elf.R_RISCV_PCREL_LO12_S
@@ -399,20 +399,20 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
399399
// If the call points to a trampoline, see if we can reach the symbol
400400
// directly. This situation can occur when the relocation symbol is
401401
// not assigned an address until after the trampolines are generated.
402-
if r.Type() == objabi.R_RISCV_CALL_TRAMP {
402+
if r.Type() == objabi.R_RISCV_JAL_TRAMP {
403403
relocs := ldr.Relocs(rs)
404404
if relocs.Count() != 1 {
405405
ldr.Errorf(s, "trampoline %v has %d relocations", ldr.SymName(rs), relocs.Count())
406406
}
407407
tr := relocs.At(0)
408-
if tr.Type() != objabi.R_RISCV_PCREL_ITYPE {
408+
if tr.Type() != objabi.R_RISCV_CALL {
409409
ldr.Errorf(s, "trampoline %v has unexpected relocation %v", ldr.SymName(rs), tr.Type())
410410
}
411411
trs := tr.Sym()
412412
if ldr.SymValue(trs) != 0 && ldr.SymType(trs) != sym.SDYNIMPORT && ldr.SymType(trs) != sym.SUNDEFEXT {
413413
trsOff := ldr.SymValue(trs) + tr.Add() - pc
414414
if trsOff >= -(1<<20) && trsOff < (1<<20) {
415-
r.SetType(objabi.R_RISCV_CALL)
415+
r.SetType(objabi.R_RISCV_JAL)
416416
r.SetSym(trs)
417417
r.SetAdd(tr.Add())
418418
rs = trs
@@ -423,10 +423,10 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
423423

424424
if target.IsExternal() {
425425
switch r.Type() {
426-
case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
426+
case objabi.R_RISCV_JAL, objabi.R_RISCV_JAL_TRAMP:
427427
return val, 1, true
428428

429-
case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE:
429+
case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE:
430430
return val, 2, true
431431
}
432432

@@ -436,11 +436,11 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
436436
off := ldr.SymValue(rs) + r.Add() - pc
437437

438438
switch r.Type() {
439-
case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
439+
case objabi.R_RISCV_JAL, objabi.R_RISCV_JAL_TRAMP:
440440
// Generate instruction immediates.
441441
imm, err := riscv.EncodeJImmediate(off)
442442
if err != nil {
443-
ldr.Errorf(s, "cannot encode R_RISCV_CALL relocation offset for %s: %v", ldr.SymName(rs), err)
443+
ldr.Errorf(s, "cannot encode J-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
444444
}
445445
immMask := int64(riscv.JTypeImmMask)
446446

@@ -574,31 +574,31 @@ func archreloc(target *ld.Target, ldr *loader.Loader, syms *ld.ArchSyms, r loade
574574
ins = (ins &^ immMask) | int64(uint32(imm))
575575
return ins, 0, true
576576

577-
case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
577+
case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE:
578578
// Generate AUIPC and second instruction immediates.
579579
low, high, err := riscv.Split32BitImmediate(off)
580580
if err != nil {
581-
ldr.Errorf(s, "R_RISCV_PCREL_ relocation does not fit in 32 bits: %d", off)
581+
ldr.Errorf(s, "pc-relative relocation does not fit in 32 bits: %d", off)
582582
}
583583

584584
auipcImm, err := riscv.EncodeUImmediate(high)
585585
if err != nil {
586-
ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ AUIPC relocation offset for %s: %v", ldr.SymName(rs), err)
586+
ldr.Errorf(s, "cannot encode AUIPC relocation offset for %s: %v", ldr.SymName(rs), err)
587587
}
588588

589589
var secondImm, secondImmMask int64
590590
switch r.Type() {
591-
case objabi.R_RISCV_PCREL_ITYPE:
591+
case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE:
592592
secondImmMask = riscv.ITypeImmMask
593593
secondImm, err = riscv.EncodeIImmediate(low)
594594
if err != nil {
595-
ldr.Errorf(s, "cannot encode R_RISCV_PCREL_ITYPE I-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
595+
ldr.Errorf(s, "cannot encode I-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
596596
}
597597
case objabi.R_RISCV_PCREL_STYPE:
598598
secondImmMask = riscv.STypeImmMask
599599
secondImm, err = riscv.EncodeSImmediate(low)
600600
if err != nil {
601-
ldr.Errorf(s, "cannot encode R_RISCV_PCREL_STYPE S-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
601+
ldr.Errorf(s, "cannot encode S-type instruction relocation offset for %s: %v", ldr.SymName(rs), err)
602602
}
603603
default:
604604
panic(fmt.Sprintf("unknown relocation type: %v", r.Type()))
@@ -623,10 +623,10 @@ func archrelocvariant(*ld.Target, *loader.Loader, loader.Reloc, sym.RelocVariant
623623

624624
func extreloc(target *ld.Target, ldr *loader.Loader, r loader.Reloc, s loader.Sym) (loader.ExtReloc, bool) {
625625
switch r.Type() {
626-
case objabi.R_RISCV_CALL, objabi.R_RISCV_CALL_TRAMP:
626+
case objabi.R_RISCV_JAL, objabi.R_RISCV_JAL_TRAMP:
627627
return ld.ExtrelocSimple(ldr, r), true
628628

629-
case objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE:
629+
case objabi.R_RISCV_CALL, objabi.R_RISCV_PCREL_ITYPE, objabi.R_RISCV_PCREL_STYPE, objabi.R_RISCV_TLS_IE, objabi.R_RISCV_TLS_LE:
630630
return ld.ExtrelocViaOuterSym(ldr, r, s), true
631631
}
632632
return loader.ExtReloc{}, false
@@ -637,7 +637,7 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
637637
r := relocs.At(ri)
638638

639639
switch r.Type() {
640-
case objabi.R_RISCV_CALL:
640+
case objabi.R_RISCV_JAL:
641641
pc := ldr.SymValue(s) + int64(r.Off())
642642
off := ldr.SymValue(rs) + r.Add() - pc
643643

@@ -691,13 +691,16 @@ func trampoline(ctxt *ld.Link, ldr *loader.Loader, ri int, rs, s loader.Sym) {
691691
// address, so we have to assume a trampoline is required. Mark
692692
// this as a call via a trampoline so that we can potentially
693693
// switch to a direct call during relocation.
694-
sb.SetRelocType(ri, objabi.R_RISCV_CALL_TRAMP)
694+
sb.SetRelocType(ri, objabi.R_RISCV_JAL_TRAMP)
695695
}
696696
relocs := sb.Relocs()
697697
r := relocs.At(ri)
698698
r.SetSym(tramp)
699699
r.SetAdd(0)
700700

701+
case objabi.R_RISCV_CALL:
702+
// Nothing to do, already using AUIPC+JALR.
703+
701704
default:
702705
ctxt.Errorf(s, "trampoline called with non-jump reloc: %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
703706
}
@@ -707,7 +710,7 @@ func genCallTramp(arch *sys.Arch, linkmode ld.LinkMode, ldr *loader.Loader, tram
707710
tramp.AddUint32(arch, 0x00000f97) // AUIPC $0, X31
708711
tramp.AddUint32(arch, 0x000f8067) // JALR X0, (X31)
709712

710-
r, _ := tramp.AddRel(objabi.R_RISCV_PCREL_ITYPE)
713+
r, _ := tramp.AddRel(objabi.R_RISCV_CALL)
711714
r.SetSiz(8)
712715
r.SetSym(target)
713716
r.SetAdd(offset)

0 commit comments

Comments
 (0)