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
|
""" 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
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)
def _dlerror_on_dlopen_untranslated(name):
"NOT_RPYTHON: 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()
raise DLOpenError(rwin32.FormatError(err))
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
|