Browse Source

dynlib: unload library before loading again & add LIBRARY_FILE_EXTENSION constant

Laytan Laats 8 months ago
parent
commit
1cece52359

+ 7 - 5
core/dynlib/example/example.odin

@@ -21,12 +21,14 @@ Symbols :: struct {
 main :: proc() {
 	sym: Symbols
 
+	LIB_PATH :: "lib." + dynlib.LIBRARY_FILE_EXTENSION
+
 	// Load symbols from `lib.dll` into Symbols struct.
 	// Each struct field is prefixed with `foo_` before lookup in the DLL's symbol table.
 	// The library's Handle (to unload) will be stored in `sym._my_lib_handle`. This way you can load multiple DLLs in one struct.
-	count, ok := dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
+	count, ok := dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
 	defer dynlib.unload_library(sym._my_lib_handle)
-	fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
+	fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
 
 	if count > 0 {
 		fmt.println("42 + 42 =", sym.add(42, 42))
@@ -34,12 +36,12 @@ main :: proc() {
 		fmt.println("hellope =", sym.hellope^)
 	}
 
-	count, ok = dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
-	fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
+	count, ok = dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
+	fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
 
 	if count > 0 {
 		fmt.println("42 + 42 =", sym.add(42, 42))
 		fmt.println("84 - 13 =", sym.sub(84, 13))
 		fmt.println("hellope =", sym.hellope^)
 	}
-}
+}

+ 23 - 13
core/dynlib/lib.odin

@@ -12,6 +12,11 @@ A handle to a dynamically loaded library.
 */
 Library :: distinct rawptr
 
+/*
+The file extension for dynamic libraries on the target OS.
+*/
+LIBRARY_FILE_EXTENSION :: _LIBRARY_FILE_EXTENSION
+
 /*
 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.
@@ -123,31 +128,36 @@ initialize_symbols :: proc(
 ) -> (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
+	// First, (re)load the library.
+	handle: Library
 	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 {
+			field_ptr := rawptr(uintptr(symbol_table) + field.offset)
+
 			// 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
 			}
+
+			handle = load_library(library_path) or_return
 			(^Library)(field_ptr)^ = handle
-			continue
+			break
 		}
+	}
+
+	// Buffer to concatenate the prefix + symbol name.
+	prefixed_symbol_buf: [2048]u8 = ---
 
-		// 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)) {
+	count = 0
+	for field in reflect.struct_fields_zipped(T) {
+		// If we're not the library handle, the field needs to be a pointer type, be it a procedure pointer or an exported global.
+		if field.name == handle_field_name || !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
 			continue
 		}
 
+		// Calculate address of struct member
+		field_ptr := rawptr(uintptr(symbol_table) + field.offset)
+
 		// Let's look up or construct the symbol name to find in the library
 		prefixed_name: string
 

+ 2 - 0
core/dynlib/lib_js.odin

@@ -4,6 +4,8 @@ package dynlib
 
 import "base:runtime"
 
+_LIBRARY_FILE_EXTENSION :: ""
+
 _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
 	return nil, false
 }

+ 2 - 0
core/dynlib/lib_unix.odin

@@ -7,6 +7,8 @@ import "base:runtime"
 import "core:strings"
 import "core:sys/posix"
 
+_LIBRARY_FILE_EXTENSION :: "dylib" when ODIN_OS == .Darwin else "so"
+
 _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
 	flags := posix.RTLD_Flags{.NOW}
 	if global_symbols {

+ 2 - 0
core/dynlib/lib_windows.odin

@@ -8,6 +8,8 @@ import win32 "core:sys/windows"
 import "core:strings"
 import "core:reflect"
 
+_LIBRARY_FILE_EXTENSION :: "dll"
+
 _load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
 	// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
 	wide_path := win32.utf8_to_wstring(path, allocator)