/*
Copyright (C) 2006 Evan Teran
                   eteran@alum.rit.edu

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "DebuggerMain.h"
#include "DebugEventServer.h"
#include "Debugger.h"
#include "Configuration.h"
#include "ByteStreamRegion.h"
#include "SymbolManager.h"
#include "DebuggerPluginInterface.h"
#include "DebuggerCoreInterface.h"
#include "DialogAttach.h"
#include "DialogMemoryRegions.h"
#include "Util.h"
#include "version.h"

#include <QSettings>
#include <QToolButton>
#include <QMessageBox>
#include <QFileDialog>
#include <QInputDialog>
#include <QTableWidgetItem>
#include <QFileInfo>

#include <fstream>
#include <cassert>

// TODO: move elf parsing functionality to another class
#include <elf.h>


//------------------------------------------------------------------------------
// Name: DebuggerMain(QWidget *parent)
// Desc: 
//------------------------------------------------------------------------------
DebuggerMain::DebuggerMain(QWidget *parent) : QMainWindow(parent), 
		m_DebugEventServer(0), m_UpdateGUI(true), m_ReenableBreakPointAddress(0), 
		m_LastEvent(0), m_StepWasRun(false), m_LastOpenDirectory("") {

	ui.setupUi(this);
	
	std::memset(&m_CurrentState, 0, sizeof(m_CurrentState));

	// setup the global pointers as early as possible
	Debugger::debuggerGUI = this;
	Debugger::debugEventHandler = this;

	// make the stack view all nice..
	// TODO: remember what the user set it as, and restore it!
	ui.stackView->setRowWidth(1);
	ui.stackView->setWordWidth(4);
	ui.stackView->setShowAsciiDump(false);
	
	// setup tabs
	setupTabButtons();
	
	// set the listbox to about 4 lines
	QFontMetrics fm(ui.listWidget->font());
	ui.listWidget->setFixedHeight(fm.height() * 5);
	
	// register our DebugEvent type so it can be used in 
	// inter-thread signals
	qRegisterMetaType<DebugEvent>("DebugEvent");
	
	ui.registerList->setContextMenuPolicy(Qt::CustomContextMenu);

	ui.cpuView->setContextMenuPolicy(Qt::CustomContextMenu);
	ui.stackView->setContextMenuPolicy(Qt::CustomContextMenu);
			
	// add toggles for the dock windows
	ui.menu_View->addAction(ui.registersDock->toggleViewAction());
	ui.menu_View->addAction(ui.dataDock->toggleViewAction());
	ui.menu_View->addAction(ui.stackDock->toggleViewAction());

	// remove the one in the designer and put in our built one
	// it's a bit ugly to do it this way, but the designer wont let me 
	// make a tabless entry..and the ones i create look slightly diff 
	// (probably lack of layout manager in mine...
	ui.tabWidget->removeTab(0);
	createNewDataTab();
	
	m_StackViewInfo.view = ui.stackView;
	
	QFont stackFont;
	stackFont.fromString(Debugger::config()->StackViewFont);
	ui.stackView->setFont(stackFont);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DebuggerMain::~DebuggerMain() {

	switch(Debugger::config()->closeBehaviour) {
	case Configuration::Terminate:
		on_action_Kill_triggered();
		break;
	case Configuration::Detach:
		on_action_Detach_triggered();
		break;
	}
	
	// remove the last tab
	removeDataTab();
	
	// clean up the stream for the stack view too
	safeDelete(m_StackViewInfo.stream);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::finishPluginSetup(QVector<DebuggerPluginInterface *> &plugins) {
	// setup the menu for all plugins that which to do so
	foreach (DebuggerPluginInterface *plugin, plugins) {
		QMenu *const menu = plugin->menu(this);
		if(menu != 0) {
			ui.menu_Plugins->addMenu(menu);
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::closeEvent(QCloseEvent *) {
	
	QSettings settings;
	const QByteArray state = saveState();
	settings.beginGroup("Window");
	settings.setValue("window_state", state);
	settings.setValue("window_width", width());
	settings.setValue("window_height", height());
	settings.endGroup();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::showEvent(QShowEvent *) {
	
	QSettings settings;
	settings.beginGroup("Window");
	const QByteArray state = settings.value("window_state").toByteArray();
	
	const int width = settings.value("window_width", -1).toInt();
	const int height = settings.value("window_height", -1).toInt();
	if(width != -1) {
		resize(width, size().height());
	}
	
	if(height != -1) {
		resize(size().width(), height);
	}
	
	settings.endGroup();
	restoreState(state);
}

//------------------------------------------------------------------------------
// Name: setupTabButtons()
// Desc: creates the add/remove tab buttons in the data view
//------------------------------------------------------------------------------
void DebuggerMain::setupTabButtons() {
	// add the corner widgets to the data view
	m_AddTab = new QToolButton(ui.tabWidget);
	m_DelTab = new QToolButton(ui.tabWidget);
	
	m_AddTab->setToolButtonStyle(Qt::ToolButtonIconOnly);
	m_DelTab->setToolButtonStyle(Qt::ToolButtonIconOnly);
	
	m_AddTab->setIcon(QIcon(":/debugger/pics/addtab.png"));
	m_DelTab->setIcon(QIcon(":/debugger/pics/closetab.png"));
	m_AddTab->setAutoRaise(true);
	m_DelTab->setAutoRaise(true);
	m_DelTab->setEnabled(false);
	
	ui.tabWidget->setCornerWidget(m_AddTab, Qt::TopLeftCorner);
	ui.tabWidget->setCornerWidget(m_DelTab, Qt::TopRightCorner);
	
	connect(m_AddTab, SIGNAL(clicked()), this, SLOT(createNewDataTab()));
	connect(m_DelTab, SIGNAL(clicked()), this, SLOT(removeDataTab()));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_registerList_customContextMenuRequested(const QPoint &pos) {
	QMenu *menu = new QMenu(this);
	menu->addAction("&Follow In Dump", this, SLOT(followInDump()));
	menu->addAction("&Follow In Dump (New Tab)", this, SLOT(followInDumpNewTab()));
	menu->addAction("&Follow In Stack", this, SLOT(followInStack()));
	menu->exec(ui.registerList->mapToGlobal(pos));
	safeDelete(menu);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_cpuView_breakPointToggled(BaseTypes::address_t address) {
	Debugger::toggleBreakPoint(address);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
unsigned int *DebuggerMain::valueFromName(const QString &s, unsigned int mode) {
	if(mode & State::EFLAGS_FLAG && s.startsWith("EFLAGS:"))	return &m_CurrentState.eflags;
	if(mode & State::EAX_FLAG && s.startsWith("EAX:")) 			return &m_CurrentState.eax;
	if(mode & State::EBX_FLAG && s.startsWith("EBX:")) 			return &m_CurrentState.ebx;
	if(mode & State::ECX_FLAG && s.startsWith("ECX:")) 			return &m_CurrentState.ecx;
	if(mode & State::EDX_FLAG && s.startsWith("EDX:")) 			return &m_CurrentState.edx;
	if(mode & State::EBP_FLAG && s.startsWith("EBP:")) 			return &m_CurrentState.ebp;
	if(mode & State::ESP_FLAG && s.startsWith("ESP:")) 			return &m_CurrentState.esp;
	if(mode & State::EDI_FLAG && s.startsWith("EDI:")) 			return &m_CurrentState.edi;;
	if(mode & State::ESI_FLAG && s.startsWith("ESI:")) 			return &m_CurrentState.esi;
	if(mode & State::EIP_FLAG && s.startsWith("EIP:")) 			return &m_CurrentState.eip;
	if(mode & State::CS_FLAG && s.startsWith("CS:")) 			return &m_CurrentState._cs;
	if(mode & State::DS_FLAG && s.startsWith("DS:")) 			return &m_CurrentState._ds;
	if(mode & State::ES_FLAG && s.startsWith("ES:")) 			return &m_CurrentState._es;
	if(mode & State::FS_FLAG && s.startsWith("FS:")) 			return &m_CurrentState._fs;
	if(mode & State::GS_FLAG && s.startsWith("GS:")) 			return &m_CurrentState._gs;
	if(mode & State::SS_FLAG && s.startsWith("SS:")) 			return &m_CurrentState._ss;
	
	return 0;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::followInStack() {
	const QListWidgetItem *const i = ui.registerList->currentItem();
	if(i != 0) {
		const QString s = i->text();
		const unsigned int *value = valueFromName(s, State::GPR_FLAGS);

		if(value != 0) {
			if(!Debugger::dumpStack(*value)) {
				QMessageBox::information(this, "EDB", "There appears to be no memory at that location");
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::followRegisterInDump(bool tabbed) {
	const QListWidgetItem *const i = ui.registerList->currentItem();
	if(i != 0) {
		const QString s = i->text();
		const unsigned int *value = valueFromName(s, State::GPR_FLAGS);

		if(value != 0) {
			if(!Debugger::dumpData(*value, tabbed)) {
				QMessageBox::information(this, "EDB", "There appears to be no memory at that location");
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_registerList_itemDoubleClicked(QListWidgetItem *item) {

	const QString s = item->text();
	unsigned int *value = valueFromName(s, State::ANY_FLAGS);
	
	if(value != 0) {
		if(Debugger::getValueFromUser(*value, "Register Value")) {			
			Debugger::debuggerBase->setState(m_CurrentState);
			updateGUI();
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_About_triggered() {
	QMessageBox::about(this, "About EDB", QString().sprintf(
		"EDB (Evan's Debugger) is designed to be an easy to use, modular, and cross platform debugger.\n"
		"Written by Evan Teran. \n\n"
		"version: %s", version_string));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionAbout_QT_triggered() {
	QMessageBox::aboutQt(this, "About QT");
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUJumpToAddress() {
    bool ok;
    QString text = QInputDialog::getText(this, "Goto Address", "Address:", QLineEdit::Normal, "", &ok);
    if (ok && !text.isEmpty()) {
		const BaseTypes::address_t address = text.toUInt(&ok, 16);
		if(ok) {
			if(!Debugger::jumpToAddress(address)) {
				QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location.");
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuCPUJumpToEIP() {
	if(!Debugger::jumpToAddress(m_CurrentState.eip)) {
		QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location.");
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Configure_Debugger_triggered() {
	
	if(Debugger::dialogOptions()->exec() == QDialog::Accepted) {	
		// reload symbols incase they changed

		safeDelete(Debugger::symbolManager);
		Debugger::symbolManager = new SymbolManager(Debugger::config()->symbolPath);

		if(Debugger::memoryRegions != 0) {
			Debugger::memoryRegions->sync();
		}
		
		QFont stackFont;
		stackFont.fromString(Debugger::config()->StackViewFont);
		ui.stackView->setFont(stackFont);

		updateGUI();
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_cpuView_customContextMenuRequested(const QPoint &pos) {
	QMenu *menu = new QMenu(this);
	// TODO: add comment
	//menu->addAction("Add &Comment", this, SLOT(""));
	//menu->addSeparator();
	menu->addAction("&Goto Address", this, SLOT(mnuCPUJumpToAddress()));
	menu->addAction("&Goto EIP", this, SLOT(mnuCPUJumpToEIP()));
	
	menu->exec(ui.cpuView->viewport()->mapToGlobal(pos));
	safeDelete(menu);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuGotoAddress() {

    bool ok;
    QString text = QInputDialog::getText(this, "Goto Address", "Address:", QLineEdit::Normal, "", &ok);
    if (ok && !text.isEmpty()) {
		const BaseTypes::address_t address = text.toUInt(&ok, 16);
		if(ok) {		
			if(!Debugger::dumpData(address)) {
				QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location.");
			}
		}
	}
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doFollowInDump(QHexView *hv) {
	if(hv != 0) {
		if(hv->hasSelectedText()) {
			const QByteArray data = hv->selectedBytes();

			if(data.size() == 4) {
				union {
					quint8	b[4];
					quint32	d;
				} dword;

				dword.b[0] = data[0];
				dword.b[1] = data[1];
				dword.b[2] = data[2];
				dword.b[3] = data[3];
				
				if(!Debugger::dumpData(dword.d)) {
					QMessageBox::information(this, "No Memory Found", "There appears to be no memory at that location");
				}
			} else {
				QMessageBox::information(this, "Nothing Selected", "Please select a DWORD (4-bytes) to use this function.");
			}
		} else {
			QMessageBox::information(this, "Nothing Selected", "Please select a DWORD (4-bytes) to use this function.");
		}
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuFollowInDump() {
	doFollowInDump(qobject_cast<QHexView *>(ui.tabWidget->currentWidget()));
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::mnuStackFollowInDump() {
	doFollowInDump(ui.stackView);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionApplication_Arguments_triggered() {
    bool ok;
    const QString text = QInputDialog::getText(this, "Application Arguments", "Arguments:", QLineEdit::Normal, m_Arguments, &ok);
	if(ok) {
		m_Arguments = text;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_stackView_customContextMenuRequested(const QPoint &pos) {
	QHexView *const s = ui.stackView;
	if(s != 0) {
		QMenu *menu = s->createStandardContextMenu();
		menu->addSeparator();
		menu->addAction("Follow DWORD In &Dump", this, SLOT(mnuStackFollowInDump()));
		menu->exec(s->mapToGlobal(pos));
		safeDelete(menu);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::dumpContextMenu(const QPoint &pos) {
	QHexView *const s = qobject_cast<QHexView *>(sender());
	if(s != 0) {
		QMenu *menu = s->createStandardContextMenu();
		menu->addSeparator();
		menu->addAction("Follow DWORD In &Dump", this, SLOT(mnuFollowInDump()));
		menu->addAction("&Goto Address", this, SLOT(mnuGotoAddress()));
		menu->exec(s->mapToGlobal(pos));
		safeDelete(menu);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateMenuState(GUI_STATE guiState) {

	switch(guiState) {
	case PAUSED:
	#if 0
		ui.actionRun_Until_Return->setEnabled(true);
	#endif
		ui.action_Run->setEnabled(true);
		ui.action_Pause->setEnabled(false);
		ui.action_Step_Into->setEnabled(true);
		ui.action_Step_Over->setEnabled(true);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(true);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(true);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(true);
		break;
	case RUNNING:
	#if 0
		ui.actionRun_Until_Return->setEnabled(false);
	#endif
		ui.action_Run->setEnabled(false);
		ui.action_Pause->setEnabled(true);
		ui.action_Step_Into->setEnabled(false);
		ui.action_Step_Over->setEnabled(false);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(false);
		break;
	case TERMINATED:
	#if 0
		ui.actionRun_Until_Return->setEnabled(false);
	#endif
		ui.action_Run->setEnabled(false);
		ui.action_Pause->setEnabled(false);
		ui.action_Step_Into->setEnabled(false);
		ui.action_Step_Over->setEnabled(false);
		ui.action_Step_Into_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Step_Over_Pass_Signal_To_Application->setEnabled(false);
		ui.action_Run_Pass_Signal_To_Application->setEnabled(false);
		break;
	}
	m_GUIState = guiState;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventSignaled() {
	switch(m_LastEvent.exitSignal()) {
	case DebugEvent::sigkill:
		QMessageBox::information(this, "Application Killed", "The debugged application was killed.");
		break;
	default:
		Debugger::log(QString().sprintf("Unknown Termination Signal Recieved! %d", m_LastEvent.exitSignal()));
		break;
	}

	on_action_Detach_triggered();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventStopped() {


	// ok we just came in from a signal, we need to test some things, 
	// generally, we will want to check if it was a step, if it was, was it because
	// we just hit a break point or because we wanted to run, but had to step first
	// in case were were on a breakpoint already...

	switch(m_LastEvent.stopSignal()) {
	case DebugEvent::sigsegv:
		QMessageBox::information(this, "Illegal Access Fault", QString().sprintf(
			"The debugged application encountered a segmentation fault!\n"
			"The address %#08x could not be accessed.\n\n"
			"If you would like to pass this exception to the application press Shift+[F7/F8/F9]", 
			Debugger::debuggerBase->getFaultAddress())
			);
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigill:
		QMessageBox::information(this, "Illegal Instruction Fault", 
			"The debugged application attempted to execute an illegal instruction.\n\n"
			"If you would like to pass this exception to the application press Shift+[F7/F8/F9]"
			);
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigfpe:
		QMessageBox::information(this, "Floating Point Exception", 
			"The debugged application encountered a floating-point exception!\n"
			"If you would like to pass this exception to the application press Shift+[F7/F8/F9]"
			);
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigstop:
		// user asked to pause the debugged process
		m_StepWasRun = false;
		break;
		
	case DebugEvent::sigtrap:
		
		// we just got a SIGTRAP, there are a few possible causes
		// #1: we just did an execve, in which case we want to stop cause we just attached
		// #2: we hit a 0xCC breakpoint, if so, then we want to stop
		// #3: we did a step
		
		do {
			// prevEIP will have the address of the instruction which brought us here!
			const BaseTypes::address_t prevEIP = m_CurrentState.eip - Debugger::debuggerBase->executionBreakPointSize();
			
			// look it up in our breakpoint list, make sure it is one of OUR int3s!
			// if it is, we need to backup EIP and pause ourselves
			const BreakPoint *const bp = Debugger::debuggerBase->findBreakPoint(prevEIP);
			if(bp != 0 && bp->enabled()) {
						
				// if it's a one time breakpoint then we should remove it upon
				// triggering, this is mainly used for "run-until" situations
				// like step over

				if(bp->oneTime()) {
					Debugger::debuggerBase->removeBreakpoint(prevEIP);
				} else {
					// TODO: make this work
					//++bp->passcount;					
				}

				// back up eip, since we executed a breakpoint instead of 
				// the real code that belongs there
				m_CurrentState.eip = prevEIP;
				Debugger::debuggerBase->setState(m_CurrentState);

				m_StepWasRun = false;
			} else {
				// it wasn't one of our int3s, so what do we do?

				// this code attempts to cancel a run which will
				// cause overrun a breakpoint potentially				
				uint8_t breakpointData[Debugger::debuggerBase->executionBreakPointSize()];
				if(Debugger::debuggerBase->readBytes(prevEIP, breakpointData, sizeof(breakpointData))) {
					if(std::memcmp(
						breakpointData, 
						Debugger::debuggerBase->executionBreakPointData().data(), 
						Debugger::debuggerBase->executionBreakPointSize()) == 0) {
						m_StepWasRun = false;			
					}
				}
			}
		} while(0);
		break;

	case DebugEvent::sigchld:
	case DebugEvent::sigcont:
		// this one is probably not very interesting, so we'll just resume
		Debugger::debuggerBase->resume(m_LastEvent.stopSignal());
		// we do an early return so it's like it never happened
		return;
	default:
		Debugger::log(QString().sprintf("Unknown Stop Signal Recieved! %d", m_LastEvent.stopSignal()));
		Debugger::debuggerBase->resume(m_LastEvent.stopSignal());
		return;
	}

	// re-enable any breakpoints we disabled
	if(m_ReenableBreakPointAddress != 0) {
		Debugger::debuggerBase->enableBreakpoint(m_ReenableBreakPointAddress);
		m_ReenableBreakPointAddress = 0;
	}

	// if we stepped with the intention of running, well we should run then!
	if(m_StepWasRun) {
		m_StepWasRun = false;
		Debugger::debuggerBase->resume(0);
	} else {
		updateGUI();
		updateMenuState(PAUSED);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEventExited() {
	QMessageBox::information(this, "Application Exited", QString().sprintf("The debugged application exited normally with exit code %d.",  m_LastEvent.exitStatus()));
	on_action_Detach_triggered();
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::handleEvent(DebugEvent event) {
	if(Debugger::debuggerBase != 0) {
		Debugger::debuggerBase->getState(m_CurrentState);
	}
	
	// it is possible but rare that that we get an event when no memory regions
	// are setup (on detach)
	if(Debugger::memoryRegions != 0) {
		Debugger::memoryRegions->sync();
	}
	
	m_LastEvent = event;
	
	switch(m_LastEvent.reason()) {
	case DebugEvent::EVENT_STOPPED:
		handleEventStopped();
		break;
		
	case DebugEvent::EVENT_SIGNALED:
		handleEventSignaled();
		break;
		
	case DebugEvent::EVENT_EXITED:
		handleEventExited();
		break;
		
	case DebugEvent::EVENT_UNKNOWN:
		break;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::debugEventHandler(DebugEvent event) {

	if(Debugger::debugEventHandler != 0) {
		Debugger::debugEventHandler->handleEvent(event);
	} else {
		assert(0);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::DataViewInfo::update() {

	safeDelete(stream);
	stream = new ByteStreamRegion(region);
	
	if(view != 0) {
		view->setAddressOffset(region.start);
		view->setData(stream);
	}
}


//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateData(DebuggerMain::DataViewInfo *v) {
	if(v != 0) {
		v->update();	
		ui.tabWidget->setTabText(ui.tabWidget->indexOf(v->view), QString().sprintf("%08x-%08x", v->region.start, v->region.end));
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::clearData(DataViewInfo *v) {
	if(v != 0) {
		v->view->clear();
		v->view->scrollTo(0);
		ui.tabWidget->setTabText(ui.tabWidget->indexOf(v->view), QString().sprintf("%08x-%08x", v->region.start, v->region.end));
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateInstructionInfo(BaseTypes::address_t address) {

	ui.listWidget->clear();
	
	if(Debugger::debuggerBase != 0) {
		uint8_t buffer[MAX_INSN_SIZE];
		x86_insn_t insn;
		
		Debugger::debuggerBase->readBytes(address, buffer, sizeof(buffer));
		const int size = x86_disasm(buffer, sizeof(buffer), address, 0, &insn);
		if(size != 0) {
		
			// fetch the 3 possible operands
			x86_op_t *const operands[3] = {
				x86_operand_1st(&insn),
				x86_operand_2nd(&insn),
				x86_operand_3rd(&insn)
			};

			// figure out the instruction type and display some information about it
			switch(insn.type) {
			case insn_return:
				do {
					BaseTypes::address_t returnAddress;
					Debugger::debuggerBase->readBytes(m_CurrentState.esp, &returnAddress, sizeof(returnAddress));
					
					const QString symname = findFunctionSymbol(returnAddress, "");
					if(symname != "") {
						ui.listWidget->addItem(QString().sprintf("Return to %08x <%s>", returnAddress, qPrintable(symname)));
					} else {
						ui.listWidget->addItem(QString().sprintf("Return to %08x", returnAddress));
					}
				} while(0);
				break;
			case insn_jcc:
				do {
					bool taken = true;
					const bool cf = (m_CurrentState.eflags & 0x0001) != 0;
					const bool zf = (m_CurrentState.eflags & 0x0040) != 0;
					const bool of = (m_CurrentState.eflags & 0x0800) != 0;
					const bool df = (m_CurrentState.eflags & 0x0400) != 0;
					const bool sf = (m_CurrentState.eflags & 0x0080) != 0;
					const bool pf = (m_CurrentState.eflags & 0x0004) != 0;

					if((insn.flags_tested & insn_carry_set)					&& !cf) taken = false;
					if((insn.flags_tested & insn_zero_set)					&& !zf) taken = false;
					if((insn.flags_tested & insn_oflow_set)					&& !of) taken = false;
					if((insn.flags_tested & insn_dir_set)					&& !df) taken = false;
					if((insn.flags_tested & insn_sign_set)					&& !sf) taken = false;
					if((insn.flags_tested & insn_parity_set)				&& !pf) taken = false;
					if((insn.flags_tested & insn_carry_clear)				&& cf) taken = false;
					if((insn.flags_tested & insn_zero_clear)				&& zf) taken = false;
					if((insn.flags_tested & insn_oflow_clear)				&& of) taken = false;
					if((insn.flags_tested & insn_dir_clear)					&& df) taken = false;
					if((insn.flags_tested & insn_sign_clear)				&& sf) taken = false;
					if((insn.flags_tested & insn_parity_clear)				&& pf) taken = false;

					if((insn.flags_tested & insn_zero_set_or_sign_ne_oflow)	&& (!zf && !(sf != of)))	taken = false;
					if((insn.flags_tested & insn_carry_or_zero_set)			&& !(cf && zf))	taken = false;
					if((insn.flags_tested & insn_sign_eq_oflow)				&& !(sf == of))	taken = false;
					if((insn.flags_tested & insn_sign_ne_oflow)				&& (sf == of))	taken = false;

					if(taken) {
						ui.listWidget->addItem("jump taken");
					} else {
						ui.listWidget->addItem("jump NOT taken");
					}
				} while(0);
				// FALL THROUGH!
			case insn_jmp:
			case insn_call:
			case insn_callcc:
				if(operands[0] != 0) {
					switch(operands[0]->type) {
					case op_register:
						do {
							const BaseTypes::address_t effective_address = m_CurrentState.value(operands[0]->data.reg.name);

							char tmp[100];
							if(Debugger::config()->syntax == Configuration::Intel) {
								x86_format_operand(operands[0], tmp, sizeof(tmp), intel_syntax);
							} else {
								x86_format_operand(operands[0], tmp, sizeof(tmp), att_syntax);
							}

							const QString symname = findFunctionSymbol(effective_address, "");
							if(symname != "") {
								ui.listWidget->addItem(QString().sprintf("%s = %08x <%s>", tmp, effective_address, qPrintable(symname)));
							} else {
								ui.listWidget->addItem(QString().sprintf("%s = %08x", tmp, effective_address));
							}
						} while(0);
						break;

					case op_relative_far:
						do {
							const BaseTypes::address_t effective_address = m_CurrentState.eip + insn.size + operands[0]->data.relative_far;

							char tmp[100];
							if(Debugger::config()->syntax == Configuration::Intel) {
								x86_format_operand(operands[0], tmp, sizeof(tmp), intel_syntax);
							} else {
								x86_format_operand(operands[0], tmp, sizeof(tmp), att_syntax);
							}

							const QString symname = findFunctionSymbol(effective_address, "");
							if(symname != "") {
								ui.listWidget->addItem(QString().sprintf("%s = %08x <%s>", tmp, effective_address, qPrintable(symname)));
							}
						} while(0);
						break;
					case op_relative_near:
						do {
							const BaseTypes::address_t effective_address = m_CurrentState.eip + insn.size + operands[0]->data.relative_near;

							char tmp[100];
							if(Debugger::config()->syntax == Configuration::Intel) {
								x86_format_operand(operands[0], tmp, sizeof(tmp), intel_syntax);
							} else {
								x86_format_operand(operands[0], tmp, sizeof(tmp), att_syntax);
							}

							const QString symname = findFunctionSymbol(effective_address, "");
							if(symname != "") {
								ui.listWidget->addItem(QString().sprintf("%s = %08x <%s>", tmp, effective_address, qPrintable(symname)));
							}
						} while(0);
						break;
					case op_expression:
					default:
						do {
							x86_ea_t expression = operands[0]->data.expression;

							// to calculate address:
							// base + index * scale + disp

							const BaseTypes::reg_t base						= m_CurrentState.value(expression.base.name);
							const BaseTypes::reg_t index					= m_CurrentState.value(expression.index.name);
							const BaseTypes::address_t effective_address	= base + index * expression.scale + expression.disp;
							BaseTypes::address_t value;

							Debugger::debuggerBase->readBytes(effective_address, &value, sizeof(value));

							char tmp[100];
							if(Debugger::config()->syntax == Configuration::Intel) {
								x86_format_operand(operands[0], tmp, sizeof(tmp), intel_syntax);
							} else {
								x86_format_operand(operands[0], tmp, sizeof(tmp), att_syntax);
							}

							const QString symname = findFunctionSymbol(effective_address, "");
							if(symname != "") {
								ui.listWidget->addItem(QString().sprintf("%s = [%08x] = %08x <%s>", tmp, effective_address, value, qPrintable(symname)));
							} else {
								ui.listWidget->addItem(QString().sprintf("%s = [%08x] = %08x", tmp, effective_address, value));
							}
						} while(0);
						break;
					}
				}
				break;
			default:
				for(int j = 0; j < 3; ++j) {

					if(operands[j] != 0 && operands[j]->type == op_expression) {
						x86_ea_t expression = operands[j]->data.expression;

						// to calculate address:
						// base + index * scale + disp

						const BaseTypes::reg_t base						= m_CurrentState.value(expression.base.name);
						const BaseTypes::reg_t index					= m_CurrentState.value(expression.index.name);
						const BaseTypes::address_t effective_address	= base + index * expression.scale + expression.disp;
						BaseTypes::address_t value;

						Debugger::debuggerBase->readBytes(effective_address, &value, sizeof(value));

						char tmp[100];
						if(Debugger::config()->syntax == Configuration::Intel) {
							x86_format_operand(operands[j], tmp, sizeof(tmp), intel_syntax);
						} else {
							x86_format_operand(operands[j], tmp, sizeof(tmp), att_syntax);
						}

						ui.listWidget->addItem(QString().sprintf("%s = [%08x] = %08x", tmp, effective_address, value));
					}
				}
				break;

			}
		}

		x86_oplist_free(&insn);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::updateDisassembly(BaseTypes::address_t address, const MemoryRegions::Region &r) {

	ui.cpuView->setCurrentAddress(m_CurrentState.eip);
	ui.cpuView->setAddressOffset(r.start);
	ui.cpuView->setRegion(r);
	
	if(!ui.cpuView->addressShown(m_CurrentState.eip)) {
		ui.cpuView->scrollTo(address);
	}
	
	updateInstructionInfo(address);
}

//------------------------------------------------------------------------------
// Name: findFunctionSymbol(BaseTypes::address_t address, const QString &defaultValue)
// Desc: 
//------------------------------------------------------------------------------
QString DebuggerMain::findFunctionSymbol(BaseTypes::address_t address, const QString &defaultValue) {
	QString symname(defaultValue);

	if(Debugger::symbolManager != 0) {
		const SymbolManager::Symbol *const s = Debugger::symbolManager->findNearSymbol(address);
		if(s != 0) {
			symname = QString().sprintf("%s+%x", qPrintable(s->name), address - s->address);
		}
	}
	
	return symname;
}

//------------------------------------------------------------------------------
// Name: updateGUI()
// Desc: updates all the different displays
//------------------------------------------------------------------------------
void DebuggerMain::updateGUI() {

	if(m_UpdateGUI) {
	
		MemoryRegions::Region region;

		if(Debugger::memoryRegions != 0) {
		
			if(!Debugger::memoryRegions->findRegion(m_CurrentState.eip, region)) {
				ui.cpuView->clear();
				ui.listWidget->clear();
			} else {
				updateDisassembly(m_CurrentState.eip, region);
			}

			// update all data views with the current region data
			foreach(DataViewInfo *info, m_DataRegions) {

				// make sure the regions are still valid..
				if(Debugger::memoryRegions->findRegion(info->region.start)) {
					updateData(info);
				} else {
					clearData(info);
				}
			}

			if(!Debugger::dumpStack(m_CurrentState.esp)) {
				ui.stackView->clear();
				ui.stackView->scrollTo(0);
			}
		} else {
			
			ui.cpuView->clear();
			ui.cpuView->scrollTo(0);
			
			ui.listWidget->clear();

			// update all data views with the current region data
			foreach(DataViewInfo *info, m_DataRegions) {
				clearData(info);
			}
			
			ui.stackView->clear();
			ui.stackView->scrollTo(0);
		}

		ui.registerList->clear();
		ui.registerList->addItem(QString().sprintf("EAX: %08x", m_CurrentState.eax));
		ui.registerList->addItem(QString().sprintf("EBX: %08x", m_CurrentState.ebx));
		ui.registerList->addItem(QString().sprintf("ECX: %08x", m_CurrentState.ecx));
		ui.registerList->addItem(QString().sprintf("EDX: %08x", m_CurrentState.edx));
		ui.registerList->addItem(QString().sprintf("EBP: %08x", m_CurrentState.ebp));
		ui.registerList->addItem(QString().sprintf("ESP: %08x", m_CurrentState.esp));
		ui.registerList->addItem(QString().sprintf("ESI: %08x", m_CurrentState.esi));
		ui.registerList->addItem(QString().sprintf("EDI: %08x", m_CurrentState.edi));

		const QString symname = findFunctionSymbol(m_CurrentState.eip, region.name);

		ui.registerList->addItem(QString().sprintf("EIP: %08x <%s>", m_CurrentState.eip, qPrintable(symname)));
		ui.registerList->addItem(QString().sprintf("EFLAGS: %08x", m_CurrentState.eflags));
		ui.registerList->addItem(QString().sprintf("CS: %04x", m_CurrentState.cs & 0xffff));
		ui.registerList->addItem(QString().sprintf("DS: %04x", m_CurrentState.ds & 0xffff));
		ui.registerList->addItem(QString().sprintf("ES: %04x", m_CurrentState.es & 0xffff));
		ui.registerList->addItem(QString().sprintf("FS: %04x", m_CurrentState.fs & 0xffff));
		ui.registerList->addItem(QString().sprintf("GS: %04x", m_CurrentState.gs & 0xffff));
		ui.registerList->addItem(QString().sprintf("SS: %04x", m_CurrentState.ss & 0xffff));
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doResume(bool passExceptionToApp) {
	
	// are we currently sitting on a breakpoint
	const bool atBreakPoint = Debugger::debuggerBase->findBreakPoint(m_CurrentState.eip) != 0;
	
	// if we are on a breakpoint, disable it, and set the reenable 
	// address to the current eip
	if(atBreakPoint) {
		Debugger::debuggerBase->disableBreakpoint(m_CurrentState.eip);
		m_ReenableBreakPointAddress = m_CurrentState.eip;
	}

	// set the state to "running"
	updateMenuState(RUNNING);
	
	// if neccessary pass the signal to the application, otherwise just step
	if(passExceptionToApp && m_LastEvent.stopped() && m_LastEvent.stopSignal() != SIGTRAP && m_LastEvent.stopSignal() != SIGSTOP) {
		Debugger::debuggerBase->step(m_LastEvent.stopSignal());
	} else {
		Debugger::debuggerBase->step(0);
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Run_Pass_Signal_To_Application_triggered() {
	m_StepWasRun = true;
	doResume(true);	
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Into_Pass_Signal_To_Application_triggered() {
	doResume(true);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
DebuggerMain::InstructionType DebuggerMain::getInstructionType(uint8_t *buffer, std::size_t sz, bool &ok) {

	InstructionType ret;
	x86_insn_t insn;
	const int size = x86_disasm(buffer, sz, 0, 0, &insn);
	ok = size != 0;
	ret = insn.type;
	x86_oplist_free(&insn);
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Over_Pass_Signal_To_Application_triggered() {
	uint8_t buffer[MAX_INSN_SIZE];
	x86_insn_t insn;

	Debugger::debuggerBase->readBytes(m_CurrentState.eip, buffer, sizeof(buffer));
		
	const int size = x86_disasm(buffer, sizeof(buffer), 0, 0, &insn);
	if(size != 0 && (insn.type == insn_call || insn.type == insn_callcc)) {
		// add a temporary breakpoint at the instruction just after the call
		Debugger::debuggerBase->addBreakpoint(m_CurrentState.eip + size, true);
		on_action_Run_Pass_Signal_To_Application_triggered();
	} else {
		on_action_Step_Into_Pass_Signal_To_Application_triggered();
	}
	x86_oplist_free(&insn);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Run_triggered() {
	m_StepWasRun = true;
	doResume(false);	
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Into_triggered() {
	doResume(false);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Step_Over_triggered() {

	uint8_t buffer[MAX_INSN_SIZE];
	x86_insn_t insn;

	Debugger::debuggerBase->readBytes(m_CurrentState.eip, buffer, sizeof(buffer));
		
	const int size = x86_disasm(buffer, sizeof(buffer), 0, 0, &insn);
	if(size != 0 && (insn.type == insn_call || insn.type == insn_callcc)) {
		// add a temporary breakpoint at the instruction just after the call
		Debugger::debuggerBase->addBreakpoint(m_CurrentState.eip + size, true);
		on_action_Run_triggered();
	} else {
		on_action_Step_Into_triggered();
	}
	x86_oplist_free(&insn);
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_actionRun_Until_Return_triggered() {
	// TODO: there is a problem with this code in principle it should be fine, 
	// but on_action_Step_Over_triggered can potentially call mnuRun which will:
	// a) start a debugeventserver thread
	// b) call resume
	// this causes the run to be done concurently to the wait
	// the problem is that on_action_Step_Over_triggered will return well before
	// the run has actually hit it's breakpoint, thus giving us a
	// very inconsistant state
	
#if 0
	uint8_t buffer[MAX_INSN_SIZE];
	x86_insn_t insn;
	int size;
	
	Debugger::debuggerBase->readBytes(m_CurrentState.eip, buffer, sizeof(buffer));
	size = x86_disasm(buffer, sizeof(buffer), 0, 0, &insn);
	
	while(size != 0 && insn.type != insn_return) {

		m_UpdateGUI = false;
		on_action_Step_Over_triggered();
		m_UpdateGUI = true;
		x86_oplist_free(&insn);
		
		Debugger::debuggerBase->readBytes(m_CurrentState.eip, buffer, sizeof(buffer));
		size = x86_disasm(buffer, sizeof(buffer), 0, 0, &insn);
	}
	
	x86_oplist_free(&insn);
	
	updateGUI();
#endif
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Pause_triggered() {
	Debugger::debuggerBase->pause();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Restart_triggered() {
	
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::cleanupEventThread() {

	if(m_DebugEventServer != 0) {
		
		// make sure we don't get any spurious events!
		disconnect(m_DebugEventServer, SIGNAL(debugEvent(DebugEvent)), this, SLOT(debugEventHandler(DebugEvent)));
		
		// make sure it thinks it shoudl exit
		m_DebugEventServer->m_ShouldExit = true;

		// this is ugly, but it seems to work pretty well
		// basically we have marked the thread as "shoudl exit"
		// but, we may be blocked (likley) waiting for the next event
		// so we intentionally cause one last event giving it a change to
		// check the loop invariant one more time, which will allow
		// the thread to exit cleanly
		if(m_GUIState == RUNNING) {
			Debugger::debuggerBase->pause();
			updateMenuState(PAUSED);
		} else {
			Debugger::debuggerBase->step(0);
			updateMenuState(RUNNING);
		}
		m_DebugEventServer->wait();

		// TODO: find a way to wake up the waiting event thread
		m_DebugEventServer = 0;
	}
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::cleanupDebugger() {
	
	cleanupEventThread();

	safeDelete(Debugger::memoryRegions);
	safeDelete(Debugger::symbolManager);

	// clear up the data view
	while(ui.tabWidget->count() > 1) {
		removeDataTab();
	}
	
	setWindowTitle("EDB");
	
	updateGUI();
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doDetachFromProcess(bool kill) {

	cleanupDebugger();
	
	m_StepWasRun = false;
	
	if(Debugger::debuggerBase != 0) {
		if(kill) {
			Debugger::debuggerBase->kill();
		} else {
			Debugger::debuggerBase->detach();
		}
	}
	
	updateMenuState(TERMINATED);
}

//------------------------------------------------------------------------------
// Name: on_action_Detach_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Detach_triggered() {
	doDetachFromProcess(false);
}

//------------------------------------------------------------------------------
// Name: on_action_Kill_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Kill_triggered() {
	doDetachFromProcess(true);
}

//------------------------------------------------------------------------------
// Name: getELFEntryPoint(const QString &s)
// Desc: 
//------------------------------------------------------------------------------
BaseTypes::address_t DebuggerMain::getELFEntryPoint(const QString &s) {

	union ElfHeader {
		Elf32_Ehdr elf32;
		Elf64_Ehdr elf64;
	};

	std::ifstream file(qPrintable(s), std::ios::binary);
	if(file) {
		ElfHeader header;
		file.seekg(0);
		if(file.read(reinterpret_cast<char *>(&header), sizeof(header))) {
			if(std::memcmp(header.elf32.e_ident, ELFMAG, SELFMAG) == 0) {
				switch(header.elf32.e_ident[EI_CLASS]) {
				case ELFCLASS32:
					return header.elf32.e_entry;
					break;
				case ELFCLASS64:
					// NOT YET SUPPORTED
					assert(0);
					return header.elf64.e_entry;
					break;
				}
			}
		}
	}
	return 0;
}


//------------------------------------------------------------------------------
// Name: setInitialDebuggerState()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::setInitialDebuggerState() {

	updateMenuState(PAUSED);
	
	m_DebugEventServer = new DebugEventServer(this);
	connect(m_DebugEventServer, SIGNAL(debugEvent(DebugEvent)), this, SLOT(debugEventHandler(DebugEvent)));
	m_DebugEventServer->start();
	
	Debugger::symbolManager = new SymbolManager(Debugger::config()->symbolPath);
	Debugger::memoryRegions = new MemoryRegions(Debugger::debuggerBase->pid());
	Debugger::memoryRegions->sync();
	
	const QVector<MemoryRegions::Region> &l = Debugger::memoryRegions->regions();

	assert(m_DataRegions.size() > 0);

	if(l.size() >= 2) {
		m_DataRegions[0]->region = l[1];
	} else {
		m_DataRegions[0]->region = MemoryRegions::Region();
	}
	
	std::memset(&m_CurrentState, 0, sizeof(m_CurrentState));
	
	m_StepWasRun = false;
}

//------------------------------------------------------------------------------
// Name: setInitialBreakpoint(const QString &s)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::setInitialBreakpoint(const QString &s) {
	BaseTypes::address_t entryPoint = 0;
	if(Debugger::config()->initialBreakPoint == Configuration::MainSymbol) {

		const QFileInfo fileInfo(s);
		QString mainSymbol = fileInfo.baseName();
		if(fileInfo.completeSuffix() != "") {
			mainSymbol += ".";
			mainSymbol += fileInfo.completeSuffix();
		}
		
		mainSymbol += "::main";

		const SymbolManager::Symbol *const sym = Debugger::symbolManager->findSymbol(mainSymbol);

		if(sym != 0) {
			entryPoint = sym->address;
		} else {
			entryPoint = getELFEntryPoint(s);
		}
	} else if(Debugger::config()->initialBreakPoint == Configuration::EntryPoint) {
		entryPoint = getELFEntryPoint(s);
	}

	if(entryPoint != 0) {
		Debugger::debuggerBase->addBreakpoint(entryPoint);
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Open_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Open_triggered() {
	QFileDialog *const fd = new QFileDialog(this);
	
	fd->setFileMode(QFileDialog::AnyFile);
	const QString s = fd->getOpenFileName(0, "Choose a file", m_LastOpenDirectory, "");
	if(s != "") {
	
		m_LastOpenDirectory = QFileInfo(s).canonicalFilePath();
	
		on_action_Detach_triggered();
	
		if(Debugger::debuggerBase->open(s, m_Arguments)) {
			setInitialDebuggerState();
			setInitialBreakpoint(s);
			setWindowTitle(QString().sprintf("EDB - %s", qPrintable(s)));
		} else {
			QMessageBox::information(this, "Could Not Attach", "Failed to open and attach to process, please check priviledges and try again.");
		}
		
		Debugger::debuggerBase->getState(m_CurrentState);
		updateGUI();
	}
}

//------------------------------------------------------------------------------
// Name: doAttach(BaseTypes::pid_t pid)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::doAttach(BaseTypes::pid_t pid) {
	if(pid != Debugger::debuggerBase->pid()) {

		on_action_Detach_triggered();

		if(Debugger::debuggerBase->attach(pid)) {
			setInitialDebuggerState();
			setWindowTitle(QString().sprintf("EDB - [%d]", pid));
		} else {
			QMessageBox::information(this, "EDB", "Failed to attach to process, please check priviledges and try again.");				
		}

		Debugger::debuggerBase->getState(m_CurrentState);
		updateGUI();
	} else {
		QMessageBox::information(this, "EDB", "You are already debugging that process!");
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Attach_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Attach_triggered() {
	static DialogAttach *dlg = 0;
	if(dlg == 0) {
		dlg = new DialogAttach(this);
	} 
	
	const int ret = dlg->exec();
	
	if(ret == QDialog::Accepted) {
		QList<QTableWidgetItem *> sel = dlg->ui.tableWidget->selectedItems();
		if(sel.size() != 0) {
			BaseTypes::pid_t pid = sel.begin()[0]->text().toUInt();
			doAttach(pid);
		}
	}
}

//------------------------------------------------------------------------------
// Name: on_action_Memory_Regions_triggered()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerMain::on_action_Memory_Regions_triggered() {
	static DialogMemoryRegions *dlg = 0;
	if(dlg == 0) {
		dlg = new DialogMemoryRegions(this);
	}
	
	const int ret = dlg->exec();
	
	if(ret == QDialog::Accepted) {
		QList<QTableWidgetItem *> sel = dlg->ui.tableWidget->selectedItems();
		if(sel.size() != 0) {
			const BaseTypes::address_t region_start	= sel.begin()[0]->text().toUInt(0, 16);
			Debugger::dumpData(region_start);
		}
	}
}

//------------------------------------------------------------------------------
// Name: createNewDataTab()
// Desc: duplicates the current tab creating a new one
//------------------------------------------------------------------------------
void DebuggerMain::createNewDataTab() {
	
	const int currentIndex = ui.tabWidget->currentIndex();
	
	DataViewInfo *newDataView = new DataViewInfo;
	
	if(currentIndex != -1) {
		// duplicate the current region
		newDataView->region = m_DataRegions[currentIndex]->region;
	}
	
	newDataView->stream	= new ByteStreamRegion(newDataView->region);
	newDataView->view	= new QHexView;
		
	// setup the context menu
	newDataView->view->setContextMenuPolicy(Qt::CustomContextMenu);
	connect(newDataView->view, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(dumpContextMenu(const QPoint &)));
	
	newDataView->view->setAddressOffset(newDataView->region.start);
	newDataView->view->setData(newDataView->stream);
	
	m_DataRegions.push_back(newDataView);

	// create the tab!
	ui.tabWidget->addTab(newDataView->view, QString().sprintf("%08x-%08x", newDataView->region.start, newDataView->region.end));
	ui.tabWidget->setCurrentIndex(ui.tabWidget->count() - 1);
	
	if(ui.tabWidget->count() > 1) {
		m_DelTab->setEnabled(true);
	}
}

//------------------------------------------------------------------------------
// Name: removeDataTab() 
// Desc: handles removing of a memory view tab
//------------------------------------------------------------------------------
void DebuggerMain::removeDataTab() {
	const int currentIndex = ui.tabWidget->currentIndex();
	
	DataViewInfo *info = m_DataRegions[currentIndex];
	m_DataRegions.remove(currentIndex);
	
	ui.tabWidget->removeTab(currentIndex);
	if(ui.tabWidget->count() <= 1) {
		m_DelTab->setEnabled(false);
	}

	if(info != 0) {
		safeDelete(info->stream);
		safeDelete(info->view);
	}
	
	safeDelete(info);
}
