paging.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /*
  2. * DUMA - Red-Zone memory allocator.
  3. * Copyright (C) 2002-2009 Hayati Ayguen <h_ayguen@web.de>, Procitec GmbH
  4. * Copyright (C) 2006 Michael Eddington <meddington@gmail.com>
  5. * Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
  6. * License: GNU GPL (GNU General Public License, see COPYING-GPL)
  7. *
  8. * This program is free software; you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation; either version 2 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. *
  22. *
  23. * FILE CONTENTS:
  24. * internal implementation file
  25. * contains system/platform dependent paging functions
  26. */
  27. #ifndef DUMA_PAGING_H
  28. #define DUMA_PAGING_H
  29. /*
  30. * Lots of systems are missing the definition of PROT_NONE.
  31. */
  32. #ifndef PROT_NONE
  33. #define PROT_NONE 0
  34. #endif
  35. /*
  36. * 386 BSD has MAP_ANON instead of MAP_ANONYMOUS.
  37. */
  38. #if ( !defined(MAP_ANONYMOUS) && defined(MAP_ANON) )
  39. #define MAP_ANONYMOUS MAP_ANON
  40. #endif
  41. /*
  42. * For some reason, I can't find mprotect() in any of the headers on
  43. * IRIX or SunOS 4.1.2
  44. */
  45. /* extern C_LINKAGE int mprotect(void * addr, size_t len, int prot); */
  46. #if !defined(WIN32)
  47. static caddr_t startAddr = (caddr_t) 0;
  48. #endif
  49. /* Function: stringErrorReport
  50. *
  51. * Get formatted error string and return. For WIN32
  52. * FormatMessage is used, strerror all else.
  53. */
  54. static const char * stringErrorReport(void)
  55. {
  56. #if defined(WIN32)
  57. DWORD LastError;
  58. LPVOID lpMsgBuf;
  59. LastError = GetLastError();
  60. FormatMessage(
  61. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
  62. , NULL
  63. , LastError
  64. , MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) /* Default language */
  65. , (LPTSTR) &lpMsgBuf
  66. , 0
  67. , NULL
  68. );
  69. return (char*)lpMsgBuf; /* "Unknown error.\n"; */
  70. #else
  71. # ifndef DUMA_NO_STRERROR
  72. static int failing;
  73. if ( !failing )
  74. {
  75. const char *str;
  76. failing++;
  77. str = strerror(errno);
  78. failing--;
  79. if ( str != NULL )
  80. return str;
  81. }
  82. # endif
  83. return DUMA_strerror(errno);
  84. #endif
  85. }
  86. /* Function: mprotectFailed
  87. *
  88. * Report that VirtualProtect or mprotect failed and abort
  89. * program execution.
  90. */
  91. static void mprotectFailed(void)
  92. {
  93. #if defined(WIN32)
  94. DUMA_Abort("VirtualProtect() failed: %s", stringErrorReport());
  95. #else
  96. DUMA_Abort("mprotect() failed: %s.\nCheck README section 'MEMORY USAGE AND EXECUTION SPEED'\n your (Linux) system may limit the number of different page mappings per process", stringErrorReport());
  97. #endif
  98. }
  99. /* Function: Page_Create
  100. *
  101. * Create memory. Allocates actual memory. Uses
  102. * VirtualAlloc on windows and mmap on unix.
  103. *
  104. * See Also:
  105. * <Page_Delete>
  106. */
  107. static void *
  108. Page_Create(size_t size, int exitonfail, int printerror)
  109. {
  110. caddr_t allocation;
  111. #if defined(WIN32)
  112. allocation = VirtualAlloc(
  113. NULL /* address of region to reserve or commit */
  114. , (DWORD) size /* size of region */
  115. , (DWORD) MEM_COMMIT /* type of allocation */
  116. , (DWORD) PAGE_READWRITE /* type of access protection */
  117. );
  118. if ( (caddr_t)0 == allocation )
  119. {
  120. if ( exitonfail )
  121. DUMA_Abort("VirtualAlloc(%d) failed: %s", (DUMA_SIZE)size, stringErrorReport());
  122. else if ( printerror )
  123. DUMA_Print("\nDUMA warning: VirtualAlloc(%d) failed: %s", (DUMA_SIZE)size, stringErrorReport());
  124. }
  125. #elif defined(MAP_ANONYMOUS)
  126. /*
  127. * In this version, "startAddr" is a _hint_, not a demand.
  128. * When the memory I map here is contiguous with other
  129. * mappings, the allocator can coalesce the memory from two
  130. * or more mappings into one large contiguous chunk, and thus
  131. * might be able to find a fit that would not otherwise have
  132. * been possible. I could _force_ it to be contiguous by using
  133. * the MMAP_FIXED flag, but I don't want to stomp on memory mappings
  134. * generated by other software, etc.
  135. */
  136. allocation = (caddr_t) mmap(
  137. startAddr
  138. , (int)size
  139. , PROT_READ|PROT_WRITE
  140. , MAP_PRIVATE|MAP_ANONYMOUS
  141. , -1
  142. , 0
  143. );
  144. #ifndef __hpux
  145. /*
  146. * Set the "address hint" for the next mmap() so that it will abut
  147. * the mapping we just created.
  148. *
  149. * HP/UX 9.01 has a kernel bug that makes mmap() fail sometimes
  150. * when given a non-zero address hint, so we'll leave the hint set
  151. * to zero on that system. HP recently told me this is now fixed.
  152. * Someone please tell me when it is probable to assume that most
  153. * of those systems that were running 9.01 have been upgraded.
  154. */
  155. startAddr = allocation + size;
  156. #endif
  157. if ( allocation == (caddr_t)-1 )
  158. {
  159. allocation = (caddr_t)0;
  160. if ( exitonfail )
  161. DUMA_Abort("mmap(%d) failed: %s", (DUMA_SIZE)size, stringErrorReport());
  162. else if ( printerror )
  163. DUMA_Print("\nDUMA warning: mmap(%d) failed: %s", (DUMA_SIZE)size, stringErrorReport());
  164. }
  165. #else
  166. static int devZeroFd = -1;
  167. if ( devZeroFd == -1 )
  168. {
  169. devZeroFd = open("/dev/zero", O_RDWR);
  170. if ( devZeroFd < 0 )
  171. DUMA_Abort( "open() on /dev/zero failed: %s", stringErrorReport() );
  172. }
  173. /*
  174. * In this version, "startAddr" is a _hint_, not a demand.
  175. * When the memory I map here is contiguous with other
  176. * mappings, the allocator can coalesce the memory from two
  177. * or more mappings into one large contiguous chunk, and thus
  178. * might be able to find a fit that would not otherwise have
  179. * been possible. I could _force_ it to be contiguous by using
  180. * the MMAP_FIXED flag, but I don't want to stomp on memory mappings
  181. * generated by other software, etc.
  182. */
  183. allocation = (caddr_t) mmap(
  184. startAddr
  185. , (int)size
  186. , PROT_READ|PROT_WRITE
  187. , MAP_PRIVATE
  188. , devZeroFd
  189. , 0
  190. );
  191. startAddr = allocation + size;
  192. if ( allocation == (caddr_t)-1 )
  193. {
  194. allocation = (caddr_t)0;
  195. if ( exitonfail )
  196. DUMA_Abort("mmap(%d) failed: %s", (DUMA_SIZE)size, stringErrorReport());
  197. else if ( printerror )
  198. DUMA_Print("\nDUMA warning: mmap(%d) failed: %s", (DUMA_SIZE)size, stringErrorReport());
  199. }
  200. #endif
  201. // TESTING
  202. // memset((void*)allocation, 0, startAddr);
  203. return (void *)allocation;
  204. }
  205. /* Function: Page_AllowAccess
  206. *
  207. * Allow memory access to allocated memory.
  208. *
  209. * See Also:
  210. * <Page_DenyAccess>
  211. */
  212. void Page_AllowAccess(void * address, size_t size)
  213. {
  214. #if defined(WIN32)
  215. SIZE_T OldProtect, retQuery;
  216. MEMORY_BASIC_INFORMATION MemInfo;
  217. size_t tail_size;
  218. BOOL ret;
  219. while (size >0)
  220. {
  221. retQuery = VirtualQuery(address, &MemInfo, sizeof(MemInfo));
  222. if (retQuery < sizeof(MemInfo))
  223. DUMA_Abort("VirtualQuery() failed\n");
  224. tail_size = (size > MemInfo.RegionSize) ? MemInfo.RegionSize : size;
  225. ret = VirtualProtect(
  226. (LPVOID) address /* address of region of committed pages */
  227. , (DWORD) tail_size /* size of the region */
  228. , (DWORD) PAGE_READWRITE /* desired access protection */
  229. , (PDWORD) &OldProtect /* address of variable to get old protection */
  230. );
  231. if (0 == ret)
  232. mprotectFailed();
  233. address = ((char *)address) + tail_size;
  234. size -= tail_size;
  235. }
  236. #else
  237. if ( mprotect((caddr_t)address, size, PROT_READ|PROT_WRITE) < 0 )
  238. mprotectFailed();
  239. #endif
  240. }
  241. /* Function: Page_DenyAccess
  242. *
  243. * Deny access to allocated memory region.
  244. *
  245. * See Also:
  246. * <Page_AllowAccess>
  247. */
  248. static void Page_DenyAccess(void * address, size_t size)
  249. {
  250. #if defined(WIN32)
  251. SIZE_T OldProtect, retQuery;
  252. MEMORY_BASIC_INFORMATION MemInfo;
  253. size_t tail_size;
  254. BOOL ret;
  255. while (size >0)
  256. {
  257. retQuery = VirtualQuery(address, &MemInfo, sizeof(MemInfo));
  258. if (retQuery < sizeof(MemInfo))
  259. DUMA_Abort("VirtualQuery() failed\n");
  260. tail_size = (size > MemInfo.RegionSize) ? MemInfo.RegionSize : size;
  261. ret = VirtualProtect(
  262. (LPVOID) address /* address of region of committed pages */
  263. , (DWORD) tail_size /* size of the region */
  264. , (DWORD) PAGE_NOACCESS /* desired access protection */
  265. , (PDWORD) &OldProtect /* address of variable to get old protection */
  266. );
  267. if (0 == ret)
  268. mprotectFailed();
  269. address = ((char *)address) + tail_size;
  270. size -= tail_size;
  271. }
  272. #else
  273. if ( mprotect((caddr_t)address, size, PROT_NONE) < 0 )
  274. mprotectFailed();
  275. #endif
  276. }
  277. /* Function: Page_Delete
  278. *
  279. * Free's DUMA allocated memory. This is the real deal, make sure
  280. * the page is no longer in our slot list first!
  281. *
  282. * See Also:
  283. * <Page_Create>
  284. */
  285. static void Page_Delete(void * address, size_t size)
  286. {
  287. #if defined(WIN32)
  288. void * alloc_address = address;
  289. SIZE_T retQuery;
  290. MEMORY_BASIC_INFORMATION MemInfo;
  291. BOOL ret;
  292. /* release physical memory commited to virtual address space */
  293. while (size >0)
  294. {
  295. retQuery = VirtualQuery(address, &MemInfo, sizeof(MemInfo));
  296. if (retQuery < sizeof(MemInfo))
  297. DUMA_Abort("VirtualQuery() failed\n");
  298. if ( MemInfo.State == MEM_COMMIT )
  299. {
  300. ret = VirtualFree(
  301. (LPVOID) MemInfo.BaseAddress /* base of committed pages */
  302. , (DWORD) MemInfo.RegionSize /* size of the region */
  303. , (DWORD) MEM_DECOMMIT /* type of free operation */
  304. );
  305. if (0 == ret)
  306. DUMA_Abort("VirtualFree(,,MEM_DECOMMIT) failed: %s", stringErrorReport());
  307. }
  308. address = ((char *)address) + MemInfo.RegionSize;
  309. size -= MemInfo.RegionSize;
  310. }
  311. /* release virtual address space */
  312. ret = VirtualFree(
  313. (LPVOID) alloc_address
  314. , (DWORD) 0
  315. , (DWORD) MEM_RELEASE
  316. );
  317. if (0 == ret)
  318. DUMA_Abort("VirtualFree(,,MEM_RELEASE) failed: %s", stringErrorReport());
  319. #else
  320. if ( munmap((caddr_t)address, size) < 0 )
  321. Page_DenyAccess(address, size);
  322. #endif
  323. }
  324. /* Function: Page_Size
  325. *
  326. * Retrieve page size.
  327. */
  328. static size_t
  329. Page_Size(void)
  330. {
  331. #if defined(WIN32)
  332. SYSTEM_INFO SystemInfo;
  333. GetSystemInfo( &SystemInfo );
  334. return (size_t)SystemInfo.dwPageSize;
  335. #elif defined(_SC_PAGESIZE)
  336. return (size_t)sysconf(_SC_PAGESIZE);
  337. #elif defined(_SC_PAGE_SIZE)
  338. return (size_t)sysconf(_SC_PAGE_SIZE);
  339. #else
  340. return getpagesize();
  341. #endif
  342. }
  343. #endif /* DUMA_PAGING_H */