Newer
Older
alicedbg / src / adbg / dbg / context.d
@dd86k dd86k on 20 Mar 2021 10 KB context: small tweak
/**
 * License: BSD-3-Clause
 */
module adbg.dbg.context;

import adbg.dbg.debugger : g_debuggee;

version (Windows) {
	import adbg.sys.windows.wow64;
	import core.sys.windows.windows;
} else
version (Posix) {
	import adbg.sys.linux.user;
	import adbg.sys.posix.ptrace;
	import core.sys.posix.signal;
}

version (X86)
	version = X86_ANY;
else version (X86_64)
	version = X86_ANY;

extern (C):

version (X86) {
	enum EX_REG_COUNT = 10;	/// Number of registers for platform
} else version (X86_64) {
	enum EX_REG_COUNT = 18;	/// Number of registers for platform
} else
	static assert(0, "EX_REG_COUNT not defined");

/// Register size
enum RegisterType {
	u8, u16, u32, u64, f32, f64,
	flags_x86, flags_x86_64
}

/// Register structure, designs a single register for UI ends to understand
struct register_t {
	RegisterType type;	/// Register type (size)
	union {
		ubyte  u8;	/// Register data: ubyte (u8)
		ushort u16;	/// Register data: ushort (u16)
		uint   u32;	/// Register data: uint (u32)
		ulong  u64;	/// Register data: ulong (u64)
		float  f32;	/// Register data: float (f32)
		double f64;	/// Register data: double (f64)
	}
	const(char) *name;	/// Register name from adbg_ex_reg_init
}

/// Represents a thread context structure with the register values once a
/// process is paused.
struct thread_context_t {
	/// Register count in registers field, populated by
	/// adbg_ex_reg_init.
	uint count;
	/// Register population, this may depends by platform.
	register_t [EX_REG_COUNT]items;
	/// If context was initiated.
	bool initiated;
}

/// (Internal) Initiate register fields with their names and sizes.
/// Params: e = Exception structure
void adbg_ctx_init(thread_context_t *e) {
	version (X86) {
		adbg_ctx_init_x86(e);
	} else version (X86_64) {
		version (Win64) {
			if (g_debuggee.wow64)
				adbg_ctx_init_x86(e);
			else
				adbg_ctx_init_x86_64(e);
		} else
			adbg_ctx_init_x86_64(e);
	}
}

/// (Internal) Get the thread context from debuggee
/// Params: ctx = Thread context structure pointer
void adbg_ctx_get(thread_context_t *ctx) {
	version (Windows) {
		CONTEXT winctx = void;
		version (Win64) {
			WOW64_CONTEXT winctxwow64 = void;
			if (g_debuggee.wow64) {
				winctxwow64.ContextFlags = CONTEXT_ALL;
				if (Wow64GetThreadContext(g_debuggee.htid, &winctxwow64) == FALSE) {
					ctx.count = 0;
					return;
				}
				adbg_ctx_os_wow64(ctx, &winctxwow64);
			} else {
				winctx.ContextFlags = CONTEXT_ALL;
				if (GetThreadContext(g_debuggee.htid, &winctx)) {
					ctx.count = 0;
					return;
				}
				adbg_ctx_os(ctx, &winctx);
			}
		} else {
			winctx.ContextFlags = CONTEXT_ALL;
			if (GetThreadContext(g_debuggee.htid, &winctx)) {
				ctx.count = 0;
				return;
			}
			adbg_ctx_os(ctx, &winctx);
		}
	} else
	version (Posix) {
		//TODO: PTRACE_GETFPREGS
		user_regs_struct u = void;
		if (ptrace(PTRACE_GETREGS, g_debuggee.pid, null, &u) < 0) {
			ctx.count = 0;
			return;
		}
		adbg_ctx_os(ctx, &u);
	}
}

//TODO: adbg_ctx_set

/// Format a register depending on their type as a zero-padded number.
/// Params: reg = register_t structure
/// Returns: Formatted hexadecimal string
const(char) *adbg_ctx_reg_hex(register_t *reg) {
	import adbg.utils.str : adbg_util_strf;
	with (RegisterType)
	switch (reg.type) {
	case u8:
		return adbg_util_strf("%02x", reg.u8);
	case u16:
		return adbg_util_strf("%04x", reg.u16);
	case u32, f32, flags_x86:
		return adbg_util_strf("%08x", reg.u32);
	case u64, f64, flags_x86_64:
		return adbg_util_strf("%016llx", reg.u64);
	default:
		assert(0);
	}
}

/// Format a register's context with their formatted value.
/// Params: reg = register_t structure
/// Returns: Formatted string
const(char) *adbg_ctx_reg_val(register_t *reg) {
	import adbg.utils.str : adbg_util_strf, empty_string;
	import adbg.utils.bit : BIT;
	enum F_X86_CF = BIT!(0);
	enum F_X86_PF = BIT!(2);
	enum F_X86_AF = BIT!(4);
	enum F_X86_ZF = BIT!(6);
	enum F_X86_SF = BIT!(7);
	enum F_X86_TF = BIT!(8);
	enum F_X86_IF = BIT!(9);
	enum F_X86_DF = BIT!(10);
	enum F_X86_OF = BIT!(11);
	enum F_X86_IOPL = BIT!(12) | BIT!(13);
	enum F_X86_NT = BIT!(14);
	enum F_X86_RF = BIT!(16);
	enum F_X86_VM = BIT!(17);
	enum F_X86_AC = BIT!(18);
	enum F_X86_VIF = BIT!(19);
	enum F_X86_VIP = BIT!(20);
	enum F_X86_ID = BIT!(21);
	__gshared const(char) *FS_X86_CF	= " CF";
	__gshared const(char) *FS_X86_PF	= " PF";
	__gshared const(char) *FS_X86_AF	= " AF";
	__gshared const(char) *FS_X86_ZF	= " ZF";
	__gshared const(char) *FS_X86_SF	= " SF";
	__gshared const(char) *FS_X86_TF	= " TF";
	__gshared const(char) *FS_X86_IF	= " IF";
	__gshared const(char) *FS_X86_DF	= " DF";
	__gshared const(char) *FS_X86_OF	= " OF";
	__gshared const(char) *FS_X86_NT	= " NT";
	__gshared const(char) *FS_X86_RF	= " RF";
	__gshared const(char) *FS_X86_VM	= " VM";
	__gshared const(char) *FS_X86_AC	= " AC";
	__gshared const(char) *FS_X86_VIF	= " VIF";
	__gshared const(char) *FS_X86_VIP	= " VIP";
	__gshared const(char) *FS_X86_ID	= " ID";
	with (RegisterType)
	switch (reg.type) {
	case u8:	return adbg_util_strf("%u", reg.u8);
	case u16:	return adbg_util_strf("%u", reg.u16);
	case u32:	return adbg_util_strf("%u", reg.u32);
	case u64:	return adbg_util_strf("%llu", reg.u64);
	case f32:	return adbg_util_strf("%f", reg.f32);
	case f64:	return adbg_util_strf("%f", reg.f64);
	case flags_x86, flags_x86_64:
		uint f = reg.u32;
		return adbg_util_strf(
			"[%s%s%s%s%s%s%s%s%s IOPL=%d%s%s%s%s%s%s ]",
			f & F_X86_CF ? FS_X86_CF : empty_string,
			f & F_X86_PF ? FS_X86_PF : empty_string,
			f & F_X86_AF ? FS_X86_AF : empty_string,
			f & F_X86_ZF ? FS_X86_ZF : empty_string,
			f & F_X86_SF ? FS_X86_SF : empty_string,
			f & F_X86_TF ? FS_X86_TF : empty_string,
			f & F_X86_IF ? FS_X86_IF : empty_string,
			f & F_X86_DF ? FS_X86_DF : empty_string,
			f & F_X86_OF ? FS_X86_OF : empty_string,
			(f & F_X86_IOPL) >> 12,
			f & F_X86_NT ? FS_X86_NT : empty_string,
			f & F_X86_RF ? FS_X86_RF : empty_string,
			f & F_X86_VM ? FS_X86_VM : empty_string,
			f & F_X86_AC ? FS_X86_AC : empty_string,
			f & F_X86_VIF ? FS_X86_VIF : empty_string,
			f & F_X86_VIP ? FS_X86_VIP : empty_string,
			f & F_X86_ID ? FS_X86_ID : empty_string,
			);
	default:	assert(0);
	}
}

version (X86_ANY)
private void adbg_ctx_init_x86(thread_context_t *ctx) {
	ctx.count = EX_REG_COUNT;
	ctx.items[0].name = "eip";
	ctx.items[0].type = RegisterType.u32;
	ctx.items[1].name = "eflags";
	ctx.items[1].type = RegisterType.flags_x86;
	ctx.items[2].name = "eax";
	ctx.items[2].type = RegisterType.u32;
	ctx.items[3].name = "ebx";
	ctx.items[3].type = RegisterType.u32;
	ctx.items[4].name = "ecx";
	ctx.items[4].type = RegisterType.u32;
	ctx.items[5].name = "edx";
	ctx.items[5].type = RegisterType.u32;
	ctx.items[6].name = "esp";
	ctx.items[6].type = RegisterType.u32;
	ctx.items[7].name = "ebp";
	ctx.items[7].type = RegisterType.u32;
	ctx.items[8].name = "esi";
	ctx.items[8].type = RegisterType.u32;
	ctx.items[9].name = "edi";
	ctx.items[9].type = RegisterType.u32;
}

version (X86_64)
private void adbg_ctx_init_x86_64(thread_context_t *ctx) {
	ctx.count = EX_REG_COUNT;
	ctx.items[0].name  = "rip";
	ctx.items[0].type  = RegisterType.u64;
	ctx.items[1].name  = "rflags";
	ctx.items[1].type  = RegisterType.flags_x86_64;
	ctx.items[2].name  = "rax";
	ctx.items[2].type  = RegisterType.u64;
	ctx.items[3].name  = "rbx";
	ctx.items[3].type  = RegisterType.u64;
	ctx.items[4].name  = "rcx";
	ctx.items[4].type  = RegisterType.u64;
	ctx.items[5].name  = "rdx";
	ctx.items[5].type  = RegisterType.u64;
	ctx.items[6].name  = "rsp";
	ctx.items[6].type  = RegisterType.u64;
	ctx.items[7].name  = "rbp";
	ctx.items[7].type  = RegisterType.u64;
	ctx.items[8].name  = "rsi";
	ctx.items[8].type  = RegisterType.u64;
	ctx.items[9].name  = "rdi";
	ctx.items[9].type  = RegisterType.u64;
	ctx.items[10].name = "r8";
	ctx.items[10].type = RegisterType.u64;
	ctx.items[11].name = "r9";
	ctx.items[11].type = RegisterType.u64;
	ctx.items[12].name = "r10";
	ctx.items[12].type = RegisterType.u64;
	ctx.items[13].name = "r11";
	ctx.items[13].type = RegisterType.u64;
	ctx.items[14].name = "r12";
	ctx.items[14].type = RegisterType.u64;
	ctx.items[15].name = "r13";
	ctx.items[15].type = RegisterType.u64;
	ctx.items[16].name = "r14";
	ctx.items[16].type = RegisterType.u64;
	ctx.items[17].name = "r15";
	ctx.items[17].type = RegisterType.u64;
}

version (Windows) {
	//
	// ANCHOR Windows functions
	//

	// Populate exception_t.registers array from Windows' CONTEXT
	void adbg_ctx_os(thread_context_t *ctx, CONTEXT *winctx) {
		version (X86) {
			ctx.items[0].u32 = winctx.Eip;
			ctx.items[1].u32 = winctx.EFlags;
			ctx.items[2].u32 = winctx.Eax;
			ctx.items[3].u32 = winctx.Ebx;
			ctx.items[4].u32 = winctx.Ecx;
			ctx.items[5].u32 = winctx.Edx;
			ctx.items[6].u32 = winctx.Esp;
			ctx.items[7].u32 = winctx.Ebp;
			ctx.items[8].u32 = winctx.Esi;
			ctx.items[9].u32 = winctx.Edi;
		} else
		version (X86_64) {
			ctx.items[0].u64  = winctx.Rip;
			ctx.items[1].u64  = winctx.EFlags;
			ctx.items[2].u64  = winctx.Rax;
			ctx.items[3].u64  = winctx.Rbx;
			ctx.items[4].u64  = winctx.Rcx;
			ctx.items[5].u64  = winctx.Rdx;
			ctx.items[6].u64  = winctx.Rsp;
			ctx.items[7].u64  = winctx.Rbp;
			ctx.items[8].u64  = winctx.Rsi;
			ctx.items[9].u64  = winctx.Rdi;
			ctx.items[10].u64 = winctx.R8;
			ctx.items[11].u64 = winctx.R9;
			ctx.items[12].u64 = winctx.R10;
			ctx.items[13].u64 = winctx.R11;
			ctx.items[14].u64 = winctx.R12;
			ctx.items[15].u64 = winctx.R13;
			ctx.items[16].u64 = winctx.R14;
			ctx.items[17].u64 = winctx.R15;
		}
	}

	version (Win64)
	package void adbg_ctx_os_wow64(thread_context_t *ctx, WOW64_CONTEXT *winctx) {
		ctx.items[0].u32 = winctx.Eip;
		ctx.items[1].u32 = winctx.EFlags;
		ctx.items[2].u32 = winctx.Eax;
		ctx.items[3].u32 = winctx.Ebx;
		ctx.items[4].u32 = winctx.Ecx;
		ctx.items[5].u32 = winctx.Edx;
		ctx.items[6].u32 = winctx.Esp;
		ctx.items[7].u32 = winctx.Ebp;
		ctx.items[8].u32 = winctx.Esi;
		ctx.items[9].u32 = winctx.Edi;
	}
} else version (Posix) {
	//
	// ANCHOR Posix functions
	//

	/// Populate exception_t.registers array from user_regs_struct
	package void adbg_ctx_os(thread_context_t *ctx, user_regs_struct *u) {
		version (X86) {
			ctx.items[0].u32 = u.eip;
			ctx.items[1].u32 = u.eflags;
			ctx.items[2].u32 = u.eax;
			ctx.items[3].u32 = u.ebx;
			ctx.items[4].u32 = u.ecx;
			ctx.items[5].u32 = u.edx;
			ctx.items[6].u32 = u.esp;
			ctx.items[7].u32 = u.ebp;
			ctx.items[8].u32 = u.esi;
			ctx.items[9].u32 = u.edi;
		} else
		version (X86_64) {
			ctx.items[0].u64 = u.rip;
			ctx.items[1].u64 = u.eflags;
			ctx.items[2].u64 = u.rax;
			ctx.items[3].u64 = u.rbx;
			ctx.items[4].u64 = u.rcx;
			ctx.items[5].u64 = u.rdx;
			ctx.items[6].u64 = u.rsp;
			ctx.items[7].u64 = u.rbp;
			ctx.items[8].u64 = u.rsi;
			ctx.items[9].u64 = u.rdi;
			ctx.items[10].u64 = u.r8;
			ctx.items[11].u64 = u.r9;
			ctx.items[12].u64 = u.r10;
			ctx.items[13].u64 = u.r11;
			ctx.items[14].u64 = u.r12;
			ctx.items[15].u64 = u.r13;
			ctx.items[16].u64 = u.r14;
			ctx.items[17].u64 = u.r15;
		}
	}
} // version Posix