blob: 8b1980fb4006ff02d14a7e6dcc368cb876a5184f [file] [log] [blame]
Aram Hăvărneanu1c986192014-02-25 21:12:10 +11001// 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
5package syscall
6
7import (
8 "sync"
9 "sync/atomic"
10 "unsafe"
11)
12
13// soError describes reasons for shared library load failures.
14type soError struct {
15 Err error
16 ObjName string
17 Msg string
18}
19
20func (e *soError) Error() string { return e.Msg }
21
Aram Hăvărneanub4c28d92014-09-06 19:35:46 +020022// Implemented in asm_solaris_amd64.s.
Aram Hăvărneanu1c986192014-02-25 21:12:10 +110023func rawSysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
24func sysvicall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
25func dlclose(handle uintptr) (err Errno)
26func dlopen(name *uint8, mode uintptr) (handle uintptr, err Errno)
27func dlsym(handle uintptr, name *uint8) (proc uintptr, err Errno)
28
29// A so implements access to a single shared library object.
30type so struct {
31 Name string
32 Handle uintptr
33}
34
35// loadSO loads shared library file into memory.
36func 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
Russ Coxcf622d72014-09-08 16:59:59 -040042 use(unsafe.Pointer(namep))
Aram Hăvărneanu1c986192014-02-25 21:12:10 +110043 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.
58func 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.
68func (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)
Russ Coxcf622d72014-09-08 16:59:59 -040074 use(unsafe.Pointer(namep))
Aram Hăvărneanu1c986192014-02-25 21:12:10 +110075 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.
91func (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.
100func (d *so) Release() (err error) {
101 return dlclose(d.Handle)
102}
103
104// A proc implements access to a procedure inside a shared library.
105type 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.
113func (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.
125func (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.
150type 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.
158func (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.
178func (d *lazySO) mustLoad() {
179 e := d.Load()
180 if e != nil {
181 panic(e)
182 }
183}
184
185// Handle returns d's module handle.
186func (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.
192func (d *lazySO) NewProc(name string) *lazyProc {
193 return &lazyProc{l: d, Name: name}
194}
195
196// newLazySO creates new lazySO associated with SO file.
197func 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.
203type 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.
213func (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.
237func (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.
246func (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.
259func (p *lazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
260 p.mustFind()
261 return p.proc.Call(a...)
262}