mirror of
https://github.com/golang/go.git
synced 2025-10-19 19:13:18 +00:00
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 <thanm@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
9782dcfd16
commit
9c507e7942
4 changed files with 77 additions and 18 deletions
|
@ -372,6 +372,9 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
|||
}
|
||||
tableIdxs = append(tableIdxs, uint64(numResumePoints))
|
||||
s.Size = pc + 1
|
||||
if pc >= 1<<16 {
|
||||
ctxt.Diag("function too big: %s exceeds 65536 blocks", s)
|
||||
}
|
||||
|
||||
if needMoreStack {
|
||||
p := pMorestack
|
||||
|
|
|
@ -496,6 +496,15 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
|
|||
// to the start of the first text section, even if there are multiple.
|
||||
if sect.Name == ".text" {
|
||||
o = ldr.SymValue(rs) - int64(Segtext.Sections[0].Vaddr) + r.Add()
|
||||
if target.IsWasm() {
|
||||
// On Wasm, textoff (e.g. in the method table) is just the function index,
|
||||
// whereas the "PC" (rs's Value) is function index << 16 + block index (see
|
||||
// ../wasm/asm.go:assignAddress).
|
||||
if o&(1<<16-1) != 0 {
|
||||
st.err.Errorf(s, "textoff relocation %s does not target function entry: %s %#x", rt, ldr.SymName(rs), o)
|
||||
}
|
||||
o >>= 16
|
||||
}
|
||||
} else {
|
||||
o = ldr.SymValue(rs) - int64(ldr.SymSect(rs).Vaddr) + r.Add()
|
||||
}
|
||||
|
@ -606,16 +615,16 @@ func (st *relocSymState) relocsym(s loader.Sym, P []byte) {
|
|||
P[off] = byte(int8(o))
|
||||
case 2:
|
||||
if (rt == objabi.R_PCREL || rt == objabi.R_CALL) && o != int64(int16(o)) {
|
||||
st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
|
||||
st.err.Errorf(s, "pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), o)
|
||||
} else if o != int64(int16(o)) && o != int64(uint16(o)) {
|
||||
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
|
||||
st.err.Errorf(s, "non-pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), uint64(o))
|
||||
}
|
||||
target.Arch.ByteOrder.PutUint16(P[off:], uint16(o))
|
||||
case 4:
|
||||
if (rt == objabi.R_PCREL || rt == objabi.R_CALL) && o != int64(int32(o)) {
|
||||
st.err.Errorf(s, "pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), o)
|
||||
st.err.Errorf(s, "pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), o)
|
||||
} else if o != int64(int32(o)) && o != int64(uint32(o)) {
|
||||
st.err.Errorf(s, "non-pc-relative relocation address for %s is too big: %#x", ldr.SymName(rs), uint64(o))
|
||||
st.err.Errorf(s, "non-pc-relative relocation %s address for %s is too big: %#x", rt, ldr.SymName(rs), uint64(o))
|
||||
}
|
||||
target.Arch.ByteOrder.PutUint32(P[off:], uint32(o))
|
||||
case 8:
|
||||
|
|
|
@ -614,16 +614,36 @@ func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64
|
|||
return size, startLocations
|
||||
}
|
||||
|
||||
// textOff computes the offset of a text symbol, relative to textStart,
|
||||
// similar to an R_ADDROFF relocation, for various runtime metadata and
|
||||
// tables (see runtime/symtab.go:(*moduledata).textAddr).
|
||||
func textOff(ctxt *Link, s loader.Sym, textStart int64) uint32 {
|
||||
ldr := ctxt.loader
|
||||
off := ldr.SymValue(s) - textStart
|
||||
if off < 0 {
|
||||
panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
|
||||
}
|
||||
if ctxt.IsWasm() {
|
||||
// On Wasm, the function table contains just the function index, whereas
|
||||
// the "PC" (s's Value) is function index << 16 + block index (see
|
||||
// ../wasm/asm.go:assignAddress).
|
||||
if off&(1<<16-1) != 0 {
|
||||
ctxt.Errorf(s, "nonzero PC_B at function entry: %#x", off)
|
||||
}
|
||||
off >>= 16
|
||||
}
|
||||
if int64(uint32(off)) != off {
|
||||
ctxt.Errorf(s, "textOff overflow: %#x", off)
|
||||
}
|
||||
return uint32(off)
|
||||
}
|
||||
|
||||
// writePCToFunc writes the PC->func lookup table.
|
||||
func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32) {
|
||||
ldr := ctxt.loader
|
||||
textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0))
|
||||
pcOff := func(s loader.Sym) uint32 {
|
||||
off := ldr.SymValue(s) - textStart
|
||||
if off < 0 {
|
||||
panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
|
||||
}
|
||||
return uint32(off)
|
||||
return textOff(ctxt, s, textStart)
|
||||
}
|
||||
for i, s := range funcs {
|
||||
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
|
|||
|
||||
// Final entry of table is just end pc offset.
|
||||
lastFunc := funcs[len(funcs)-1]
|
||||
sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, pcOff(lastFunc)+uint32(ldr.SymSize(lastFunc)))
|
||||
lastPC := pcOff(lastFunc) + uint32(ldr.SymSize(lastFunc))
|
||||
if ctxt.IsWasm() {
|
||||
lastPC = pcOff(lastFunc) + 1 // On Wasm it is function index (see above)
|
||||
}
|
||||
sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, lastPC)
|
||||
}
|
||||
|
||||
// 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
|
|||
var pcsp, pcfile, pcline, pcinline loader.Sym
|
||||
var pcdata []loader.Sym
|
||||
|
||||
// Write the individual func objects.
|
||||
// Write the individual func objects (runtime._func struct).
|
||||
for i, s := range funcs {
|
||||
startLine := int32(0)
|
||||
fi := ldr.FuncInfo(s)
|
||||
|
@ -658,10 +682,7 @@ func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSym
|
|||
|
||||
off := int64(startLocations[i])
|
||||
// entryOff uint32 (offset of func entry PC from textStart)
|
||||
entryOff := ldr.SymValue(s) - textStart
|
||||
if entryOff < 0 {
|
||||
panic(fmt.Sprintf("expected func %s(%x) to be placed before or at textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart))
|
||||
}
|
||||
entryOff := textOff(ctxt, s, textStart)
|
||||
off = sb.SetUint32(ctxt.Arch, off, uint32(entryOff))
|
||||
|
||||
// nameOff int32
|
||||
|
|
|
@ -647,8 +647,15 @@ func moduledataverify1(datap *moduledata) {
|
|||
|
||||
min := datap.textAddr(datap.ftab[0].entryoff)
|
||||
max := datap.textAddr(datap.ftab[nftab].entryoff)
|
||||
if datap.minpc != min || datap.maxpc != max {
|
||||
println("minpc=", hex(datap.minpc), "min=", hex(min), "maxpc=", hex(datap.maxpc), "max=", hex(max))
|
||||
minpc := datap.minpc
|
||||
maxpc := datap.maxpc
|
||||
if GOARCH == "wasm" {
|
||||
// On Wasm, the func table contains the function index, whereas
|
||||
// the "PC" is function index << 16 + block index.
|
||||
maxpc = alignUp(maxpc, 1<<16) // round up for end PC
|
||||
}
|
||||
if minpc != min || maxpc != max {
|
||||
println("minpc=", hex(minpc), "min=", hex(min), "maxpc=", hex(maxpc), "max=", hex(max))
|
||||
throw("minpc or maxpc invalid")
|
||||
}
|
||||
|
||||
|
@ -694,6 +701,11 @@ func (md *moduledata) textAddr(off32 uint32) uintptr {
|
|||
throw("runtime: text offset out of range")
|
||||
}
|
||||
}
|
||||
if GOARCH == "wasm" {
|
||||
// On Wasm, a text offset (e.g. in the method table) is function index, whereas
|
||||
// the "PC" is function index << 16 + block index.
|
||||
res <<= 16
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
|
@ -704,8 +716,17 @@ func (md *moduledata) textAddr(off32 uint32) uintptr {
|
|||
//
|
||||
//go:nosplit
|
||||
func (md *moduledata) textOff(pc uintptr) (uint32, bool) {
|
||||
res := uint32(pc - md.text)
|
||||
off := pc - md.text
|
||||
if GOARCH == "wasm" {
|
||||
// On Wasm, the func table contains the function index, whereas
|
||||
// the "PC" is function index << 16 + block index.
|
||||
off >>= 16
|
||||
}
|
||||
res := uint32(off)
|
||||
if len(md.textsectmap) > 1 {
|
||||
if GOARCH == "wasm" {
|
||||
fatal("unexpected multiple text sections on Wasm")
|
||||
}
|
||||
for i, sect := range md.textsectmap {
|
||||
if sect.baseaddr > pc {
|
||||
// pc is not in any section.
|
||||
|
@ -904,6 +925,11 @@ func findfunc(pc uintptr) funcInfo {
|
|||
}
|
||||
|
||||
x := uintptr(pcOff) + datap.text - datap.minpc // TODO: are datap.text and datap.minpc always equal?
|
||||
if GOARCH == "wasm" {
|
||||
// On Wasm, pcOff is the function index, whereas
|
||||
// the "PC" is function index << 16 + block index.
|
||||
x = uintptr(pcOff)<<16 + datap.text - datap.minpc
|
||||
}
|
||||
b := x / abi.FuncTabBucketSize
|
||||
i := x % abi.FuncTabBucketSize / (abi.FuncTabBucketSize / nsub)
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue