github.com/tcnksm/go@v0.0.0-20141208075154-439b32936367/src/syscall/so_solaris.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package syscall
     6  
     7  import (
     8  	"sync"
     9  	"sync/atomic"
    10  	"unsafe"
    11  )
    12  
    13  // soError describes reasons for shared library load failures.
    14  type soError struct {
    15  	Err     error
    16  	ObjName string
    17  	Msg     string
    18  }
    19  
    20  func (e *soError) Error() string { return e.Msg }
    21  
    22  // Implemented in asm_solaris_amd64.s.
    23  func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
    24  func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
    25  func dlclose(handle uintptr) (err Errno)
    26  func dlopen(name *uint8, mode uintptr) (handle uintptr, err Errno)
    27  func dlsym(handle uintptr, name *uint8) (proc uintptr, err Errno)
    28  
    29  // A so implements access to a single shared library object.
    30  type so struct {
    31  	Name   string
    32  	Handle uintptr
    33  }
    34  
    35  // loadSO loads shared library file into memory.
    36  func loadSO(name string) (*so, error) {
    37  	namep, err := BytePtrFromString(name)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  	h, e := dlopen(namep, 1) // RTLD_LAZY
    42  	use(unsafe.Pointer(namep))
    43  	if e != 0 {
    44  		return nil, &soError{
    45  			Err:     e,
    46  			ObjName: name,
    47  			Msg:     "Failed to load " + name + ": " + e.Error(),
    48  		}
    49  	}
    50  	d := &so{
    51  		Name:   name,
    52  		Handle: uintptr(h),
    53  	}
    54  	return d, nil
    55  }
    56  
    57  // mustLoadSO is like loadSO but panics if load operation fails.
    58  func mustLoadSO(name string) *so {
    59  	d, e := loadSO(name)
    60  	if e != nil {
    61  		panic(e)
    62  	}
    63  	return d
    64  }
    65  
    66  // FindProc searches shared library d for procedure named name and returns
    67  // *proc if found. It returns an error if the search fails.
    68  func (d *so) FindProc(name string) (*proc, error) {
    69  	namep, err := BytePtrFromString(name)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	a, _ := dlsym(uintptr(d.Handle), namep)
    74  	use(unsafe.Pointer(namep))
    75  	if a == 0 {
    76  		return nil, &soError{
    77  			Err:     ENOSYS,
    78  			ObjName: name,
    79  			Msg:     "Failed to find " + name + " procedure in " + d.Name,
    80  		}
    81  	}
    82  	p := &proc{
    83  		SO:   d,
    84  		Name: name,
    85  		addr: a,
    86  	}
    87  	return p, nil
    88  }
    89  
    90  // MustFindProc is like FindProc but panics if search fails.
    91  func (d *so) MustFindProc(name string) *proc {
    92  	p, e := d.FindProc(name)
    93  	if e != nil {
    94  		panic(e)
    95  	}
    96  	return p
    97  }
    98  
    99  // Release unloads shared library d from memory.
   100  func (d *so) Release() (err error) {
   101  	return dlclose(d.Handle)
   102  }
   103  
   104  // A proc implements access to a procedure inside a shared library.
   105  type proc struct {
   106  	SO   *so
   107  	Name string
   108  	addr uintptr
   109  }
   110  
   111  // Addr returns the address of the procedure represented by p.
   112  // The return value can be passed to Syscall to run the procedure.
   113  func (p *proc) Addr() uintptr {
   114  	return p.addr
   115  }
   116  
   117  // Call executes procedure p with arguments a. It will panic, if more then
   118  // 6 arguments are supplied.
   119  //
   120  // The returned error is always non-nil, constructed from the result of
   121  // GetLastError.  Callers must inspect the primary return value to decide
   122  // whether an error occurred (according to the semantics of the specific
   123  // function being called) before consulting the error. The error will be
   124  // guaranteed to contain syscall.Errno.
   125  func (p *proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
   126  	switch len(a) {
   127  	case 0:
   128  		return sysvicall6(p.Addr(), uintptr(len(a)), 0, 0, 0, 0, 0, 0)
   129  	case 1:
   130  		return sysvicall6(p.Addr(), uintptr(len(a)), a[0], 0, 0, 0, 0, 0)
   131  	case 2:
   132  		return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], 0, 0, 0, 0)
   133  	case 3:
   134  		return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], 0, 0, 0)
   135  	case 4:
   136  		return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], 0, 0)
   137  	case 5:
   138  		return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], 0)
   139  	case 6:
   140  		return sysvicall6(p.Addr(), uintptr(len(a)), a[0], a[1], a[2], a[3], a[4], a[5])
   141  	default:
   142  		panic("Call " + p.Name + " with too many arguments " + itoa(len(a)) + ".")
   143  	}
   144  	return
   145  }
   146  
   147  // A lazySO implements access to a single shared library.  It will delay
   148  // the load of the shared library until the first call to its Handle method
   149  // or to one of its lazyProc's Addr method.
   150  type lazySO struct {
   151  	mu   sync.Mutex
   152  	so   *so // non nil once SO is loaded
   153  	Name string
   154  }
   155  
   156  // Load loads single shared file d.Name into memory. It returns an error if
   157  // fails.  Load will not try to load SO, if it is already loaded into memory.
   158  func (d *lazySO) Load() error {
   159  	// Non-racy version of:
   160  	// if d.so == nil {
   161  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.so))) == nil {
   162  		d.mu.Lock()
   163  		defer d.mu.Unlock()
   164  		if d.so == nil {
   165  			so, e := loadSO(d.Name)
   166  			if e != nil {
   167  				return e
   168  			}
   169  			// Non-racy version of:
   170  			// d.so = so
   171  			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.so)), unsafe.Pointer(so))
   172  		}
   173  	}
   174  	return nil
   175  }
   176  
   177  // mustLoad is like Load but panics if search fails.
   178  func (d *lazySO) mustLoad() {
   179  	e := d.Load()
   180  	if e != nil {
   181  		panic(e)
   182  	}
   183  }
   184  
   185  // Handle returns d's module handle.
   186  func (d *lazySO) Handle() uintptr {
   187  	d.mustLoad()
   188  	return uintptr(d.so.Handle)
   189  }
   190  
   191  // NewProc returns a lazyProc for accessing the named procedure in the SO d.
   192  func (d *lazySO) NewProc(name string) *lazyProc {
   193  	return &lazyProc{l: d, Name: name}
   194  }
   195  
   196  // newLazySO creates new lazySO associated with SO file.
   197  func newLazySO(name string) *lazySO {
   198  	return &lazySO{Name: name}
   199  }
   200  
   201  // A lazyProc implements access to a procedure inside a lazySO.
   202  // It delays the lookup until the Addr method is called.
   203  type lazyProc struct {
   204  	mu   sync.Mutex
   205  	Name string
   206  	l    *lazySO
   207  	proc *proc
   208  }
   209  
   210  // Find searches the shared library for procedure named p.Name. It returns an
   211  // error if search fails. Find will not search procedure, if it is already
   212  // found and loaded into memory.
   213  func (p *lazyProc) Find() error {
   214  	// Non-racy version of:
   215  	// if p.proc == nil {
   216  	if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
   217  		p.mu.Lock()
   218  		defer p.mu.Unlock()
   219  		if p.proc == nil {
   220  			e := p.l.Load()
   221  			if e != nil {
   222  				return e
   223  			}
   224  			proc, e := p.l.so.FindProc(p.Name)
   225  			if e != nil {
   226  				return e
   227  			}
   228  			// Non-racy version of:
   229  			// p.proc = proc
   230  			atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
   231  		}
   232  	}
   233  	return nil
   234  }
   235  
   236  // mustFind is like Find but panics if search fails.
   237  func (p *lazyProc) mustFind() {
   238  	e := p.Find()
   239  	if e != nil {
   240  		panic(e)
   241  	}
   242  }
   243  
   244  // Addr returns the address of the procedure represented by p.
   245  // The return value can be passed to Syscall to run the procedure.
   246  func (p *lazyProc) Addr() uintptr {
   247  	p.mustFind()
   248  	return p.proc.Addr()
   249  }
   250  
   251  // Call executes procedure p with arguments a. It will panic, if more then
   252  // 6 arguments are supplied.
   253  //
   254  // The returned error is always non-nil, constructed from the result of
   255  // GetLastError.  Callers must inspect the primary return value to decide
   256  // whether an error occurred (according to the semantics of the specific
   257  // function being called) before consulting the error. The error will be
   258  // guaranteed to contain syscall.Errno.
   259  func (p *lazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
   260  	p.mustFind()
   261  	return p.proc.Call(a...)
   262  }