mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 20:39:01 +00:00
473 lines
No EOL
16 KiB
C#
473 lines
No EOL
16 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Data;
|
|
using System.Drawing;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace Cosmos.Debug.GDB {
|
|
public partial class FormMain : Form {
|
|
protected class GdbAsmLine {
|
|
public readonly UInt32 mAddr;
|
|
public readonly string mLabel;
|
|
public readonly string mOp;
|
|
public readonly string mData = string.Empty;
|
|
public readonly bool mEIPHere;
|
|
|
|
public GdbAsmLine(string aInput) {
|
|
//"0x0056d2b9 <_end_data+0>:\tmov DWORD PTR ds:0x550020,ebx\n"
|
|
var s = GDB.Unescape(aInput);
|
|
var xSplit1 = s.Split(Global.TabSeparator, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
var xSplit2 = xSplit1[0].Split(Global.SpaceSeparator, StringSplitOptions.RemoveEmptyEntries);
|
|
|
|
int xIndex = 0;
|
|
//newer gdb above 6.6 or higher versions
|
|
if (xSplit2[0] == "=>")
|
|
{
|
|
mEIPHere = true;
|
|
xIndex = 1;
|
|
}
|
|
mAddr = Global.FromHexWithLeadingZeroX(xSplit2[xIndex]);
|
|
string xLabel;
|
|
if (xSplit2.Length > xIndex + 1) {
|
|
xLabel = xSplit2[xIndex + 1];
|
|
}
|
|
|
|
xSplit2 = xSplit1[1].Split(Global.SpaceSeparator, StringSplitOptions.RemoveEmptyEntries);
|
|
mOp = xSplit2[0];
|
|
if (xSplit2.Length > 1) {
|
|
for (int j = 1; j < xSplit2.Length; j++) {
|
|
mData += xSplit2[j] + " ";
|
|
}
|
|
mData = mData.TrimEnd();
|
|
}
|
|
}
|
|
|
|
public override string ToString() {
|
|
// First char reserved for breakpoint (*)
|
|
return " " + mAddr.ToString("X8") + ": " + mOp + " " + mData.TrimEnd();
|
|
}
|
|
}
|
|
|
|
const int MAX_RETRY = 3;
|
|
protected string mFuncName;
|
|
protected bool mCreated;
|
|
protected int mConnectRetry;
|
|
|
|
protected void OnGDBResponse(GDB.Response aResponse) {
|
|
try {
|
|
Windows.mLogForm.Log(aResponse);
|
|
var xCmdLine = aResponse.Command.ToLower();
|
|
if (xCmdLine == "info registers") {
|
|
Windows.mRegistersForm.UpdateRegisters(aResponse);
|
|
Windows.UpdateAfterRegisterUpdate();
|
|
}else if(xCmdLine.Length == 0) {
|
|
if (aResponse.Text.Count == 2 && aResponse.Text[0] == "Breakpoint")
|
|
{
|
|
// program breaks on aResponse.Text[1]
|
|
}
|
|
else
|
|
{
|
|
// contains address where we are
|
|
}
|
|
} else {
|
|
var xCmdParts = xCmdLine.Split(Global.SpaceSeparator);
|
|
var xCmd = xCmdParts[0];
|
|
if (xCmd.EndsWith("&")) {
|
|
xCmd = xCmd.Substring(0, xCmd.Length - 1);
|
|
}
|
|
if (xCmd == "disassemble") {
|
|
OnDisassemble(aResponse);
|
|
} else if (xCmd == "symbol-file") { // nothing
|
|
} else if (xCmd == "set") { // nothing
|
|
} else if (xCmd == "target") {
|
|
|
|
if (Global.GDB.Connected)
|
|
{
|
|
mitmRefresh.Enabled = true;
|
|
lablConnected.Visible = true;
|
|
lablRunning.Visible = true;
|
|
|
|
mitmConnect.Enabled = true;
|
|
butnConnect.Enabled = true;
|
|
mitmConnect.Text = "&Disconnect";
|
|
butnConnect.Text = "&Disconnect";
|
|
|
|
Settings.InitWindows();
|
|
|
|
lboxDisassemble.SetItems(Global.AsmSource.Lines);
|
|
}
|
|
else
|
|
{
|
|
if (mConnectRetry < MAX_RETRY + 1)
|
|
{
|
|
Connect();
|
|
}
|
|
else
|
|
{
|
|
mitmConnect.Enabled = true;
|
|
butnConnect.Enabled = true;
|
|
mitmConnect.Text = "&Connect";
|
|
butnConnect.Text = "&Connect";
|
|
lboxDisassemble.Items.Clear();
|
|
}
|
|
}
|
|
} else if (xCmd == "detach") {
|
|
if (false == Global.GDB.Connected)
|
|
{
|
|
mitmConnect.Text = "&Connect";
|
|
butnConnect.Text = "&Connect";
|
|
mitmRefresh.Enabled = false;
|
|
mitmContinue.Enabled = false;
|
|
butnContinue.Enabled = false;
|
|
mitmStepInto.Enabled = false;
|
|
mitmStepOver.Enabled = false;
|
|
lboxDisassemble.Items.Clear();
|
|
lablConnected.Visible = false;
|
|
lablRunning.Visible = false;
|
|
textCurrentFunction.Visible = false;
|
|
}
|
|
} else if (xCmd == "delete") {
|
|
Windows.mBreakpointsForm.OnDelete(aResponse);
|
|
} else if ((xCmd == "stepi") || (xCmd == "nexti")) {
|
|
} else if (xCmd == "continue" || xCmd== "fg") {
|
|
//lboxDisassemble.Items.Clear();
|
|
} else if (xCmd == "where") {
|
|
Windows.mCallStackForm.OnWhere(aResponse);
|
|
} else if (xCmd == "break") {
|
|
Windows.mBreakpointsForm.OnBreak(aResponse);
|
|
} else if (xCmd.StartsWith("x/")) {
|
|
Windows.mWatchesForm.OnWatchUpdate(aResponse);
|
|
} else {
|
|
throw new Exception("Unrecognized command response: " + aResponse.Command);
|
|
}
|
|
}
|
|
} catch (Exception e) {
|
|
MessageBox.Show("Exception: " + e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
|
|
}
|
|
}
|
|
|
|
public void Disassemble(string aLabel) {
|
|
textCurrentFunction.Text = string.Empty;
|
|
textCurrentFunction.Visible = true;
|
|
// force space free at end
|
|
var xDisAsmCmd = "disassemble";
|
|
var xLabelTrimed = aLabel.TrimEnd();
|
|
if (xLabelTrimed.Length > 0)
|
|
xDisAsmCmd += " " + xLabelTrimed;
|
|
Global.GDB.SendCmd(xDisAsmCmd);
|
|
}
|
|
|
|
protected void OnDisassemble(GDB.Response xResponse)
|
|
{
|
|
var xResult = xResponse.Text;
|
|
// In some cases GDB might return no results. This is common when no symbols are loaded.
|
|
if (xResult.Count == 0)
|
|
return;
|
|
// Get function name
|
|
var xSplit = GDB.Unescape(xResult[0]).Split(Global.SpaceSeparator, StringSplitOptions.RemoveEmptyEntries);
|
|
mFuncName = xSplit[xSplit.Length - 1];
|
|
textCurrentFunction.Text = mFuncName;
|
|
|
|
// remove ':'
|
|
mFuncName = mFuncName.Substring(0, mFuncName.Length - 1);
|
|
|
|
int labelLine = Global.AsmSource.GetLineOfLabel(mFuncName);
|
|
labelLine++;
|
|
|
|
// 1 and -2 to eliminate header and footer line
|
|
for (int i = 1; i <= xResult.Count - 2; i++, labelLine++)
|
|
{
|
|
var asmLine = Global.AsmSource.Lines[labelLine];
|
|
|
|
while (asmLine.IsLabel ||
|
|
(asmLine.FirstToken != null && (asmLine.FirstToken == "global" || asmLine.FirstToken.StartsWith(";"))))
|
|
{
|
|
labelLine++;
|
|
asmLine = Global.AsmSource.Lines[labelLine];
|
|
}
|
|
|
|
var gdbLine = new GdbAsmLine(xResult[i]);
|
|
asmLine.Address = gdbLine.mAddr;
|
|
|
|
// check if line different, if so, we set a line for tooltip
|
|
string strGdbLine = gdbLine.ToString();
|
|
string gdbLineWithoutAddress = strGdbLine.Substring(strGdbLine.IndexOf(":") + 3);
|
|
string gdbLineWithoutAddressLower = gdbLineWithoutAddress.Replace(" ", string.Empty).ToLower().Replace("dwordptr",string.Empty);
|
|
|
|
string asmlineFromFile = asmLine.OrignalLine.TrimStart('\t', ' ').ToLower().Replace("dword", string.Empty);
|
|
string asmlineFromFileWithoutspace = asmlineFromFile.Replace(" ", string.Empty);
|
|
if (gdbLineWithoutAddressLower != asmlineFromFileWithoutspace)
|
|
{
|
|
asmLine.GDBLine = gdbLineWithoutAddress;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetEIP(UInt32 aAddr) {
|
|
lboxDisassemble.SelectedAddress = aAddr;
|
|
}
|
|
|
|
public FormMain() {
|
|
InitializeComponent();
|
|
}
|
|
|
|
// TODO
|
|
// watches
|
|
// View stack
|
|
// If close without connect, it wipes out the settings file
|
|
|
|
private void mitmExit_Click(object sender, EventArgs e) {
|
|
Close();
|
|
}
|
|
|
|
private void mitmStepInto_Click(object sender, EventArgs e) {
|
|
Global.GDB.SendCmd("stepi&");
|
|
}
|
|
|
|
private void mitmStepOver_Click(object sender, EventArgs e) {
|
|
Global.GDB.SendCmd("nexti&");
|
|
}
|
|
|
|
protected void Connect() {
|
|
if (Settings.OutputPath == null)
|
|
{
|
|
// path of asm, obj and cgdb
|
|
using(var xDialog = new OpenFileDialog())
|
|
{
|
|
xDialog.Filter = "Symbols (*.asm;*.obj;*.mdf)|*.asm;*.obj;*.mdf";
|
|
xDialog.ShowHelp = true;
|
|
xDialog.HelpRequest += new EventHandler(xDialog_HelpRequest);
|
|
|
|
if (xDialog.ShowDialog(this) != System.Windows.Forms.DialogResult.OK)
|
|
return;
|
|
|
|
if (false == Settings.LoadOnFly(xDialog.FileName))
|
|
{
|
|
MessageBox.Show("Error on loading selection!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1);
|
|
return;
|
|
}
|
|
mitmSave.Enabled = true;
|
|
}
|
|
}
|
|
|
|
mitmConnect.Enabled = false;
|
|
butnConnect.Enabled = false;
|
|
|
|
mitmConnect.Text = "Try " + mConnectRetry;
|
|
butnConnect.Text = "Try " + mConnectRetry++;
|
|
|
|
if (false == mCreated)
|
|
{
|
|
Windows.CreateForms();
|
|
Global.AsmSource = new AsmFile(Path.Combine(Settings.OutputPath, Settings.AsmFile));
|
|
Global.GDB = new GDB(OnGDBResponse, OnRunStateChanged);
|
|
mCreated = true;
|
|
}
|
|
Global.GDB.Connect();
|
|
}
|
|
|
|
void xDialog_HelpRequest(object sender, EventArgs e)
|
|
{
|
|
MessageBox.Show("Select a folder which contain files of type asm, obj and mdf with same name.", "Help", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
|
|
}
|
|
|
|
private void OnRunStateChanged(bool stopped)
|
|
{
|
|
if (InvokeRequired)
|
|
{
|
|
Invoke((MethodInvoker) delegate{OnRunStateChanged(stopped);});
|
|
return;
|
|
}
|
|
if (stopped)
|
|
{
|
|
lablRunning.Text = "Stopped";
|
|
mitmContinue.Enabled = true;
|
|
butnContinue.Enabled = true;
|
|
mitmBreak.Enabled = false;
|
|
butnBreak.Enabled = false;
|
|
mitmConnect.Enabled = true;
|
|
butnConnect.Enabled = true;
|
|
mitmStepInto.Enabled = true;
|
|
mitmStepOver.Enabled = true;
|
|
Windows.UpdateAllWindows();
|
|
}
|
|
else
|
|
{
|
|
lablRunning.Text = "Running";
|
|
mitmContinue.Enabled = false;
|
|
butnContinue.Enabled = false;
|
|
mitmBreak.Enabled = true;
|
|
butnBreak.Enabled = true;
|
|
mitmConnect.Enabled = false;
|
|
butnConnect.Enabled = false;
|
|
mitmStepInto.Enabled = false;
|
|
mitmStepOver.Enabled = false;
|
|
}
|
|
}
|
|
|
|
private void mitmConnect_Click(object sender, EventArgs e) {
|
|
if (!mitmConnect.Enabled)
|
|
return;
|
|
if (mCreated && Global.GDB.Connected)
|
|
{
|
|
Global.GDB.Disconnect();
|
|
}
|
|
else
|
|
{
|
|
mConnectRetry = 1;
|
|
Connect();
|
|
}
|
|
}
|
|
|
|
private void mitmRefresh_Click(object sender, EventArgs e) {
|
|
Windows.UpdateAllWindows();
|
|
}
|
|
|
|
private void mitmContinue_Click(object sender, EventArgs e) {
|
|
Global.GDB.SendCmd("continue&");
|
|
}
|
|
|
|
private void mitmMainViewCallStack_Click(object sender, EventArgs e) {
|
|
Windows.Show(Windows.mCallStackForm);
|
|
}
|
|
|
|
private void mitmMainViewWatches_Click(object sender, EventArgs e) {
|
|
Windows.Show(Windows.mWatchesForm);
|
|
}
|
|
|
|
protected FormWindowState mLastWindowState = FormWindowState.Normal;
|
|
private void FormMain_Resize(object sender, EventArgs e) {
|
|
if (WindowState == FormWindowState.Minimized) {
|
|
// Window is being minimized
|
|
Windows.Hide();
|
|
} else if ((mLastWindowState == FormWindowState.Minimized) && (WindowState != FormWindowState.Minimized)) {
|
|
// Window is being restored
|
|
Windows.Reshow();
|
|
}
|
|
mLastWindowState = WindowState;
|
|
}
|
|
|
|
private void mitmViewLog_Click(object sender, EventArgs e) {
|
|
Windows.Show(Windows.mLogForm);
|
|
}
|
|
|
|
private void FormMain_Load(object sender, EventArgs e) {
|
|
Windows.mMainForm = this;
|
|
}
|
|
|
|
private void mitmViewBreakpoints_Click(object sender, EventArgs e) {
|
|
Windows.Show(Windows.mBreakpointsForm);
|
|
}
|
|
|
|
private void mitmRegisters_Click(object sender, EventArgs e) {
|
|
Windows.Show(Windows.mRegistersForm);
|
|
}
|
|
|
|
private void FormMain_Shown(object sender, EventArgs e) {
|
|
mitmContinue.Enabled = false;
|
|
butnContinue.Enabled = false;
|
|
mitmBreak.Enabled = false;
|
|
butnBreak.Enabled = false;
|
|
mitmStepInto.Enabled = false;
|
|
mitmStepOver.Enabled = false;
|
|
mitmRefresh.Enabled = false;
|
|
|
|
if (Settings.OutputPath == null)
|
|
{
|
|
mitmSave.Enabled = false;
|
|
}
|
|
|
|
|
|
// Dont put this in load. Load happens in main call from Main.cs and on exceptions just
|
|
// goes out, no message.
|
|
// Also we want to show other forms after main form, not before.
|
|
// We also only want to run this once, not on each possible show.
|
|
if (mitmConnect.Enabled) {
|
|
if (Settings.AutoConnect) {
|
|
mConnectRetry = 1;
|
|
Connect();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void BringWindowsToTop() {
|
|
mIgnoreNextActivate = true;
|
|
foreach (var xWindow in Windows.mForms) {
|
|
if (xWindow == this) {
|
|
continue;
|
|
}
|
|
if (xWindow.Visible) {
|
|
xWindow.Activate();
|
|
}
|
|
}
|
|
this.Activate();
|
|
}
|
|
|
|
private void mitmWindowsToForeground_Click(object sender, EventArgs e) {
|
|
BringWindowsToTop();
|
|
}
|
|
|
|
private bool mIgnoreNextActivate = false;
|
|
private void FormMain_Activated(object sender, EventArgs e) {
|
|
// Necessary else we get looping becuase BringWindowsToTop reactivates this.
|
|
if (mIgnoreNextActivate) {
|
|
mIgnoreNextActivate = false;
|
|
} else {
|
|
BringWindowsToTop();
|
|
}
|
|
}
|
|
|
|
private void mitmSave_Click(object sender, EventArgs e) {
|
|
Settings.Save();
|
|
}
|
|
|
|
private void mitemDisassemblyAddBreakpoint_Click(object sender, EventArgs e) {
|
|
if (lboxDisassemble.SelectedIndices.Count == 0)
|
|
return;
|
|
var x = Global.AsmSource.Lines[lboxDisassemble.SelectedIndices[0]];
|
|
if (x.Address != 0) {
|
|
Windows.mBreakpointsForm.AddBreakpoint("*0x" + x.Address.ToString("X8"));
|
|
}
|
|
}
|
|
|
|
private void mitmCopyToClipboard_Click(object sender, EventArgs e) {
|
|
if (lboxDisassemble.SelectedIndices.Count == 0)
|
|
return;
|
|
var x = Global.AsmSource.Lines[lboxDisassemble.SelectedIndices[0]];
|
|
Clipboard.SetText(x.ToString());
|
|
}
|
|
|
|
private void butnBreakpoints_Click(object sender, EventArgs e) {
|
|
mitmViewBreakpoints.PerformClick();
|
|
}
|
|
|
|
private void textCurrentFunction_TextChanged(object sender, EventArgs e) {
|
|
base.OnTextChanged(e);
|
|
|
|
using (Graphics g = textCurrentFunction.CreateGraphics()) {
|
|
SizeF size = g.MeasureString(textCurrentFunction.Text, textCurrentFunction.Font);
|
|
textCurrentFunction.Width = (int)size.Width + textCurrentFunction.Padding.Horizontal;
|
|
}
|
|
}
|
|
|
|
private void mitmBreak_Click(object sender, EventArgs e) {
|
|
Global.GDB.SendCmd("-exec-interrupt");
|
|
}
|
|
|
|
private void FormMain_FormClosed(object sender, FormClosedEventArgs e)
|
|
{
|
|
if (mCreated && Global.GDB.Connected)
|
|
{
|
|
Global.GDB.Disconnect();
|
|
Global.GDB.SendCmd("quit");
|
|
}
|
|
}
|
|
}
|
|
} |