#include "DccFrontend.h" #include "dcc.h" #include "msvc_fixes.h" #include "project.h" #include "disassem.h" #include "CallGraph.h" #include #include #include class Loader { bool loadIntoProject(IProject *); }; struct PSP { /* PSP structure */ uint16_t int20h; /* interrupt 20h */ uint16_t eof; /* segment, end of allocation block */ uint8_t res1; /* reserved */ uint8_t dosDisp[5]; /* far call to DOS function dispatcher */ uint8_t int22h[4]; /* vector for terminate routine */ uint8_t int23h[4]; /* vector for ctrl+break routine */ uint8_t int24h[4]; /* vector for error routine */ uint8_t res2[22]; /* reserved */ uint16_t segEnv; /* segment address of environment block */ uint8_t res3[34]; /* reserved */ uint8_t int21h[6]; /* opcode for int21h and far return */ uint8_t res4[6]; /* reserved */ uint8_t fcb1[16]; /* default file control block 1 */ uint8_t fcb2[16]; /* default file control block 2 */ uint8_t res5[4]; /* reserved */ uint8_t cmdTail[0x80]; /* command tail and disk transfer area */ }; static struct MZHeader { /* EXE file header */ uint8_t sigLo; /* .EXE signature: 0x4D 0x5A */ uint8_t sigHi; uint16_t lastPageSize; /* Size of the last page */ uint16_t numPages; /* Number of pages in the file */ uint16_t numReloc; /* Number of relocation items */ uint16_t numParaHeader; /* # of paragraphs in the header */ uint16_t minAlloc; /* Minimum number of paragraphs */ uint16_t maxAlloc; /* Maximum number of paragraphs */ uint16_t initSS; /* Segment displacement of stack */ uint16_t initSP; /* Contents of SP at entry */ uint16_t checkSum; /* Complemented checksum */ uint16_t initIP; /* Contents of IP at entry */ uint16_t initCS; /* Segment displacement of code */ uint16_t relocTabOffset; /* Relocation table offset */ uint16_t overlayNum; /* Overlay number */ } header; #define EXE_RELOCATION 0x10 /* EXE images rellocated to above PSP */ //static void LoadImage(char *filename); static void displayMemMap(void); /**************************************************************************** * displayLoadInfo - Displays low level loader type info. ***************************************************************************/ void PROG::displayLoadInfo(void) { int i; printf("File type is %s\n", (fCOM)?"COM":"EXE"); if (not fCOM) { printf("Signature = %02X%02X\n", header.sigLo, header.sigHi); printf("File size %% 512 = %04X\n", LH(&header.lastPageSize)); printf("File size / 512 = %04X pages\n", LH(&header.numPages)); printf("# relocation items = %04X\n", LH(&header.numReloc)); printf("Offset to load image = %04X paras\n", LH(&header.numParaHeader)); printf("Minimum allocation = %04X paras\n", LH(&header.minAlloc)); printf("Maximum allocation = %04X paras\n", LH(&header.maxAlloc)); } printf("Load image size = %08lX\n", cbImage - sizeof(PSP)); printf("Initial SS:SP = %04X:%04X\n", initSS, initSP); printf("Initial CS:IP = %04X:%04X\n", initCS, initIP); if (option.VeryVerbose and cReloc) { printf("\nRelocation Table\n"); for (i = 0; i < cReloc; i++) { printf("%06X -> [%04X]\n", relocTable[i],LH(image() + relocTable[i])); } } printf("\n"); } /***************************************************************************** * fill - Fills line for displayMemMap() ****************************************************************************/ static void fill(int ip, char *bf) { PROG &prog(Project::get()->prog); static uint8_t type[4] = {'.', 'd', 'c', 'x'}; uint8_t i; for (i = 0; i < 16; i++, ip++) { *bf++ = ' '; *bf++ = (ip < prog.cbImage)? type[(prog.map[ip >> 2] >> ((ip & 3) * 2)) & 3]: ' '; } *bf = '\0'; } /***************************************************************************** * displayMemMap - Displays the memory bitmap ****************************************************************************/ static void displayMemMap(void) { PROG &prog(Project::get()->prog); char c, b1[33], b2[33], b3[33]; uint8_t i; int ip = 0; printf("\nMemory Map\n"); while (ip < prog.cbImage) { fill(ip, b1); printf("%06X %s\n", ip, b1); ip += 16; for (i = 3, c = b1[1]; i < 32 and c == b1[i]; i += 2) ; /* Check if all same */ if (i > 32) { fill(ip, b2); /* Skip until next two are not same */ fill(ip+16, b3); if (not (strcmp(b1, b2) || strcmp(b1, b3))) { printf(" :\n"); do { ip += 16; fill(ip+16, b1); } while (0==strcmp(b1, b2)); } } } printf("\n"); } DccFrontend::DccFrontend(QObject *parent) : QObject(parent) { } /***************************************************************************** * FrontEnd - invokes the loader, parser, disassembler (if asm1), icode * rewritter, and displays any useful information. ****************************************************************************/ bool DccFrontend::FrontEnd () { /* Do depth first flow analysis building call graph and procedure list, * and attaching the I-code to each procedure */ parse (*Project::get()); if (option.asm1) { qWarning() << "dcc: writing assembler file "<pProcList) { f.markImpure(); if (option.asm1) { ds.disassem(&f); } } if (option.Interact) { interactDis(&Project::get()->pProcList.front(), 0); /* Interactive disassembler */ } /* Converts jump target addresses to icode offsets */ for(Function &f : Project::get()->pProcList) { f.bindIcodeOff(); } /* Print memory bitmap */ if (option.Map) displayMemMap(); return(true); // we no longer own proj ! } struct DosLoader { protected: void prepareImage(PROG &prog,size_t sz,QFile &fp) { /* Allocate a block of memory for the program. */ prog.cbImage = sz + sizeof(PSP); prog.Imagez = new uint8_t [prog.cbImage]; prog.Imagez[0] = 0xCD; /* Fill in PSP int 20h location */ prog.Imagez[1] = 0x20; /* for termination checking */ /* Read in the image past where a PSP would go */ if (sz != fp.read((char *)prog.Imagez + sizeof(PSP),sz)) fatalError(CANNOT_READ, fp.fileName().toLocal8Bit().data()); } }; struct ComLoader : public DosLoader { bool canLoad(QFile &fp) { fp.seek(0); char sig[2]; if(2==fp.read(sig,2)) { return not (sig[0] == 0x4D and sig[1] == 0x5A); } return false; } bool load(PROG &prog,QFile &fp) { fp.seek(0); /* COM file * In this case the load module size is just the file length */ auto cb = fp.size(); /* COM programs start off with an ORG 100H (to leave room for a PSP) * This is also the implied start address so if we load the image * at offset 100H addresses should all line up properly again. */ prog.initCS = 0; prog.initIP = 0x100; prog.initSS = 0; prog.initSP = 0xFFFE; prog.cReloc = 0; prepareImage(prog, cb, fp); /* Set up memory map */ cb = (prog.cbImage + 3) / 4; prog.map = (uint8_t *)malloc(cb); memset(prog.map, BM_UNKNOWN, (size_t)cb); return true; } }; #if 0 struct RomLoader { bool canLoad(QFile &fp) { fp.seek(0xFFF0); uint8_t sig[1]; if(fp.read((char *)sig,1) == 1) { return (sig[0] == 0xEA); } return false; } bool load(PROG &prog,QFile &fp) { printf("Loading ROM...\n"); fp.seek(0); /* ROM file * In this case the load module size is just the file length */ auto cb = fp.size(); fp.seek(cb - 0x10); uint8_t buf[5]; printf("Going to get CS/IP...\n"); if(fp.read((char *)buf, 5) != 5) { return false; } fp.seek(0); /* ROM File, Hard to say where it is suppose to start, so try to trust the */ prog.initIP = (buf[2] << 8) | buf[1]; //prog.initCS = 0; prog.initCS = (buf[4] << 8) | buf[3]; prog.initSS = 0; prog.initSP = 0xFFFE; prog.cReloc = 0; prepareImage(prog, cb, fp); /* Set up memory map */ cb = (prog.cbImage + 3) / 4; prog.map = (uint8_t *)malloc(cb); memset(prog.map, BM_UNKNOWN, (size_t)cb); return true; } protected: void prepareImage(PROG &prog, size_t sz, QFile &fp) { int32_t start = 0x100000 - sz; /* Allocate a block of memory for the program. */ prog.cbImage = 1 * 1024 * 1024; /* Allocate the whole 1MB memory */ //prog.cbImage = 64 * 1024; /* Allocate the whole 1MB memory */ prog.Imagez = new uint8_t [prog.cbImage]; if (fp.read((char *)prog.Imagez + start, sz) != sz) //if (fp.read((char *)prog.Imagez, sz) != sz) { fatalError(CANNOT_READ, fp.fileName().toLocal8Bit().data()); } } }; #else struct RomLoader { bool canLoad(QFile &fp) { fp.seek(0xFFF0); uint8_t sig[1]; if(fp.read((char *)sig,1) == 1) { return (sig[0] == 0xEA); } return false; } bool load(PROG &prog,QFile &fp) { fp.seek(0); /* COM file * In this case the load module size is just the file length */ auto cb = fp.size(); /* COM programs start off with an ORG 100H (to leave room for a PSP) * This is also the implied start address so if we load the image * at offset 100H addresses should all line up properly again. */ prog.initCS = 0; prog.initIP = 0x000; prog.initSS = 0; prog.initSP = 0xFFFE; prog.cReloc = 0; prepareImage(prog, cb, fp); /* Set up memory map */ cb = (prog.cbImage + 3) / 4; prog.map = (uint8_t *)malloc(cb); memset(prog.map, BM_UNKNOWN, (size_t)cb); return true; } protected: void prepareImage(PROG &prog, size_t sz, QFile &fp) { /* Allocate a block of memory for the program. */ prog.cbImage = sz; prog.Imagez = new uint8_t[prog.cbImage]; if (sz != fp.read((char *)prog.Imagez, sz)) fatalError(CANNOT_READ, fp.fileName().toLocal8Bit().data()); } }; #endif struct ExeLoader : public DosLoader { bool canLoad(QFile &fp) { if(fp.size()> 8); } return true; } }; /***************************************************************************** * LoadImage ****************************************************************************/ bool Project::load() { // addTask(loaderSelection,PreCond(BinaryImage)) // addTask(applyLoader,PreCond(Loader)) const char *fname = binary_path().toLocal8Bit().data(); QFile finfo(binary_path()); /* Open the input file */ if(not finfo.open(QFile::ReadOnly)) { fatalError(CANNOT_OPEN, fname); } /* Read in first 2 bytes to check EXE signature */ if (finfo.size()<=2) { fatalError(CANNOT_READ, fname); } RomLoader rom_loader; ComLoader com_loader; ExeLoader exe_loader; if(rom_loader.canLoad(finfo)) { /* We have no relacation and code should be on 64K only, * So let's consider it as a COM file */ prog.fCOM = true; return rom_loader.load(prog,finfo); } if(exe_loader.canLoad(finfo)) { prog.fCOM = false; return exe_loader.load(prog,finfo); } if(com_loader.canLoad(finfo)) { prog.fCOM = true; return com_loader.load(prog,finfo); } return false; } uint32_t SynthLab; /* Parses the program, builds the call graph, and returns the list of * procedures found */ void DccFrontend::parse(Project &proj) { PROG &prog(proj.prog); STATE state; /* Set initial state */ state.setState(rES, 0); /* PSP segment */ state.setState(rDS, 0); state.setState(rCS, prog.initCS); state.setState(rSS, prog.initSS); state.setState(rSP, prog.initSP); state.IP = ((uint32_t)prog.initCS << 4) + prog.initIP; SynthLab = SYNTHESIZED_MIN; /* Check for special settings of initial state, based on idioms of the startup code */ state.checkStartup(); ilFunction start_proc; /* Make a struct for the initial procedure */ if (prog.offMain != -1) { start_proc = proj.createFunction(0,"main"); start_proc->retVal.loc = REG_FRAME; start_proc->retVal.type = TYPE_WORD_SIGN; start_proc->retVal.id.regi = rAX; /* We know where main() is. Start the flow of control from there */ start_proc->procEntry = prog.offMain; /* In medium and large models, the segment of main may (will?) not be the same as the initial CS segment (of the startup code) */ state.setState(rCS, prog.segMain); state.IP = prog.offMain; } else { start_proc = proj.createFunction(0,"start"); /* Create initial procedure at program start address */ start_proc->procEntry = (uint32_t)state.IP; } /* The state info is for the first procedure */ start_proc->state = state; /* Set up call graph initial node */ proj.callGraph = new CALL_GRAPH; proj.callGraph->proc = start_proc; /* This proc needs to be called to set things up for LibCheck(), which checks a proc to see if it is a know C (etc) library */ prog.bSigs = SetupLibCheck(); //BUG: proj and g_proj are 'live' at this point ! /* Recursively build entire procedure list */ start_proc->FollowCtrl(proj.callGraph, &state); /* This proc needs to be called to clean things up from SetupLibCheck() */ CleanupLibCheck(); }