dyncalls.nim 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This file implements the ability to call native procs from libraries.
  10. # It is not possible to do this in a platform independent way, unfortunately.
  11. # However, the interface has been designed to take platform differences into
  12. # account and been ported to all major platforms.
  13. {.push stack_trace: off.}
  14. const
  15. NilLibHandle: LibHandle = nil
  16. proc nimLoadLibraryError(path: string) =
  17. # carefully written to avoid memory allocation:
  18. const prefix = "could not load: "
  19. cstderr.rawWrite(prefix)
  20. cstderr.rawWrite(path)
  21. when not defined(nimDebugDlOpen) and not defined(windows):
  22. cstderr.rawWrite("\n(compile with -d:nimDebugDlOpen for more information)")
  23. when defined(windows):
  24. const badExe = "\n(bad format; library may be wrong architecture)"
  25. let loadError = GetLastError()
  26. if loadError == ERROR_BAD_EXE_FORMAT:
  27. cstderr.rawWrite(badExe)
  28. when defined(guiapp):
  29. # Because console output is not shown in GUI apps, display the error as a
  30. # message box instead:
  31. var
  32. msg: array[1000, char]
  33. msgLeft = msg.len - 1 # leave (at least) one for nullchar
  34. msgIdx = 0
  35. copyMem(msg[msgIdx].addr, prefix.cstring, prefix.len)
  36. msgLeft -= prefix.len
  37. msgIdx += prefix.len
  38. let pathLen = min(path.len, msgLeft)
  39. copyMem(msg[msgIdx].addr, path.cstring, pathLen)
  40. msgLeft -= pathLen
  41. msgIdx += pathLen
  42. if loadError == ERROR_BAD_EXE_FORMAT and msgLeft >= badExe.len:
  43. copyMem(msg[msgIdx].addr, badExe.cstring, badExe.len)
  44. discard MessageBoxA(nil, msg[0].addr, nil, 0)
  45. cstderr.rawWrite("\n")
  46. rawQuit(1)
  47. proc procAddrError(name: cstring) {.compilerproc, nonReloadable, hcrInline.} =
  48. # carefully written to avoid memory allocation:
  49. cstderr.rawWrite("could not import: ")
  50. cstderr.rawWrite(name)
  51. cstderr.rawWrite("\n")
  52. rawQuit(1)
  53. # this code was inspired from Lua's source code:
  54. # Lua - An Extensible Extension Language
  55. # Tecgraf: Computer Graphics Technology Group, PUC-Rio, Brazil
  56. # http://www.lua.org
  57. # mailto:info@lua.org
  58. when defined(posix):
  59. #
  60. # =========================================================================
  61. # This is an implementation based on the dlfcn interface.
  62. # The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
  63. # NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
  64. # as an emulation layer on top of native functions.
  65. # =========================================================================
  66. #
  67. # c stuff:
  68. when defined(linux) or defined(macosx):
  69. const RTLD_NOW = cint(2)
  70. else:
  71. var
  72. RTLD_NOW {.importc: "RTLD_NOW", header: "<dlfcn.h>".}: cint
  73. proc dlclose(lib: LibHandle) {.importc, header: "<dlfcn.h>".}
  74. proc dlopen(path: cstring, mode: cint): LibHandle {.
  75. importc, header: "<dlfcn.h>".}
  76. proc dlsym(lib: LibHandle, name: cstring): ProcAddr {.
  77. importc, header: "<dlfcn.h>".}
  78. proc dlerror(): cstring {.importc, header: "<dlfcn.h>".}
  79. proc nimUnloadLibrary(lib: LibHandle) =
  80. dlclose(lib)
  81. proc nimLoadLibrary(path: string): LibHandle =
  82. let flags =
  83. when defined(globalSymbols): RTLD_NOW or RTLD_GLOBAL
  84. else: RTLD_NOW
  85. result = dlopen(path, flags)
  86. when defined(nimDebugDlOpen):
  87. let error = dlerror()
  88. if error != nil:
  89. cstderr.rawWrite(error)
  90. cstderr.rawWrite("\n")
  91. proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
  92. result = dlsym(lib, name)
  93. if result == nil: procAddrError(name)
  94. elif defined(windows) or defined(dos):
  95. #
  96. # =======================================================================
  97. # Native Windows Implementation
  98. # =======================================================================
  99. #
  100. when defined(cpp):
  101. type
  102. THINSTANCE {.importc: "HINSTANCE".} = object
  103. x: pointer
  104. proc getProcAddress(lib: THINSTANCE, name: cstring): ProcAddr {.
  105. importcpp: "(void*)GetProcAddress(@)", header: "<windows.h>", stdcall.}
  106. else:
  107. type
  108. THINSTANCE {.importc: "HINSTANCE".} = pointer
  109. proc getProcAddress(lib: THINSTANCE, name: cstring): ProcAddr {.
  110. importc: "GetProcAddress", header: "<windows.h>", stdcall.}
  111. proc freeLibrary(lib: THINSTANCE) {.
  112. importc: "FreeLibrary", header: "<windows.h>", stdcall.}
  113. proc winLoadLibrary(path: cstring): THINSTANCE {.
  114. importc: "LoadLibraryA", header: "<windows.h>", stdcall.}
  115. proc nimUnloadLibrary(lib: LibHandle) =
  116. freeLibrary(cast[THINSTANCE](lib))
  117. proc nimLoadLibrary(path: string): LibHandle =
  118. result = cast[LibHandle](winLoadLibrary(path))
  119. proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
  120. result = getProcAddress(cast[THINSTANCE](lib), name)
  121. if result != nil: return
  122. const decoratedLength = 250
  123. var decorated: array[decoratedLength, char]
  124. decorated[0] = '_'
  125. var m = 1
  126. while m < (decoratedLength - 5):
  127. if name[m - 1] == '\x00': break
  128. decorated[m] = name[m - 1]
  129. inc(m)
  130. decorated[m] = '@'
  131. for i in countup(0, 50):
  132. var k = i * 4
  133. if k div 100 == 0:
  134. if k div 10 == 0:
  135. m = m + 1
  136. else:
  137. m = m + 2
  138. else:
  139. m = m + 3
  140. decorated[m + 1] = '\x00'
  141. while true:
  142. decorated[m] = chr(ord('0') + (k %% 10))
  143. dec(m)
  144. k = k div 10
  145. if k == 0: break
  146. result = getProcAddress(cast[THINSTANCE](lib), cast[cstring](addr decorated))
  147. if result != nil: return
  148. procAddrError(name)
  149. elif defined(genode):
  150. proc nimUnloadLibrary(lib: LibHandle) =
  151. raiseAssert("nimUnloadLibrary not implemented")
  152. proc nimLoadLibrary(path: string): LibHandle =
  153. raiseAssert("nimLoadLibrary not implemented")
  154. proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
  155. raiseAssert("nimGetProcAddr not implemented")
  156. elif defined(nintendoswitch) or defined(freertos) or defined(zephyr):
  157. proc nimUnloadLibrary(lib: LibHandle) =
  158. cstderr.rawWrite("nimUnLoadLibrary not implemented")
  159. cstderr.rawWrite("\n")
  160. rawQuit(1)
  161. proc nimLoadLibrary(path: string): LibHandle =
  162. cstderr.rawWrite("nimLoadLibrary not implemented")
  163. cstderr.rawWrite("\n")
  164. rawQuit(1)
  165. proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr =
  166. cstderr.rawWrite("nimGetProAddr not implemented")
  167. cstderr.rawWrite(name)
  168. cstderr.rawWrite("\n")
  169. rawQuit(1)
  170. else:
  171. {.error: "no implementation for dyncalls".}
  172. {.pop.}