heapalloc.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /**--------------------------------------------------------------------------**\
  2. ===================================
  3. Y Sever Includes - Malloc Functions
  4. ===================================
  5. Description:
  6. Functions for using malloc/calloc/free type functions in PAWN.
  7. Legal:
  8. Version: MPL 1.1
  9. The contents of this file are subject to the Mozilla Public License Version
  10. 1.1 (the "License"); you may not use this file except in compliance with
  11. the License. You may obtain a copy of the License at
  12. http://www.mozilla.org/MPL/
  13. Software distributed under the License is distributed on an "AS IS" basis,
  14. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  15. for the specific language governing rights and limitations under the
  16. License.
  17. The Original Code is the YSI malloc include.
  18. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  19. Portions created by the Initial Developer are Copyright (C) 2011
  20. the Initial Developer. All Rights Reserved.
  21. Contributors:
  22. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  23. Thanks:
  24. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  25. ZeeX - Very productive conversations.
  26. koolk - IsPlayerinAreaEx code.
  27. TheAlpha - Danish translation.
  28. breadfish - German translation.
  29. Fireburn - Dutch translation.
  30. yom - French translation.
  31. 50p - Polish translation.
  32. Zamaroht - Spanish translation.
  33. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  34. for me to strive to better.
  35. Pixels^ - Running XScripters where the idea was born.
  36. Matite - Pestering me to release it and using it.
  37. Very special thanks to:
  38. Thiadmer - PAWN, whose limits continue to amaze me!
  39. Kye/Kalcor - SA:MP.
  40. SA:MP Team past, present and future - SA:MP.
  41. Version:
  42. 0.1
  43. Changelog:
  44. 02/12/11:
  45. Added variable argument functions.
  46. 22/12/08:
  47. First version.
  48. Functions:
  49. Public
  50. -
  51. Core:
  52. -
  53. Stock:
  54. malloc - Allocate a block of memory (may be inline).
  55. calloc - Allocate a block of memory and blank.
  56. free - Free an allocated block of memory (may be inline).
  57. Malloc_Set - Set a value in an allocated array (may be inline).
  58. Malloc_Get - Get a value in an allocated array (may be inline).
  59. Malloc_SetS - Set a string in an allocated array.
  60. Malloc_GetS - Get a string in an allocated array.
  61. Malloc_Allocate - Do the memory allocation (may be static).
  62. Malloc_Free - Do the memory freeing (may be static).
  63. Malloc_SlotSize - Get the size of an allocated block (may be inline).
  64. Malloc_NewS - Allocate for and store a given string.
  65. Static:
  66. Malloc_Allocate - Do the memory allocation (may be stock).
  67. Malloc_Free - Do the memory freeing (may be stock).
  68. Inline:
  69. mget - Get data from an allocation unit.
  70. mset - Set data in an allocation unit.
  71. mgets - Get a string from an allocation unit.
  72. msets - Set a string in an allocation unit.
  73. malloc - Allocate a block of memory (may be stock).
  74. free - Free an allocated block of memory (may be stock).
  75. Malloc_Set - Set a value in an allocated array (may be stock).
  76. Malloc_Get - Get a value in an allocated array (may be stock).
  77. Malloc_NextSlot - Get the next free data block.
  78. Malloc_GetSlotSize - Get the size of a slot.
  79. Malloc_SetSlotSize - Set the size of a block.
  80. Malloc_GetData - Direct data access getter.
  81. Malloc_SetData - Direct data access setter.
  82. Malloc_SlotSize - Get the size of an allocated block (may be stock).
  83. API:
  84. -
  85. Callbacks:
  86. -
  87. Definitions:
  88. MALLOC_KB_TO_CELL - Multiplication value to convert kb to cells.
  89. NO_ALLOC - A failed allocation (NULL, but YSI already has NULL).
  90. Enums:
  91. -
  92. Macros:
  93. -
  94. Tags:
  95. Alloc - An allocated block handle variable.
  96. Variables:
  97. Global:
  98. YSI_gMallocMemory - Stores the data (may be static).
  99. Static:
  100. YSI_gMallocMemory - Stores the data (may be global).
  101. YSI_g_sUnusedStart - Start of free memory.
  102. Commands:
  103. -
  104. Compile options:
  105. MALLOC_MEMORY - Number of cells to reserve.
  106. MALLOC_MEMORY_KB - Number of killobytes to reserve.
  107. MALLOC_MEMORY_B - Number of bytes to reserve.
  108. MALLOC_MEMORY_MB - Number of megabytes to reserve.
  109. YSI_MALLOC_SECURE - Use enhanced bounds checking.
  110. YSI_MALLOC_NO_SHORT - Avoid conflicts with mget/mset.
  111. Operators:
  112. -
  113. \**--------------------------------------------------------------------------**/
  114. main()
  115. {
  116. P:1("Malloc_main called");
  117. Malloc_TrySetup();
  118. #if defined Malloc_main
  119. Malloc_main();
  120. #endif
  121. return 1;
  122. //return MALLOC_SOLIDIFYHEAP(3);
  123. }
  124. #if defined _ALS_main
  125. #undef main
  126. #else
  127. #define _ALS_main
  128. #endif
  129. #define main() forward Malloc_main();public Malloc_main()
  130. /**--------------------------------------------------------------------------**\
  131. <summary>Malloc_FindStackTop</summary>
  132. <returns>
  133. -
  134. </returns>
  135. <remarks>
  136. Loop back up through the stack and find the start of the current stack. If
  137. it doesn't equal the top of the true stack then we've been called via
  138. "CallLocalFunction" at some point and thus MAY get some memory corruption.
  139. Based on ZeeX's GetStackTrace, but gets frames instead of returns.
  140. </remarks>
  141. \**--------------------------------------------------------------------------**/
  142. static Malloc_FindStackTop()
  143. {
  144. new
  145. frm_addr;
  146. #emit LCTRL 5
  147. #emit STOR.S.pri frm_addr
  148. // Loop until we hit a return address of 0.
  149. while (GetFrameReturn(frm_addr))
  150. {
  151. #emit LREF.S.pri frm_addr
  152. #emit STOR.S.pri frm_addr
  153. }
  154. // We can't accurately get the number or parameters because of y_hooks :(.
  155. return frm_addr + 12;
  156. }
  157. /**--------------------------------------------------------------------------**\
  158. <summary>OnScriptInit</summary>
  159. <returns>
  160. -
  161. </returns>
  162. <remarks>
  163. Finds all "BOUNDS 0" OpCodes in the AMX and rewrites them to "NOP NOP". The
  164. byte pattern for this code is "OP_BOUNDS 0", which (AFAIK) can not appear
  165. anywhere else in the DAT segment. You can have "OP_BOUNDS" as a parameter
  166. to something, but it would then be followed by an OpCode of "0", which is
  167. never valid (OP_NONE).
  168. I've tried to make this as resiliant as possible to being called via
  169. "CallLocalFunction" as not the first callback in the script but there may
  170. still be a few problems - we won't see till people start testing I guess...
  171. </remarks>
  172. \**--------------------------------------------------------------------------**/
  173. public OnScriptInit()
  174. {
  175. P:1("Malloc_OnScriptInit called");
  176. // First ever LEGITIMATE call to "heapspace"!
  177. if (heapspace() < MALLOC_MEMORY + 4 * 1024)
  178. {
  179. P:E("heapspace too low for y_malloc: Did you use \"#pragma dynamic\"?");
  180. }
  181. new
  182. Opcode:bounds = RelocateOpcode(OP_BOUNDS),
  183. nop = _:RelocateOpcode(OP_NOP);
  184. for (new i = AMX_HEADER_COD, end = AMX_HEADER_DAT - 4; i < end; i += 4)
  185. {
  186. // Make sure this isn't just something that LOOKS like "bounds 0",
  187. // although I don't think there can be because "0" is not a valid
  188. // opcode.
  189. if (AMX_Read(i) == _:bounds && AMX_Read(i + 4) == 0)
  190. {
  191. // Found a bounds code.
  192. AMX_Write(i, nop);
  193. AMX_Write(i += 4, nop);
  194. }
  195. }
  196. Malloc_TrySetup();
  197. #if defined Malloc_OnScriptInit
  198. Malloc_OnScriptInit();
  199. #endif
  200. SetTimer("Malloc_SolidifyTimer", 0, 0);
  201. SetTimer("Malloc_SolidifyTimer", 0, 0);
  202. return 1;
  203. }
  204. #undef OnScriptInit
  205. #define OnScriptInit Malloc_OnScriptInit
  206. #if defined Malloc_OnScriptInit
  207. forward Malloc_OnScriptInit();
  208. #endif
  209. /**--------------------------------------------------------------------------**\
  210. <summary>Malloc_TrySetup</summary>
  211. <returns>
  212. -
  213. </returns>
  214. <remarks>
  215. Move the heap pointer up a load. This is called multiple times at the start
  216. of the mode because we need to beat protections added in by the virtual
  217. machine to steal away its heap area.
  218. </remarks>
  219. \**--------------------------------------------------------------------------**/
  220. static Malloc_TrySetup()
  221. {
  222. P:1("Malloc_TrySetup called");
  223. new
  224. temp;
  225. #emit LCTRL 3
  226. #emit STOR.S.pri temp
  227. if (temp > Malloc_FindStackTop() + 4)
  228. {
  229. P:W("y_malloc set up via \"CallLocalFunction\", memory corruption is a remote possibility");
  230. }
  231. temp = MALLOC_MEMORY * 4;
  232. // Allocate a ton of space on the heap.
  233. #emit LCTRL 2
  234. #emit LOAD.S.alt temp
  235. #emit ADD
  236. #emit STOR.S.pri temp
  237. // Now there's only the normal bit of the stack and heap left.
  238. if (temp == AMX_HEADER_HEA + MALLOC_MEMORY * 4 * 2)
  239. {
  240. // Already allocated and now trying to double allocate.
  241. return;
  242. }
  243. if (temp != AMX_HEADER_HEA + MALLOC_MEMORY * 4)
  244. {
  245. P:F("y_malloc: Not the first HEAP allocation!");
  246. return;
  247. }
  248. else
  249. {
  250. #emit LOAD.S.pri temp
  251. #emit SCTRL 2
  252. if (YSI_g_sHeapStart) return;
  253. }
  254. YSI_g_sHeapStart = temp - MALLOC_MEMORY * 4;
  255. P:2("Malloc_OnScriptInit: %d %d %d", YSI_g_sHeapStart, MALLOC_MEMORY * 4, AMX_HEADER_HEA);
  256. #emit CONST.alt YSI_gMallocMemory
  257. #emit LOAD.pri __YSI_g_sHeapStart
  258. #emit SUB
  259. #emit SHR.C.pri 2 // Divide by 4 to get cells.
  260. #emit STOR.pri __YSI_g_sHeapStart
  261. YSI_gMallocMemory[YSI_g_sHeapStart] = MALLOC_MEMORY - 1;
  262. YSI_g_sUnusedStart = YSI_g_sHeapStart + 1;
  263. // Blank the whole memory. Maybe required if the heap has been used
  264. // already (better to be safe than sorry).
  265. memset(YSI_gMallocMemory[YSI_g_sHeapStart + 1], 0, MALLOC_MEMORY - 1);
  266. #if _DEBUG > 3
  267. // The "#if" is actually ignored by these "#emit" codes, as always.
  268. #emit CONST.alt YSI_gMallocMemory
  269. #emit STOR.S.alt temp
  270. printf("Malloc_OnScriptInit: AMX_HEADER_HEA = %d, YSI_gMallocMemory = %d, YSI_g_sHeapStart = %d", _:AMX_HEADER_HEA, temp, YSI_g_sHeapStart);
  271. printf("Malloc_OnScriptInit: YSI_gMallocMemory + 4 * YSI_g_sHeapStart = %d", temp + 4 * YSI_g_sHeapStart);
  272. #endif
  273. // This is never read, but we now have a spare cell because we HAD to
  274. // allocate an array of some size.
  275. YSI_gMallocMemory[0] = MALLOC_MEMORY;
  276. }
  277. static
  278. //YSI_g_sSolidifyHeap,
  279. YSI_g_sHeapSetup = 0;
  280. static const
  281. // Split in to three because of line length limits.
  282. YSI_g_scErrorMessage1[] =
  283. " \n" \
  284. " \n" \
  285. " ============================================================================\n" \
  286. " | |\n" \
  287. " | ****************** |\n" \
  288. " | * VERY IMPORTANT * |\n" \
  289. " | ****************** |",
  290. YSI_g_scErrorMessage2[] =
  291. " | |\n" \
  292. " | Your \"crashdetect\" plugin is out of date, ignore the \"error 12\" above! |\n" \
  293. " | |\n" \
  294. " | [debug] Run time error 12: \"(sleep mode)\" |\n" \
  295. " | [debug] AMX backtrace: |",
  296. //" | [debug] #0 <SOME ADDRESS> in %s () from <FILE>%s|\n"
  297. YSI_g_scErrorMessage3[] =
  298. " | [debug] #0 <HEX NUMBER> in public Malloc_SolidifyTimer () |\n" \
  299. " | |\n" \
  300. " | For more information, see the YSI.tl release topic. |\n" \
  301. " | |\n" \
  302. " ============================================================================\n" \
  303. " \n";
  304. forward OnRuntimeError(code, &bool:suppress);
  305. public OnRuntimeError(code, &bool:suppress)
  306. {
  307. // This is called if the "crashdetect" plugin is installed.
  308. if (code == 12)
  309. {
  310. P:0(YSI_g_scErrorMessage1);
  311. P:0(YSI_g_scErrorMessage2);
  312. P:0(YSI_g_scErrorMessage3);
  313. }
  314. #if defined Malloc_OnRuntimeError
  315. Malloc_OnRuntimeError(code, suppress);
  316. #endif
  317. if (code == 12)
  318. {
  319. // Make sure our heap is correctly set still...
  320. return Malloc_SolidifyHeap();
  321. }
  322. else return 0;
  323. }
  324. #if defined _ALS_OnRuntimeError
  325. #undef OnRuntimeError
  326. #else
  327. #define _ALS_OnRuntimeError
  328. #endif
  329. #define OnRuntimeError Malloc_OnRuntimeError
  330. #if defined Malloc_OnRuntimeError
  331. forward Malloc_OnRuntimeError(code, &bool:suppress);
  332. #endif
  333. forward Malloc_SolidifyTimer();
  334. public Malloc_SolidifyTimer()
  335. {
  336. P:1("Malloc_SolidifyTimer called");
  337. Malloc_TrySetup();
  338. Malloc_SolidifyHeap();
  339. }
  340. static Malloc_SolidifyHeap()
  341. {
  342. if (YSI_g_sHeapSetup == 2) return 1;
  343. ++YSI_g_sHeapSetup;
  344. #emit LCTRL 3
  345. #emit MOVE.alt
  346. #emit SCTRL 5
  347. #emit SCTRL 4 // Set the original stack pointer.
  348. // Call to save "stk" and "frm", since they aren't in early builds.
  349. #emit PUSH.C 0
  350. #emit SYSREQ.C heapspace // The pre-processor can't touch this.
  351. #emit STACK 4
  352. // Unfortunately, "heapspace" has a parameter pushed first so it saves the
  353. // wrong stack value (the value 4 below where it should be). The only other
  354. // opcode that reliably saves "stk" in "amx->stk" without trying to call a
  355. // native function is "OP_RETN", so let's BADLY invoke it! Note that we
  356. // still need the "heapspace" call (or any function call) to save "hea" and
  357. // "frm", which are NOT saved by "OP_RETN" but are by "OP_SYSREQ_C".
  358. #emit PUSH.C 0 // No parameters
  359. #emit LCTRL 6 // Return to the next instruction...
  360. #emit ADD.C 20
  361. #emit PUSH.pri
  362. #emit PUSH.alt // Same frame.
  363. #emit RETN
  364. // "return" to here...
  365. #emit HALT 12
  366. return 0;
  367. }
  368. /**--------------------------------------------------------------------------**\
  369. <summary>Malloc_OnPlayerConnect</summary>
  370. <param name="playerid">Player that just connected.</param>
  371. <returns>
  372. -
  373. </returns>
  374. <remarks>
  375. This is the only callback that can be called before our timers when the mode
  376. starts. Make sure the heap is set up correctly.
  377. </remarks>
  378. \**--------------------------------------------------------------------------**/
  379. forward Malloc_DoPlayerConnect(playerid);
  380. public Malloc_DoPlayerConnect(playerid)
  381. {
  382. if (YSI_g_sHeapSetup != 2) Malloc_TrySetup();
  383. // Ensure we only call the originsl.
  384. return call OnPlayerConnect(playerid);
  385. }