Implementation

This commit is contained in:
nemerle
2016-04-26 16:18:23 +02:00
parent b2be1cf2da
commit 62d8633113
13 changed files with 555 additions and 114 deletions

97
src/Command.cpp Normal file
View File

@@ -0,0 +1,97 @@
#include "Command.h"
#include "project.h"
#include "Loaders.h"
#include <QFile>
bool LoaderSelection::execute(CommandContext * ctx, Project *p)
{
if(p->binary_path().isEmpty()) {
ctx->recordFailure(this,QString("No executable path set in project %1").arg(p->project_name()));
return false;
}
QFile finfo(p->binary_path());
/* Open the input file */
if(not finfo.open(QFile::ReadOnly)) {
ctx->recordFailure(this,QString("Cannot open file %1").arg(p->binary_path()));
return false;
}
/* Read in first 2 bytes to check EXE signature */
if (finfo.size()<=2)
{
ctx->recordFailure(this,QString("File %1 is too small").arg(p->binary_path()));
}
ComLoader com_loader;
ExeLoader exe_loader;
if(exe_loader.canLoad(finfo)) {
p->m_selected_loader = new ExeLoader;
return true;
}
if(com_loader.canLoad(finfo)) {
p->m_selected_loader = new ComLoader;
return true;
}
ctx->recordFailure(this,QString("None of the available loaders can load file %1").arg(p->binary_path()));
return true;
}
bool LoaderApplication::execute(CommandContext * ctx, Project *p)
{
if(!p)
return false;
if(!p->m_selected_loader) {
ctx->recordFailure(this,QString("No loader selected for project %1").arg(p->project_name()));
return false;
}
QFile finfo(p->binary_path());
if(not finfo.open(QFile::ReadOnly)) {
ctx->recordFailure(this,QString("Cannot open file %1").arg(p->binary_path()));
return false;
}
return p->m_selected_loader->load(p->prog,finfo);
}
bool CommandStream::add(Command * c) {
if(m_commands.size()>=m_maximum_command_count)
return false;
m_commands.push_back(c);
return true;
}
void CommandStream::setMaximumCommandCount(int maximum_command_count) {
m_maximum_command_count = maximum_command_count;
}
void CommandStream::processAll(CommandContext *ctx)
{
while(not m_commands.isEmpty()) {
Command *cmd = m_commands.takeFirst();
if(false==cmd->execute(ctx,ctx->proj)) {
emit streamCompleted(false);
break;
}
m_recently_executed.push_back(cmd);
}
emit streamCompleted(true);
}
void CommandStream::clear()
{
qDeleteAll(m_commands);
qDeleteAll(m_recently_executed);
m_commands.clear();
m_recently_executed.clear();
}
void CommandContext::reset()
{
for(int i=0; i<m_failures.size(); ++i) {
delete m_failures[i].first;
}
m_failures.clear();
}

81
src/Command.h Normal file
View File

@@ -0,0 +1,81 @@
#ifndef COMMAND_H
#define COMMAND_H
#include <QtCore/QObject>
#include <QtCore/QVector>
#include <QtCore/QPair>
class Project;
enum CommandLevel {
eProject,
eBinary,
eFunction,
eBasicBlock,
eInstruction
};
class Command;
class CommandContext {
public:
void recordFailure(Command *cmd,QString error_message) {
m_failures.push_back({cmd,error_message});
}
Project *proj;
QVector<QPair<Command *,QString>> m_failures;
void reset();
};
class Command
{
QString m_command_name;
CommandLevel m_level;
public:
Command(QString n,CommandLevel level) : m_command_name(n),m_level(level) {}
QString name() const { return m_command_name;}
virtual bool execute(CommandContext *,Project *) { return false; }
};
class CompoundCommand : public Command {
QVector<Command *> m_contained;
public:
CompoundCommand(QString n,CommandLevel l) : Command(n,l) {
}
void addCommand(Command *c) {
m_contained.push_back(c);
}
bool execute(CommandContext * ctx,Project *v) {
for(Command * c : m_contained) {
if(!c->execute(ctx,v))
return false;
}
return true;
}
};
class CommandStream : public QObject
{
Q_OBJECT
int m_maximum_command_count;
public:
QVector<Command *> m_recently_executed;
QVector<Command *> m_commands;
bool add(Command *c);
void setMaximumCommandCount(int maximum_command_count);
void processAll(CommandContext *ctx);
void clear();
signals:
void streamCompleted(bool success);
};
class LoaderSelection : public Command {
public:
virtual ~LoaderSelection() {}
LoaderSelection() : Command("Select loader",eProject) {}
bool execute(CommandContext * ctx,Project *) override;
};
class LoaderApplication : public Command {
public:
virtual ~LoaderApplication() {}
LoaderApplication() : Command("Apply loader",eProject) {}
bool execute(CommandContext * ctx,Project *) override;
};
#endif // COMMAND_H

View File

@@ -11,11 +11,6 @@
#include <cstdio>
class Loader
{
bool loadIntoProject(IProject *);
};
struct PSP { /* PSP structure */
uint16_t int20h; /* interrupt 20h */
uint16_t eof; /* segment, end of allocation block */
@@ -325,53 +320,21 @@ struct ExeLoader : public DosLoader {
/*****************************************************************************
* LoadImage
****************************************************************************/
bool Project::load()
void DccFrontend::initializeMachineState(Project &proj)
{
// 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);
}
ComLoader com_loader;
ExeLoader exe_loader;
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;
const PROG &prog(proj.prog);
proj.m_entry_state.setState(rES, 0); /* PSP segment */
proj.m_entry_state.setState(rDS, 0);
proj.m_entry_state.setState(rCS, prog.initCS);
proj.m_entry_state.setState(rSS, prog.initSS);
proj.m_entry_state.setState(rSP, prog.initSP);
proj.m_entry_state.IP = ((uint32_t)prog.initCS << 4) + prog.initIP;
proj.SynthLab = SYNTHESIZED_MIN;
}
uint32_t SynthLab;
/* Parses the program, builds the call graph, and returns the list of
* procedures found */
void DccFrontend::parse(Project &proj)
void DccFrontend::createEntryProc(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)
@@ -384,30 +347,43 @@ void DccFrontend::parse(Project &proj)
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;
proj.m_entry_state.setState(rCS, prog.segMain);
proj.m_entry_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;
start_proc->procEntry = (uint32_t)proj.m_entry_state.IP;
}
/* The state info is for the first procedure */
start_proc->state = state;
start_proc->state = proj.m_entry_state;
/* Set up call graph initial node */
proj.callGraph = new CALL_GRAPH;
proj.callGraph->proc = start_proc;
}
/* Parses the program, builds the call graph, and returns the list of
* procedures found */
void DccFrontend::parse(Project &proj)
{
/* Set initial state */
initializeMachineState(proj);
/* Check for special settings of initial state, based on idioms of the
startup code */
proj.m_entry_state.checkStartup();
createEntryProc(proj);
/* 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();
proj.prog.bSigs = SetupLibCheck();
//BUG: proj and g_proj are 'live' at this point !
/* Recursively build entire procedure list */
start_proc->FollowCtrl(proj.callGraph, &state);
proj.callGraph->proc->FollowCtrl(proj.callGraph, &proj.m_entry_state);
/* This proc needs to be called to clean things up from SetupLibCheck() */
CleanupLibCheck();

177
src/Loaders.cpp Normal file
View File

@@ -0,0 +1,177 @@
#include "Loaders.h"
#include "dcc.h"
#include <QtCore/QDebug>
#define EXE_RELOCATION 0x10 /* EXE images rellocated to above PSP */
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;
void DosLoader::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());
}
bool ComLoader::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 ComLoader::load(PROG & prog, QFile & fp) {
prog.fCOM = true;
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;
}
bool ExeLoader::canLoad(QFile & fp) {
if(fp.size()<sizeof(header))
return false;
MZHeader tmp_header;
fp.seek(0);
fp.read((char *)&tmp_header, sizeof(header));
if(not (tmp_header.sigLo == 0x4D and tmp_header.sigHi == 0x5A))
return false;
/* This is a typical DOS kludge! */
if (LH(&header.relocTabOffset) == 0x40)
{
qDebug() << "Don't understand new EXE format";
return false;
}
return true;
}
bool ExeLoader::load(PROG & prog, QFile & fp) {
prog.fCOM = false;
/* Read rest of header */
fp.seek(0);
if (fp.read((char *)&header, sizeof(header)) != sizeof(header))
return false;
/* Calculate the load module size.
* This is the number of pages in the file
* less the length of the header and reloc table
* less the number of bytes unused on last page
*/
uint32_t cb = (uint32_t)LH(&header.numPages) * 512 - (uint32_t)LH(&header.numParaHeader) * 16;
if (header.lastPageSize)
{
cb -= 512 - LH(&header.lastPageSize);
}
/* We quietly ignore minAlloc and maxAlloc since for our
* purposes it doesn't really matter where in real memory
* the program would end up. EXE programs can't really rely on
* their load location so setting the PSP segment to 0 is fine.
* Certainly programs that prod around in DOS or BIOS are going
* to have to load DS from a constant so it'll be pretty
* obvious.
*/
prog.initCS = (int16_t)LH(&header.initCS) + EXE_RELOCATION;
prog.initIP = (int16_t)LH(&header.initIP);
prog.initSS = (int16_t)LH(&header.initSS) + EXE_RELOCATION;
prog.initSP = (int16_t)LH(&header.initSP);
prog.cReloc = (int16_t)LH(&header.numReloc);
/* Allocate the relocation table */
if (prog.cReloc)
{
prog.relocTable.resize(prog.cReloc);
fp.seek(LH(&header.relocTabOffset));
/* Read in seg:offset pairs and convert to Image ptrs */
uint8_t buf[4];
for (int i = 0; i < prog.cReloc; i++)
{
fp.read((char *)buf,4);
prog.relocTable[i] = LH(buf) + (((int)LH(buf+2) + EXE_RELOCATION)<<4);
}
}
/* Seek to start of image */
uint32_t start_of_image= LH(&header.numParaHeader) * 16;
fp.seek(start_of_image);
/* Allocate a block of memory for the program. */
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);
/* Relocate segment constants */
for(uint32_t v : prog.relocTable) {
uint8_t *p = &prog.Imagez[v];
uint16_t w = (uint16_t)LH(p) + EXE_RELOCATION;
*p++ = (uint8_t)(w & 0x00FF);
*p = (uint8_t)((w & 0xFF00) >> 8);
}
return true;
}

31
src/Loaders.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef LOADERS_H
#define LOADERS_H
#include "BinaryImage.h"
#include <QtCore/QFile>
#include <stdlib.h>
struct DosLoader {
protected:
void prepareImage(PROG &prog,size_t sz,QFile &fp);
public:
virtual bool canLoad(QFile &fp)=0;
virtual QString loaderName() const =0;
virtual bool load(PROG &prog,QFile &fp)=0;
};
struct ComLoader : public DosLoader {
virtual ~ComLoader() {}
bool canLoad(QFile &fp) override;
bool load(PROG &prog,QFile &fp) override;
QString loaderName() const override { return "16-bit DOS - COM loader"; }
};
struct ExeLoader : public DosLoader {
virtual ~ExeLoader() {}
bool canLoad(QFile &fp) override;
bool load(PROG &prog,QFile &fp) override;
QString loaderName() const override { return "16-bit DOS - EXE loader"; }
};
#endif // LOADERS_H

View File

@@ -185,18 +185,22 @@ int main(int argc, char **argv)
QCoreApplication::setApplicationVersion("0.1");
setupOptions(app);
Project *proj = Project::get();
/* Front end reads in EXE or COM file, parses it into I-code while
* building the call graph and attaching appropriate bits of code for
* each procedure.
*/
Project::get()->create(option.filename);
proj->create(option.filename);
DccFrontend fe(&app);
if(not Project::get()->load()) {
proj->addLoadCommands();
proj->processAllCommands();
if(proj->m_error_state) {
proj->dumpAllErrors();
return -1;
}
if (option.verbose)
Project::get()->prog.displayLoadInfo();
proj->prog.displayLoadInfo();
if(false==fe.FrontEnd ())
return -1;
if(option.asm1)
@@ -213,9 +217,9 @@ int main(int argc, char **argv)
* analysis, data flow etc. and outputs it to output file ready for
* re-compilation.
*/
BackEnd(Project::get()->callGraph);
BackEnd(proj->callGraph);
Project::get()->callGraph->write();
proj->callGraph->write();
if (option.Stats)
displayTotalStats();

View File

@@ -24,8 +24,6 @@ static void setBits(int16_t type, uint32_t start, uint32_t len);
static void process_MOV(LLInst &ll, STATE * pstate);
static SYM * lookupAddr (LLOperand *pm, STATE * pstate, int size, uint16_t duFlag);
void interactDis(Function * initProc, int ic);
extern uint32_t SynthLab;
/* Returns the size of the string pointed by sym and delimited by delim.
* Size includes delimiter. */
@@ -57,7 +55,7 @@ ICODE * Function::translate_DIV(LLInst *ll, ICODE &_Icode)
eIcode.setRegDU( rAX, eUSE);
eIcode.setRegDU( rTMP, eDEF);
eIcode.ll()->setFlags( SYNTHETIC );
/* eIcode.ll()->label = SynthLab++; */
/* eIcode.ll()->label = Project::get()->SynthLab++; */
eIcode.ll()->label = _Icode.ll()->label;
Icode.addIcode(&eIcode);
@@ -70,7 +68,7 @@ ICODE * Function::translate_DIV(LLInst *ll, ICODE &_Icode)
eIcode.ll()->set(iMOD,ll->getFlag() | SYNTHETIC | IM_TMP_DST);
eIcode.ll()->replaceSrc(_Icode.ll()->src());
eIcode.du = _Icode.du;
eIcode.ll()->label = SynthLab++;
eIcode.ll()->label = Project::get()->SynthLab++;
return Icode.addIcode(&eIcode);
}
ICODE *Function::translate_XCHG(LLInst *ll,ICODE &_Icode)
@@ -108,7 +106,7 @@ ICODE *Function::translate_XCHG(LLInst *ll,ICODE &_Icode)
}
eIcode.ll()->replaceSrc(rTMP);
eIcode.setRegDU( rTMP, eUSE);
eIcode.ll()->label = SynthLab++;
eIcode.ll()->label = Project::get()->SynthLab++;
return Icode.addIcode(&eIcode);
}
@@ -160,7 +158,7 @@ void Function::FollowCtrl(CALL_GRAPH * pcallGraph, STATE *pstate)
_Icode.type = LOW_LEVEL;
ll->set(iJMP,I | SYNTHETIC | NO_OPS);
ll->replaceSrc(LLOperand::CreateImm2(labLoc->ll()->GetLlLabel()));
ll->label = SynthLab++;
ll->label = Project::get()->SynthLab++;
}
/* Copy Icode to Proc */

View File

@@ -1,10 +1,13 @@
#include <QtCore/QString>
#include <QtCore/QDir>
#include <utility>
#include "dcc.h"
#include "CallGraph.h"
#include "project.h"
#include "Procedure.h"
#include <QtCore/QString>
#include <QtCore/QDir>
#include <QtCore/QDebug>
#include <utility>
using namespace std;
//Project g_proj;
QString asm1_name, asm2_name; /* Assembler output filenames */
@@ -15,10 +18,13 @@ OPTION option; /* Command line options */
Project *Project::s_instance = nullptr;
Project::Project() : callGraph(nullptr)
{
m_project_command_stream.setMaximumCommandCount(10);
connect(&m_project_command_stream,SIGNAL(streamCompleted(bool)),SLOT(onCommandStreamFinished(bool)));
memset(&prog,0,sizeof(prog));
}
void Project::initialize()
{
resetCommandsAndErrorState();
delete callGraph;
callGraph = nullptr;
}
@@ -106,3 +112,38 @@ SourceMachine *Project::machine()
return nullptr;
}
void Project::onCommandStreamFinished(bool state)
{
if(false==state) {
m_error_state = true;
}
}
void Project::dumpAllErrors() {
for(QPair<Command *,QString> v : m_command_ctx.m_failures) {
qDebug() << QString("%1 command failed with : %2").arg(v.first->name()).arg(v.second);
}
}
bool Project::addLoadCommands()
{
if(!addCommand<LoaderSelection>())
return false;
if(!addCommand<LoaderApplication>()) {
return false;
}
return true;
}
void Project::processAllCommands()
{
m_command_ctx.proj = this;
m_project_command_stream.processAll(&m_command_ctx);
}
void Project::resetCommandsAndErrorState()
{
m_error_state = false;
m_command_ctx.reset();
m_command_ctx.proj = this;
m_project_command_stream.clear();
}