/**************************************************************************** * dcc project disassembler * (C) Cristina Cifuentes, Mike van Emmerik, Jeff Ledermann ****************************************************************************/ #include "disassem.h" #include "dcc.h" #include "msvc_fixes.h" #include "symtab.h" #include "project.h" #include #include #include #include #include #include #include #include #include #include "src/ui/StructuredTextTarget.h" // Note: for the time being, there is no interactive disassembler // for unix using namespace std; #define POS_LAB 15 /* Position of label */ #define POS_OPC 20 /* Position of opcode */ #define POS_OPR 25 /* Position of operand */ #define WID_PTR 10 /* Width of the "xword ptr" lingo */ #define POS_OPR2 POS_OPR+WID_PTR /* Position of operand after "xword ptr" */ #define POS_CMT 54 /* Position of comment */ static const char *szFlops0C[] = { "FCHS", "FABS", "???", "???", "FTST", "FXAM", "???", "???" }; static const char *szFlops0D[] = { "FLD1", "FLDL2T","FLDL2E","FLDP1", "FLDLG2","FLDLN2","FLDZ", "???" }; static const char *szFlops0E[] = { "F2XM1", "FYL2X", "FPTAN", "FPATAN","FXTRACT","FPREM1","FDECSTP","FINCSTP" }; static const char *szFlops0F[] = { "FPREM", "FYLXP1","FSQRT", "FSINCOS","FRNDINT","FSCALE","FSIN","FCOS" }; static const char *szFlops15[] = { "???", "FUCOMPP", "???", "???", "???", "???", "???", "???" }; static const char *szFlops1C[] = { "???", "???", "FCLEX", "FINIT", "FTST", "FXAM", "???", "???" }; static const char *szFlops33[] = { "???", "FCOMPP", "???", "???", "???", "???", "???", "???" }; static const char *szFlops3C[] = { "FSTSWAX","???", "???", "???", "???", "???", "???", "???" }; static const char *szPtr[2] = { "word ptr ", "byte ptr " }; static void formatRM(QTextStream & p, const LLOperand &pm); static QTextStream & strDst(QTextStream & os, uint32_t flg, const LLOperand &pm); static char *strHex(uint32_t d); //static int checkScanned(uint32_t pcCur); //static void setProc(Function * proc); //static void dispData(uint16_t dataSeg); bool callArg(uint16_t off, char *temp); /* Check for procedure name */ //static FILE *dis_g_fp; static CIcodeRec pc; static int cb, j, numIcode, allocIcode; static map pl; static uint32_t nextInst; static bool fImpure; //static int g_lab; static PtrFunction pProc; /* Points to current proc struct */ struct POSSTACK_ENTRY { int ic; /* An icode offset */ Function * pProc; /* A pointer to a PROCEDURE structure */ } ; static vector posStack; /* position stack */ //static uint8_t iPS; /* Index into the stack */ // These are "curses equivalent" functions. (Used to use curses for all this, // but it was too much of a distribution hassle #define printfd(x) printf(x) #define dis_newline() printf("\n") #define dis_show() // Nothing to do unless using Curses void LLInst::findJumpTargets(CIcodeRec &_pc) { if (testFlags(I) and not testFlags(JMP_ICODE) and isJmpInst()) { /* Replace the immediate operand with an icode index */ iICODE labTgt=_pc.labelSrch(src().getImm2()); if (labTgt!=_pc.end()) { m_src.SetImmediateOp(labTgt->loc_ip); /* This icode is the target of a jump */ labTgt->ll()->setFlags(TARGET); setFlags(JMP_ICODE); /* So its not done twice */ } else { /* This jump cannot be linked to a label */ setFlags(NO_LABEL); } } } /***************************************************************************** * disassem - Prints a disassembled listing of a procedure. * pass == 1 generates output on file .a1 * pass == 2 generates output on file .a2 * pass == 3 generates output on file .b ****************************************************************************/ void Disassembler::disassem(PtrFunction ppProc) { pProc = ppProc; /* Save the passes pProc */ createSymTables(); allocIcode = numIcode = pProc->Icode.size(); cb = allocIcode * sizeof(ICODE); if (numIcode == 0) { return; /* No Icode */ } /* Open the output file (.a1 or .a2 only) */ if (pass != 3) { auto p = (pass == 1)? asm1_name: asm2_name; m_disassembly_target = new QFile(p); if(!m_disassembly_target->open(QFile::WriteOnly|QFile::Text|QFile::Append)) { fatalError(CANNOT_OPEN, p.toStdString().c_str()); } m_fp.setDevice(m_disassembly_target); } /* Create temporary code array */ // Mike: needs objectising! pc=pProc->Icode; if (pass == 1) { /* Bind jump offsets to labels */ //for (i = 0; i < numIcode; i++) for( ICODE &icode : pc) { LLInst *ll=icode.ll(); ll->findJumpTargets(pc); } } /* Create label array to keep track of location => label name */ pl.clear(); /* Write procedure header */ if (pass != 3) { const char * near_far=(pProc->flg & PROC_FAR)? "FAR": "NEAR"; m_fp << "\t\t"<name<<" PROC "<< near_far<<"\n"; } /* Loop over array printing each record */ nextInst = 0; for( ICODE &icode : pc) { this->dis1Line(*icode.ll(),icode.loc_ip,pass); } /* Write procedure epilogue */ if (pass != 3) { m_fp << "\n\t\t"<name<<" ENDP\n\n"; m_fp.setDevice(nullptr); m_disassembly_target->close(); delete m_disassembly_target; } pc.clear(); destroySymTables(); } /**************************************************************************** * dis1Line() - disassemble one line to stream fp * * i is index into Icode for this proc * * It is assumed that icode i is already scanned * ****************************************************************************/ void Disassembler::dis1Line(LLInst &inst,int loc_ip, int pass) { PROG &prog(Project::get()->prog); QString oper_contents; QTextStream oper_stream(&oper_contents); QString hex_bytes; QString result_contents; QTextStream result_stream(&result_contents); QString opcode_with_mods; QString operands_contents; QTextStream operands_s(&operands_contents); oper_stream.setNumberFlags(QTextStream::UppercaseBase|QTextStream::UppercaseDigits); /* Disassembly stage 1 -- * Do not try to display NO_CODE entries or synthetic instructions, * other than JMPs, that have been introduced for def/use analysis. */ if ((option.asm1) and ( inst.testFlags(NO_CODE) or (inst.testFlags(SYNTHETIC) and (inst.getOpcode() != iJMP)))) { return; } else if (inst.testFlags(NO_CODE)) { return; } if (inst.testFlags(TARGET | CASE)) { if (pass == 3) cCode.appendCode("\n"); /* Print to c code buffer */ else m_fp<< "\n"; /* No, print to the stream */ } /* Find next instruction label and print hex bytes */ if (inst.testFlags(SYNTHETIC)) nextInst = inst.label; else { cb = (uint32_t) inst.numBytes; nextInst = inst.label + cb; /* Output hex code in program image */ if (pass != 3) { for (j = 0; j < cb; j++) { hex_bytes += QString("%1").arg(uint16_t(prog.image()[inst.label + j]),2,16,QChar('0')).toUpper(); } hex_bytes += ' '; } } oper_stream.setFieldWidth(POS_LAB); oper_stream.setFieldAlignment(QTextStream::AlignLeft); oper_stream << hex_bytes; /* Check if there is a symbol here */ selectTable(Label); oper_stream.setFieldWidth(5); // align for the labels { QString lab_contents; QTextStream lab_stream(&lab_contents); if (readVal(lab_stream, inst.label, nullptr)) { lab_stream << ':'; /* Also removes the null */ } else if (inst.testFlags(TARGET)) /* Symbols override Lnn labels */ { /* Print label */ if (pl.count(loc_ip)==0) { pl[loc_ip] = ++g_lab; } lab_stream<< "L"<ll()->label, nullptr)) { break; /* Symbolic label. Done */ } } if (inst.testFlags(NO_LABEL)) { //strcpy(p + WID_PTR, strHex(pIcode->ll()->immed.op)); operands_s<name); operands_s<< qPrintable(oper); } else if (inst.getOpcode() == iCALLF) { operands_s<<"dword ptr "; inst.strSrc(operands_s,true); } else strDst(operands_s,I, inst.src()); break; case iENTER: operands_s< 0 and j < (int)nextInst; j++) { fImpure |= BITMAP(j, BM_DATA); } } result_stream.setFieldWidth(54); result_stream.setFieldAlignment(QTextStream::AlignLeft); oper_stream.flush(); result_stream << oper_contents; result_stream.setFieldWidth(0); /* Check for user supplied comment */ selectTable(Comment); QString cbuf_contents; QTextStream cbuf(&cbuf_contents); if (readVal(cbuf, inst.label, nullptr)) { cbuf.flush(); result_stream <<"; "<<*cbuf.string(); } else if (fImpure or (inst.testFlags(SWITCH | CASE | SEG_IMMED | IMPURE | SYNTHETIC | TERMINATES))) { if (inst.testFlags(CASE)) { result_stream << ";Case l"<< inst.caseEntry; } if (inst.testFlags(SWITCH)) { result_stream << ";Switch "; } if (fImpure) { result_stream << ";Accessed as data "; } if (inst.testFlags(IMPURE)) { result_stream << ";Impure operand "; } if (inst.testFlags(SEG_IMMED)) { result_stream << ";Segment constant"; } if (inst.testFlags(TERMINATES)) { result_stream << ";Exit to DOS"; } } /* Comment on iINT icodes */ if (inst.getOpcode() == iINT) inst.writeIntComment(result_stream); /* Display output line */ if(pass==3) { /* output to .b code buffer */ if (inst.testFlags(SYNTHETIC)) result_stream<<";Synthetic inst"; if (pass == 3) { /* output to .b code buffer */ cCode.appendCode("%s\n", qPrintable(result_contents)); } } else { char buf[12]; /* output to .a1 or .a2 file */ if (not inst.testFlags(SYNTHETIC) ) { sprintf(buf,"%03d %06X",loc_ip, inst.label); } else /* SYNTHETIC instruction */ { sprintf(buf,"%03d ",loc_ip); result_stream<<";Synthetic inst"; } result_stream.flush(); m_fp< 9)? "h": ""); return (buf + (buf[1] <= '9')); } /**************************************************************************** * interactDis - interactive disassembler * ****************************************************************************/ void interactDis(const PtrFunction & initProc, int initIC) { QString procname = "UNKNOWN"; if(initProc) procname = initProc->name; qDebug() << "Wanted to start interactive disasassembler for "<= 0x20) and (op <= 0x27)) { /* This is the ST(i), ST form. */ out << "ST("<prtt(Machine_X86::regName(pm.segOver)+':'); } if (pm.regi == rUNDEF) { out->prtt(QString("[")+strHex((uint32_t)pm.off)+"]"); } else if (pm.isReg()) { out->prtt(Machine_X86::regName(pm.regi)); } else if (pm.off) { if (pm.off < 0) { out->prtt("["+Machine_X86::regName(pm.regi)+"-"+strHex((uint32_t)(- pm.off))+"]"); } else { out->prtt("["+Machine_X86::regName(pm.regi)+"+"+strHex((uint32_t)(pm.off))+"]"); } } else out->prtt("["+Machine_X86::regName(pm.regi)+"]"); } static void strDst(IStructuredTextTarget *out,uint32_t flg, const LLOperand &pm) { /* Immediates to memory require size descriptor */ //os << setw(WID_PTR); if ((flg & I) and not pm.isReg()) { out->addTaggedString(XT_Keyword,szPtr[flg&B]); } formatRM(out,pm); } static void strSrc(IStructuredTextTarget *out,LLInst *insn,bool skip_comma=false) { if(false==skip_comma) out->prtt(", "); if (insn->testFlags(I)) out->addTaggedString(XT_Number,strHex(insn->src().getImm2())); else if (insn->testFlags(IM_SRC)) /* level 2 */ out->addTaggedString(XT_Symbol,"dx:ax"); else formatRM(out,insn->src()); } void toStructuredText(LLInst *insn,IStructuredTextTarget *out, int level) { const LLInst &inst(*insn); QString opcode = Machine_X86::opcodeName(insn->getOpcode()); out->addSpace(4); out->addTaggedString(XT_Number,strHex(insn->label)); out->addSpace(4); out->addTaggedString(XT_Keyword,Machine_X86::opcodeName(insn->getOpcode()),insn); out->addSpace(2); switch(insn->getOpcode()) { case iADD: case iADC: case iSUB: case iSBB: case iAND: case iOR: case iXOR: case iTEST: case iCMP: case iMOV: case iLEA: case iXCHG: strDst(out,insn->getFlag(), insn->m_dst); strSrc(out,insn); break; case iSAR: case iSHL: case iSHR: case iRCL: case iRCR: case iROL: case iROR: strDst(out,insn->getFlag() | I, insn->m_dst); if(insn->testFlags(I)) strSrc(out,insn); else { out->prtt(", "); out->addTaggedString(XT_Symbol,"cl"); } break; case iINC: case iDEC: case iNEG: case iNOT: case iPOP: strDst(out,insn->getFlag() | I, insn->m_dst); break; case iPUSH: if (inst.testFlags(I)) { out->addTaggedString(XT_Number,strHex(inst.src().getImm2())); } else { strDst(out,insn->getFlag() | I, insn->m_dst); } break; case iDIV: case iIDIV: case iMUL: case iIMUL: case iMOD: if (inst.testFlags(I)) { strDst(out,insn->getFlag(), insn->m_dst); out->prtt(", "); formatRM(out, inst.src()); strSrc(out,insn); } else strDst(out,insn->getFlag() | I, insn->m_dst); break; case iLDS: case iLES: case iBOUND: strDst(out,inst.getFlag(), inst.m_dst); out->prtt(", "); out->addTaggedString(XT_Keyword,"dword ptr"); strSrc(out,insn,true); break; case iJB: case iJBE: case iJAE: case iJA: case iJL: case iJLE: case iJGE: case iJG: case iJE: case iJNE: case iJS: case iJNS: case iJO: case iJNO: case iJP: case iJNP: case iJCXZ:case iLOOP: case iLOOPE:case iLOOPNE: case iJMP: case iJMPF: /* Check if there is a symbol here */ { // ICODE *lab=pc.GetIcode(inst.src().getImm2()); // selectTable(Label); // if ((inst.src().getImm2() < (uint32_t)numIcode) and /* Ensure in range */ // readVal(operands_s, lab->ll()->label, nullptr)) // { // break; /* Symbolic label. Done */ // } } break; } out->addEOL(); }