PageRenderTime 65ms CodeModel.GetById 37ms RepoModel.GetById 1ms app.codeStats 0ms

/mono/utils/mono-mmap.c

https://github.com/hollow87/mono
C | 644 lines | 462 code | 72 blank | 110 comment | 73 complexity | 8ffcfacf1cac2b98f582cd8cf76070d9 MD5 | raw file
  1. /*
  2. * mono-mmap.c: Support for mapping code into the process address space
  3. *
  4. * Author:
  5. * Mono Team (mono-list@lists.ximian.com)
  6. *
  7. * Copyright 2001-2008 Novell, Inc.
  8. */
  9. #include "config.h"
  10. #ifdef HOST_WIN32
  11. #include <windows.h>
  12. #include <io.h>
  13. #else
  14. #include <sys/types.h>
  15. #if HAVE_SYS_STAT_H
  16. #include <sys/stat.h>
  17. #endif
  18. #if HAVE_SYS_MMAN_H
  19. #include <sys/mman.h>
  20. #endif
  21. #include <fcntl.h>
  22. #include <string.h>
  23. #include <unistd.h>
  24. #include <stdlib.h>
  25. #include <signal.h>
  26. #include <errno.h>
  27. #endif
  28. #include "mono-mmap.h"
  29. #include "mono-proclib.h"
  30. #ifndef MAP_ANONYMOUS
  31. #define MAP_ANONYMOUS MAP_ANON
  32. #endif
  33. #ifndef MAP_32BIT
  34. #define MAP_32BIT 0
  35. #endif
  36. typedef struct {
  37. int size;
  38. int pid;
  39. int reserved;
  40. short stats_start;
  41. short stats_end;
  42. } SAreaHeader;
  43. static void* malloced_shared_area = NULL;
  44. static void*
  45. malloc_shared_area (int pid)
  46. {
  47. int size = mono_pagesize ();
  48. SAreaHeader *sarea = g_malloc0 (size);
  49. sarea->size = size;
  50. sarea->pid = pid;
  51. sarea->stats_start = sizeof (SAreaHeader);
  52. sarea->stats_end = sizeof (SAreaHeader);
  53. return sarea;
  54. }
  55. #ifdef HOST_WIN32
  56. int
  57. mono_pagesize (void)
  58. {
  59. SYSTEM_INFO info;
  60. static int saved_pagesize = 0;
  61. if (saved_pagesize)
  62. return saved_pagesize;
  63. GetSystemInfo (&info);
  64. saved_pagesize = info.dwAllocationGranularity;
  65. return saved_pagesize;
  66. }
  67. static int
  68. prot_from_flags (int flags)
  69. {
  70. int prot = flags & (MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC);
  71. switch (prot) {
  72. case 0: prot = PAGE_NOACCESS; break;
  73. case MONO_MMAP_READ: prot = PAGE_READONLY; break;
  74. case MONO_MMAP_READ|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READ; break;
  75. case MONO_MMAP_READ|MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
  76. case MONO_MMAP_READ|MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
  77. case MONO_MMAP_WRITE: prot = PAGE_READWRITE; break;
  78. case MONO_MMAP_WRITE|MONO_MMAP_EXEC: prot = PAGE_EXECUTE_READWRITE; break;
  79. case MONO_MMAP_EXEC: prot = PAGE_EXECUTE; break;
  80. default:
  81. g_assert_not_reached ();
  82. }
  83. return prot;
  84. }
  85. void*
  86. mono_valloc (void *addr, size_t length, int flags)
  87. {
  88. void *ptr;
  89. int mflags = MEM_COMMIT;
  90. int prot = prot_from_flags (flags);
  91. /* translate the flags */
  92. ptr = VirtualAlloc (addr, length, mflags, prot);
  93. return ptr;
  94. }
  95. int
  96. mono_vfree (void *addr, size_t length)
  97. {
  98. int res = VirtualFree (addr, 0, MEM_RELEASE);
  99. g_assert (res);
  100. return 0;
  101. }
  102. void*
  103. mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
  104. {
  105. void *ptr;
  106. int mflags = 0;
  107. HANDLE file, mapping;
  108. int prot = prot_from_flags (flags);
  109. /* translate the flags */
  110. /*if (flags & MONO_MMAP_PRIVATE)
  111. mflags |= MAP_PRIVATE;
  112. if (flags & MONO_MMAP_SHARED)
  113. mflags |= MAP_SHARED;
  114. if (flags & MONO_MMAP_ANON)
  115. mflags |= MAP_ANONYMOUS;
  116. if (flags & MONO_MMAP_FIXED)
  117. mflags |= MAP_FIXED;
  118. if (flags & MONO_MMAP_32BIT)
  119. mflags |= MAP_32BIT;*/
  120. mflags = FILE_MAP_READ;
  121. if (flags & MONO_MMAP_WRITE)
  122. mflags = FILE_MAP_COPY;
  123. file = (HANDLE) _get_osfhandle (fd);
  124. mapping = CreateFileMapping (file, NULL, prot, 0, 0, NULL);
  125. if (mapping == NULL)
  126. return NULL;
  127. ptr = MapViewOfFile (mapping, mflags, 0, offset, length);
  128. if (ptr == NULL) {
  129. CloseHandle (mapping);
  130. return NULL;
  131. }
  132. *ret_handle = (void*)mapping;
  133. return ptr;
  134. }
  135. int
  136. mono_file_unmap (void *addr, void *handle)
  137. {
  138. UnmapViewOfFile (addr);
  139. CloseHandle ((HANDLE)handle);
  140. return 0;
  141. }
  142. int
  143. mono_mprotect (void *addr, size_t length, int flags)
  144. {
  145. DWORD oldprot;
  146. int prot = prot_from_flags (flags);
  147. if (flags & MONO_MMAP_DISCARD) {
  148. VirtualFree (addr, length, MEM_DECOMMIT);
  149. VirtualAlloc (addr, length, MEM_COMMIT, prot);
  150. return 0;
  151. }
  152. return VirtualProtect (addr, length, prot, &oldprot) == 0;
  153. }
  154. void*
  155. mono_shared_area (void)
  156. {
  157. /* get the pid here */
  158. return malloc_shared_area (0);
  159. }
  160. void
  161. mono_shared_area_remove (void)
  162. {
  163. if (malloced_shared_area)
  164. g_free (malloced_shared_area);
  165. }
  166. void*
  167. mono_shared_area_for_pid (void *pid)
  168. {
  169. return NULL;
  170. }
  171. void
  172. mono_shared_area_unload (void *area)
  173. {
  174. }
  175. int
  176. mono_shared_area_instances (void **array, int count)
  177. {
  178. return 0;
  179. }
  180. #else
  181. #if defined(HAVE_MMAP)
  182. /**
  183. * mono_pagesize:
  184. * Get the page size in use on the system. Addresses and sizes in the
  185. * mono_mmap(), mono_munmap() and mono_mprotect() calls must be pagesize
  186. * aligned.
  187. *
  188. * Returns: the page size in bytes.
  189. */
  190. int
  191. mono_pagesize (void)
  192. {
  193. static int saved_pagesize = 0;
  194. if (saved_pagesize)
  195. return saved_pagesize;
  196. saved_pagesize = getpagesize ();
  197. return saved_pagesize;
  198. }
  199. static int
  200. prot_from_flags (int flags)
  201. {
  202. int prot = PROT_NONE;
  203. /* translate the protection bits */
  204. if (flags & MONO_MMAP_READ)
  205. prot |= PROT_READ;
  206. if (flags & MONO_MMAP_WRITE)
  207. prot |= PROT_WRITE;
  208. if (flags & MONO_MMAP_EXEC)
  209. prot |= PROT_EXEC;
  210. return prot;
  211. }
  212. /**
  213. * mono_valloc:
  214. * @addr: memory address
  215. * @length: memory area size
  216. * @flags: protection flags
  217. *
  218. * Allocates @length bytes of virtual memory with the @flags
  219. * protection. @addr can be a preferred memory address or a
  220. * mandatory one if MONO_MMAP_FIXED is set in @flags.
  221. * @addr must be pagesize aligned and can be NULL.
  222. * @length must be a multiple of pagesize.
  223. *
  224. * Returns: NULL on failure, the address of the memory area otherwise
  225. */
  226. void*
  227. mono_valloc (void *addr, size_t length, int flags)
  228. {
  229. void *ptr;
  230. int mflags = 0;
  231. int prot = prot_from_flags (flags);
  232. /* translate the flags */
  233. if (flags & MONO_MMAP_FIXED)
  234. mflags |= MAP_FIXED;
  235. if (flags & MONO_MMAP_32BIT)
  236. mflags |= MAP_32BIT;
  237. mflags |= MAP_ANONYMOUS;
  238. mflags |= MAP_PRIVATE;
  239. ptr = mmap (addr, length, prot, mflags, -1, 0);
  240. if (ptr == (void*)-1) {
  241. int fd = open ("/dev/zero", O_RDONLY);
  242. if (fd != -1) {
  243. ptr = mmap (addr, length, prot, mflags, fd, 0);
  244. close (fd);
  245. }
  246. if (ptr == (void*)-1)
  247. return NULL;
  248. }
  249. return ptr;
  250. }
  251. /**
  252. * mono_vfree:
  253. * @addr: memory address returned by mono_valloc ()
  254. * @length: size of memory area
  255. *
  256. * Remove the memory mapping at the address @addr.
  257. *
  258. * Returns: 0 on success.
  259. */
  260. int
  261. mono_vfree (void *addr, size_t length)
  262. {
  263. return munmap (addr, length);
  264. }
  265. /**
  266. * mono_file_map:
  267. * @length: size of data to map
  268. * @flags: protection flags
  269. * @fd: file descriptor
  270. * @offset: offset in the file
  271. * @ret_handle: pointer to storage for returning a handle for the map
  272. *
  273. * Map the area of the file pointed to by the file descriptor @fd, at offset
  274. * @offset and of size @length in memory according to the protection flags
  275. * @flags.
  276. * @offset and @length must be multiples of the page size.
  277. * @ret_handle must point to a void*: this value must be used when unmapping
  278. * the memory area using mono_file_unmap ().
  279. *
  280. */
  281. void*
  282. mono_file_map (size_t length, int flags, int fd, guint64 offset, void **ret_handle)
  283. {
  284. void *ptr;
  285. int mflags = 0;
  286. int prot = prot_from_flags (flags);
  287. /* translate the flags */
  288. if (flags & MONO_MMAP_PRIVATE)
  289. mflags |= MAP_PRIVATE;
  290. if (flags & MONO_MMAP_SHARED)
  291. mflags |= MAP_SHARED;
  292. if (flags & MONO_MMAP_FIXED)
  293. mflags |= MAP_FIXED;
  294. if (flags & MONO_MMAP_32BIT)
  295. mflags |= MAP_32BIT;
  296. ptr = mmap (0, length, prot, mflags, fd, offset);
  297. if (ptr == (void*)-1)
  298. return NULL;
  299. *ret_handle = (void*)length;
  300. return ptr;
  301. }
  302. /**
  303. * mono_file_unmap:
  304. * @addr: memory address returned by mono_file_map ()
  305. * @handle: handle of memory map
  306. *
  307. * Remove the memory mapping at the address @addr.
  308. * @handle must be the value returned in ret_handle by mono_file_map ().
  309. *
  310. * Returns: 0 on success.
  311. */
  312. int
  313. mono_file_unmap (void *addr, void *handle)
  314. {
  315. return munmap (addr, (size_t)handle);
  316. }
  317. /**
  318. * mono_mprotect:
  319. * @addr: memory address
  320. * @length: size of memory area
  321. * @flags: new protection flags
  322. *
  323. * Change the protection for the memory area at @addr for @length bytes
  324. * to matche the supplied @flags.
  325. * If @flags includes MON_MMAP_DISCARD the pages are discarded from memory
  326. * and the area is cleared to zero.
  327. * @addr must be aligned to the page size.
  328. * @length must be a multiple of the page size.
  329. *
  330. * Returns: 0 on success.
  331. */
  332. int
  333. mono_mprotect (void *addr, size_t length, int flags)
  334. {
  335. int prot = prot_from_flags (flags);
  336. if (flags & MONO_MMAP_DISCARD) {
  337. /* on non-linux the pages are not guaranteed to be zeroed (*bsd, osx at least) */
  338. #ifdef __linux__
  339. if (madvise (addr, length, MADV_DONTNEED))
  340. memset (addr, 0, length);
  341. #else
  342. memset (addr, 0, length);
  343. #ifdef HAVE_MADVISE
  344. madvise (addr, length, MADV_DONTNEED);
  345. madvise (addr, length, MADV_FREE);
  346. #else
  347. posix_madvise (addr, length, POSIX_MADV_DONTNEED);
  348. #endif
  349. #endif
  350. }
  351. return mprotect (addr, length, prot);
  352. }
  353. #else
  354. /* dummy malloc-based implementation */
  355. int
  356. mono_pagesize (void)
  357. {
  358. return 4096;
  359. }
  360. void*
  361. mono_valloc (void *addr, size_t length, int flags)
  362. {
  363. return malloc (length);
  364. }
  365. void*
  366. mono_valloc_aligned (size_t length, size_t alignment, int flags)
  367. {
  368. g_assert_not_reached ();
  369. }
  370. #define HAVE_VALLOC_ALIGNED
  371. int
  372. mono_vfree (void *addr, size_t length)
  373. {
  374. free (addr);
  375. return 0;
  376. }
  377. int
  378. mono_mprotect (void *addr, size_t length, int flags)
  379. {
  380. if (flags & MONO_MMAP_DISCARD) {
  381. memset (addr, 0, length);
  382. }
  383. return 0;
  384. }
  385. #endif // HAVE_MMAP
  386. #if defined(HAVE_SHM_OPEN) && !defined (DISABLE_SHARED_PERFCOUNTERS)
  387. static int
  388. mono_shared_area_instances_slow (void **array, int count, gboolean cleanup)
  389. {
  390. int i, j = 0;
  391. int num;
  392. void *data;
  393. gpointer *processes = mono_process_list (&num);
  394. for (i = 0; i < num; ++i) {
  395. data = mono_shared_area_for_pid (processes [i]);
  396. if (!data)
  397. continue;
  398. mono_shared_area_unload (data);
  399. if (!cleanup) {
  400. if (j < count)
  401. array [j++] = processes [i];
  402. else
  403. break;
  404. }
  405. }
  406. g_free (processes);
  407. return j;
  408. }
  409. static int
  410. mono_shared_area_instances_helper (void **array, int count, gboolean cleanup)
  411. {
  412. const char *name;
  413. int i = 0;
  414. int curpid = getpid ();
  415. GDir *dir = g_dir_open ("/dev/shm/", 0, NULL);
  416. if (!dir)
  417. return mono_shared_area_instances_slow (array, count, cleanup);
  418. while ((name = g_dir_read_name (dir))) {
  419. int pid;
  420. char *nend;
  421. if (strncmp (name, "mono.", 5))
  422. continue;
  423. pid = strtol (name + 5, &nend, 10);
  424. if (pid <= 0 || nend == name + 5 || *nend)
  425. continue;
  426. if (!cleanup) {
  427. if (i < count)
  428. array [i++] = GINT_TO_POINTER (pid);
  429. else
  430. break;
  431. }
  432. if (curpid != pid && kill (pid, 0) == -1 && (errno == ESRCH || errno == ENOMEM)) {
  433. char buf [128];
  434. g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
  435. shm_unlink (buf);
  436. }
  437. }
  438. g_dir_close (dir);
  439. return i;
  440. }
  441. void*
  442. mono_shared_area (void)
  443. {
  444. int fd;
  445. int pid = getpid ();
  446. /* we should allow the user to configure the size */
  447. int size = mono_pagesize ();
  448. char buf [128];
  449. void *res;
  450. SAreaHeader *header;
  451. /* perform cleanup of segments left over from dead processes */
  452. mono_shared_area_instances_helper (NULL, 0, TRUE);
  453. g_snprintf (buf, sizeof (buf), "/mono.%d", pid);
  454. fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
  455. if (fd == -1 && errno == EEXIST) {
  456. /* leftover */
  457. shm_unlink (buf);
  458. fd = shm_open (buf, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP);
  459. }
  460. /* in case of failure we try to return a memory area anyway,
  461. * even if it means the data can't be read by other processes
  462. */
  463. if (fd == -1)
  464. return malloc_shared_area (pid);
  465. if (ftruncate (fd, size) != 0) {
  466. shm_unlink (buf);
  467. close (fd);
  468. }
  469. res = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
  470. if (res == MAP_FAILED) {
  471. shm_unlink (buf);
  472. close (fd);
  473. return malloc_shared_area (pid);
  474. }
  475. /* we don't need the file descriptor anymore */
  476. close (fd);
  477. header = res;
  478. header->size = size;
  479. header->pid = pid;
  480. header->stats_start = sizeof (SAreaHeader);
  481. header->stats_end = sizeof (SAreaHeader);
  482. atexit (mono_shared_area_remove);
  483. return res;
  484. }
  485. void
  486. mono_shared_area_remove (void)
  487. {
  488. char buf [128];
  489. g_snprintf (buf, sizeof (buf), "/mono.%d", getpid ());
  490. shm_unlink (buf);
  491. if (malloced_shared_area)
  492. g_free (malloced_shared_area);
  493. }
  494. void*
  495. mono_shared_area_for_pid (void *pid)
  496. {
  497. int fd;
  498. /* we should allow the user to configure the size */
  499. int size = mono_pagesize ();
  500. char buf [128];
  501. void *res;
  502. g_snprintf (buf, sizeof (buf), "/mono.%d", GPOINTER_TO_INT (pid));
  503. fd = shm_open (buf, O_RDONLY, S_IRUSR|S_IRGRP);
  504. if (fd == -1)
  505. return NULL;
  506. res = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0);
  507. if (res == MAP_FAILED) {
  508. close (fd);
  509. return NULL;
  510. }
  511. /* FIXME: validate the area */
  512. /* we don't need the file descriptor anymore */
  513. close (fd);
  514. return res;
  515. }
  516. void
  517. mono_shared_area_unload (void *area)
  518. {
  519. /* FIXME: currently we load only a page */
  520. munmap (area, mono_pagesize ());
  521. }
  522. int
  523. mono_shared_area_instances (void **array, int count)
  524. {
  525. return mono_shared_area_instances_helper (array, count, FALSE);
  526. }
  527. #else
  528. void*
  529. mono_shared_area (void)
  530. {
  531. return malloc_shared_area (getpid ());
  532. }
  533. void
  534. mono_shared_area_remove (void)
  535. {
  536. if (malloced_shared_area)
  537. g_free (malloced_shared_area);
  538. malloced_shared_area = NULL;
  539. }
  540. void*
  541. mono_shared_area_for_pid (void *pid)
  542. {
  543. return NULL;
  544. }
  545. void
  546. mono_shared_area_unload (void *area)
  547. {
  548. }
  549. int
  550. mono_shared_area_instances (void **array, int count)
  551. {
  552. return 0;
  553. }
  554. #endif // HAVE_SHM_OPEN
  555. #endif // HOST_WIN32
  556. #ifndef HAVE_VALLOC_ALIGNED
  557. void*
  558. mono_valloc_aligned (size_t size, size_t alignment, int flags)
  559. {
  560. /* Allocate twice the memory to be able to put the block on an aligned address */
  561. char *mem = mono_valloc (NULL, size + alignment, flags);
  562. char *aligned;
  563. g_assert (mem);
  564. aligned = (char*)((gulong)(mem + (alignment - 1)) & ~(alignment - 1));
  565. g_assert (aligned >= mem && aligned + size <= mem + size + alignment && !((gulong)aligned & (alignment - 1)));
  566. if (aligned > mem)
  567. mono_vfree (mem, aligned - mem);
  568. if (aligned + size < mem + size + alignment)
  569. mono_vfree (aligned + size, (mem + size + alignment) - (aligned + size));
  570. return aligned;
  571. }
  572. #endif