Files
llgo/ssa/abitype.go
xgopilot dba9bcc4e4 fix(ssa): use correct package path for interface metadata in abiInterfaceOf
When converting concrete type pointers to interfaces with private methods
across packages, the interface metadata's PkgPath was incorrectly set to
the current compilation package instead of the interface definition package.

This caused the runtime to only fill exported methods in the itab, leaving
private method slots as NULL (0x0), which led to segmentation faults when
calling these private methods.

The fix extracts the package path from the interface's private methods
(if any) instead of using the current package path. This ensures the
runtime receives the correct package path for the visibility check.

Fixes #1370

Generated with [codeagent](https://github.com/qbox/codeagent)
Co-authored-by: luoliwoshang <luoliwoshang@users.noreply.github.com>
2025-10-23 10:30:00 +00:00

519 lines
14 KiB
Go

/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ssa
import (
"go/token"
"go/types"
"unsafe"
"github.com/goplus/llgo/ssa/abi"
"github.com/goplus/llvm"
"golang.org/x/tools/go/types/typeutil"
)
// -----------------------------------------------------------------------------
// abiBasic returns the abi type of the specified basic kind.
func (b Builder) abiBasic(t *types.Basic) func() Expr {
/*
TODO(xsw):
return b.abiExtern(abi.BasicName(t))
*/
return func() Expr {
kind := int(abi.BasicKind(t))
dk, _, _ := abi.DataKindOf(t, 0, b.Prog.is32Bits)
switch dk {
case abi.Integer, abi.BitCast, abi.Pointer:
const kindDirectIface = 1 << 5
kind |= kindDirectIface
}
return b.InlineCall(b.Pkg.rtFunc("Basic"), b.Prog.Val(kind))
}
}
/*
func (b Builder) abiExtern(name string) Expr {
g := b.Pkg.NewVarFrom(name, b.Prog.AbiTypePtrPtr())
return b.Load(g.Expr)
}
*/
func (b Builder) abiTypeOf(t types.Type) func() Expr {
switch t := t.(type) {
case *types.Basic:
return b.abiBasic(t)
case *types.Pointer:
return b.abiPointerOf(t)
case *types.Struct:
return b.abiStructOf(t)
case *types.Named:
if _, ok := t.Underlying().(*types.Interface); ok {
return b.abiNamedInterfaceOf(t)
}
return b.abiNamedOf(t)
case *types.Interface:
return b.abiInterfaceOf(t)
case *types.Signature:
return b.abiFuncOf(t)
case *types.Slice:
return b.abiSliceOf(t)
case *types.Array:
return b.abiArrayOf(t)
case *types.Chan:
return b.abiChanOf(t)
case *types.Map:
return b.abiMapOf(t)
case *types.Alias:
return b.abiTypeOf(types.Unalias(t))
}
panic("todo")
}
func (b Builder) abiTupleOf(t *types.Tuple) func() Expr {
n := t.Len()
tuple := make([]func() Expr, n)
for i := 0; i < n; i++ {
tuple[i] = b.abiTypeOf(t.At(i).Type())
}
return func() Expr {
prog := b.Prog
tSlice := prog.Slice(prog.AbiTypePtr())
elts := make([]Expr, n)
for i := 0; i < n; i++ {
elts[i] = tuple[i]()
}
return b.SliceLit(tSlice, elts...)
}
}
// func Func(in, out []*Type, variadic bool)
func (b Builder) abiFuncOf(sig *types.Signature) func() Expr {
params := b.abiTupleOf(sig.Params())
results := b.abiTupleOf(sig.Results())
return func() Expr {
prog := b.Prog
pkg := b.Pkg
fn := pkg.rtFunc("Func")
variadic := prog.Val(sig.Variadic())
return b.Call(fn, params(), results(), variadic)
}
}
// Imethod{name string, typ *FuncType}
func (b Builder) abiImethodOf(mName string, typ Expr) Expr {
prog := b.Prog
name := b.Str(mName)
return b.aggregateValue(prog.rtType("Imethod"), name.impl, typ.impl)
}
func (b Builder) abiMethods(t *types.Named) (ret, pret int) {
methods := typeutil.IntuitiveMethodSet(t, nil)
pret = len(methods)
for _, m := range methods {
if _, ok := m.Recv().(*types.Pointer); ok {
continue
}
ret++
}
return
}
// Method{name string, typ *FuncType, ifn, tfn abi.Text}
func (b Builder) abiMethodOf(mPkg *types.Package, mName string, mSig *types.Signature /*, bg Background = InGo */) (mthd, ptrMthd Expr) {
prog := b.Prog
name := b.Str(mName).impl
if !token.IsExported(mName) {
name = b.Str(abi.FullName(mPkg, mName)).impl
}
abiSigGo := types.NewSignatureType(nil, nil, nil, mSig.Params(), mSig.Results(), mSig.Variadic())
abiSig := prog.FuncDecl(abiSigGo, InGo).raw.Type
abiTyp := b.abiType(abiSig)
abiTypImpl := abiTyp.impl
recv := mSig.Recv()
recvType := recv.Type()
if _, ok := recvType.(*types.Pointer); ok {
ptrMthd, _ = b.abiMthd(mPkg, mName, mSig, name, abiTypImpl, llvm.Value{})
return
}
ptrRecv := types.NewVar(0, nil, "", types.NewPointer(recvType))
ptrSig := types.NewSignatureType(ptrRecv, nil, nil, mSig.Params(), mSig.Results(), mSig.Variadic())
ptrMthd, ifn := b.abiMthd(mPkg, mName, ptrSig, name, abiTypImpl, llvm.Value{})
mthd, _ = b.abiMthd(mPkg, mName, mSig, name, abiTypImpl, ifn)
return
}
func (b Builder) abiMthd(mPkg *types.Package, mName string, mSig *types.Signature, name, abiTyp, ifn llvm.Value) (ret Expr, tfn llvm.Value) {
fullName := FuncName(mPkg, mName, mSig.Recv(), false)
if mSig.TypeParams().Len() > 0 || mSig.RecvTypeParams().Len() > 0 {
if !b.Pkg.Prog.FuncCompiled(fullName) {
return
}
}
if b.Pkg.fnlink != nil {
fullName = b.Pkg.fnlink(fullName)
}
tfn = b.Pkg.NewFunc(fullName, mSig, InGo).impl // TODO(xsw): use rawType to speed up
if ifn.IsNil() {
ifn = tfn
}
ret = b.aggregateValue(b.Prog.rtType("Method"), name, abiTyp, ifn, tfn)
return
}
// func Interface(pkgPath, name string, methods []abi.Imethod)
func (b Builder) abiInterfaceOf(t *types.Interface) func() Expr {
n := t.NumMethods()
typs := make([]Expr, n)
for i := 0; i < n; i++ {
m := t.Method(i)
typs[i] = b.abiType(m.Type())
}
return func() Expr {
prog := b.Prog
methods := make([]Expr, n)
pkgPath := ""
for i := 0; i < n; i++ {
m := t.Method(i)
mName := m.Name()
if !token.IsExported(mName) {
if pkgPath == "" {
if mPkg := m.Pkg(); mPkg != nil {
pkgPath = mPkg.Path()
}
}
mName = abi.FullName(m.Pkg(), mName)
}
methods[i] = b.abiImethodOf(mName, typs[i])
}
pkg := b.Pkg
if pkgPath == "" {
pkgPath = pkg.Path()
}
fn := pkg.rtFunc("Interface")
tSlice := lastParamType(prog, fn)
methodSlice := b.SliceLit(tSlice, methods...)
return b.Call(fn, b.Str(pkgPath), methodSlice)
}
}
func (b Builder) abiInitNamedInterface(ret Expr, t *types.Interface) func() Expr {
n := t.NumMethods()
typs := make([]Expr, n)
for i := 0; i < n; i++ {
m := t.Method(i)
typs[i] = b.abiType(m.Type())
}
return func() Expr {
prog := b.Prog
methods := make([]Expr, n)
for i := 0; i < n; i++ {
m := t.Method(i)
mName := m.Name()
if !token.IsExported(mName) {
mName = abi.FullName(m.Pkg(), mName)
}
methods[i] = b.abiImethodOf(mName, typs[i])
}
fn := b.Pkg.rtFunc("InitNamedInterface")
tSlice := lastParamType(prog, fn)
methodSlice := b.SliceLit(tSlice, methods...)
return b.Call(fn, ret, methodSlice)
}
}
// func NewNamed(kind abi.Kind, methods, ptrMethods int)
func (b Builder) abiNamedOf(t *types.Named) func() Expr {
expr := b.abiNamed(t)
return func() Expr {
return expr
}
}
func (b Builder) abiNamed(t *types.Named) Expr {
pkg := b.Pkg
tunder := t.Underlying()
kind := int(abi.UnderlyingKind(tunder))
size := b.sizeof(tunder)
numMethods, numPtrMethods := b.abiMethods(t)
newNamed := pkg.rtFunc("NewNamed")
obj := t.Obj()
expr := b.Call(newNamed, b.Str(abi.PathOf(obj.Pkg())), b.Str(abi.NamedName(t)), b.Prog.Val(kind), b.Prog.IntVal(uint64(size), b.Prog.Uintptr()), b.Prog.Val(numMethods), b.Prog.Val(numPtrMethods))
return expr
}
func (b Builder) abiNamedInterfaceOf(t *types.Named) func() Expr {
obj := t.Obj()
fn := b.Pkg.rtFunc("NewNamedInterface")
expr := b.Call(fn, b.Str(abi.PathOf(obj.Pkg())), b.Str(obj.Name()))
return func() Expr {
return expr
}
}
func (b Builder) sizeof(t types.Type) int64 {
sizes := (*goProgram)(unsafe.Pointer(b.Prog))
return sizes.Sizeof(t)
}
// func InitNamed(ret *Type, pkgPath, name string, underlying *Type, methods, ptrMethods []Method)
func (b Builder) abiInitNamed(ret Expr, t *types.Named) func() Expr {
under := b.abiType(t.Underlying())
return func() Expr {
pkg := b.Pkg
prog := b.Prog
var initNamed = pkg.rtFunc("InitNamed")
var tSlice = lastParamType(prog, initNamed)
mset := typeutil.IntuitiveMethodSet(t, nil)
n := len(mset)
var methods, ptrMethods Expr
if n == 0 {
methods = prog.Zero(tSlice)
ptrMethods = methods
} else {
var mthds []Expr
var ptrMthds = make([]Expr, 0, n)
for i := 0; i < n; i++ {
var mPkg *types.Package
var mSig *types.Signature
m := mset[i]
obj := m.Obj()
mName := obj.Name()
if token.IsExported(mName) {
mPkg = t.Obj().Pkg()
mSig = m.Type().(*types.Signature)
} else {
mPkg = obj.Pkg()
mSig = obj.Type().(*types.Signature)
}
mthd, ptrMthd := b.abiMethodOf(mPkg, mName, mSig)
if !mthd.IsNil() {
mthds = append(mthds, mthd)
}
if !ptrMthd.IsNil() {
ptrMthds = append(ptrMthds, ptrMthd)
}
}
if len(mthds) > 0 {
methods = b.SliceLit(tSlice, mthds...)
} else {
methods = prog.Zero(tSlice)
}
if len(ptrMthds) > 0 {
ptrMethods = b.SliceLit(tSlice, ptrMthds...)
} else {
ptrMethods = prog.Zero(tSlice)
}
}
return b.Call(initNamed, ret, under, methods, ptrMethods)
}
}
func (b Builder) abiPointerOf(t *types.Pointer) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
return b.Call(b.Pkg.rtFunc("PointerTo"), elem())
}
}
func (b Builder) abiSliceOf(t *types.Slice) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
return b.Call(b.Pkg.rtFunc("SliceOf"), elem())
}
}
func (b Builder) abiArrayOf(t *types.Array) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
n := b.Prog.IntVal(uint64(t.Len()), b.Prog.Uintptr())
return b.Call(b.Pkg.rtFunc("ArrayOf"), n, elem())
}
}
func (b Builder) abiChanOf(t *types.Chan) func() Expr {
elem := b.abiTypeOf(t.Elem())
return func() Expr {
dir, s := abi.ChanDir(t.Dir())
return b.Call(b.Pkg.rtFunc("ChanOf"), b.Prog.IntVal(uint64(dir), b.Prog.Int()), b.Str(s), elem())
}
}
func (b Builder) abiMapOf(t *types.Map) func() Expr {
key := b.abiTypeOf(t.Key())
elem := b.abiTypeOf(t.Elem())
sizes := (*goProgram)(unsafe.Pointer(b.Prog))
bucket := b.abiTypeOf(abi.MapBucketType(t, sizes))
flags := abi.MapTypeFlags(t, sizes)
return func() Expr {
return b.Call(b.Pkg.rtFunc("MapOf"), key(), elem(), bucket(), b.Prog.Val(flags))
}
}
// func StructField(name string, typ *abi.Type, off uintptr, tag string, embedded bool)
// func Struct(pkgPath string, size uintptr, fields []abi.StructField)
func (b Builder) abiStructOf(t *types.Struct) func() Expr {
n := t.NumFields()
typs := make([]func() Expr, n)
for i := 0; i < n; i++ {
f := t.Field(i)
typs[i] = b.abiTypeOf(f.Type())
}
return func() Expr {
pkg := b.Pkg
prog := b.Prog
flds := make([]Expr, n)
strucAbi := pkg.rtFunc("Struct")
sfAbi := pkg.rtFunc("StructField")
tStruc := prog.rawType(t)
for i := 0; i < n; i++ {
f := t.Field(i)
off := uintptr(prog.OffsetOf(tStruc, i))
name := b.Str(f.Name())
tag := b.Str(t.Tag(i))
embedded := prog.Val(f.Embedded())
flds[i] = b.Call(sfAbi, name, typs[i](), prog.Val(off), tag, embedded)
}
pkgPath := b.Str(pkg.Path())
tSlice := lastParamType(prog, strucAbi)
fldSlice := b.SliceLit(tSlice, flds...)
size := prog.IntVal(prog.SizeOf(tStruc), prog.Uintptr())
return b.Call(strucAbi, pkgPath, size, fldSlice)
}
}
func lastParamType(prog Program, fn Expr) Type {
params := fn.raw.Type.(*types.Signature).Params()
return prog.rawType(params.At(params.Len() - 1).Type())
}
// -----------------------------------------------------------------------------
func (p Package) patchType(t types.Type) types.Type {
switch t := t.(type) {
case *types.Pointer:
return types.NewPointer(p.patchType(t.Elem()))
}
return p.patch(t)
}
func (p Package) abiTypeInit(g Global, t types.Type, pub bool) {
b := p.afterBuilder()
if p.patch != nil {
t = p.patchType(t)
}
tabi := b.abiTypeOf(t)
expr := g.Expr
var eq Expr
var blks []BasicBlock
if pub {
eq = b.BinOp(token.EQL, b.Load(expr), b.Prog.Nil(expr.Type))
blks = b.Func.MakeBlocks(2)
b.If(eq, blks[0], blks[1])
b.SetBlockEx(blks[0], AtEnd, false)
}
vexpr := tabi()
prog := p.Prog
if kind, _, _ := abi.DataKindOf(t, 0, prog.is32Bits); kind == abi.Pointer {
b.InlineCall(b.Pkg.rtFunc("SetDirectIface"), vexpr)
}
b.Store(expr, vexpr)
if pub {
b.Jump(blks[1])
b.SetBlockEx(blks[1], AtEnd, false)
b.blk.last = blks[1].last
}
if t, ok := t.(*types.Named); ok {
if iface, ok := t.Underlying().(*types.Interface); ok {
tabi = b.abiInitNamedInterface(vexpr, iface)
} else {
tabi = b.abiInitNamed(vexpr, t)
}
if pub {
blks = b.Func.MakeBlocks(2)
b.If(eq, blks[0], blks[1])
b.SetBlockEx(blks[0], AtEnd, false)
}
tabi()
if pub {
b.Jump(blks[1])
b.SetBlockEx(blks[1], AtEnd, false)
b.blk.last = blks[1].last
}
}
}
// abiType returns the abi type of the specified type.
func (b Builder) abiType(t types.Type) Expr {
switch t := t.(type) {
case *types.Pointer:
b.checkAbi(t.Elem())
case *types.Array:
b.checkAbi(t.Elem())
case *types.Map:
b.checkAbi(t.Key())
b.checkAbi(t.Elem())
case *types.Slice:
b.checkAbi(t.Elem())
case *types.Chan:
b.checkAbi(t.Elem())
case *types.Struct:
for i := 0; i < t.NumFields(); i++ {
b.checkAbi(t.Field(i).Type())
}
case *types.Interface:
for i := 0; i < t.NumMethods(); i++ {
b.checkAbi(t.Method(i).Type())
}
case *types.Signature:
for i := 0; i < t.Params().Len(); i++ {
b.checkAbi(t.Params().At(i).Type())
}
for i := 0; i < t.Results().Len(); i++ {
b.checkAbi(t.Results().At(i).Type())
}
}
g := b.loadType(t)
return b.Load(g.Expr)
}
func (b Builder) checkAbi(t types.Type) {
if b.Pkg.chkabi[t] {
return
}
b.abiType(t)
}
func (b Builder) loadType(t types.Type) Global {
b.Pkg.chkabi[t] = true
pkg := b.Pkg
name, pub := pkg.abi.TypeName(t)
g := pkg.VarOf(name)
if g == nil {
prog := b.Prog
g = pkg.doNewVar(name, prog.AbiTypePtrPtr())
g.InitNil()
g.impl.SetLinkage(llvm.LinkOnceAnyLinkage)
pkg.abiTypeInit(g, t, pub)
}
return g
}
// -----------------------------------------------------------------------------