module: disasm author: Jon Thackray Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // A disassembler for i386, i486 and i586 code define method arg-pair-to-string(arg1 :: , arg2 :: ) => (str :: ) concatenate(arg1, ", ", arg2); end method arg-pair-to-string; define method hex-digit-to-byte(x :: ) => (ch :: ) if (x < 0 | x > 15) '?' else as (, if (x < 10) as(, '0') + x else as(, 'a') + x - 10 end) end end method hex-digit-to-byte; define method hex-byte-to-string(x :: ) => (str :: ) if (x < 0 | x > 255) make(, size: 2, fill: '?') else let str = make(, size: 2); let (hi, lo) = truncate/(x, 16); str[0] := hex-digit-to-byte(hi); str[1] := hex-digit-to-byte(lo); str end end method hex-byte-to-string; define method hex-byte-to-string(x :: ) => (str :: ) hex-byte-to-string(as(, x)); end method hex-byte-to-string; define method byte-sequence-to-string(code-vector :: , from :: , to :: ) => (str :: ) let size = if (to <= from) 0 else to - from end; let out-string = make(, size: size * 2); for (i from 0 below size) let str = hex-byte-to-string(code-vector[from + i]); let two-i = 2 * i; out-string[two-i] := str[0]; out-string[two-i + 1] := str[1]; end for; out-string end method byte-sequence-to-string; define method immediate-value-to-string(imm :: ) => (str :: ) // format-out("Entering immediate-value-to-string for byte-immediate-value 0x%x\n", imm.byte-immediate-value); let imm = imm.byte-immediate-value; let imm1 = if (imm < #x80) imm else #x100 - imm end; let hex = hex-byte-to-string(imm1); if (imm < #x80) hex else concatenate("-", hex) end end method immediate-value-to-string; define method immediate-value-to-string(imm :: ) => (str :: ) // format-out("Entering immediate-value-to-string for short-immediate-value 0x%x\n", imm.short-immediate-value); let (hi, lo) = truncate/(imm.short-immediate-value, #x100); concatenate(hex-byte-to-string(hi), hex-byte-to-string(lo)); end method immediate-value-to-string; define method word-imm-to-string(imm :: ) => (str :: ) let lo = logand(imm, #xffff); let hi = ash(imm, -16); concatenate(immediate-value-to-string(make(, short-immediate-value: hi)), immediate-value-to-string(make(, short-immediate-value: lo))); end method word-imm-to-string; define method relocated-imm-to-string(ext :: , i :: ) => (res :: ) word-imm-to-string(i); end method; define method relocated-word-imm-to-string(ext :: , imm :: ) => (str :: ) let label-str = ext.label-name; if (imm ~== 0) let val-str = word-imm-to-string(imm); concatenate(label-str, "+", val-str) else label-str end end method relocated-word-imm-to-string; define method relocated-imm-to-string(ext :: , imm :: ) => (res :: ) relocated-word-imm-to-string(ext, imm); end method; define method relocated-imm-to-string(ext :: , imm :: ) => (res :: ) let val = imm + ext.offset; relocated-word-imm-to-string(ext, val); end method; define method immediate-value-to-string(imm :: ) => (str :: ) // format-out("Entering immediate-value-to-string for word-immediate-value 0x%x\n", imm.word-immediate-value); let external = imm.word-relocation; let val = imm.word-immediate-value; relocated-imm-to-string(external, val); end method immediate-value-to-string; define method immediate-value-to-string-plus-sign(imm :: ) => (str :: ) let str = immediate-value-to-string(imm); if (str[0] == '-') str else concatenate("+", str) end end method immediate-value-to-string-plus-sign; define method immediate-value-to-string-plus-sign(imm :: ) => (str :: ) concatenate("+", immediate-value-to-string(imm)) end method immediate-value-to-string-plus-sign; define method register-to-string(register :: ) => (str :: ) register.register-name; end method register-to-string; define method fp-register-to-string(fp-register :: ) => (str :: ) let reg = fp-register.fp-register-pos; if (reg == 0) "ST" else format-to-string("ST(%d)", reg) end end method fp-register-to-string; define method add-offset-for-immediate(imm :: , str :: ) => (res :: ) select (imm.word-relocation by instance?) => str; otherwise concatenate("offset ", str) end select; end method; define method add-offset-for-immediate(imm :: , str :: ) => (res :: ) str end method; define method arg-to-string(arg :: ) => (str :: ) // format-out("Entering arg-to-string for immediate-arg\n"); let imm = arg.arg-immediate-value; let foo = immediate-value-to-string(imm); // format-out("returning '%s'\n", foo); add-offset-for-immediate(arg.arg-immediate-value, foo); end method arg-to-string; define method arg-to-string(arg :: ) => (str :: ) // format-out("Entering arg-to-string for register-arg\n"); let foo = register-to-string(arg.register-arg); // format-out("returning '%s'\n", foo); foo; end method arg-to-string; define method arg-to-string(arg :: ) => (str :: ) // format-out("Entering arg-to-string for fp-register-arg\n"); let foo = fp-register-to-string(arg.fp-register-arg); // format-out("returning '%s'\n", foo); foo; end method arg-to-string; define method scale-to-string(scale :: ) => (str :: ) select(scale) 1 => "2"; 2 => "4"; 3 => "8"; otherwise error ("Invalid scale %d in scale-to-string\n", scale); end end method scale-to-string; define method memory-index-to-string(index :: ) => (str :: ) error("memory-index-to-string called with no-memory-index\n"); end method memory-index-to-string; define method memory-index-to-string(index :: ) => (str :: ) let reg = register-to-string(index.indexed-memory-index-reg); if (index.indexed-memory-index-scale == 0) reg else concatenate(reg, "*", scale-to-string(index.indexed-memory-index-scale)); end end method memory-index-to-string; define method disp-base-index-to-string(disp :: , base :: , index :: ) => (str :: ) error("memory-arg without base, displacement or index\n"); end method disp-base-index-to-string; define method disp-base-index-to-string(disp :: , base :: , index :: ) => (str :: ) error("memory-arg with index and no base or displacement\n"); end method disp-base-index-to-string; define method disp-base-index-to-string(disp :: , base :: , index :: ) => (str :: ) concatenate("[", register-to-string(base.memory-base-reg), "+", memory-index-to-string(index), "]"); end method disp-base-index-to-string; define method disp-base-index-to-string( disp :: , base :: , index :: ) => (str :: ) concatenate("[", register-to-string(base.memory-base-reg), "]"); end method disp-base-index-to-string; define method disp-base-index-to-string(disp :: , base :: , index :: ) => (str :: ) concatenate("[", immediate-value-to-string(disp.memory-displacement), "]"); end method disp-base-index-to-string; define method disp-base-index-to-string(disp :: , base :: , index :: ) => (str :: ) concatenate("[", register-to-string(base.memory-base-reg), immediate-value-to-string-plus-sign(disp.memory-displacement), "]"); end method disp-base-index-to-string; define method disp-base-index-to-string(disp :: , base :: , index :: ) => (str :: ) concatenate("[", memory-index-to-string(index), immediate-value-to-string-plus-sign(disp.memory-displacement), "]"); end method disp-base-index-to-string; define method disp-base-index-to-string(disp :: , base :: , index :: ) => (str :: ) concatenate("[", memory-index-to-string(index), "+", register-to-string(base.memory-base-reg), immediate-value-to-string-plus-sign(disp.memory-displacement), "]"); end method disp-base-index-to-string; define method absolute-address(disp :: , base :: , index :: , seg :: ) => (bool :: ) #t end method absolute-address; define method absolute-address(disp :: , base :: , index :: , seg :: ) => (bool :: ) #f end method absolute-address; define method imm-to-integer(imm :: ) => (int :: ) imm.byte-immediate-value end method imm-to-integer; define method imm-to-integer(imm :: ) => (int :: ) imm.short-immediate-value end method imm-to-integer; define method imm-to-integer(imm :: ) => (int :: ) imm.word-immediate-value end method imm-to-integer; define method disp-to-integer(disp :: ) => (int :: ) 0 end method disp-to-integer; define method disp-to-integer(disp :: ) => (int :: ) imm-to-integer(disp.memory-displacement) end method disp-to-integer; define method address-to-string(address :: ) => (str :: ) let hex1 = hex-byte-to-string(logand(address, #xff)); let hex2 = hex-byte-to-string(logand(ash(address, -8), #xff)); let hex3 = hex-byte-to-string(logand(ash(address, -16), #xff)); let hex4 = hex-byte-to-string(logand(ash(address, -24), #xff)); concatenate(hex4, hex3, hex2, hex1) end method address-to-string; define method memory-arg-size-to-string(arg :: ) => (res :: ) "byte ptr" end method memory-arg-size-to-string; define method memory-arg-size-to-string(arg :: ) => (res :: ) "word ptr" end method memory-arg-size-to-string; define method memory-arg-size-to-string(arg :: ) => (res :: ) "dword ptr" end method memory-arg-size-to-string; define method memory-arg-size-to-string(arg :: ) => (res :: ) "qword ptr" end method memory-arg-size-to-string; define method memory-arg-size-to-string(arg :: ) => (res :: ) "dword ptr" end method memory-arg-size-to-string; define method memory-arg-size-to-string(arg :: ) => (res :: ) "qword ptr" end method memory-arg-size-to-string; define method memory-arg-size-to-string(arg :: ) => (res :: ) "tbyte ptr" end method memory-arg-size-to-string; define method memory-arg-to-string(arg :: , seg :: , table-lookup :: ) => (str :: ) // format-out("Entering arg-to-string for memory-arg\n"); let disp = arg.memory-arg-disp; let base = arg.memory-arg-base; let index = arg.memory-arg-index; let arg-szie = arg.memory-arg-size; let addr = if (absolute-address(disp, base, index, seg) & table-lookup(disp-to-integer(disp)) ~== #f) let (str, offset) = table-lookup(disp-to-integer(disp)); concatenate(str, "+", address-to-string(offset)) else let addr = disp-base-index-to-string(disp, base, index); select (seg by instance?) => addr; otherwise concatenate(seg.segment-register.segment-register-name, ":", addr); end select; end; concatenate(memory-arg-size-to-string(arg.memory-arg-size), " ", addr); end method memory-arg-to-string; define method get-offset-as-integer(offset :: ) => (int :: , external :: ) let int = offset.byte-offset; values(if (int < #x80) int else int - #x100 end, $no-external) end method get-offset-as-integer; define method get-offset-as-integer(offset :: ) => (int :: , external :: ) let int = offset.short-offset; values(if (int < #x8000) int else int - #x10000 end, $no-external) end method get-offset-as-integer; define method get-offset-as-integer(offset :: ) => (int :: , external :: ) let int = offset.word-offset; let max-neg = #x8000 * #x10000; let max = 2 * max-neg; values(if (int < max-neg) int else int - max end, offset.word-offset-relocation) end method get-offset-as-integer; define method arg-plus-override-to-string(arg :: , seg :: , table-lookup :: ) => (str :: ) // format-out("Entering arg-plus-override-to-string\n"); select (arg by instance?) => memory-arg-to-string(arg, seg, table-lookup); otherwise arg-to-string(arg); end select end method arg-plus-override-to-string; define method arg-plus-override-plus-table-plus-address-to-string(arg :: , seg :: , table :: , end-index :: , address :: , table-lookup :: ) => (str :: ) // format-out("Entering arg-plus-override-to-string\n"); select (arg by instance?) => memory-arg-to-string(arg, seg, table-lookup); => let (offset, external) = get-offset-as-integer(arg.offset-arg); select (external by instance?) => let offset-in-chunk = end-index + offset; /* The real destination address */ let res = element(table, offset-in-chunk, default: #f); if (res == #f) let full-address = address + offset-in-chunk; let (str, offset) = table-lookup(full-address); if (str == #f) address-to-string(full-address) else concatenate(str, "+", address-to-string(offset)) end else res end; => let name = external.label-name; if (offset == 0) name else format-to-string("%s+%s", name, address-to-string(offset)) end; otherwise => error("arg-to-string got funny external\n"); end select; otherwise arg-to-string(arg); end select end method arg-plus-override-plus-table-plus-address-to-string; define method opcode-to-string(table ::
, opcode :: , end-index :: , address :: , table-lookup :: ) => (str :: ) local method args-to-string(args :: , seg :: ) => (str :: ) // format-out("Entering args-to-string with %d args\n", args.size); select (args.size) 1 => arg-plus-override-plus-table-plus-address-to-string(args[0], seg, table, end-index, address, table-lookup); 2 => arg-pair-to-string(arg-plus-override-to-string(args[0], seg, table-lookup), arg-plus-override-to-string(args[1], seg, table-lookup)); 3 => concatenate(arg-pair-to-string(arg-plus-override-to-string(args[0], seg, table-lookup), arg-plus-override-to-string(args[1], seg, table-lookup)), ", ", arg-plus-override-to-string(args[2], seg, table-lookup)); otherwise error("decoded opcode has no or incorrect arguments (%d)\n", args.size); end select; end method args-to-string; // format-out("opcode-to-string getting name\n"); let name = opcode.proper-opcode-name; // format-out("opcode-to-string getting args\n"); let args = opcode.proper-opcode-args; // format-out("opcode-to-string getting seg\n"); let seg = opcode.proper-opcode-seg; // format-out("opcode-to-string calling args-to-string if necessary\n"); if (args.size == 0) name else concatenate(name, " ", args-to-string(args, seg)) end; end method opcode-to-string; define method opcode-to-string(table ::
, opcode :: , end-index :: , address :: , table-lookup :: ) => (str :: ) "not an opcode (source unknown)"; end method opcode-to-string; define method opcode-to-string(table ::
, opcode :: , end-index :: , address :: , table-lookup :: ) => (str :: ) concatenate("The following byte sequence is not an opcode '", byte-sequence-to-string(opcode.bytes-read, 0, opcode.bytes-read.size), "'") end method opcode-to-string; define method pad(int :: ) => (str :: ) select (int) 1 => " "; 2 => " "; 3 => " "; 4 => " "; 5 => " "; otherwise " "; end select; end method pad; define method opcode-and-index-to-string(table ::
, opc :: , address :: , table-lookup :: ) => (str :: ) let index = opc.general-opcode-offset; let end-index = opc.general-opcode-end-offset; let res = element(table, index, default: #f); let prefix = if (res == #f) " " else let len = res.size; if (len < 7) concatenate(res, ":", pad(7 - len)) else concatenate(res, ":") end; end; concatenate(prefix, " ", opcode-to-string(table, opc.general-opcode-opcode, end-index, address, table-lookup)) end method opcode-and-index-to-string; define method add-label-at-offset(table ::
, index :: , label-number :: ) => (table ::
, label-number :: ) let label = format-to-string("L%d", label-number); table[index] := label; values(table, label-number + 1) end method add-label-at-offset; define method add-labels(list :: , opcode-list :: , max-index :: ) => (list :: ) if (empty?(opcode-list)) list else let opcode = head(opcode-list); let general-opcode = opcode.general-opcode-opcode; let list = select (general-opcode by instance?) => begin let args = general-opcode.proper-opcode-args; if (args.size == 1) let arg = args[0]; select (arg by instance?) => begin let (offset, external) = get-offset-as-integer(arg.offset-arg); let end-opcode = opcode.general-opcode-end-offset; let index = end-opcode + offset; select (external by instance?) => if (index >= 0 & index < max-index) pair(index, list) else list end; otherwise list end select end; otherwise list end select else list end end; otherwise list end; add-labels(list, tail(opcode-list), max-index) end end method add-labels; define method compute-max-index(index :: , opcode-list :: ) => (index :: ) if (empty?(opcode-list)) index else let opcode = head(opcode-list); compute-max-index(opcode.general-opcode-end-offset, tail(opcode-list)) end end method compute-max-index; define method make-label-table(table ::
, label-number :: , indexes :: ) => (table ::
) if (empty?(indexes)) table else let (table, new-label-number) = add-label-at-offset(table, head(indexes), label-number); make-label-table(table, new-label-number, tail(indexes)) end end method make-label-table; define method opcs-to-string(label-table ::
, opcode-list :: , address :: , table-lookup :: , results :: ) => (res :: ) if (empty?(opcode-list)) reverse(results) else let opcode = head(opcode-list); let opc-string = opcode-and-index-to-string(label-table, opcode, address, table-lookup); let index = opcode.general-opcode-offset; opcs-to-string(label-table, tail(opcode-list), address, table-lookup, pair(opc-string, results)) end end method opcs-to-string; define method opcodes-to-string(opcode-list :: , address :: , table-lookup :: ) => (string-list :: ) let table = make(
, size: 1); // First generate the necessary labels let max-index = compute-max-index(0, opcode-list); let index-list = sort(remove-duplicates(add-labels(#(), opcode-list, max-index))); let label-table = make-label-table(table, 0, index-list); opcs-to-string(table, opcode-list, address, table-lookup, #()) end method opcodes-to-string;