Skip to content

Commit 9c507e7

Browse files
committed
cmd/link, runtime: on Wasm, put only function index in method table and func table
In the type descriptor's method table, it contains relative PCs of the methods (relative to the start of the text section) stored as 32-bit offsets. On Wasm, a PC is PC_F<<16 + PC_B, where PC_F is the function index, and PC_B is the block index. When there are more than 65536 functions, the PC will not fit into 32-bit (and relative to the section start doesn't help). Since there are no more bits for the function index, and the method table always targets the entry of a method, we put just the PC_F there, and rewrite back to a full PC at run time when we need the PC. This way we can have more than 65536 functions. The func table also contains 32-bit relative PCs, and it also always points to function entries. Do the same there, as well as other places where we use relative text offsets. Also add the relocation type in the relocation overflow error message. Also add check for function too big on Wasm. If a function has more than 65536 blocks, PC_B will overflow and PC = PC_F<<16 + PC_B will points to the wrong function. Fixes #64856. Change-Id: If9c307e9fb1641f367a5f19c39f88f455805d0bb Reviewed-on: https://go-review.googlesource.com/c/go/+/552835 Reviewed-by: Than McIntosh <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 9782dcf commit 9c507e7

File tree

4 files changed

+77
-18
lines changed

4 files changed

+77
-18
lines changed

src/cmd/internal/obj/wasm/wasmobj.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
372372
}
373373
tableIdxs = append(tableIdxs, uint64(numResumePoints))
374374
s.Size = pc + 1
375+
if pc >= 1<<16 {
376+
ctxt.Diag("function too big: %s exceeds 65536 blocks", s)
377+
}
375378

376379
if needMoreStack {
377380
p := pMorestack

src/cmd/link/internal/ld/data.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -496,6 +496,15 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
496496
// to the start of the first text section, even if there are multiple.
497497
if sect.Name == ".text" {
498498
o = ldr.SymValue(rs) - int64(Segtext.Sections[0].Vaddr) + r.Add()
499+
if target.IsWasm() {
500+
// On Wasm, textoff (e.g. in the method table) is just the function index,
501+
// whereas the "PC" (rs's Value) is function index << 16 + block index (see
502+
// ../wasm/asm.go:assignAddress).
503+
if o&(1<<16-1) != 0 {
504+
st.err.Errorf(s, "textoff relocation %s does not target function entry: %s %#x", rt, ldr.SymName(rs), o)
505+
}
506+
o >>= 16
507+
}
499508
} else {
500509
o = ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + r.Add()
501510
}
@@ -606,16 +615,16 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
606615
P[off] = byte(int8(o))
607616
case 2:
608617
if (rt == objabi.R_PCREL || rt == objabi.R_CALL) && o != int64(int16(o)) {
609-
st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
618+
st.err.Errorf(s, "pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), o)
610619
} else if o != int64(int16(o)) && o != int64(uint16(o)) {
611-
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
620+
st.err.Errorf(s, "non-pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), uint64(o))
612621
}
613622
target.Arch.ByteOrder.PutUint16(P[off:], uint16(o))
614623
case 4:
615624
if (rt == objabi.R_PCREL || rt == objabi.R_CALL) && o != int64(int32(o)) {
616-
st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
625+
st.err.Errorf(s, "pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), o)
617626
} else if o != int64(int32(o)) && o != int64(uint32(o)) {
618-
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
627+
st.err.Errorf(s, "non-pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), uint64(o))
619628
}
620629
target.Arch.ByteOrder.PutUint32(P[off:], uint32(o))
621630
case 8:

src/cmd/link/internal/ld/pcln.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -614,16 +614,36 @@ func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64
614614
return size, startLocations
615615
}
616616

617+
// textOff computes the offset of a text symbol, relative to textStart,
618+
// similar to an R_ADDROFF relocation, for various runtime metadata and
619+
// tables (see runtime/symtab.go:(*moduledata).textAddr).
620+
func textOff(ctxt *Link, s loader.Sym, textStart int64) uint32 {
621+
ldr := ctxt.loader
622+
off := ldr.SymValue(s) - textStart
623+
if off < 0 {
624+
panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
625+
}
626+
if ctxt.IsWasm() {
627+
// On Wasm, the function table contains just the function index, whereas
628+
// the "PC" (s's Value) is function index << 16 + block index (see
629+
// ../wasm/asm.go:assignAddress).
630+
if off&(1<<16-1) != 0 {
631+
ctxt.Errorf(s, "nonzero PC_B at function entry: %#x", off)
632+
}
633+
off >>= 16
634+
}
635+
if int64(uint32(off)) != off {
636+
ctxt.Errorf(s, "textOff overflow: %#x", off)
637+
}
638+
return uint32(off)
639+
}
640+
617641
// writePCToFunc writes the PC->func lookup table.
618642
func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32) {
619643
ldr := ctxt.loader
620644
textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
621645
pcOff := func(s loader.Sym) uint32 {
622-
off := ldr.SymValue(s) - textStart
623-
if off < 0 {
624-
panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
625-
}
626-
return uint32(off)
646+
return textOff(ctxt, s, textStart)
627647
}
628648
for i, s := range funcs {
629649
sb.SetUint32(ctxt.Arch, int64(i*2*4), pcOff(s))
@@ -632,7 +652,11 @@ func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, sta
632652

633653
// Final entry of table is just end pc offset.
634654
lastFunc := funcs[len(funcs)-1]
635-
sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, pcOff(lastFunc)+uint32(ldr.SymSize(lastFunc)))
655+
lastPC := pcOff(lastFunc) + uint32(ldr.SymSize(lastFunc))
656+
if ctxt.IsWasm() {
657+
lastPC = pcOff(lastFunc) + 1 // On Wasm it is function index (see above)
658+
}
659+
sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, lastPC)
636660
}
637661

638662
// writeFuncs writes the func structures and pcdata to runtime.functab.
@@ -646,7 +670,7 @@ func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSym
646670
var pcsp, pcfile, pcline, pcinline loader.Sym
647671
var pcdata []loader.Sym
648672

649-
// Write the individual func objects.
673+
// Write the individual func objects (runtime._func struct).
650674
for i, s := range funcs {
651675
startLine := int32(0)
652676
fi := ldr.FuncInfo(s)
@@ -658,10 +682,7 @@ func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSym
658682

659683
off := int64(startLocations[i])
660684
// entryOff uint32 (offset of func entry PC from textStart)
661-
entryOff := ldr.SymValue(s) - textStart
662-
if entryOff < 0 {
663-
panic(fmt.Sprintf("expected func %s(%x) to be placed before or at textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
664-
}
685+
entryOff := textOff(ctxt, s, textStart)
665686
off = sb.SetUint32(ctxt.Arch, off, uint32(entryOff))
666687

667688
// nameOff int32

src/runtime/symtab.go

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,8 +647,15 @@ func moduledataverify1(datap *moduledata) {
647647

648648
min := datap.textAddr(datap.ftab[0].entryoff)
649649
max := datap.textAddr(datap.ftab[nftab].entryoff)
650-
if datap.minpc != min || datap.maxpc != max {
651-
println("minpc=", hex(datap.minpc), "min=", hex(min), "maxpc=", hex(datap.maxpc), "max=", hex(max))
650+
minpc := datap.minpc
651+
maxpc := datap.maxpc
652+
if GOARCH == "wasm" {
653+
// On Wasm, the func table contains the function index, whereas
654+
// the "PC" is function index << 16 + block index.
655+
maxpc = alignUp(maxpc, 1<<16) // round up for end PC
656+
}
657+
if minpc != min || maxpc != max {
658+
println("minpc=", hex(minpc), "min=", hex(min), "maxpc=", hex(maxpc), "max=", hex(max))
652659
throw("minpc or maxpc invalid")
653660
}
654661

@@ -694,6 +701,11 @@ func (md *moduledata) textAddr(off32 uint32) uintptr {
694701
throw("runtime: text offset out of range")
695702
}
696703
}
704+
if GOARCH == "wasm" {
705+
// On Wasm, a text offset (e.g. in the method table) is function index, whereas
706+
// the "PC" is function index << 16 + block index.
707+
res <<= 16
708+
}
697709
return res
698710
}
699711

@@ -704,8 +716,17 @@ func (md *moduledata) textAddr(off32 uint32) uintptr {
704716
//
705717
//go:nosplit
706718
func (md *moduledata) textOff(pc uintptr) (uint32, bool) {
707-
res := uint32(pc - md.text)
719+
off := pc - md.text
720+
if GOARCH == "wasm" {
721+
// On Wasm, the func table contains the function index, whereas
722+
// the "PC" is function index << 16 + block index.
723+
off >>= 16
724+
}
725+
res := uint32(off)
708726
if len(md.textsectmap) > 1 {
727+
if GOARCH == "wasm" {
728+
fatal("unexpected multiple text sections on Wasm")
729+
}
709730
for i, sect := range md.textsectmap {
710731
if sect.baseaddr > pc {
711732
// pc is not in any section.
@@ -904,6 +925,11 @@ func findfunc(pc uintptr) funcInfo {
904925
}
905926

906927
x := uintptr(pcOff) + datap.text - datap.minpc // TODO: are datap.text and datap.minpc always equal?
928+
if GOARCH == "wasm" {
929+
// On Wasm, pcOff is the function index, whereas
930+
// the "PC" is function index << 16 + block index.
931+
x = uintptr(pcOff)<<16 + datap.text - datap.minpc
932+
}
907933
b := x / abi.FuncTabBucketSize
908934
i := x % abi.FuncTabBucketSize / (abi.FuncTabBucketSize / nsub)
909935

0 commit comments

Comments
 (0)