123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185 |
- 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, i 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()
- }
|