mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-29 01:14:40 +00:00
186 lines
5.4 KiB
Odin
186 lines
5.4 KiB
Odin
package dynlib
|
|
|
|
import "base:intrinsics"
|
|
import "core:reflect"
|
|
import "base:runtime"
|
|
_ :: intrinsics
|
|
_ :: reflect
|
|
_ :: runtime
|
|
|
|
/*
|
|
A handle to a dynamically loaded library.
|
|
*/
|
|
Library :: distinct rawptr
|
|
|
|
/*
|
|
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
|
library available to resolve references in subsequently loaded libraries.
|
|
|
|
The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
|
|
On `windows` this paramater is ignored.
|
|
|
|
The underlying behaviour is platform specific.
|
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
|
|
On `windows` refer to `LoadLibraryW`.
|
|
|
|
**Implicit Allocators**
|
|
`context.temp_allocator`
|
|
|
|
Example:
|
|
import "core:dynlib"
|
|
import "core:fmt"
|
|
|
|
load_my_library :: proc() {
|
|
LIBRARY_PATH :: "my_library.dll"
|
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
|
if ! ok {
|
|
fmt.eprintln(dynlib.last_error())
|
|
return
|
|
}
|
|
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
|
|
}
|
|
*/
|
|
load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
|
|
return _load_library(path, global_symbols)
|
|
}
|
|
|
|
/*
|
|
Unloads a dynamic library.
|
|
|
|
The underlying behaviour is platform specific.
|
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlclose`.
|
|
On `windows` refer to `FreeLibrary`.
|
|
|
|
Example:
|
|
import "core:dynlib"
|
|
import "core:fmt"
|
|
|
|
load_then_unload_my_library :: proc() {
|
|
LIBRARY_PATH :: "my_library.dll"
|
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
|
if ! ok {
|
|
fmt.eprintln(dynlib.last_error())
|
|
return
|
|
}
|
|
did_unload := dynlib.unload_library(library)
|
|
if ! did_unload {
|
|
fmt.eprintln(dynlib.last_error())
|
|
return
|
|
}
|
|
fmt.println("The library %q was successfully unloaded", LIBRARY_PATH)
|
|
}
|
|
*/
|
|
unload_library :: proc(library: Library) -> (did_unload: bool) {
|
|
return _unload_library(library)
|
|
}
|
|
|
|
/*
|
|
Loads the address of a procedure/variable from a dynamic library.
|
|
|
|
The underlying behaviour is platform specific.
|
|
On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
|
|
On `windows` refer to `GetProcAddress`.
|
|
|
|
**Implicit Allocators**
|
|
`context.temp_allocator`
|
|
|
|
Example:
|
|
import "core:dynlib"
|
|
import "core:fmt"
|
|
|
|
find_a_in_my_library :: proc() {
|
|
LIBRARY_PATH :: "my_library.dll"
|
|
library, ok := dynlib.load_library(LIBRARY_PATH)
|
|
if ! ok {
|
|
fmt.eprintln(dynlib.last_error())
|
|
return
|
|
}
|
|
|
|
a, found_a := dynlib.symbol_address(library, "a")
|
|
if found_a {
|
|
fmt.printf("The symbol %q was found at the address %v", "a", a)
|
|
} else {
|
|
fmt.eprintln(dynlib.last_error())
|
|
}
|
|
}
|
|
*/
|
|
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
|
return _symbol_address(library, symbol)
|
|
}
|
|
|
|
/*
|
|
Scans a dynamic library for symbols matching a struct's members, assigning found procedure pointers to the corresponding entry.
|
|
Optionally takes a symbol prefix added to the struct's member name to construct the symbol looked up in the library.
|
|
Optionally also takes the struct member to assign the library handle to, `__handle` by default.
|
|
|
|
This allows using one struct to hold library handles and symbol pointers for more than 1 dynamic library.
|
|
|
|
Loading the same library twice unloads the previous incarnation, allowing for straightforward hot reload support.
|
|
|
|
Returns:
|
|
* `-1, false` if the library could not be loaded.
|
|
* The number of symbols assigned on success. `ok` = true if `count` > 0
|
|
|
|
See doc.odin for an example.
|
|
*/
|
|
initialize_symbols :: proc(
|
|
symbol_table: ^$T, library_path: string,
|
|
symbol_prefix := "", handle_field_name := "__handle",
|
|
) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
|
|
assert(symbol_table != nil)
|
|
|
|
handle := load_library(library_path) or_return
|
|
|
|
// Buffer to concatenate the prefix + symbol name.
|
|
prefixed_symbol_buf: [2048]u8 = ---
|
|
|
|
count = 0
|
|
for field in reflect.struct_fields_zipped(T) {
|
|
// Calculate address of struct member
|
|
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
|
|
|
// If we've come across the struct member for the handle, store it and continue scanning for other symbols.
|
|
if field.name == handle_field_name {
|
|
// We appear to be hot reloading. Unload previous incarnation of the library.
|
|
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
|
|
unload_library(old_handle) or_return
|
|
}
|
|
(^Library)(field_ptr)^ = handle
|
|
continue
|
|
}
|
|
|
|
// We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
|
|
if !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
|
|
continue
|
|
}
|
|
|
|
// Let's look up or construct the symbol name to find in the library
|
|
prefixed_name: string
|
|
|
|
// Do we have a symbol override tag?
|
|
if override, tag_ok := reflect.struct_tag_lookup(field.tag, "dynlib"); tag_ok {
|
|
prefixed_name = override
|
|
}
|
|
|
|
// No valid symbol override tag found, fall back to `<symbol_prefix>name`.
|
|
if len(prefixed_name) == 0 {
|
|
offset := copy(prefixed_symbol_buf[:], symbol_prefix)
|
|
copy(prefixed_symbol_buf[offset:], field.name)
|
|
prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field.name)])
|
|
}
|
|
|
|
// Assign procedure (or global) pointer if found.
|
|
sym_ptr := symbol_address(handle, prefixed_name) or_continue
|
|
(^rawptr)(field_ptr)^ = sym_ptr
|
|
count += 1
|
|
}
|
|
return count, count > 0
|
|
}
|
|
|
|
/*
|
|
Returns an error message for the last failed procedure call.
|
|
*/
|
|
last_error :: proc() -> string {
|
|
return _last_error()
|
|
}
|