y_malloc_heapalloc.inc 11 KB

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