1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
|
""" Various rpython-level functions for dlopen
"""
from rpython.rtyper.tool import rffi_platform
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib.objectmodel import we_are_translated, not_rpython
from rpython.rlib.rarithmetic import r_uint
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.translator.platform import platform
import sys, os, string
# maaaybe isinstance here would be better. Think
_MSVC = platform.name == "msvc"
_MINGW = platform.name == "mingw32"
_WIN32 = _MSVC or _MINGW
_MAC_OS = platform.name == "darwin"
_FREEBSD = sys.platform.startswith("freebsd")
_NETBSD = sys.platform.startswith("netbsd")
if _WIN32:
from rpython.rlib import rwin32
includes = ['windows.h']
else:
includes = ['dlfcn.h']
if _MAC_OS:
pre_include_bits = ['#define MACOSX']
else:
pre_include_bits = []
if _FREEBSD or _NETBSD or _WIN32:
libraries = []
else:
libraries = ['dl']
# this 'eci' is also used in pypy/module/sys/initpath.py
eci = ExternalCompilationInfo(
pre_include_bits = pre_include_bits,
includes = includes,
libraries = libraries,
)
class CConfig:
_compilation_info_ = eci
RTLD_LOCAL = rffi_platform.DefinedConstantInteger('RTLD_LOCAL')
RTLD_GLOBAL = rffi_platform.DefinedConstantInteger('RTLD_GLOBAL')
RTLD_NOW = rffi_platform.DefinedConstantInteger('RTLD_NOW')
RTLD_LAZY = rffi_platform.DefinedConstantInteger('RTLD_LAZY')
RTLD_NODELETE = rffi_platform.DefinedConstantInteger('RTLD_NODELETE')
RTLD_NOLOAD = rffi_platform.DefinedConstantInteger('RTLD_NOLOAD')
RTLD_DEEPBIND = rffi_platform.DefinedConstantInteger('RTLD_DEEPBIND')
class cConfig:
pass
for k, v in rffi_platform.configure(CConfig).items():
setattr(cConfig, k, v)
def external(name, args, result, **kwds):
return rffi.llexternal(name, args, result, compilation_info=eci, **kwds)
class DLOpenError(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return repr(self.msg)
if not _WIN32:
c_dlopen = external('dlopen', [rffi.CCHARP, rffi.INT], rffi.VOIDP)
c_dlclose = external('dlclose', [rffi.VOIDP], rffi.INT, releasegil=False)
c_dlerror = external('dlerror', [], rffi.CCHARP)
c_dlsym = external('dlsym', [rffi.VOIDP, rffi.CCHARP], rffi.VOIDP)
DLLHANDLE = rffi.VOIDP
RTLD_LOCAL = cConfig.RTLD_LOCAL
RTLD_GLOBAL = cConfig.RTLD_GLOBAL
RTLD_NOW = cConfig.RTLD_NOW
RTLD_LAZY = cConfig.RTLD_LAZY
def dlerror():
# XXX this would never work on top of ll2ctypes, because
# ctypes are calling dlerror itself, unsure if I can do much in this
# area (nor I would like to)
if not we_are_translated():
return "error info not available, not translated"
res = c_dlerror()
if not res:
return ""
return rffi.charp2str(res)
@not_rpython
def _dlerror_on_dlopen_untranslated(name):
# aaargh
import ctypes
name = rffi.charp2str(name)
try:
ctypes.CDLL(name)
except OSError as e:
# common case: ctypes fails too, with the real dlerror()
# message in str(e). Return that error message.
return str(e)
else:
# uncommon case: may happen if 'name' is a linker script
# (which the C-level dlopen() can't handle) and we are
# directly running on pypy (whose implementation of ctypes
# or cffi will resolve linker scripts). In that case,
# unsure what we can do.
return ("opening %r with ctypes.CDLL() works, "
"but not with c_dlopen()??" % (name,))
def _retry_as_ldscript(err, mode):
""" ld scripts are fairly straightforward to parse (the library we want
is in a form like 'GROUP ( <actual-filepath.so>'. A simple state machine
can parse that out (avoids regexes)."""
parts = err.split(":")
if len(parts) != 2:
return lltype.nullptr(rffi.VOIDP.TO)
fullpath = parts[0]
actual = ""
last_five = " "
state = 0
ldscript = os.open(fullpath, os.O_RDONLY, 0777)
c = os.read(ldscript, 1)
while c != "":
if state == 0:
last_five += c
last_five = last_five[1:6]
if last_five == "GROUP":
state = 1
elif state == 1:
if c == "(":
state = 2
elif state == 2:
if c not in string.whitespace:
actual += c
state = 3
elif state == 3:
if c in string.whitespace or c == ")":
break
else:
actual += c
c = os.read(ldscript, 1)
os.close(ldscript)
if actual != "":
a = rffi.str2charp(actual)
lib = c_dlopen(a, rffi.cast(rffi.INT, mode))
rffi.free_charp(a)
return lib
else:
return lltype.nullptr(rffi.VOIDP.TO)
def _dlopen_default_mode():
""" The default dlopen mode if it hasn't been changed by the user.
"""
mode = RTLD_NOW
if RTLD_LOCAL is not None:
mode |= RTLD_LOCAL
return mode
def dlopen(name, mode=-1):
""" Wrapper around C-level dlopen
"""
if mode == -1:
mode = _dlopen_default_mode()
elif (mode & (RTLD_LAZY | RTLD_NOW)) == 0:
mode |= RTLD_NOW
#
# haaaack for 'pypy py.test -A' if libm.so is a linker script
# (see reason in _dlerror_on_dlopen_untranslated())
must_free = False
if not we_are_translated() and platform.name == "linux":
if name and rffi.charp2str(name) == 'libm.so':
name = rffi.str2charp('libm.so.6')
must_free = True
#
res = c_dlopen(name, rffi.cast(rffi.INT, mode))
if must_free:
rffi.free_charp(name)
if not res:
if not we_are_translated():
err = _dlerror_on_dlopen_untranslated(name)
else:
err = dlerror()
if platform.name == "linux" and 'invalid ELF header' in err:
# some linux distros put ld linker scripts in .so files
# to load libraries more dynamically. The error contains the
# full path to something that is probably a script to load
# the library we want.
res = _retry_as_ldscript(err, mode)
if not res:
raise DLOpenError(err)
return res
else:
raise DLOpenError(err)
return res
dlclose = c_dlclose
def dlsym(libhandle, name):
""" Wrapper around C-level dlsym
"""
res = c_dlsym(libhandle, name)
if not res:
raise KeyError(name)
# XXX rffi.cast here...
return res
def dlsym_byordinal(handle, index):
# Never called
raise KeyError(index)
else: # _WIN32
DLLHANDLE = rwin32.HMODULE
RTLD_GLOBAL = None
def _dlopen_default_mode():
""" The default dlopen mode if it hasn't been changed by the user.
"""
return 0
def dlopen(name, mode=-1):
# mode is unused on windows, but a consistant signature
res = rwin32.LoadLibrary(name)
if not res:
err = rwin32.GetLastError_saved()
ustr, lgt = rwin32.FormatErrorW(err)
raise DLOpenError(ustr)
return res
def dlopenex(name):
res = rwin32.LoadLibraryExA(name)
if not res:
err = rwin32.GetLastError_saved()
ustr, lgt = rwin32.FormatErrorW(err)
raise DLOpenError(ustr)
return res
def dlopenU(name, mode=-1):
# mode is unused on windows, but a consistant signature
res = rwin32.LoadLibraryW(name)
if not res:
err = rwin32.GetLastError_saved()
ustr, lgt = rwin32.FormatErrorW(err)
raise DLOpenError(ustr)
return res
def dlclose(handle):
res = rwin32.FreeLibrary(handle)
if res:
return 0 # success
else:
return -1 # error
def dlsym(handle, name):
res = rwin32.GetProcAddress(handle, name)
if not res:
raise KeyError(name)
# XXX rffi.cast here...
return res
def dlsym_byordinal(handle, index):
# equivalent to MAKEINTRESOURCEA
intresource = rffi.cast(rffi.CCHARP, r_uint(index) & 0xFFFF)
res = rwin32.GetProcAddress(handle, intresource)
if not res:
raise KeyError(index)
# XXX rffi.cast here...
return res
LoadLibrary = rwin32.LoadLibrary
GetModuleHandle = rwin32.GetModuleHandle
|