disasm.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. // Copyright (C) 2012 Zeex
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a
  4. // copy of this software and associated documentation files (the "Software"),
  5. // to deal in the Software without restriction, including without limitation
  6. // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  7. // and/or sell copies of the Software, and to permit persons to whom the
  8. // Software is furnished to do so, subject to the following conditions:
  9. //
  10. // The above copyright notice and this permission notice shall be included in
  11. // all copies or substantial portions of the Software.
  12. //
  13. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  14. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  18. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  19. // DEALINGS IN THE SOFTWARE.
  20. #if defined DISASM_INC
  21. #endinput
  22. #endif
  23. #define DISASM_INC
  24. #include <string>
  25. #include "amx_base"
  26. #include "amx_header"
  27. #include "amx_memory"
  28. #include "opcode"
  29. #define DISASM_MAX_INSN_NAME 15
  30. #define DISASM_MAX_PUBLIC_NAME 32
  31. #define DISASM_MAX_NATIVE_NAME 100
  32. enum DisasmInsnInfo {
  33. DisasmInsnInfo_name[DISASM_MAX_INSN_NAME],
  34. DisasmInsnInfo_num_opers,
  35. bool:DisasmInsnInfo_needs_reloc
  36. }
  37. enum DisasmContext {
  38. DisasmContext_start_ip,
  39. DisasmContext_end_ip,
  40. DisasmContext_nip,
  41. DisasmContext_cip,
  42. Opcode:DisasmContext_opcode,
  43. DisasmContext_insn[DisasmInsnInfo]
  44. }
  45. static stock const insn_table[][DisasmInsnInfo] = {
  46. { "none", 0, false },
  47. { "load.pri", 1, false },
  48. { "load.alt", 1, false },
  49. { "load.s.pri", 1, false },
  50. { "load.s.alt", 1, false },
  51. { "lref.pri", 1, false },
  52. { "lref.alt", 1, false },
  53. { "lref.s.pri", 1, false },
  54. { "lref.s.alt", 1, false },
  55. { "load.i", 0, false },
  56. { "lodb.i", 1, false },
  57. { "const.pri", 1, false },
  58. { "const.alt", 1, false },
  59. { "addr.pri", 1, false },
  60. { "addr.alt", 1, false },
  61. { "stor.pri", 1, false },
  62. { "stor.alt", 1, false },
  63. { "stor.s.pri", 1, false },
  64. { "stor.s.alt", 1, false },
  65. { "sref.pri", 1, false },
  66. { "sref.alt", 1, false },
  67. { "sref.s.pri", 1, false },
  68. { "sref.s.alt", 1, false },
  69. { "stor.i", 0, false },
  70. { "strb.i", 1, false },
  71. { "lidx", 0, false },
  72. { "lidx.b", 1, false },
  73. { "idxaddr", 0, false },
  74. { "idxaddr.b", 1, false },
  75. { "align.pri", 1, false },
  76. { "align.alt", 1, false },
  77. { "lctrl", 1, false },
  78. { "sctrl", 1, false },
  79. { "move.pri", 0, false },
  80. { "move.alt", 0, false },
  81. { "xchg", 0, false },
  82. { "push.pri", 0, false },
  83. { "push.alt", 0, false },
  84. { "push.r", 1, false },
  85. { "push.c", 1, false },
  86. { "push", 1, false },
  87. { "push.s", 1, false },
  88. { "pop.pri", 0, false },
  89. { "pop.alt", 0, false },
  90. { "stack", 1, false },
  91. { "heap", 1, false },
  92. { "proc", 0, false },
  93. { "ret", 0, false },
  94. { "retn", 0, false },
  95. { "call", 1, true },
  96. { "call.pri", 0, false },
  97. { "jump", 1, true },
  98. { "jrel", 1, true },
  99. { "jzer", 1, true },
  100. { "jnz", 1, true },
  101. { "jeq", 1, true },
  102. { "jneq", 1, true },
  103. { "jless", 1, true },
  104. { "jleq", 1, true },
  105. { "jgrtr", 1, true },
  106. { "jgeq", 1, true },
  107. { "jsless", 1, true },
  108. { "jsleq", 1, true },
  109. { "jsgrtr", 1, true },
  110. { "jsgeq", 1, true },
  111. { "shl", 0, false },
  112. { "shr", 0, false },
  113. { "sshr", 0, false },
  114. { "shl.c.pri", 1, false },
  115. { "shl.c.alt", 1, false },
  116. { "shr.c.pri", 1, false },
  117. { "shr.c.alt", 1, false },
  118. { "smul", 0, false },
  119. { "sdiv", 0, false },
  120. { "sdiv.alt", 0, false },
  121. { "umul", 0, false },
  122. { "udiv", 0, false },
  123. { "udiv.alt", 0, false },
  124. { "add", 0, false },
  125. { "sub", 0, false },
  126. { "sub.alt", 0, false },
  127. { "and", 0, false },
  128. { "or", 0, false },
  129. { "xort", 0, false },
  130. { "not", 0, false },
  131. { "neg", 0, false },
  132. { "invert", 0, false },
  133. { "add.c", 1, false },
  134. { "smul.c", 1, false },
  135. { "zero.pri", 0, false },
  136. { "zero.alt", 0, false },
  137. { "zero", 1, false },
  138. { "zero.s", 1, false },
  139. { "sign.pri", 0, false },
  140. { "sign.alt", 0, false },
  141. { "eq", 0, false },
  142. { "neq", 0, false },
  143. { "less", 0, false },
  144. { "leq", 0, false },
  145. { "grtr", 0, false },
  146. { "geq", 0, false },
  147. { "sless", 0, false },
  148. { "sleq", 0, false },
  149. { "sgrtr", 0, false },
  150. { "sgeq", 0, false },
  151. { "eq.c.pri", 1, false },
  152. { "eq.c.alt", 1, false },
  153. { "inc.pri", 0, false },
  154. { "inc.alt", 0, false },
  155. { "inc", 1, false },
  156. { "inc.s", 1, false },
  157. { "inc.i", 0, false },
  158. { "dec.pri", 0, false },
  159. { "dec.alt", 0, false },
  160. { "dec", 1, false },
  161. { "dec.s", 1, false },
  162. { "dec.i", 0, false },
  163. { "movs", 1, false },
  164. { "cmps", 1, false },
  165. { "fill", 1, false },
  166. { "halt", 1, false },
  167. { "bounds", 1, false },
  168. { "sysreq.pri", 1, false },
  169. { "sysreq.c", 1, false },
  170. { "file", -1, false }, // obsolete
  171. { "line", -1, false }, // obsolete
  172. { "symbol", -1, false }, // obsolete
  173. { "srange", -1, false }, // obsolete
  174. { "jump.pri", 0, false },
  175. { "switch", 1, true },
  176. { "casetbl", -1, true },
  177. { "swap.pri", 0, false },
  178. { "swap.alt", 0, false },
  179. { "push.adr", 1, false },
  180. { "nop", 0, false },
  181. { "sysreq.d", 1, false },
  182. { "symtag", 1, false }, // obsolete
  183. { "break", 0, false }
  184. };
  185. stock DisasmInit(ctx[DisasmContext], start = 0, end = 0) {
  186. new hdr[AMX_HDR];
  187. GetAmxHeader(hdr);
  188. new dat = hdr[AMX_HDR_DAT];
  189. new cod = hdr[AMX_HDR_COD];
  190. new code_base = cod - dat;
  191. start += code_base;
  192. ctx[DisasmContext_nip] = start;
  193. ctx[DisasmContext_cip] = start;
  194. ctx[DisasmContext_start_ip] = start;
  195. if (end != 0) {
  196. ctx[DisasmContext_end_ip] = code_base + end;
  197. } else {
  198. ctx[DisasmContext_end_ip] = code_base + (dat - cod);
  199. }
  200. }
  201. stock bool:DisasmDecodeInsn(ctx[DisasmContext]) {
  202. new ip = ctx[DisasmContext_nip];
  203. if (ip >= 0) {
  204. return false;
  205. }
  206. new Opcode:opcode = UnrelocateOpcode(Opcode:ReadAmxMemory(ip));
  207. if (opcode <= OP_NONE || _:opcode >= NUM_OPCODES) {
  208. return false;
  209. }
  210. ctx[DisasmContext_cip] = ip;
  211. ctx[DisasmContext_opcode] = opcode;
  212. ctx[DisasmContext_insn] = insn_table[_:opcode];
  213. ip += 4;
  214. if (opcode == OP_CASETBL) {
  215. new n = ReadAmxMemory(ip);
  216. ip += 4;
  217. ip += (2 * n + 1) * 4;
  218. ctx[DisasmContext_insn][DisasmInsnInfo_num_opers] = n;
  219. } else {
  220. ip += 4 * insn_table[_:opcode][DisasmInsnInfo_num_opers];
  221. }
  222. ctx[DisasmContext_nip] = ip;
  223. return true;
  224. }
  225. stock Opcode:DisasmNextInsn(ctx[DisasmContext]) {
  226. if (DisasmDecodeInsn(ctx)) {
  227. return ctx[DisasmContext_opcode];
  228. }
  229. return OP_NONE;
  230. }
  231. stock Opcode:DisasmGetOpcode(ctx[DisasmContext]) {
  232. return ctx[DisasmContext_opcode];
  233. }
  234. stock DisasmGetOperand(ctx[DisasmContext], index = 0) {
  235. return ReadAmxMemory(ctx[DisasmContext_cip] + (index + 1) * 4);
  236. }
  237. stock DisasmGetNumOperands(ctx[DisasmContext]) {
  238. return ctx[DisasmContext_insn][DisasmInsnInfo_num_opers];
  239. }
  240. stock bool:DisasmNeedReloc(ctx[DisasmContext]) {
  241. return ctx[DisasmContext_insn][DisasmInsnInfo_needs_reloc];
  242. }
  243. stock DisasmGetNextIp(ctx[DisasmContext]) {
  244. return ctx[DisasmContext_nip];
  245. }
  246. stock DisasmGetCurIp(ctx[DisasmContext]) {
  247. return ctx[DisasmContext_cip];
  248. }
  249. stock DisasmGetInsnName(ctx[DisasmContext], name[], size = sizeof(name)) {
  250. name[0] = '\0';
  251. strcat(name, ctx[DisasmContext_insn][DisasmInsnInfo_name], size);
  252. }
  253. static stock ToHexStr(x) {
  254. new s[11];
  255. new i = 0;
  256. new j = 0;
  257. while (i < sizeof(s) && j < 8) {
  258. new n = x >> (7 - j) * 4 & 0xF;
  259. switch (n) {
  260. case 0x0..0x9:
  261. s[i] = n + '0';
  262. case 0xA..0xF:
  263. s[i] = n + 'a' - 0xA;
  264. }
  265. i++;
  266. j++;
  267. }
  268. return s;
  269. }
  270. static stock bool:IsPrintableAscii(c) {
  271. return 32 <= c <= 126;
  272. }
  273. static stock ToPrintableAscii(c) {
  274. return IsPrintableAscii(c) ? c : ' ';
  275. }
  276. stock DisasmWriteCode(File:file) {
  277. new ctx[DisasmContext];
  278. DisasmInit(ctx);
  279. new base = GetAmxBaseAddress();
  280. new hdr[AMX_HDR];
  281. GetAmxHeader(hdr);
  282. new dat = hdr[AMX_HDR_DAT];
  283. new cod = hdr[AMX_HDR_COD];
  284. fwrite(file, "; CODE\n\n");
  285. while (DisasmGetNextIp(ctx) < ctx[DisasmContext_end_ip])
  286. {
  287. if (!DisasmDecodeInsn(ctx)) {
  288. new cip = DisasmGetNextIp(ctx);
  289. ctx[DisasmContext_nip] += 4;
  290. fwrite(file, ToHexStr(cip + dat - cod));
  291. fwrite(file, " ???? ");
  292. fwrite(file, ToHexStr(ReadAmxMemory(cip)));
  293. fwrite(file, "\n");
  294. continue;
  295. }
  296. new Opcode:opcode = DisasmGetOpcode(ctx);
  297. if (opcode == OP_PROC) {
  298. fwrite(file, "\n");
  299. }
  300. new cip = DisasmGetCurIp(ctx);
  301. new insn_name[DISASM_MAX_INSN_NAME];
  302. DisasmGetInsnName(ctx, insn_name);
  303. fwrite(file, ToHexStr(cip + dat - cod));
  304. fwrite(file, " ");
  305. fwrite(file, insn_name);
  306. fwrite(file, " ");
  307. switch (opcode) {
  308. case OP_PROC: {
  309. new name[32];
  310. new address = cip + dat - cod;
  311. if (address == hdr[AMX_HDR_CIP]) {
  312. strcat(name, "main");
  313. } else {
  314. new index = GetPublicIndexFromAddress(address);
  315. if (index >= 0) {
  316. GetPublicNameFromIndex(index, name);
  317. }
  318. }
  319. if (strlen(name) != 0) {
  320. fwrite(file, "; ");
  321. fwrite(file, name);
  322. }
  323. }
  324. case OP_CASETBL: {
  325. new num = DisasmGetNumOperands(ctx);
  326. fwrite(file, ToHexStr(num));
  327. fwrite(file, " ");
  328. new rel_addr = DisasmGetOperand(ctx, 1) - (base + cod);
  329. fwrite(file, ToHexStr(rel_addr));
  330. for (new i = 1; i <= num; i++) {
  331. fwrite(file, "\n case ");
  332. new val = DisasmGetOperand(ctx, i * 2);
  333. fwrite(file, ToHexStr(val));
  334. fwrite(file, " ");
  335. rel_addr = DisasmGetOperand(ctx, i * 2 + 1) - (base + cod);
  336. fwrite(file, ToHexStr(rel_addr));
  337. }
  338. }
  339. case OP_CALL: {
  340. new name[DISASM_MAX_PUBLIC_NAME];
  341. new address = DisasmGetOperand(ctx) - base - cod;
  342. if (address == hdr[AMX_HDR_CIP]) {
  343. strcat(name, "main");
  344. } else {
  345. new index = GetPublicIndexFromAddress(address);
  346. if (index >= 0) {
  347. GetPublicNameFromIndex(index, name);
  348. }
  349. }
  350. fwrite(file, ToHexStr(address));
  351. if (strlen(name) > 0) {
  352. fwrite(file, "; ");
  353. fwrite(file, name);
  354. }
  355. }
  356. case OP_SYSREQ_C, OP_SYSREQ_D: {
  357. new name[DISASM_MAX_NATIVE_NAME];
  358. new address = DisasmGetOperand(ctx);
  359. if (opcode == OP_SYSREQ_C) {
  360. new index = DisasmGetOperand(ctx);
  361. GetNativeNameFromIndex(index, name);
  362. } else {
  363. new index = GetNativeIndexFromAddress(address);
  364. if (index >= 0) {
  365. GetNativeNameFromIndex(index, name);
  366. }
  367. }
  368. fwrite(file, ToHexStr(address));
  369. if (strlen(name) > 0) {
  370. fwrite(file, "; ");
  371. fwrite(file, name);
  372. }
  373. }
  374. default: {
  375. new n = DisasmGetNumOperands(ctx);
  376. for (new i = 0; i < n; i++) {
  377. new operand = DisasmGetOperand(ctx, i);
  378. if (DisasmNeedReloc(ctx)) {
  379. operand -= base + cod;
  380. }
  381. fwrite(file, ToHexStr(operand));
  382. }
  383. }
  384. }
  385. fwrite(file, "\n");
  386. }
  387. }
  388. stock DisasmWriteDataRowChar(File:file, start, num, max) {
  389. new cur = start;
  390. new end = start + num*4;
  391. while (cur < max) {
  392. new p[4 char + 1];
  393. p[0] = ReadAmxMemory(cur);
  394. new u[4 + 1];
  395. u[0] = ToPrintableAscii(p{0});
  396. u[1] = ToPrintableAscii(p{1});
  397. u[2] = ToPrintableAscii(p{2});
  398. u[3] = ToPrintableAscii(p{3});
  399. u[4] = '\0';
  400. if (cur < end) {
  401. fwrite(file, u);
  402. } else {
  403. fwrite(file, " ");
  404. }
  405. cur += 4;
  406. }
  407. }
  408. stock DisasmWriteDataRowHex(File:file, start, num, max) {
  409. new cur = start;
  410. new end = start + num*4;
  411. while (cur < max) {
  412. if (cur < end) {
  413. fwrite(file, ToHexStr(ReadAmxMemory(cur)));
  414. } else {
  415. fwrite(file, " ");
  416. }
  417. fwrite(file, " ");
  418. cur += 4;
  419. }
  420. }
  421. stock DisasmWriteData(File:file) {
  422. fwrite(file, "\n\n; DATA\n");
  423. new hdr[AMX_HDR];
  424. GetAmxHeader(hdr);
  425. new dat = hdr[AMX_HDR_DAT];
  426. new hea = hdr[AMX_HDR_HEA];
  427. new data_end = hea - dat;
  428. for (new i = 0; i < data_end; i += 0x10) {
  429. fwrite(file, ToHexStr(i));
  430. fwrite(file, " ");
  431. DisasmWriteDataRowHex(file, i, 4, min(i + 0x10, data_end));
  432. fwrite(file, " ");
  433. DisasmWriteDataRowChar(file, i, 4, min(i + 0x10, data_end));
  434. fwrite(file, "\n");
  435. }
  436. }
  437. stock DisasmWriteFile(File:file) {
  438. DisasmWriteCode(file);
  439. DisasmWriteData(file);
  440. }
  441. stock bool:DisasmWrite(const filename[]) {
  442. new File:file = fopen(filename, io_write);
  443. if (file) {
  444. DisasmWriteFile(file);
  445. fclose(file);
  446. return true;
  447. }
  448. return false;
  449. }
  450. stock DisasmDump(const filename[]) {
  451. DisasmWrite(filename);
  452. }