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