mirror of
https://github.com/BishopFox/sliver.git
synced 2026-11-09 17:09:39 -10:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8dec1e7904 | ||
|
|
09781382af | ||
|
|
e369e5a346 | ||
|
|
96811decb5 | ||
|
|
dc5397f10e | ||
|
|
5671bac381 | ||
|
|
fc61c7f00a | ||
|
|
bd952727f2 | ||
|
|
013a123ec6 | ||
|
|
28299d6c21 | ||
|
|
543b71c1f3 | ||
|
|
482fa1576b | ||
|
|
161c6d78a7 | ||
|
|
858140e286 |
No files matched your search
@@ -117,6 +117,8 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/ebitengine/purego v0.5.1 h1:hNunhThpOf1vzKl49v6YxIsXLhl92vbBEv1/2Ez3ZrY=
|
||||
github.com/ebitengine/purego v0.5.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
||||
+3
-6
@@ -6,8 +6,8 @@ toolchain go1.21.4
|
||||
|
||||
require (
|
||||
filippo.io/age v1.1.1
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313
|
||||
github.com/Ne0nd0g/go-clr v1.0.3
|
||||
github.com/ebitengine/purego v0.5.1
|
||||
github.com/gofrs/uuid v4.0.0+incompatible
|
||||
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f
|
||||
github.com/lesnuages/go-winio v0.4.19
|
||||
@@ -18,17 +18,16 @@ require (
|
||||
github.com/things-go/go-socks5 v0.0.3-0.20210722055343-24af464efe43
|
||||
github.com/yiya1989/sshkrb5 v0.0.0-20201110125252-a1455b75a35e
|
||||
golang.org/x/crypto v0.13.0
|
||||
golang.org/x/net v0.15.0
|
||||
golang.org/x/sys v0.14.0
|
||||
golang.zx2c4.com/wireguard v0.0.0-20231022001213-2e0774f246fb
|
||||
google.golang.org/protobuf v1.31.0
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2 // indirect
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc // indirect
|
||||
github.com/Microsoft/go-winio v0.6.0 // indirect
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 // indirect
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
@@ -37,7 +36,6 @@ require (
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
golang.org/x/mod v0.13.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
@@ -48,5 +46,4 @@ require (
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20230927004350-cbd86285d259 // indirect
|
||||
)
|
||||
+2
-9
@@ -1,22 +1,16 @@
|
||||
filippo.io/age v1.1.1 h1:pIpO7l151hCnQ4BdyBujnGP2YlUo0uj6sAVNHGBvXHg=
|
||||
filippo.io/age v1.1.1/go.mod h1:l03SrzDUrBkdBx8+IILdnn2KZysqQdbEBUQ4p3sqEQE=
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2 h1:8kQNJC9AxAaNs0JkXnWUfbNeDnIO1QLYYWjYqC6JEE4=
|
||||
github.com/Binject/debug v0.0.0-20210225042342-c9b8b45728d2/go.mod h1:QzgxDLY/qdKlvnbnb65eqTedhvQPbaSP2NqIbcuKvsQ=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313 h1:hX9boCRvCxIpsiV4q9ts+WOYNnjHiji2FOR5BsDzxiY=
|
||||
github.com/Binject/universal v0.0.0-20210304094126-daefaa886313/go.mod h1:J3XDRlam5pPYca3i6EqgQ35GCCEoyxafpCbLkta0ozc=
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc h1:7D+Bh06CRPCJO3gr2F7h1sriovOZ8BMhca2Rg85c2nk=
|
||||
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
|
||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
||||
github.com/Ne0nd0g/go-clr v1.0.3 h1:xt92wwuqY23ZSC7RuHD3mKu3K22Bk5NNbxI803vojK4=
|
||||
github.com/Ne0nd0g/go-clr v1.0.3/go.mod h1:TKYSQ/5xT25EvBUttAlUrzpR8yHuI0qTRK495I5xG/I=
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0 h1:JjwxKkxzcBk4k8147g0eBQRCIy0UN1Be8AAv6RaIj4Q=
|
||||
github.com/awgh/cppgo v0.0.0-20210224085512-3d24bca8edc0/go.mod h1:IbERvuyb387Hppp8hX0SQTFt/mkej8+OhuS8L0nC2CI=
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4 h1:cIAK2NNf2yafdgpFRNJrgZMwvy61BEVpGoHc2n4/yWs=
|
||||
github.com/awgh/rawreader v0.0.0-20200626064944-56820a9c6da4/go.mod h1:SalMPBCab3yuID8nIhLfzwoBV+lBRyaC7NhuN8qL8xE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/ebitengine/purego v0.5.1 h1:hNunhThpOf1vzKl49v6YxIsXLhl92vbBEv1/2Ez3ZrY=
|
||||
github.com/ebitengine/purego v0.5.1/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5 h1:Y5Q2mEwfzjMt5+3u70Gtw93ZOu2UuPeeeTBDntF7FoY=
|
||||
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
@@ -82,7 +76,6 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package extension
|
||||
|
||||
type CallbackArray []uintptr
|
||||
|
||||
const (
|
||||
SendOutputCallback = iota
|
||||
SendErrorCallback
|
||||
)
|
||||
|
||||
var callbacks CallbackArray
|
||||
|
||||
func init() {
|
||||
callbacks = make(CallbackArray, 2)
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
//go:build darwin
|
||||
|
||||
package extension
|
||||
|
||||
/*
|
||||
@@ -19,35 +21,27 @@ package extension
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Binject/universal"
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
type DarwinExtension struct {
|
||||
id string
|
||||
data []byte
|
||||
module *universal.Library
|
||||
module uintptr
|
||||
arch string
|
||||
serverStore bool
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type extensionArguments struct {
|
||||
inDataSize uintptr
|
||||
inDataBuff uintptr
|
||||
outDataSize uintptr
|
||||
outDataBuff uintptr
|
||||
}
|
||||
|
||||
func NewDarwinExtension(data []byte, id string, arch string, init string) *DarwinExtension {
|
||||
return &DarwinExtension{
|
||||
id: id,
|
||||
@@ -66,65 +60,65 @@ func (d *DarwinExtension) GetArch() string {
|
||||
}
|
||||
|
||||
func (d *DarwinExtension) Load() error {
|
||||
var err error
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
loader, err := universal.NewLoader()
|
||||
|
||||
extTmpFile, err := createTempFile(d.data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.module, err = loader.LoadLibrary(d.id, &d.data)
|
||||
defer cleanupTempFile(extTmpFile)
|
||||
d.module, err = purego.Dlopen(extTmpFile, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("couldn't purego.dlopen(): %v\n", err)
|
||||
// {{end}}
|
||||
return err
|
||||
}
|
||||
|
||||
// if err := purego.Dlerror(); err != "" {
|
||||
// return errors.New(err)
|
||||
// }
|
||||
|
||||
if d.init != "" {
|
||||
if _, errInit := d.module.Call(d.init); errInit != nil {
|
||||
return errInit
|
||||
}
|
||||
var initFunc func()
|
||||
purego.RegisterLibFunc(&initFunc, d.module, d.init)
|
||||
initFunc()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DarwinExtension) Call(export string, arguments []byte, onFinish func([]byte)) error {
|
||||
// We currently have 2 issues with Darwin extensions:
|
||||
// - cppgo (used by universal) fucks up when calling a function with more than 1 argument
|
||||
// - we don't have Go callback support for the loaded extension,
|
||||
// so we have to wait for the call to finish to get the results
|
||||
// To circumvent these issues, we pass the extensionArguments structure
|
||||
// as the only argument to the call, so we can pass args in and extract
|
||||
// the result at the same time.
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
extArgs := extensionArguments{}
|
||||
if len(arguments) > 0 {
|
||||
extArgs.inDataBuff = uintptr(unsafe.Pointer(&arguments[0]))
|
||||
extArgs.inDataSize = uintptr(uint64(len(arguments)))
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Calling %s, arg size: %d\n", export, extArgs.inDataSize)
|
||||
// {{end}}
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
_, err := d.module.Call(export, uintptr(unsafe.Pointer(&extArgs)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("%s done!\n", export)
|
||||
// {{end}}
|
||||
outData := new(bytes.Buffer)
|
||||
outDataSize := int(extArgs.outDataSize)
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("Out data size: %d\n", outDataSize)
|
||||
// {{end}}
|
||||
for i := 0; i < outDataSize; i++ {
|
||||
b := (*byte)(unsafe.Pointer(uintptr(i) + extArgs.outDataBuff))
|
||||
outData.WriteByte(*b)
|
||||
}
|
||||
// We currently don't have a way to trigger a callback
|
||||
// in the loaded code for Darwin.
|
||||
if outData.Len() > 0 {
|
||||
onFinish(outData.Bytes())
|
||||
}
|
||||
d.onFinish = onFinish
|
||||
outCallback := purego.NewCallback(d.extensionCallback)
|
||||
var exportFunc func([]byte, uint64, uintptr) uint32
|
||||
purego.RegisterLibFunc(&exportFunc, d.module, export)
|
||||
exportFunc(arguments, uint64(len(arguments)), outCallback)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DarwinExtension) extensionCallback(data uintptr, length uintptr) {
|
||||
outDataSize := int(length)
|
||||
outBytes := unsafe.Slice((*byte)(unsafe.Pointer(data)), outDataSize)
|
||||
d.onFinish(outBytes)
|
||||
}
|
||||
|
||||
func createTempFile(data []byte) (string, error) {
|
||||
tmpFile, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
_, err = tmpFile.Write(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpFile.Name(), nil
|
||||
}
|
||||
|
||||
func cleanupTempFile(path string) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
//go:build linux
|
||||
|
||||
package extension
|
||||
|
||||
/*
|
||||
Sliver Implant Framework
|
||||
Copyright (C) 2021 Bishop Fox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import (
|
||||
// {{if .Config.Debug}}
|
||||
"log"
|
||||
// {{end}}
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
type LinuxExtension struct {
|
||||
id string
|
||||
data []byte
|
||||
module uintptr
|
||||
arch string
|
||||
serverStore bool
|
||||
init string
|
||||
onFinish func([]byte)
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
func NewLinuxExtension(data []byte, id string, arch string, init string) *LinuxExtension {
|
||||
return &LinuxExtension{
|
||||
id: id,
|
||||
data: data,
|
||||
arch: arch,
|
||||
init: init,
|
||||
}
|
||||
}
|
||||
|
||||
func (d *LinuxExtension) GetID() string {
|
||||
return d.id
|
||||
}
|
||||
|
||||
func (d *LinuxExtension) GetArch() string {
|
||||
return d.arch
|
||||
}
|
||||
|
||||
func (d *LinuxExtension) Load() error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
extTmpFile, err := createTempFile(d.data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanupTempFile(extTmpFile)
|
||||
d.module, err = purego.Dlopen(extTmpFile, purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
// {{if .Config.Debug}}
|
||||
log.Printf("couldn't purego.dlopen(): %v\n", err)
|
||||
// {{end}}
|
||||
return err
|
||||
}
|
||||
|
||||
// if err := purego.Dlerror(); err != "" {
|
||||
// return errors.New(err)
|
||||
// }
|
||||
|
||||
if d.init != "" {
|
||||
var initFunc func()
|
||||
purego.RegisterLibFunc(&initFunc, d.module, d.init)
|
||||
initFunc()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *LinuxExtension) Call(export string, arguments []byte, onFinish func([]byte)) error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
d.onFinish = onFinish
|
||||
outCallback := purego.NewCallback(d.extensionCallback)
|
||||
var exportFunc func([]byte, uint64, uintptr) uint32
|
||||
purego.RegisterLibFunc(&exportFunc, d.module, export)
|
||||
exportFunc(arguments, uint64(len(arguments)), outCallback)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *LinuxExtension) extensionCallback(data uintptr, length uintptr) {
|
||||
outDataSize := int(length)
|
||||
outBytes := unsafe.Slice((*byte)(unsafe.Pointer(data)), outDataSize)
|
||||
d.onFinish(outBytes)
|
||||
}
|
||||
|
||||
func createTempFile(data []byte) (string, error) {
|
||||
tmpFile, err := os.CreateTemp("", "")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer tmpFile.Close()
|
||||
_, err = tmpFile.Write(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tmpFile.Name(), nil
|
||||
}
|
||||
|
||||
func cleanupTempFile(path string) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
@@ -31,10 +31,12 @@ import (
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/bishopfox/sliver/implant/sliver/extension"
|
||||
"github.com/bishopfox/sliver/implant/sliver/procdump"
|
||||
"github.com/bishopfox/sliver/implant/sliver/taskrunner"
|
||||
"github.com/bishopfox/sliver/protobuf/commonpb"
|
||||
"github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
pb "github.com/bishopfox/sliver/protobuf/sliverpb"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
// {{if .Config.Debug}}
|
||||
@@ -73,6 +75,11 @@ var (
|
||||
sliverpb.MsgProcessDumpReq: dumpHandler,
|
||||
sliverpb.MsgGrepReq: grepHandler,
|
||||
|
||||
// Extensions
|
||||
pb.MsgRegisterExtensionReq: registerExtensionHandler,
|
||||
pb.MsgCallExtensionReq: callExtensionHandler,
|
||||
pb.MsgListExtensionsReq: listExtensionsHandler,
|
||||
|
||||
// Wasm Extensions - Note that execution can be done via a tunnel handler
|
||||
sliverpb.MsgRegisterWasmExtensionReq: registerWasmExtensionHandler,
|
||||
sliverpb.MsgDeregisterWasmExtensionReq: deregisterWasmExtensionHandler,
|
||||
@@ -445,3 +452,70 @@ finished:
|
||||
data, err = proto.Marshal(chown)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
// Extensions
|
||||
|
||||
func registerExtensionHandler(data []byte, resp RPCResponse) {
|
||||
registerReq := &pb.RegisterExtensionReq{}
|
||||
|
||||
err := proto.Unmarshal(data, registerReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
ext := extension.NewLinuxExtension(registerReq.Data, registerReq.Name, registerReq.OS, registerReq.Init)
|
||||
extension.Add(ext)
|
||||
err = ext.Load()
|
||||
registerResp := &pb.RegisterExtension{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
if err != nil {
|
||||
registerResp.Response.Err = err.Error()
|
||||
}
|
||||
data, err = proto.Marshal(registerResp)
|
||||
resp(data, err)
|
||||
}
|
||||
|
||||
func callExtensionHandler(data []byte, resp RPCResponse) {
|
||||
callReq := &pb.CallExtensionReq{}
|
||||
|
||||
err := proto.Unmarshal(data, callReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
callResp := &pb.CallExtension{
|
||||
Response: &commonpb.Response{},
|
||||
}
|
||||
gotOutput := false
|
||||
err = extension.Run(callReq.Name, callReq.Export, callReq.Args, func(out []byte) {
|
||||
gotOutput = true
|
||||
callResp.Output = out
|
||||
data, err = proto.Marshal(callResp)
|
||||
resp(data, err)
|
||||
})
|
||||
// Only send back synchronously if there was an error
|
||||
if err != nil || !gotOutput {
|
||||
if err != nil {
|
||||
callResp.Response.Err = err.Error()
|
||||
}
|
||||
data, err = proto.Marshal(callResp)
|
||||
resp(data, err)
|
||||
}
|
||||
}
|
||||
|
||||
func listExtensionsHandler(data []byte, resp RPCResponse) {
|
||||
lstReq := &pb.ListExtensionsReq{}
|
||||
err := proto.Unmarshal(data, lstReq)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
exts := extension.List()
|
||||
lstResp := &pb.ListExtensions{
|
||||
Response: &commonpb.Response{},
|
||||
Names: exts,
|
||||
}
|
||||
data, err = proto.Marshal(lstResp)
|
||||
resp(data, err)
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-3000
File diff suppressed because it is too large.
Load diff
-32
@@ -1,32 +0,0 @@
|
||||
package elf
|
||||
|
||||
/*
|
||||
Any symbol in the dynamic symbol table (in .dynsym) for which .st_shndx == SHN_UNDEF
|
||||
(references undefined section) is an import, and every other symbol is defined and exported.
|
||||
*/
|
||||
|
||||
// Export - describes a single export entry
|
||||
type Export struct {
|
||||
Name string
|
||||
VirtualAddress uint64
|
||||
}
|
||||
|
||||
// Exports - gets exports
|
||||
func (f *File) Exports() ([]Export, error) {
|
||||
|
||||
var exports []Export
|
||||
symbols, err := f.DynamicSymbols()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, s := range symbols {
|
||||
if s.Section != SHN_UNDEF {
|
||||
exports = append(exports, Export{
|
||||
Name: s.Name,
|
||||
VirtualAddress: s.Value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return exports, nil
|
||||
}
|
||||
-964
@@ -1,964 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package elf implements access to ELF object files.
|
||||
package elf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// seekStart, seekCurrent, seekEnd are copies of
|
||||
// io.SeekStart, io.SeekCurrent, and io.SeekEnd.
|
||||
// We can't use the ones from package io because
|
||||
// we want this code to build with Go 1.4 during
|
||||
// cmd/dist bootstrap.
|
||||
const (
|
||||
seekStart int = 0
|
||||
seekCurrent int = 1
|
||||
seekEnd int = 2
|
||||
)
|
||||
|
||||
// TODO: error reporting detail
|
||||
|
||||
/*
|
||||
* Internal ELF representation
|
||||
*/
|
||||
|
||||
// A FileHeader represents an ELF file header.
|
||||
type FileHeader struct {
|
||||
Class Class
|
||||
Data Data
|
||||
Version Version
|
||||
OSABI OSABI
|
||||
ABIVersion uint8
|
||||
ByteOrder binary.ByteOrder
|
||||
Type Type
|
||||
Machine Machine
|
||||
Entry uint64
|
||||
SHTOffset int64
|
||||
ShStrIndex int
|
||||
}
|
||||
|
||||
// A File represents an open ELF file.
|
||||
type File struct {
|
||||
FileHeader
|
||||
Sections []*Section
|
||||
Progs []*Prog
|
||||
closer io.Closer
|
||||
gnuNeed []verneed
|
||||
gnuVersym []byte
|
||||
Insertion []byte
|
||||
InsertionEOF []byte
|
||||
|
||||
DynTags []DynTagValue
|
||||
}
|
||||
|
||||
// A SectionHeader represents a single ELF section header.
|
||||
type SectionHeader struct {
|
||||
Name string
|
||||
Type SectionType
|
||||
Flags SectionFlag
|
||||
Addr uint64
|
||||
Offset uint64
|
||||
Size uint64
|
||||
Link uint32
|
||||
Info uint32
|
||||
Addralign uint64
|
||||
Entsize uint64
|
||||
|
||||
Shnum int // Section Header Number
|
||||
Shname uint32 // Section Header Name (index)
|
||||
|
||||
// FileSize is the size of this section in the file in bytes.
|
||||
// If a section is compressed, FileSize is the size of the
|
||||
// compressed data, while Size (above) is the size of the
|
||||
// uncompressed data.
|
||||
FileSize uint64
|
||||
}
|
||||
|
||||
// A Section represents a single section in an ELF file.
|
||||
type Section struct {
|
||||
SectionHeader
|
||||
|
||||
// Embed ReaderAt for ReadAt method.
|
||||
// Do not embed SectionReader directly
|
||||
// to avoid having Read and Seek.
|
||||
// If a client wants Read and Seek it must use
|
||||
// Open() to avoid fighting over the seek offset
|
||||
// with other clients.
|
||||
//
|
||||
// ReaderAt may be nil if the section is not easily available
|
||||
// in a random-access form. For example, a compressed section
|
||||
// may have a nil ReaderAt.
|
||||
io.ReaderAt
|
||||
sr *io.SectionReader
|
||||
|
||||
compressionType CompressionType
|
||||
compressionOffset int64
|
||||
}
|
||||
|
||||
// Data reads and returns the contents of the ELF section.
|
||||
// Even if the section is stored compressed in the ELF file,
|
||||
// Data returns uncompressed data.
|
||||
func (s *Section) Data() ([]byte, error) {
|
||||
dat := make([]byte, s.Size)
|
||||
n, err := io.ReadFull(s.Open(), dat)
|
||||
return dat[0:n], err
|
||||
}
|
||||
|
||||
// stringTable reads and returns the string table given by the
|
||||
// specified link value.
|
||||
func (f *File) stringTable(link uint32) ([]byte, error) {
|
||||
if link <= 0 || link >= uint32(len(f.Sections)) {
|
||||
return nil, errors.New("section has invalid string table link")
|
||||
}
|
||||
return f.Sections[link].Data()
|
||||
}
|
||||
|
||||
// Open returns a new ReadSeeker reading the ELF section.
|
||||
// Even if the section is stored compressed in the ELF file,
|
||||
// the ReadSeeker reads uncompressed data.
|
||||
func (s *Section) Open() io.ReadSeeker {
|
||||
if s.Flags&SHF_COMPRESSED == 0 {
|
||||
return io.NewSectionReader(s.sr, 0, 1<<63-1)
|
||||
}
|
||||
if s.compressionType == COMPRESS_ZLIB {
|
||||
return &readSeekerFromReader{
|
||||
reset: func() (io.Reader, error) {
|
||||
fr := io.NewSectionReader(s.sr, s.compressionOffset, int64(s.FileSize)-s.compressionOffset)
|
||||
return zlib.NewReader(fr)
|
||||
},
|
||||
size: int64(s.Size),
|
||||
}
|
||||
}
|
||||
err := &FormatError{int64(s.Offset), "unknown compression type", s.compressionType}
|
||||
return errorReader{err}
|
||||
}
|
||||
|
||||
// A ProgHeader represents a single ELF program header.
|
||||
type ProgHeader struct {
|
||||
Type ProgType
|
||||
Flags ProgFlag
|
||||
Off uint64
|
||||
Vaddr uint64
|
||||
Paddr uint64
|
||||
Filesz uint64
|
||||
Memsz uint64
|
||||
Align uint64
|
||||
}
|
||||
|
||||
// A Prog represents a single ELF program header in an ELF binary.
|
||||
type Prog struct {
|
||||
ProgHeader
|
||||
|
||||
// Embed ReaderAt for ReadAt method.
|
||||
// Do not embed SectionReader directly
|
||||
// to avoid having Read and Seek.
|
||||
// If a client wants Read and Seek it must use
|
||||
// Open() to avoid fighting over the seek offset
|
||||
// with other clients.
|
||||
io.ReaderAt
|
||||
sr *io.SectionReader
|
||||
}
|
||||
|
||||
// Open returns a new ReadSeeker reading the ELF program body.
|
||||
func (p *Prog) Open() io.ReadSeeker { return io.NewSectionReader(p.sr, 0, 1<<63-1) }
|
||||
|
||||
// A Symbol represents an entry in an ELF symbol table section.
|
||||
type Symbol struct {
|
||||
Name string
|
||||
NameIndex uint32
|
||||
Info, Other byte
|
||||
Section SectionIndex
|
||||
SectIndex uint16
|
||||
Value, Size uint64
|
||||
}
|
||||
|
||||
// ToSym64 - Convert to a Sym64
|
||||
func (s Symbol) ToSym64() (retval Sym64) {
|
||||
|
||||
retval.Name = s.NameIndex
|
||||
retval.Info = s.Info
|
||||
retval.Other = s.Other
|
||||
retval.Shndx = s.SectIndex
|
||||
retval.Value = s.Value
|
||||
retval.Size = s.Size
|
||||
return
|
||||
}
|
||||
|
||||
// ToSym32 - Convert to a Sym32
|
||||
func (s Symbol) ToSym32() (retval Sym32) {
|
||||
|
||||
retval.Name = s.NameIndex
|
||||
retval.Info = s.Info
|
||||
retval.Other = s.Other
|
||||
retval.Shndx = s.SectIndex
|
||||
retval.Value = uint32(s.Value)
|
||||
retval.Size = uint32(s.Size)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
* ELF reader
|
||||
*/
|
||||
|
||||
type FormatError struct {
|
||||
off int64
|
||||
msg string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func (e *FormatError) Error() string {
|
||||
msg := e.msg
|
||||
if e.val != nil {
|
||||
msg += fmt.Sprintf(" '%v' ", e.val)
|
||||
}
|
||||
msg += fmt.Sprintf("in record at byte %#x", e.off)
|
||||
return msg
|
||||
}
|
||||
|
||||
// Open opens the named file using os.Open and prepares it for use as an ELF binary.
|
||||
func Open(name string) (*File, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ff, err := NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
ff.closer = f
|
||||
return ff, nil
|
||||
}
|
||||
|
||||
// Close closes the File.
|
||||
// If the File was created using NewFile directly instead of Open,
|
||||
// Close has no effect.
|
||||
func (f *File) Close() error {
|
||||
var err error
|
||||
if f.closer != nil {
|
||||
err = f.closer.Close()
|
||||
f.closer = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// SectionByType returns the first section in f with the
|
||||
// given type, or nil if there is no such section.
|
||||
func (f *File) SectionByType(typ SectionType) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Type == typ {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SectionByName returns the first section in f with the
|
||||
// given name, or nil if there is no such section.
|
||||
func (f *File) SectionByName(name string) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Name == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewFile creates a new File for accessing an ELF binary in an underlying reader.
|
||||
// The ELF binary is expected to start at position 0 in the ReaderAt.
|
||||
func NewFile(r io.ReaderAt) (*File, error) {
|
||||
sr := io.NewSectionReader(r, 0, 1<<63-1)
|
||||
// Read and decode ELF identifier
|
||||
var ident [16]uint8
|
||||
if _, err := r.ReadAt(ident[0:], 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {
|
||||
return nil, &FormatError{0, "bad magic number", ident[0:4]}
|
||||
}
|
||||
|
||||
f := new(File)
|
||||
f.Class = Class(ident[EI_CLASS])
|
||||
switch f.Class {
|
||||
case ELFCLASS32:
|
||||
case ELFCLASS64:
|
||||
// ok
|
||||
default:
|
||||
return nil, &FormatError{0, "unknown ELF class", f.Class}
|
||||
}
|
||||
|
||||
f.Data = Data(ident[EI_DATA])
|
||||
switch f.Data {
|
||||
case ELFDATA2LSB:
|
||||
f.ByteOrder = binary.LittleEndian
|
||||
case ELFDATA2MSB:
|
||||
f.ByteOrder = binary.BigEndian
|
||||
default:
|
||||
return nil, &FormatError{0, "unknown ELF data encoding", f.Data}
|
||||
}
|
||||
|
||||
f.Version = Version(ident[EI_VERSION])
|
||||
if f.Version != EV_CURRENT {
|
||||
return nil, &FormatError{0, "unknown ELF version", f.Version}
|
||||
}
|
||||
|
||||
f.OSABI = OSABI(ident[EI_OSABI])
|
||||
f.ABIVersion = ident[EI_ABIVERSION]
|
||||
|
||||
// Read ELF file header
|
||||
var phoff int64
|
||||
var phentsize, phnum int
|
||||
var shentsize, shnum int
|
||||
f.ShStrIndex = -1
|
||||
switch f.Class {
|
||||
case ELFCLASS32:
|
||||
hdr := new(Header32)
|
||||
sr.Seek(0, seekStart)
|
||||
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Type = Type(hdr.Type)
|
||||
f.Machine = Machine(hdr.Machine)
|
||||
f.Entry = uint64(hdr.Entry)
|
||||
if v := Version(hdr.Version); v != f.Version {
|
||||
return nil, &FormatError{0, "mismatched ELF version", v}
|
||||
}
|
||||
phoff = int64(hdr.Phoff)
|
||||
phentsize = int(hdr.Phentsize)
|
||||
phnum = int(hdr.Phnum)
|
||||
f.SHTOffset = int64(hdr.Shoff)
|
||||
shentsize = int(hdr.Shentsize)
|
||||
shnum = int(hdr.Shnum)
|
||||
f.ShStrIndex = int(hdr.Shstrndx)
|
||||
case ELFCLASS64:
|
||||
hdr := new(Header64)
|
||||
sr.Seek(0, seekStart)
|
||||
if err := binary.Read(sr, f.ByteOrder, hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Type = Type(hdr.Type)
|
||||
f.Machine = Machine(hdr.Machine)
|
||||
f.Entry = hdr.Entry
|
||||
if v := Version(hdr.Version); v != f.Version {
|
||||
return nil, &FormatError{0, "mismatched ELF version", v}
|
||||
}
|
||||
phoff = int64(hdr.Phoff)
|
||||
phentsize = int(hdr.Phentsize)
|
||||
phnum = int(hdr.Phnum)
|
||||
f.SHTOffset = int64(hdr.Shoff)
|
||||
shentsize = int(hdr.Shentsize)
|
||||
shnum = int(hdr.Shnum)
|
||||
f.ShStrIndex = int(hdr.Shstrndx)
|
||||
}
|
||||
|
||||
if shnum > 0 && f.SHTOffset > 0 && (f.ShStrIndex < 0 || f.ShStrIndex >= shnum) {
|
||||
return nil, &FormatError{0, "invalid ELF shstrndx", f.ShStrIndex}
|
||||
}
|
||||
|
||||
// Read program headers
|
||||
f.Progs = make([]*Prog, phnum)
|
||||
for i := 0; i < phnum; i++ {
|
||||
off := phoff + int64(i)*int64(phentsize)
|
||||
sr.Seek(off, seekStart)
|
||||
p := new(Prog)
|
||||
switch f.Class {
|
||||
case ELFCLASS32:
|
||||
ph := new(Prog32)
|
||||
if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.ProgHeader = ProgHeader{
|
||||
Type: ProgType(ph.Type),
|
||||
Flags: ProgFlag(ph.Flags),
|
||||
Off: uint64(ph.Off),
|
||||
Vaddr: uint64(ph.Vaddr),
|
||||
Paddr: uint64(ph.Paddr),
|
||||
Filesz: uint64(ph.Filesz),
|
||||
Memsz: uint64(ph.Memsz),
|
||||
Align: uint64(ph.Align),
|
||||
}
|
||||
case ELFCLASS64:
|
||||
ph := new(Prog64)
|
||||
if err := binary.Read(sr, f.ByteOrder, ph); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.ProgHeader = ProgHeader{
|
||||
Type: ProgType(ph.Type),
|
||||
Flags: ProgFlag(ph.Flags),
|
||||
Off: ph.Off,
|
||||
Vaddr: ph.Vaddr,
|
||||
Paddr: ph.Paddr,
|
||||
Filesz: ph.Filesz,
|
||||
Memsz: ph.Memsz,
|
||||
Align: ph.Align,
|
||||
}
|
||||
}
|
||||
p.sr = io.NewSectionReader(r, int64(p.Off), int64(p.Filesz))
|
||||
p.ReaderAt = p.sr
|
||||
f.Progs[i] = p
|
||||
}
|
||||
|
||||
// Read section headers
|
||||
f.Sections = make([]*Section, shnum)
|
||||
names := make([]uint32, shnum)
|
||||
for i := 0; i < shnum; i++ {
|
||||
off := f.SHTOffset + int64(i)*int64(shentsize)
|
||||
sr.Seek(off, seekStart)
|
||||
s := new(Section)
|
||||
switch f.Class {
|
||||
case ELFCLASS32:
|
||||
sh := new(Section32)
|
||||
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names[i] = sh.Name
|
||||
s.SectionHeader = SectionHeader{
|
||||
Type: SectionType(sh.Type),
|
||||
Flags: SectionFlag(sh.Flags),
|
||||
Addr: uint64(sh.Addr),
|
||||
Offset: uint64(sh.Off),
|
||||
FileSize: uint64(sh.Size),
|
||||
Link: sh.Link,
|
||||
Info: sh.Info,
|
||||
Addralign: uint64(sh.Addralign),
|
||||
Entsize: uint64(sh.Entsize),
|
||||
Shnum: i,
|
||||
Shname: sh.Name,
|
||||
}
|
||||
|
||||
case ELFCLASS64:
|
||||
sh := new(Section64)
|
||||
if err := binary.Read(sr, f.ByteOrder, sh); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
names[i] = sh.Name
|
||||
s.SectionHeader = SectionHeader{
|
||||
Type: SectionType(sh.Type),
|
||||
Flags: SectionFlag(sh.Flags),
|
||||
Offset: sh.Off,
|
||||
FileSize: sh.Size,
|
||||
Addr: sh.Addr,
|
||||
Link: sh.Link,
|
||||
Info: sh.Info,
|
||||
Addralign: sh.Addralign,
|
||||
Entsize: sh.Entsize,
|
||||
Shnum: i,
|
||||
Shname: sh.Name,
|
||||
}
|
||||
}
|
||||
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.FileSize))
|
||||
|
||||
if s.Flags&SHF_COMPRESSED == 0 {
|
||||
s.ReaderAt = s.sr
|
||||
s.Size = s.FileSize
|
||||
} else {
|
||||
// Read the compression header.
|
||||
switch f.Class {
|
||||
case ELFCLASS32:
|
||||
ch := new(Chdr32)
|
||||
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.compressionType = CompressionType(ch.Type)
|
||||
s.Size = uint64(ch.Size)
|
||||
s.Addralign = uint64(ch.Addralign)
|
||||
s.compressionOffset = int64(binary.Size(ch))
|
||||
case ELFCLASS64:
|
||||
ch := new(Chdr64)
|
||||
if err := binary.Read(s.sr, f.ByteOrder, ch); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.compressionType = CompressionType(ch.Type)
|
||||
s.Size = ch.Size
|
||||
s.Addralign = ch.Addralign
|
||||
s.compressionOffset = int64(binary.Size(ch))
|
||||
}
|
||||
}
|
||||
|
||||
f.Sections[i] = s
|
||||
}
|
||||
|
||||
if err := f.parseDynTags(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(f.Sections) == 0 {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Load section header string table.
|
||||
shstrtab, err := f.Sections[f.ShStrIndex].Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i, s := range f.Sections {
|
||||
var ok bool
|
||||
s.Name, ok = getString(shstrtab, int(names[i]))
|
||||
if !ok {
|
||||
return nil, &FormatError{f.SHTOffset + int64(i*shentsize), "bad section name index", names[i]}
|
||||
}
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// getSymbols returns a slice of Symbols from parsing the symbol table
|
||||
// with the given type, along with the associated string table.
|
||||
func (f *File) getSymbols(typ SectionType) ([]Symbol, []byte, error) {
|
||||
switch f.Class {
|
||||
case ELFCLASS64:
|
||||
return f.getSymbols64(typ)
|
||||
|
||||
case ELFCLASS32:
|
||||
return f.getSymbols32(typ)
|
||||
}
|
||||
|
||||
return nil, nil, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// ErrNoSymbols is returned by File.Symbols and File.DynamicSymbols
|
||||
// if there is no such section in the File.
|
||||
var ErrNoSymbols = errors.New("no symbol section")
|
||||
|
||||
func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) {
|
||||
symtabSection := f.SectionByType(typ)
|
||||
if symtabSection == nil {
|
||||
return nil, nil, ErrNoSymbols
|
||||
}
|
||||
|
||||
data, err := symtabSection.Data()
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("cannot load symbol section")
|
||||
}
|
||||
symtab := bytes.NewReader(data)
|
||||
if symtab.Len()%Sym32Size != 0 {
|
||||
return nil, nil, errors.New("length of symbol section is not a multiple of SymSize")
|
||||
}
|
||||
|
||||
strdata, err := f.stringTable(symtabSection.Link)
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("cannot load string table section")
|
||||
}
|
||||
|
||||
// The first entry is all zeros.
|
||||
var skip [Sym32Size]byte
|
||||
symtab.Read(skip[:])
|
||||
|
||||
symbols := make([]Symbol, symtab.Len()/Sym32Size)
|
||||
|
||||
i := 0
|
||||
var sym Sym32
|
||||
for symtab.Len() > 0 {
|
||||
binary.Read(symtab, f.ByteOrder, &sym)
|
||||
str, _ := getString(strdata, int(sym.Name))
|
||||
symbols[i].Name = str
|
||||
symbols[i].Info = sym.Info
|
||||
symbols[i].Other = sym.Other
|
||||
symbols[i].Section = SectionIndex(sym.Shndx)
|
||||
symbols[i].Value = uint64(sym.Value)
|
||||
symbols[i].Size = uint64(sym.Size)
|
||||
i++
|
||||
}
|
||||
|
||||
return symbols, strdata, nil
|
||||
}
|
||||
|
||||
func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) {
|
||||
symtabSection := f.SectionByType(typ)
|
||||
if symtabSection == nil {
|
||||
return nil, nil, ErrNoSymbols
|
||||
}
|
||||
|
||||
data, err := symtabSection.Data()
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("cannot load symbol section")
|
||||
}
|
||||
symtab := bytes.NewReader(data)
|
||||
if symtab.Len()%Sym64Size != 0 {
|
||||
return nil, nil, errors.New("length of symbol section is not a multiple of Sym64Size")
|
||||
}
|
||||
|
||||
strdata, err := f.stringTable(symtabSection.Link)
|
||||
if err != nil {
|
||||
return nil, nil, errors.New("cannot load string table section")
|
||||
}
|
||||
|
||||
// The first entry is all zeros.
|
||||
var skip [Sym64Size]byte
|
||||
symtab.Read(skip[:])
|
||||
|
||||
symbols := make([]Symbol, symtab.Len()/Sym64Size)
|
||||
|
||||
i := 0
|
||||
var sym Sym64
|
||||
for symtab.Len() > 0 {
|
||||
binary.Read(symtab, f.ByteOrder, &sym)
|
||||
str, _ := getString(strdata, int(sym.Name))
|
||||
symbols[i].Name = str
|
||||
symbols[i].NameIndex = sym.Name
|
||||
symbols[i].Info = sym.Info
|
||||
symbols[i].Other = sym.Other
|
||||
symbols[i].Section = SectionIndex(sym.Shndx)
|
||||
symbols[i].SectIndex = sym.Shndx
|
||||
symbols[i].Value = sym.Value
|
||||
symbols[i].Size = sym.Size
|
||||
i++
|
||||
}
|
||||
|
||||
return symbols, strdata, nil
|
||||
}
|
||||
|
||||
// getString extracts a string from an ELF string table.
|
||||
func getString(section []byte, start int) (string, bool) {
|
||||
if start < 0 || start >= len(section) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
for end := start; end < len(section); end++ {
|
||||
if section[end] == 0 {
|
||||
return string(section[start:end]), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Section returns a section with the given name, or nil if no such
|
||||
// section exists.
|
||||
func (f *File) Section(name string) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Name == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DWARF - No idea what this does
|
||||
func (f *File) DWARF() (*dwarf.Data, error) {
|
||||
dwarfSuffix := func(s *Section) string {
|
||||
switch {
|
||||
case strings.HasPrefix(s.Name, ".debug_"):
|
||||
return s.Name[7:]
|
||||
case strings.HasPrefix(s.Name, ".zdebug_"):
|
||||
return s.Name[8:]
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
}
|
||||
// sectionData gets the data for s, checks its size, and
|
||||
// applies any applicable relations.
|
||||
sectionData := func(i int, s *Section) ([]byte, error) {
|
||||
b, err := s.Data()
|
||||
if err != nil && uint64(len(b)) < s.Size {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b) >= 12 && string(b[:4]) == "ZLIB" {
|
||||
dlen := binary.BigEndian.Uint64(b[4:12])
|
||||
dbuf := make([]byte, dlen)
|
||||
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, dbuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = dbuf
|
||||
}
|
||||
|
||||
for _, r := range f.Sections {
|
||||
if r.Type != SHT_RELA && r.Type != SHT_REL {
|
||||
continue
|
||||
}
|
||||
if int(r.Info) != i {
|
||||
continue
|
||||
}
|
||||
rd, err := r.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = f.applyRelocations(b, rd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// There are many other DWARF sections, but these
|
||||
// are the ones the debug/dwarf package uses.
|
||||
// Don't bother loading others.
|
||||
var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
|
||||
for i, s := range f.Sections {
|
||||
suffix := dwarfSuffix(s)
|
||||
if suffix == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := dat[suffix]; !ok {
|
||||
continue
|
||||
}
|
||||
b, err := sectionData(i, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dat[suffix] = b
|
||||
}
|
||||
|
||||
d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Look for DWARF4 .debug_types sections.
|
||||
for i, s := range f.Sections {
|
||||
suffix := dwarfSuffix(s)
|
||||
if suffix != "types" {
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := sectionData(i, s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// Symbols returns the symbol table for f. The symbols will be listed in the order
|
||||
// they appear in f.
|
||||
//
|
||||
// For compatibility with Go 1.0, Symbols omits the null symbol at index 0.
|
||||
// After retrieving the symbols as symtab, an externally supplied index x
|
||||
// corresponds to symtab[x-1], not symtab[x].
|
||||
func (f *File) Symbols() ([]Symbol, error) {
|
||||
sym, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
return sym, err
|
||||
}
|
||||
|
||||
// DynamicSymbols returns the dynamic symbol table for f. The symbols
|
||||
// will be listed in the order they appear in f.
|
||||
//
|
||||
// For compatibility with Symbols, DynamicSymbols omits the null symbol at index 0.
|
||||
// After retrieving the symbols as symtab, an externally supplied index x
|
||||
// corresponds to symtab[x-1], not symtab[x].
|
||||
func (f *File) DynamicSymbols() ([]Symbol, error) {
|
||||
sym, _, err := f.getSymbols(SHT_DYNSYM)
|
||||
return sym, err
|
||||
}
|
||||
|
||||
type ImportedSymbol struct {
|
||||
Name string
|
||||
Version string
|
||||
Library string
|
||||
}
|
||||
|
||||
// ImportedSymbols returns the names of all symbols
|
||||
// referred to by the binary f that are expected to be
|
||||
// satisfied by other libraries at dynamic load time.
|
||||
// It does not return weak symbols.
|
||||
func (f *File) ImportedSymbols() ([]ImportedSymbol, error) {
|
||||
sym, str, err := f.getSymbols(SHT_DYNSYM)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.gnuVersionInit(str)
|
||||
var all []ImportedSymbol
|
||||
for i, s := range sym {
|
||||
if ST_BIND(s.Info) == STB_GLOBAL && s.Section == SHN_UNDEF {
|
||||
all = append(all, ImportedSymbol{Name: s.Name})
|
||||
f.gnuVersion(i, &all[len(all)-1])
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
type verneed struct {
|
||||
File string
|
||||
Name string
|
||||
}
|
||||
|
||||
// gnuVersionInit parses the GNU version tables
|
||||
// for use by calls to gnuVersion.
|
||||
func (f *File) gnuVersionInit(str []byte) {
|
||||
// Accumulate verneed information.
|
||||
vn := f.SectionByType(SHT_GNU_VERNEED)
|
||||
if vn == nil {
|
||||
return
|
||||
}
|
||||
d, _ := vn.Data()
|
||||
|
||||
var need []verneed
|
||||
i := 0
|
||||
for {
|
||||
if i+16 > len(d) {
|
||||
break
|
||||
}
|
||||
vers := f.ByteOrder.Uint16(d[i : i+2])
|
||||
if vers != 1 {
|
||||
break
|
||||
}
|
||||
cnt := f.ByteOrder.Uint16(d[i+2 : i+4])
|
||||
fileoff := f.ByteOrder.Uint32(d[i+4 : i+8])
|
||||
aux := f.ByteOrder.Uint32(d[i+8 : i+12])
|
||||
next := f.ByteOrder.Uint32(d[i+12 : i+16])
|
||||
file, _ := getString(str, int(fileoff))
|
||||
|
||||
var name string
|
||||
j := i + int(aux)
|
||||
for c := 0; c < int(cnt); c++ {
|
||||
if j+16 > len(d) {
|
||||
break
|
||||
}
|
||||
// hash := f.ByteOrder.Uint32(d[j:j+4])
|
||||
// flags := f.ByteOrder.Uint16(d[j+4:j+6])
|
||||
other := f.ByteOrder.Uint16(d[j+6 : j+8])
|
||||
nameoff := f.ByteOrder.Uint32(d[j+8 : j+12])
|
||||
next := f.ByteOrder.Uint32(d[j+12 : j+16])
|
||||
name, _ = getString(str, int(nameoff))
|
||||
ndx := int(other)
|
||||
if ndx >= len(need) {
|
||||
a := make([]verneed, 2*(ndx+1))
|
||||
copy(a, need)
|
||||
need = a
|
||||
}
|
||||
|
||||
need[ndx] = verneed{file, name}
|
||||
if next == 0 {
|
||||
break
|
||||
}
|
||||
j += int(next)
|
||||
}
|
||||
|
||||
if next == 0 {
|
||||
break
|
||||
}
|
||||
i += int(next)
|
||||
}
|
||||
|
||||
// Versym parallels symbol table, indexing into verneed.
|
||||
vs := f.SectionByType(SHT_GNU_VERSYM)
|
||||
if vs == nil {
|
||||
return
|
||||
}
|
||||
d, _ = vs.Data()
|
||||
|
||||
f.gnuNeed = need
|
||||
f.gnuVersym = d
|
||||
}
|
||||
|
||||
// gnuVersion adds Library and Version information to sym,
|
||||
// which came from offset i of the symbol table.
|
||||
func (f *File) gnuVersion(i int, sym *ImportedSymbol) {
|
||||
// Each entry is two bytes.
|
||||
i = (i + 1) * 2
|
||||
if i >= len(f.gnuVersym) {
|
||||
return
|
||||
}
|
||||
j := int(f.ByteOrder.Uint16(f.gnuVersym[i:]))
|
||||
if j < 2 || j >= len(f.gnuNeed) {
|
||||
return
|
||||
}
|
||||
n := &f.gnuNeed[j]
|
||||
sym.Library = n.File
|
||||
sym.Version = n.Name
|
||||
}
|
||||
|
||||
// ImportedLibraries returns the names of all libraries
|
||||
// referred to by the binary f that are expected to be
|
||||
// linked with the binary at dynamic link time.
|
||||
func (f *File) ImportedLibraries() ([]string, error) {
|
||||
return f.DynString(DT_NEEDED)
|
||||
}
|
||||
|
||||
func (f *File) parseDynTags() error {
|
||||
s := f.SectionByType(SHT_DYNAMIC)
|
||||
if s == nil {
|
||||
return nil // nothing to do
|
||||
}
|
||||
|
||||
var m []DynTagValue
|
||||
d, err := s.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//fmt.Printf("%+v\nd: %x len(%d)\n", s, d, len(d))
|
||||
|
||||
r := bytes.NewBuffer(d)
|
||||
for {
|
||||
var t, v uint64
|
||||
if err := binary.Read(r, f.ByteOrder, &t); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := binary.Read(r, f.ByteOrder, &v); err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
m = append(m, DynTagValue{Tag: DynTag(t), Value: v})
|
||||
//fmt.Printf("%x -> %x\n", t, v)
|
||||
}
|
||||
f.DynTags = m
|
||||
return nil
|
||||
}
|
||||
|
||||
// DynString returns the strings listed for the given tag in the file's dynamic
|
||||
// section.
|
||||
//
|
||||
// The tag must be one that takes string values: DT_NEEDED, DT_SONAME, DT_RPATH, or
|
||||
// DT_RUNPATH.
|
||||
func (f *File) DynString(tag DynTag) ([]string, error) {
|
||||
switch tag {
|
||||
case DT_NEEDED, DT_SONAME, DT_RPATH, DT_RUNPATH:
|
||||
default:
|
||||
return nil, fmt.Errorf("non-string-valued tag %v", tag)
|
||||
}
|
||||
|
||||
ds := f.SectionByType(SHT_DYNAMIC)
|
||||
if ds == nil {
|
||||
// not dynamic, so no libraries
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
str, err := f.stringTable(ds.Link)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var all []string
|
||||
for _, taggedValue := range f.DynTags {
|
||||
if taggedValue.Tag == tag {
|
||||
s, ok := getString(str, int(taggedValue.Value))
|
||||
if ok {
|
||||
all = append(all, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package elf
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// errorReader returns error from all operations.
|
||||
type errorReader struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (r errorReader) Read(p []byte) (n int, err error) {
|
||||
return 0, r.error
|
||||
}
|
||||
|
||||
func (r errorReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
return 0, r.error
|
||||
}
|
||||
|
||||
func (r errorReader) Seek(offset int64, whence int) (int64, error) {
|
||||
return 0, r.error
|
||||
}
|
||||
|
||||
func (r errorReader) Close() error {
|
||||
return r.error
|
||||
}
|
||||
|
||||
// readSeekerFromReader converts an io.Reader into an io.ReadSeeker.
|
||||
// In general Seek may not be efficient, but it is optimized for
|
||||
// common cases such as seeking to the end to find the length of the
|
||||
// data.
|
||||
type readSeekerFromReader struct {
|
||||
reset func() (io.Reader, error)
|
||||
r io.Reader
|
||||
size int64
|
||||
offset int64
|
||||
}
|
||||
|
||||
func (r *readSeekerFromReader) start() {
|
||||
x, err := r.reset()
|
||||
if err != nil {
|
||||
r.r = errorReader{err}
|
||||
} else {
|
||||
r.r = x
|
||||
}
|
||||
r.offset = 0
|
||||
}
|
||||
|
||||
func (r *readSeekerFromReader) Read(p []byte) (n int, err error) {
|
||||
if r.r == nil {
|
||||
r.start()
|
||||
}
|
||||
n, err = r.r.Read(p)
|
||||
r.offset += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *readSeekerFromReader) Seek(offset int64, whence int) (int64, error) {
|
||||
var newOffset int64
|
||||
switch whence {
|
||||
case seekStart:
|
||||
newOffset = offset
|
||||
case seekCurrent:
|
||||
newOffset = r.offset + offset
|
||||
case seekEnd:
|
||||
newOffset = r.size + offset
|
||||
default:
|
||||
return 0, os.ErrInvalid
|
||||
}
|
||||
|
||||
switch {
|
||||
case newOffset == r.offset:
|
||||
return newOffset, nil
|
||||
|
||||
case newOffset < 0, newOffset > r.size:
|
||||
return 0, os.ErrInvalid
|
||||
|
||||
case newOffset == 0:
|
||||
r.r = nil
|
||||
|
||||
case newOffset == r.size:
|
||||
r.r = errorReader{io.EOF}
|
||||
|
||||
default:
|
||||
if newOffset < r.offset {
|
||||
// Restart at the beginning.
|
||||
r.start()
|
||||
}
|
||||
// Read until we reach offset.
|
||||
var buf [512]byte
|
||||
for r.offset < newOffset {
|
||||
b := buf[:]
|
||||
if newOffset-r.offset < int64(len(buf)) {
|
||||
b = buf[:newOffset-r.offset]
|
||||
}
|
||||
if _, err := r.Read(b); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
r.offset = newOffset
|
||||
return r.offset, nil
|
||||
}
|
||||
-529
@@ -1,529 +0,0 @@
|
||||
package elf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// applyRelocations applies relocations to dst. rels is a relocations section
|
||||
// in REL or RELA format.
|
||||
func (f *File) applyRelocations(dst []byte, rels []byte) error {
|
||||
switch {
|
||||
case f.Class == ELFCLASS64 && f.Machine == EM_X86_64:
|
||||
return f.applyRelocationsAMD64(dst, rels)
|
||||
case f.Class == ELFCLASS32 && f.Machine == EM_386:
|
||||
return f.applyRelocations386(dst, rels)
|
||||
case f.Class == ELFCLASS32 && f.Machine == EM_ARM:
|
||||
return f.applyRelocationsARM(dst, rels)
|
||||
case f.Class == ELFCLASS64 && f.Machine == EM_AARCH64:
|
||||
return f.applyRelocationsARM64(dst, rels)
|
||||
case f.Class == ELFCLASS32 && f.Machine == EM_PPC:
|
||||
return f.applyRelocationsPPC(dst, rels)
|
||||
case f.Class == ELFCLASS64 && f.Machine == EM_PPC64:
|
||||
return f.applyRelocationsPPC64(dst, rels)
|
||||
case f.Class == ELFCLASS32 && f.Machine == EM_MIPS:
|
||||
return f.applyRelocationsMIPS(dst, rels)
|
||||
case f.Class == ELFCLASS64 && f.Machine == EM_MIPS:
|
||||
return f.applyRelocationsMIPS64(dst, rels)
|
||||
case f.Class == ELFCLASS64 && f.Machine == EM_RISCV:
|
||||
return f.applyRelocationsRISCV64(dst, rels)
|
||||
case f.Class == ELFCLASS64 && f.Machine == EM_S390:
|
||||
return f.applyRelocationss390x(dst, rels)
|
||||
case f.Class == ELFCLASS64 && f.Machine == EM_SPARCV9:
|
||||
return f.applyRelocationsSPARC64(dst, rels)
|
||||
default:
|
||||
return errors.New("applyRelocations: not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error {
|
||||
// 24 is the size of Rela64.
|
||||
if len(rels)%24 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 24")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela64
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
symNo := rela.Info >> 32
|
||||
t := R_X86_64(rela.Info & 0xffff)
|
||||
|
||||
if symNo == 0 || symNo > uint64(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
if SymType(sym.Info&0xf) != STT_SECTION {
|
||||
// We don't handle non-section relocations for now.
|
||||
continue
|
||||
}
|
||||
|
||||
// There are relocations, so this must be a normal
|
||||
// object file, and we only look at section symbols,
|
||||
// so we assume that the symbol value is 0.
|
||||
|
||||
switch t {
|
||||
case R_X86_64_64:
|
||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
|
||||
case R_X86_64_32:
|
||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocations386(dst []byte, rels []byte) error {
|
||||
// 8 is the size of Rel32.
|
||||
if len(rels)%8 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 8")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rel Rel32
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rel)
|
||||
symNo := rel.Info >> 8
|
||||
t := R_386(rel.Info & 0xff)
|
||||
|
||||
if symNo == 0 || symNo > uint32(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
|
||||
if t == R_386_32 {
|
||||
if rel.Off+4 >= uint32(len(dst)) {
|
||||
continue
|
||||
}
|
||||
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
|
||||
val += uint32(sym.Value)
|
||||
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsARM(dst []byte, rels []byte) error {
|
||||
// 8 is the size of Rel32.
|
||||
if len(rels)%8 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 8")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rel Rel32
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rel)
|
||||
symNo := rel.Info >> 8
|
||||
t := R_ARM(rel.Info & 0xff)
|
||||
|
||||
if symNo == 0 || symNo > uint32(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
|
||||
switch t {
|
||||
case R_ARM_ABS32:
|
||||
if rel.Off+4 >= uint32(len(dst)) {
|
||||
continue
|
||||
}
|
||||
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
|
||||
val += uint32(sym.Value)
|
||||
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsARM64(dst []byte, rels []byte) error {
|
||||
// 24 is the size of Rela64.
|
||||
if len(rels)%24 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 24")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela64
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
symNo := rela.Info >> 32
|
||||
t := R_AARCH64(rela.Info & 0xffff)
|
||||
|
||||
if symNo == 0 || symNo > uint64(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
if SymType(sym.Info&0xf) != STT_SECTION {
|
||||
// We don't handle non-section relocations for now.
|
||||
continue
|
||||
}
|
||||
|
||||
// There are relocations, so this must be a normal
|
||||
// object file, and we only look at section symbols,
|
||||
// so we assume that the symbol value is 0.
|
||||
|
||||
switch t {
|
||||
case R_AARCH64_ABS64:
|
||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
|
||||
case R_AARCH64_ABS32:
|
||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsPPC(dst []byte, rels []byte) error {
|
||||
// 12 is the size of Rela32.
|
||||
if len(rels)%12 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 12")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela32
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
symNo := rela.Info >> 8
|
||||
t := R_PPC(rela.Info & 0xff)
|
||||
|
||||
if symNo == 0 || symNo > uint32(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
if SymType(sym.Info&0xf) != STT_SECTION {
|
||||
// We don't handle non-section relocations for now.
|
||||
continue
|
||||
}
|
||||
|
||||
switch t {
|
||||
case R_PPC_ADDR32:
|
||||
if rela.Off+4 >= uint32(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsPPC64(dst []byte, rels []byte) error {
|
||||
// 24 is the size of Rela64.
|
||||
if len(rels)%24 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 24")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela64
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
symNo := rela.Info >> 32
|
||||
t := R_PPC64(rela.Info & 0xffff)
|
||||
|
||||
if symNo == 0 || symNo > uint64(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
if SymType(sym.Info&0xf) != STT_SECTION {
|
||||
// We don't handle non-section relocations for now.
|
||||
continue
|
||||
}
|
||||
|
||||
switch t {
|
||||
case R_PPC64_ADDR64:
|
||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
|
||||
case R_PPC64_ADDR32:
|
||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsMIPS(dst []byte, rels []byte) error {
|
||||
// 8 is the size of Rel32.
|
||||
if len(rels)%8 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 8")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rel Rel32
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rel)
|
||||
symNo := rel.Info >> 8
|
||||
t := R_MIPS(rel.Info & 0xff)
|
||||
|
||||
if symNo == 0 || symNo > uint32(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
|
||||
switch t {
|
||||
case R_MIPS_32:
|
||||
if rel.Off+4 >= uint32(len(dst)) {
|
||||
continue
|
||||
}
|
||||
val := f.ByteOrder.Uint32(dst[rel.Off : rel.Off+4])
|
||||
val += uint32(sym.Value)
|
||||
f.ByteOrder.PutUint32(dst[rel.Off:rel.Off+4], val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsMIPS64(dst []byte, rels []byte) error {
|
||||
// 24 is the size of Rela64.
|
||||
if len(rels)%24 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 24")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela64
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
var symNo uint64
|
||||
var t R_MIPS
|
||||
if f.ByteOrder == binary.BigEndian {
|
||||
symNo = rela.Info >> 32
|
||||
t = R_MIPS(rela.Info & 0xff)
|
||||
} else {
|
||||
symNo = rela.Info & 0xffffffff
|
||||
t = R_MIPS(rela.Info >> 56)
|
||||
}
|
||||
|
||||
if symNo == 0 || symNo > uint64(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
if SymType(sym.Info&0xf) != STT_SECTION {
|
||||
// We don't handle non-section relocations for now.
|
||||
continue
|
||||
}
|
||||
|
||||
switch t {
|
||||
case R_MIPS_64:
|
||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
|
||||
case R_MIPS_32:
|
||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsRISCV64(dst []byte, rels []byte) error {
|
||||
// 24 is the size of Rela64.
|
||||
if len(rels)%24 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 24")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela64
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
symNo := rela.Info >> 32
|
||||
t := R_RISCV(rela.Info & 0xffff)
|
||||
|
||||
if symNo == 0 || symNo > uint64(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
switch SymType(sym.Info & 0xf) {
|
||||
case STT_SECTION, STT_NOTYPE:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
switch t {
|
||||
case R_RISCV_64:
|
||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
val := sym.Value + uint64(rela.Addend)
|
||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val)
|
||||
case R_RISCV_32:
|
||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
val := uint32(sym.Value) + uint32(rela.Addend)
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationss390x(dst []byte, rels []byte) error {
|
||||
// 24 is the size of Rela64.
|
||||
if len(rels)%24 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 24")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela64
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
symNo := rela.Info >> 32
|
||||
t := R_390(rela.Info & 0xffff)
|
||||
|
||||
if symNo == 0 || symNo > uint64(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
switch SymType(sym.Info & 0xf) {
|
||||
case STT_SECTION, STT_NOTYPE:
|
||||
break
|
||||
default:
|
||||
continue
|
||||
}
|
||||
|
||||
switch t {
|
||||
case R_390_64:
|
||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
val := sym.Value + uint64(rela.Addend)
|
||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], val)
|
||||
case R_390_32:
|
||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
val := uint32(sym.Value) + uint32(rela.Addend)
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) applyRelocationsSPARC64(dst []byte, rels []byte) error {
|
||||
// 24 is the size of Rela64.
|
||||
if len(rels)%24 != 0 {
|
||||
return errors.New("length of relocation section is not a multiple of 24")
|
||||
}
|
||||
|
||||
symbols, _, err := f.getSymbols(SHT_SYMTAB)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b := bytes.NewReader(rels)
|
||||
var rela Rela64
|
||||
|
||||
for b.Len() > 0 {
|
||||
binary.Read(b, f.ByteOrder, &rela)
|
||||
symNo := rela.Info >> 32
|
||||
t := R_SPARC(rela.Info & 0xff)
|
||||
|
||||
if symNo == 0 || symNo > uint64(len(symbols)) {
|
||||
continue
|
||||
}
|
||||
sym := &symbols[symNo-1]
|
||||
if SymType(sym.Info&0xf) != STT_SECTION {
|
||||
// We don't handle non-section relocations for now.
|
||||
continue
|
||||
}
|
||||
|
||||
switch t {
|
||||
case R_SPARC_64, R_SPARC_UA64:
|
||||
if rela.Off+8 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint64(dst[rela.Off:rela.Off+8], uint64(rela.Addend))
|
||||
case R_SPARC_32, R_SPARC_UA32:
|
||||
if rela.Off+4 >= uint64(len(dst)) || rela.Addend < 0 {
|
||||
continue
|
||||
}
|
||||
f.ByteOrder.PutUint32(dst[rela.Off:rela.Off+4], uint32(rela.Addend))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
-281
@@ -1,281 +0,0 @@
|
||||
package elf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Bytes - returns the bytes of an Elf file
|
||||
func (elfFile *File) Bytes() ([]byte, error) {
|
||||
|
||||
bytesWritten := uint64(0)
|
||||
elfBuf := bytes.NewBuffer(nil)
|
||||
w := bufio.NewWriter(elfBuf)
|
||||
|
||||
// Write Elf Magic
|
||||
w.WriteByte('\x7f')
|
||||
w.WriteByte('E')
|
||||
w.WriteByte('L')
|
||||
w.WriteByte('F')
|
||||
bytesWritten += 4
|
||||
|
||||
// ident[EI_CLASS]
|
||||
w.WriteByte(byte(elfFile.Class))
|
||||
// ident[EI_DATA]
|
||||
w.WriteByte(byte(elfFile.Data))
|
||||
// ident[EI_VERSION]
|
||||
w.WriteByte(byte(elfFile.Version))
|
||||
// ident[EI_OSABI]
|
||||
w.WriteByte(byte(elfFile.OSABI))
|
||||
// ident[EI_ABIVERSION]
|
||||
w.WriteByte(byte(elfFile.ABIVersion))
|
||||
// ident[EI_PAD] ( 7 bytes )
|
||||
w.Write([]byte{0, 0, 0, 0, 0, 0, 0})
|
||||
bytesWritten += 12
|
||||
|
||||
// Type
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(elfFile.Type))
|
||||
// Machine
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(elfFile.Machine))
|
||||
// Version
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(elfFile.Version))
|
||||
bytesWritten += 8
|
||||
|
||||
phsize := 0
|
||||
|
||||
switch elfFile.Class {
|
||||
case ELFCLASS32:
|
||||
phsize = 0x20
|
||||
// Entry 32
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(elfFile.Entry))
|
||||
// PH Offset 32
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(0x34))
|
||||
// SH Offset 32 // 0x20 0x28 4 8 e_shoff Points to the start of the section header table.
|
||||
binary.Write(w, elfFile.ByteOrder, int32(elfFile.FileHeader.SHTOffset))
|
||||
// Flags
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(0)) // todo
|
||||
// EH Size
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(52))
|
||||
// PH Size // 0x2A 0x36 2 e_phentsize Contains the size of a program header table entry.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(phsize))
|
||||
// PH Num // 0x2C 0x38 2 e_phnum Contains the number of entries in the program header table.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Progs)))
|
||||
// SH Size // 0x2E 0x3A 2 e_shentsize Contains the size of a section header table entry.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(0x28))
|
||||
bytesWritten += 24
|
||||
|
||||
case ELFCLASS64:
|
||||
phsize = 0x38
|
||||
// Entry 64
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(elfFile.Entry))
|
||||
// PH Offset 64
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(0x40))
|
||||
// SH Offset 64 // 0x20 0x28 4 8 e_shoff Points to the start of the section header table.
|
||||
binary.Write(w, elfFile.ByteOrder, int64(elfFile.FileHeader.SHTOffset))
|
||||
// Flags
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(0)) // I think right?
|
||||
// EH Size
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(64))
|
||||
// PH Size // 0x2A 0x36 2 e_phentsize Contains the size of a program header table entry.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(phsize))
|
||||
// PH Num // 0x2C 0x38 2 e_phnum Contains the number of entries in the program header table.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Progs)))
|
||||
// SH Size // 0x2E 0x3A 2 e_shentsize Contains the size of a section header table entry.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(0x40))
|
||||
bytesWritten += 36
|
||||
}
|
||||
|
||||
// SH Num // 0x30 0x3C 2 e_shnum Contains the number of entries in the section header table.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(len(elfFile.Sections)))
|
||||
// SH Str Ndx // 0x32 0x3E 2 e_shstrndx Contains index of the section header table entry that contains the section names.
|
||||
binary.Write(w, elfFile.ByteOrder, uint16(elfFile.ShStrIndex))
|
||||
bytesWritten += 4
|
||||
|
||||
// Program Header
|
||||
for _, p := range elfFile.Progs {
|
||||
// Type (segment)
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Type))
|
||||
bytesWritten += 4
|
||||
|
||||
switch elfFile.Class {
|
||||
case ELFCLASS32:
|
||||
// Offset of Segment in File
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Off))
|
||||
|
||||
// Vaddr
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Vaddr))
|
||||
|
||||
// Paddr
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Paddr))
|
||||
|
||||
// File Size
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Filesz))
|
||||
|
||||
// Memory Size
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Memsz))
|
||||
|
||||
// Flags (segment)
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Flags))
|
||||
|
||||
// Alignment
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Align))
|
||||
|
||||
bytesWritten += 28
|
||||
|
||||
case ELFCLASS64:
|
||||
// Flags (segment)
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(p.Flags))
|
||||
|
||||
// Offset of Segment in File
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(p.Off))
|
||||
|
||||
// Vaddr
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(p.Vaddr))
|
||||
|
||||
// Paddr
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(p.Paddr))
|
||||
|
||||
// File Size
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(p.Filesz))
|
||||
|
||||
// Memory Size
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(p.Memsz))
|
||||
|
||||
// Alignment
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(p.Align))
|
||||
|
||||
bytesWritten += 52
|
||||
}
|
||||
}
|
||||
|
||||
sortedSections := elfFile.Sections[:]
|
||||
//sort.Slice(sortedSections, func(a, b int) bool { return elfFile.Sections[a].Link < elfFile.Sections[b].Link })
|
||||
for _, s := range sortedSections {
|
||||
|
||||
//log.Printf("Writing section: %s type: %+v\n", s.Name, s.Type)
|
||||
//log.Printf("written: %x offset: %x\n", bytesWritten, s.Offset)
|
||||
|
||||
if s.Type == SHT_NULL || s.Type == SHT_NOBITS || s.FileSize == 0 {
|
||||
//log.Println("continuing...")
|
||||
continue
|
||||
}
|
||||
|
||||
if bytesWritten > s.Offset {
|
||||
log.Printf("Overlapping Sections in Generated Elf: %+v\n", s.Name)
|
||||
continue
|
||||
}
|
||||
if s.Offset != 0 && bytesWritten < s.Offset {
|
||||
pad := make([]byte, s.Offset-bytesWritten)
|
||||
w.Write(pad)
|
||||
//log.Printf("Padding before section %s at %x: length:%x to:%x\n", s.Name, bytesWritten, len(pad), s.Offset)
|
||||
bytesWritten += uint64(len(pad))
|
||||
}
|
||||
|
||||
slen := 0
|
||||
switch s.Type {
|
||||
case SHT_DYNAMIC:
|
||||
for _, taggedValue := range elfFile.DynTags {
|
||||
//log.Printf("writing %d (%x) -> %d (%x)\n", taggedValue.Tag, taggedValue.Tag, taggedValue.Value, taggedValue.Value)
|
||||
switch elfFile.Class {
|
||||
case ELFCLASS32:
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(taggedValue.Tag))
|
||||
binary.Write(w, elfFile.ByteOrder, uint32(taggedValue.Value))
|
||||
bytesWritten += 8
|
||||
case ELFCLASS64:
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(taggedValue.Tag))
|
||||
binary.Write(w, elfFile.ByteOrder, uint64(taggedValue.Value))
|
||||
bytesWritten += 16
|
||||
}
|
||||
}
|
||||
default:
|
||||
section, err := ioutil.ReadAll(s.Open())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.Write(w, elfFile.ByteOrder, section)
|
||||
slen = len(section)
|
||||
//log.Printf("Wrote %s section at %x, length %x\n", s.Name, bytesWritten, slen)
|
||||
bytesWritten += uint64(slen)
|
||||
}
|
||||
|
||||
// todo: elfFile.Insertion should be renamed InsertionLoadEnd or similar
|
||||
if s.Type == SHT_PROGBITS && len(elfFile.Insertion) > 0 && s.Size-uint64(slen) >= uint64(len(elfFile.Insertion)) {
|
||||
binary.Write(w, elfFile.ByteOrder, elfFile.Insertion)
|
||||
bytesWritten += uint64(len(elfFile.Insertion))
|
||||
}
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
// Pad to Section Header Table
|
||||
if bytesWritten < uint64(elfFile.FileHeader.SHTOffset) {
|
||||
pad := make([]byte, uint64(elfFile.FileHeader.SHTOffset)-bytesWritten)
|
||||
w.Write(pad)
|
||||
//log.Printf("Padding before SHT at %x: length:%x to:%x\n", bytesWritten, len(pad), elfFile.FileHeader.SHTOffset)
|
||||
bytesWritten += uint64(len(pad))
|
||||
}
|
||||
|
||||
// Write Section Header Table
|
||||
|
||||
for _, s := range elfFile.Sections[:] {
|
||||
|
||||
switch elfFile.Class {
|
||||
case ELFCLASS32:
|
||||
binary.Write(w, elfFile.ByteOrder, &Section32{
|
||||
Name: s.Shname,
|
||||
Type: uint32(s.Type),
|
||||
Flags: uint32(s.Flags),
|
||||
Addr: uint32(s.Addr),
|
||||
Off: uint32(s.Offset),
|
||||
Size: uint32(s.Size),
|
||||
Link: s.Link,
|
||||
Info: s.Info,
|
||||
Addralign: uint32(s.Addralign),
|
||||
Entsize: uint32(s.Entsize)})
|
||||
case ELFCLASS64:
|
||||
binary.Write(w, elfFile.ByteOrder, &Section64{
|
||||
Name: s.Shname,
|
||||
Type: uint32(s.Type),
|
||||
Flags: uint64(s.Flags),
|
||||
Addr: s.Addr,
|
||||
Off: s.Offset,
|
||||
Size: s.Size,
|
||||
Link: s.Link,
|
||||
Info: s.Info,
|
||||
Addralign: s.Addralign,
|
||||
Entsize: s.Entsize})
|
||||
}
|
||||
}
|
||||
|
||||
// Do I have a PT_NOTE segment to add at the end?
|
||||
|
||||
if len(elfFile.InsertionEOF) > 0 {
|
||||
binary.Write(w, elfFile.ByteOrder, elfFile.InsertionEOF)
|
||||
bytesWritten += uint64(len(elfFile.InsertionEOF))
|
||||
}
|
||||
|
||||
w.Flush()
|
||||
return elfBuf.Bytes(), nil
|
||||
}
|
||||
|
||||
// WriteFile - Creates a new file and writes it using the Bytes func above
|
||||
func (elfFile *File) WriteFile(destFile string) error {
|
||||
f, err := os.Create(destFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
elfData, err := elfFile.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(elfData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
## More Info
|
||||
|
||||
More info on the Mach-O file structure here: [Mach-O Deep Dive](https://www.symbolcrash.com/2019/02/25/so-you-want-to-be-a-mach-o-man/)
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
package macho
|
||||
|
||||
const N_SECT = 0x0E
|
||||
const N_PEXT = 0x10
|
||||
const N_EXT = 0x01
|
||||
|
||||
// Export - describes a single export entry (similar to PE version for future refactoring)
|
||||
type Export struct {
|
||||
//Ordinal uint32 // no ordinals for Mach-O
|
||||
Name string
|
||||
VirtualAddress uint64
|
||||
}
|
||||
|
||||
// Exports - gets exports, including private exports
|
||||
func (f *File) Exports() []Export {
|
||||
var exports []Export
|
||||
for _, symbol := range f.Symtab.Syms {
|
||||
if (symbol.Type&N_PEXT == N_PEXT ||
|
||||
symbol.Type&N_EXT == N_EXT) && symbol.Value != 0 {
|
||||
var export Export
|
||||
export.Name = symbol.Name
|
||||
export.VirtualAddress = symbol.Value
|
||||
exports = append(exports, export)
|
||||
}
|
||||
}
|
||||
return exports
|
||||
}
|
||||
-146
@@ -1,146 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package macho
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// A FatFile is a Mach-O universal binary that contains at least one architecture.
|
||||
type FatFile struct {
|
||||
Magic uint32
|
||||
Arches []FatArch
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
// A FatArchHeader represents a fat header for a specific image architecture.
|
||||
type FatArchHeader struct {
|
||||
Cpu Cpu
|
||||
SubCpu uint32
|
||||
Offset uint32
|
||||
Size uint32
|
||||
Align uint32
|
||||
}
|
||||
|
||||
const fatArchHeaderSize = 5 * 4
|
||||
|
||||
// A FatArch is a Mach-O File inside a FatFile.
|
||||
type FatArch struct {
|
||||
FatArchHeader
|
||||
*File
|
||||
}
|
||||
|
||||
// ErrNotFat is returned from NewFatFile or OpenFat when the file is not a
|
||||
// universal binary but may be a thin binary, based on its magic number.
|
||||
var ErrNotFat = &FormatError{0, "not a fat Mach-O file", nil}
|
||||
|
||||
// NewFatFile creates a new FatFile for accessing all the Mach-O images in a
|
||||
// universal binary. The Mach-O binary is expected to start at position 0 in
|
||||
// the ReaderAt.
|
||||
func NewFatFile(r io.ReaderAt) (*FatFile, error) {
|
||||
var ff FatFile
|
||||
sr := io.NewSectionReader(r, 0, 1<<63-1)
|
||||
|
||||
// Read the fat_header struct, which is always in big endian.
|
||||
// Start with the magic number.
|
||||
err := binary.Read(sr, binary.BigEndian, &ff.Magic)
|
||||
if err != nil {
|
||||
return nil, &FormatError{0, "error reading magic number", nil}
|
||||
} else if ff.Magic != MagicFat {
|
||||
// See if this is a Mach-O file via its magic number. The magic
|
||||
// must be converted to little endian first though.
|
||||
var buf [4]byte
|
||||
binary.BigEndian.PutUint32(buf[:], ff.Magic)
|
||||
leMagic := binary.LittleEndian.Uint32(buf[:])
|
||||
if leMagic == Magic32 || leMagic == Magic64 {
|
||||
return nil, ErrNotFat
|
||||
} else {
|
||||
return nil, &FormatError{0, "invalid magic number", nil}
|
||||
}
|
||||
}
|
||||
offset := int64(4)
|
||||
|
||||
// Read the number of FatArchHeaders that come after the fat_header.
|
||||
var narch uint32
|
||||
err = binary.Read(sr, binary.BigEndian, &narch)
|
||||
if err != nil {
|
||||
return nil, &FormatError{offset, "invalid fat_header", nil}
|
||||
}
|
||||
offset += 4
|
||||
|
||||
if narch < 1 {
|
||||
return nil, &FormatError{offset, "file contains no images", nil}
|
||||
}
|
||||
|
||||
// Combine the Cpu and SubCpu (both uint32) into a uint64 to make sure
|
||||
// there are not duplicate architectures.
|
||||
seenArches := make(map[uint64]bool, narch)
|
||||
// Make sure that all images are for the same MH_ type.
|
||||
var machoType Type
|
||||
|
||||
// Following the fat_header comes narch fat_arch structs that index
|
||||
// Mach-O images further in the file.
|
||||
ff.Arches = make([]FatArch, narch)
|
||||
for i := uint32(0); i < narch; i++ {
|
||||
fa := &ff.Arches[i]
|
||||
err = binary.Read(sr, binary.BigEndian, &fa.FatArchHeader)
|
||||
if err != nil {
|
||||
return nil, &FormatError{offset, "invalid fat_arch header", nil}
|
||||
}
|
||||
offset += fatArchHeaderSize
|
||||
|
||||
fr := io.NewSectionReader(r, int64(fa.Offset), int64(fa.Size))
|
||||
fa.File, err = NewFile(fr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the architecture for this image is not duplicate.
|
||||
seenArch := (uint64(fa.Cpu) << 32) | uint64(fa.SubCpu)
|
||||
if o, k := seenArches[seenArch]; o || k {
|
||||
return nil, &FormatError{offset, fmt.Sprintf("duplicate architecture cpu=%v, subcpu=%#x", fa.Cpu, fa.SubCpu), nil}
|
||||
}
|
||||
seenArches[seenArch] = true
|
||||
|
||||
// Make sure the Mach-O type matches that of the first image.
|
||||
if i == 0 {
|
||||
machoType = fa.Type
|
||||
} else {
|
||||
if fa.Type != machoType {
|
||||
return nil, &FormatError{offset, fmt.Sprintf("Mach-O type for architecture #%d (type=%#x) does not match first (type=%#x)", i, fa.Type, machoType), nil}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &ff, nil
|
||||
}
|
||||
|
||||
// OpenFat opens the named file using os.Open and prepares it for use as a Mach-O
|
||||
// universal binary.
|
||||
func OpenFat(name string) (*FatFile, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ff, err := NewFatFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
ff.closer = f
|
||||
return ff, nil
|
||||
}
|
||||
|
||||
func (ff *FatFile) Close() error {
|
||||
var err error
|
||||
if ff.closer != nil {
|
||||
err = ff.closer.Close()
|
||||
ff.closer = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
-952
@@ -1,952 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package macho implements access to Mach-O object files.
|
||||
package macho
|
||||
|
||||
// High level access to low level data structures.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// A File represents an open Mach-O file.
|
||||
type File struct {
|
||||
FileHeader
|
||||
ByteOrder binary.ByteOrder
|
||||
Loads []Load
|
||||
Sections []*Section
|
||||
|
||||
Symtab *Symtab
|
||||
Dysymtab *Dysymtab
|
||||
SigBlock *SigBlock
|
||||
FuncStarts *FuncStarts
|
||||
DataInCode *DataInCode
|
||||
DylinkInfo *DylinkInfo
|
||||
|
||||
EntryPoint uint64
|
||||
Insertion []byte
|
||||
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
// A Load represents any Mach-O load command.
|
||||
type Load interface {
|
||||
Raw() []byte
|
||||
}
|
||||
|
||||
// A LoadBytes is the uninterpreted bytes of a Mach-O load command.
|
||||
type LoadBytes []byte
|
||||
|
||||
func (b LoadBytes) Raw() []byte { return b }
|
||||
|
||||
// A SegmentHeader is the header for a Mach-O 32-bit or 64-bit load segment command.
|
||||
type SegmentHeader struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Name string
|
||||
Addr uint64
|
||||
Memsz uint64
|
||||
Offset uint64
|
||||
Filesz uint64
|
||||
Maxprot uint32
|
||||
Prot uint32
|
||||
Nsect uint32
|
||||
Flag uint32
|
||||
}
|
||||
|
||||
type SigBlock struct {
|
||||
Len uint32
|
||||
Offset uint64
|
||||
RawDat []byte
|
||||
}
|
||||
|
||||
type FuncStarts struct {
|
||||
Len uint32
|
||||
Offset uint64
|
||||
RawDat []byte
|
||||
}
|
||||
|
||||
type DataInCode struct {
|
||||
Len uint32
|
||||
Offset uint64
|
||||
RawDat []byte
|
||||
}
|
||||
|
||||
type DylinkInfo struct {
|
||||
RebaseLen uint32
|
||||
RebaseOffset uint64
|
||||
RebaseDat []byte
|
||||
BindingInfoLen uint32
|
||||
BindingInfoOffset uint64
|
||||
BindingInfoDat []byte
|
||||
WeakBindingLen uint32
|
||||
WeakBindingOffset uint64
|
||||
WeakBindingDat []byte
|
||||
LazyBindingLen uint32
|
||||
LazyBindingOffset uint64
|
||||
LazyBindingDat []byte
|
||||
ExportInfoLen uint32
|
||||
ExportInfoOffset uint64
|
||||
ExportInfoDat []byte
|
||||
}
|
||||
|
||||
// A Segment represents a Mach-O 32-bit or 64-bit load segment command.
|
||||
type Segment struct {
|
||||
LoadBytes
|
||||
SegmentHeader
|
||||
|
||||
// Embed ReaderAt for ReadAt method.
|
||||
// Do not embed SectionReader directly
|
||||
// to avoid having Read and Seek.
|
||||
// If a client wants Read and Seek it must use
|
||||
// Open() to avoid fighting over the seek offset
|
||||
// with other clients.
|
||||
io.ReaderAt
|
||||
sr *io.SectionReader
|
||||
}
|
||||
|
||||
// Data reads and returns the contents of the segment.
|
||||
func (s *Segment) Data() ([]byte, error) {
|
||||
dat := make([]byte, s.sr.Size())
|
||||
n, err := s.sr.ReadAt(dat, 0)
|
||||
if n == len(dat) {
|
||||
err = nil
|
||||
}
|
||||
return dat[0:n], err
|
||||
}
|
||||
|
||||
// Open returns a new ReadSeeker reading the segment.
|
||||
func (s *Segment) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
|
||||
|
||||
type SectionHeader struct {
|
||||
Name string
|
||||
Seg string
|
||||
Addr uint64
|
||||
Size uint64
|
||||
Offset uint32
|
||||
Align uint32
|
||||
Reloff uint32
|
||||
Nreloc uint32
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
// A Reloc represents a Mach-O relocation.
|
||||
type Reloc struct {
|
||||
Addr uint32
|
||||
Value uint32
|
||||
// when Scattered == false && Extern == true, Value is the symbol number.
|
||||
// when Scattered == false && Extern == false, Value is the section number.
|
||||
// when Scattered == true, Value is the value that this reloc refers to.
|
||||
Type uint8
|
||||
Len uint8 // 0=byte, 1=word, 2=long, 3=quad
|
||||
Pcrel bool
|
||||
Extern bool // valid if Scattered == false
|
||||
Scattered bool
|
||||
}
|
||||
|
||||
type Section struct {
|
||||
SectionHeader
|
||||
Relocs []Reloc
|
||||
|
||||
// Embed ReaderAt for ReadAt method.
|
||||
// Do not embed SectionReader directly
|
||||
// to avoid having Read and Seek.
|
||||
// If a client wants Read and Seek it must use
|
||||
// Open() to avoid fighting over the seek offset
|
||||
// with other clients.
|
||||
io.ReaderAt
|
||||
sr *io.SectionReader
|
||||
}
|
||||
|
||||
// Data reads and returns the contents of the Mach-O section.
|
||||
func (s *Section) Data() ([]byte, error) {
|
||||
dat := make([]byte, s.sr.Size())
|
||||
n, err := s.sr.ReadAt(dat, 0)
|
||||
if n == len(dat) {
|
||||
err = nil
|
||||
}
|
||||
return dat[0:n], err
|
||||
}
|
||||
|
||||
// Open returns a new ReadSeeker reading the Mach-O section.
|
||||
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }
|
||||
|
||||
// A Dylinker represents a Mach-O load dynamic library command.
|
||||
type Dylinker struct {
|
||||
LoadBytes
|
||||
Name string
|
||||
}
|
||||
|
||||
// A Dylib represents a Mach-O load dynamic library command.
|
||||
type Dylib struct {
|
||||
LoadBytes
|
||||
Name string
|
||||
Time uint32
|
||||
CurrentVersion uint32
|
||||
CompatVersion uint32
|
||||
}
|
||||
|
||||
// A Symtab represents a Mach-O symbol table command.
|
||||
type Symtab struct {
|
||||
LoadBytes
|
||||
SymtabCmd
|
||||
Syms []Symbol
|
||||
RawSymtab []byte
|
||||
RawStringtab []byte
|
||||
}
|
||||
|
||||
// A Dysymtab represents a Mach-O dynamic symbol table command.
|
||||
type Dysymtab struct {
|
||||
LoadBytes
|
||||
DysymtabCmd
|
||||
IndirectSyms []uint32 // indices into Symtab.Syms
|
||||
RawDysymtab []byte
|
||||
}
|
||||
|
||||
// A Rpath represents a Mach-O rpath command.
|
||||
type Rpath struct {
|
||||
LoadBytes
|
||||
Path string
|
||||
}
|
||||
|
||||
// A Symbol is a Mach-O 32-bit or 64-bit symbol table entry.
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Type uint8
|
||||
Sect uint8
|
||||
Desc uint16
|
||||
Value uint64
|
||||
}
|
||||
|
||||
/*
|
||||
* Mach-O reader
|
||||
*/
|
||||
|
||||
// FormatError is returned by some operations if the data does
|
||||
// not have the correct format for an object file.
|
||||
type FormatError struct {
|
||||
off int64
|
||||
msg string
|
||||
val interface{}
|
||||
}
|
||||
|
||||
func (e *FormatError) Error() string {
|
||||
msg := e.msg
|
||||
if e.val != nil {
|
||||
msg += fmt.Sprintf(" '%v'", e.val)
|
||||
}
|
||||
msg += fmt.Sprintf(" in record at byte %#x", e.off)
|
||||
return msg
|
||||
}
|
||||
|
||||
// Open opens the named file using os.Open and prepares it for use as a Mach-O binary.
|
||||
func Open(name string) (*File, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ff, err := NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
ff.closer = f
|
||||
return ff, nil
|
||||
}
|
||||
|
||||
// Close closes the File.
|
||||
// If the File was created using NewFile directly instead of Open,
|
||||
// Close has no effect.
|
||||
func (f *File) Close() error {
|
||||
var err error
|
||||
if f.closer != nil {
|
||||
err = f.closer.Close()
|
||||
f.closer = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// NewFile creates a new macho.File for accessing a Mach-o binary file in an underlying reader.
|
||||
func NewFile(r io.ReaderAt) (*File, error) {
|
||||
return newFileInternal(r, false)
|
||||
}
|
||||
|
||||
// NewFileFromMemory creates a new macho.File for accessing a Mach-O binary in-memory image in an underlying reader.
|
||||
func NewFileFromMemory(r io.ReaderAt) (*File, error) {
|
||||
return newFileInternal(r, true)
|
||||
}
|
||||
|
||||
// NewFile creates a new File for accessing a PE binary in an underlying reader.
|
||||
func newFileInternal(r io.ReaderAt, memoryMode bool) (*File, error) {
|
||||
|
||||
f := new(File)
|
||||
sr := io.NewSectionReader(r, 0, 1<<63-1)
|
||||
|
||||
// Read and decode Mach magic to determine byte order, size.
|
||||
// Magic32 and Magic64 differ only in the bottom bit.
|
||||
var ident [4]byte
|
||||
if _, err := r.ReadAt(ident[0:], 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
be := binary.BigEndian.Uint32(ident[0:])
|
||||
le := binary.LittleEndian.Uint32(ident[0:])
|
||||
switch Magic32 &^ 1 {
|
||||
case be &^ 1:
|
||||
f.ByteOrder = binary.BigEndian
|
||||
f.Magic = be
|
||||
case le &^ 1:
|
||||
f.ByteOrder = binary.LittleEndian
|
||||
f.Magic = le
|
||||
default:
|
||||
return nil, &FormatError{0, "invalid magic number", nil}
|
||||
}
|
||||
|
||||
// Read entire file header.
|
||||
if err := binary.Read(sr, f.ByteOrder, &f.FileHeader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Then load commands.
|
||||
offset := int64(fileHeaderSize32)
|
||||
if f.Magic == Magic64 {
|
||||
offset = fileHeaderSize64
|
||||
}
|
||||
dat := make([]byte, f.Cmdsz)
|
||||
if _, err := r.ReadAt(dat, offset); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Loads = make([]Load, f.Ncmd)
|
||||
bo := f.ByteOrder
|
||||
for i := range f.Loads {
|
||||
// Each load command begins with uint32 command and length.
|
||||
if len(dat) < 8 {
|
||||
return nil, &FormatError{offset, "command block too small", nil}
|
||||
}
|
||||
cmd, siz := LoadCmd(bo.Uint32(dat[0:4])), bo.Uint32(dat[4:8])
|
||||
if siz < 8 || siz > uint32(len(dat)) {
|
||||
return nil, &FormatError{offset, "invalid command block size", nil}
|
||||
}
|
||||
var cmddat []byte
|
||||
cmddat, dat = dat[0:siz], dat[siz:]
|
||||
offset += int64(siz)
|
||||
var s *Segment
|
||||
//fmt.Printf("LoadCmdVal: %+v\n", cmd)
|
||||
switch cmd {
|
||||
default:
|
||||
f.Loads[i] = LoadBytes(cmddat)
|
||||
|
||||
case LoadCmdRpath:
|
||||
var hdr RpathCmd
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := new(Rpath)
|
||||
if hdr.Path >= uint32(len(cmddat)) {
|
||||
return nil, &FormatError{offset, "invalid path in rpath command", hdr.Path}
|
||||
}
|
||||
l.Path = cstring(cmddat[hdr.Path:])
|
||||
l.LoadBytes = LoadBytes(cmddat)
|
||||
f.Loads[i] = l
|
||||
|
||||
case LoadCmdDylinker:
|
||||
var hdr DylinkerCmd
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := new(Dylinker)
|
||||
if hdr.Name >= uint32(len(cmddat)) {
|
||||
return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
|
||||
}
|
||||
l.Name = cstring(cmddat[hdr.Name:])
|
||||
l.LoadBytes = LoadBytes(cmddat)
|
||||
f.Loads[i] = l
|
||||
|
||||
case LoadCmdDylib:
|
||||
var hdr DylibCmd
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l := new(Dylib)
|
||||
if hdr.Name >= uint32(len(cmddat)) {
|
||||
return nil, &FormatError{offset, "invalid name in dynamic library command", hdr.Name}
|
||||
}
|
||||
l.Name = cstring(cmddat[hdr.Name:])
|
||||
l.Time = hdr.Time
|
||||
l.CurrentVersion = hdr.CurrentVersion
|
||||
l.CompatVersion = hdr.CompatVersion
|
||||
l.LoadBytes = LoadBytes(cmddat)
|
||||
f.Loads[i] = l
|
||||
|
||||
case LoadCmdSymtab:
|
||||
var hdr SymtabCmd
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
strtab := make([]byte, hdr.Strsize)
|
||||
|
||||
var linkeditAddr, textAddr, linkeditOffset int64
|
||||
if !memoryMode {
|
||||
if _, err := r.ReadAt(strtab, int64(hdr.Stroff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// in memory, we have to translate the file offsets for strtab/symtab into offsets into LINKEDIT segment
|
||||
for _, load := range f.Loads {
|
||||
switch segment := load.(type) {
|
||||
case *Segment:
|
||||
if segment == nil {
|
||||
continue
|
||||
}
|
||||
if segment.Name == "__LINKEDIT" {
|
||||
linkeditAddr = int64(segment.Addr)
|
||||
linkeditOffset = int64(segment.Offset)
|
||||
} else if segment.Name == "__TEXT" {
|
||||
textAddr = int64(segment.Addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
strtabAddr := (linkeditAddr - textAddr) + (int64(hdr.Stroff) - linkeditOffset)
|
||||
if _, err := r.ReadAt(strtab, strtabAddr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var symsz int
|
||||
if f.Magic == Magic64 {
|
||||
symsz = 16
|
||||
} else {
|
||||
symsz = 12
|
||||
}
|
||||
symdat := make([]byte, int(hdr.Nsyms)*symsz)
|
||||
|
||||
if !memoryMode {
|
||||
if _, err := r.ReadAt(symdat, int64(hdr.Symoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if _, err := r.ReadAt(symdat, (linkeditAddr-textAddr)+(int64(hdr.Symoff)-linkeditOffset)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
st, err := f.parseSymtab(symdat, strtab, cmddat, &hdr, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Loads[i] = st
|
||||
f.Symtab = st
|
||||
f.Symtab.Symoff = hdr.Symoff
|
||||
f.Symtab.Stroff = hdr.Stroff
|
||||
|
||||
case LoadCmdSignature:
|
||||
var sigCmd SigBlockCmd
|
||||
s := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(s, bo, &sigCmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//fmt.Printf("SigData: %+v\n", sigCmd)
|
||||
sig := make([]byte, sigCmd.Sigsize)
|
||||
if _, err := r.ReadAt(sig, int64(sigCmd.Sigoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var block SigBlock
|
||||
block.Offset = uint64(sigCmd.Sigoff)
|
||||
block.Len = sigCmd.Sigsize
|
||||
block.RawDat = sig
|
||||
f.SigBlock = &block
|
||||
f.Loads[i] = LoadBytes(cmddat)
|
||||
|
||||
case LoadCmdFuncStarts:
|
||||
var funcCmd FuncStartsCmd
|
||||
fsc := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(fsc, bo, &funcCmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//fmt.Printf("FuncStartsData: %+v\n", funcCmd)
|
||||
fs := make([]byte, funcCmd.Datasize)
|
||||
if _, err := r.ReadAt(fs, int64(funcCmd.Dataoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var funcs FuncStarts
|
||||
funcs.Offset = uint64(funcCmd.Dataoff)
|
||||
funcs.Len = funcCmd.Datasize
|
||||
funcs.RawDat = fs
|
||||
f.FuncStarts = &funcs
|
||||
f.Loads[i] = LoadBytes(cmddat)
|
||||
|
||||
case LoadCmdDataInCode:
|
||||
var dataCmd DataInCodeCmd
|
||||
dcc := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(dcc, bo, &dataCmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//fmt.Printf("DataInCode: %+v\n", dataCmd)
|
||||
dc := make([]byte, dataCmd.Datasize)
|
||||
if _, err := r.ReadAt(dc, int64(dataCmd.Dataoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var datacode DataInCode
|
||||
datacode.Offset = uint64(dataCmd.Dataoff)
|
||||
datacode.Len = dataCmd.Datasize
|
||||
datacode.RawDat = dc
|
||||
f.DataInCode = &datacode
|
||||
f.Loads[i] = LoadBytes(cmddat)
|
||||
|
||||
case LoadCmdDylinkInfo:
|
||||
var dylinkInfoCmd DylinkInfoCmd
|
||||
dic := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(dic, bo, &dylinkInfoCmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//fmt.Printf("LoadCmdDylinkInfo: %+v\n", dylinkInfoCmd)
|
||||
// Copy each section next
|
||||
var dylinkInfo DylinkInfo
|
||||
// Rebase deets
|
||||
if dylinkInfoCmd.Rebasesize > 0 {
|
||||
if !memoryMode { // this data is in LINKEDIT already
|
||||
rebase := make([]byte, dylinkInfoCmd.Rebasesize)
|
||||
if _, err := r.ReadAt(rebase, int64(dylinkInfoCmd.Rebaseoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dylinkInfo.RebaseDat = rebase
|
||||
}
|
||||
dylinkInfo.RebaseLen = dylinkInfoCmd.Rebasesize
|
||||
dylinkInfo.RebaseOffset = uint64(dylinkInfoCmd.Rebaseoff)
|
||||
}
|
||||
// BindingInfo deets
|
||||
if dylinkInfoCmd.Bindinginfosize > 0 {
|
||||
if !memoryMode { // this data is in LINKEDIT already
|
||||
binding := make([]byte, dylinkInfoCmd.Bindinginfosize)
|
||||
if _, err := r.ReadAt(binding, int64(dylinkInfoCmd.Bindinginfooff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dylinkInfo.BindingInfoDat = binding
|
||||
}
|
||||
dylinkInfo.BindingInfoLen = dylinkInfoCmd.Bindinginfosize
|
||||
dylinkInfo.BindingInfoOffset = uint64(dylinkInfoCmd.Bindinginfooff)
|
||||
}
|
||||
// Weak deets
|
||||
if dylinkInfoCmd.Weakbindingsize > 0 {
|
||||
if !memoryMode { // this data is in LINKEDIT already
|
||||
weak := make([]byte, dylinkInfoCmd.Weakbindingsize)
|
||||
if _, err := r.ReadAt(weak, int64(dylinkInfoCmd.Weakbindingoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dylinkInfo.WeakBindingDat = weak
|
||||
}
|
||||
dylinkInfo.WeakBindingLen = dylinkInfoCmd.Weakbindingsize
|
||||
dylinkInfo.WeakBindingOffset = uint64(dylinkInfoCmd.Weakbindingoff)
|
||||
}
|
||||
// Lazy deets
|
||||
if dylinkInfoCmd.Lazybindingsize > 0 {
|
||||
if !memoryMode { // this data is in LINKEDIT already
|
||||
lazy := make([]byte, dylinkInfoCmd.Lazybindingsize)
|
||||
if _, err := r.ReadAt(lazy, int64(dylinkInfoCmd.Lazybindingoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dylinkInfo.LazyBindingDat = lazy
|
||||
}
|
||||
dylinkInfo.LazyBindingLen = dylinkInfoCmd.Lazybindingsize
|
||||
dylinkInfo.LazyBindingOffset = uint64(dylinkInfoCmd.Lazybindingoff)
|
||||
}
|
||||
// ExportInfo deets
|
||||
if dylinkInfoCmd.Exportinfosize > 0 {
|
||||
if !memoryMode { // this data is in LINKEDIT already
|
||||
export := make([]byte, dylinkInfoCmd.Exportinfosize)
|
||||
if _, err := r.ReadAt(export, int64(dylinkInfoCmd.Exportinfooff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dylinkInfo.ExportInfoDat = export
|
||||
}
|
||||
dylinkInfo.ExportInfoLen = dylinkInfoCmd.Exportinfosize
|
||||
dylinkInfo.ExportInfoOffset = uint64(dylinkInfoCmd.Exportinfooff)
|
||||
}
|
||||
// Finalize the object
|
||||
f.DylinkInfo = &dylinkInfo
|
||||
f.Loads[i] = LoadBytes(cmddat)
|
||||
|
||||
case LoadCmdDysymtab:
|
||||
var hdr DysymtabCmd
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &hdr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dat := make([]byte, hdr.Nindirectsyms*4)
|
||||
if _, err := r.ReadAt(dat, int64(hdr.Indirectsymoff)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := make([]uint32, hdr.Nindirectsyms)
|
||||
if err := binary.Read(bytes.NewReader(dat), bo, x); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
st := new(Dysymtab)
|
||||
st.LoadBytes = LoadBytes(cmddat)
|
||||
st.DysymtabCmd = hdr
|
||||
st.IndirectSyms = x
|
||||
f.Loads[i] = st
|
||||
f.Dysymtab = st
|
||||
f.Dysymtab.Indirectsymoff = hdr.Indirectsymoff
|
||||
f.Dysymtab.RawDysymtab = dat
|
||||
|
||||
case LoadCmdSegment:
|
||||
var seg32 Segment32
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &seg32); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s = new(Segment)
|
||||
s.LoadBytes = cmddat
|
||||
s.Cmd = cmd
|
||||
s.Len = siz
|
||||
s.Name = cstring(seg32.Name[0:])
|
||||
s.Addr = uint64(seg32.Addr)
|
||||
s.Memsz = uint64(seg32.Memsz)
|
||||
s.Offset = uint64(seg32.Offset)
|
||||
s.Filesz = uint64(seg32.Filesz)
|
||||
s.Maxprot = seg32.Maxprot
|
||||
s.Prot = seg32.Prot
|
||||
s.Nsect = seg32.Nsect
|
||||
s.Flag = seg32.Flag
|
||||
if uint64((seg32.Offset + seg32.Filesz)) > FinalSegEnd {
|
||||
FinalSegEnd = uint64((seg32.Offset + seg32.Filesz))
|
||||
}
|
||||
f.Loads[i] = s
|
||||
for i := 0; i < int(s.Nsect); i++ {
|
||||
var sh32 Section32
|
||||
if err := binary.Read(b, bo, &sh32); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sh := new(Section)
|
||||
sh.Name = cstring(sh32.Name[0:])
|
||||
sh.Seg = cstring(sh32.Seg[0:])
|
||||
sh.Addr = uint64(sh32.Addr)
|
||||
sh.Size = uint64(sh32.Size)
|
||||
sh.Offset = sh32.Offset
|
||||
sh.Align = sh32.Align
|
||||
sh.Reloff = sh32.Reloff
|
||||
sh.Nreloc = sh32.Nreloc
|
||||
sh.Flags = sh32.Flags
|
||||
if err := f.pushSection(sh, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
case LoadCmdSegment64:
|
||||
var seg64 Segment64
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &seg64); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s = new(Segment)
|
||||
s.LoadBytes = cmddat
|
||||
s.Cmd = cmd
|
||||
s.Len = siz
|
||||
s.Name = cstring(seg64.Name[0:])
|
||||
s.Addr = seg64.Addr
|
||||
s.Memsz = seg64.Memsz
|
||||
s.Offset = seg64.Offset
|
||||
s.Filesz = seg64.Filesz
|
||||
s.Maxprot = seg64.Maxprot
|
||||
s.Prot = seg64.Prot
|
||||
s.Nsect = seg64.Nsect
|
||||
s.Flag = seg64.Flag
|
||||
if uint64((seg64.Offset + seg64.Filesz)) > FinalSegEnd {
|
||||
FinalSegEnd = uint64((seg64.Offset + seg64.Filesz))
|
||||
}
|
||||
f.Loads[i] = s
|
||||
for i := 0; i < int(s.Nsect); i++ {
|
||||
var sh64 Section64
|
||||
if err := binary.Read(b, bo, &sh64); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sh := new(Section)
|
||||
sh.Name = cstring(sh64.Name[0:])
|
||||
sh.Seg = cstring(sh64.Seg[0:])
|
||||
sh.Addr = sh64.Addr
|
||||
sh.Size = sh64.Size
|
||||
sh.Offset = sh64.Offset
|
||||
sh.Align = sh64.Align
|
||||
sh.Reloff = sh64.Reloff
|
||||
sh.Nreloc = sh64.Nreloc
|
||||
sh.Flags = sh64.Flags
|
||||
if err := f.pushSection(sh, r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
//case LoadCmdUnixThread:
|
||||
// todo: do we have to support thread_command here for older binaries? or is the LC_MAIN handling backwards compatible?
|
||||
|
||||
case LoadCmdMain:
|
||||
var entryPoint EntryPointCmd
|
||||
b := bytes.NewReader(cmddat)
|
||||
if err := binary.Read(b, bo, &entryPoint); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.EntryPoint = entryPoint.EntryOff
|
||||
}
|
||||
if s != nil {
|
||||
if !memoryMode {
|
||||
s.sr = io.NewSectionReader(r, int64(s.Offset), int64(s.Filesz))
|
||||
} else {
|
||||
s.sr = io.NewSectionReader(r, int64(s.Addr), int64(s.Filesz))
|
||||
}
|
||||
s.ReaderAt = s.sr
|
||||
}
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *File) parseSymtab(symdat, strtab, cmddat []byte, hdr *SymtabCmd, offset int64) (*Symtab, error) {
|
||||
bo := f.ByteOrder
|
||||
symtab := make([]Symbol, hdr.Nsyms)
|
||||
b := bytes.NewReader(symdat)
|
||||
for i := range symtab {
|
||||
var n Nlist64
|
||||
if f.Magic == Magic64 {
|
||||
if err := binary.Read(b, bo, &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
var n32 Nlist32
|
||||
if err := binary.Read(b, bo, &n32); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
n.Name = n32.Name
|
||||
n.Type = n32.Type
|
||||
n.Sect = n32.Sect
|
||||
n.Desc = n32.Desc
|
||||
n.Value = uint64(n32.Value)
|
||||
}
|
||||
sym := &symtab[i]
|
||||
if n.Name >= uint32(len(strtab)) {
|
||||
return nil, &FormatError{offset, "invalid name in symbol table", n.Name}
|
||||
}
|
||||
sym.Name = cstring(strtab[n.Name:])
|
||||
sym.Type = n.Type
|
||||
sym.Sect = n.Sect
|
||||
sym.Desc = n.Desc
|
||||
sym.Value = n.Value
|
||||
}
|
||||
st := new(Symtab)
|
||||
st.LoadBytes = LoadBytes(cmddat)
|
||||
st.Syms = symtab
|
||||
st.RawSymtab = symdat
|
||||
st.RawStringtab = strtab
|
||||
return st, nil
|
||||
}
|
||||
|
||||
type relocInfo struct {
|
||||
Addr uint32
|
||||
Symnum uint32
|
||||
}
|
||||
|
||||
func (f *File) pushSection(sh *Section, r io.ReaderAt) error {
|
||||
f.Sections = append(f.Sections, sh)
|
||||
sh.sr = io.NewSectionReader(r, int64(sh.Offset), int64(sh.Size))
|
||||
sh.ReaderAt = sh.sr
|
||||
|
||||
if sh.Nreloc > 0 {
|
||||
reldat := make([]byte, int(sh.Nreloc)*8)
|
||||
if _, err := r.ReadAt(reldat, int64(sh.Reloff)); err != nil {
|
||||
return err
|
||||
}
|
||||
b := bytes.NewReader(reldat)
|
||||
|
||||
bo := f.ByteOrder
|
||||
|
||||
sh.Relocs = make([]Reloc, sh.Nreloc)
|
||||
for i := range sh.Relocs {
|
||||
rel := &sh.Relocs[i]
|
||||
|
||||
var ri relocInfo
|
||||
if err := binary.Read(b, bo, &ri); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ri.Addr&(1<<31) != 0 { // scattered
|
||||
rel.Addr = ri.Addr & (1<<24 - 1)
|
||||
rel.Type = uint8((ri.Addr >> 24) & (1<<4 - 1))
|
||||
rel.Len = uint8((ri.Addr >> 28) & (1<<2 - 1))
|
||||
rel.Pcrel = ri.Addr&(1<<30) != 0
|
||||
rel.Value = ri.Symnum
|
||||
rel.Scattered = true
|
||||
} else {
|
||||
switch bo {
|
||||
case binary.LittleEndian:
|
||||
rel.Addr = ri.Addr
|
||||
rel.Value = ri.Symnum & (1<<24 - 1)
|
||||
rel.Pcrel = ri.Symnum&(1<<24) != 0
|
||||
rel.Len = uint8((ri.Symnum >> 25) & (1<<2 - 1))
|
||||
rel.Extern = ri.Symnum&(1<<27) != 0
|
||||
rel.Type = uint8((ri.Symnum >> 28) & (1<<4 - 1))
|
||||
case binary.BigEndian:
|
||||
rel.Addr = ri.Addr
|
||||
rel.Value = ri.Symnum >> 8
|
||||
rel.Pcrel = ri.Symnum&(1<<7) != 0
|
||||
rel.Len = uint8((ri.Symnum >> 5) & (1<<2 - 1))
|
||||
rel.Extern = ri.Symnum&(1<<4) != 0
|
||||
rel.Type = uint8(ri.Symnum & (1<<4 - 1))
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cstring(b []byte) string {
|
||||
i := bytes.IndexByte(b, 0)
|
||||
if i == -1 {
|
||||
i = len(b)
|
||||
}
|
||||
return string(b[0:i])
|
||||
}
|
||||
|
||||
// Segment returns the first Segment with the given name, or nil if no such segment exists.
|
||||
func (f *File) Segment(name string) *Segment {
|
||||
for _, l := range f.Loads {
|
||||
if s, ok := l.(*Segment); ok && s.Name == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Section returns the first section with the given name, or nil if no such
|
||||
// section exists.
|
||||
func (f *File) Section(name string) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Name == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DWARF returns the DWARF debug information for the Mach-O file.
|
||||
func (f *File) DWARF() (*dwarf.Data, error) {
|
||||
dwarfSuffix := func(s *Section) string {
|
||||
switch {
|
||||
case strings.HasPrefix(s.Name, "__debug_"):
|
||||
return s.Name[8:]
|
||||
case strings.HasPrefix(s.Name, "__zdebug_"):
|
||||
return s.Name[9:]
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
}
|
||||
sectionData := func(s *Section) ([]byte, error) {
|
||||
b, err := s.Data()
|
||||
if err != nil && uint64(len(b)) < s.Size {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(b) >= 12 && string(b[:4]) == "ZLIB" {
|
||||
dlen := binary.BigEndian.Uint64(b[4:12])
|
||||
dbuf := make([]byte, dlen)
|
||||
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, dbuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = dbuf
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// There are many other DWARF sections, but these
|
||||
// are the ones the debug/dwarf package uses.
|
||||
// Don't bother loading others.
|
||||
var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
|
||||
for _, s := range f.Sections {
|
||||
suffix := dwarfSuffix(s)
|
||||
if suffix == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := dat[suffix]; !ok {
|
||||
continue
|
||||
}
|
||||
b, err := sectionData(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dat[suffix] = b
|
||||
}
|
||||
|
||||
d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Look for DWARF4 .debug_types sections.
|
||||
for i, s := range f.Sections {
|
||||
suffix := dwarfSuffix(s)
|
||||
if suffix != "types" {
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := sectionData(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// ImportedSymbols returns the names of all symbols
|
||||
// referred to by the binary f that are expected to be
|
||||
// satisfied by other libraries at dynamic load time.
|
||||
func (f *File) ImportedSymbols() ([]string, error) {
|
||||
if f.Dysymtab == nil || f.Symtab == nil {
|
||||
return nil, &FormatError{0, "missing symbol table", nil}
|
||||
}
|
||||
|
||||
st := f.Symtab
|
||||
dt := f.Dysymtab
|
||||
var all []string
|
||||
for _, s := range st.Syms[dt.Iundefsym : dt.Iundefsym+dt.Nundefsym] {
|
||||
all = append(all, s.Name)
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// ImportedLibraries returns the paths of all libraries
|
||||
// referred to by the binary f that are expected to be
|
||||
// linked with the binary at dynamic link time.
|
||||
func (f *File) ImportedLibraries() ([]string, error) {
|
||||
var all []string
|
||||
for _, l := range f.Loads {
|
||||
if lib, ok := l.(*Dylib); ok {
|
||||
all = append(all, lib.Name)
|
||||
}
|
||||
}
|
||||
return all, nil
|
||||
}
|
||||
-414
@@ -1,414 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Mach-O header data structures
|
||||
// http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
|
||||
|
||||
package macho
|
||||
|
||||
import "strconv"
|
||||
|
||||
// A FileHeader represents a Mach-O file header.
|
||||
type FileHeader struct {
|
||||
Magic uint32
|
||||
Cpu Cpu
|
||||
SubCpu uint32
|
||||
Type Type
|
||||
Ncmd uint32
|
||||
Cmdsz uint32
|
||||
Flags uint32
|
||||
}
|
||||
|
||||
const (
|
||||
fileHeaderSize32 = 7 * 4
|
||||
fileHeaderSize64 = 8 * 4
|
||||
)
|
||||
|
||||
const (
|
||||
Magic32 uint32 = 0xfeedface
|
||||
Magic64 uint32 = 0xfeedfacf
|
||||
MagicFat uint32 = 0xcafebabe
|
||||
)
|
||||
|
||||
// A Type is the Mach-O file type, e.g. an object file, executable, or dynamic library.
|
||||
type Type uint32
|
||||
|
||||
const (
|
||||
TypeObj Type = 1
|
||||
TypeExec Type = 2
|
||||
TypeDylib Type = 6
|
||||
TypeBundle Type = 8
|
||||
)
|
||||
|
||||
var typeStrings = []intName{
|
||||
{uint32(TypeObj), "Obj"},
|
||||
{uint32(TypeExec), "Exec"},
|
||||
{uint32(TypeDylib), "Dylib"},
|
||||
{uint32(TypeBundle), "Bundle"},
|
||||
}
|
||||
|
||||
func (t Type) String() string { return stringName(uint32(t), typeStrings, false) }
|
||||
func (t Type) GoString() string { return stringName(uint32(t), typeStrings, true) }
|
||||
|
||||
// A Cpu is a Mach-O cpu type.
|
||||
type Cpu uint32
|
||||
|
||||
const cpuArch64 = 0x01000000
|
||||
|
||||
const (
|
||||
Cpu386 Cpu = 7
|
||||
CpuAmd64 Cpu = Cpu386 | cpuArch64
|
||||
CpuArm Cpu = 12
|
||||
CpuArm64 Cpu = CpuArm | cpuArch64
|
||||
CpuPpc Cpu = 18
|
||||
CpuPpc64 Cpu = CpuPpc | cpuArch64
|
||||
)
|
||||
|
||||
var cpuStrings = []intName{
|
||||
{uint32(Cpu386), "Cpu386"},
|
||||
{uint32(CpuAmd64), "CpuAmd64"},
|
||||
{uint32(CpuArm), "CpuArm"},
|
||||
{uint32(CpuArm64), "CpuArm64"},
|
||||
{uint32(CpuPpc), "CpuPpc"},
|
||||
{uint32(CpuPpc64), "CpuPpc64"},
|
||||
}
|
||||
|
||||
var FinalSegEnd uint64
|
||||
|
||||
func (i Cpu) String() string { return stringName(uint32(i), cpuStrings, false) }
|
||||
func (i Cpu) GoString() string { return stringName(uint32(i), cpuStrings, true) }
|
||||
|
||||
// A LoadCmd is a Mach-O load command.
|
||||
type LoadCmd uint32
|
||||
|
||||
const (
|
||||
LoadCmdSegment LoadCmd = 0x1
|
||||
LoadCmdSymtab LoadCmd = 0x2
|
||||
LoadCmdThread LoadCmd = 0x4
|
||||
LoadCmdUnixThread LoadCmd = 0x5 // thread+stack
|
||||
LoadCmdDysymtab LoadCmd = 0xb
|
||||
LoadCmdDylib LoadCmd = 0xc // load dylib command
|
||||
LoadCmdDylinker LoadCmd = 0xf // id dylinker command (not load dylinker command)
|
||||
LoadCmdSegment64 LoadCmd = 0x19
|
||||
LoadCmdSignature LoadCmd = 0x1d
|
||||
LoadCmdFuncStarts LoadCmd = 0x26 // Function Starts
|
||||
LoadCmdDataInCode LoadCmd = 0x29 // Data In Code
|
||||
|
||||
LoadReqDyld LoadCmd = 0x80000000
|
||||
LoadCmdMain LoadCmd = (0x28 | LoadReqDyld) // replacement for LC_UNIXTHREAD
|
||||
LoadCmdRpath LoadCmd = 0x8000001c
|
||||
LoadCmdDylinkInfo LoadCmd = 0x80000022 // Dynamic Linker Info Only
|
||||
)
|
||||
|
||||
var cmdStrings = []intName{
|
||||
{uint32(LoadCmdSegment), "LoadCmdSegment"},
|
||||
{uint32(LoadCmdThread), "LoadCmdThread"},
|
||||
{uint32(LoadCmdUnixThread), "LoadCmdUnixThread"},
|
||||
{uint32(LoadCmdDylib), "LoadCmdDylib"},
|
||||
{uint32(LoadCmdSegment64), "LoadCmdSegment64"},
|
||||
{uint32(LoadCmdRpath), "LoadCmdRpath"},
|
||||
{uint32(LoadCmdSignature), "LoadCmdSignature"},
|
||||
{uint32(LoadCmdFuncStarts), "LoadCmdFuncStarts"},
|
||||
{uint32(LoadCmdDataInCode), "LoadCmdDataInCode"},
|
||||
{uint32(LoadCmdDylinkInfo), "LoadCmdDylinkInfo"},
|
||||
}
|
||||
|
||||
func (i LoadCmd) String() string { return stringName(uint32(i), cmdStrings, false) }
|
||||
func (i LoadCmd) GoString() string { return stringName(uint32(i), cmdStrings, true) }
|
||||
|
||||
// Prog.Flag
|
||||
type ProgFlag uint32
|
||||
|
||||
const (
|
||||
PF_NONE ProgFlag = 0x0 /* None */
|
||||
PF_R ProgFlag = 0x1 /* Readable. */
|
||||
PF_W ProgFlag = 0x2 /* Writable. */
|
||||
PF_X ProgFlag = 0x4 /* Executable. */
|
||||
)
|
||||
|
||||
type (
|
||||
// A Segment32 is a 32-bit Mach-O segment load command.
|
||||
Segment32 struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Name [16]byte
|
||||
Addr uint32
|
||||
Memsz uint32
|
||||
Offset uint32
|
||||
Filesz uint32
|
||||
Maxprot uint32 // ProgFlag - Maximum Segment Virtual Memory Protections
|
||||
Prot uint32 // ProgFlag - Initial Segment Virtual Memory Protections
|
||||
Nsect uint32
|
||||
Flag uint32
|
||||
}
|
||||
|
||||
// A Segment64 is a 64-bit Mach-O segment load command.
|
||||
Segment64 struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Name [16]byte
|
||||
Addr uint64
|
||||
Memsz uint64
|
||||
Offset uint64
|
||||
Filesz uint64
|
||||
Maxprot uint32
|
||||
Prot uint32
|
||||
Nsect uint32
|
||||
Flag uint32
|
||||
}
|
||||
|
||||
// A SigBlockCmd is a Mach-O symbol table command.
|
||||
SigBlockCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Sigoff uint32
|
||||
Sigsize uint32
|
||||
}
|
||||
|
||||
// A SymtabCmd is a Mach-O symbol table command.
|
||||
SymtabCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Symoff uint32
|
||||
Nsyms uint32
|
||||
Stroff uint32
|
||||
Strsize uint32
|
||||
}
|
||||
|
||||
// A DysymtabCmd is a Mach-O dynamic symbol table command.
|
||||
DysymtabCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Ilocalsym uint32
|
||||
Nlocalsym uint32
|
||||
Iextdefsym uint32
|
||||
Nextdefsym uint32
|
||||
Iundefsym uint32
|
||||
Nundefsym uint32
|
||||
Tocoffset uint32
|
||||
Ntoc uint32
|
||||
Modtaboff uint32
|
||||
Nmodtab uint32
|
||||
Extrefsymoff uint32
|
||||
Nextrefsyms uint32
|
||||
Indirectsymoff uint32
|
||||
Nindirectsyms uint32
|
||||
Extreloff uint32
|
||||
Nextrel uint32
|
||||
Locreloff uint32
|
||||
Nlocrel uint32
|
||||
}
|
||||
|
||||
// DylinkerCmd is a load command to identify the dynamic linker
|
||||
DylinkerCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Name uint32
|
||||
}
|
||||
|
||||
// A DylibCmd is a Mach-O load dynamic library command.
|
||||
DylibCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Name uint32
|
||||
Time uint32
|
||||
CurrentVersion uint32
|
||||
CompatVersion uint32
|
||||
}
|
||||
|
||||
// A FuncStartsCmd is a Mach-O load Function Starts command
|
||||
FuncStartsCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Dataoff uint32
|
||||
Datasize uint32
|
||||
}
|
||||
|
||||
// A DataInCodeCmd is a Mach-O load for Data In Code command
|
||||
DataInCodeCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Dataoff uint32
|
||||
Datasize uint32
|
||||
}
|
||||
|
||||
// A DylinkInfoCmd is a Mach-O load for Dynamic Linker Info Only Command
|
||||
DylinkInfoCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Rebaseoff uint32
|
||||
Rebasesize uint32
|
||||
Bindinginfooff uint32
|
||||
Bindinginfosize uint32
|
||||
Weakbindingoff uint32
|
||||
Weakbindingsize uint32
|
||||
Lazybindingoff uint32
|
||||
Lazybindingsize uint32
|
||||
Exportinfooff uint32
|
||||
Exportinfosize uint32
|
||||
}
|
||||
|
||||
// A RpathCmd is a Mach-O rpath command.
|
||||
RpathCmd struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Path uint32
|
||||
}
|
||||
|
||||
// A Thread is a Mach-O thread state command.
|
||||
Thread struct {
|
||||
Cmd LoadCmd
|
||||
Len uint32
|
||||
Type uint32
|
||||
Data []uint32
|
||||
}
|
||||
|
||||
// A EntryPointCmd is a Mach-O entry point command.
|
||||
EntryPointCmd struct {
|
||||
Cmd uint32 /* LC_MAIN only used in MH_EXECUTE filetypes */
|
||||
CmdSize uint32 /* 24 */
|
||||
EntryOff uint64 /* file (__TEXT) offset of main() */
|
||||
StackSize uint64 /* if not zero, initial stack size */
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
FlagNoUndefs uint32 = 0x1
|
||||
FlagIncrLink uint32 = 0x2
|
||||
FlagDyldLink uint32 = 0x4
|
||||
FlagBindAtLoad uint32 = 0x8
|
||||
FlagPrebound uint32 = 0x10
|
||||
FlagSplitSegs uint32 = 0x20
|
||||
FlagLazyInit uint32 = 0x40
|
||||
FlagTwoLevel uint32 = 0x80
|
||||
FlagForceFlat uint32 = 0x100
|
||||
FlagNoMultiDefs uint32 = 0x200
|
||||
FlagNoFixPrebinding uint32 = 0x400
|
||||
FlagPrebindable uint32 = 0x800
|
||||
FlagAllModsBound uint32 = 0x1000
|
||||
FlagSubsectionsViaSymbols uint32 = 0x2000
|
||||
FlagCanonical uint32 = 0x4000
|
||||
FlagWeakDefines uint32 = 0x8000
|
||||
FlagBindsToWeak uint32 = 0x10000
|
||||
FlagAllowStackExecution uint32 = 0x20000
|
||||
FlagRootSafe uint32 = 0x40000
|
||||
FlagSetuidSafe uint32 = 0x80000
|
||||
FlagNoReexportedDylibs uint32 = 0x100000
|
||||
FlagPIE uint32 = 0x200000
|
||||
FlagDeadStrippableDylib uint32 = 0x400000
|
||||
FlagHasTLVDescriptors uint32 = 0x800000
|
||||
FlagNoHeapExecution uint32 = 0x1000000
|
||||
FlagAppExtensionSafe uint32 = 0x2000000
|
||||
)
|
||||
|
||||
// A Section32 is a 32-bit Mach-O section header.
|
||||
type Section32 struct {
|
||||
Name [16]byte
|
||||
Seg [16]byte
|
||||
Addr uint32
|
||||
Size uint32
|
||||
Offset uint32
|
||||
Align uint32
|
||||
Reloff uint32
|
||||
Nreloc uint32
|
||||
Flags uint32
|
||||
Reserve1 uint32
|
||||
Reserve2 uint32
|
||||
}
|
||||
|
||||
// A Section64 is a 64-bit Mach-O section header.
|
||||
type Section64 struct {
|
||||
Name [16]byte
|
||||
Seg [16]byte
|
||||
Addr uint64
|
||||
Size uint64
|
||||
Offset uint32
|
||||
Align uint32
|
||||
Reloff uint32
|
||||
Nreloc uint32
|
||||
Flags uint32
|
||||
Reserve1 uint32
|
||||
Reserve2 uint32
|
||||
Reserve3 uint32
|
||||
}
|
||||
|
||||
// An Nlist32 is a Mach-O 32-bit symbol table entry.
|
||||
type Nlist32 struct {
|
||||
Name uint32
|
||||
Type uint8
|
||||
Sect uint8
|
||||
Desc uint16
|
||||
Value uint32
|
||||
}
|
||||
|
||||
// An Nlist64 is a Mach-O 64-bit symbol table entry.
|
||||
type Nlist64 struct {
|
||||
Name uint32
|
||||
Type uint8
|
||||
Sect uint8
|
||||
Desc uint16
|
||||
Value uint64
|
||||
}
|
||||
|
||||
// Regs386 is the Mach-O 386 register structure.
|
||||
type Regs386 struct {
|
||||
AX uint32
|
||||
BX uint32
|
||||
CX uint32
|
||||
DX uint32
|
||||
DI uint32
|
||||
SI uint32
|
||||
BP uint32
|
||||
SP uint32
|
||||
SS uint32
|
||||
FLAGS uint32
|
||||
IP uint32
|
||||
CS uint32
|
||||
DS uint32
|
||||
ES uint32
|
||||
FS uint32
|
||||
GS uint32
|
||||
}
|
||||
|
||||
// RegsAMD64 is the Mach-O AMD64 register structure.
|
||||
type RegsAMD64 struct {
|
||||
AX uint64
|
||||
BX uint64
|
||||
CX uint64
|
||||
DX uint64
|
||||
DI uint64
|
||||
SI uint64
|
||||
BP uint64
|
||||
SP uint64
|
||||
R8 uint64
|
||||
R9 uint64
|
||||
R10 uint64
|
||||
R11 uint64
|
||||
R12 uint64
|
||||
R13 uint64
|
||||
R14 uint64
|
||||
R15 uint64
|
||||
IP uint64
|
||||
FLAGS uint64
|
||||
CS uint64
|
||||
FS uint64
|
||||
GS uint64
|
||||
}
|
||||
|
||||
type intName struct {
|
||||
i uint32
|
||||
s string
|
||||
}
|
||||
|
||||
func stringName(i uint32, names []intName, goSyntax bool) string {
|
||||
for _, n := range names {
|
||||
if n.i == i {
|
||||
if goSyntax {
|
||||
return "macho." + n.s
|
||||
}
|
||||
return n.s
|
||||
}
|
||||
}
|
||||
return strconv.FormatUint(uint64(i), 10)
|
||||
}
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package macho
|
||||
|
||||
//go:generate stringer -type=RelocTypeGeneric,RelocTypeX86_64,RelocTypeARM,RelocTypeARM64 -output reloctype_string.go
|
||||
|
||||
type RelocTypeGeneric int
|
||||
|
||||
const (
|
||||
GENERIC_RELOC_VANILLA RelocTypeGeneric = 0
|
||||
GENERIC_RELOC_PAIR RelocTypeGeneric = 1
|
||||
GENERIC_RELOC_SECTDIFF RelocTypeGeneric = 2
|
||||
GENERIC_RELOC_PB_LA_PTR RelocTypeGeneric = 3
|
||||
GENERIC_RELOC_LOCAL_SECTDIFF RelocTypeGeneric = 4
|
||||
GENERIC_RELOC_TLV RelocTypeGeneric = 5
|
||||
)
|
||||
|
||||
func (r RelocTypeGeneric) GoString() string { return "macho." + r.String() }
|
||||
|
||||
type RelocTypeX86_64 int
|
||||
|
||||
const (
|
||||
X86_64_RELOC_UNSIGNED RelocTypeX86_64 = 0
|
||||
X86_64_RELOC_SIGNED RelocTypeX86_64 = 1
|
||||
X86_64_RELOC_BRANCH RelocTypeX86_64 = 2
|
||||
X86_64_RELOC_GOT_LOAD RelocTypeX86_64 = 3
|
||||
X86_64_RELOC_GOT RelocTypeX86_64 = 4
|
||||
X86_64_RELOC_SUBTRACTOR RelocTypeX86_64 = 5
|
||||
X86_64_RELOC_SIGNED_1 RelocTypeX86_64 = 6
|
||||
X86_64_RELOC_SIGNED_2 RelocTypeX86_64 = 7
|
||||
X86_64_RELOC_SIGNED_4 RelocTypeX86_64 = 8
|
||||
X86_64_RELOC_TLV RelocTypeX86_64 = 9
|
||||
)
|
||||
|
||||
func (r RelocTypeX86_64) GoString() string { return "macho." + r.String() }
|
||||
|
||||
type RelocTypeARM int
|
||||
|
||||
const (
|
||||
ARM_RELOC_VANILLA RelocTypeARM = 0
|
||||
ARM_RELOC_PAIR RelocTypeARM = 1
|
||||
ARM_RELOC_SECTDIFF RelocTypeARM = 2
|
||||
ARM_RELOC_LOCAL_SECTDIFF RelocTypeARM = 3
|
||||
ARM_RELOC_PB_LA_PTR RelocTypeARM = 4
|
||||
ARM_RELOC_BR24 RelocTypeARM = 5
|
||||
ARM_THUMB_RELOC_BR22 RelocTypeARM = 6
|
||||
ARM_THUMB_32BIT_BRANCH RelocTypeARM = 7
|
||||
ARM_RELOC_HALF RelocTypeARM = 8
|
||||
ARM_RELOC_HALF_SECTDIFF RelocTypeARM = 9
|
||||
)
|
||||
|
||||
func (r RelocTypeARM) GoString() string { return "macho." + r.String() }
|
||||
|
||||
type RelocTypeARM64 int
|
||||
|
||||
const (
|
||||
ARM64_RELOC_UNSIGNED RelocTypeARM64 = 0
|
||||
ARM64_RELOC_SUBTRACTOR RelocTypeARM64 = 1
|
||||
ARM64_RELOC_BRANCH26 RelocTypeARM64 = 2
|
||||
ARM64_RELOC_PAGE21 RelocTypeARM64 = 3
|
||||
ARM64_RELOC_PAGEOFF12 RelocTypeARM64 = 4
|
||||
ARM64_RELOC_GOT_LOAD_PAGE21 RelocTypeARM64 = 5
|
||||
ARM64_RELOC_GOT_LOAD_PAGEOFF12 RelocTypeARM64 = 6
|
||||
ARM64_RELOC_POINTER_TO_GOT RelocTypeARM64 = 7
|
||||
ARM64_RELOC_TLVP_LOAD_PAGE21 RelocTypeARM64 = 8
|
||||
ARM64_RELOC_TLVP_LOAD_PAGEOFF12 RelocTypeARM64 = 9
|
||||
ARM64_RELOC_ADDEND RelocTypeARM64 = 10
|
||||
)
|
||||
|
||||
func (r RelocTypeARM64) GoString() string { return "macho." + r.String() }
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
// Code generated by "stringer -type=RelocTypeGeneric,RelocTypeX86_64,RelocTypeARM,RelocTypeARM64 -output reloctype_string.go"; DO NOT EDIT.
|
||||
|
||||
package macho
|
||||
|
||||
import "strconv"
|
||||
|
||||
const _RelocTypeGeneric_name = "GENERIC_RELOC_VANILLAGENERIC_RELOC_PAIRGENERIC_RELOC_SECTDIFFGENERIC_RELOC_PB_LA_PTRGENERIC_RELOC_LOCAL_SECTDIFFGENERIC_RELOC_TLV"
|
||||
|
||||
var _RelocTypeGeneric_index = [...]uint8{0, 21, 39, 61, 84, 112, 129}
|
||||
|
||||
func (i RelocTypeGeneric) String() string {
|
||||
if i < 0 || i >= RelocTypeGeneric(len(_RelocTypeGeneric_index)-1) {
|
||||
return "RelocTypeGeneric(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _RelocTypeGeneric_name[_RelocTypeGeneric_index[i]:_RelocTypeGeneric_index[i+1]]
|
||||
}
|
||||
|
||||
const _RelocTypeX86_64_name = "X86_64_RELOC_UNSIGNEDX86_64_RELOC_SIGNEDX86_64_RELOC_BRANCHX86_64_RELOC_GOT_LOADX86_64_RELOC_GOTX86_64_RELOC_SUBTRACTORX86_64_RELOC_SIGNED_1X86_64_RELOC_SIGNED_2X86_64_RELOC_SIGNED_4X86_64_RELOC_TLV"
|
||||
|
||||
var _RelocTypeX86_64_index = [...]uint8{0, 21, 40, 59, 80, 96, 119, 140, 161, 182, 198}
|
||||
|
||||
func (i RelocTypeX86_64) String() string {
|
||||
if i < 0 || i >= RelocTypeX86_64(len(_RelocTypeX86_64_index)-1) {
|
||||
return "RelocTypeX86_64(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _RelocTypeX86_64_name[_RelocTypeX86_64_index[i]:_RelocTypeX86_64_index[i+1]]
|
||||
}
|
||||
|
||||
const _RelocTypeARM_name = "ARM_RELOC_VANILLAARM_RELOC_PAIRARM_RELOC_SECTDIFFARM_RELOC_LOCAL_SECTDIFFARM_RELOC_PB_LA_PTRARM_RELOC_BR24ARM_THUMB_RELOC_BR22ARM_THUMB_32BIT_BRANCHARM_RELOC_HALFARM_RELOC_HALF_SECTDIFF"
|
||||
|
||||
var _RelocTypeARM_index = [...]uint8{0, 17, 31, 49, 73, 92, 106, 126, 148, 162, 185}
|
||||
|
||||
func (i RelocTypeARM) String() string {
|
||||
if i < 0 || i >= RelocTypeARM(len(_RelocTypeARM_index)-1) {
|
||||
return "RelocTypeARM(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _RelocTypeARM_name[_RelocTypeARM_index[i]:_RelocTypeARM_index[i+1]]
|
||||
}
|
||||
|
||||
const _RelocTypeARM64_name = "ARM64_RELOC_UNSIGNEDARM64_RELOC_SUBTRACTORARM64_RELOC_BRANCH26ARM64_RELOC_PAGE21ARM64_RELOC_PAGEOFF12ARM64_RELOC_GOT_LOAD_PAGE21ARM64_RELOC_GOT_LOAD_PAGEOFF12ARM64_RELOC_POINTER_TO_GOTARM64_RELOC_TLVP_LOAD_PAGE21ARM64_RELOC_TLVP_LOAD_PAGEOFF12ARM64_RELOC_ADDEND"
|
||||
|
||||
var _RelocTypeARM64_index = [...]uint16{0, 20, 42, 62, 80, 101, 128, 158, 184, 212, 243, 261}
|
||||
|
||||
func (i RelocTypeARM64) String() string {
|
||||
if i < 0 || i >= RelocTypeARM64(len(_RelocTypeARM64_index)-1) {
|
||||
return "RelocTypeARM64(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _RelocTypeARM64_name[_RelocTypeARM64_index[i]:_RelocTypeARM64_index[i+1]]
|
||||
}
|
||||
-343
@@ -1,343 +0,0 @@
|
||||
package macho
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Bytes - Returns the bytes of an assembled *macho.File
|
||||
func (machoFile *File) Bytes() ([]byte, error) {
|
||||
var bytesWritten uint64
|
||||
w := bytes.NewBuffer(nil)
|
||||
|
||||
// Write entire file header.
|
||||
buf := &bytes.Buffer{}
|
||||
err := binary.Write(buf, machoFile.ByteOrder, machoFile.FileHeader)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
headerLength := len(buf.Bytes())
|
||||
binary.Write(w, machoFile.ByteOrder, machoFile.FileHeader)
|
||||
bytesWritten += uint64(headerLength)
|
||||
//log.Printf("%x: Wrote file header of size: %v", bytesWritten, bytesWritten)
|
||||
|
||||
// Reserved 4 bytes at end of header
|
||||
w.Write([]byte{0, 0, 0, 0})
|
||||
bytesWritten += 4
|
||||
|
||||
// Write Load Commands Loop
|
||||
for _, singleLoad := range machoFile.Loads {
|
||||
buf2 := &bytes.Buffer{}
|
||||
err = binary.Write(buf2, machoFile.ByteOrder, singleLoad.Raw())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
LoadCmdLen := len(buf2.Bytes())
|
||||
binary.Write(w, machoFile.ByteOrder, singleLoad.Raw())
|
||||
bytesWritten += uint64(LoadCmdLen)
|
||||
//log.Printf("%x: Wrote Load Command, total size of: %v", bytesWritten, LoadCmdLen)
|
||||
}
|
||||
|
||||
// Shellcode gets caved in between the final load command and the first section
|
||||
if len(machoFile.Insertion) > 0 {
|
||||
binary.Write(w, machoFile.ByteOrder, machoFile.Insertion)
|
||||
bytesWritten += uint64(len(machoFile.Insertion))
|
||||
}
|
||||
|
||||
// Sort Sections
|
||||
sortedSections := machoFile.Sections[:]
|
||||
sort.Slice(sortedSections, func(a, b int) bool { return machoFile.Sections[a].Offset < machoFile.Sections[b].Offset })
|
||||
|
||||
/*
|
||||
var caveOffset, caveSize uint64
|
||||
for _, s := range sortedSections {
|
||||
if s.SectionHeader.Seg == "__TEXT" && s.Name == "__text" {
|
||||
caveOffset = bytesWritten
|
||||
caveSize = uint64(s.Offset) - caveOffset
|
||||
log.Printf("Code Cave Size: %d - %d = %d\n", s.Offset, caveOffset, caveSize)
|
||||
log.Println("Shellcode Size: ", len(machoFile.Insertion))
|
||||
break
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// Write Sections
|
||||
for _, s := range sortedSections {
|
||||
|
||||
if bytesWritten > uint64(s.Offset) {
|
||||
log.Printf("Overlapping Sections in Generated macho: %+v\n", s.Name)
|
||||
continue
|
||||
}
|
||||
if bytesWritten < uint64(s.Offset) {
|
||||
pad := make([]byte, uint64(s.Offset)-bytesWritten)
|
||||
w.Write(pad)
|
||||
bytesWritten += uint64(len(pad))
|
||||
//log.Printf("%x: wrote %d padding bytes\n", bytesWritten, len(pad))
|
||||
}
|
||||
section, err := ioutil.ReadAll(s.Open())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
binary.Write(w, machoFile.ByteOrder, section)
|
||||
bytesWritten += uint64(len(section))
|
||||
//log.Printf("%x: wrote %d bytes section/segment named: %s %s\n", bytesWritten, uint64(len(section)), s.Name, s.Seg)
|
||||
}
|
||||
// Write Dynamic Loader Info if it exists
|
||||
if machoFile.DylinkInfo != nil {
|
||||
// Write Rebase if it exists
|
||||
if len(machoFile.DylinkInfo.RebaseDat) > 0 {
|
||||
//log.Printf("Rebase Offset: %d", machoFile.DylinkInfo.RebaseOffset)
|
||||
if int64(machoFile.DylinkInfo.RebaseOffset)-int64(bytesWritten) > 0 {
|
||||
padA := make([]byte, machoFile.DylinkInfo.RebaseOffset-bytesWritten)
|
||||
w.Write(padA)
|
||||
bytesWritten += uint64(len(padA))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padA))
|
||||
}
|
||||
//log.Printf("Rebase: %+v \n", machoFile.DylinkInfo.RebaseDat)
|
||||
w.Write(machoFile.DylinkInfo.RebaseDat)
|
||||
bytesWritten += uint64(machoFile.DylinkInfo.RebaseLen)
|
||||
//log.Printf("%x: Wrote raw Rebase, length of: %d", bytesWritten, machoFile.DylinkInfo.RebaseLen)
|
||||
}
|
||||
//Binding
|
||||
if len(machoFile.DylinkInfo.BindingInfoDat) > 0 {
|
||||
//log.Printf("Binding Offset: %d", machoFile.DylinkInfo.BindingInfoOffset)
|
||||
if int64(machoFile.DylinkInfo.BindingInfoOffset)-int64(bytesWritten) > 0 {
|
||||
padB := make([]byte, machoFile.DylinkInfo.BindingInfoOffset-bytesWritten)
|
||||
w.Write(padB)
|
||||
bytesWritten += uint64(len(padB))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padB))
|
||||
}
|
||||
//log.Printf("Binding Info: %+v \n", machoFile.DylinkInfo.BindingInfoDat)
|
||||
w.Write(machoFile.DylinkInfo.BindingInfoDat)
|
||||
bytesWritten += uint64(machoFile.DylinkInfo.BindingInfoLen)
|
||||
//log.Printf("%x: Wrote raw Binding Info, length of: %d", bytesWritten, machoFile.DylinkInfo.BindingInfoLen)
|
||||
}
|
||||
//Lazy
|
||||
if len(machoFile.DylinkInfo.LazyBindingDat) > 0 {
|
||||
//log.Printf("Lazy Offset: %d", machoFile.DylinkInfo.LazyBindingOffset)
|
||||
if int64(machoFile.DylinkInfo.LazyBindingOffset)-int64(bytesWritten) > 0 {
|
||||
padD := make([]byte, machoFile.DylinkInfo.LazyBindingOffset-bytesWritten)
|
||||
w.Write(padD)
|
||||
bytesWritten += uint64(len(padD))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padD))
|
||||
}
|
||||
//log.Printf("Lazy Binding Data: %+v \n", machoFile.DylinkInfo.LazyBindingDat)
|
||||
w.Write(machoFile.DylinkInfo.LazyBindingDat)
|
||||
bytesWritten += uint64(machoFile.DylinkInfo.LazyBindingLen)
|
||||
//log.Printf("%x: Wrote raw lazybinding, length of: %d", bytesWritten, machoFile.DylinkInfo.LazyBindingLen)
|
||||
}
|
||||
//Export
|
||||
if len(machoFile.DylinkInfo.ExportInfoDat) > 0 {
|
||||
//log.Printf("Export Offset: %d", machoFile.DylinkInfo.ExportInfoOffset)
|
||||
if int64(machoFile.DylinkInfo.ExportInfoOffset)-int64(bytesWritten) > 0 {
|
||||
padE := make([]byte, machoFile.DylinkInfo.ExportInfoOffset-bytesWritten)
|
||||
w.Write(padE)
|
||||
bytesWritten += uint64(len(padE))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padE))
|
||||
}
|
||||
//log.Printf("Export Info: %+v \n", machoFile.DylinkInfo.ExportInfoDat)
|
||||
w.Write(machoFile.DylinkInfo.ExportInfoDat)
|
||||
bytesWritten += uint64(machoFile.DylinkInfo.ExportInfoLen)
|
||||
//log.Printf("%x: Wrote raw Export Info, length of: %d", bytesWritten, machoFile.DylinkInfo.ExportInfoLen)
|
||||
}
|
||||
//Weak
|
||||
if len(machoFile.DylinkInfo.WeakBindingDat) > 0 {
|
||||
//log.Printf("Weak Offset: %d", machoFile.DylinkInfo.WeakBindingOffset)
|
||||
if int64(machoFile.DylinkInfo.WeakBindingOffset)-int64(bytesWritten) > 0 {
|
||||
padC := make([]byte, machoFile.DylinkInfo.WeakBindingOffset-bytesWritten)
|
||||
w.Write(padC)
|
||||
bytesWritten += uint64(len(padC))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padC))
|
||||
}
|
||||
//log.Printf("Weak Binding: %+v \n", machoFile.DylinkInfo.WeakBindingDat)
|
||||
w.Write(machoFile.DylinkInfo.WeakBindingDat)
|
||||
bytesWritten += uint64(machoFile.DylinkInfo.WeakBindingLen)
|
||||
//log.Printf("%x: Wrote raw Weak Binding, length of: %d", bytesWritten, machoFile.DylinkInfo.WeakBindingLen)
|
||||
}
|
||||
}
|
||||
|
||||
// Write the Func Starts if they exist
|
||||
if machoFile.FuncStarts != nil {
|
||||
//log.Printf("new pad: %d", machoFile.FuncStarts.Offset-bytesWritten)
|
||||
if int64(machoFile.FuncStarts.Offset)-int64(bytesWritten) > 0 {
|
||||
padY := make([]byte, machoFile.FuncStarts.Offset-bytesWritten)
|
||||
w.Write(padY)
|
||||
bytesWritten += uint64(len(padY))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padY))
|
||||
}
|
||||
//log.Printf("FuncStarts: %+v \n", machoFile.FuncStarts)
|
||||
w.Write(machoFile.FuncStarts.RawDat)
|
||||
bytesWritten += uint64(machoFile.FuncStarts.Len)
|
||||
//log.Printf("%x: Wrote raw funcstarts, length of: %d", bytesWritten, machoFile.FuncStarts.Len)
|
||||
}
|
||||
|
||||
// Write the Data in Code Entries if they exist
|
||||
if machoFile.DataInCode != nil {
|
||||
if int64(machoFile.DataInCode.Offset)-int64(bytesWritten) > 0 {
|
||||
padZ := make([]byte, machoFile.DataInCode.Offset-bytesWritten)
|
||||
w.Write(padZ)
|
||||
bytesWritten += uint64(len(padZ))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padZ))
|
||||
}
|
||||
//log.Printf("DataInCode: %+v \n", machoFile.DataInCode)
|
||||
w.Write(machoFile.DataInCode.RawDat)
|
||||
bytesWritten += uint64(machoFile.DataInCode.Len)
|
||||
//log.Printf("%x: Wrote raw dataincode, length of: %d", bytesWritten, machoFile.DataInCode.Len)
|
||||
}
|
||||
|
||||
// Write Symbols is next I think
|
||||
symtab := machoFile.Symtab
|
||||
//log.Printf("Bytes written: %d", bytesWritten)
|
||||
//log.Printf("Indirect symbol offset: %d", machoFile.Dysymtab.DysymtabCmd.Indirectsymoff)
|
||||
//log.Printf("Locrel offset: %d", machoFile.Dysymtab.Locreloff)
|
||||
//log.Printf("Symtab offset: %d", symtab.Symoff)
|
||||
//log.Printf("String table offset: %d", symtab.Stroff)
|
||||
if int64(symtab.Symoff)-int64(bytesWritten) > 0 {
|
||||
pad := make([]byte, uint64(symtab.Symoff)-bytesWritten)
|
||||
w.Write(pad)
|
||||
bytesWritten += (uint64(symtab.Symoff) - bytesWritten)
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, uint64(symtab.Symoff)-bytesWritten)
|
||||
}
|
||||
w.Write(symtab.RawSymtab)
|
||||
bytesWritten += uint64(len(symtab.RawSymtab))
|
||||
//log.Printf("%x: Wrote raw symtab, length of: %d", bytesWritten, len(symtab.RawSymtab))
|
||||
|
||||
// Write DySymTab next!
|
||||
dysymtab := machoFile.Dysymtab
|
||||
if int64(dysymtab.Indirectsymoff)-int64(bytesWritten) > 0 {
|
||||
pad2 := make([]byte, uint64(dysymtab.Indirectsymoff)-bytesWritten)
|
||||
w.Write(pad2)
|
||||
bytesWritten += uint64(len(pad2))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(pad2))
|
||||
}
|
||||
w.Write(dysymtab.RawDysymtab)
|
||||
bytesWritten += uint64(len(dysymtab.RawDysymtab))
|
||||
//log.Printf("%x: Wrote raw indirect symbols, length of: %d", bytesWritten, len(dysymtab.RawDysymtab))
|
||||
|
||||
// Write StringTab!
|
||||
if int64(symtab.Stroff)-int64(bytesWritten) > 0 {
|
||||
pad3 := make([]byte, uint64(symtab.Stroff)-bytesWritten)
|
||||
w.Write(pad3)
|
||||
bytesWritten += uint64(len(pad3))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(pad3))
|
||||
}
|
||||
w.Write(symtab.RawStringtab)
|
||||
bytesWritten += uint64(len(symtab.RawStringtab))
|
||||
//log.Printf("%x: Wrote raw stringtab, length of: %d", bytesWritten, len(symtab.RawStringtab))
|
||||
|
||||
// Write The Signature Block, if it exists
|
||||
//log.Printf("SigBlock Dat: %v", machoFile.SigBlock)
|
||||
if machoFile.SigBlock != nil {
|
||||
if int64(machoFile.SigBlock.Offset)-int64(bytesWritten) > 0 {
|
||||
padX := make([]byte, int64(machoFile.SigBlock.Offset)-int64(bytesWritten))
|
||||
w.Write(padX)
|
||||
bytesWritten += uint64(len(padX))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(padX))
|
||||
}
|
||||
w.Write(machoFile.SigBlock.RawDat)
|
||||
bytesWritten += uint64(machoFile.SigBlock.Len)
|
||||
//log.Printf("%x: Wrote raw sigblock, length of: %d", bytesWritten, machoFile.SigBlock.Len)
|
||||
}
|
||||
|
||||
// Write 0s to the end of the final segment
|
||||
if int64(FinalSegEnd)-int64(bytesWritten) > 0 {
|
||||
pad4 := make([]byte, uint64(FinalSegEnd)-bytesWritten)
|
||||
w.Write(pad4)
|
||||
bytesWritten += uint64(len(pad4))
|
||||
//log.Printf("%x: wrote pad of: %d", bytesWritten, len(pad4))
|
||||
}
|
||||
|
||||
//log.Println("All done!")
|
||||
machoBytes := w.Bytes()
|
||||
return machoBytes, nil
|
||||
}
|
||||
|
||||
// WriteFile - Creates a new file and writes it using the Bytes func above
|
||||
func (machoFile *File) WriteFile(destFile string) error {
|
||||
f, err := os.Create(destFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
machoData, err := machoFile.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = f.Write(machoData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteFatFile - Creates a new Fat file and multiple machos into it
|
||||
func (FatyFile *FatFile) WriteFatFile(destFile string) error {
|
||||
f, err := os.Create(destFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
w := bufio.NewWriter(f)
|
||||
bytesWritten := uint64(0)
|
||||
var FatyMachos []File
|
||||
var FatyMachosOffsets []uint32
|
||||
|
||||
// Fat Header First
|
||||
// Magic Bytes
|
||||
binary.Write(w, binary.BigEndian, FatyFile.Magic)
|
||||
bytesWritten += 4
|
||||
// Number of Fat Arches [4 bytes]
|
||||
FatArches := uint32(len(FatyFile.Arches))
|
||||
binary.Write(w, binary.BigEndian, FatArches)
|
||||
bytesWritten += 4
|
||||
w.Flush()
|
||||
// Arch Size
|
||||
for _, arch := range FatyFile.Arches {
|
||||
log.Printf("Arch details: %v\n", arch)
|
||||
FatyMachos = append(FatyMachos, *(arch.File))
|
||||
//Cpu Type
|
||||
binary.Write(w, binary.BigEndian, uint32(arch.Cpu))
|
||||
bytesWritten += 4
|
||||
//Sub CPU type
|
||||
binary.Write(w, binary.BigEndian, uint32(arch.SubCpu))
|
||||
bytesWritten += 4
|
||||
//FileOffset
|
||||
FatyMachosOffsets = append(FatyMachosOffsets, arch.Offset)
|
||||
binary.Write(w, binary.BigEndian, uint32(arch.Offset))
|
||||
bytesWritten += 4
|
||||
//Size
|
||||
binary.Write(w, binary.BigEndian, uint32(arch.Size))
|
||||
bytesWritten += 4
|
||||
//Align
|
||||
binary.Write(w, binary.BigEndian, uint32(arch.Align))
|
||||
bytesWritten += 4
|
||||
w.Flush()
|
||||
}
|
||||
// End of Fat Headers
|
||||
|
||||
// Write each Macho File at its Offset
|
||||
for index, mach := range FatyMachos {
|
||||
// Pad to the offset
|
||||
if bytesWritten < uint64(FatyMachosOffsets[index]) {
|
||||
pad := make([]byte, uint64(FatyMachosOffsets[index])-bytesWritten)
|
||||
w.Write(pad)
|
||||
bytesWritten += uint64(len(pad))
|
||||
}
|
||||
// Write the Mach-o file
|
||||
machOut, err := mach.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
binary.Write(w, binary.BigEndian, machOut)
|
||||
bytesWritten += uint64(len(machOut))
|
||||
w.Flush()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
-50
@@ -1,50 +0,0 @@
|
||||
package pe
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// CERTIFICATE_TABLE is the index of the Certificate Table info in the Data Directory structure
|
||||
// in the PE header
|
||||
const CERTIFICATE_TABLE = 4
|
||||
|
||||
func readCertTable(f *File, r io.ReadSeeker) ([]byte, error) {
|
||||
if f.OptionalHeader == nil { // Optional header is optional, might not exist
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var certTableOffset, certTableSize uint32
|
||||
|
||||
switch f.FileHeader.Machine {
|
||||
case IMAGE_FILE_MACHINE_I386:
|
||||
certTableOffset = f.OptionalHeader.(*OptionalHeader32).DataDirectory[CERTIFICATE_TABLE].VirtualAddress
|
||||
certTableSize = f.OptionalHeader.(*OptionalHeader32).DataDirectory[CERTIFICATE_TABLE].Size
|
||||
case IMAGE_FILE_MACHINE_AMD64:
|
||||
certTableOffset = f.OptionalHeader.(*OptionalHeader64).DataDirectory[CERTIFICATE_TABLE].VirtualAddress
|
||||
certTableSize = f.OptionalHeader.(*OptionalHeader64).DataDirectory[CERTIFICATE_TABLE].Size
|
||||
default:
|
||||
return nil, errors.New("architecture not supported")
|
||||
}
|
||||
|
||||
// check if certificate table exists
|
||||
if certTableOffset == 0 || certTableSize == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
_, err = r.Seek(int64(certTableOffset), seekStart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to seek to certificate table: %v", err)
|
||||
}
|
||||
|
||||
// grab the cert
|
||||
cert := make([]byte, certTableSize)
|
||||
_, err = io.ReadFull(r, cert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to read certificate table: %v", err)
|
||||
}
|
||||
|
||||
return cert, nil
|
||||
}
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
package pe
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
// ExportDirectory - data directory definition for exported functions
|
||||
type ExportDirectory struct {
|
||||
ExportFlags uint32 // reserved, must be zero
|
||||
TimeDateStamp uint32
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
NameRVA uint32 // pointer to the name of the DLL
|
||||
OrdinalBase uint32
|
||||
NumberOfFunctions uint32
|
||||
NumberOfNames uint32 // also Ordinal Table Len
|
||||
AddressTableAddr uint32 // RVA of EAT, relative to image base
|
||||
NameTableAddr uint32 // RVA of export name pointer table, relative to image base
|
||||
OrdinalTableAddr uint32 // address of the ordinal table, relative to iamge base
|
||||
|
||||
DllName string
|
||||
}
|
||||
|
||||
// Export - describes a single export entry
|
||||
type Export struct {
|
||||
Ordinal uint32
|
||||
Name string
|
||||
VirtualAddress uint32
|
||||
}
|
||||
|
||||
// Exports - gets exports
|
||||
func (f *File) Exports() ([]Export, error) {
|
||||
pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
|
||||
|
||||
// grab the number of data directory entries
|
||||
var ddLength uint32
|
||||
if pe64 {
|
||||
ddLength = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
|
||||
} else {
|
||||
ddLength = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
|
||||
}
|
||||
|
||||
// check that the length of data directory entries is large
|
||||
// enough to include the exports directory.
|
||||
if ddLength < IMAGE_DIRECTORY_ENTRY_EXPORT+1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// grab the export data directory entry
|
||||
var edd DataDirectory
|
||||
if pe64 {
|
||||
edd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
|
||||
} else {
|
||||
edd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
|
||||
}
|
||||
|
||||
// figure out which section contains the export directory table
|
||||
var ds *Section
|
||||
ds = nil
|
||||
for _, s := range f.Sections {
|
||||
if s.VirtualAddress <= edd.VirtualAddress && edd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
|
||||
ds = s
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find a section, so no exports were found
|
||||
if ds == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
d, err := ds.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exportDirOffset := edd.VirtualAddress - ds.VirtualAddress
|
||||
|
||||
// seek to the virtual address specified in the export data directory
|
||||
dxd := d[exportDirOffset:]
|
||||
|
||||
// deserialize export directory
|
||||
var dt ExportDirectory
|
||||
dt.ExportFlags = binary.LittleEndian.Uint32(dxd[0:4])
|
||||
dt.TimeDateStamp = binary.LittleEndian.Uint32(dxd[4:8])
|
||||
dt.MajorVersion = binary.LittleEndian.Uint16(dxd[8:10])
|
||||
dt.MinorVersion = binary.LittleEndian.Uint16(dxd[10:12])
|
||||
dt.NameRVA = binary.LittleEndian.Uint32(dxd[12:16])
|
||||
dt.OrdinalBase = binary.LittleEndian.Uint32(dxd[16:20])
|
||||
dt.NumberOfFunctions = binary.LittleEndian.Uint32(dxd[20:24])
|
||||
dt.NumberOfNames = binary.LittleEndian.Uint32(dxd[24:28])
|
||||
dt.AddressTableAddr = binary.LittleEndian.Uint32(dxd[28:32])
|
||||
dt.NameTableAddr = binary.LittleEndian.Uint32(dxd[32:36])
|
||||
dt.OrdinalTableAddr = binary.LittleEndian.Uint32(dxd[36:40])
|
||||
|
||||
dt.DllName, _ = getString(d, int(dt.NameRVA-ds.VirtualAddress))
|
||||
|
||||
// seek to ordinal table
|
||||
dno := d[dt.OrdinalTableAddr-ds.VirtualAddress:]
|
||||
// seek to names table
|
||||
dnn := d[dt.NameTableAddr-ds.VirtualAddress:]
|
||||
|
||||
// build whole ordinal->name table
|
||||
ordinalTable := make(map[uint16]uint32)
|
||||
for n := uint32(0); n < dt.NumberOfNames; n++ {
|
||||
ord := binary.LittleEndian.Uint16(dno[n*2 : (n*2)+2])
|
||||
nameRVA := binary.LittleEndian.Uint32(dnn[n*4 : (n*4)+4])
|
||||
ordinalTable[ord] = nameRVA
|
||||
}
|
||||
dno = nil
|
||||
dnn = nil
|
||||
|
||||
// seek to ordinal table
|
||||
dna := d[dt.AddressTableAddr-ds.VirtualAddress:]
|
||||
|
||||
var exports []Export
|
||||
for i := uint32(0); i < dt.NumberOfFunctions; i++ {
|
||||
var export Export
|
||||
export.VirtualAddress =
|
||||
binary.LittleEndian.Uint32(dna[i*4 : (i*4)+4])
|
||||
export.Ordinal = dt.OrdinalBase + i
|
||||
|
||||
// check the entire ordinal table looking for this index to see if we have a name
|
||||
_, ok := ordinalTable[uint16(i)]
|
||||
if ok { // a name exists for this exported function
|
||||
nameRVA, _ := ordinalTable[uint16(i)]
|
||||
export.Name, _ = getString(d, int(nameRVA-ds.VirtualAddress))
|
||||
}
|
||||
exports = append(exports, export)
|
||||
}
|
||||
return exports, nil
|
||||
}
|
||||
-467
@@ -1,467 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package pe implements access to PE (Microsoft Windows Portable Executable) files.
|
||||
package pe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Avoid use of post-Go 1.4 io features, to make safe for toolchain bootstrap.
|
||||
const (
|
||||
seekStart = 0
|
||||
seekCurrent = 1
|
||||
)
|
||||
|
||||
// A File represents an open PE file.
|
||||
type File struct {
|
||||
DosHeader
|
||||
DosExists bool
|
||||
DosStub [64]byte // TODO(capnspacehook) make slice and correctly parse any DOS stub
|
||||
RichHeader []byte
|
||||
FileHeader
|
||||
OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
|
||||
Sections []*Section
|
||||
BaseRelocationTable *[]RelocationTableEntry
|
||||
Symbols []*Symbol // COFF symbols with auxiliary symbol records removed
|
||||
COFFSymbols []COFFSymbol // all COFF symbols (including auxiliary symbol records)
|
||||
StringTable StringTable
|
||||
CertificateTable []byte
|
||||
|
||||
OptionalHeaderOffset int64 // offset of the start of the Optional Header
|
||||
InsertionAddr uint32
|
||||
InsertionBytes []byte
|
||||
|
||||
Net Net //If a managed executable, Net provides an interface to some of the metadata
|
||||
|
||||
closer io.Closer
|
||||
}
|
||||
|
||||
// Open opens the named file using os.Open and prepares it for use as a PE binary.
|
||||
func Open(name string) (*File, error) {
|
||||
f, err := os.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ff, err := NewFile(f)
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return nil, err
|
||||
}
|
||||
ff.closer = f
|
||||
return ff, nil
|
||||
}
|
||||
|
||||
// Close closes the File.
|
||||
// If the File was created using NewFile directly instead of Open,
|
||||
// Close has no effect.
|
||||
func (f *File) Close() error {
|
||||
var err error
|
||||
if f.closer != nil {
|
||||
err = f.closer.Close()
|
||||
f.closer = nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{}))
|
||||
sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{}))
|
||||
)
|
||||
|
||||
// TODO(brainman): add Load function, as a replacement for NewFile, that does not call removeAuxSymbols (for performance)
|
||||
|
||||
// NewFile creates a new pe.File for accessing a PE binary file in an underlying reader.
|
||||
func NewFile(r io.ReaderAt) (*File, error) {
|
||||
return newFileInternal(r, false)
|
||||
}
|
||||
|
||||
// NewFileFromMemory creates a new pe.File for accessing a PE binary in-memory image in an underlying reader.
|
||||
func NewFileFromMemory(r io.ReaderAt) (*File, error) {
|
||||
return newFileInternal(r, true)
|
||||
}
|
||||
|
||||
// NewFile creates a new File for accessing a PE binary in an underlying reader.
|
||||
func newFileInternal(r io.ReaderAt, memoryMode bool) (*File, error) {
|
||||
|
||||
f := new(File)
|
||||
sr := io.NewSectionReader(r, 0, 1<<63-1)
|
||||
|
||||
binary.Read(sr, binary.LittleEndian, &f.DosHeader)
|
||||
dosHeaderSize := binary.Size(f.DosHeader)
|
||||
if dosHeaderSize > int(f.DosHeader.AddressOfNewExeHeader) {
|
||||
binary.Read(sr, binary.LittleEndian, &f.DosStub)
|
||||
f.DosExists = true
|
||||
} else {
|
||||
f.DosExists = false
|
||||
}
|
||||
|
||||
possibleRichHeaderStart := dosHeaderSize
|
||||
if f.DosExists {
|
||||
possibleRichHeaderStart += binary.Size(f.DosStub)
|
||||
}
|
||||
possibleRichHeaderEnd := int(f.DosHeader.AddressOfNewExeHeader)
|
||||
if possibleRichHeaderEnd > possibleRichHeaderStart {
|
||||
richHeader := make([]byte, possibleRichHeaderEnd-possibleRichHeaderStart)
|
||||
binary.Read(sr, binary.LittleEndian, richHeader)
|
||||
|
||||
if richIndex := bytes.Index(richHeader, []byte("Rich")); richIndex != -1 {
|
||||
f.RichHeader = richHeader[:richIndex+8]
|
||||
}
|
||||
}
|
||||
|
||||
var peHeaderOffset int64
|
||||
if f.DosHeader.MZSignature == 0x5a4d {
|
||||
peHeaderOffset = int64(f.DosHeader.AddressOfNewExeHeader)
|
||||
var sign [4]byte
|
||||
r.ReadAt(sign[:], peHeaderOffset)
|
||||
if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
|
||||
return nil, fmt.Errorf("Invalid PE COFF file signature of %v", sign)
|
||||
}
|
||||
peHeaderOffset += int64(4)
|
||||
} else {
|
||||
peHeaderOffset = int64(0)
|
||||
}
|
||||
|
||||
sr.Seek(peHeaderOffset, seekStart)
|
||||
if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch f.FileHeader.Machine {
|
||||
case IMAGE_FILE_MACHINE_UNKNOWN, IMAGE_FILE_MACHINE_ARMNT, IMAGE_FILE_MACHINE_AMD64, IMAGE_FILE_MACHINE_I386:
|
||||
default:
|
||||
return nil, fmt.Errorf("Unrecognised COFF file header machine value of 0x%x", f.FileHeader.Machine)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
if memoryMode {
|
||||
//get strings table location - offset is wrong in the header because we are in memory mode. Can we fix it? Yes we can!
|
||||
restore, err := sr.Seek(0, seekCurrent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Had a bad time getting restore point: %v", err)
|
||||
}
|
||||
//seek to table start (skip the headers)
|
||||
sr.Seek(peHeaderOffset+int64(binary.Size(f.FileHeader))+int64(f.FileHeader.SizeOfOptionalHeader), seekStart)
|
||||
|
||||
//iterate through the sections to find the raw offset value that matches the original symbol table value
|
||||
for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
|
||||
sh := new(SectionHeader32)
|
||||
if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
//original offset matches the pointer to the symbol table, update the header so other things can reference it good again
|
||||
if sh.PointerToRawData == f.FileHeader.PointerToSymbolTable {
|
||||
f.FileHeader.PointerToSymbolTable = sh.VirtualAddress
|
||||
}
|
||||
}
|
||||
//restore the original location of sr (this shouldn't actually be required, but just in case)
|
||||
sr.Seek(restore, seekStart)
|
||||
}
|
||||
|
||||
// Read string table.
|
||||
f.StringTable, err = readStringTable(&f.FileHeader, sr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read symbol table.
|
||||
f.COFFSymbols, err = readCOFFSymbols(&f.FileHeader, sr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f.Symbols, err = removeAuxSymbols(f.COFFSymbols, f.StringTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read optional header.
|
||||
f.OptionalHeaderOffset = peHeaderOffset + int64(binary.Size(f.FileHeader))
|
||||
sr.Seek(f.OptionalHeaderOffset, seekStart)
|
||||
|
||||
var oh32 OptionalHeader32
|
||||
var oh64 OptionalHeader64
|
||||
switch f.FileHeader.SizeOfOptionalHeader {
|
||||
case sizeofOptionalHeader32:
|
||||
if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if oh32.Magic != 0x10b { // PE32
|
||||
return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
|
||||
}
|
||||
f.OptionalHeader = &oh32
|
||||
case sizeofOptionalHeader64:
|
||||
if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if oh64.Magic != 0x20b { // PE32+
|
||||
return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
|
||||
}
|
||||
f.OptionalHeader = &oh64
|
||||
}
|
||||
|
||||
// Process sections.
|
||||
f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
|
||||
for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
|
||||
sh := new(SectionHeader32)
|
||||
if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name, err := sh.fullName(f.StringTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := new(Section)
|
||||
s.SectionHeader = SectionHeader{
|
||||
Name: name,
|
||||
OriginalName: sh.Name,
|
||||
VirtualSize: sh.VirtualSize,
|
||||
VirtualAddress: sh.VirtualAddress,
|
||||
Size: sh.SizeOfRawData,
|
||||
Offset: sh.PointerToRawData,
|
||||
PointerToRelocations: sh.PointerToRelocations,
|
||||
PointerToLineNumbers: sh.PointerToLineNumbers,
|
||||
NumberOfRelocations: sh.NumberOfRelocations,
|
||||
NumberOfLineNumbers: sh.NumberOfLineNumbers,
|
||||
Characteristics: sh.Characteristics,
|
||||
}
|
||||
r2 := r
|
||||
if sh.PointerToRawData == 0 { // .bss must have all 0s
|
||||
r2 = zeroReaderAt{}
|
||||
}
|
||||
if !memoryMode {
|
||||
s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
|
||||
} else {
|
||||
s.sr = io.NewSectionReader(r2, int64(s.SectionHeader.VirtualAddress), int64(s.SectionHeader.Size))
|
||||
}
|
||||
s.ReaderAt = s.sr
|
||||
f.Sections[i] = s
|
||||
}
|
||||
for i := range f.Sections {
|
||||
var err error
|
||||
f.Sections[i].Relocs, err = readRelocs(&f.Sections[i].SectionHeader, sr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Read Base Relocation Block and Items
|
||||
f.BaseRelocationTable, err = f.readBaseRelocationTable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Read certificate table
|
||||
f.CertificateTable, err = readCertTable(f, sr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//fill net info
|
||||
if f.IsManaged() {
|
||||
var va, size uint32
|
||||
|
||||
//determine location of the COM descriptor directory
|
||||
switch v := f.OptionalHeader.(type) {
|
||||
case *OptionalHeader32:
|
||||
va = v.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress
|
||||
size = v.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size
|
||||
case *OptionalHeader64:
|
||||
va = v.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress
|
||||
size = v.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].Size
|
||||
}
|
||||
|
||||
//I'm unsure how to get a reader (not a readerat) for a particular thing, so copying buffers around.. this could be more optimal
|
||||
buff := make([]byte, size)
|
||||
|
||||
//none of these reads have errors being caught either, which is probably not ideal
|
||||
if !memoryMode {
|
||||
r.ReadAt(buff, int64(f.RVAToFileOffset(va)))
|
||||
} else {
|
||||
r.ReadAt(buff, int64(va))
|
||||
}
|
||||
binary.Read(bytes.NewReader(buff), binary.LittleEndian, &f.Net.NetDirectory)
|
||||
|
||||
//Now that we have the COR20 header (COM descriptor directory header), we can get the metadata section header, which has the version
|
||||
buff = make([]byte, f.Net.NetDirectory.MetaDataSize)
|
||||
//again, none of the reads are error checked :shrug:
|
||||
if !memoryMode {
|
||||
r.ReadAt(buff, int64(f.RVAToFileOffset(f.Net.NetDirectory.MetaDataRVA)))
|
||||
} else {
|
||||
r.ReadAt(buff, int64(f.Net.NetDirectory.MetaDataRVA))
|
||||
}
|
||||
f.Net.MetaData, _ = newMetadataHeader(bytes.NewReader(buff))
|
||||
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// zeroReaderAt is ReaderAt that reads 0s.
|
||||
type zeroReaderAt struct{}
|
||||
|
||||
// ReadAt writes len(p) 0s into p.
|
||||
func (w zeroReaderAt) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
for i := range p {
|
||||
p[i] = 0
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// getString extracts a string from symbol string table.
|
||||
func getString(section []byte, start int) (string, bool) {
|
||||
if start < 0 || start >= len(section) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
for end := start; end < len(section); end++ {
|
||||
if section[end] == 0 {
|
||||
return string(section[start:end]), true
|
||||
}
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// Section returns the first section with the given name, or nil if no such
|
||||
// section exists.
|
||||
func (f *File) Section(name string) *Section {
|
||||
for _, s := range f.Sections {
|
||||
if s.Name == name {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *File) DWARF() (*dwarf.Data, error) {
|
||||
dwarfSuffix := func(s *Section) string {
|
||||
switch {
|
||||
case strings.HasPrefix(s.Name, ".debug_"):
|
||||
return s.Name[7:]
|
||||
case strings.HasPrefix(s.Name, ".zdebug_"):
|
||||
return s.Name[8:]
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// sectionData gets the data for s and checks its size.
|
||||
sectionData := func(s *Section) ([]byte, error) {
|
||||
b, err := s.Data()
|
||||
if err != nil && uint32(len(b)) < s.Size {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
|
||||
b = b[:s.VirtualSize]
|
||||
}
|
||||
|
||||
if len(b) >= 12 && string(b[:4]) == "ZLIB" {
|
||||
dlen := binary.BigEndian.Uint64(b[4:12])
|
||||
dbuf := make([]byte, dlen)
|
||||
r, err := zlib.NewReader(bytes.NewBuffer(b[12:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := io.ReadFull(r, dbuf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := r.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b = dbuf
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// There are many other DWARF sections, but these
|
||||
// are the ones the debug/dwarf package uses.
|
||||
// Don't bother loading others.
|
||||
var dat = map[string][]byte{"abbrev": nil, "info": nil, "str": nil, "line": nil, "ranges": nil}
|
||||
for _, s := range f.Sections {
|
||||
suffix := dwarfSuffix(s)
|
||||
if suffix == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := dat[suffix]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := sectionData(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dat[suffix] = b
|
||||
}
|
||||
|
||||
d, err := dwarf.New(dat["abbrev"], nil, nil, dat["info"], dat["line"], nil, dat["ranges"], dat["str"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Look for DWARF4 .debug_types sections.
|
||||
for i, s := range f.Sections {
|
||||
suffix := dwarfSuffix(s)
|
||||
if suffix != "types" {
|
||||
continue
|
||||
}
|
||||
|
||||
b, err := sectionData(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d.AddTypes(fmt.Sprintf("types-%d", i), b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// FormatError is unused.
|
||||
// The type is retained for compatibility.
|
||||
type FormatError struct {
|
||||
}
|
||||
|
||||
func (e *FormatError) Error() string {
|
||||
return "unknown error"
|
||||
}
|
||||
|
||||
//RVAToFileOffset Converts a Relative offset to the actual offset in the file.
|
||||
func (f *File) RVAToFileOffset(rva uint32) uint32 {
|
||||
var offset uint32
|
||||
for _, section := range f.Sections {
|
||||
if rva >= section.SectionHeader.VirtualAddress && rva <= section.SectionHeader.VirtualAddress+section.SectionHeader.Size {
|
||||
offset = section.SectionHeader.Offset + (rva - section.SectionHeader.VirtualAddress)
|
||||
}
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
//IsManaged returns true if the loaded PE file references the CLR header (aka is a .net exe)
|
||||
func (f *File) IsManaged() bool {
|
||||
switch v := f.OptionalHeader.(type) {
|
||||
case *OptionalHeader32:
|
||||
if v.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress != 0 {
|
||||
return true
|
||||
}
|
||||
case *OptionalHeader64:
|
||||
if v.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR].VirtualAddress != 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
-196
@@ -1,196 +0,0 @@
|
||||
package pe
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// ImportDirectory entry
|
||||
type ImportDirectory struct {
|
||||
OriginalFirstThunk uint32
|
||||
TimeDateStamp uint32
|
||||
ForwarderChain uint32
|
||||
NameRVA uint32
|
||||
FirstThunk uint32
|
||||
|
||||
DllName string
|
||||
}
|
||||
|
||||
// ImportedSymbols returns the names of all symbols
|
||||
// referred to by the binary f that are expected to be
|
||||
// satisfied by other libraries at dynamic load time.
|
||||
// It does not return weak symbols.
|
||||
func (f *File) ImportedSymbols() ([]string, error) {
|
||||
pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
|
||||
|
||||
// grab the number of data directory entries
|
||||
var ddLength uint32
|
||||
if pe64 {
|
||||
ddLength = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
|
||||
} else {
|
||||
ddLength = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
|
||||
}
|
||||
|
||||
// check that the length of data directory entries is large
|
||||
// enough to include the imports directory.
|
||||
if ddLength < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// grab the import data directory entry
|
||||
var idd DataDirectory
|
||||
if pe64 {
|
||||
idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
||||
} else {
|
||||
idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
||||
}
|
||||
|
||||
// figure out which section contains the import directory table
|
||||
var ds *Section
|
||||
ds = nil
|
||||
for _, s := range f.Sections {
|
||||
if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
|
||||
ds = s
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find a section, so no import libraries were found
|
||||
if ds == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sectionData, err := ds.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// seek to the virtual address specified in the import data directory
|
||||
d := sectionData[idd.VirtualAddress-ds.VirtualAddress:]
|
||||
|
||||
// start decoding the import directory
|
||||
var ida []ImportDirectory
|
||||
for len(d) > 0 {
|
||||
var dt ImportDirectory
|
||||
dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
|
||||
dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
|
||||
dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
|
||||
dt.NameRVA = binary.LittleEndian.Uint32(d[12:16])
|
||||
dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
|
||||
d = d[20:]
|
||||
if dt.OriginalFirstThunk == 0 {
|
||||
break
|
||||
}
|
||||
ida = append(ida, dt)
|
||||
}
|
||||
var all []string
|
||||
for _, dt := range ida {
|
||||
dt.DllName, _ = getString(sectionData, int(dt.NameRVA-ds.VirtualAddress))
|
||||
d = sectionData[:]
|
||||
// seek to OriginalFirstThunk
|
||||
d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
|
||||
for len(d) > 0 {
|
||||
if pe64 { // 64bit
|
||||
va := binary.LittleEndian.Uint64(d[0:8])
|
||||
d = d[8:]
|
||||
if va == 0 {
|
||||
break
|
||||
}
|
||||
if va&0x8000000000000000 > 0 { // is Ordinal
|
||||
// TODO add dynimport ordinal support.
|
||||
} else {
|
||||
fn, _ := getString(sectionData, int(uint32(va)-ds.VirtualAddress+2))
|
||||
all = append(all, fn+":"+dt.DllName)
|
||||
}
|
||||
} else { // 32bit
|
||||
va := binary.LittleEndian.Uint32(d[0:4])
|
||||
d = d[4:]
|
||||
if va == 0 {
|
||||
break
|
||||
}
|
||||
if va&0x80000000 > 0 { // is Ordinal
|
||||
// TODO add dynimport ordinal support.
|
||||
//ord := va&0x0000FFFF
|
||||
} else {
|
||||
fn, _ := getString(sectionData, int(va-ds.VirtualAddress+2))
|
||||
all = append(all, fn+":"+dt.DllName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
// ImportedLibraries returns the names of all libraries
|
||||
// referred to by the binary f that are expected to be
|
||||
// linked with the binary at dynamic link time.
|
||||
func (f *File) ImportedLibraries() ([]string, error) {
|
||||
|
||||
pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
|
||||
|
||||
// grab the number of data directory entries
|
||||
var ddLength uint32
|
||||
if pe64 {
|
||||
ddLength = f.OptionalHeader.(*OptionalHeader64).NumberOfRvaAndSizes
|
||||
} else {
|
||||
ddLength = f.OptionalHeader.(*OptionalHeader32).NumberOfRvaAndSizes
|
||||
}
|
||||
|
||||
// check that the length of data directory entries is large
|
||||
// enough to include the imports directory.
|
||||
if ddLength < IMAGE_DIRECTORY_ENTRY_IMPORT+1 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// grab the import data directory entry
|
||||
var idd DataDirectory
|
||||
if pe64 {
|
||||
idd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
||||
} else {
|
||||
idd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]
|
||||
}
|
||||
|
||||
// figure out which section contains the import directory table
|
||||
var ds *Section
|
||||
ds = nil
|
||||
for _, s := range f.Sections {
|
||||
if s.VirtualAddress <= idd.VirtualAddress && idd.VirtualAddress < s.VirtualAddress+s.VirtualSize {
|
||||
ds = s
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// didn't find a section, so no import libraries were found
|
||||
if ds == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
sectionData, err := ds.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// seek to the virtual address specified in the import data directory
|
||||
d := sectionData[idd.VirtualAddress-ds.VirtualAddress:]
|
||||
|
||||
// start decoding the import directory
|
||||
var ida []ImportDirectory
|
||||
for len(d) > 0 {
|
||||
var dt ImportDirectory
|
||||
dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
|
||||
dt.TimeDateStamp = binary.LittleEndian.Uint32(d[4:8])
|
||||
dt.ForwarderChain = binary.LittleEndian.Uint32(d[8:12])
|
||||
dt.NameRVA = binary.LittleEndian.Uint32(d[12:16])
|
||||
dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
|
||||
d = d[20:]
|
||||
if dt.OriginalFirstThunk == 0 {
|
||||
break
|
||||
}
|
||||
ida = append(ida, dt)
|
||||
}
|
||||
var all []string
|
||||
for _, dt := range ida {
|
||||
dt.DllName, _ = getString(sectionData, int(dt.NameRVA-ds.VirtualAddress))
|
||||
all = append(all, dt.DllName)
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
-69
@@ -1,69 +0,0 @@
|
||||
package pe
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
type IMAGE_COR20_HEADER struct {
|
||||
Cb uint32
|
||||
MajorRuntimeVersion uint16
|
||||
MinorRuntimeVersion uint16
|
||||
MetaDataRVA, MetaDataSize uint32
|
||||
Flags uint32 //todo: define flags
|
||||
EntryPointToken uint32
|
||||
ResourcesRVA, ResourcesSize,
|
||||
StrongNameSignatureRVA, StrongNameSignatureSize,
|
||||
CodeManagerTableRVA, CodeManagerTableSize,
|
||||
VTableFixupsRVA, VTableFixupsSize,
|
||||
ExportAddressTableJumpsRVA, ExportAddressTableJumpsSize,
|
||||
ManagedNativeHeaderRVA, ManagedNativeHeaderSize uint32
|
||||
}
|
||||
|
||||
//Net provides a public interface for getting at some net info.
|
||||
type Net struct {
|
||||
NetDirectory IMAGE_COR20_HEADER //Net directory information
|
||||
MetaData NetMetaData //MetaData Header
|
||||
}
|
||||
|
||||
type NetMetaData struct {
|
||||
Signature [4]byte //should be 0x424a4542
|
||||
MajorVersion uint16
|
||||
MinorVersion uint16
|
||||
Reserved uint32
|
||||
VersionLength uint32
|
||||
VersionString []byte
|
||||
Flags uint16 //todo: define flags betterer
|
||||
NumberOfStreams uint16
|
||||
}
|
||||
|
||||
func newMetadataHeader(i io.Reader) (NetMetaData, error) {
|
||||
r := NetMetaData{}
|
||||
|
||||
//todo: error checks/sanity checks
|
||||
binary.Read(i, binary.LittleEndian, &r.Signature)
|
||||
binary.Read(i, binary.LittleEndian, &r.MajorVersion)
|
||||
binary.Read(i, binary.LittleEndian, &r.MinorVersion)
|
||||
binary.Read(i, binary.LittleEndian, &r.Reserved)
|
||||
|
||||
// it appears that this value is terminated by two nulls.. that might be important at some point
|
||||
binary.Read(i, binary.LittleEndian, &r.VersionLength)
|
||||
r.VersionString = make([]byte, r.VersionLength)
|
||||
i.Read(r.VersionString)
|
||||
|
||||
binary.Read(i, binary.LittleEndian, r.Flags)
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
//NetCLRVersion returns the CLR version specified by the binary. Returns an empty string if not a net binary. String has had trailing nulls stripped.
|
||||
func (f File) NetCLRVersion() string {
|
||||
b := f.Net.MetaData.VersionString
|
||||
for i, x := range b {
|
||||
if x == 0x00 {
|
||||
b = b[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
-153
@@ -1,153 +0,0 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pe
|
||||
|
||||
type DosHeader struct {
|
||||
MZSignature uint16
|
||||
UsedBytesInTheLastPage uint16
|
||||
FileSizeInPages uint16
|
||||
NumberOfRelocationItems uint16
|
||||
HeaderSizeInParagraphs uint16
|
||||
MinimumExtraParagraphs uint16
|
||||
MaximumExtraParagraphs uint16
|
||||
InitialRelativeSS uint16
|
||||
InitialSP uint16
|
||||
CheckSum uint16
|
||||
InitialIP uint16
|
||||
InitialRelativeCS uint16
|
||||
AddressOfRelocationTable uint16
|
||||
OverlayNumber uint16
|
||||
Reserved [4]uint16
|
||||
OEMid uint16
|
||||
OEMinfo uint16
|
||||
Reserved2 [10]uint16
|
||||
AddressOfNewExeHeader uint32
|
||||
}
|
||||
|
||||
type FileHeader struct {
|
||||
Machine uint16
|
||||
NumberOfSections uint16
|
||||
TimeDateStamp uint32
|
||||
PointerToSymbolTable uint32
|
||||
NumberOfSymbols uint32
|
||||
SizeOfOptionalHeader uint16
|
||||
Characteristics uint16
|
||||
}
|
||||
|
||||
type DataDirectory struct {
|
||||
VirtualAddress uint32
|
||||
Size uint32
|
||||
}
|
||||
|
||||
type OptionalHeader32 struct {
|
||||
Magic uint16
|
||||
MajorLinkerVersion uint8
|
||||
MinorLinkerVersion uint8
|
||||
SizeOfCode uint32
|
||||
SizeOfInitializedData uint32
|
||||
SizeOfUninitializedData uint32
|
||||
AddressOfEntryPoint uint32
|
||||
BaseOfCode uint32
|
||||
BaseOfData uint32
|
||||
ImageBase uint32
|
||||
SectionAlignment uint32
|
||||
FileAlignment uint32
|
||||
MajorOperatingSystemVersion uint16
|
||||
MinorOperatingSystemVersion uint16
|
||||
MajorImageVersion uint16
|
||||
MinorImageVersion uint16
|
||||
MajorSubsystemVersion uint16
|
||||
MinorSubsystemVersion uint16
|
||||
Win32VersionValue uint32
|
||||
SizeOfImage uint32
|
||||
SizeOfHeaders uint32
|
||||
CheckSum uint32
|
||||
Subsystem uint16
|
||||
DllCharacteristics uint16
|
||||
SizeOfStackReserve uint32
|
||||
SizeOfStackCommit uint32
|
||||
SizeOfHeapReserve uint32
|
||||
SizeOfHeapCommit uint32
|
||||
LoaderFlags uint32
|
||||
NumberOfRvaAndSizes uint32
|
||||
DataDirectory [16]DataDirectory
|
||||
}
|
||||
|
||||
type OptionalHeader64 struct {
|
||||
Magic uint16
|
||||
MajorLinkerVersion uint8
|
||||
MinorLinkerVersion uint8
|
||||
SizeOfCode uint32
|
||||
SizeOfInitializedData uint32
|
||||
SizeOfUninitializedData uint32
|
||||
AddressOfEntryPoint uint32
|
||||
BaseOfCode uint32
|
||||
ImageBase uint64
|
||||
SectionAlignment uint32
|
||||
FileAlignment uint32
|
||||
MajorOperatingSystemVersion uint16
|
||||
MinorOperatingSystemVersion uint16
|
||||
MajorImageVersion uint16
|
||||
MinorImageVersion uint16
|
||||
MajorSubsystemVersion uint16
|
||||
MinorSubsystemVersion uint16
|
||||
Win32VersionValue uint32
|
||||
SizeOfImage uint32
|
||||
SizeOfHeaders uint32
|
||||
CheckSum uint32
|
||||
Subsystem uint16
|
||||
DllCharacteristics uint16
|
||||
SizeOfStackReserve uint64
|
||||
SizeOfStackCommit uint64
|
||||
SizeOfHeapReserve uint64
|
||||
SizeOfHeapCommit uint64
|
||||
LoaderFlags uint32
|
||||
NumberOfRvaAndSizes uint32
|
||||
DataDirectory [16]DataDirectory
|
||||
}
|
||||
|
||||
const (
|
||||
IMAGE_FILE_MACHINE_UNKNOWN = 0x0
|
||||
IMAGE_FILE_MACHINE_AM33 = 0x1d3
|
||||
IMAGE_FILE_MACHINE_AMD64 = 0x8664
|
||||
IMAGE_FILE_MACHINE_ARM = 0x1c0
|
||||
IMAGE_FILE_MACHINE_ARMNT = 0x1c4
|
||||
IMAGE_FILE_MACHINE_ARM64 = 0xaa64
|
||||
IMAGE_FILE_MACHINE_EBC = 0xebc
|
||||
IMAGE_FILE_MACHINE_I386 = 0x14c
|
||||
IMAGE_FILE_MACHINE_IA64 = 0x200
|
||||
IMAGE_FILE_MACHINE_M32R = 0x9041
|
||||
IMAGE_FILE_MACHINE_MIPS16 = 0x266
|
||||
IMAGE_FILE_MACHINE_MIPSFPU = 0x366
|
||||
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466
|
||||
IMAGE_FILE_MACHINE_POWERPC = 0x1f0
|
||||
IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1
|
||||
IMAGE_FILE_MACHINE_R4000 = 0x166
|
||||
IMAGE_FILE_MACHINE_SH3 = 0x1a2
|
||||
IMAGE_FILE_MACHINE_SH3DSP = 0x1a3
|
||||
IMAGE_FILE_MACHINE_SH4 = 0x1a6
|
||||
IMAGE_FILE_MACHINE_SH5 = 0x1a8
|
||||
IMAGE_FILE_MACHINE_THUMB = 0x1c2
|
||||
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169
|
||||
)
|
||||
|
||||
// IMAGE_DIRECTORY_ENTRY constants
|
||||
const (
|
||||
IMAGE_DIRECTORY_ENTRY_EXPORT = 0
|
||||
IMAGE_DIRECTORY_ENTRY_IMPORT = 1
|
||||
IMAGE_DIRECTORY_ENTRY_RESOURCE = 2
|
||||
IMAGE_DIRECTORY_ENTRY_EXCEPTION = 3
|
||||
IMAGE_DIRECTORY_ENTRY_SECURITY = 4
|
||||
IMAGE_DIRECTORY_ENTRY_BASERELOC = 5
|
||||
IMAGE_DIRECTORY_ENTRY_DEBUG = 6
|
||||
IMAGE_DIRECTORY_ENTRY_ARCHITECTURE = 7
|
||||
IMAGE_DIRECTORY_ENTRY_GLOBALPTR = 8
|
||||
IMAGE_DIRECTORY_ENTRY_TLS = 9
|
||||
IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG = 10
|
||||
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT = 11
|
||||
IMAGE_DIRECTORY_ENTRY_IAT = 12
|
||||
IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT = 13
|
||||
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR = 14
|
||||
)
|
||||
-171
@@ -1,171 +0,0 @@
|
||||
package pe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// RelocationTable - for base relocation entries
|
||||
type RelocationTableEntry struct {
|
||||
RelocationBlock
|
||||
BlockItems []BlockItem
|
||||
}
|
||||
|
||||
// RelocationBlock - for base relocation entries
|
||||
type RelocationBlock struct {
|
||||
VirtualAddress uint32
|
||||
SizeOfBlock uint32
|
||||
}
|
||||
|
||||
// BlockItem - relocation block item
|
||||
type BlockItem struct {
|
||||
Type byte // 4 bits
|
||||
Offset uint16 // 12 bits
|
||||
}
|
||||
|
||||
// Reloc represents a PE COFF relocation.
|
||||
// Each section contains its own relocation list.
|
||||
type Reloc struct {
|
||||
VirtualAddress uint32
|
||||
SymbolTableIndex uint32
|
||||
Type uint16
|
||||
}
|
||||
|
||||
const (
|
||||
//IMAGE_REL_BASED_ABSOLUTE - The base relocation is skipped. This type can be used to pad a block.
|
||||
IMAGE_REL_BASED_ABSOLUTE = 0
|
||||
|
||||
//IMAGE_REL_BASED_HIGH = 1
|
||||
//IMAGE_REL_BASED_LOW = 2
|
||||
|
||||
//IMAGE_REL_BASED_HIGHLOW - The base relocation applies all 32 bits of the difference to the 32-bit field at offset.
|
||||
IMAGE_REL_BASED_HIGHLOW = 3
|
||||
|
||||
//IMAGE_REL_BASED_HIGHADJ = 4
|
||||
//IMAGE_REL_BASED_MIPS_JMPADDR = 5
|
||||
//IMAGE_REL_BASED_ARM_MOV32 = 5
|
||||
//IMAGE_REL_BASED_RISCV_HIGH20 = 5
|
||||
//IMAGE_REL_BASED_THUMB_MOV32 = 7
|
||||
//IMAGE_REL_BASED_RISCV_LOW12I = 7
|
||||
//IMAGE_REL_BASED_RISCV_LOW12S = 8
|
||||
//IMAGE_REL_BASED_MIPS_JMPADDR16 = 9
|
||||
IMAGE_REL_BASED_DIR64 = 10
|
||||
)
|
||||
|
||||
// readBaseRelocationTable - reads the base relocation table from the file and stores it
|
||||
func (f *File) readBaseRelocationTable() (*[]RelocationTableEntry, error) {
|
||||
|
||||
if f.OptionalHeader == nil { // Optional header is optional, might not exist
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var dd DataDirectory
|
||||
if f.Machine == IMAGE_FILE_MACHINE_AMD64 {
|
||||
dd = f.OptionalHeader.(*OptionalHeader64).DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]
|
||||
} else {
|
||||
dd = f.OptionalHeader.(*OptionalHeader32).DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]
|
||||
}
|
||||
var sectionData []byte
|
||||
var err error
|
||||
for _, section := range f.Sections {
|
||||
if section.VirtualAddress == dd.VirtualAddress {
|
||||
sectionData, err = section.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
r := bytes.NewReader(sectionData)
|
||||
var reloBlocks []RelocationTableEntry
|
||||
bytesRead := uint32(0)
|
||||
for bytesRead < dd.Size {
|
||||
var reloBlock RelocationBlock
|
||||
err = binary.Read(r, binary.LittleEndian, &reloBlock)
|
||||
bytesRead += 8
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to read relocation block: %v", err)
|
||||
}
|
||||
numBlocks := (reloBlock.SizeOfBlock - 8) / 2
|
||||
blocks := make([]BlockItem, numBlocks)
|
||||
for i := uint32(0); i < numBlocks; i++ {
|
||||
var buf [2]byte
|
||||
err = binary.Read(r, binary.LittleEndian, &buf)
|
||||
bytesRead += 2
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to read relocation block item %d: %v", i, err)
|
||||
}
|
||||
var item BlockItem
|
||||
val := binary.LittleEndian.Uint16(buf[:2])
|
||||
item.Type = byte(val >> 12)
|
||||
item.Offset = val & 0x0fff
|
||||
blocks[i] = item
|
||||
}
|
||||
reloBlocks = append(reloBlocks, RelocationTableEntry{reloBlock, blocks})
|
||||
}
|
||||
return &reloBlocks, nil
|
||||
}
|
||||
|
||||
// Relocate - performs base relocations on this image to the given offset
|
||||
func (f *File) Relocate(baseAddr uint64, image *[]byte) {
|
||||
var imageBase uint64
|
||||
pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
|
||||
if pe64 {
|
||||
imageBase = f.OptionalHeader.(*OptionalHeader64).ImageBase
|
||||
} else {
|
||||
imageBase = uint64(f.OptionalHeader.(*OptionalHeader32).ImageBase)
|
||||
}
|
||||
for _, block := range *f.BaseRelocationTable {
|
||||
pageRVA := block.VirtualAddress
|
||||
for _, item := range block.BlockItems {
|
||||
if item.Type == IMAGE_REL_BASED_HIGHLOW { // 32 bit
|
||||
delta := uint32(baseAddr - imageBase)
|
||||
fileOffset := f.RVAToFileOffset(pageRVA)
|
||||
idx := fileOffset + uint32(item.Offset)
|
||||
originalAddress := binary.LittleEndian.Uint32((*image)[idx : idx+4])
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, originalAddress+delta)
|
||||
copy((*image)[idx:idx+4], b)
|
||||
} else if item.Type == IMAGE_REL_BASED_DIR64 { // 64 bit
|
||||
delta := baseAddr - imageBase
|
||||
fileOffset := f.RVAToFileOffset(pageRVA)
|
||||
idx := fileOffset + uint32(item.Offset)
|
||||
originalAddress := binary.LittleEndian.Uint64((*image)[idx : idx+8])
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, originalAddress+delta)
|
||||
copy((*image)[idx:idx+8], b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update imageBase in the optional header
|
||||
if pe64 {
|
||||
b := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(b, baseAddr)
|
||||
idx := f.OptionalHeaderOffset + 24
|
||||
copy((*image)[idx:idx+8], b)
|
||||
} else {
|
||||
b := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(b, uint32(baseAddr))
|
||||
idx := f.OptionalHeaderOffset + 28
|
||||
copy((*image)[idx:idx+4], b)
|
||||
}
|
||||
}
|
||||
|
||||
func readRelocs(sh *SectionHeader, r io.ReadSeeker) ([]Reloc, error) {
|
||||
if sh.NumberOfRelocations <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
_, err := r.Seek(int64(sh.PointerToRelocations), seekStart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to seek to %q section relocations: %v", sh.Name, err)
|
||||
}
|
||||
relocs := make([]Reloc, sh.NumberOfRelocations)
|
||||
err = binary.Read(r, binary.LittleEndian, relocs)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to read section relocations: %v", err)
|
||||
}
|
||||
return relocs, nil
|
||||
}
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// SectionHeader32 represents real PE COFF section header.
|
||||
type SectionHeader32 struct {
|
||||
Name [8]uint8
|
||||
VirtualSize uint32
|
||||
VirtualAddress uint32
|
||||
SizeOfRawData uint32
|
||||
PointerToRawData uint32
|
||||
PointerToRelocations uint32
|
||||
PointerToLineNumbers uint32
|
||||
NumberOfRelocations uint16
|
||||
NumberOfLineNumbers uint16
|
||||
Characteristics uint32
|
||||
}
|
||||
|
||||
// fullName finds real name of section sh. Normally name is stored
|
||||
// in sh.Name, but if it is longer then 8 characters, it is stored
|
||||
// in COFF string table st instead.
|
||||
func (sh *SectionHeader32) fullName(st StringTable) (string, error) {
|
||||
if sh.Name[0] != '/' {
|
||||
return cstring(sh.Name[:]), nil
|
||||
}
|
||||
i, err := strconv.Atoi(cstring(sh.Name[1:]))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return st.String(uint32(i))
|
||||
}
|
||||
|
||||
// SectionHeader is similar to SectionHeader32 with Name
|
||||
// field replaced by Go string. OriginalName is the
|
||||
// original name of the section on disk.
|
||||
type SectionHeader struct {
|
||||
Name string
|
||||
OriginalName [8]uint8
|
||||
VirtualSize uint32
|
||||
VirtualAddress uint32
|
||||
Size uint32
|
||||
Offset uint32
|
||||
PointerToRelocations uint32
|
||||
PointerToLineNumbers uint32
|
||||
NumberOfRelocations uint16
|
||||
NumberOfLineNumbers uint16
|
||||
Characteristics uint32
|
||||
}
|
||||
|
||||
// Section provides access to PE COFF section.
|
||||
type Section struct {
|
||||
SectionHeader
|
||||
Relocs []Reloc
|
||||
|
||||
// Embed ReaderAt for ReadAt method.
|
||||
// Do not embed SectionReader directly
|
||||
// to avoid having Read and Seek.
|
||||
// If a client wants Read and Seek it must use
|
||||
// Open() to avoid fighting over the seek offset
|
||||
// with other clients.
|
||||
io.ReaderAt
|
||||
sr *io.SectionReader
|
||||
}
|
||||
|
||||
// Data reads and returns the contents of the PE section s.
|
||||
func (s *Section) Data() ([]byte, error) {
|
||||
|
||||
if s.sr == nil { // This section was added from code, the internal SectionReader is nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
dat := make([]byte, s.sr.Size())
|
||||
n, err := s.sr.ReadAt(dat, 0)
|
||||
if n == len(dat) {
|
||||
err = nil
|
||||
}
|
||||
return dat[0:n], err
|
||||
}
|
||||
|
||||
// Open returns a new ReadSeeker reading the PE section s.
|
||||
func (s *Section) Open() io.ReadSeeker {
|
||||
return io.NewSectionReader(s.sr, 0, 1<<63-1)
|
||||
}
|
||||
|
||||
// Section Flags (Characteristics field)
|
||||
const (
|
||||
IMAGE_SCN_CNT_CODE = 0x00000020 // Section contains code
|
||||
IMAGE_SCN_MEM_EXECUTE = 0x20000000 // Section is executable
|
||||
IMAGE_SCN_MEM_READ = 0x40000000 // Section is readable
|
||||
|
||||
IMAGE_FILE_RELOCS_STRIPPED = 0x0001 // Relocation info stripped from file
|
||||
|
||||
IMAGE_DLLCHARACTERISTICS_NX_COMPAT = 0x0100 // Image is NX compatable
|
||||
|
||||
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 // DLL can move
|
||||
)
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// cstring converts ASCII byte sequence b to string.
|
||||
// It stops once it finds 0 or reaches end of b.
|
||||
func cstring(b []byte) string {
|
||||
i := bytes.IndexByte(b, 0)
|
||||
if i == -1 {
|
||||
i = len(b)
|
||||
}
|
||||
return string(b[:i])
|
||||
}
|
||||
|
||||
// StringTable is a COFF string table.
|
||||
type StringTable []byte
|
||||
|
||||
func readStringTable(fh *FileHeader, r io.ReadSeeker) (StringTable, error) {
|
||||
// COFF string table is located right after COFF symbol table.
|
||||
if fh.PointerToSymbolTable <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
offset := fh.PointerToSymbolTable + COFFSymbolSize*fh.NumberOfSymbols
|
||||
_, err := r.Seek(int64(offset), seekStart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to seek to string table: %v", err)
|
||||
}
|
||||
var l uint32
|
||||
err = binary.Read(r, binary.LittleEndian, &l)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to read string table length: %v", err)
|
||||
}
|
||||
// string table length includes itself
|
||||
if l <= 4 {
|
||||
return nil, nil
|
||||
}
|
||||
l -= 4
|
||||
buf := make([]byte, l)
|
||||
_, err = io.ReadFull(r, buf)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to read string table: %v", err)
|
||||
}
|
||||
// re-add the length to the first four bytes of the string table
|
||||
lbuf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(lbuf, l)
|
||||
|
||||
return StringTable(append(lbuf, buf...)), nil
|
||||
}
|
||||
|
||||
// String extracts string from COFF string table st at offset start.
|
||||
func (st StringTable) String(start uint32) (string, error) {
|
||||
// start includes 4 bytes of string table length
|
||||
if start < 4 {
|
||||
return "", fmt.Errorf("offset %d is before the start of string table", start)
|
||||
}
|
||||
//start -= 4 // we are now including the uint32 length in the StringTable buffer as a prefix, this might change
|
||||
if int(start) > len(st) {
|
||||
return "", fmt.Errorf("offset %d is beyond the end of string table", start)
|
||||
}
|
||||
return cstring(st[start:]), nil
|
||||
}
|
||||
-98
@@ -1,98 +0,0 @@
|
||||
// Copyright 2016 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pe
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const COFFSymbolSize = 18
|
||||
|
||||
// COFFSymbol represents single COFF symbol table record.
|
||||
type COFFSymbol struct {
|
||||
Name [8]uint8
|
||||
Value uint32
|
||||
SectionNumber int16
|
||||
Type uint16
|
||||
StorageClass uint8
|
||||
NumberOfAuxSymbols uint8
|
||||
}
|
||||
|
||||
func readCOFFSymbols(fh *FileHeader, r io.ReadSeeker) ([]COFFSymbol, error) {
|
||||
if fh.PointerToSymbolTable == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if fh.NumberOfSymbols <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
_, err := r.Seek(int64(fh.PointerToSymbolTable), seekStart)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to seek to symbol table: %v", err)
|
||||
}
|
||||
syms := make([]COFFSymbol, fh.NumberOfSymbols)
|
||||
err = binary.Read(r, binary.LittleEndian, syms)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to read symbol table: %v", err)
|
||||
}
|
||||
return syms, nil
|
||||
}
|
||||
|
||||
// isSymNameOffset checks symbol name if it is encoded as offset into string table.
|
||||
func isSymNameOffset(name [8]byte) (bool, uint32) {
|
||||
if name[0] == 0 && name[1] == 0 && name[2] == 0 && name[3] == 0 {
|
||||
return true, binary.LittleEndian.Uint32(name[4:])
|
||||
}
|
||||
return false, 0
|
||||
}
|
||||
|
||||
// FullName finds real name of symbol sym. Normally name is stored
|
||||
// in sym.Name, but if it is longer then 8 characters, it is stored
|
||||
// in COFF string table st instead.
|
||||
func (sym *COFFSymbol) FullName(st StringTable) (string, error) {
|
||||
if ok, offset := isSymNameOffset(sym.Name); ok {
|
||||
return st.String(offset)
|
||||
}
|
||||
return cstring(sym.Name[:]), nil
|
||||
}
|
||||
|
||||
func removeAuxSymbols(allsyms []COFFSymbol, st StringTable) ([]*Symbol, error) {
|
||||
if len(allsyms) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
syms := make([]*Symbol, 0)
|
||||
aux := uint8(0)
|
||||
for _, sym := range allsyms {
|
||||
if aux > 0 {
|
||||
aux--
|
||||
continue
|
||||
}
|
||||
name, err := sym.FullName(st)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aux = sym.NumberOfAuxSymbols
|
||||
s := &Symbol{
|
||||
Name: name,
|
||||
Value: sym.Value,
|
||||
SectionNumber: sym.SectionNumber,
|
||||
Type: sym.Type,
|
||||
StorageClass: sym.StorageClass,
|
||||
}
|
||||
syms = append(syms, s)
|
||||
}
|
||||
return syms, nil
|
||||
}
|
||||
|
||||
// Symbol is similar to COFFSymbol with Name field replaced
|
||||
// by Go string. Symbol also does not have NumberOfAuxSymbols.
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Value uint32
|
||||
SectionNumber int16
|
||||
Type uint16
|
||||
StorageClass uint8
|
||||
}
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
package pe
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func (peFile *File) Bytes() ([]byte, error) {
|
||||
var bytesWritten uint64
|
||||
peBuf := bytes.NewBuffer(nil)
|
||||
|
||||
// write DOS header and stub
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.DosHeader)
|
||||
bytesWritten += uint64(binary.Size(peFile.DosHeader))
|
||||
if peFile.DosExists {
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.DosStub)
|
||||
bytesWritten += uint64(binary.Size(peFile.DosStub))
|
||||
}
|
||||
|
||||
// write Rich header
|
||||
if peFile.RichHeader != nil {
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.RichHeader)
|
||||
bytesWritten += uint64(len(peFile.RichHeader))
|
||||
}
|
||||
|
||||
// apply padding before PE header if necessary
|
||||
if uint32(bytesWritten) != peFile.DosHeader.AddressOfNewExeHeader {
|
||||
padding := make([]byte, peFile.DosHeader.AddressOfNewExeHeader-uint32(bytesWritten))
|
||||
binary.Write(peBuf, binary.LittleEndian, padding)
|
||||
bytesWritten += uint64(len(padding))
|
||||
}
|
||||
|
||||
// write PE header
|
||||
peMagic := []byte{'P', 'E', 0x00, 0x00}
|
||||
binary.Write(peBuf, binary.LittleEndian, peMagic)
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.FileHeader)
|
||||
bytesWritten += uint64(binary.Size(peFile.FileHeader) + len(peMagic))
|
||||
|
||||
var (
|
||||
is32bit bool
|
||||
oldCertTableOffset, oldCertTableSize uint32
|
||||
)
|
||||
|
||||
switch peFile.FileHeader.Machine {
|
||||
case IMAGE_FILE_MACHINE_I386:
|
||||
is32bit = true
|
||||
optionalHeader := peFile.OptionalHeader.(*OptionalHeader32)
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.OptionalHeader.(*OptionalHeader32))
|
||||
bytesWritten += uint64(binary.Size(optionalHeader))
|
||||
|
||||
oldCertTableOffset = optionalHeader.DataDirectory[CERTIFICATE_TABLE].VirtualAddress
|
||||
oldCertTableSize = optionalHeader.DataDirectory[CERTIFICATE_TABLE].Size
|
||||
case IMAGE_FILE_MACHINE_AMD64:
|
||||
is32bit = false
|
||||
optionalHeader := peFile.OptionalHeader.(*OptionalHeader64)
|
||||
binary.Write(peBuf, binary.LittleEndian, optionalHeader)
|
||||
bytesWritten += uint64(binary.Size(optionalHeader))
|
||||
|
||||
oldCertTableOffset = optionalHeader.DataDirectory[CERTIFICATE_TABLE].VirtualAddress
|
||||
oldCertTableSize = optionalHeader.DataDirectory[CERTIFICATE_TABLE].Size
|
||||
default:
|
||||
return nil, errors.New("architecture not supported")
|
||||
}
|
||||
|
||||
// write section headers
|
||||
sectionHeaders := make([]SectionHeader32, len(peFile.Sections))
|
||||
for idx, section := range peFile.Sections {
|
||||
// write section header
|
||||
sectionHeader := SectionHeader32{
|
||||
Name: section.OriginalName,
|
||||
VirtualSize: section.VirtualSize,
|
||||
VirtualAddress: section.VirtualAddress,
|
||||
SizeOfRawData: section.Size,
|
||||
PointerToRawData: section.Offset,
|
||||
PointerToRelocations: section.PointerToRelocations,
|
||||
PointerToLineNumbers: section.PointerToLineNumbers,
|
||||
NumberOfRelocations: section.NumberOfRelocations,
|
||||
NumberOfLineNumbers: section.NumberOfLineNumbers,
|
||||
Characteristics: section.Characteristics,
|
||||
}
|
||||
|
||||
// if the PE file was pulled from memory, the symbol table offset in the header will be wrong.
|
||||
// Fix it up by picking the section that lines up, and use the raw offset instead.
|
||||
if peFile.FileHeader.PointerToSymbolTable == sectionHeader.VirtualAddress {
|
||||
peFile.FileHeader.PointerToSymbolTable = sectionHeader.PointerToRawData
|
||||
}
|
||||
|
||||
sectionHeaders[idx] = sectionHeader
|
||||
|
||||
//log.Printf("section: %+v\nsectionHeader: %+v\n", section, sectionHeader)
|
||||
|
||||
binary.Write(peBuf, binary.LittleEndian, sectionHeader)
|
||||
bytesWritten += uint64(binary.Size(sectionHeader))
|
||||
}
|
||||
|
||||
// write sections' data
|
||||
for idx, sectionHeader := range sectionHeaders {
|
||||
section := peFile.Sections[idx]
|
||||
sectionData, err := section.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if sectionData == nil { // for sections that weren't in the original file
|
||||
sectionData = []byte{}
|
||||
}
|
||||
if section.Offset != 0 && bytesWritten < uint64(section.Offset) {
|
||||
pad := make([]byte, uint64(section.Offset)-bytesWritten)
|
||||
peBuf.Write(pad)
|
||||
//log.Printf("Padding before section %s at %x: length:%x to:%x\n", section.Name, bytesWritten, len(pad), section.Offset)
|
||||
bytesWritten += uint64(len(pad))
|
||||
}
|
||||
// if our shellcode insertion address is inside this section, insert it at the correct offset in sectionData
|
||||
if peFile.InsertionAddr >= section.Offset && int64(peFile.InsertionAddr) < (int64(section.Offset)+int64(section.Size)-int64(len(peFile.InsertionBytes))) {
|
||||
sectionData = append(sectionData, peFile.InsertionBytes[:]...)
|
||||
datalen := len(sectionData)
|
||||
if sectionHeader.SizeOfRawData > uint32(datalen) {
|
||||
paddingSize := sectionHeader.SizeOfRawData - uint32(datalen)
|
||||
padding := make([]byte, paddingSize, paddingSize)
|
||||
sectionData = append(sectionData, padding...)
|
||||
//log.Printf("Padding after section %s: length:%d\n", section.Name, paddingSize)
|
||||
}
|
||||
}
|
||||
|
||||
binary.Write(peBuf, binary.LittleEndian, sectionData)
|
||||
bytesWritten += uint64(len(sectionData))
|
||||
}
|
||||
|
||||
// write symbols
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.COFFSymbols)
|
||||
bytesWritten += uint64(binary.Size(peFile.COFFSymbols))
|
||||
|
||||
// write the string table
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.StringTable)
|
||||
bytesWritten += uint64(binary.Size(peFile.StringTable))
|
||||
|
||||
var newCertTableOffset, newCertTableSize uint32
|
||||
|
||||
// write the certificate table
|
||||
if peFile.CertificateTable != nil {
|
||||
newCertTableOffset = uint32(bytesWritten)
|
||||
newCertTableSize = uint32(len(peFile.CertificateTable))
|
||||
} else {
|
||||
newCertTableOffset = 0
|
||||
newCertTableSize = 0
|
||||
}
|
||||
|
||||
binary.Write(peBuf, binary.LittleEndian, peFile.CertificateTable)
|
||||
bytesWritten += uint64(len(peFile.CertificateTable))
|
||||
|
||||
peData := peBuf.Bytes()
|
||||
|
||||
// write the offset and size of the new Certificate Table if it changed
|
||||
if newCertTableOffset != oldCertTableOffset || newCertTableSize != oldCertTableSize {
|
||||
certTableInfo := &DataDirectory{
|
||||
VirtualAddress: newCertTableOffset,
|
||||
Size: newCertTableSize,
|
||||
}
|
||||
|
||||
var certTableInfoBuf bytes.Buffer
|
||||
binary.Write(&certTableInfoBuf, binary.LittleEndian, certTableInfo)
|
||||
|
||||
var certTableLoc int64
|
||||
if is32bit {
|
||||
certTableLoc = int64(peFile.DosHeader.AddressOfNewExeHeader) + 24 + 128
|
||||
} else {
|
||||
certTableLoc = int64(peFile.DosHeader.AddressOfNewExeHeader) + 24 + 144
|
||||
}
|
||||
|
||||
peData = append(peData[:certTableLoc], append(certTableInfoBuf.Bytes(), peData[int(certTableLoc)+binary.Size(certTableInfo):]...)...)
|
||||
}
|
||||
|
||||
return peData, nil
|
||||
}
|
||||
|
||||
func (peFile *File) WriteFile(destFile string) error {
|
||||
f, err := os.Create(destFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
peData, err := peFile.Bytes()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = f.Write(peData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
-674
@@ -1,674 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
# The Universal Loader
|
||||
|
||||
This loader provides a unified Go interface for loading shared libraries **from memory** on Windows, OSX, and Linux.
|
||||
|
||||
Also included is a cross-platform `Call()` implementation that lets you call into exported symbols from those libraries without stress.
|
||||
|
||||
### Basic Usage
|
||||
|
||||
*libraryPath* set to `lib.so` for Linux, `lib.dyld` for OSX, or `lib.DLL` for Windows, then:
|
||||
|
||||
```
|
||||
image, err = ioutil.ReadFile(libraryPath)
|
||||
...
|
||||
|
||||
loader, err := universal.NewLoader()
|
||||
...
|
||||
|
||||
library, err := loader.LoadLibrary("main", &image)
|
||||
...
|
||||
|
||||
val, err := library.Call("Runme", 7)
|
||||
...
|
||||
```
|
||||
|
||||
Complete working examples of usage can be found in the examples/ folder in this repo.
|
||||
|
||||
|
||||
### Features and Limitations
|
||||
- OSX backend uses the system loader, so multiple interdependent libraries can be loaded.
|
||||
- OSX backend **automatically adds the underscore prefix for you**, so you can refer to symbols the same way on Linux, Windows, and OSX.
|
||||
- Linux and Windows backends do not use the system loader, so interdependent libraries cannot be loaded.
|
||||
- Linux backend does not use memfd!
|
||||
***I believe this is the first Golang Linux loader that does not use memfd!***
|
||||
|
||||
### Supported Architectures
|
||||
- OSX arm64 M1 Apple Chip (tested)
|
||||
***This is the first loader I've seen, Golang or not, that works on the M1!***
|
||||
- OSX amd64 (tested)
|
||||
- Windows amd64 (tested)
|
||||
- Windows 386 (untested, should work)
|
||||
- Linux amd64 (tested)
|
||||
- Linux 386 (untested, should work)
|
||||
- Other POSIX systems on 386, amd64, or arm64 (untested, should work)
|
||||
|
||||
**If you try this on any untested platforms, whether it works or not, let me know!**
|
||||
-188
@@ -1,188 +0,0 @@
|
||||
package universal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Binject/debug/macho"
|
||||
"github.com/awgh/cppgo/asmcall/cdecl"
|
||||
"github.com/awgh/rawreader"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const MaxUint = ^uint(0)
|
||||
const MaxInt = int(MaxUint >> 1)
|
||||
|
||||
// LoadLibraryImpl - loads a single library to memory, without trying to check or load required imports
|
||||
func LoadLibraryImpl(name string, image *[]byte) (*Library, error) {
|
||||
|
||||
// force input image to be a bundle
|
||||
// this is not the most efficient method, but it does create the abililty to do more fixups down the road,
|
||||
// like if we added universal file support
|
||||
machoFile, err := macho.NewFile(bytes.NewReader(*image))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
machoFile.FileHeader.Type = macho.TypeBundle
|
||||
newImage, err := machoFile.Bytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
image = &newImage
|
||||
|
||||
// Find dyld, loop through all the Mach-O images in memory until we find
|
||||
// one that exports NSCreateObjectFileImageFromMemory and NSLinkModule
|
||||
var execBase uintptr = uintptr(0x0000000001000000)
|
||||
ptr := execBase
|
||||
var createObjectAddr, linkModuleAddr uint64
|
||||
|
||||
for i := 0; i < 10; i++ { // quit after the first ten images
|
||||
ptr, err = findMacho(ptr, 0x1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Found image at 0x%08x\n", ptr)
|
||||
rawr := rawreader.New(ptr, MaxInt)
|
||||
machoFile, err := macho.NewFileFromMemory(rawr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exports := machoFile.Exports()
|
||||
|
||||
foundCreateObj := false
|
||||
foundLinkModule := false
|
||||
for _, export := range exports {
|
||||
if export.Name == "_NSCreateObjectFileImageFromMemory" {
|
||||
fmt.Println("found NSCreateObjectFileImageFromMemory")
|
||||
createObjectAddr = export.VirtualAddress
|
||||
foundCreateObj = true
|
||||
}
|
||||
if export.Name == "_NSLinkModule" {
|
||||
fmt.Println("found NSLinkModule")
|
||||
linkModuleAddr = export.VirtualAddress
|
||||
foundLinkModule = true
|
||||
}
|
||||
}
|
||||
if foundCreateObj && foundLinkModule {
|
||||
fmt.Println("Found dyld!")
|
||||
break
|
||||
}
|
||||
ptr = ptr + 0x1000
|
||||
}
|
||||
|
||||
createObjectProc := ptr + uintptr(createObjectAddr)
|
||||
linkModuleProc := ptr + uintptr(linkModuleAddr)
|
||||
|
||||
imageLen := uint64(len(*image))
|
||||
var NSObjectFileImage uintptr
|
||||
_, err = cdecl.Call(createObjectProc,
|
||||
uintptr(unsafe.Pointer(&(*image)[0])), // pointer to image (*char)
|
||||
uintptr(unsafe.Pointer(&imageLen)), // length of image (size_t)
|
||||
uintptr(unsafe.Pointer(&NSObjectFileImage))) // output pointer to struct
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options := uint64(3) // NSLINKMODULE_OPTION_PRIVATE | NSLINKMODULE_OPTION_BINDNOW
|
||||
modPtr, err := cdecl.Call(linkModuleProc,
|
||||
uintptr(unsafe.Pointer(NSObjectFileImage)), // file image struct from NSCreateObjectFileImageFromMemory
|
||||
uintptr(unsafe.Pointer(&name)), // image name
|
||||
uintptr(unsafe.Pointer(&options))) // image options flags
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
libPtr, err := findMachoPtr(modPtr)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, err
|
||||
}
|
||||
fmt.Printf("Found image at 0x%08x\n", libPtr)
|
||||
|
||||
// parse the Mach-o we just loaded to get the exports
|
||||
rawr := rawreader.New(libPtr, MaxInt)
|
||||
machoFile, err = macho.NewFileFromMemory(rawr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exports := machoFile.Exports()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lib := Library{
|
||||
BaseAddress: libPtr,
|
||||
Exports: make(map[string]uint64),
|
||||
}
|
||||
for _, x := range exports {
|
||||
lib.Exports[x.Name] = uint64(x.VirtualAddress)
|
||||
}
|
||||
return &lib, nil
|
||||
}
|
||||
|
||||
func isPtrValid(p uintptr) bool {
|
||||
r1, _, err := unix.RawSyscall(unix.SYS_CHMOD, p, 0777, 0)
|
||||
if r1 != 0 && err != 2 {
|
||||
return false
|
||||
}
|
||||
return err == 2
|
||||
}
|
||||
|
||||
func findMacho(addr uintptr, increment uint32) (uintptr, error) {
|
||||
var (
|
||||
base uintptr
|
||||
header uint32
|
||||
err error = fmt.Errorf("could not find macho")
|
||||
)
|
||||
ptr := addr
|
||||
for {
|
||||
if isPtrValid(ptr) {
|
||||
header = *(*uint32)(unsafe.Pointer(ptr))
|
||||
if header == macho.Magic64 {
|
||||
base = ptr
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
ptr += uintptr(increment)
|
||||
}
|
||||
return base, err
|
||||
}
|
||||
|
||||
func findMachoPtr(addr uintptr) (uintptr, error) {
|
||||
var (
|
||||
base uintptr
|
||||
header uint32
|
||||
err error = fmt.Errorf("could not find macho")
|
||||
)
|
||||
idx := addr
|
||||
ptr := uintptr(*(*uint64)(unsafe.Pointer(idx)))
|
||||
for {
|
||||
if isPtrValid(ptr) {
|
||||
header = *(*uint32)(unsafe.Pointer(ptr))
|
||||
if header == macho.Magic64 {
|
||||
base = ptr
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
}
|
||||
idx += 8
|
||||
ptr = uintptr(*(*uint64)(unsafe.Pointer(idx)))
|
||||
}
|
||||
return base, err
|
||||
}
|
||||
|
||||
// Call - call a function in a loaded library
|
||||
func (l *Library) Call(functionName string, args ...uintptr) (uintptr, error) {
|
||||
|
||||
if len(functionName) > 0 && functionName[0] != '_' {
|
||||
functionName = "_" + functionName
|
||||
} // OSX has underscore-prefixed exports
|
||||
|
||||
proc, ok := l.FindProc(functionName)
|
||||
if !ok {
|
||||
return 0, errors.New("Call did not find export " + functionName)
|
||||
}
|
||||
val, err := cdecl.Call(proc, args...)
|
||||
return val, err
|
||||
}
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
package universal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/Binject/debug/elf"
|
||||
"github.com/awgh/cppgo/asmcall/cdecl"
|
||||
)
|
||||
|
||||
// LoadLibraryImpl - loads a single library to memory, without trying to check or load required imports
|
||||
func LoadLibraryImpl(name string, image *[]byte) (*Library, error) {
|
||||
elflib, err := elf.NewFile(bytes.NewReader(*image))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if elflib.FileHeader.Type != elf.ET_DYN {
|
||||
return nil, errors.New("Cannot load non-dynamic libraries")
|
||||
}
|
||||
exports, err := elflib.Exports()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pageSize := uint64(unix.Getpagesize())
|
||||
var memSize uint64
|
||||
|
||||
loads := make([]*elf.Prog, 0)
|
||||
for _, p := range elflib.Progs {
|
||||
if p.Type != elf.PT_LOAD || p.Filesz == 0 || p.Memsz == 0 {
|
||||
continue
|
||||
}
|
||||
highest := p.Memsz + p.Vaddr
|
||||
if highest > memSize {
|
||||
memSize = highest
|
||||
}
|
||||
loads = append(loads, p)
|
||||
}
|
||||
if memSize%pageSize != 0 { // round up to page size
|
||||
memSize = ((memSize / pageSize) * pageSize) + pageSize
|
||||
}
|
||||
baseBuf, err := unix.Mmap(-1, 0, int(memSize),
|
||||
unix.PROT_READ|unix.PROT_WRITE|unix.PROT_EXEC, unix.MAP_PRIVATE|unix.MAP_ANON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range loads {
|
||||
copy(baseBuf[p.Vaddr:], (*image)[p.Off:p.Off+p.Filesz])
|
||||
}
|
||||
ex := make(map[string]uint64)
|
||||
for _, x := range exports {
|
||||
ex[x.Name] = x.VirtualAddress
|
||||
}
|
||||
library := Library{
|
||||
BaseAddress: uintptr(unsafe.Pointer(&baseBuf[0])),
|
||||
Exports: ex,
|
||||
}
|
||||
return &library, nil
|
||||
}
|
||||
|
||||
// Call - call a function in a loaded library
|
||||
func (l *Library) Call(functionName string, args ...uintptr) (uintptr, error) {
|
||||
proc, ok := l.FindProc(functionName)
|
||||
if !ok {
|
||||
return 0, errors.New("Call did not find export " + functionName)
|
||||
}
|
||||
val, err := cdecl.Call(proc, args...)
|
||||
return val, err
|
||||
}
|
||||
-205
@@ -1,205 +0,0 @@
|
||||
package universal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"os"
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"unsafe"
|
||||
|
||||
"github.com/Binject/debug/pe"
|
||||
)
|
||||
|
||||
const (
|
||||
MEM_COMMIT = 0x001000
|
||||
MEM_RESERVE = 0x002000
|
||||
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS = 0x00000004
|
||||
INFINITE = 0xFFFFFFFF
|
||||
)
|
||||
|
||||
// LoadLibraryImpl - loads a single library to memory, without trying to check or load required imports
|
||||
func LoadLibraryImpl(name string, image *[]byte) (*Library, error) {
|
||||
const PtrSize = 32 << uintptr(^uintptr(0)>>63) // are we on a 32bit or 64bit system?
|
||||
pelib, err := pe.NewFile(bytes.NewReader(*image))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pe64 := pelib.Machine == pe.IMAGE_FILE_MACHINE_AMD64
|
||||
if pe64 && PtrSize != 64 {
|
||||
return nil, errors.New("Cannot load a 64bit DLL from a 32bit process")
|
||||
} else if !pe64 && PtrSize != 32 {
|
||||
return nil, errors.New("Cannot load a 32bit DLL from a 64bit process")
|
||||
}
|
||||
|
||||
var sizeOfImage uint32
|
||||
if pe64 {
|
||||
sizeOfImage = pelib.OptionalHeader.(*pe.OptionalHeader64).SizeOfImage
|
||||
} else {
|
||||
sizeOfImage = pelib.OptionalHeader.(*pe.OptionalHeader32).SizeOfImage
|
||||
}
|
||||
r, err := virtualAlloc(0, sizeOfImage, MEM_RESERVE, syscall.PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dst, err := virtualAlloc(r, sizeOfImage, MEM_COMMIT, syscall.PAGE_EXECUTE_READWRITE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
//perform base relocations
|
||||
pelib.Relocate(uint64(dst), image)
|
||||
|
||||
//write to memory
|
||||
CopySections(pelib, image, dst)
|
||||
|
||||
exports, err := pelib.Exports()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
lib := Library{
|
||||
BaseAddress: dst,
|
||||
Exports: make(map[string]uint64),
|
||||
}
|
||||
for _, x := range exports {
|
||||
lib.Exports[x.Name] = uint64(x.VirtualAddress)
|
||||
}
|
||||
|
||||
return &lib, nil
|
||||
}
|
||||
|
||||
// CopySections - writes the sections of a PE image to the given base address in memory
|
||||
func CopySections(pefile *pe.File, image *[]byte, loc uintptr) error {
|
||||
// Copy Headers
|
||||
var sizeOfHeaders uint32
|
||||
if pefile.Machine == pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
sizeOfHeaders = pefile.OptionalHeader.(*pe.OptionalHeader64).SizeOfHeaders
|
||||
} else {
|
||||
sizeOfHeaders = pefile.OptionalHeader.(*pe.OptionalHeader32).SizeOfHeaders
|
||||
}
|
||||
hbuf := (*[^uint32(0)]byte)(unsafe.Pointer(uintptr(loc)))
|
||||
for index := uint32(0); index < sizeOfHeaders; index++ {
|
||||
hbuf[index] = (*image)[index]
|
||||
}
|
||||
|
||||
// Copy Sections
|
||||
for _, section := range pefile.Sections {
|
||||
//fmt.Println("Writing:", fmt.Sprintf("%s %x %x", section.Name, loc, uint32(loc)+section.VirtualAddress))
|
||||
if section.Size == 0 {
|
||||
continue
|
||||
}
|
||||
d, err := section.Data()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dataLen := uint32(len(d))
|
||||
dst := uint64(loc) + uint64(section.VirtualAddress)
|
||||
buf := (*[^uint32(0)]byte)(unsafe.Pointer(uintptr(dst)))
|
||||
for index := uint32(0); index < dataLen; index++ {
|
||||
buf[index] = d[index]
|
||||
}
|
||||
}
|
||||
|
||||
// Write symbol and string tables
|
||||
bbuf := bytes.NewBuffer(nil)
|
||||
binary.Write(bbuf, binary.LittleEndian, pefile.COFFSymbols)
|
||||
binary.Write(bbuf, binary.LittleEndian, pefile.StringTable)
|
||||
b := bbuf.Bytes()
|
||||
blen := uint32(len(b))
|
||||
for index := uint32(0); index < blen; index++ {
|
||||
hbuf[index+pefile.FileHeader.PointerToSymbolTable] = b[index]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.MustLoadDLL("kernel32.dll")
|
||||
procVirtualAlloc = kernel32.MustFindProc("VirtualAlloc")
|
||||
)
|
||||
|
||||
func virtualAlloc(addr uintptr, size, allocType, protect uint32) (uintptr, error) {
|
||||
r1, _, e1 := procVirtualAlloc.Call(
|
||||
addr,
|
||||
uintptr(size),
|
||||
uintptr(allocType),
|
||||
uintptr(protect))
|
||||
|
||||
if int(r1) == 0 {
|
||||
return r1, os.NewSyscallError("VirtualAlloc", e1)
|
||||
}
|
||||
return r1, nil
|
||||
}
|
||||
|
||||
// Call - call a function in a loaded library
|
||||
//
|
||||
// cribbed from: https://golang.org/src/Syscall/dll_windows.go
|
||||
//
|
||||
// On amd64, Call can pass and return floating-point values. To pass
|
||||
// an argument x with C type "float", use
|
||||
// uintptr(math.Float32bits(x)). To pass an argument with C type
|
||||
// "double", use uintptr(math.Float64bits(x)). Floating-point return
|
||||
// values are returned in r2. The return value for C type "float" is
|
||||
// math.Float32frombits(uint32(r2)). For C type "double", it is
|
||||
// math.Float64frombits(uint64(r2)).
|
||||
func (l *Library) Call(functionName string, args ...uintptr) (uintptr, error) {
|
||||
|
||||
proc, ok := l.FindProc(functionName)
|
||||
if !ok {
|
||||
return 0, errors.New("Call did not find export " + functionName)
|
||||
}
|
||||
var r uintptr
|
||||
var errno syscall.Errno
|
||||
var err error
|
||||
switch len(args) {
|
||||
case 0:
|
||||
r, _, errno = syscall.Syscall(proc, uintptr(len(args)), 0, 0, 0)
|
||||
case 1:
|
||||
r, _, errno = syscall.Syscall(proc, uintptr(len(args)), args[0], 0, 0)
|
||||
case 2:
|
||||
r, _, errno = syscall.Syscall(proc, uintptr(len(args)), args[0], args[1], 0)
|
||||
case 3:
|
||||
r, _, errno = syscall.Syscall(proc, uintptr(len(args)), args[0], args[1], args[2])
|
||||
case 4:
|
||||
r, _, errno = syscall.Syscall6(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], 0, 0)
|
||||
case 5:
|
||||
r, _, errno = syscall.Syscall6(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], 0)
|
||||
case 6:
|
||||
r, _, errno = syscall.Syscall6(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5])
|
||||
case 7:
|
||||
r, _, errno = syscall.Syscall9(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], 0, 0)
|
||||
case 8:
|
||||
r, _, errno = syscall.Syscall9(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], 0)
|
||||
case 9:
|
||||
r, _, errno = syscall.Syscall9(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8])
|
||||
case 10:
|
||||
r, _, errno = syscall.Syscall12(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], 0, 0)
|
||||
case 11:
|
||||
r, _, errno = syscall.Syscall12(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], 0)
|
||||
case 12:
|
||||
r, _, errno = syscall.Syscall12(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11])
|
||||
case 13:
|
||||
r, _, errno = syscall.Syscall15(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], 0, 0)
|
||||
case 14:
|
||||
r, _, errno = syscall.Syscall15(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], 0)
|
||||
case 15:
|
||||
r, _, errno = syscall.Syscall15(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14])
|
||||
case 16:
|
||||
r, _, errno = syscall.Syscall18(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], 0, 0)
|
||||
case 17:
|
||||
r, _, errno = syscall.Syscall18(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], 0)
|
||||
case 18:
|
||||
r, _, errno = syscall.Syscall18(proc, uintptr(len(args)), args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9], args[10], args[11], args[12], args[13], args[14], args[15], args[16], args[17])
|
||||
default:
|
||||
return 0, errors.New("Call " + functionName + " with too many arguments " + strconv.Itoa(len(args)) + ".")
|
||||
}
|
||||
if errno != 0 {
|
||||
errString := errno.Error()
|
||||
if errString != "" {
|
||||
err = errors.New(errString)
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
package universal
|
||||
|
||||
// Library - describes a loaded library
|
||||
type Library struct {
|
||||
Name string
|
||||
BaseAddress uintptr
|
||||
Exports map[string]uint64
|
||||
}
|
||||
|
||||
// Loader - keeps track of
|
||||
type Loader struct {
|
||||
Libraries []*Library
|
||||
}
|
||||
|
||||
// NewLoader - returns a new instance of a Loader
|
||||
func NewLoader() (*Loader, error) {
|
||||
return &Loader{}, nil
|
||||
}
|
||||
|
||||
// LoadLibrary - loads a library into this process from the given buffer
|
||||
func (l *Loader) LoadLibrary(name string, image *[]byte) (*Library, error) {
|
||||
|
||||
library, err := LoadLibraryImpl(name, image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
library.Name = name
|
||||
l.Libraries = append(l.Libraries, library)
|
||||
return library, nil
|
||||
}
|
||||
|
||||
// FindProc - returns the address of the given function from the given library
|
||||
func (l *Loader) FindProc(libname string, funcname string) (uintptr, bool) {
|
||||
|
||||
for _, lib := range l.Libraries {
|
||||
if lib.Name == libname {
|
||||
return lib.FindProc(funcname)
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// FindProc - returns the address of the given function in this library
|
||||
func (l *Library) FindProc(funcname string) (uintptr, bool) {
|
||||
v, ok := l.Exports[funcname]
|
||||
return l.BaseAddress + uintptr(v), ok
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2017 Loren Segal
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
// Package cdecl implements method call ABI for the POSIX C/C++ calling
|
||||
// convention.
|
||||
//
|
||||
// Note that this package does not rely on cgo to implement calling, so a
|
||||
// compiler is not needed to call into C functions using this library.
|
||||
package cdecl
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Call calls a cdecl style function at memory address addr with the arguments
|
||||
// list a. The function result value is returned as a uintptr to be translated
|
||||
// by the caller. If the function cannot be called (usually due to an invalid
|
||||
// number of argument), an error is returned.
|
||||
func Call(addr uintptr, a ...uintptr) (uintptr, error) {
|
||||
switch l := len(a); l {
|
||||
case 0:
|
||||
return call0(addr), nil
|
||||
case 1:
|
||||
return call1(addr, a[0]), nil
|
||||
case 2:
|
||||
return call2(addr, a[0], a[1]), nil
|
||||
case 3:
|
||||
return call3(addr, a[0], a[1], a[2]), nil
|
||||
case 4:
|
||||
return call4(addr, a[0], a[1], a[2], a[3]), nil
|
||||
case 5:
|
||||
return call5(addr, a[0], a[1], a[2], a[3], a[4]), nil
|
||||
case 6:
|
||||
return call6(addr, a[0], a[1], a[2], a[3], a[4], a[5]), nil
|
||||
default:
|
||||
return 0, errors.New("too many arguments")
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
TEXT ·call0(SB),4,$0-8
|
||||
MOVL addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVL AX, ret+4(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call1(SB),4,$4-12
|
||||
MOVL a+4(FP), BX
|
||||
MOVL BX, 0(SP)
|
||||
|
||||
MOVL addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVL AX, ret+8(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call2(SB),4,$8-16
|
||||
MOVL a+4(FP), BX
|
||||
MOVL BX, 0(SP)
|
||||
MOVL b+8(FP), BX
|
||||
MOVL BX, 4(SP)
|
||||
|
||||
MOVL addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVL AX, ret+12(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call3(SB),4,$12-20
|
||||
MOVL a+4(FP), BX
|
||||
MOVL BX, 0(SP)
|
||||
MOVL b+8(FP), BX
|
||||
MOVL BX, 4(SP)
|
||||
MOVL c+12(FP), BX
|
||||
MOVL BX, 8(SP)
|
||||
|
||||
MOVL addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVL AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call4(SB),4,$16-24
|
||||
MOVL a+4(FP), BX
|
||||
MOVL BX, 0(SP)
|
||||
MOVL b+8(FP), BX
|
||||
MOVL BX, 4(SP)
|
||||
MOVL c+12(FP), BX
|
||||
MOVL BX, 8(SP)
|
||||
MOVL d+16(FP), BX
|
||||
MOVL BX, 12(SP)
|
||||
|
||||
MOVL addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVL AX, ret+20(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB),4,$20-28
|
||||
MOVL a+4(FP), BX
|
||||
MOVL BX, 0(SP)
|
||||
MOVL b+8(FP), BX
|
||||
MOVL BX, 4(SP)
|
||||
MOVL c+12(FP), BX
|
||||
MOVL BX, 8(SP)
|
||||
MOVL d+16(FP), BX
|
||||
MOVL BX, 12(SP)
|
||||
MOVL e+20(FP), BX
|
||||
MOVL BX, 16(SP)
|
||||
|
||||
MOVL addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVL AX, ret+24(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call6(SB),4,$24-32
|
||||
MOVL a+4(FP), BX
|
||||
MOVL BX, 0(SP)
|
||||
MOVL b+8(FP), BX
|
||||
MOVL BX, 4(SP)
|
||||
MOVL c+12(FP), BX
|
||||
MOVL BX, 8(SP)
|
||||
MOVL d+16(FP), BX
|
||||
MOVL BX, 12(SP)
|
||||
MOVL e+20(FP), BX
|
||||
MOVL BX, 16(SP)
|
||||
MOVL f+24(FP), BX
|
||||
MOVL BX, 20(SP)
|
||||
|
||||
MOVL addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVL AX, ret+28(FP)
|
||||
RET
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package cdecl
|
||||
|
||||
func call0(addr uintptr) uintptr
|
||||
func call1(addr uintptr, a uintptr) uintptr
|
||||
func call2(addr uintptr, a uintptr, b uintptr) uintptr
|
||||
func call3(addr uintptr, a uintptr, b uintptr, c uintptr) uintptr
|
||||
func call4(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr) uintptr
|
||||
func call5(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) uintptr
|
||||
func call6(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) uintptr
|
||||
-70
@@ -1,70 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
TEXT ·call0(SB),4,$0-16
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+8(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call1(SB),4,$0-24
|
||||
MOVQ a+8(FP), DI
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call2(SB),4,$0-32
|
||||
MOVQ a+8(FP), DI
|
||||
MOVQ b+16(FP), SI
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+24(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call3(SB),4,$0-40
|
||||
MOVQ a+8(FP), DI
|
||||
MOVQ b+16(FP), SI
|
||||
MOVQ c+24(FP), DX
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+32(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call4(SB),4,$0-48
|
||||
MOVQ a+8(FP), DI
|
||||
MOVQ b+16(FP), SI
|
||||
MOVQ c+24(FP), DX
|
||||
MOVQ d+32(FP), CX
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+40(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB),4,$0-56
|
||||
MOVQ a+8(FP), DI
|
||||
MOVQ b+16(FP), SI
|
||||
MOVQ c+24(FP), DX
|
||||
MOVQ d+32(FP), CX
|
||||
MOVQ e+40(FP), R8
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call6(SB),4,$0-64
|
||||
MOVQ a+8(FP), DI
|
||||
MOVQ b+16(FP), SI
|
||||
MOVQ c+24(FP), DX
|
||||
MOVQ d+32(FP), CX
|
||||
MOVQ e+40(FP), R8
|
||||
MOVQ f+48(FP), R9
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+56(FP)
|
||||
RET
|
||||
-64
@@ -1,64 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
TEXT ·call0(SB),4,$16-8
|
||||
MOVD addr+0(FP), R0
|
||||
CALL R0
|
||||
MOVD R0, ret+8(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call1(SB),4,$24-16
|
||||
MOVD addr+0(FP), R1
|
||||
MOVD arg1+8(FP), R0
|
||||
CALL R1
|
||||
MOVD R0, ret+16(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call2(SB),4,$32-24
|
||||
MOVD addr+0(FP), R2
|
||||
MOVD arg1+8(FP), R0
|
||||
MOVD arg2+16(FP), R1
|
||||
CALL R2
|
||||
MOVD R0, ret+24(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call3(SB),4,$40-32
|
||||
MOVD addr+0(FP), R3
|
||||
MOVD arg1+8(FP), R0
|
||||
MOVD arg2+16(FP), R1
|
||||
MOVD arg3+24(FP), R2
|
||||
CALL R3
|
||||
MOVD R0, ret+32(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call4(SB),4,$48-40
|
||||
MOVD addr+0(FP), R4
|
||||
MOVD arg1+8(FP), R0
|
||||
MOVD arg2+16(FP), R1
|
||||
MOVD arg3+24(FP), R2
|
||||
MOVD arg4+32(FP), R3
|
||||
CALL R4
|
||||
MOVD R0, ret+40(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB),4,$56-48
|
||||
MOVD addr+0(FP), R5
|
||||
MOVD arg1+8(FP), R0
|
||||
MOVD arg2+16(FP), R1
|
||||
MOVD arg3+24(FP), R2
|
||||
MOVD arg4+32(FP), R3
|
||||
MOVD arg5+40(FP), R4
|
||||
CALL R5
|
||||
MOVD R0, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call6(SB),4,$64-56
|
||||
MOVD addr+0(FP), R6
|
||||
MOVD arg1+8(FP), R0
|
||||
MOVD arg2+16(FP), R1
|
||||
MOVD arg3+24(FP), R2
|
||||
MOVD arg4+32(FP), R3
|
||||
MOVD arg5+40(FP), R4
|
||||
MOVD arg6+48(FP), R5
|
||||
CALL R6
|
||||
MOVD R0, ret+56(FP)
|
||||
RET
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
package cdecl
|
||||
|
||||
func call0(addr uintptr) uintptr
|
||||
func call1(addr uintptr, a uintptr) uintptr
|
||||
func call2(addr uintptr, a uintptr, b uintptr) uintptr
|
||||
func call3(addr uintptr, a uintptr, b uintptr, c uintptr) uintptr
|
||||
func call4(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr) uintptr
|
||||
func call5(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr) uintptr
|
||||
func call6(addr uintptr, a uintptr, b uintptr, c uintptr, d uintptr, e uintptr, f uintptr) uintptr
|
||||
-73
@@ -1,73 +0,0 @@
|
||||
// +build windows
|
||||
|
||||
TEXT ·call0(SB),4,$32-16
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+8(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call1(SB),0,$32-24
|
||||
MOVQ a+8(FP), CX
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+16(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call2(SB),0,$32-32
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), DX
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+24(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call3(SB),0,$32-40
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), DX
|
||||
MOVQ c+24(FP), R8
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+32(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call4(SB),0,$32-48
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), DX
|
||||
MOVQ c+24(FP), R8
|
||||
MOVQ d+32(FP), R9
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+40(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB),0,$40-56
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), DX
|
||||
MOVQ c+24(FP), R8
|
||||
MOVQ d+32(FP), R9
|
||||
MOVQ e+40(FP), BX
|
||||
MOVQ BX, e-8(SP)
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·call6(SB),0,$48-64
|
||||
MOVQ a+8(FP), CX
|
||||
MOVQ b+16(FP), DX
|
||||
MOVQ c+24(FP), R8
|
||||
MOVQ d+32(FP), R9
|
||||
MOVQ e+40(FP), BX
|
||||
MOVQ BX, e-16(SP)
|
||||
MOVQ f+48(FP), BX
|
||||
MOVQ BX, f-8(SP)
|
||||
|
||||
MOVQ addr+0(FP), AX
|
||||
CALL AX
|
||||
MOVQ AX, ret+56(FP)
|
||||
RET
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 awgh
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
# rawreader
|
||||
Implements io.ReaderAt over raw memory.
|
||||
|
||||
You should REFLECT on how UNSAFE this is.
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
package rawreader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// RawReader implements interfaces implemented by files over raw memory
|
||||
// this is completely unsafe by design
|
||||
// you should either know why you want this exactly or stay away!
|
||||
type RawReader struct {
|
||||
sliceHeader *reflect.SliceHeader
|
||||
rawPtr uintptr
|
||||
Data []byte
|
||||
Length int
|
||||
}
|
||||
|
||||
// New returns a new populated RawReader
|
||||
func New(start uintptr, length int) *RawReader {
|
||||
sh := &reflect.SliceHeader{
|
||||
Data: start,
|
||||
Len: length,
|
||||
Cap: length,
|
||||
}
|
||||
data := *(*[]byte)(unsafe.Pointer(sh))
|
||||
return &RawReader{sliceHeader: sh, rawPtr: start, Data: data, Length: length}
|
||||
}
|
||||
|
||||
// ReadAt implements io.ReaderAt https://golang.org/pkg/io/#ReaderAt
|
||||
// ReadAt reads len(p) bytes into p starting at offset off in the underlying input source.
|
||||
// It returns the number of bytes read (0 <= n <= len(p)) and any error encountered.
|
||||
//
|
||||
// When ReadAt returns n < len(p), it returns a non-nil error explaining why more bytes were not returned.
|
||||
// In this respect, ReadAt is stricter than Read.
|
||||
//
|
||||
// Even if ReadAt returns n < len(p), it may use all of p as scratch space during the call.
|
||||
// If some data is available but not len(p) bytes, ReadAt blocks until either all the data is available or an error occurs.
|
||||
// In this respect ReadAt is different from Read.
|
||||
//
|
||||
// If the n = len(p) bytes returned by ReadAt are at the end of the input source,
|
||||
// ReadAt may return either err == EOF or err == nil.
|
||||
//
|
||||
// If ReadAt is reading from an input source with a seek offset,
|
||||
// ReadAt should not affect nor be affected by the underlying seek offset.
|
||||
// Clients of ReadAt can execute parallel ReadAt calls on the same input source.
|
||||
func (f *RawReader) ReadAt(p []byte, off int64) (n int, err error) {
|
||||
if off < 0 {
|
||||
return 0, errors.New("RawReader.ReadAt: negative offset")
|
||||
}
|
||||
reqLen := len(p)
|
||||
buffLen := int64(f.Length)
|
||||
if off >= buffLen {
|
||||
return 0, io.EOF
|
||||
}
|
||||
|
||||
n = copy(p, f.Data[off:])
|
||||
if n < reqLen {
|
||||
err = io.EOF
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
*~
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
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.
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
# purego
|
||||
[](https://pkg.go.dev/github.com/ebitengine/purego?GOOS=darwin)
|
||||
|
||||
A library for calling C functions from Go without Cgo.
|
||||
|
||||
## Motivation
|
||||
|
||||
The [Ebitengine](https://github.com/hajimehoshi/ebiten) game engine was ported to use only Go on Windows. This enabled
|
||||
cross-compiling to Windows from any other operating system simply by setting `GOOS=windows`. The purego project was
|
||||
born to bring that same vision to the other platforms supported by Ebitengine.
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Simple Cross-Compilation**: No C means you can build for other platforms easily without a C compiler.
|
||||
- **Faster Compilation**: Efficiently cache your entirely Go builds.
|
||||
- **Smaller Binaries**: Using Cgo generates a C wrapper function for each C function called. Purego doesn't!
|
||||
- **Dynamic Linking**: Load symbols at runtime and use it as a plugin system.
|
||||
- **Foreign Function Interface**: Call into other languages that are compiled into shared objects.
|
||||
|
||||
## Example
|
||||
|
||||
This example only works on macOS and Linux. For a complete example look at [libc](https://github.com/ebitengine/purego/tree/main/examples/libc) which supports Windows and FreeBSD.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/ebitengine/purego"
|
||||
)
|
||||
|
||||
func getSystemLibrary() string {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
return "/usr/lib/libSystem.B.dylib"
|
||||
case "linux":
|
||||
return "libc.so.6"
|
||||
default:
|
||||
panic(fmt.Errorf("GOOS=%s is not supported", runtime.GOOS))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
libc, err := purego.Dlopen(getSystemLibrary(), purego.RTLD_NOW|purego.RTLD_GLOBAL)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var puts func(string)
|
||||
purego.RegisterLibFunc(&puts, libc, "puts")
|
||||
puts("Calling C from Go without Cgo!")
|
||||
}
|
||||
```
|
||||
|
||||
Then to run: `CGO_ENABLED=0 go run main.go`
|
||||
|
||||
### External Code
|
||||
|
||||
Purego uses code that originates from the Go runtime. These files are under the BSD-3
|
||||
License that can be found [in the Go Source](https://github.com/golang/go/blob/master/LICENSE).
|
||||
This is a list of the copied files:
|
||||
|
||||
* `abi_*.h` from package `runtime/cgo`
|
||||
* `zcallback_darwin_*.s` from package `runtime`
|
||||
* `internal/fakecgo/abi_*.h` from package `runtime/cgo`
|
||||
* `internal/fakecgo/asm_GOARCH.s` from package `runtime/cgo`
|
||||
* `internal/fakecgo/callbacks.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/go_GOOS_GOARCH.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/iscgo.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/setenv.go` from package `runtime/cgo`
|
||||
* `internal/fakecgo/freebsd.go` from package `runtime/cgo`
|
||||
|
||||
The files `abi_*.h` and `internal/fakecgo/abi_*.h` are the same because Bazel does not support cross-package use of `#include` so we need each one once per package. (cf. [issue](https://github.com/bazelbuild/rules_go/issues/3636))
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These save the frame pointer, so in general, functions that use
|
||||
// these should have zero frame size to suppress the automatic frame
|
||||
// pointer, though it's harmless to not do this.
|
||||
|
||||
#ifdef GOOS_windows
|
||||
|
||||
// REGS_HOST_TO_ABI0_STACK is the stack bytes used by
|
||||
// PUSH_REGS_HOST_TO_ABI0.
|
||||
#define REGS_HOST_TO_ABI0_STACK (28*8 + 8)
|
||||
|
||||
// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from
|
||||
// the host ABI to Go ABI0 code. It saves all registers that are
|
||||
// callee-save in the host ABI and caller-save in Go ABI0 and prepares
|
||||
// for entry to Go.
|
||||
//
|
||||
// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag.
|
||||
// Clear the DF flag for the Go ABI.
|
||||
// MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
PUSHFQ \
|
||||
CLD \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
MOVQ DI, (0*0)(SP) \
|
||||
MOVQ SI, (1*8)(SP) \
|
||||
MOVQ BP, (2*8)(SP) \
|
||||
MOVQ BX, (3*8)(SP) \
|
||||
MOVQ R12, (4*8)(SP) \
|
||||
MOVQ R13, (5*8)(SP) \
|
||||
MOVQ R14, (6*8)(SP) \
|
||||
MOVQ R15, (7*8)(SP) \
|
||||
MOVUPS X6, (8*8)(SP) \
|
||||
MOVUPS X7, (10*8)(SP) \
|
||||
MOVUPS X8, (12*8)(SP) \
|
||||
MOVUPS X9, (14*8)(SP) \
|
||||
MOVUPS X10, (16*8)(SP) \
|
||||
MOVUPS X11, (18*8)(SP) \
|
||||
MOVUPS X12, (20*8)(SP) \
|
||||
MOVUPS X13, (22*8)(SP) \
|
||||
MOVUPS X14, (24*8)(SP) \
|
||||
MOVUPS X15, (26*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*0)(SP), DI \
|
||||
MOVQ (1*8)(SP), SI \
|
||||
MOVQ (2*8)(SP), BP \
|
||||
MOVQ (3*8)(SP), BX \
|
||||
MOVQ (4*8)(SP), R12 \
|
||||
MOVQ (5*8)(SP), R13 \
|
||||
MOVQ (6*8)(SP), R14 \
|
||||
MOVQ (7*8)(SP), R15 \
|
||||
MOVUPS (8*8)(SP), X6 \
|
||||
MOVUPS (10*8)(SP), X7 \
|
||||
MOVUPS (12*8)(SP), X8 \
|
||||
MOVUPS (14*8)(SP), X9 \
|
||||
MOVUPS (16*8)(SP), X10 \
|
||||
MOVUPS (18*8)(SP), X11 \
|
||||
MOVUPS (20*8)(SP), X12 \
|
||||
MOVUPS (22*8)(SP), X13 \
|
||||
MOVUPS (24*8)(SP), X14 \
|
||||
MOVUPS (26*8)(SP), X15 \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
POPFQ
|
||||
|
||||
#else
|
||||
// SysV ABI
|
||||
|
||||
#define REGS_HOST_TO_ABI0_STACK (6*8)
|
||||
|
||||
// SysV MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
// Both SysV and Go require DF to be cleared, so that's already clear.
|
||||
// The SysV and Go frame pointer conventions are compatible.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK) \
|
||||
MOVQ BP, (5*8)(SP) \
|
||||
LEAQ (5*8)(SP), BP \
|
||||
MOVQ BX, (0*8)(SP) \
|
||||
MOVQ R12, (1*8)(SP) \
|
||||
MOVQ R13, (2*8)(SP) \
|
||||
MOVQ R14, (3*8)(SP) \
|
||||
MOVQ R15, (4*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*8)(SP), BX \
|
||||
MOVQ (1*8)(SP), R12 \
|
||||
MOVQ (2*8)(SP), R13 \
|
||||
MOVQ (3*8)(SP), R14 \
|
||||
MOVQ (4*8)(SP), R15 \
|
||||
MOVQ (5*8)(SP), BP \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK)
|
||||
|
||||
#endif
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These macros save and restore the callee-saved registers
|
||||
// from the stack, but they don't adjust stack pointer, so
|
||||
// the user should prepare stack space in advance.
|
||||
// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP).
|
||||
//
|
||||
// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP).
|
||||
//
|
||||
// R29 is not saved because Go will save and restore it.
|
||||
|
||||
#define SAVE_R19_TO_R28(offset) \
|
||||
STP (R19, R20), ((offset)+0*8)(RSP) \
|
||||
STP (R21, R22), ((offset)+2*8)(RSP) \
|
||||
STP (R23, R24), ((offset)+4*8)(RSP) \
|
||||
STP (R25, R26), ((offset)+6*8)(RSP) \
|
||||
STP (R27, g), ((offset)+8*8)(RSP)
|
||||
#define RESTORE_R19_TO_R28(offset) \
|
||||
LDP ((offset)+0*8)(RSP), (R19, R20) \
|
||||
LDP ((offset)+2*8)(RSP), (R21, R22) \
|
||||
LDP ((offset)+4*8)(RSP), (R23, R24) \
|
||||
LDP ((offset)+6*8)(RSP), (R25, R26) \
|
||||
LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */
|
||||
#define SAVE_F8_TO_F15(offset) \
|
||||
FSTPD (F8, F9), ((offset)+0*8)(RSP) \
|
||||
FSTPD (F10, F11), ((offset)+2*8)(RSP) \
|
||||
FSTPD (F12, F13), ((offset)+4*8)(RSP) \
|
||||
FSTPD (F14, F15), ((offset)+6*8)(RSP)
|
||||
#define RESTORE_F8_TO_F15(offset) \
|
||||
FLDPD ((offset)+0*8)(RSP), (F8, F9) \
|
||||
FLDPD ((offset)+2*8)(RSP), (F10, F11) \
|
||||
FLDPD ((offset)+4*8)(RSP), (F12, F13) \
|
||||
FLDPD ((offset)+6*8)(RSP), (F14, F15)
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build cgo && (darwin || freebsd || linux)
|
||||
|
||||
package purego
|
||||
|
||||
// if CGO_ENABLED=1 import the Cgo runtime to ensure that it is set up properly.
|
||||
// This is required since some frameworks need TLS setup the C way which Go doesn't do.
|
||||
// We currently don't support ios in fakecgo mode so force Cgo or fail
|
||||
// Even if CGO_ENABLED=1 the Cgo runtime is not imported unless `import "C"` is used.
|
||||
// which will import this package automatically. Normally this isn't an issue since it
|
||||
// usually isn't possible to call into C without using that import. However, with purego
|
||||
// it is since we don't use `import "C"`!
|
||||
import _ "runtime/cgo"
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package purego
|
||||
|
||||
// Dlerror represents an error value returned from Dlopen, Dlsym, or Dlclose.
|
||||
type Dlerror struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (e Dlerror) Error() string {
|
||||
return e.s
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Unix Specification for dlfcn.h: https://pubs.opengroup.org/onlinepubs/7908799/xsh/dlfcn.h.html
|
||||
|
||||
var (
|
||||
fnDlopen func(path string, mode int) uintptr
|
||||
fnDlsym func(handle uintptr, name string) uintptr
|
||||
fnDlerror func() string
|
||||
fnDlclose func(handle uintptr) bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterFunc(&fnDlopen, dlopenABI0)
|
||||
RegisterFunc(&fnDlsym, dlsymABI0)
|
||||
RegisterFunc(&fnDlerror, dlerrorABI0)
|
||||
RegisterFunc(&fnDlclose, dlcloseABI0)
|
||||
}
|
||||
|
||||
// Dlopen examines the dynamic library or bundle file specified by path. If the file is compatible
|
||||
// with the current process and has not already been loaded into the
|
||||
// current process, it is loaded and linked. After being linked, if it contains
|
||||
// any initializer functions, they are called, before Dlopen
|
||||
// returns. It returns a handle that can be used with Dlsym and Dlclose.
|
||||
// A second call to Dlopen with the same path will return the same handle, but the internal
|
||||
// reference count for the handle will be incremented. Therefore, all
|
||||
// Dlopen calls should be balanced with a Dlclose call.
|
||||
func Dlopen(path string, mode int) (uintptr, error) {
|
||||
u := fnDlopen(path, mode)
|
||||
if u == 0 {
|
||||
return 0, Dlerror{fnDlerror()}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Dlsym takes a "handle" of a dynamic library returned by Dlopen and the symbol name.
|
||||
// It returns the address where that symbol is loaded into memory. If the symbol is not found,
|
||||
// in the specified library or any of the libraries that were automatically loaded by Dlopen
|
||||
// when that library was loaded, Dlsym returns zero.
|
||||
func Dlsym(handle uintptr, name string) (uintptr, error) {
|
||||
u := fnDlsym(handle, name)
|
||||
if u == 0 {
|
||||
return 0, Dlerror{fnDlerror()}
|
||||
}
|
||||
return u, nil
|
||||
}
|
||||
|
||||
// Dlclose decrements the reference count on the dynamic library handle.
|
||||
// If the reference count drops to zero and no other loaded libraries
|
||||
// use symbols in it, then the dynamic library is unloaded.
|
||||
func Dlclose(handle uintptr) error {
|
||||
if fnDlclose(handle) {
|
||||
return Dlerror{fnDlerror()}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//go:linkname openLibrary openLibrary
|
||||
func openLibrary(name string) (uintptr, error) {
|
||||
return Dlopen(name, RTLD_NOW|RTLD_GLOBAL)
|
||||
}
|
||||
|
||||
func loadSymbol(handle uintptr, name string) (uintptr, error) {
|
||||
return Dlsym(handle, name)
|
||||
}
|
||||
|
||||
// these functions exist in dlfcn_stubs.s and are calling C functions linked to in dlfcn_GOOS.go
|
||||
// the indirection is necessary because a function is actually a pointer to the pointer to the code.
|
||||
// sadly, I do not know of anyway to remove the assembly stubs entirely because //go:linkname doesn't
|
||||
// appear to work if you link directly to the C function on darwin arm64.
|
||||
|
||||
//go:linkname dlopen dlopen
|
||||
var dlopen uintptr
|
||||
var dlopenABI0 = uintptr(unsafe.Pointer(&dlopen))
|
||||
|
||||
//go:linkname dlsym dlsym
|
||||
var dlsym uintptr
|
||||
var dlsymABI0 = uintptr(unsafe.Pointer(&dlsym))
|
||||
|
||||
//go:linkname dlclose dlclose
|
||||
var dlclose uintptr
|
||||
var dlcloseABI0 = uintptr(unsafe.Pointer(&dlclose))
|
||||
|
||||
//go:linkname dlerror dlerror
|
||||
var dlerror uintptr
|
||||
var dlerrorABI0 = uintptr(unsafe.Pointer(&dlerror))
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
// Source for constants: https://opensource.apple.com/source/dyld/dyld-360.14/include/dlfcn.h.auto.html
|
||||
|
||||
const (
|
||||
RTLD_DEFAULT = ^uintptr(0) - 1 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||
RTLD_LAZY = 0x1 // Relocations are performed at an implementation-dependent time.
|
||||
RTLD_NOW = 0x2 // Relocations are performed when the object is loaded.
|
||||
RTLD_LOCAL = 0x4 // All symbols are not made available for relocation processing by other modules.
|
||||
RTLD_GLOBAL = 0x8 // All symbols are available for relocation processing of other modules.
|
||||
)
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "/usr/lib/libSystem.B.dylib"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
// Constants as defined in https://github.com/freebsd/freebsd-src/blob/main/include/dlfcn.h
|
||||
const (
|
||||
RTLD_DEFAULT = ^uintptr(0) - 2 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||
RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time.
|
||||
RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded.
|
||||
RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules.
|
||||
RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules.
|
||||
)
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "libc.so.7"
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package purego
|
||||
|
||||
// Source for constants: https://codebrowser.dev/glibc/glibc/bits/dlfcn.h.html
|
||||
|
||||
const (
|
||||
RTLD_DEFAULT = 0x00000 // Pseudo-handle for dlsym so search for any loaded symbol
|
||||
RTLD_LAZY = 0x00001 // Relocations are performed at an implementation-dependent time.
|
||||
RTLD_NOW = 0x00002 // Relocations are performed when the object is loaded.
|
||||
RTLD_LOCAL = 0x00000 // All symbols are not made available for relocation processing by other modules.
|
||||
RTLD_GLOBAL = 0x00100 // All symbols are available for relocation processing of other modules.
|
||||
)
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package purego
|
||||
|
||||
// if there is no Cgo we must link to each of the functions from dlfcn.h
|
||||
// then the functions are called inside dlfcn_stubs.s
|
||||
|
||||
//go:cgo_import_dynamic purego_dlopen dlopen "libdl.so.2"
|
||||
//go:cgo_import_dynamic purego_dlsym dlsym "libdl.so.2"
|
||||
//go:cgo_import_dynamic purego_dlerror dlerror "libdl.so.2"
|
||||
//go:cgo_import_dynamic purego_dlclose dlclose "libdl.so.2"
|
||||
|
||||
// on amd64 we don't need the following line - on 386 we do...
|
||||
// anyway - with those lines the output is better (but doesn't matter) - without it on amd64 we get multiple DT_NEEDED with "libc.so.6" etc
|
||||
|
||||
//go:cgo_import_dynamic _ _ "libdl.so.2"
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || (linux && !cgo)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func dlopen(path *byte, mode int) (ret uintptr)
|
||||
TEXT dlopen(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlopen(SB)
|
||||
RET
|
||||
|
||||
// func dlsym(handle uintptr, symbol *byte) (ret uintptr)
|
||||
TEXT dlsym(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlsym(SB)
|
||||
RET
|
||||
|
||||
// func dlerror() (ret *byte)
|
||||
TEXT dlerror(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlerror(SB)
|
||||
RET
|
||||
|
||||
// func dlclose(handle uintptr) (ret int)
|
||||
TEXT dlclose(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_dlclose(SB)
|
||||
RET
|
||||
+309
@@ -0,0 +1,309 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || windows
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ebitengine/purego/internal/strings"
|
||||
)
|
||||
|
||||
// RegisterLibFunc is a wrapper around RegisterFunc that uses the C function returned from Dlsym(handle, name).
|
||||
// It panics if it can't find the name symbol.
|
||||
func RegisterLibFunc(fptr interface{}, handle uintptr, name string) {
|
||||
sym, err := loadSymbol(handle, name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
RegisterFunc(fptr, sym)
|
||||
}
|
||||
|
||||
// RegisterFunc takes a pointer to a Go function representing the calling convention of the C function.
|
||||
// fptr will be set to a function that when called will call the C function given by cfn with the
|
||||
// parameters passed in the correct registers and stack.
|
||||
//
|
||||
// A panic is produced if the type is not a function pointer or if the function returns more than 1 value.
|
||||
//
|
||||
// These conversions describe how a Go type in the fptr will be used to call
|
||||
// the C function. It is important to note that there is no way to verify that fptr
|
||||
// matches the C function. This also holds true for struct types where the padding
|
||||
// needs to be ensured to match that of C; RegisterFunc does not verify this.
|
||||
//
|
||||
// # Type Conversions (Go <=> C)
|
||||
//
|
||||
// string <=> char*
|
||||
// bool <=> _Bool
|
||||
// uintptr <=> uintptr_t
|
||||
// uint <=> uint32_t or uint64_t
|
||||
// uint8 <=> uint8_t
|
||||
// uint16 <=> uint16_t
|
||||
// uint32 <=> uint32_t
|
||||
// uint64 <=> uint64_t
|
||||
// int <=> int32_t or int64_t
|
||||
// int8 <=> int8_t
|
||||
// int16 <=> int16_t
|
||||
// int32 <=> int32_t
|
||||
// int64 <=> int64_t
|
||||
// float32 <=> float (WIP)
|
||||
// float64 <=> double (WIP)
|
||||
// struct <=> struct (WIP)
|
||||
// func <=> C function
|
||||
// unsafe.Pointer, *T <=> void*
|
||||
// []T => void*
|
||||
//
|
||||
// There is a special case when the last argument of fptr is a variadic interface (or []interface}
|
||||
// it will be expanded into a call to the C function as if it had the arguments in that slice.
|
||||
// This means that using arg ...interface{} is like a cast to the function with the arguments inside arg.
|
||||
// This is not the same as C variadic.
|
||||
//
|
||||
// There are some limitations when using RegisterFunc on Linux. First, there is no support for function arguments.
|
||||
// Second, float32 and float64 arguments and return values do not work when CGO_ENABLED=1. Otherwise, Linux
|
||||
// has the same feature parity as Darwin.
|
||||
//
|
||||
// # Memory
|
||||
//
|
||||
// In general it is not possible for purego to guarantee the lifetimes of objects returned or received from
|
||||
// calling functions using RegisterFunc. For arguments to a C function it is important that the C function doesn't
|
||||
// hold onto a reference to Go memory. This is the same as the [Cgo rules].
|
||||
//
|
||||
// However, there are some special cases. When passing a string as an argument if the string does not end in a null
|
||||
// terminated byte (\x00) then the string will be copied into memory maintained by purego. The memory is only valid for
|
||||
// that specific call. Therefore, if the C code keeps a reference to that string it may become invalid at some
|
||||
// undefined time. However, if the string does already contain a null-terminated byte then no copy is done.
|
||||
// It is then the responsibility of the caller to ensure the string stays alive as long as it's needed in C memory.
|
||||
// This can be done using runtime.KeepAlive or allocating the string in C memory using malloc. When a C function
|
||||
// returns a null-terminated pointer to char a Go string can be used. Purego will allocate a new string in Go memory
|
||||
// and copy the data over. This string will be garbage collected whenever Go decides it's no longer referenced.
|
||||
// This C created string will not be freed by purego. If the pointer to char is not null-terminated or must continue
|
||||
// to point to C memory (because it's a buffer for example) then use a pointer to byte and then convert that to a slice
|
||||
// using unsafe.Slice. Doing this means that it becomes the responsibility of the caller to care about the lifetime
|
||||
// of the pointer
|
||||
//
|
||||
// # Example
|
||||
//
|
||||
// All functions below call this C function:
|
||||
//
|
||||
// char *foo(char *str);
|
||||
//
|
||||
// // Let purego convert types
|
||||
// var foo func(s string) string
|
||||
// goString := foo("copied")
|
||||
// // Go will garbage collect this string
|
||||
//
|
||||
// // Manually, handle allocations
|
||||
// var foo2 func(b string) *byte
|
||||
// mustFree := foo2("not copied\x00")
|
||||
// defer free(mustFree)
|
||||
//
|
||||
// [Cgo rules]: https://pkg.go.dev/cmd/cgo#hdr-Go_references_to_C
|
||||
func RegisterFunc(fptr interface{}, cfn uintptr) {
|
||||
fn := reflect.ValueOf(fptr).Elem()
|
||||
ty := fn.Type()
|
||||
if ty.Kind() != reflect.Func {
|
||||
panic("purego: fptr must be a function pointer")
|
||||
}
|
||||
if ty.NumOut() > 1 {
|
||||
panic("purego: function can only return zero or one values")
|
||||
}
|
||||
if cfn == 0 {
|
||||
panic("purego: cfn is nil")
|
||||
}
|
||||
{
|
||||
// this code checks how many registers and stack this function will use
|
||||
// to avoid crashing with too many arguments
|
||||
var ints int
|
||||
var floats int
|
||||
var stack int
|
||||
for i := 0; i < ty.NumIn(); i++ {
|
||||
arg := ty.In(i)
|
||||
switch arg.Kind() {
|
||||
case reflect.String, reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Ptr, reflect.UnsafePointer, reflect.Slice,
|
||||
reflect.Func, reflect.Bool:
|
||||
if ints < numOfIntegerRegisters() {
|
||||
ints++
|
||||
} else {
|
||||
stack++
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if floats < numOfFloats {
|
||||
floats++
|
||||
} else {
|
||||
stack++
|
||||
}
|
||||
default:
|
||||
panic("purego: unsupported kind " + arg.Kind().String())
|
||||
}
|
||||
}
|
||||
sizeOfStack := maxArgs - numOfIntegerRegisters()
|
||||
if stack > sizeOfStack {
|
||||
panic("purego: too many arguments")
|
||||
}
|
||||
}
|
||||
v := reflect.MakeFunc(ty, func(args []reflect.Value) (results []reflect.Value) {
|
||||
if len(args) > 0 {
|
||||
if variadic, ok := args[len(args)-1].Interface().([]interface{}); ok {
|
||||
// subtract one from args bc the last argument in args is []interface{}
|
||||
// which we are currently expanding
|
||||
tmp := make([]reflect.Value, len(args)-1+len(variadic))
|
||||
n := copy(tmp, args[:len(args)-1])
|
||||
for i, v := range variadic {
|
||||
tmp[n+i] = reflect.ValueOf(v)
|
||||
}
|
||||
args = tmp
|
||||
}
|
||||
}
|
||||
var sysargs [maxArgs]uintptr
|
||||
stack := sysargs[numOfIntegerRegisters():]
|
||||
var floats [numOfFloats]uintptr
|
||||
var numInts int
|
||||
var numFloats int
|
||||
var numStack int
|
||||
var addStack, addInt, addFloat func(x uintptr)
|
||||
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
||||
// Windows arm64 uses the same calling convention as macOS and Linux
|
||||
addStack = func(x uintptr) {
|
||||
stack[numStack] = x
|
||||
numStack++
|
||||
}
|
||||
addInt = func(x uintptr) {
|
||||
if numInts >= numOfIntegerRegisters() {
|
||||
addStack(x)
|
||||
} else {
|
||||
sysargs[numInts] = x
|
||||
numInts++
|
||||
}
|
||||
}
|
||||
addFloat = func(x uintptr) {
|
||||
if numFloats < len(floats) {
|
||||
floats[numFloats] = x
|
||||
numFloats++
|
||||
} else {
|
||||
addStack(x)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// On Windows amd64 the arguments are passed in the numbered registered.
|
||||
// So the first int is in the first integer register and the first float
|
||||
// is in the second floating register if there is already a first int.
|
||||
// This is in contrast to how macOS and Linux pass arguments which
|
||||
// tries to use as many registers as possible in the calling convention.
|
||||
addStack = func(x uintptr) {
|
||||
sysargs[numStack] = x
|
||||
numStack++
|
||||
}
|
||||
addInt = addStack
|
||||
addFloat = addStack
|
||||
}
|
||||
|
||||
var keepAlive []interface{}
|
||||
defer func() {
|
||||
runtime.KeepAlive(keepAlive)
|
||||
runtime.KeepAlive(args)
|
||||
}()
|
||||
for _, v := range args {
|
||||
switch v.Kind() {
|
||||
case reflect.String:
|
||||
ptr := strings.CString(v.String())
|
||||
keepAlive = append(keepAlive, ptr)
|
||||
addInt(uintptr(unsafe.Pointer(ptr)))
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
addInt(uintptr(v.Uint()))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
addInt(uintptr(v.Int()))
|
||||
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice:
|
||||
// There is no need to keepAlive this pointer separately because it is kept alive in the args variable
|
||||
addInt(v.Pointer())
|
||||
case reflect.Func:
|
||||
addInt(NewCallback(v.Interface()))
|
||||
case reflect.Bool:
|
||||
if v.Bool() {
|
||||
addInt(1)
|
||||
} else {
|
||||
addInt(0)
|
||||
}
|
||||
case reflect.Float32:
|
||||
addFloat(uintptr(math.Float32bits(float32(v.Float()))))
|
||||
case reflect.Float64:
|
||||
addFloat(uintptr(math.Float64bits(v.Float())))
|
||||
default:
|
||||
panic("purego: unsupported kind: " + v.Kind().String())
|
||||
}
|
||||
}
|
||||
// TODO: support structs
|
||||
var r1, r2 uintptr
|
||||
if runtime.GOARCH == "arm64" || runtime.GOOS != "windows" {
|
||||
// Use the normal arm64 calling convention even on Windows
|
||||
syscall := syscall9Args{
|
||||
cfn,
|
||||
sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], sysargs[6], sysargs[7], sysargs[8],
|
||||
floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7],
|
||||
0, 0, 0,
|
||||
}
|
||||
runtime_cgocall(syscall9XABI0, unsafe.Pointer(&syscall))
|
||||
r1, r2 = syscall.r1, syscall.r2
|
||||
} else {
|
||||
// This is a fallback for amd64, 386, and arm. Note this may not support floats
|
||||
r1, r2, _ = syscall_syscall9X(cfn, sysargs[0], sysargs[1], sysargs[2], sysargs[3], sysargs[4], sysargs[5], sysargs[6], sysargs[7], sysargs[8])
|
||||
}
|
||||
if ty.NumOut() == 0 {
|
||||
return nil
|
||||
}
|
||||
outType := ty.Out(0)
|
||||
v := reflect.New(outType).Elem()
|
||||
switch outType.Kind() {
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
v.SetUint(uint64(r1))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
v.SetInt(int64(r1))
|
||||
case reflect.Bool:
|
||||
v.SetBool(byte(r1) != 0)
|
||||
case reflect.UnsafePointer:
|
||||
// We take the address and then dereference it to trick go vet from creating a possible miss-use of unsafe.Pointer
|
||||
v.SetPointer(*(*unsafe.Pointer)(unsafe.Pointer(&r1)))
|
||||
case reflect.Ptr:
|
||||
// It is safe to have the address of r1 not escape because it is immediately dereferenced with .Elem()
|
||||
v = reflect.NewAt(outType, runtime_noescape(unsafe.Pointer(&r1))).Elem()
|
||||
case reflect.Func:
|
||||
// wrap this C function in a nicely typed Go function
|
||||
v = reflect.New(outType)
|
||||
RegisterFunc(v.Interface(), r1)
|
||||
case reflect.String:
|
||||
v.SetString(strings.GoString(r1))
|
||||
case reflect.Float32:
|
||||
// NOTE: r2 is only the floating return value on 64bit platforms.
|
||||
// On 32bit platforms r2 is the upper part of a 64bit return.
|
||||
v.SetFloat(float64(math.Float32frombits(uint32(r2))))
|
||||
case reflect.Float64:
|
||||
// NOTE: r2 is only the floating return value on 64bit platforms.
|
||||
// On 32bit platforms r2 is the upper part of a 64bit return.
|
||||
v.SetFloat(math.Float64frombits(uint64(r2)))
|
||||
default:
|
||||
panic("purego: unsupported return kind: " + outType.Kind().String())
|
||||
}
|
||||
return []reflect.Value{v}
|
||||
})
|
||||
fn.Set(v)
|
||||
}
|
||||
|
||||
func numOfIntegerRegisters() int {
|
||||
switch runtime.GOARCH {
|
||||
case "arm64":
|
||||
return 8
|
||||
case "amd64":
|
||||
return 6
|
||||
// TODO: figure out why 386 tests are not working
|
||||
/*case "386":
|
||||
return 0
|
||||
case "arm":
|
||||
return 4*/
|
||||
default:
|
||||
panic("purego: unknown GOARCH (" + runtime.GOARCH + ")")
|
||||
}
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux || windows
|
||||
|
||||
package purego
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
//go:linkname runtime_cgocall runtime.cgocall
|
||||
func runtime_cgocall(fn uintptr, arg unsafe.Pointer) int32 // from runtime/sys_libc.go
|
||||
|
||||
//go:linkname runtime_noescape runtime.noescape
|
||||
//go:noescape
|
||||
func runtime_noescape(p unsafe.Pointer) unsafe.Pointer // from runtime/stubs.go
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build freebsd || linux
|
||||
|
||||
package cgo
|
||||
|
||||
// this file is placed inside internal/cgo and not package purego
|
||||
// because Cgo and assembly files can't be in the same package.
|
||||
|
||||
/*
|
||||
#cgo LDFLAGS: -ldl
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
|
||||
typedef struct syscall9Args {
|
||||
uintptr_t fn;
|
||||
uintptr_t a1, a2, a3, a4, a5, a6, a7, a8, a9;
|
||||
uintptr_t f1, f2, f3, f4, f5, f6, f7, f8;
|
||||
uintptr_t r1, r2, err;
|
||||
} syscall9Args;
|
||||
|
||||
void syscall9(struct syscall9Args *args) {
|
||||
assert((args->f1|args->f2|args->f3|args->f4|args->f5|args->f6|args->f7|args->f8) == 0);
|
||||
uintptr_t (*func_name)(uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9);
|
||||
*(void**)(&func_name) = (void*)(args->fn);
|
||||
uintptr_t r1 = func_name(args->a1,args->a2,args->a3,args->a4,args->a5,args->a6,args->a7,args->a8,args->a9);
|
||||
args->r1 = r1;
|
||||
args->err = errno;
|
||||
}
|
||||
|
||||
*/
|
||||
import "C"
|
||||
import "unsafe"
|
||||
|
||||
// assign purego.syscall9XABI0 to the C version of this function.
|
||||
var Syscall9XABI0 = unsafe.Pointer(C.syscall9)
|
||||
|
||||
// all that is needed is to assign each dl function because then its
|
||||
// symbol will then be made available to the linker and linked to inside dlfcn.go
|
||||
var (
|
||||
_ = C.dlopen
|
||||
_ = C.dlsym
|
||||
_ = C.dlerror
|
||||
_ = C.dlclose
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
func Syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) {
|
||||
args := C.syscall9Args{C.uintptr_t(fn), C.uintptr_t(a1), C.uintptr_t(a2), C.uintptr_t(a3),
|
||||
C.uintptr_t(a4), C.uintptr_t(a5), C.uintptr_t(a6),
|
||||
C.uintptr_t(a7), C.uintptr_t(a8), C.uintptr_t(a9), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
C.syscall9(&args)
|
||||
return uintptr(args.r1), uintptr(args.r2), uintptr(args.err)
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These save the frame pointer, so in general, functions that use
|
||||
// these should have zero frame size to suppress the automatic frame
|
||||
// pointer, though it's harmless to not do this.
|
||||
|
||||
#ifdef GOOS_windows
|
||||
|
||||
// REGS_HOST_TO_ABI0_STACK is the stack bytes used by
|
||||
// PUSH_REGS_HOST_TO_ABI0.
|
||||
#define REGS_HOST_TO_ABI0_STACK (28*8 + 8)
|
||||
|
||||
// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from
|
||||
// the host ABI to Go ABI0 code. It saves all registers that are
|
||||
// callee-save in the host ABI and caller-save in Go ABI0 and prepares
|
||||
// for entry to Go.
|
||||
//
|
||||
// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag.
|
||||
// Clear the DF flag for the Go ABI.
|
||||
// MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
PUSHFQ \
|
||||
CLD \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
MOVQ DI, (0*0)(SP) \
|
||||
MOVQ SI, (1*8)(SP) \
|
||||
MOVQ BP, (2*8)(SP) \
|
||||
MOVQ BX, (3*8)(SP) \
|
||||
MOVQ R12, (4*8)(SP) \
|
||||
MOVQ R13, (5*8)(SP) \
|
||||
MOVQ R14, (6*8)(SP) \
|
||||
MOVQ R15, (7*8)(SP) \
|
||||
MOVUPS X6, (8*8)(SP) \
|
||||
MOVUPS X7, (10*8)(SP) \
|
||||
MOVUPS X8, (12*8)(SP) \
|
||||
MOVUPS X9, (14*8)(SP) \
|
||||
MOVUPS X10, (16*8)(SP) \
|
||||
MOVUPS X11, (18*8)(SP) \
|
||||
MOVUPS X12, (20*8)(SP) \
|
||||
MOVUPS X13, (22*8)(SP) \
|
||||
MOVUPS X14, (24*8)(SP) \
|
||||
MOVUPS X15, (26*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*0)(SP), DI \
|
||||
MOVQ (1*8)(SP), SI \
|
||||
MOVQ (2*8)(SP), BP \
|
||||
MOVQ (3*8)(SP), BX \
|
||||
MOVQ (4*8)(SP), R12 \
|
||||
MOVQ (5*8)(SP), R13 \
|
||||
MOVQ (6*8)(SP), R14 \
|
||||
MOVQ (7*8)(SP), R15 \
|
||||
MOVUPS (8*8)(SP), X6 \
|
||||
MOVUPS (10*8)(SP), X7 \
|
||||
MOVUPS (12*8)(SP), X8 \
|
||||
MOVUPS (14*8)(SP), X9 \
|
||||
MOVUPS (16*8)(SP), X10 \
|
||||
MOVUPS (18*8)(SP), X11 \
|
||||
MOVUPS (20*8)(SP), X12 \
|
||||
MOVUPS (22*8)(SP), X13 \
|
||||
MOVUPS (24*8)(SP), X14 \
|
||||
MOVUPS (26*8)(SP), X15 \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \
|
||||
POPFQ
|
||||
|
||||
#else
|
||||
// SysV ABI
|
||||
|
||||
#define REGS_HOST_TO_ABI0_STACK (6*8)
|
||||
|
||||
// SysV MXCSR matches the Go ABI, so we don't have to set that,
|
||||
// and Go doesn't modify it, so we don't have to save it.
|
||||
// Both SysV and Go require DF to be cleared, so that's already clear.
|
||||
// The SysV and Go frame pointer conventions are compatible.
|
||||
#define PUSH_REGS_HOST_TO_ABI0() \
|
||||
ADJSP $(REGS_HOST_TO_ABI0_STACK) \
|
||||
MOVQ BP, (5*8)(SP) \
|
||||
LEAQ (5*8)(SP), BP \
|
||||
MOVQ BX, (0*8)(SP) \
|
||||
MOVQ R12, (1*8)(SP) \
|
||||
MOVQ R13, (2*8)(SP) \
|
||||
MOVQ R14, (3*8)(SP) \
|
||||
MOVQ R15, (4*8)(SP)
|
||||
|
||||
#define POP_REGS_HOST_TO_ABI0() \
|
||||
MOVQ (0*8)(SP), BX \
|
||||
MOVQ (1*8)(SP), R12 \
|
||||
MOVQ (2*8)(SP), R13 \
|
||||
MOVQ (3*8)(SP), R14 \
|
||||
MOVQ (4*8)(SP), R15 \
|
||||
MOVQ (5*8)(SP), BP \
|
||||
ADJSP $-(REGS_HOST_TO_ABI0_STACK)
|
||||
|
||||
#endif
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Macros for transitioning from the host ABI to Go ABI0.
|
||||
//
|
||||
// These macros save and restore the callee-saved registers
|
||||
// from the stack, but they don't adjust stack pointer, so
|
||||
// the user should prepare stack space in advance.
|
||||
// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP).
|
||||
//
|
||||
// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space
|
||||
// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP).
|
||||
//
|
||||
// R29 is not saved because Go will save and restore it.
|
||||
|
||||
#define SAVE_R19_TO_R28(offset) \
|
||||
STP (R19, R20), ((offset)+0*8)(RSP) \
|
||||
STP (R21, R22), ((offset)+2*8)(RSP) \
|
||||
STP (R23, R24), ((offset)+4*8)(RSP) \
|
||||
STP (R25, R26), ((offset)+6*8)(RSP) \
|
||||
STP (R27, g), ((offset)+8*8)(RSP)
|
||||
#define RESTORE_R19_TO_R28(offset) \
|
||||
LDP ((offset)+0*8)(RSP), (R19, R20) \
|
||||
LDP ((offset)+2*8)(RSP), (R21, R22) \
|
||||
LDP ((offset)+4*8)(RSP), (R23, R24) \
|
||||
LDP ((offset)+6*8)(RSP), (R25, R26) \
|
||||
LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */
|
||||
#define SAVE_F8_TO_F15(offset) \
|
||||
FSTPD (F8, F9), ((offset)+0*8)(RSP) \
|
||||
FSTPD (F10, F11), ((offset)+2*8)(RSP) \
|
||||
FSTPD (F12, F13), ((offset)+4*8)(RSP) \
|
||||
FSTPD (F14, F15), ((offset)+6*8)(RSP)
|
||||
#define RESTORE_F8_TO_F15(offset) \
|
||||
FLDPD ((offset)+0*8)(RSP), (F8, F9) \
|
||||
FLDPD ((offset)+2*8)(RSP), (F10, F11) \
|
||||
FLDPD ((offset)+4*8)(RSP), (F12, F13) \
|
||||
FLDPD ((offset)+6*8)(RSP), (F14, F15)
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "abi_amd64.h"
|
||||
|
||||
// Called by C code generated by cmd/cgo.
|
||||
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
|
||||
// Saves C callee-saved registers and calls cgocallback with three arguments.
|
||||
// fn is the PC of a func(a unsafe.Pointer) function.
|
||||
// This signature is known to SWIG, so we can't change it.
|
||||
TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0-0
|
||||
PUSH_REGS_HOST_TO_ABI0()
|
||||
|
||||
// Make room for arguments to cgocallback.
|
||||
ADJSP $0x18
|
||||
|
||||
#ifndef GOOS_windows
|
||||
MOVQ DI, 0x0(SP) // fn
|
||||
MOVQ SI, 0x8(SP) // arg
|
||||
|
||||
// Skip n in DX.
|
||||
MOVQ CX, 0x10(SP) // ctxt
|
||||
|
||||
#else
|
||||
MOVQ CX, 0x0(SP) // fn
|
||||
MOVQ DX, 0x8(SP) // arg
|
||||
|
||||
// Skip n in R8.
|
||||
MOVQ R9, 0x10(SP) // ctxt
|
||||
|
||||
#endif
|
||||
|
||||
CALL runtime·cgocallback(SB)
|
||||
|
||||
ADJSP $-0x18
|
||||
POP_REGS_HOST_TO_ABI0()
|
||||
RET
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "abi_arm64.h"
|
||||
|
||||
// Called by C code generated by cmd/cgo.
|
||||
// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr)
|
||||
// Saves C callee-saved registers and calls cgocallback with three arguments.
|
||||
// fn is the PC of a func(a unsafe.Pointer) function.
|
||||
TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0
|
||||
/*
|
||||
* We still need to save all callee save register as before, and then
|
||||
* push 3 args for fn (R0, R1, R3), skipping R2.
|
||||
* Also note that at procedure entry in gc world, 8(RSP) will be the
|
||||
* first arg.
|
||||
*/
|
||||
SUB $(8*24), RSP
|
||||
STP (R0, R1), (8*1)(RSP)
|
||||
MOVD R3, (8*3)(RSP)
|
||||
|
||||
SAVE_R19_TO_R28(8*4)
|
||||
SAVE_F8_TO_F15(8*14)
|
||||
STP (R29, R30), (8*22)(RSP)
|
||||
|
||||
// Initialize Go ABI environment
|
||||
BL runtime·load_g(SB)
|
||||
BL runtime·cgocallback(SB)
|
||||
|
||||
RESTORE_R19_TO_R28(8*4)
|
||||
RESTORE_F8_TO_F15(8*14)
|
||||
LDP (8*22)(RSP), (R29, R30)
|
||||
|
||||
ADD $(8*24), RSP
|
||||
RET
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package fakecgo
|
||||
|
||||
import (
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
// TODO: decide if we need _runtime_cgo_panic_internal
|
||||
|
||||
//go:linkname x_cgo_init_trampoline x_cgo_init_trampoline
|
||||
//go:linkname _cgo_init _cgo_init
|
||||
var x_cgo_init_trampoline byte
|
||||
var _cgo_init = &x_cgo_init_trampoline
|
||||
|
||||
// Creates a new system thread without updating any Go state.
|
||||
//
|
||||
// This method is invoked during shared library loading to create a new OS
|
||||
// thread to perform the runtime initialization. This method is similar to
|
||||
// _cgo_sys_thread_start except that it doesn't update any Go state.
|
||||
|
||||
//go:linkname x_cgo_thread_start_trampoline x_cgo_thread_start_trampoline
|
||||
//go:linkname _cgo_thread_start _cgo_thread_start
|
||||
var x_cgo_thread_start_trampoline byte
|
||||
var _cgo_thread_start = &x_cgo_thread_start_trampoline
|
||||
|
||||
// Notifies that the runtime has been initialized.
|
||||
//
|
||||
// We currently block at every CGO entry point (via _cgo_wait_runtime_init_done)
|
||||
// to ensure that the runtime has been initialized before the CGO call is
|
||||
// executed. This is necessary for shared libraries where we kickoff runtime
|
||||
// initialization in a separate thread and return without waiting for this
|
||||
// thread to complete the init.
|
||||
|
||||
//go:linkname x_cgo_notify_runtime_init_done_trampoline x_cgo_notify_runtime_init_done_trampoline
|
||||
//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done
|
||||
var x_cgo_notify_runtime_init_done_trampoline byte
|
||||
var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done_trampoline
|
||||
|
||||
// Indicates whether a dummy thread key has been created or not.
|
||||
//
|
||||
// When calling go exported function from C, we register a destructor
|
||||
// callback, for a dummy thread key, by using pthread_key_create.
|
||||
|
||||
//go:linkname _cgo_pthread_key_created _cgo_pthread_key_created
|
||||
var x_cgo_pthread_key_created uintptr
|
||||
var _cgo_pthread_key_created = &x_cgo_pthread_key_created
|
||||
|
||||
// Set the x_crosscall2_ptr C function pointer variable point to crosscall2.
|
||||
// It's for the runtime package to call at init time.
|
||||
func set_crosscall2() {
|
||||
// nothing needs to be done here for fakecgo
|
||||
// because it's possible to just call cgocallback directly
|
||||
}
|
||||
|
||||
//go:linkname _set_crosscall2 runtime.set_crosscall2
|
||||
var _set_crosscall2 = set_crosscall2
|
||||
|
||||
// Store the g into the thread-specific value.
|
||||
// So that pthread_key_destructor will dropm when the thread is exiting.
|
||||
|
||||
//go:linkname x_cgo_bindm_trampoline x_cgo_bindm_trampoline
|
||||
//go:linkname _cgo_bindm _cgo_bindm
|
||||
var x_cgo_bindm_trampoline byte
|
||||
var _cgo_bindm = &x_cgo_bindm_trampoline
|
||||
|
||||
// TODO: decide if we need x_cgo_set_context_function
|
||||
// TODO: decide if we need _cgo_yield
|
||||
|
||||
var (
|
||||
// In Go 1.20 the race detector was rewritten to pure Go
|
||||
// on darwin. This means that when CGO_ENABLED=0 is set
|
||||
// fakecgo is built with race detector code. This is not
|
||||
// good since this code is pretending to be C. The go:norace
|
||||
// pragma is not enough, since it only applies to the native
|
||||
// ABIInternal function. The ABIO wrapper (which is necessary,
|
||||
// since all references to text symbols from assembly will use it)
|
||||
// does not inherit the go:norace pragma, so it will still be
|
||||
// instrumented by the race detector.
|
||||
//
|
||||
// To circumvent this issue, using closure calls in the
|
||||
// assembly, which forces the compiler to use the ABIInternal
|
||||
// native implementation (which has go:norace) instead.
|
||||
threadentry_call = threadentry
|
||||
x_cgo_init_call = x_cgo_init
|
||||
x_cgo_setenv_call = x_cgo_setenv
|
||||
x_cgo_unsetenv_call = x_cgo_unsetenv
|
||||
x_cgo_thread_start_call = x_cgo_thread_start
|
||||
)
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd
|
||||
|
||||
// Package fakecgo implements the Cgo runtime (runtime/cgo) entirely in Go.
|
||||
// This allows code that calls into C to function properly when CGO_ENABLED=0.
|
||||
//
|
||||
// # Goals
|
||||
//
|
||||
// fakecgo attempts to replicate the same naming structure as in the runtime.
|
||||
// For example, functions that have the prefix "gcc_*" are named "go_*".
|
||||
// This makes it easier to port other GOOSs and GOARCHs as well as to keep
|
||||
// it in sync with runtime/cgo.
|
||||
//
|
||||
// # Support
|
||||
//
|
||||
// Currently, fakecgo only supports macOS on amd64 & arm64. It also cannot
|
||||
// be used with -buildmode=c-archive because that requires special initialization
|
||||
// that fakecgo does not implement at the moment.
|
||||
//
|
||||
// # Usage
|
||||
//
|
||||
// Using fakecgo is easy just import _ "github.com/ebitengine/purego" and then
|
||||
// set the environment variable CGO_ENABLED=0.
|
||||
// The recommended usage for fakecgo is to prefer using runtime/cgo if possible
|
||||
// but if cross-compiling or fast build times are important fakecgo is available.
|
||||
// Purego will pick which ever Cgo runtime is available and prefer the one that
|
||||
// comes with Go (runtime/cgo).
|
||||
package fakecgo
|
||||
|
||||
//go:generate go run gen.go
|
||||
//go:generate gofmt -s -w symbols.go
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build freebsd && !cgo
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
// Supply environ and __progname, because we don't
|
||||
// link against the standard FreeBSD crt0.o and the
|
||||
// libc dynamic library needs them.
|
||||
|
||||
// Note: when building with cross-compiling or CGO_ENABLED=0, add
|
||||
// the following argument to `go` so that these symbols are defined by
|
||||
// making fakecgo the Cgo.
|
||||
// -gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"
|
||||
|
||||
//go:linkname _environ environ
|
||||
//go:linkname _progname __progname
|
||||
|
||||
//go:cgo_export_dynamic environ
|
||||
//go:cgo_export_dynamic __progname
|
||||
|
||||
var _environ uintptr
|
||||
var _progname uintptr
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_setstacksize(&attr, size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
|
||||
setg_func = setg
|
||||
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096))
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_setstacksize(&attr, size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
// TODO: support ios
|
||||
//#if TARGET_OS_IPHONE
|
||||
// darwin_arm_init_thread_exception_port();
|
||||
//#endif
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
|
||||
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
|
||||
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
|
||||
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
|
||||
//
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
|
||||
setg_func = setg
|
||||
size = pthread_get_stacksize_np(pthread_self())
|
||||
g.stacklo = uintptr(unsafe.Add(unsafe.Pointer(&size), -size+4096))
|
||||
|
||||
//TODO: support ios
|
||||
//#if TARGET_OS_IPHONE
|
||||
// darwin_arm_init_mach_exception_handler();
|
||||
// darwin_arm_init_thread_exception_port();
|
||||
// init_working_dir();
|
||||
//#endif
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
|
||||
// but this should be OK since we are taking the address of the first variable in this function.
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
|
||||
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
|
||||
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
|
||||
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
|
||||
//
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package fakecgo
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
pthread_g pthread_key_t
|
||||
|
||||
runtime_init_cond = PTHREAD_COND_INITIALIZER
|
||||
runtime_init_mu = PTHREAD_MUTEX_INITIALIZER
|
||||
runtime_init_done int
|
||||
)
|
||||
|
||||
//go:nosplit
|
||||
func x_cgo_notify_runtime_init_done() {
|
||||
pthread_mutex_lock(&runtime_init_mu)
|
||||
runtime_init_done = 1
|
||||
pthread_cond_broadcast(&runtime_init_cond)
|
||||
pthread_mutex_unlock(&runtime_init_mu)
|
||||
}
|
||||
|
||||
// Store the g into a thread-specific value associated with the pthread key pthread_g.
|
||||
// And pthread_key_destructor will dropm when the thread is exiting.
|
||||
func x_cgo_bindm(g unsafe.Pointer) {
|
||||
// We assume this will always succeed, otherwise, there might be extra M leaking,
|
||||
// when a C thread exits after a cgo call.
|
||||
// We only invoke this function once per thread in runtime.needAndBindM,
|
||||
// and the next calls just reuse the bound m.
|
||||
pthread_setspecific(pthread_g, g)
|
||||
}
|
||||
|
||||
// _cgo_try_pthread_create retries pthread_create if it fails with
|
||||
// EAGAIN.
|
||||
//
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func _cgo_try_pthread_create(thread *pthread_t, attr *pthread_attr_t, pfn unsafe.Pointer, arg *ThreadStart) int {
|
||||
var ts syscall.Timespec
|
||||
// tries needs to be the same type as syscall.Timespec.Nsec
|
||||
// but the fields are int32 on 32bit and int64 on 64bit.
|
||||
// tries is assigned to syscall.Timespec.Nsec in order to match its type.
|
||||
tries := ts.Nsec
|
||||
var err int
|
||||
|
||||
for tries = 0; tries < 20; tries++ {
|
||||
err = int(pthread_create(thread, attr, pfn, unsafe.Pointer(arg)))
|
||||
if err == 0 {
|
||||
pthread_detach(*thread)
|
||||
return 0
|
||||
}
|
||||
if err != int(syscall.EAGAIN) {
|
||||
return err
|
||||
}
|
||||
ts.Sec = 0
|
||||
ts.Nsec = (tries + 1) * 1000 * 1000 // Milliseconds.
|
||||
nanosleep(&ts, nil)
|
||||
}
|
||||
return int(syscall.EAGAIN)
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
// runtime/cgo uses __builtin_frame_address(0) instead of `uintptr(unsafe.Pointer(&size))`
|
||||
// but this should be OK since we are taking the address of the first variable in this function.
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:nosplit
|
||||
func _cgo_sys_thread_start(ts *ThreadStart) {
|
||||
var attr pthread_attr_t
|
||||
var ign, oset sigset_t
|
||||
var p pthread_t
|
||||
var size size_t
|
||||
var err int
|
||||
|
||||
//fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug
|
||||
sigfillset(&ign)
|
||||
pthread_sigmask(SIG_SETMASK, &ign, &oset)
|
||||
|
||||
pthread_attr_init(&attr)
|
||||
pthread_attr_getstacksize(&attr, &size)
|
||||
// Leave stacklo=0 and set stackhi=size; mstart will do the rest.
|
||||
ts.g.stackhi = uintptr(size)
|
||||
|
||||
err = _cgo_try_pthread_create(&p, &attr, unsafe.Pointer(threadentry_trampolineABI0), ts)
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &oset, nil)
|
||||
|
||||
if err != 0 {
|
||||
print("fakecgo: pthread_create failed: ")
|
||||
println(err)
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// threadentry_trampolineABI0 maps the C ABI to Go ABI then calls the Go function
|
||||
//
|
||||
//go:linkname x_threadentry_trampoline threadentry_trampoline
|
||||
var x_threadentry_trampoline byte
|
||||
var threadentry_trampolineABI0 = &x_threadentry_trampoline
|
||||
|
||||
//go:nosplit
|
||||
func threadentry(v unsafe.Pointer) unsafe.Pointer {
|
||||
ts := *(*ThreadStart)(v)
|
||||
free(v)
|
||||
|
||||
setg_trampoline(setg_func, uintptr(unsafe.Pointer(ts.g)))
|
||||
|
||||
// faking funcs in go is a bit a... involved - but the following works :)
|
||||
fn := uintptr(unsafe.Pointer(&ts.fn))
|
||||
(*(*func())(unsafe.Pointer(&fn)))()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// here we will store a pointer to the provided setg func
|
||||
var setg_func uintptr
|
||||
|
||||
// x_cgo_init(G *g, void (*setg)(void*)) (runtime/cgo/gcc_linux_amd64.c)
|
||||
// This get's called during startup, adjusts stacklo, and provides a pointer to setg_gcc for us
|
||||
// Additionally, if we set _cgo_init to non-null, go won't do it's own TLS setup
|
||||
// This function can't be go:systemstack since go is not in a state where the systemcheck would work.
|
||||
//
|
||||
//go:nosplit
|
||||
func x_cgo_init(g *G, setg uintptr) {
|
||||
var size size_t
|
||||
var attr *pthread_attr_t
|
||||
|
||||
/* The memory sanitizer distributed with versions of clang
|
||||
before 3.8 has a bug: if you call mmap before malloc, mmap
|
||||
may return an address that is later overwritten by the msan
|
||||
library. Avoid this problem by forcing a call to malloc
|
||||
here, before we ever call malloc.
|
||||
|
||||
This is only required for the memory sanitizer, so it's
|
||||
unfortunate that we always run it. It should be possible
|
||||
to remove this when we no longer care about versions of
|
||||
clang before 3.8. The test for this is
|
||||
misc/cgo/testsanitizers.
|
||||
|
||||
GCC works hard to eliminate a seemingly unnecessary call to
|
||||
malloc, so we actually use the memory we allocate. */
|
||||
|
||||
setg_func = setg
|
||||
attr = (*pthread_attr_t)(malloc(unsafe.Sizeof(*attr)))
|
||||
if attr == nil {
|
||||
println("fakecgo: malloc failed")
|
||||
abort()
|
||||
}
|
||||
pthread_attr_init(attr)
|
||||
pthread_attr_getstacksize(attr, &size)
|
||||
g.stacklo = uintptr(unsafe.Pointer(&size)) - uintptr(size) + 4096
|
||||
pthread_attr_destroy(attr)
|
||||
free(unsafe.Pointer(attr))
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_setenv(arg *[2]*byte) {
|
||||
setenv(arg[0], arg[1], 1)
|
||||
}
|
||||
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_unsetenv(arg *[1]*byte) {
|
||||
unsetenv(arg[0])
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package fakecgo
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// _cgo_thread_start is split into three parts in cgo since only one part is system dependent (keep it here for easier handling)
|
||||
|
||||
// _cgo_thread_start(ThreadStart *arg) (runtime/cgo/gcc_util.c)
|
||||
// This get's called instead of the go code for creating new threads
|
||||
// -> pthread_* stuff is used, so threads are setup correctly for C
|
||||
// If this is missing, TLS is only setup correctly on thread 1!
|
||||
// This function should be go:systemstack instead of go:nosplit (but that requires runtime)
|
||||
//
|
||||
//go:nosplit
|
||||
//go:norace
|
||||
func x_cgo_thread_start(arg *ThreadStart) {
|
||||
var ts *ThreadStart
|
||||
// Make our own copy that can persist after we return.
|
||||
// _cgo_tsan_acquire();
|
||||
ts = (*ThreadStart)(malloc(unsafe.Sizeof(*ts)))
|
||||
// _cgo_tsan_release();
|
||||
if ts == nil {
|
||||
println("fakecgo: out of memory in thread_start")
|
||||
abort()
|
||||
}
|
||||
// *ts = *arg would cause a writebarrier so use memmove instead
|
||||
memmove(unsafe.Pointer(ts), unsafe.Pointer(arg), unsafe.Sizeof(*ts))
|
||||
_cgo_sys_thread_start(ts) // OS-dependent half
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
// The runtime package contains an uninitialized definition
|
||||
// for runtime·iscgo. Override it to tell the runtime we're here.
|
||||
// There are various function pointers that should be set too,
|
||||
// but those depend on dynamic linker magic to get initialized
|
||||
// correctly, and sometimes they break. This variable is a
|
||||
// backup: it depends only on old C style static linking rules.
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
//go:linkname _iscgo runtime.iscgo
|
||||
var _iscgo bool = true
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
size_t uintptr
|
||||
sigset_t [128]byte
|
||||
pthread_attr_t [64]byte
|
||||
pthread_t int
|
||||
pthread_key_t uint64
|
||||
)
|
||||
|
||||
// for pthread_sigmask:
|
||||
|
||||
type sighow int32
|
||||
|
||||
const (
|
||||
SIG_BLOCK sighow = 0
|
||||
SIG_UNBLOCK sighow = 1
|
||||
SIG_SETMASK sighow = 2
|
||||
)
|
||||
|
||||
type G struct {
|
||||
stacklo uintptr
|
||||
stackhi uintptr
|
||||
}
|
||||
|
||||
type ThreadStart struct {
|
||||
g *G
|
||||
tls *uintptr
|
||||
fn uintptr
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
pthread_mutex_t struct {
|
||||
sig int64
|
||||
opaque [56]byte
|
||||
}
|
||||
pthread_cond_t struct {
|
||||
sig int64
|
||||
opaque [40]byte
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
PTHREAD_COND_INITIALIZER = pthread_cond_t{sig: 0x3CB0B1BB}
|
||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{sig: 0x32AAABA7}
|
||||
)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
pthread_cond_t uintptr
|
||||
pthread_mutex_t uintptr
|
||||
)
|
||||
|
||||
var (
|
||||
PTHREAD_COND_INITIALIZER = pthread_cond_t(0)
|
||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t(0)
|
||||
)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package fakecgo
|
||||
|
||||
type (
|
||||
pthread_cond_t [48]byte
|
||||
pthread_mutex_t [48]byte
|
||||
)
|
||||
|
||||
var (
|
||||
PTHREAD_COND_INITIALIZER = pthread_cond_t{}
|
||||
PTHREAD_MUTEX_INITIALIZER = pthread_mutex_t{}
|
||||
)
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package fakecgo
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
//go:linkname x_cgo_setenv_trampoline x_cgo_setenv_trampoline
|
||||
//go:linkname _cgo_setenv runtime._cgo_setenv
|
||||
var x_cgo_setenv_trampoline byte
|
||||
var _cgo_setenv = &x_cgo_setenv_trampoline
|
||||
|
||||
//go:linkname x_cgo_unsetenv_trampoline x_cgo_unsetenv_trampoline
|
||||
//go:linkname _cgo_unsetenv runtime._cgo_unsetenv
|
||||
var x_cgo_unsetenv_trampoline byte
|
||||
var _cgo_unsetenv = &x_cgo_unsetenv_trampoline
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
package fakecgo
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// setg_trampoline calls setg with the G provided
|
||||
func setg_trampoline(setg uintptr, G uintptr)
|
||||
|
||||
//go:linkname memmove runtime.memmove
|
||||
func memmove(to, from unsafe.Pointer, n uintptr)
|
||||
|
||||
// call5 takes fn the C function and 5 arguments and calls the function with those arguments
|
||||
func call5(fn, a1, a2, a3, a4, a5 uintptr) uintptr
|
||||
|
||||
func malloc(size uintptr) unsafe.Pointer {
|
||||
ret := call5(mallocABI0, uintptr(size), 0, 0, 0, 0)
|
||||
// this indirection is to avoid go vet complaining about possible misuse of unsafe.Pointer
|
||||
return *(*unsafe.Pointer)(unsafe.Pointer(&ret))
|
||||
}
|
||||
|
||||
func free(ptr unsafe.Pointer) {
|
||||
call5(freeABI0, uintptr(ptr), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
func setenv(name *byte, value *byte, overwrite int32) int32 {
|
||||
return int32(call5(setenvABI0, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(value)), uintptr(overwrite), 0, 0))
|
||||
}
|
||||
|
||||
func unsetenv(name *byte) int32 {
|
||||
return int32(call5(unsetenvABI0, uintptr(unsafe.Pointer(name)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func sigfillset(set *sigset_t) int32 {
|
||||
return int32(call5(sigfillsetABI0, uintptr(unsafe.Pointer(set)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func nanosleep(ts *syscall.Timespec, rem *syscall.Timespec) int32 {
|
||||
return int32(call5(nanosleepABI0, uintptr(unsafe.Pointer(ts)), uintptr(unsafe.Pointer(rem)), 0, 0, 0))
|
||||
}
|
||||
|
||||
func abort() {
|
||||
call5(abortABI0, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
func pthread_attr_init(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_initABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_create(thread *pthread_t, attr *pthread_attr_t, start unsafe.Pointer, arg unsafe.Pointer) int32 {
|
||||
return int32(call5(pthread_createABI0, uintptr(unsafe.Pointer(thread)), uintptr(unsafe.Pointer(attr)), uintptr(start), uintptr(arg), 0))
|
||||
}
|
||||
|
||||
func pthread_detach(thread pthread_t) int32 {
|
||||
return int32(call5(pthread_detachABI0, uintptr(thread), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_sigmask(how sighow, ign *sigset_t, oset *sigset_t) int32 {
|
||||
return int32(call5(pthread_sigmaskABI0, uintptr(how), uintptr(unsafe.Pointer(ign)), uintptr(unsafe.Pointer(oset)), 0, 0))
|
||||
}
|
||||
|
||||
func pthread_self() pthread_t {
|
||||
return pthread_t(call5(pthread_selfABI0, 0, 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_get_stacksize_np(thread pthread_t) size_t {
|
||||
return size_t(call5(pthread_get_stacksize_npABI0, uintptr(thread), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_attr_getstacksize(attr *pthread_attr_t, stacksize *size_t) int32 {
|
||||
return int32(call5(pthread_attr_getstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(unsafe.Pointer(stacksize)), 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_attr_setstacksize(attr *pthread_attr_t, size size_t) int32 {
|
||||
return int32(call5(pthread_attr_setstacksizeABI0, uintptr(unsafe.Pointer(attr)), uintptr(size), 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_attr_destroy(attr *pthread_attr_t) int32 {
|
||||
return int32(call5(pthread_attr_destroyABI0, uintptr(unsafe.Pointer(attr)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_mutex_lock(mutex *pthread_mutex_t) int32 {
|
||||
return int32(call5(pthread_mutex_lockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_mutex_unlock(mutex *pthread_mutex_t) int32 {
|
||||
return int32(call5(pthread_mutex_unlockABI0, uintptr(unsafe.Pointer(mutex)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_cond_broadcast(cond *pthread_cond_t) int32 {
|
||||
return int32(call5(pthread_cond_broadcastABI0, uintptr(unsafe.Pointer(cond)), 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
func pthread_setspecific(key pthread_key_t, value unsafe.Pointer) int32 {
|
||||
return int32(call5(pthread_setspecificABI0, uintptr(key), uintptr(value), 0, 0, 0))
|
||||
}
|
||||
|
||||
//go:linkname _malloc _malloc
|
||||
var _malloc uintptr
|
||||
var mallocABI0 = uintptr(unsafe.Pointer(&_malloc))
|
||||
|
||||
//go:linkname _free _free
|
||||
var _free uintptr
|
||||
var freeABI0 = uintptr(unsafe.Pointer(&_free))
|
||||
|
||||
//go:linkname _setenv _setenv
|
||||
var _setenv uintptr
|
||||
var setenvABI0 = uintptr(unsafe.Pointer(&_setenv))
|
||||
|
||||
//go:linkname _unsetenv _unsetenv
|
||||
var _unsetenv uintptr
|
||||
var unsetenvABI0 = uintptr(unsafe.Pointer(&_unsetenv))
|
||||
|
||||
//go:linkname _sigfillset _sigfillset
|
||||
var _sigfillset uintptr
|
||||
var sigfillsetABI0 = uintptr(unsafe.Pointer(&_sigfillset))
|
||||
|
||||
//go:linkname _nanosleep _nanosleep
|
||||
var _nanosleep uintptr
|
||||
var nanosleepABI0 = uintptr(unsafe.Pointer(&_nanosleep))
|
||||
|
||||
//go:linkname _abort _abort
|
||||
var _abort uintptr
|
||||
var abortABI0 = uintptr(unsafe.Pointer(&_abort))
|
||||
|
||||
//go:linkname _pthread_attr_init _pthread_attr_init
|
||||
var _pthread_attr_init uintptr
|
||||
var pthread_attr_initABI0 = uintptr(unsafe.Pointer(&_pthread_attr_init))
|
||||
|
||||
//go:linkname _pthread_create _pthread_create
|
||||
var _pthread_create uintptr
|
||||
var pthread_createABI0 = uintptr(unsafe.Pointer(&_pthread_create))
|
||||
|
||||
//go:linkname _pthread_detach _pthread_detach
|
||||
var _pthread_detach uintptr
|
||||
var pthread_detachABI0 = uintptr(unsafe.Pointer(&_pthread_detach))
|
||||
|
||||
//go:linkname _pthread_sigmask _pthread_sigmask
|
||||
var _pthread_sigmask uintptr
|
||||
var pthread_sigmaskABI0 = uintptr(unsafe.Pointer(&_pthread_sigmask))
|
||||
|
||||
//go:linkname _pthread_self _pthread_self
|
||||
var _pthread_self uintptr
|
||||
var pthread_selfABI0 = uintptr(unsafe.Pointer(&_pthread_self))
|
||||
|
||||
//go:linkname _pthread_get_stacksize_np _pthread_get_stacksize_np
|
||||
var _pthread_get_stacksize_np uintptr
|
||||
var pthread_get_stacksize_npABI0 = uintptr(unsafe.Pointer(&_pthread_get_stacksize_np))
|
||||
|
||||
//go:linkname _pthread_attr_getstacksize _pthread_attr_getstacksize
|
||||
var _pthread_attr_getstacksize uintptr
|
||||
var pthread_attr_getstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_getstacksize))
|
||||
|
||||
//go:linkname _pthread_attr_setstacksize _pthread_attr_setstacksize
|
||||
var _pthread_attr_setstacksize uintptr
|
||||
var pthread_attr_setstacksizeABI0 = uintptr(unsafe.Pointer(&_pthread_attr_setstacksize))
|
||||
|
||||
//go:linkname _pthread_attr_destroy _pthread_attr_destroy
|
||||
var _pthread_attr_destroy uintptr
|
||||
var pthread_attr_destroyABI0 = uintptr(unsafe.Pointer(&_pthread_attr_destroy))
|
||||
|
||||
//go:linkname _pthread_mutex_lock _pthread_mutex_lock
|
||||
var _pthread_mutex_lock uintptr
|
||||
var pthread_mutex_lockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_lock))
|
||||
|
||||
//go:linkname _pthread_mutex_unlock _pthread_mutex_unlock
|
||||
var _pthread_mutex_unlock uintptr
|
||||
var pthread_mutex_unlockABI0 = uintptr(unsafe.Pointer(&_pthread_mutex_unlock))
|
||||
|
||||
//go:linkname _pthread_cond_broadcast _pthread_cond_broadcast
|
||||
var _pthread_cond_broadcast uintptr
|
||||
var pthread_cond_broadcastABI0 = uintptr(unsafe.Pointer(&_pthread_cond_broadcast))
|
||||
|
||||
//go:linkname _pthread_setspecific _pthread_setspecific
|
||||
var _pthread_setspecific uintptr
|
||||
var pthread_setspecificABI0 = uintptr(unsafe.Pointer(&_pthread_setspecific))
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:cgo_import_dynamic purego_malloc malloc "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_free free "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_setenv setenv "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_unsetenv unsetenv "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_abort abort "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_self pthread_self "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "/usr/lib/libSystem.B.dylib"
|
||||
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "/usr/lib/libSystem.B.dylib"
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:cgo_import_dynamic purego_malloc malloc "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_free free "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_setenv setenv "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_abort abort "libc.so.7"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so"
|
||||
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so"
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package fakecgo
|
||||
|
||||
//go:cgo_import_dynamic purego_malloc malloc "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_free free "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_setenv setenv "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_unsetenv unsetenv "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_sigfillset sigfillset "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_nanosleep nanosleep "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_abort abort "libc.so.6"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_init pthread_attr_init "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_create pthread_create "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_detach pthread_detach "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_sigmask pthread_sigmask "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_self pthread_self "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_get_stacksize_np pthread_get_stacksize_np "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_getstacksize pthread_attr_getstacksize "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_setstacksize pthread_attr_setstacksize "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_attr_destroy pthread_attr_destroy "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_lock pthread_mutex_lock "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_mutex_unlock pthread_mutex_unlock "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_cond_broadcast pthread_cond_broadcast "libpthread.so.0"
|
||||
//go:cgo_import_dynamic purego_pthread_setspecific pthread_setspecific "libpthread.so.0"
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || linux || freebsd
|
||||
|
||||
/*
|
||||
trampoline for emulating required C functions for cgo in go (see cgo.go)
|
||||
(we convert cdecl calling convention to go and vice-versa)
|
||||
|
||||
Since we're called from go and call into C we can cheat a bit with the calling conventions:
|
||||
- in go all the registers are caller saved
|
||||
- in C we have a couple of callee saved registers
|
||||
|
||||
=> we can use BX, R12, R13, R14, R15 instead of the stack
|
||||
|
||||
C Calling convention cdecl used here (we only need integer args):
|
||||
1. arg: DI
|
||||
2. arg: SI
|
||||
3. arg: DX
|
||||
4. arg: CX
|
||||
5. arg: R8
|
||||
6. arg: R9
|
||||
We don't need floats with these functions -> AX=0
|
||||
return value will be in AX
|
||||
*/
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
|
||||
// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $16
|
||||
MOVQ DI, AX
|
||||
MOVQ SI, BX
|
||||
MOVQ ·x_cgo_init_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_thread_start_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_setenv_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $8
|
||||
MOVQ DI, AX
|
||||
MOVQ ·x_cgo_unsetenv_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0
|
||||
CALL ·x_cgo_notify_runtime_init_done(SB)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0
|
||||
CALL ·x_cgo_bindm(SB)
|
||||
RET
|
||||
|
||||
// func setg_trampoline(setg uintptr, g uintptr)
|
||||
TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
|
||||
MOVQ G+8(FP), DI
|
||||
MOVQ setg+0(FP), BX
|
||||
XORL AX, AX
|
||||
CALL BX
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $16
|
||||
MOVQ DI, AX
|
||||
MOVQ ·threadentry_call(SB), DX
|
||||
MOVQ (DX), CX
|
||||
CALL CX
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-56
|
||||
MOVQ fn+0(FP), BX
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), CX
|
||||
MOVQ a5+40(FP), R8
|
||||
|
||||
XORL AX, AX // no floats
|
||||
|
||||
PUSHQ BP // save BP
|
||||
MOVQ SP, BP // save SP inside BP bc BP is callee-saved
|
||||
SUBQ $16, SP // allocate space for alignment
|
||||
ANDQ $-16, SP // align on 16 bytes for SSE
|
||||
|
||||
CALL BX
|
||||
|
||||
MOVQ BP, SP // get SP back
|
||||
POPQ BP // restore BP
|
||||
|
||||
MOVQ AX, ret+48(FP)
|
||||
RET
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
#include "textflag.h"
|
||||
#include "go_asm.h"
|
||||
|
||||
// these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions.
|
||||
|
||||
TEXT x_cgo_init_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD R1, 16(RSP)
|
||||
MOVD ·x_cgo_init_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·x_cgo_thread_start_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_setenv_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·x_cgo_setenv_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·x_cgo_unsetenv_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT, $0-0
|
||||
CALL ·x_cgo_notify_runtime_init_done(SB)
|
||||
RET
|
||||
|
||||
TEXT x_cgo_bindm_trampoline(SB), NOSPLIT, $0
|
||||
CALL ·x_cgo_bindm(SB)
|
||||
RET
|
||||
|
||||
// func setg_trampoline(setg uintptr, g uintptr)
|
||||
TEXT ·setg_trampoline(SB), NOSPLIT, $0-16
|
||||
MOVD G+8(FP), R0
|
||||
MOVD setg+0(FP), R1
|
||||
CALL R1
|
||||
RET
|
||||
|
||||
TEXT threadentry_trampoline(SB), NOSPLIT, $0-0
|
||||
MOVD R0, 8(RSP)
|
||||
MOVD ·threadentry_call(SB), R26
|
||||
MOVD (R26), R2
|
||||
CALL (R2)
|
||||
MOVD $0, R0 // TODO: get the return value from threadentry
|
||||
RET
|
||||
|
||||
TEXT ·call5(SB), NOSPLIT, $0-0
|
||||
MOVD fn+0(FP), R6
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
CALL R6
|
||||
MOVD R0, ret+48(FP)
|
||||
RET
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
// Code generated by 'go generate' with gen.go. DO NOT EDIT.
|
||||
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build darwin || freebsd || linux
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// these stubs are here because it is not possible to go:linkname directly the C functions on darwin arm64
|
||||
|
||||
TEXT _malloc(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_malloc(SB)
|
||||
RET
|
||||
|
||||
TEXT _free(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_free(SB)
|
||||
RET
|
||||
|
||||
TEXT _setenv(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_setenv(SB)
|
||||
RET
|
||||
|
||||
TEXT _unsetenv(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_unsetenv(SB)
|
||||
RET
|
||||
|
||||
TEXT _sigfillset(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_sigfillset(SB)
|
||||
RET
|
||||
|
||||
TEXT _nanosleep(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_nanosleep(SB)
|
||||
RET
|
||||
|
||||
TEXT _abort(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_abort(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_init(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_init(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_create(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_create(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_detach(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_detach(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_sigmask(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_sigmask(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_self(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_self(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_get_stacksize_np(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_get_stacksize_np(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_getstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_getstacksize(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_setstacksize(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_setstacksize(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_attr_destroy(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_attr_destroy(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_mutex_lock(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_mutex_lock(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_mutex_unlock(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_mutex_unlock(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_cond_broadcast(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_cond_broadcast(SB)
|
||||
RET
|
||||
|
||||
TEXT _pthread_setspecific(SB), NOSPLIT|NOFRAME, $0-0
|
||||
JMP purego_pthread_setspecific(SB)
|
||||
RET
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
package strings
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// hasSuffix tests whether the string s ends with suffix.
|
||||
func hasSuffix(s, suffix string) bool {
|
||||
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
|
||||
}
|
||||
|
||||
// CString converts a go string to *byte that can be passed to C code.
|
||||
func CString(name string) *byte {
|
||||
if hasSuffix(name, "\x00") {
|
||||
return &(*(*[]byte)(unsafe.Pointer(&name)))[0]
|
||||
}
|
||||
b := make([]byte, len(name)+1)
|
||||
copy(b, name)
|
||||
return &b[0]
|
||||
}
|
||||
|
||||
// GoString copies a null-terminated char* to a Go string.
|
||||
func GoString(c uintptr) string {
|
||||
// We take the address and then dereference it to trick go vet from creating a possible misuse of unsafe.Pointer
|
||||
ptr := *(*unsafe.Pointer)(unsafe.Pointer(&c))
|
||||
if ptr == nil {
|
||||
return ""
|
||||
}
|
||||
var length int
|
||||
for {
|
||||
if *(*byte)(unsafe.Add(ptr, uintptr(length))) == '\x00' {
|
||||
break
|
||||
}
|
||||
length++
|
||||
}
|
||||
return string(unsafe.Slice((*byte)(ptr), length))
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo
|
||||
|
||||
package purego
|
||||
|
||||
// if you are getting this error it means that you have
|
||||
// CGO_ENABLED=0 while trying to build for ios.
|
||||
// purego does not support this mode yet.
|
||||
// the fix is to set CGO_ENABLED=1 which will require
|
||||
// a C compiler.
|
||||
var _ = _PUREGO_REQUIRES_CGO_ON_IOS
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
|
||||
|
||||
//go:build !cgo && (darwin || freebsd || linux)
|
||||
|
||||
package purego
|
||||
|
||||
// if CGO_ENABLED=0 import fakecgo to setup the Cgo runtime correctly.
|
||||
// This is required since some frameworks need TLS setup the C way which Go doesn't do.
|
||||
// We currently don't support ios in fakecgo mode so force Cgo or fail
|
||||
//
|
||||
// The way that the Cgo runtime (runtime/cgo) works is by setting some variables found
|
||||
// in runtime with non-null GCC compiled functions. The variables that are replaced are
|
||||
// var (
|
||||
// iscgo bool // in runtime/cgo.go
|
||||
// _cgo_init unsafe.Pointer // in runtime/cgo.go
|
||||
// _cgo_thread_start unsafe.Pointer // in runtime/cgo.go
|
||||
// _cgo_notify_runtime_init_done unsafe.Pointer // in runtime/cgo.go
|
||||
// _cgo_setenv unsafe.Pointer // in runtime/env_posix.go
|
||||
// _cgo_unsetenv unsafe.Pointer // in runtime/env_posix.go
|
||||
// )
|
||||
// importing fakecgo will set these (using //go:linkname) with functions written
|
||||
// entirely in Go (except for some assembly trampolines to change GCC ABI to Go ABI).
|
||||
// Doing so makes it possible to build applications that call into C without CGO_ENABLED=1.
|
||||
import _ "github.com/ebitengine/purego/internal/fakecgo"
|
||||
Loaded 100 of 112 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user