mirror of https://github.com/EasyDarwin/EasyDarwin
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
262 lines
6.9 KiB
262 lines
6.9 KiB
6 years ago
|
// 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 unix
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
"sync/atomic"
|
||
|
"syscall"
|
||
|
"unsafe"
|
||
|
)
|
||
|
|
||
|
// soError describes reasons for shared library load failures.
|
||
|
type soError struct {
|
||
|
Err error
|
||
|
ObjName string
|
||
|
Msg string
|
||
|
}
|
||
|
|
||
|
func (e *soError) Error() string { return e.Msg }
|
||
|
|
||
|
// Implemented in runtime/syscall_solaris.goc.
|
||
|
func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||
|
func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)
|
||
|
func dlclose(handle uintptr) (err syscall.Errno)
|
||
|
func dlopen(name *uint8, mode uintptr) (handle uintptr, err syscall.Errno)
|
||
|
func dlsym(handle uintptr, name *uint8) (proc uintptr, err syscall.Errno)
|
||
|
|
||
|
// A so implements access to a single shared library object.
|
||
|
type so struct {
|
||
|
Name string
|
||
|
Handle uintptr
|
||
|
}
|
||
|
|
||
|
// loadSO loads shared library file into memory.
|
||
|
func loadSO(name string) (*so, error) {
|
||
|
namep, err := BytePtrFromString(name)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
h, e := dlopen(namep, 1) // RTLD_LAZY
|
||
|
if e != 0 {
|
||
|
return nil, &soError{
|
||
|
Err: e,
|
||
|
ObjName: name,
|
||
|
Msg: "Failed to load " + name + ": " + e.Error(),
|
||
|
}
|
||
|
}
|
||
|
d := &so{
|
||
|
Name: name,
|
||
|
Handle: uintptr(h),
|
||
|
}
|
||
|
return d, nil
|
||
|
}
|
||
|
|
||
|
// mustLoadSO is like loadSO but panics if load operation fails.
|
||
|
func mustLoadSO(name string) *so {
|
||
|
d, e := loadSO(name)
|
||
|
if e != nil {
|
||
|
panic(e)
|
||
|
}
|
||
|
return d
|
||
|
}
|
||
|
|
||
|
// FindProc searches shared library d for procedure named name and returns
|
||
|
// *proc if found. It returns an error if the search fails.
|
||
|
func (d *so) FindProc(name string) (*proc, error) {
|
||
|
namep, err := BytePtrFromString(name)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
a, _ := dlsym(uintptr(d.Handle), namep)
|
||
|
if a == 0 {
|
||
|
return nil, &soError{
|
||
|
Err: ENOSYS,
|
||
|
ObjName: name,
|
||
|
Msg: "Failed to find " + name + " procedure in " + d.Name,
|
||
|
}
|
||
|
}
|
||
|
p := &proc{
|
||
|
SO: d,
|
||
|
Name: name,
|
||
|
addr: a,
|
||
|
}
|
||
|
return p, nil
|
||
|
}
|
||
|
|
||
|
// MustFindProc is like FindProc but panics if search fails.
|
||
|
func (d *so) MustFindProc(name string) *proc {
|
||
|
p, e := d.FindProc(name)
|
||
|
if e != nil {
|
||
|
panic(e)
|
||
|
}
|
||
|
return p
|
||
|
}
|
||
|
|
||
|
// Release unloads shared library d from memory.
|
||
|
func (d *so) Release() (err error) {
|
||
|
return dlclose(d.Handle)
|
||
|
}
|
||
|
|
||
|
// A proc implements access to a procedure inside a shared library.
|
||
|
type proc struct {
|
||
|
SO *so
|
||
|
Name string
|
||
|
addr uintptr
|
||
|
}
|
||
|
|
||
|
// Addr returns the address of the procedure represented by p.
|
||
|
// The return value can be passed to Syscall to run the procedure.
|
||
|
func (p *proc) Addr() uintptr {
|
||
|
return p.addr
|
||
|
}
|
||
|
|
||
|
// Call executes procedure p with arguments a. It will panic, if more then
|
||
|
// 6 arguments are supplied.
|
||
|
//
|
||
|
// The returned error is always non-nil, constructed from the result of
|
||
|
// GetLastError. Callers must inspect the primary return value to decide
|
||
|
// whether an error occurred (according to the semantics of the specific
|
||
|
// function being called) before consulting the error. The error will be
|
||
|
// guaranteed to contain syscall.Errno.
|
||
|
func (p *proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||
|
switch len(a) {
|
||
|
case 0:
|
||
|
return sysvicall6(p.Addr(), uintptr(len(a)), 0, 0, 0, 0, 0, 0)
|
||
|
case 1:
|
||
|
return sysvicall6(p.Addr(), uintptr(len(a)), a[0], 0, 0, 0, 0, 0)
|
||
|
case 2:
|
||
|
return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], 0, 0, 0, 0)
|
||
|
case 3:
|
||
|
return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], 0, 0, 0)
|
||
|
case 4:
|
||
|
return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
|
||
|
case 5:
|
||
|
return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
|
||
|
case 6:
|
||
|
return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
|
||
|
default:
|
||
|
panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// A lazySO implements access to a single shared library. It will delay
|
||
|
// the load of the shared library until the first call to its Handle method
|
||
|
// or to one of its lazyProc's Addr method.
|
||
|
type lazySO struct {
|
||
|
mu sync.Mutex
|
||
|
so *so // non nil once SO is loaded
|
||
|
Name string
|
||
|
}
|
||
|
|
||
|
// Load loads single shared file d.Name into memory. It returns an error if
|
||
|
// fails. Load will not try to load SO, if it is already loaded into memory.
|
||
|
func (d *lazySO) Load() error {
|
||
|
// Non-racy version of:
|
||
|
// if d.so == nil {
|
||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.so))) == nil {
|
||
|
d.mu.Lock()
|
||
|
defer d.mu.Unlock()
|
||
|
if d.so == nil {
|
||
|
so, e := loadSO(d.Name)
|
||
|
if e != nil {
|
||
|
return e
|
||
|
}
|
||
|
// Non-racy version of:
|
||
|
// d.so = so
|
||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.so)), unsafe.Pointer(so))
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// mustLoad is like Load but panics if search fails.
|
||
|
func (d *lazySO) mustLoad() {
|
||
|
e := d.Load()
|
||
|
if e != nil {
|
||
|
panic(e)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Handle returns d's module handle.
|
||
|
func (d *lazySO) Handle() uintptr {
|
||
|
d.mustLoad()
|
||
|
return uintptr(d.so.Handle)
|
||
|
}
|
||
|
|
||
|
// NewProc returns a lazyProc for accessing the named procedure in the SO d.
|
||
|
func (d *lazySO) NewProc(name string) *lazyProc {
|
||
|
return &lazyProc{l: d, Name: name}
|
||
|
}
|
||
|
|
||
|
// newLazySO creates new lazySO associated with SO file.
|
||
|
func newLazySO(name string) *lazySO {
|
||
|
return &lazySO{Name: name}
|
||
|
}
|
||
|
|
||
|
// A lazyProc implements access to a procedure inside a lazySO.
|
||
|
// It delays the lookup until the Addr method is called.
|
||
|
type lazyProc struct {
|
||
|
mu sync.Mutex
|
||
|
Name string
|
||
|
l *lazySO
|
||
|
proc *proc
|
||
|
}
|
||
|
|
||
|
// Find searches the shared library for procedure named p.Name. It returns an
|
||
|
// error if search fails. Find will not search procedure, if it is already
|
||
|
// found and loaded into memory.
|
||
|
func (p *lazyProc) Find() error {
|
||
|
// Non-racy version of:
|
||
|
// if p.proc == nil {
|
||
|
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
|
||
|
p.mu.Lock()
|
||
|
defer p.mu.Unlock()
|
||
|
if p.proc == nil {
|
||
|
e := p.l.Load()
|
||
|
if e != nil {
|
||
|
return e
|
||
|
}
|
||
|
proc, e := p.l.so.FindProc(p.Name)
|
||
|
if e != nil {
|
||
|
return e
|
||
|
}
|
||
|
// Non-racy version of:
|
||
|
// p.proc = proc
|
||
|
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// mustFind is like Find but panics if search fails.
|
||
|
func (p *lazyProc) mustFind() {
|
||
|
e := p.Find()
|
||
|
if e != nil {
|
||
|
panic(e)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Addr returns the address of the procedure represented by p.
|
||
|
// The return value can be passed to Syscall to run the procedure.
|
||
|
func (p *lazyProc) Addr() uintptr {
|
||
|
p.mustFind()
|
||
|
return p.proc.Addr()
|
||
|
}
|
||
|
|
||
|
// Call executes procedure p with arguments a. It will panic, if more then
|
||
|
// 6 arguments are supplied.
|
||
|
//
|
||
|
// The returned error is always non-nil, constructed from the result of
|
||
|
// GetLastError. Callers must inspect the primary return value to decide
|
||
|
// whether an error occurred (according to the semantics of the specific
|
||
|
// function being called) before consulting the error. The error will be
|
||
|
// guaranteed to contain syscall.Errno.
|
||
|
func (p *lazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||
|
p.mustFind()
|
||
|
return p.proc.Call(a...)
|
||
|
}
|