lib.odin 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package dynlib
  2. import "base:intrinsics"
  3. import "core:reflect"
  4. import "base:runtime"
  5. _ :: intrinsics
  6. _ :: reflect
  7. _ :: runtime
  8. /*
  9. A handle to a dynamically loaded library.
  10. */
  11. Library :: distinct rawptr
  12. /*
  13. Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
  14. library available to resolve references in subsequently loaded libraries.
  15. The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
  16. On `windows` this paramater is ignored.
  17. The underlying behaviour is platform specific.
  18. On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
  19. On `windows` refer to `LoadLibraryW`.
  20. **Implicit Allocators**
  21. `context.temp_allocator`
  22. Example:
  23. import "core:dynlib"
  24. import "core:fmt"
  25. load_my_library :: proc() {
  26. LIBRARY_PATH :: "my_library.dll"
  27. library, ok := dynlib.load_library(LIBRARY_PATH)
  28. if ! ok {
  29. fmt.eprintln(dynlib.last_error())
  30. return
  31. }
  32. fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
  33. }
  34. */
  35. load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
  36. return _load_library(path, global_symbols)
  37. }
  38. /*
  39. Unloads a dynamic library.
  40. The underlying behaviour is platform specific.
  41. On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlclose`.
  42. On `windows` refer to `FreeLibrary`.
  43. Example:
  44. import "core:dynlib"
  45. import "core:fmt"
  46. load_then_unload_my_library :: proc() {
  47. LIBRARY_PATH :: "my_library.dll"
  48. library, ok := dynlib.load_library(LIBRARY_PATH)
  49. if ! ok {
  50. fmt.eprintln(dynlib.last_error())
  51. return
  52. }
  53. did_unload := dynlib.unload_library(library)
  54. if ! did_unload {
  55. fmt.eprintln(dynlib.last_error())
  56. return
  57. }
  58. fmt.println("The library %q was successfully unloaded", LIBRARY_PATH)
  59. }
  60. */
  61. unload_library :: proc(library: Library) -> (did_unload: bool) {
  62. return _unload_library(library)
  63. }
  64. /*
  65. Loads the address of a procedure/variable from a dynamic library.
  66. The underlying behaviour is platform specific.
  67. On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
  68. On `windows` refer to `GetProcAddress`.
  69. **Implicit Allocators**
  70. `context.temp_allocator`
  71. Example:
  72. import "core:dynlib"
  73. import "core:fmt"
  74. find_a_in_my_library :: proc() {
  75. LIBRARY_PATH :: "my_library.dll"
  76. library, ok := dynlib.load_library(LIBRARY_PATH)
  77. if ! ok {
  78. fmt.eprintln(dynlib.last_error())
  79. return
  80. }
  81. a, found_a := dynlib.symbol_address(library, "a")
  82. if found_a {
  83. fmt.printf("The symbol %q was found at the address %v", "a", a)
  84. } else {
  85. fmt.eprintln(dynlib.last_error())
  86. }
  87. }
  88. */
  89. symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
  90. return _symbol_address(library, symbol)
  91. }
  92. /*
  93. Scans a dynamic library for symbols matching a struct's members, assigning found procedure pointers to the corresponding entry.
  94. Optionally takes a symbol prefix added to the struct's member name to construct the symbol looked up in the library.
  95. Optionally also takes the struct member to assign the library handle to, `__handle` by default.
  96. This allows using one struct to hold library handles and symbol pointers for more than 1 dynamic library.
  97. Loading the same library twice unloads the previous incarnation, allowing for straightforward hot reload support.
  98. Returns:
  99. * `-1, false` if the library could not be loaded.
  100. * The number of symbols assigned on success. `ok` = true if `count` > 0
  101. See doc.odin for an example.
  102. */
  103. initialize_symbols :: proc(
  104. symbol_table: ^$T, library_path: string,
  105. symbol_prefix := "", handle_field_name := "__handle",
  106. ) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
  107. assert(symbol_table != nil)
  108. handle := load_library(library_path) or_return
  109. // Buffer to concatenate the prefix + symbol name.
  110. prefixed_symbol_buf: [2048]u8 = ---
  111. count = 0
  112. for field, i in reflect.struct_fields_zipped(T) {
  113. // Calculate address of struct member
  114. field_ptr := rawptr(uintptr(symbol_table) + field.offset)
  115. // If we've come across the struct member for the handle, store it and continue scanning for other symbols.
  116. if field.name == handle_field_name {
  117. // We appear to be hot reloading. Unload previous incarnation of the library.
  118. if old_handle := (^Library)(field_ptr)^; old_handle != nil {
  119. unload_library(old_handle) or_return
  120. }
  121. (^Library)(field_ptr)^ = handle
  122. continue
  123. }
  124. // We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
  125. if !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
  126. continue
  127. }
  128. // Let's look up or construct the symbol name to find in the library
  129. prefixed_name: string
  130. // Do we have a symbol override tag?
  131. if override, tag_ok := reflect.struct_tag_lookup(field.tag, "dynlib"); tag_ok {
  132. prefixed_name = override
  133. }
  134. // No valid symbol override tag found, fall back to `<symbol_prefix>name`.
  135. if len(prefixed_name) == 0 {
  136. offset := copy(prefixed_symbol_buf[:], symbol_prefix)
  137. copy(prefixed_symbol_buf[offset:], field.name)
  138. prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field.name)])
  139. }
  140. // Assign procedure (or global) pointer if found.
  141. sym_ptr := symbol_address(handle, prefixed_name) or_continue
  142. (^rawptr)(field_ptr)^ = sym_ptr
  143. count += 1
  144. }
  145. return count, count > 0
  146. }
  147. /*
  148. Returns an error message for the last failed procedure call.
  149. */
  150. last_error :: proc() -> string {
  151. return _last_error()
  152. }