| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- // Copyright (C) 2012 Zeex
- //
- // Permission is hereby granted, free of charge, to any person obtaining a
- // copy of this software and associated documentation files (the "Software"),
- // to deal in the Software without restriction, including without limitation
- // the rights to use, copy, modify, merge, publish, distribute, sublicense,
- // and/or sell copies of the Software, and to permit persons to whom the
- // Software is furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- // DEALINGS IN THE SOFTWARE.
- #if defined DISASM_INC
- #endinput
- #endif
- #define DISASM_INC
- #include <string>
- #include <file>
- #include "amx_base"
- #include "amx_header"
- #include "amx_memory"
- #include "opcode"
- #define DISASM_MAX_PUBLIC_NAME 32
- #define DISASM_MAX_NATIVE_NAME 100
- enum DisasmContext {
- DisasmContext_start_ip,
- DisasmContext_end_ip,
- DisasmContext_nip,
- DisasmContext_cip,
- Opcode:DisasmContext_opcode,
- DisasmContext_insn[OpcodeInsnInfo]
- }
- enum DisasmResult {
- DISASM_DONE = 0,
- DISASM_NOP = 1,
- DISASM_OK = 2
- }
- static stock gCodBase;
- stock DisasmInit(ctx[DisasmContext], start = 0, end = 0) {
- new hdr[AMX_HDR];
- GetAmxHeader(hdr);
-
- gCodBase = GetAmxBaseAddress() + hdr[AMX_HDR_COD];
- new dat = hdr[AMX_HDR_DAT];
- new cod = hdr[AMX_HDR_COD];
- new code_base = cod - dat;
- start += code_base;
- ctx[DisasmContext_nip] = start;
- ctx[DisasmContext_cip] = start;
- ctx[DisasmContext_start_ip] = start;
- if (end != 0) {
- ctx[DisasmContext_end_ip] = code_base + end;
- } else {
- // code_base + (dat - cod)
- // = (cod - dat) + (dat - cod)
- // = cod - dat + dat - cod
- // = cod - cod + dat - dat
- // = 0
- ctx[DisasmContext_end_ip] = 0;
- }
- }
- stock bool:DisasmDecodeInsn(ctx[DisasmContext]) {
- new ip = ctx[DisasmContext_nip];
- if (ip >= 0) {
- return false;
- }
- new Opcode:opcode = UnrelocateOpcode(Opcode:ReadAmxMemory(ip));
- if (opcode <= OP_NONE || _:opcode >= NUM_OPCODES) {
- return false;
- }
- ctx[DisasmContext_cip] = ip;
- ctx[DisasmContext_opcode] = opcode;
- ctx[DisasmContext_insn] = GetOpcodeInstructionInformation(opcode);
- ip += 4;
- if (opcode == OP_CASETBL) {
- new n = ReadAmxMemory(ip);
- ip += 4;
- ip += (2 * n + 1) * 4;
- ctx[DisasmContext_insn][OpcodeInsnInfo_num_opers] = n;
- } else {
- ip += 4 * ctx[DisasmContext_insn][OpcodeInsnInfo_num_opers];
- }
- ctx[DisasmContext_nip] = ip;
- return true;
- }
- stock DisasmResult:DisasmNext(ctx[DisasmContext]) {
- // This function is just a slightly more consistent wrapper around
- // "DisasmDecodeInsn". I wanted to change that function, but that would
- // cause breaking changes. This returns non-zero while there is still data
- // being read, even if that data is not a valid opcode. For invalid code,
- // it skips the current cell and returns an invalid opcode. It also now
- // checks the end point correctly.
- if (ctx[DisasmContext_nip] >= ctx[DisasmContext_end_ip]) {
- return DISASM_DONE;
- } else if (DisasmDecodeInsn(ctx)) {
- return DISASM_OK;
- } else {
- ctx[DisasmContext_cip] = ctx[DisasmContext_nip],
- ctx[DisasmContext_nip] += 4,
- ctx[DisasmContext_opcode] = Opcode:NUM_OPCODES;
- return DISASM_NOP;
- }
- }
- stock Opcode:DisasmNextInsn(ctx[DisasmContext]) {
- if (DisasmDecodeInsn(ctx)) {
- return ctx[DisasmContext_opcode];
- }
- return OP_NONE;
- }
- stock Opcode:DisasmGetOpcode(ctx[DisasmContext]) {
- return ctx[DisasmContext_opcode];
- }
- stock DisasmGetOperand(ctx[DisasmContext], index = 0) {
- return ReadAmxMemory(ctx[DisasmContext_cip] + (index + 1) * 4);
- }
- stock DisasmGetNumOperands(ctx[DisasmContext]) {
- return ctx[DisasmContext_insn][OpcodeInsnInfo_num_opers];
- }
- stock bool:DisasmNeedReloc(ctx[DisasmContext]) {
- return ctx[DisasmContext_insn][OpcodeInsnInfo_needs_reloc];
- }
- stock DisasmReloc(addr) {
- return addr - gCodBase;
- }
- stock DisasmGetNextIp(ctx[DisasmContext]) {
- return ctx[DisasmContext_nip];
- }
- stock DisasmGetCurIp(ctx[DisasmContext]) {
- return ctx[DisasmContext_cip];
- }
- stock DisasmGetRemaining(ctx[DisasmContext]) {
- return ctx[DisasmContext_end_ip] - ctx[DisasmContext_nip];
- }
- stock DisasmGetInsnName(ctx[DisasmContext], name[], size = sizeof(name)) {
- name[0] = '\0';
- strcat(name, ctx[DisasmContext_insn][OpcodeInsnInfo_name], size);
- }
- stock DisasmGetOperandReloc(ctx[DisasmContext], index = 0) {
- new param = DisasmGetOperand(ctx, index);
- // Needs special code for dealing with "CASETBL", which has multiple
- // parameters - not all of them to be relocated. If the opcode is NOT that,
- // then check if it is any other opcode requiring relocation. This does
- // result in the odd pattern of having a triadic operator in a conditional,
- // but the alternative would be:
- //
- // if (ctx[DisasmContext_opcode == OP_CASETBL) {
- // if (index & 1) {
- // return param - base;
- // }
- // } else if (DisasmNeedReloc(ctx)) {
- // return param - base;
- // }
- // return param;
- //
- // Or:
- //
- // if ((ctx[DisasmContext_opcode == OP_CASETBL && (index & 1)) ||
- // (ctx[DisasmContext_opcode != OP_CASETBL && DisasmNeedReloc(ctx))) {
- // return param - base;
- // } else {
- // return param;
- // }
- //
- // I think the conditional ends up nicer in this rare case.
- if ((ctx[DisasmContext_opcode] == OP_CASETBL) ? (index & 1) : _:DisasmNeedReloc(ctx)) {
- return DisasmReloc(param);
- } else {
- return param;
- }
- }
- static stock ToHexStr(x) {
- new s[11];
- new i = 0;
- new j = 0;
- while (i < sizeof(s) && j < 8) {
- new n = x >> (7 - j) * 4 & 0xF;
- switch (n) {
- case 0x0..0x9:
- s[i] = n + '0';
- case 0xA..0xF:
- s[i] = n + 'a' - 0xA;
- }
- i++;
- j++;
- }
- return s;
- }
- static stock bool:IsPrintableAscii(c) {
- return 32 <= c <= 126;
- }
- static stock ToPrintableAscii(c) {
- return IsPrintableAscii(c) ? c : ' ';
- }
- stock DisasmWriteCode(File:file) {
- new ctx[DisasmContext];
- DisasmInit(ctx);
- new hdr[AMX_HDR];
- GetAmxHeader(hdr);
- new dat = hdr[AMX_HDR_DAT];
- new cod = hdr[AMX_HDR_COD];
- fwrite(file, "; CODE\n\n");
- while (DisasmGetNextIp(ctx) < ctx[DisasmContext_end_ip])
- {
- if (!DisasmDecodeInsn(ctx)) {
- new cip = DisasmGetNextIp(ctx);
- ctx[DisasmContext_nip] += 4;
- fwrite(file, ToHexStr(cip + dat - cod));
- fwrite(file, " ???? ");
- fwrite(file, ToHexStr(ReadAmxMemory(cip)));
- fwrite(file, "\n");
- continue;
- }
- new cip = DisasmGetCurIp(ctx);
- new Opcode:opcode = DisasmGetOpcode(ctx);
- if (opcode == OP_PROC) {
- fwrite(file, "\n");
- }
- new insn_name[OPCODE_MAX_INSN_NAME];
- DisasmGetInsnName(ctx, insn_name);
- fwrite(file, ToHexStr(cip + dat - cod));
- fwrite(file, " ");
- fwrite(file, insn_name);
- fwrite(file, " ");
- switch (opcode) {
- case OP_PROC: {
- new name[32];
- new address = cip + dat - cod;
- if (address == hdr[AMX_HDR_CIP]) {
- strcat(name, "main");
- } else {
- new index = GetPublicIndexFromAddress(address);
- if (index >= 0) {
- GetPublicNameFromIndex(index, name);
- }
- }
- if (strlen(name) != 0) {
- fwrite(file, "; ");
- fwrite(file, name);
- }
- }
- case OP_CASETBL: {
- new num = DisasmGetNumOperands(ctx);
- fwrite(file, ToHexStr(num));
- fwrite(file, " ");
- new rel_addr = DisasmGetOperand(ctx, 1) - gCodBase;
- fwrite(file, ToHexStr(rel_addr));
- for (new i = 1; i <= num; i++) {
- fwrite(file, "\n case ");
- new val = DisasmGetOperand(ctx, i * 2);
- fwrite(file, ToHexStr(val));
- fwrite(file, " ");
- rel_addr = DisasmGetOperand(ctx, i * 2 + 1) - gCodBase;
- fwrite(file, ToHexStr(rel_addr));
- }
- }
- case OP_CALL: {
- new name[DISASM_MAX_PUBLIC_NAME];
- new address = DisasmGetOperand(ctx) - gCodBase;
- if (address == hdr[AMX_HDR_CIP]) {
- strcat(name, "main");
- } else {
- new index = GetPublicIndexFromAddress(address);
- if (index >= 0) {
- GetPublicNameFromIndex(index, name);
- }
- }
- fwrite(file, ToHexStr(address));
- if (strlen(name) > 0) {
- fwrite(file, "; ");
- fwrite(file, name);
- }
- }
- case OP_SYSREQ_C, OP_SYSREQ_D: {
- new name[DISASM_MAX_NATIVE_NAME];
- new address = DisasmGetOperand(ctx);
- if (opcode == OP_SYSREQ_C) {
- new index = DisasmGetOperand(ctx);
- GetNativeNameFromIndex(index, name);
- } else {
- new index = GetNativeIndexFromAddress(address);
- if (index >= 0) {
- GetNativeNameFromIndex(index, name);
- }
- }
- fwrite(file, ToHexStr(address));
- if (strlen(name) > 0) {
- fwrite(file, "; ");
- fwrite(file, name);
- }
- }
- default: {
- new n = DisasmGetNumOperands(ctx);
- for (new i = 0; i < n; i++) {
- new operand = DisasmGetOperandReloc(ctx, i);
- fwrite(file, ToHexStr(operand));
- }
- }
- }
- fwrite(file, "\n");
- }
- }
- stock DisasmWriteDataRowChar(File:file, start, num, max) {
- new cur = start;
- new end = start + num*4;
- while (cur < max) {
- new p[4 char + 1];
- p[0] = ReadAmxMemory(cur);
- new u[4 + 1];
- u[0] = ToPrintableAscii(p{0});
- u[1] = ToPrintableAscii(p{1});
- u[2] = ToPrintableAscii(p{2});
- u[3] = ToPrintableAscii(p{3});
- u[4] = '\0';
- if (cur < end) {
- fwrite(file, u);
- } else {
- fwrite(file, " ");
- }
- cur += 4;
- }
- }
- stock DisasmWriteDataRowHex(File:file, start, num, max) {
- new cur = start;
- new end = start + num*4;
- while (cur < max) {
- if (cur < end) {
- fwrite(file, ToHexStr(ReadAmxMemory(cur)));
- } else {
- fwrite(file, " ");
- }
- fwrite(file, " ");
- cur += 4;
- }
- }
- stock DisasmWriteData(File:file) {
- fwrite(file, "\n\n; DATA\n");
- new hdr[AMX_HDR];
- GetAmxHeader(hdr);
- new dat = hdr[AMX_HDR_DAT];
- new hea = hdr[AMX_HDR_HEA];
- new data_end = hea - dat;
- for (new i = 0; i < data_end; i += 0x10) {
- fwrite(file, ToHexStr(i));
- fwrite(file, " ");
- DisasmWriteDataRowHex(file, i, 4, min(i + 0x10, data_end));
- fwrite(file, " ");
- DisasmWriteDataRowChar(file, i, 4, min(i + 0x10, data_end));
- fwrite(file, "\n");
- }
- }
- stock DisasmWriteFile(File:file) {
- DisasmWriteCode(file);
- DisasmWriteData(file);
- }
- stock bool:DisasmWrite(const filename[]) {
- new File:file = fopen(filename, io_write);
- if (file) {
- DisasmWriteFile(file);
- fclose(file);
- return true;
- }
- return false;
- }
- stock DisasmDump(const filename[]) {
- DisasmWrite(filename);
- }
|