PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/rlib/rmmap.py

https://bitbucket.org/pypy/pypy/
Python | 964 lines | 884 code | 42 blank | 38 comment | 46 complexity | 26c5b305bf5f29d3e392235c5c520e3e MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. """Interp-level mmap-like object.
  2. Note that all the methods assume that the mmap is valid (or writable, for
  3. writing methods). You have to call check_valid() from the higher-level API,
  4. as well as maybe check_writeable(). In the case of PyPy, this is done from
  5. pypy/module/mmap/.
  6. """
  7. from rpython.rtyper.tool import rffi_platform
  8. from rpython.rtyper.lltypesystem import rffi, lltype
  9. from rpython.rlib import rposix
  10. from rpython.translator.tool.cbuild import ExternalCompilationInfo
  11. from rpython.rlib.objectmodel import we_are_translated
  12. from rpython.rlib.nonconst import NonConstant
  13. from rpython.rlib.rarithmetic import intmask
  14. import sys
  15. import os
  16. import platform
  17. import stat
  18. _POSIX = os.name == "posix"
  19. _MS_WINDOWS = os.name == "nt"
  20. _64BIT = "64bit" in platform.architecture()[0]
  21. _CYGWIN = "cygwin" == sys.platform
  22. class RMMapError(Exception):
  23. def __init__(self, message):
  24. self.message = message
  25. class RValueError(RMMapError):
  26. pass
  27. class RTypeError(RMMapError):
  28. pass
  29. includes = ["sys/types.h"]
  30. if _POSIX:
  31. includes += ['unistd.h', 'sys/mman.h']
  32. elif _MS_WINDOWS:
  33. includes += ['winsock2.h', 'windows.h']
  34. class CConfig:
  35. _compilation_info_ = ExternalCompilationInfo(
  36. includes=includes,
  37. #pre_include_bits=['#ifndef _GNU_SOURCE\n' +
  38. # '#define _GNU_SOURCE\n' +
  39. # '#endif']
  40. # ^^^ _GNU_SOURCE is always defined by the ExternalCompilationInfo now
  41. )
  42. size_t = rffi_platform.SimpleType("size_t", rffi.LONG)
  43. off_t = rffi_platform.SimpleType("off_t", rffi.LONG)
  44. constants = {}
  45. if _POSIX:
  46. # constants, look in sys/mman.h and platform docs for the meaning
  47. # some constants are linux only so they will be correctly exposed outside
  48. # depending on the OS
  49. constant_names = ['MAP_SHARED', 'MAP_PRIVATE', 'MAP_FIXED',
  50. 'PROT_READ', 'PROT_WRITE',
  51. 'MS_SYNC']
  52. opt_constant_names = ['MAP_ANON', 'MAP_ANONYMOUS', 'MAP_NORESERVE',
  53. 'PROT_EXEC',
  54. 'MAP_DENYWRITE', 'MAP_EXECUTABLE']
  55. for name in constant_names:
  56. setattr(CConfig, name, rffi_platform.ConstantInteger(name))
  57. for name in opt_constant_names:
  58. setattr(CConfig, name, rffi_platform.DefinedConstantInteger(name))
  59. CConfig.MREMAP_MAYMOVE = (
  60. rffi_platform.DefinedConstantInteger("MREMAP_MAYMOVE"))
  61. CConfig.has_mremap = rffi_platform.Has('mremap(NULL, 0, 0, 0)')
  62. CConfig.has_madvise = rffi_platform.Has('madvise(NULL, 0, 0)')
  63. # ^^ both are a dirty hack, this is probably a macro
  64. CConfig.MADV_DONTNEED = (
  65. rffi_platform.DefinedConstantInteger('MADV_DONTNEED'))
  66. CConfig.MADV_FREE = (
  67. rffi_platform.DefinedConstantInteger('MADV_FREE'))
  68. elif _MS_WINDOWS:
  69. constant_names = ['PAGE_READONLY', 'PAGE_READWRITE', 'PAGE_WRITECOPY',
  70. 'FILE_MAP_READ', 'FILE_MAP_WRITE', 'FILE_MAP_COPY',
  71. 'DUPLICATE_SAME_ACCESS', 'MEM_COMMIT', 'MEM_RESERVE',
  72. 'MEM_RELEASE', 'PAGE_EXECUTE_READWRITE', 'PAGE_NOACCESS',
  73. 'MEM_RESET']
  74. for name in constant_names:
  75. setattr(CConfig, name, rffi_platform.ConstantInteger(name))
  76. from rpython.rlib import rwin32
  77. from rpython.rlib.rwin32 import HANDLE, LPHANDLE
  78. from rpython.rlib.rwin32 import NULL_HANDLE, INVALID_HANDLE_VALUE
  79. from rpython.rlib.rwin32 import DWORD, WORD, DWORD_PTR, LPDWORD
  80. from rpython.rlib.rwin32 import BOOL, LPVOID, LPCSTR, SIZE_T
  81. from rpython.rlib.rwin32 import LONG, PLONG
  82. # export the constants inside and outside. see __init__.py
  83. cConfig = rffi_platform.configure(CConfig)
  84. constants.update(cConfig)
  85. if _POSIX:
  86. # MAP_ANONYMOUS is not always present but it's always available at CPython level
  87. if constants["MAP_ANONYMOUS"] is None:
  88. constants["MAP_ANONYMOUS"] = constants["MAP_ANON"]
  89. assert constants["MAP_ANONYMOUS"] is not None
  90. constants["MAP_ANON"] = constants["MAP_ANONYMOUS"]
  91. locals().update(constants)
  92. _ACCESS_DEFAULT, ACCESS_READ, ACCESS_WRITE, ACCESS_COPY = range(4)
  93. if rffi.sizeof(off_t) > rffi.sizeof(lltype.Signed):
  94. HAVE_LARGEFILE_SUPPORT = True
  95. else:
  96. HAVE_LARGEFILE_SUPPORT = False
  97. def external(name, args, result, save_err_on_unsafe=0, save_err_on_safe=0,
  98. **kwargs):
  99. unsafe = rffi.llexternal(name, args, result,
  100. compilation_info=CConfig._compilation_info_,
  101. save_err=save_err_on_unsafe,
  102. **kwargs)
  103. safe = rffi.llexternal(name, args, result,
  104. compilation_info=CConfig._compilation_info_,
  105. sandboxsafe=True, releasegil=False,
  106. save_err=save_err_on_safe,
  107. **kwargs)
  108. return unsafe, safe
  109. def winexternal(name, args, result, **kwargs):
  110. unsafe = rffi.llexternal(name, args, result,
  111. compilation_info=CConfig._compilation_info_,
  112. calling_conv='win',
  113. **kwargs)
  114. safe = rffi.llexternal(name, args, result,
  115. compilation_info=CConfig._compilation_info_,
  116. calling_conv='win',
  117. sandboxsafe=True, releasegil=False,
  118. **kwargs)
  119. return unsafe, safe
  120. PTR = rffi.CCHARP
  121. if _CYGWIN:
  122. # XXX: macro=True hack for newer versions of Cygwin (as of 12/2012)
  123. _, c_malloc_safe = external('malloc', [size_t], PTR, macro=True)
  124. _, c_free_safe = external('free', [PTR], lltype.Void, macro=True)
  125. c_memmove, _ = external('memmove', [PTR, PTR, size_t], lltype.Void)
  126. if _POSIX:
  127. has_mremap = cConfig['has_mremap']
  128. has_madvise = cConfig['has_madvise']
  129. c_mmap, c_mmap_safe = external('mmap', [PTR, size_t, rffi.INT, rffi.INT,
  130. rffi.INT, off_t], PTR, macro=True,
  131. save_err_on_unsafe=rffi.RFFI_SAVE_ERRNO)
  132. # 'mmap' on linux32 is a macro that calls 'mmap64'
  133. _, c_munmap_safe = external('munmap', [PTR, size_t], rffi.INT)
  134. c_msync, _ = external('msync', [PTR, size_t, rffi.INT], rffi.INT,
  135. save_err_on_unsafe=rffi.RFFI_SAVE_ERRNO)
  136. if has_mremap:
  137. c_mremap, _ = external('mremap',
  138. [PTR, size_t, size_t, rffi.ULONG], PTR)
  139. if has_madvise:
  140. _, c_madvise_safe = external('madvise', [PTR, size_t, rffi.INT],
  141. rffi.INT, _nowrapper=True)
  142. # this one is always safe
  143. _pagesize = rffi_platform.getintegerfunctionresult('getpagesize',
  144. includes=includes)
  145. _get_allocation_granularity = _get_page_size = lambda: _pagesize
  146. elif _MS_WINDOWS:
  147. class ComplexCConfig:
  148. _compilation_info_ = CConfig._compilation_info_
  149. SYSINFO_STRUCT = rffi.CStruct(
  150. 'SYSINFO_STRUCT',
  151. ("wProcessorArchitecture", WORD),
  152. ("wReserved", WORD),
  153. )
  154. SYSINFO_UNION = rffi.CStruct(
  155. 'union SYSINFO_UNION',
  156. ("dwOemId", DWORD),
  157. ("_struct_", SYSINFO_STRUCT),
  158. )
  159. # sorry, I can't find a way to insert the above
  160. # because the union field has no name
  161. SYSTEM_INFO = rffi_platform.Struct(
  162. 'SYSTEM_INFO', [
  163. ## ("_union_", SYSINFO_UNION),
  164. ## instead, we put the smaller fields, here
  165. ("wProcessorArchitecture", WORD),
  166. ("wReserved", WORD),
  167. ## should be a union. dwOemId is obsolete, anyway
  168. ("dwPageSize", DWORD),
  169. ("lpMinimumApplicationAddress", LPVOID),
  170. ("lpMaximumApplicationAddress", LPVOID),
  171. ("dwActiveProcessorMask", DWORD_PTR),
  172. ("dwNumberOfProcessors", DWORD),
  173. ("dwProcessorType", DWORD),
  174. ("dwAllocationGranularity", DWORD),
  175. ("wProcessorLevel", WORD),
  176. ("wProcessorRevision", WORD),
  177. ])
  178. config = rffi_platform.configure(ComplexCConfig)
  179. SYSTEM_INFO = config['SYSTEM_INFO']
  180. SYSTEM_INFO_P = lltype.Ptr(SYSTEM_INFO)
  181. GetSystemInfo, _ = winexternal('GetSystemInfo', [SYSTEM_INFO_P], lltype.Void)
  182. GetFileSize, _ = winexternal('GetFileSize', [HANDLE, LPDWORD], DWORD,
  183. save_err=rffi.RFFI_SAVE_LASTERROR)
  184. GetCurrentProcess, _ = winexternal('GetCurrentProcess', [], HANDLE)
  185. DuplicateHandle, _ = winexternal('DuplicateHandle',
  186. [HANDLE, HANDLE, HANDLE, LPHANDLE, DWORD,
  187. BOOL, DWORD], BOOL,
  188. save_err=rffi.RFFI_SAVE_LASTERROR)
  189. CreateFileMapping, _ = winexternal('CreateFileMappingA',
  190. [HANDLE, rwin32.LPSECURITY_ATTRIBUTES,
  191. DWORD, DWORD, DWORD, LPCSTR], HANDLE,
  192. save_err=rffi.RFFI_SAVE_LASTERROR)
  193. MapViewOfFile, _ = winexternal('MapViewOfFile', [HANDLE, DWORD, DWORD,
  194. DWORD, SIZE_T], LPCSTR,
  195. save_err=rffi.RFFI_SAVE_LASTERROR) ##!!LPVOID
  196. _, UnmapViewOfFile_safe = winexternal('UnmapViewOfFile', [LPCSTR], BOOL)
  197. FlushViewOfFile, _ = winexternal('FlushViewOfFile', [LPCSTR, SIZE_T], BOOL)
  198. SetFilePointer, _ = winexternal('SetFilePointer', [HANDLE, LONG, PLONG, DWORD], DWORD)
  199. SetEndOfFile, _ = winexternal('SetEndOfFile', [HANDLE], BOOL)
  200. VirtualAlloc, VirtualAlloc_safe = winexternal('VirtualAlloc',
  201. [rffi.VOIDP, rffi.SIZE_T, DWORD, DWORD],
  202. rffi.VOIDP)
  203. _, _VirtualAlloc_safe_no_wrapper = winexternal('VirtualAlloc',
  204. [rffi.VOIDP, rffi.SIZE_T, DWORD, DWORD],
  205. rffi.VOIDP, _nowrapper=True)
  206. _, _VirtualProtect_safe = winexternal('VirtualProtect',
  207. [rffi.VOIDP, rffi.SIZE_T, DWORD, LPDWORD],
  208. BOOL)
  209. def VirtualProtect(addr, size, mode, oldmode_ptr):
  210. return _VirtualProtect_safe(addr,
  211. rffi.cast(rffi.SIZE_T, size),
  212. rffi.cast(DWORD, mode),
  213. oldmode_ptr)
  214. VirtualProtect._annspecialcase_ = 'specialize:ll'
  215. VirtualFree, VirtualFree_safe = winexternal('VirtualFree',
  216. [rffi.VOIDP, rffi.SIZE_T, DWORD], BOOL)
  217. def _get_page_size():
  218. try:
  219. si = rffi.make(SYSTEM_INFO)
  220. GetSystemInfo(si)
  221. return int(si.c_dwPageSize)
  222. finally:
  223. lltype.free(si, flavor="raw")
  224. def _get_allocation_granularity():
  225. try:
  226. si = rffi.make(SYSTEM_INFO)
  227. GetSystemInfo(si)
  228. return int(si.c_dwAllocationGranularity)
  229. finally:
  230. lltype.free(si, flavor="raw")
  231. def _get_file_size(handle):
  232. # XXX use native Windows types like WORD
  233. high_ref = lltype.malloc(LPDWORD.TO, 1, flavor='raw')
  234. try:
  235. low = GetFileSize(handle, high_ref)
  236. low = rffi.cast(lltype.Signed, low)
  237. # XXX should be propagate the real type, allowing
  238. # for 2*sys.maxint?
  239. high = high_ref[0]
  240. high = rffi.cast(lltype.Signed, high)
  241. # low might just happen to have the value INVALID_FILE_SIZE
  242. # so we need to check the last error also
  243. INVALID_FILE_SIZE = -1
  244. if low == INVALID_FILE_SIZE:
  245. err = rwin32.GetLastError_saved()
  246. if err:
  247. raise WindowsError(err, "mmap")
  248. return low, high
  249. finally:
  250. lltype.free(high_ref, flavor='raw')
  251. INVALID_HANDLE = INVALID_HANDLE_VALUE
  252. PAGESIZE = _get_page_size()
  253. ALLOCATIONGRANULARITY = _get_allocation_granularity()
  254. NULL = lltype.nullptr(PTR.TO)
  255. NODATA = lltype.nullptr(PTR.TO)
  256. class MMap(object):
  257. def __init__(self, access, offset):
  258. self.size = 0
  259. self.pos = 0
  260. self.access = access
  261. self.offset = offset
  262. if _MS_WINDOWS:
  263. self.map_handle = NULL_HANDLE
  264. self.file_handle = NULL_HANDLE
  265. self.tagname = ""
  266. elif _POSIX:
  267. self.fd = -1
  268. self.closed = False
  269. def check_valid(self):
  270. if _MS_WINDOWS:
  271. to_close = self.map_handle == INVALID_HANDLE
  272. elif _POSIX:
  273. to_close = self.closed
  274. if to_close:
  275. raise RValueError("map closed or invalid")
  276. def check_writeable(self):
  277. if not (self.access != ACCESS_READ):
  278. raise RTypeError("mmap can't modify a readonly memory map.")
  279. def check_resizeable(self):
  280. if not (self.access == ACCESS_WRITE or self.access == _ACCESS_DEFAULT):
  281. raise RTypeError("mmap can't resize a readonly or copy-on-write memory map.")
  282. def setdata(self, data, size):
  283. """Set the internal data and map size from a PTR."""
  284. assert size >= 0
  285. self.data = data
  286. self.size = size
  287. def unmap(self):
  288. if _MS_WINDOWS:
  289. UnmapViewOfFile_safe(self.getptr(0))
  290. elif _POSIX:
  291. self.unmap_range(0, self.size)
  292. if _POSIX:
  293. def unmap_range(self, offset, size):
  294. """Unmap (a portion of) the mapped range.
  295. Per munmap(1), the offset must be a multiple of the page size,
  296. and the size will be rounded up to a multiple of the page size.
  297. """
  298. c_munmap_safe(self.getptr(offset), size)
  299. def close(self):
  300. if _MS_WINDOWS:
  301. if self.size > 0:
  302. self.unmap()
  303. self.setdata(NODATA, 0)
  304. if self.map_handle != INVALID_HANDLE:
  305. rwin32.CloseHandle_no_err(self.map_handle)
  306. self.map_handle = INVALID_HANDLE
  307. if self.file_handle != INVALID_HANDLE:
  308. rwin32.CloseHandle_no_err(self.file_handle)
  309. self.file_handle = INVALID_HANDLE
  310. elif _POSIX:
  311. self.closed = True
  312. if self.fd != -1:
  313. # XXX this is buggy - raising in an RPython del is not a good
  314. # idea, we should swallow the exception or ignore the
  315. # underlaying close error code
  316. os.close(self.fd)
  317. self.fd = -1
  318. if self.size > 0:
  319. self.unmap()
  320. self.setdata(NODATA, 0)
  321. def __del__(self):
  322. self.close()
  323. def read_byte(self):
  324. if self.pos < self.size:
  325. value = self.data[self.pos]
  326. self.pos += 1
  327. return value
  328. else:
  329. raise RValueError("read byte out of range")
  330. def readline(self):
  331. data = self.data
  332. for pos in xrange(self.pos, self.size):
  333. if data[pos] == '\n':
  334. eol = pos + 1 # we're interested in the position after new line
  335. break
  336. else: # no '\n' found
  337. eol = self.size
  338. res = self.getslice(self.pos, eol - self.pos)
  339. self.pos += len(res)
  340. return res
  341. def read(self, num=-1):
  342. if num < 0:
  343. # read all
  344. eol = self.size
  345. else:
  346. eol = self.pos + num
  347. # silently adjust out of range requests
  348. if eol > self.size:
  349. eol = self.size
  350. res = self.getslice(self.pos, eol - self.pos)
  351. self.pos += len(res)
  352. return res
  353. def find(self, tofind, start, end, reverse=False):
  354. # XXX naive! how can we reuse the rstr algorithm?
  355. if start < 0:
  356. start += self.size
  357. if start < 0:
  358. start = 0
  359. if end < 0:
  360. end += self.size
  361. if end < 0:
  362. end = 0
  363. elif end > self.size:
  364. end = self.size
  365. #
  366. upto = end - len(tofind)
  367. if not reverse:
  368. step = 1
  369. p = start
  370. if p > upto:
  371. return -1 # failure (empty range to search)
  372. else:
  373. step = -1
  374. p = upto
  375. upto = start
  376. if p < upto:
  377. return -1 # failure (empty range to search)
  378. #
  379. data = self.data
  380. while True:
  381. assert p >= 0
  382. for q in range(len(tofind)):
  383. if data[p+q] != tofind[q]:
  384. break # position 'p' is not a match
  385. else:
  386. # full match
  387. return p
  388. #
  389. if p == upto:
  390. return -1 # failure
  391. p += step
  392. def seek(self, pos, whence=0):
  393. dist = pos
  394. how = whence
  395. if how == 0: # relative to start
  396. where = dist
  397. elif how == 1: # relative to current position
  398. where = self.pos + dist
  399. elif how == 2: # relative to the end
  400. where = self.size + dist
  401. else:
  402. raise RValueError("unknown seek type")
  403. if not (0 <= where <= self.size):
  404. raise RValueError("seek out of range")
  405. self.pos = intmask(where)
  406. def tell(self):
  407. return self.pos
  408. def file_size(self):
  409. size = self.size
  410. if _MS_WINDOWS:
  411. if self.file_handle != INVALID_HANDLE:
  412. low, high = _get_file_size(self.file_handle)
  413. if not high and low <= sys.maxint:
  414. return low
  415. # not so sure if the signed/unsigned strictness is a good idea:
  416. high = rffi.cast(lltype.Unsigned, high)
  417. low = rffi.cast(lltype.Unsigned, low)
  418. size = (high << 32) + low
  419. size = rffi.cast(lltype.Signed, size)
  420. elif _POSIX:
  421. st = os.fstat(self.fd)
  422. size = st[stat.ST_SIZE]
  423. return size
  424. def write(self, data):
  425. data_len = len(data)
  426. start = self.pos
  427. if start + data_len > self.size:
  428. raise RValueError("data out of range")
  429. self.setslice(start, data)
  430. self.pos = start + data_len
  431. def write_byte(self, byte):
  432. if len(byte) != 1:
  433. raise RTypeError("write_byte() argument must be char")
  434. if self.pos >= self.size:
  435. raise RValueError("write byte out of range")
  436. self.data[self.pos] = byte[0]
  437. self.pos += 1
  438. def getptr(self, offset):
  439. return rffi.ptradd(self.data, offset)
  440. def getslice(self, start, length):
  441. return rffi.charpsize2str(self.getptr(start), length)
  442. def setslice(self, start, newdata):
  443. internaldata = self.data
  444. for i in range(len(newdata)):
  445. internaldata[start+i] = newdata[i]
  446. def flush(self, offset=0, size=0):
  447. if size == 0:
  448. size = self.size
  449. if offset < 0 or size < 0 or offset + size > self.size:
  450. raise RValueError("flush values out of range")
  451. else:
  452. start = self.getptr(offset)
  453. if _MS_WINDOWS:
  454. res = FlushViewOfFile(start, size)
  455. # XXX res == 0 means that an error occurred, but in CPython
  456. # this is not checked
  457. return res
  458. elif _POSIX:
  459. res = c_msync(start, size, MS_SYNC)
  460. if res == -1:
  461. errno = rposix.get_saved_errno()
  462. raise OSError(errno, os.strerror(errno))
  463. return 0
  464. def move(self, dest, src, count):
  465. # check boundings
  466. if (src < 0 or dest < 0 or count < 0 or
  467. src + count > self.size or dest + count > self.size):
  468. raise RValueError("source or destination out of range")
  469. datasrc = self.getptr(src)
  470. datadest = self.getptr(dest)
  471. c_memmove(datadest, datasrc, count)
  472. def resize(self, newsize):
  473. if _POSIX:
  474. if not has_mremap:
  475. raise RValueError("mmap: resizing not available--no mremap()")
  476. # resize the underlying file first
  477. os.ftruncate(self.fd, self.offset + newsize)
  478. # now resize the mmap
  479. newdata = c_mremap(self.getptr(0), self.size, newsize,
  480. MREMAP_MAYMOVE or 0)
  481. self.setdata(newdata, newsize)
  482. elif _MS_WINDOWS:
  483. # disconnect the mapping
  484. self.unmap()
  485. rwin32.CloseHandle_no_err(self.map_handle)
  486. # move to the desired EOF position
  487. if _64BIT:
  488. newsize_high = (self.offset + newsize) >> 32
  489. newsize_low = (self.offset + newsize) & 0xFFFFFFFF
  490. offset_high = self.offset >> 32
  491. offset_low = self.offset & 0xFFFFFFFF
  492. else:
  493. newsize_high = 0
  494. newsize_low = self.offset + newsize
  495. offset_high = 0
  496. offset_low = self.offset
  497. FILE_BEGIN = 0
  498. high_ref = lltype.malloc(PLONG.TO, 1, flavor='raw')
  499. try:
  500. high_ref[0] = rffi.cast(LONG, newsize_high)
  501. SetFilePointer(self.file_handle, newsize_low, high_ref,
  502. FILE_BEGIN)
  503. finally:
  504. lltype.free(high_ref, flavor='raw')
  505. # resize the file
  506. SetEndOfFile(self.file_handle)
  507. # create another mapping object and remap the file view
  508. res = CreateFileMapping(self.file_handle, NULL, PAGE_READWRITE,
  509. newsize_high, newsize_low, self.tagname)
  510. self.map_handle = res
  511. if self.map_handle:
  512. data = MapViewOfFile(self.map_handle, FILE_MAP_WRITE,
  513. offset_high, offset_low, newsize)
  514. if data:
  515. # XXX we should have a real LPVOID which must always be casted
  516. charp = rffi.cast(LPCSTR, data)
  517. self.setdata(charp, newsize)
  518. return
  519. winerror = rwin32.lastSavedWindowsError()
  520. if self.map_handle:
  521. rwin32.CloseHandle_no_err(self.map_handle)
  522. self.map_handle = INVALID_HANDLE
  523. raise winerror
  524. def len(self):
  525. return self.size
  526. def getitem(self, index):
  527. # simplified version, for rpython
  528. self.check_valid()
  529. if index < 0:
  530. index += self.size
  531. return self.data[index]
  532. def setitem(self, index, value):
  533. if len(value) != 1:
  534. raise RValueError("mmap assignment must be "
  535. "single-character string")
  536. if index < 0:
  537. index += self.size
  538. self.data[index] = value[0]
  539. def _check_map_size(size):
  540. if size < 0:
  541. raise RTypeError("memory mapped size must be positive")
  542. if _POSIX:
  543. def mmap(fileno, length, flags=MAP_SHARED,
  544. prot=PROT_WRITE | PROT_READ, access=_ACCESS_DEFAULT, offset=0):
  545. fd = fileno
  546. # check access is not there when flags and prot are there
  547. if access != _ACCESS_DEFAULT and ((flags != MAP_SHARED) or
  548. (prot != (PROT_WRITE | PROT_READ))):
  549. raise RValueError("mmap can't specify both access and flags, prot.")
  550. # check size boundaries
  551. _check_map_size(length)
  552. map_size = length
  553. if offset < 0:
  554. raise RValueError("negative offset")
  555. if access == ACCESS_READ:
  556. flags = MAP_SHARED
  557. prot = PROT_READ
  558. elif access == ACCESS_WRITE:
  559. flags = MAP_SHARED
  560. prot = PROT_READ | PROT_WRITE
  561. elif access == ACCESS_COPY:
  562. flags = MAP_PRIVATE
  563. prot = PROT_READ | PROT_WRITE
  564. elif access == _ACCESS_DEFAULT:
  565. # map prot to access type
  566. if prot & PROT_READ and prot & PROT_WRITE:
  567. pass # _ACCESS_DEFAULT
  568. elif prot & PROT_WRITE:
  569. access = ACCESS_WRITE
  570. else:
  571. access = ACCESS_READ
  572. else:
  573. raise RValueError("mmap invalid access parameter.")
  574. # check file size
  575. try:
  576. st = os.fstat(fd)
  577. except OSError:
  578. pass # ignore errors and trust map_size
  579. else:
  580. mode = st[stat.ST_MODE]
  581. size = st[stat.ST_SIZE]
  582. if stat.S_ISREG(mode):
  583. if map_size == 0:
  584. if size == 0:
  585. raise RValueError("cannot mmap an empty file")
  586. if offset > size:
  587. raise RValueError(
  588. "mmap offset is greater than file size")
  589. map_size = int(size - offset)
  590. if map_size != size - offset:
  591. raise RValueError("mmap length is too large")
  592. elif offset + map_size > size:
  593. raise RValueError("mmap length is greater than file size")
  594. m = MMap(access, offset)
  595. if fd == -1:
  596. # Assume the caller wants to map anonymous memory.
  597. # This is the same behaviour as Windows. mmap.mmap(-1, size)
  598. # on both Windows and Unix map anonymous memory.
  599. m.fd = -1
  600. flags |= MAP_ANONYMOUS
  601. else:
  602. m.fd = os.dup(fd)
  603. # XXX if we use hintp below in alloc, the NonConstant
  604. # is necessary since we want a general version of c_mmap
  605. # to be annotated with a non-constant pointer.
  606. res = c_mmap(NonConstant(NULL), map_size, prot, flags, fd, offset)
  607. if res == rffi.cast(PTR, -1):
  608. errno = rposix.get_saved_errno()
  609. raise OSError(errno, os.strerror(errno))
  610. m.setdata(res, map_size)
  611. return m
  612. def alloc_hinted(hintp, map_size):
  613. flags = MAP_PRIVATE | MAP_ANONYMOUS
  614. prot = PROT_EXEC | PROT_READ | PROT_WRITE
  615. if we_are_translated():
  616. flags = NonConstant(flags)
  617. prot = NonConstant(prot)
  618. return c_mmap_safe(hintp, map_size, prot, flags, -1, 0)
  619. def clear_large_memory_chunk_aligned(addr, map_size):
  620. addr = rffi.cast(PTR, addr)
  621. flags = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS
  622. prot = PROT_READ | PROT_WRITE
  623. if we_are_translated():
  624. flags = NonConstant(flags)
  625. prot = NonConstant(prot)
  626. res = c_mmap_safe(addr, map_size, prot, flags, -1, 0)
  627. return res == addr
  628. # XXX is this really necessary?
  629. class Hint:
  630. pos = -0x4fff0000 # for reproducible results
  631. hint = Hint()
  632. def alloc(map_size):
  633. """Allocate memory. This is intended to be used by the JIT,
  634. so the memory has the executable bit set and gets allocated
  635. internally in case of a sandboxed process.
  636. """
  637. from errno import ENOMEM
  638. from rpython.rlib import debug
  639. if _CYGWIN:
  640. # XXX: JIT memory should be using mmap MAP_PRIVATE with
  641. # PROT_EXEC but Cygwin's fork() fails. mprotect()
  642. # cannot be used, but seems to be unnecessary there.
  643. res = c_malloc_safe(map_size)
  644. if res == rffi.cast(PTR, 0):
  645. raise MemoryError
  646. return res
  647. res = alloc_hinted(rffi.cast(PTR, hint.pos), map_size)
  648. if res == rffi.cast(PTR, -1):
  649. # some systems (some versions of OS/X?) complain if they
  650. # are passed a non-zero address. Try again.
  651. res = alloc_hinted(rffi.cast(PTR, 0), map_size)
  652. if res == rffi.cast(PTR, -1):
  653. # ENOMEM simply raises MemoryError, but other errors are fatal
  654. if rposix.get_saved_errno() != ENOMEM:
  655. debug.fatalerror_notb(
  656. "Got an unexpected error trying to allocate some "
  657. "memory for the JIT (tried to do mmap() with "
  658. "PROT_EXEC|PROT_READ|PROT_WRITE). This can be caused "
  659. "by a system policy like PAX. You need to find how "
  660. "to work around the policy on your system.")
  661. raise MemoryError
  662. else:
  663. hint.pos += map_size
  664. return res
  665. alloc._annenforceargs_ = (int,)
  666. if _CYGWIN:
  667. free = c_free_safe
  668. else:
  669. free = c_munmap_safe
  670. if sys.platform.startswith('linux'):
  671. assert has_madvise
  672. assert MADV_DONTNEED is not None
  673. if MADV_FREE is None:
  674. MADV_FREE = 8 # from the kernel sources of Linux >= 4.5
  675. class CanUseMadvFree:
  676. ok = -1
  677. can_use_madv_free = CanUseMadvFree()
  678. def madvise_free(addr, map_size):
  679. # We don't know if we are running on a recent enough kernel
  680. # that supports MADV_FREE. Check that at runtime: if the
  681. # first call to madvise(MADV_FREE) fails, we assume it's
  682. # because of EINVAL and we fall back to MADV_DONTNEED.
  683. if can_use_madv_free.ok != 0:
  684. res = c_madvise_safe(rffi.cast(PTR, addr),
  685. rffi.cast(size_t, map_size),
  686. rffi.cast(rffi.INT, MADV_FREE))
  687. if can_use_madv_free.ok == -1:
  688. can_use_madv_free.ok = (rffi.cast(lltype.Signed, res) == 0)
  689. if can_use_madv_free.ok == 0:
  690. c_madvise_safe(rffi.cast(PTR, addr),
  691. rffi.cast(size_t, map_size),
  692. rffi.cast(rffi.INT, MADV_DONTNEED))
  693. elif has_madvise and not (MADV_FREE is MADV_DONTNEED is None):
  694. use_flag = MADV_FREE if MADV_FREE is not None else MADV_DONTNEED
  695. def madvise_free(addr, map_size):
  696. c_madvise_safe(rffi.cast(PTR, addr),
  697. rffi.cast(size_t, map_size),
  698. rffi.cast(rffi.INT, use_flag))
  699. else:
  700. def madvice_free(addr, map_size):
  701. "No madvice() on this platform"
  702. elif _MS_WINDOWS:
  703. def mmap(fileno, length, tagname="", access=_ACCESS_DEFAULT, offset=0):
  704. # XXX flags is or-ed into access by now.
  705. flags = 0
  706. # check size boundaries
  707. _check_map_size(length)
  708. map_size = length
  709. if offset < 0:
  710. raise RValueError("negative offset")
  711. flProtect = 0
  712. dwDesiredAccess = 0
  713. fh = NULL_HANDLE
  714. if access == ACCESS_READ:
  715. flProtect = PAGE_READONLY
  716. dwDesiredAccess = FILE_MAP_READ
  717. elif access == _ACCESS_DEFAULT or access == ACCESS_WRITE:
  718. flProtect = PAGE_READWRITE
  719. dwDesiredAccess = FILE_MAP_WRITE
  720. elif access == ACCESS_COPY:
  721. flProtect = PAGE_WRITECOPY
  722. dwDesiredAccess = FILE_MAP_COPY
  723. else:
  724. raise RValueError("mmap invalid access parameter.")
  725. # assume -1 and 0 both mean invalid file descriptor
  726. # to 'anonymously' map memory.
  727. if fileno != -1 and fileno != 0:
  728. fh = rwin32.get_osfhandle(fileno)
  729. # Win9x appears to need us seeked to zero
  730. # SEEK_SET = 0
  731. # libc._lseek(fileno, 0, SEEK_SET)
  732. # check file size
  733. try:
  734. low, high = _get_file_size(fh)
  735. except OSError:
  736. pass # ignore non-seeking files and errors and trust map_size
  737. else:
  738. if not high and low <= sys.maxint:
  739. size = low
  740. else:
  741. # not so sure if the signed/unsigned strictness is a good idea:
  742. high = rffi.cast(lltype.Unsigned, high)
  743. low = rffi.cast(lltype.Unsigned, low)
  744. size = (high << 32) + low
  745. size = rffi.cast(lltype.Signed, size)
  746. if map_size == 0:
  747. if size == 0:
  748. raise RValueError("cannot mmap an empty file")
  749. if offset > size:
  750. raise RValueError(
  751. "mmap offset is greater than file size")
  752. map_size = int(size - offset)
  753. if map_size != size - offset:
  754. raise RValueError("mmap length is too large")
  755. elif offset + map_size > size:
  756. raise RValueError("mmap length is greater than file size")
  757. m = MMap(access, offset)
  758. m.file_handle = INVALID_HANDLE
  759. m.map_handle = INVALID_HANDLE
  760. if fh:
  761. # it is necessary to duplicate the handle, so the
  762. # Python code can close it on us
  763. handle_ref = lltype.malloc(LPHANDLE.TO, 1, flavor='raw')
  764. handle_ref[0] = m.file_handle
  765. try:
  766. res = DuplicateHandle(GetCurrentProcess(), # source process handle
  767. fh, # handle to be duplicated
  768. GetCurrentProcess(), # target process handle
  769. handle_ref, # result
  770. 0, # access - ignored due to options value
  771. False, # inherited by child procs?
  772. DUPLICATE_SAME_ACCESS) # options
  773. if not res:
  774. raise rwin32.lastSavedWindowsError()
  775. m.file_handle = handle_ref[0]
  776. finally:
  777. lltype.free(handle_ref, flavor='raw')
  778. if not map_size:
  779. low, high = _get_file_size(fh)
  780. if _64BIT:
  781. map_size = (low << 32) + 1
  782. else:
  783. if high:
  784. # file is too large to map completely
  785. map_size = -1
  786. else:
  787. map_size = low
  788. if tagname:
  789. m.tagname = tagname
  790. # DWORD is a 4-byte int. If int > 4-byte it must be divided
  791. if _64BIT:
  792. size_hi = (map_size + offset) >> 32
  793. size_lo = (map_size + offset) & 0xFFFFFFFF
  794. offset_hi = offset >> 32
  795. offset_lo = offset & 0xFFFFFFFF
  796. else:
  797. size_hi = 0
  798. size_lo = map_size + offset
  799. offset_hi = 0
  800. offset_lo = offset
  801. flProtect |= flags
  802. m.map_handle = CreateFileMapping(m.file_handle, NULL, flProtect,
  803. size_hi, size_lo, m.tagname)
  804. if m.map_handle:
  805. data = MapViewOfFile(m.map_handle, dwDesiredAccess,
  806. offset_hi, offset_lo, length)
  807. if data:
  808. # XXX we should have a real LPVOID which must always be casted
  809. charp = rffi.cast(LPCSTR, data)
  810. m.setdata(charp, map_size)
  811. return m
  812. winerror = rwin32.lastSavedWindowsError()
  813. if m.map_handle:
  814. rwin32.CloseHandle_no_err(m.map_handle)
  815. m.map_handle = INVALID_HANDLE
  816. raise winerror
  817. class Hint:
  818. pos = -0x4fff0000 # for reproducible results
  819. hint = Hint()
  820. # XXX this has no effect on windows
  821. def alloc(map_size):
  822. """Allocate memory. This is intended to be used by the JIT,
  823. so the memory has the executable bit set.
  824. XXX implement me: it should get allocated internally in
  825. case of a sandboxed process
  826. """
  827. null = lltype.nullptr(rffi.VOIDP.TO)
  828. res = VirtualAlloc_safe(null, map_size, MEM_COMMIT | MEM_RESERVE,
  829. PAGE_EXECUTE_READWRITE)
  830. if not res:
  831. raise MemoryError
  832. arg = lltype.malloc(LPDWORD.TO, 1, zero=True, flavor='raw')
  833. VirtualProtect(res, map_size, PAGE_EXECUTE_READWRITE, arg)
  834. lltype.free(arg, flavor='raw')
  835. # ignore errors, just try
  836. return res
  837. alloc._annenforceargs_ = (int,)
  838. def free(ptr, map_size):
  839. VirtualFree_safe(ptr, 0, MEM_RELEASE)
  840. def madvise_free(addr, map_size):
  841. r = _VirtualAlloc_safe_no_wrapper(
  842. rffi.cast(rffi.VOIDP, addr),
  843. rffi.cast(rffi.SIZE_T, map_size),
  844. rffi.cast(DWORD, MEM_RESET),
  845. rffi.cast(DWORD, PAGE_READWRITE))
  846. #from rpython.rlib import debug
  847. #debug.debug_print("madvise_free:", r)