1// Copyright 2016 CoreOS, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// Package dlopen provides some convenience functions to dlopen a library and
16// get its symbols.
17package dlopen
18
19// #cgo LDFLAGS: -ldl
20// #include <stdlib.h>
21// #include <dlfcn.h>
22import "C"
23import (
24	"errors"
25	"fmt"
26	"unsafe"
27)
28
29var ErrSoNotFound = errors.New("unable to open a handle to the library")
30
31// LibHandle represents an open handle to a library (.so)
32type LibHandle struct {
33	Handle  unsafe.Pointer
34	Libname string
35}
36
37// GetHandle tries to get a handle to a library (.so), attempting to access it
38// by the names specified in libs and returning the first that is successfully
39// opened. Callers are responsible for closing the handler. If no library can
40// be successfully opened, an error is returned.
41func GetHandle(libs []string) (*LibHandle, error) {
42	for _, name := range libs {
43		libname := C.CString(name)
44		defer C.free(unsafe.Pointer(libname))
45		handle := C.dlopen(libname, C.RTLD_LAZY)
46		if handle != nil {
47			h := &LibHandle{
48				Handle:  handle,
49				Libname: name,
50			}
51			return h, nil
52		}
53	}
54	return nil, ErrSoNotFound
55}
56
57// GetSymbolPointer takes a symbol name and returns a pointer to the symbol.
58func (l *LibHandle) GetSymbolPointer(symbol string) (unsafe.Pointer, error) {
59	sym := C.CString(symbol)
60	defer C.free(unsafe.Pointer(sym))
61
62	C.dlerror()
63	p := C.dlsym(l.Handle, sym)
64	e := C.dlerror()
65	if e != nil {
66		return nil, fmt.Errorf("error resolving symbol %q: %v", symbol, errors.New(C.GoString(e)))
67	}
68
69	return p, nil
70}
71
72// Close closes a LibHandle.
73func (l *LibHandle) Close() error {
74	C.dlerror()
75	C.dlclose(l.Handle)
76	e := C.dlerror()
77	if e != nil {
78		return fmt.Errorf("error closing %v: %v", l.Libname, errors.New(C.GoString(e)))
79	}
80
81	return nil
82}
83