pkujhd / goloader

load and run golang code at runtime.
Apache License 2.0
497 stars 58 forks source link

Load error: unresolve external:runtime.writeBarrier #71

Closed introspection3 closed 1 year ago

introspection3 commented 1 year ago

1.20.1 windows 10

package pkg

type Farmer struct { Name *string }

func (f Farmer) Say(word string) { f.Name = &word } func (f Farmer) Get() string { return *f.Name } func New() any { return &Farmer{} }

//////////// package main

import ( "flag" "fmt" "os" "strings" "unsafe"

"github.com/pkujhd/goloader"

)

type arrayFlags struct { File []string PkgPath []string }

func (i *arrayFlags) String() string { return "my string representation" }

func (i *arrayFlags) Set(value string) error { s := strings.Split(value, ":") i.File = append(i.File, s[0]) var path string if len(s) > 1 { path = s[1] } i.PkgPath = append(i.PkgPath, path) return nil }

type IPerson interface { Say(word string) Get() string }

func parse(file, pkgpath string) { if file == "" { flag.PrintDefaults() return } f, err := os.Open(file) if err != nil { fmt.Printf("%# v\n", err) return } obj, err := goloader.Parse(f, pkgpath) fmt.Printf("%# v\n", obj) f.Close() if err != nil { fmt.Printf("error reading %s: %v\n", file, err) return } } func main() {

symPtr := make(map[string]uintptr)
err := goloader.RegSymbol(symPtr)
if err != nil {
    fmt.Println(err)
    return
}

// most of time you don't need to register function, but if loader complain about it, you have to.

str := make([]string, 0)

goloader.RegTypes(symPtr, fmt.Sprint, str)

//var file string = "./f.o"
//var pkg string = "stduy1/pkg"
//parse(&file, &pkg)
linker, err := goloader.ReadObjs([]string{"./f.o"}, []string{"stduy1/pkg"})
if err != nil {
    fmt.Println(err)
    return
}

var mmapByte []byte

codeModule, err := goloader.Load(linker, symPtr)
if err != nil {
    fmt.Println("Load error:", err)
    return
}
runFuncPtr := codeModule.Syms["pkg.New"]
if runFuncPtr == 0 {
    fmt.Println("Load error! not find function:", "pkg.New")
    return
}
funcPtrContainer := (uintptr)(unsafe.Pointer(&runFuncPtr))
runFunc := *(*func() any)(unsafe.Pointer(&funcPtrContainer))
obj := runFunc().(IPerson)
obj.Say("hello")

codeModule.Unload()

// a strict test, try to make mmap random
if mmapByte == nil {
    mmapByte, err = goloader.Mmap(1024)
    if err != nil {
        fmt.Println(err)
    }
    b := make([]byte, 1024)
    copy(mmapByte, b) // reset all bytes
} else {
    goloader.Munmap(mmapByte)
    mmapByte = nil
}

} image

pkujhd commented 1 year ago

@introspection3
不能使用go run 去执行,go run会使用-s -w参数编译文件,导致失去符号表

你给出的例子执行结果会是 panic: interface conversion: *pkg.Farmer is not main.IPerson: missing method Get

goroutine 1 [running]: main.main() D:/Code/go/goloader/loader.go:95 +0x39c

这个问题目前未处理,你可以参考https://github.com/eh-steve/goloader/tree/jit

pkujhd commented 1 year ago

Duplicate of #32

introspection3 commented 1 year ago

PS F:\goprjs\s3> go get "github.com/eh-steve/goloader/jit" go: github.com/eh-steve/goloader@v0.0.0-20221026112716-fed0a9e75321: parsing go.mod: module declares its path as: github.com/pkujhd/goloader but was required as: github.com/eh-steve/goloader

introspection3 commented 1 year ago

goloader jit project has something wrong

eh-steve commented 1 year ago

This should be fixed since https://github.com/eh-steve/goloader/commit/06a14a6e49922f03bbfeaed5a701a1a9e4dc6d27, the latest version should be go gettable now, and this test case passes https://github.com/eh-steve/goloader/commit/4ed22637d27ab155e3a4ee59c825f6874bee6506.

The reason for the original failure is because the types func() string and func(string) are duplicated across the host and JIT packages, so it appears as though the methods don't implement the interface, but they actually do. The JIT fork deduplicates these types across the host and JIT code before loading.

introspection3 commented 1 year ago

@eh-steve Can we use go get -u github.com/pkujhd/goloader to resovle this bug

eh-steve commented 1 year ago

@eh-steve Can we use go get -u github.com/pkujhd/goloader to resovle this bug

No, as it's a hard fork so you'll need to use github.com/eh-steve/goloader/jit