lib.odin 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package dynlib
  2. import "core:intrinsics"
  3. import "core:reflect"
  4. import "core: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(symbol_table: ^$T, library_path: string, symbol_prefix := "", handle_field_name := "__handle") -> (count: int, ok: bool) where intrinsics.type_is_struct(T) {
  104. assert(symbol_table != nil)
  105. handle: Library
  106. if handle, ok = load_library(library_path); !ok {
  107. return -1, false
  108. }
  109. // `symbol_table` must be a struct because of the where clause, so this can't fail.
  110. ti := runtime.type_info_base(type_info_of(T))
  111. s, _ := ti.variant.(runtime.Type_Info_Struct)
  112. // Buffer to concatenate the prefix + symbol name.
  113. prefixed_symbol_buf: [2048]u8 = ---
  114. sym_ptr: rawptr
  115. for field_name, i in s.names {
  116. // Calculate address of struct member
  117. field_ptr := rawptr(uintptr(rawptr(symbol_table)) + uintptr(s.offsets[i]))
  118. // If we've come across the struct member for the handle, store it and continue scanning for other symbols.
  119. if field_name == handle_field_name {
  120. // We appear to be hot reloading. Unload previous incarnation of the library.
  121. if old_handle := (^Library)(field_ptr)^; old_handle != nil {
  122. if ok = unload_library(old_handle); !ok {
  123. return count, ok
  124. }
  125. }
  126. (^Library)(field_ptr)^ = handle
  127. continue
  128. }
  129. // We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
  130. if !(reflect.is_procedure(s.types[i]) || reflect.is_pointer(s.types[i])) {
  131. continue
  132. }
  133. // Let's look up or construct the symbol name to find in the library
  134. prefixed_name: string
  135. // Do we have a symbol override tag?
  136. if override, tag_ok := reflect.struct_tag_lookup(reflect.Struct_Tag(s.tags[i]), "dynlib"); tag_ok {
  137. prefixed_name = string(override)
  138. }
  139. // No valid symbol override tag found, fall back to `<symbol_prefix>name`.
  140. if len(prefixed_name) == 0 {
  141. offset := copy(prefixed_symbol_buf[:], symbol_prefix)
  142. copy(prefixed_symbol_buf[offset:], field_name)
  143. prefixed_name = string(prefixed_symbol_buf[:len(symbol_prefix) + len(field_name)])
  144. }
  145. // Assign procedure (or global) pointer if found.
  146. if sym_ptr, ok = symbol_address(handle, prefixed_name); ok {
  147. (^rawptr)(field_ptr)^ = sym_ptr
  148. count += 1
  149. }
  150. }
  151. return count, count > 0
  152. }
  153. /*
  154. Returns an error message for the last failed procedure call.
  155. */
  156. last_error :: proc() -> string {
  157. return _last_error()
  158. }