/*
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 "DebuggerCore.h"

#include <sys/ptrace.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <cerrno>
#include <cassert>
#include <signal.h>
#include <endian.h>
#include <signal.h>
#include <unistd.h>

#ifndef PTRACE_GETSIGINFO
#define PTRACE_GETSIGINFO	(__ptrace_request)0x4202
#endif

#define SUPPORT_TIMEOUTS

//------------------------------------------------------------------------------
// Name: getFaultAddress()
// Desc: gets the address of a segfault (Linux >= 2.3.99-pre6)
//------------------------------------------------------------------------------
BaseTypes::address_t DebuggerCore::getFaultAddress() {
	siginfo_t info;
	BaseTypes::address_t ret = static_cast<BaseTypes::address_t>(-1);
	if(m_PID != 0) {
		ptrace(PTRACE_GETSIGINFO, m_PID, 0, &info);
		ret = reinterpret_cast<BaseTypes::address_t>(info.si_addr);
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: executionBreakPointSize() const
// Desc: returns the size of a breakpoint
//------------------------------------------------------------------------------
int DebuggerCore::executionBreakPointSize() const {
	return 1;
}

//------------------------------------------------------------------------------
// Name: executionBreakPointData() const
// Desc: returns the value of a breakpoint
//------------------------------------------------------------------------------
QByteArray DebuggerCore::executionBreakPointData() const {
	return QByteArray(1, 0xcc);
}



//------------------------------------------------------------------------------
// Name: DebuggerCore()
// Desc: constructor
//------------------------------------------------------------------------------
DebuggerCore::DebuggerCore() : m_PID(0) {
}

//------------------------------------------------------------------------------
// Name: ~DebuggerCore()
// Desc: destructor
//------------------------------------------------------------------------------
DebuggerCore::~DebuggerCore() {
	detach();
}

//------------------------------------------------------------------------------
// Name: waitForDebugEvent(DebugEvent *event, bool &ok, int secs)
// Desc: waits for a debug event, secs is a timeout (but is not yet respected)
//       ok will be set to false if the timeout expires
//------------------------------------------------------------------------------
void DebuggerCore::waitForDebugEvent(DebugEvent *event, bool &ok, int secs) {
	Q_UNUSED(secs);
	ok = false;
	if(m_PID != 0) {

		int status = 0;
		const int ret = waitpid(m_PID, &status, 0);
		ok = (ret != -1 && errno != EINTR);
		if(ok && event != 0) {
			*event = DebugEvent(status);
		}
	}
}

//------------------------------------------------------------------------------
// Name: readBytes(BaseTypes::address_t address, void *buf, std::size_t len)
// Desc: reads <len> bytes into <buf> starting at <address>
//------------------------------------------------------------------------------
bool DebuggerCore::readBytes(BaseTypes::address_t address, void *buf, std::size_t len) {
	uint8_t *p = reinterpret_cast<uint8_t *>(buf);
	bool ok = true;
	int bytesRead = 0;
	
	while(ok && len--) {
		*p++ = readByte(address++, &ok);
		if(ok) {
			++bytesRead;
		}
	}
	
	if(!ok) {
		while(len--) {
			*p++ = 0xff;
		}
	}
	
	return ok;
}

//------------------------------------------------------------------------------
// Name: writeBytes(BaseTypes::address_t address, const void *buf, std::size_t len)
// Desc: writes <len> bytes from <buf> starting at <address>
//------------------------------------------------------------------------------
bool DebuggerCore::writeBytes(BaseTypes::address_t address, const void *buf, std::size_t len) {
	const uint8_t *p = reinterpret_cast<const uint8_t *>(buf);
	bool ok = true;
	while(ok && len--) {
		writeByte(address++, *p++, &ok);
	}
	
	return ok;
}


//------------------------------------------------------------------------------
// Name: writeByte(BaseTypes::address_t address, uint8_t value, bool *ok)
// Desc: writes a single byte at a given address
//------------------------------------------------------------------------------
void DebuggerCore::writeByte(BaseTypes::address_t address, uint8_t value, bool *ok) {
	bool readOK;
	writeByteBase(address, value, readOK);
	
	// TODO: warn user if they just overwrote a break point!
	
	if(ok != 0) {
		*ok = readOK;
	}
}

//------------------------------------------------------------------------------
// Name: readByte(BaseTypes::address_t address, bool *ok)
// Desc: reads a single bytes at a given address
//------------------------------------------------------------------------------
uint8_t DebuggerCore::readByte(BaseTypes::address_t address, bool *ok) {

	// TODO: handle if breakponts have a size more than 1!
	bool readOK;
	uint8_t ret = readByteBase(address, readOK);
	if(readOK) {
		BreakPointState::const_iterator i = m_BreakPoints.find(address);
		if(i != m_BreakPoints.end()) {
			ret = i.value().originalBytes[0];
		}
	}
	
	if(ok != 0) {
		*ok = readOK;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: writeByteBase(BaseTypes::address_t address, uint8_t value, bool &ok)
// Desc: the base implementation of writing a byte
//------------------------------------------------------------------------------
void DebuggerCore::writeByteBase(BaseTypes::address_t address, uint8_t value, bool &ok) {
	// TOOD: assert that we are paused
	// TODO: make handle non-32-bit words
	// TODO: don't assume little endian

	ok = true;
	if(m_PID != 0) {
	
		long v;
	
		// this switch is to deal with the fact that since we
		// only really write a word at a time, that we have issues writing
		// near a region boundary, so whenever we get a request sufficiently
		// close to a region page boundary (regions are always page aligned)
		// we read the very last word on the page and make adjustments to make
		// it line as up desired
		switch(address & 0xfff) {
		case 0xffd:
			v = (ptrace(PTRACE_PEEKTEXT, m_PID, address - 1, 0) & 0xffff00ff) | value << 8;
			ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			if(ok) {
				v = ptrace(PTRACE_POKETEXT, m_PID, address - 1, v);
				ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			}
			break;
		case 0xffe:
			v = (ptrace(PTRACE_PEEKTEXT, m_PID, address - 2, 0) & 0xff00ffff) | value << 16;
			ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			if(ok) {
				v = ptrace(PTRACE_POKETEXT, m_PID, address - 2, v);
				ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			}
			break;
		case 0xfff:
			v = (ptrace(PTRACE_PEEKTEXT, m_PID, address - 3, 0) & 0x00ffffff) | value << 24;
			ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			if(ok) {
				v = ptrace(PTRACE_POKETEXT, m_PID, address - 3, v);
				ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			}
			break;
		default:
			v = (ptrace(PTRACE_PEEKTEXT, m_PID, address - 0, 0) & 0xffffff00) | value;
			ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			if(ok) {
				v = ptrace(PTRACE_POKETEXT, m_PID, address - 0, v);
				ok = (v != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
			}
			break;
		}
	}
}

//------------------------------------------------------------------------------
// Name: readByteBase(BaseTypes::address_t address, bool &ok)
// Desc: the base implementation of reading a byte
//------------------------------------------------------------------------------
uint8_t DebuggerCore::readByteBase(BaseTypes::address_t address, bool &ok) {
	// TOOD: assert that we are paused

	uint8_t ret = 0xff;
	ok = true;
	errno = -1;
	if(m_PID != 0) {
	
		// if this spot is unreadable, then just return 0xff, otherwise
		// continue as normal and hide any breakpoints we may have
		
		long value;
		
		// this switch is to deal with the fact that since we
		// only really read a word at a time, that we have issues reading
		// near a region boundary, so whenever we get a request sufficiently
		// close to a region page boundary (regions are always page aligned)
		// we read the very last word on the page and make adjustments to make
		// it line as up desired
		switch(address & 0xfff) {
		case 0xffd:
			value = ptrace(PTRACE_PEEKTEXT, m_PID, address - 1, 0);
			value >>= 8;
			value &= 0x00ffffff;
			break;
		case 0xffe:
			value = ptrace(PTRACE_PEEKTEXT, m_PID, address - 2, 0);
			value >>= 16;
			value &= 0x0000ffff;
			break;
		case 0xfff:
			value = ptrace(PTRACE_PEEKTEXT, m_PID, address - 3, 0);
			value >>= 24;
			value &= 0x000000ff;
			break;
		default:
			value = ptrace(PTRACE_PEEKTEXT, m_PID, address, 0);
			break;
		}
		
		ok = (value != -1) || (errno != EBUSY && errno != EFAULT && errno != EIO && errno != ESRCH);
		
		// TODO: make handle non-32-bit words
#if __BYTE_ORDER == __LITTLE_ENDIAN
		ret = value & 0xff;
#else
		ret = (value >> 24) & 0xff;
#endif	
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: attach(pid_t pid)
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerCore::attach(pid_t pid) {
	detach();
	
	const long ret = ptrace(PTRACE_ATTACH, pid, 0, 0);
	if(ret == 0) {
		m_PID = pid;
		bool ok;
		waitForDebugEvent(0, ok, INT_MAX);
	}
	
	return ret == 0;
}

//------------------------------------------------------------------------------
// Name: detach()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::detach() {
	if(m_PID != 0) {
	
		// remove all the breakpoints we had set
		foreach (BreakPoint bp, m_BreakPoints) {
			removeBreakpoint(bp.address);
		}
	
		m_BreakPoints.clear();
		
		ptrace(PTRACE_DETACH, m_PID, 0, 0);
		
		m_PID = 0;
	}
}

//------------------------------------------------------------------------------
// Name: kill()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::kill() {
	if(m_PID != 0) {
	
		// remove all the breakpoints we had set
		foreach (BreakPoint bp, m_BreakPoints) {
			removeBreakpoint(bp.address);
		}
	
		m_BreakPoints.clear();
		
		::kill(m_PID, SIGKILL);
		
		m_PID = 0;		
	}
}

//------------------------------------------------------------------------------
// Name: addBreakpoint(BaseTypes::address_t address, bool oneTime)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::addBreakpoint(BaseTypes::address_t address, bool oneTime) {

	// TODO: assert paused

	if(findBreakPoint(address) == 0) {
		// backup original bytes
		char prev[executionBreakPointSize()];
		if(readBytes(address, prev, executionBreakPointSize())) {

			// write the breakpoint
			if(writeBytes(address, executionBreakPointData(), executionBreakPointSize())) {

				// create accounting structure
				BreakPoint data;
				data.address = address;
				data.setOneTime(oneTime);
				data.setEnabled(true);
				data.originalBytes	= QByteArray(prev, executionBreakPointSize());

				m_BreakPoints[address] = data;
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: findBreakPoint(BaseTypes::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
const BreakPoint *DebuggerCore::findBreakPoint(BaseTypes::address_t address) const {
	BreakPointState::const_iterator i = m_BreakPoints.find(address);
	return (i != m_BreakPoints.end()) ? &(i.value()) : 0;
}


//------------------------------------------------------------------------------
// Name: removeBreakpoint(BaseTypes::address_t address)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::removeBreakpoint(BaseTypes::address_t address) {

	// TODO: assert paused

	BreakPointState::iterator i = m_BreakPoints.find(address);
	if(i != m_BreakPoints.end()) {
		// this is an existing breakpoint
		if(writeBytes(address, i.value().originalBytes, executionBreakPointSize())) {
			m_BreakPoints.erase(i);
		}
	}
}

//------------------------------------------------------------------------------
// Name: disableBreakpoint(BaseTypes::address_t address)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::disableBreakpoint(BaseTypes::address_t address) {

	// TODO: assert paused

	BreakPointState::iterator i = m_BreakPoints.find(address);
	if(i != m_BreakPoints.end()) {
		// this is an existing breakpoint
		if(writeBytes(address, i.value().originalBytes, executionBreakPointSize())) {
			i.value().setEnabled(false);
		}
	}
}

//------------------------------------------------------------------------------
// Name: enableBreakpoint(BaseTypes::address_t address)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::enableBreakpoint(BaseTypes::address_t address) {

	// TODO: assert paused

	BreakPointState::iterator i = m_BreakPoints.find(address);
	if(i != m_BreakPoints.end()) {
		if(!i.value().enabled()) {
		
			char prev[executionBreakPointSize()];
			if(readBytes(address, prev, executionBreakPointSize())) {
		
				if(writeBytes(address, executionBreakPointData(), executionBreakPointSize())) {
					i.value().setEnabled(true);
					i.value().originalBytes = QByteArray(prev, executionBreakPointSize());
				}
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: backupBreakPoints(BreakPointState &state) const
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::backupBreakPoints(BreakPointState &state) const {
	state = m_BreakPoints;
}

//------------------------------------------------------------------------------
// Name: pause()
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::pause() {

	if(m_PID != 0) {
		::kill(m_PID, SIGSTOP);
	}
}

//------------------------------------------------------------------------------
// Name: resume(int signum)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::resume(int signum) {
	// TOOD: assert that we are paused
	
	if(m_PID != 0) {
		ptrace(PTRACE_CONT, m_PID, 0, reinterpret_cast<void *>(signum));
	}
}

//------------------------------------------------------------------------------
// Name: step(int signum)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::step(int signum) {
	// TOOD: assert that we are paused
	
	if(m_PID != 0) {
		ptrace(PTRACE_SINGLESTEP, m_PID, 0, reinterpret_cast<void *>(signum));
	}
}

//------------------------------------------------------------------------------
// Name: getState(State &state)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::getState(State &state) {
	// TOOD: assert that we are paused
	
	user_regs_struct regs;
	if((m_PID != 0) && (ptrace(PTRACE_GETREGS, m_PID, 0, &regs) != -1)) {	
		state.eax = regs.eax;
		state.ebx = regs.ebx;
		state.ecx = regs.ecx;
		state.edx = regs.edx;
		state.esp = regs.esp;
		state.ebp = regs.ebp;
		state.edi = regs.edi;
		state.esi = regs.esi;
		state.eip = regs.eip;
		state.eflags = regs.eflags;
		state.cs = regs.xcs;
		state.ds = regs.xds;
		state.es = regs.xes;
		state.fs = regs.xfs;
		state.gs = regs.xgs;
		state.ss = regs.xss;
		state.orig_eax = regs.orig_eax;
	} else {
		std::memset(&state, 0, sizeof(State));
	}
}

//------------------------------------------------------------------------------
// Name: setState(const State &state)
// Desc: 
//------------------------------------------------------------------------------
void DebuggerCore::setState(const State &state) {
	
	// TOOD: assert that we are paused
	if(m_PID != 0) {
		user_regs_struct regs;
		
		std::memset(&regs, 0, sizeof(user_regs_struct));
		
		regs.eax = state.eax;
		regs.ebx = state.ebx;
		regs.ecx = state.ecx;
		regs.edx = state.edx;
		regs.esp = state.esp;
		regs.ebp = state.ebp;
		regs.edi = state.edi;
		regs.esi = state.esi;
		regs.eip = state.eip;
		regs.eflags = state.eflags;
		regs.xcs = state.cs;
		regs.xds = state.ds;
		regs.xes = state.es;
		regs.xfs = state.fs;
		regs.xgs = state.gs;
		regs.xss = state.ss;
		regs.orig_eax = state.orig_eax;
		
		ptrace(PTRACE_SETREGS, m_PID, 0, &regs);
	}
}

//------------------------------------------------------------------------------
// Name: open(const QString &path, const QString &cwd, const QString &args)
// Desc: 
//------------------------------------------------------------------------------
bool DebuggerCore::open(const QString &path, const QString &cwd, const QString &args) {
	detach();
	pid_t pid;
	bool ret = false;

	switch(pid = fork()) {
	case 0:
	
		// set ourselves (the child proc) up to be traced
		ptrace(PTRACE_TRACEME, 0, 0, 0);
		
		chdir(cwd.toAscii());
		
		do {
						
			// we support up to 1024 arguments
			char *argv_pointers[1024];
			int argv_index = 1;

			// allocate a buffer for the whole command
			// no need to free it since this memory will be reclaimed when the execv
			// replaces the core image of this process
			const size_t sz = path.length() + 1 + args.length() + 1;
			char *const argv = new char[sz];
			snprintf(argv, sz, "%s %s", path.toAscii().data(), args.toAscii().data());
			
			// setup argv[0]
			argv_pointers[0] = &argv[0];
			char *argv_iterator = &argv[path.length() + 1];
			argv_iterator[-1] = '\0';
			
			// setup argv[1]
			while(*argv_iterator != '\0') {
				
				// eat up preceding white space
				while(std::isspace(*argv_iterator)) {
					++argv_iterator;
				}
				
				// ok we started to read non-white space
				// if it's not the end of the string
				// we should store it in our array
				if(*argv_iterator != '\0') {
					argv_pointers[argv_index++] = argv_iterator;
				}
				
				
				// eat up this param!
				while(*argv_iterator != '\0' && !std::isspace(*argv_iterator)) {			
					++argv_iterator;
				}
				
				// if we stopped cause of white space, terminate it!
				if(*argv_iterator != '\0') {
					*argv_iterator++ = '\0';
				}
			}

			argv_pointers[argv_index] = 0;
			execv(argv_pointers[0], &argv_pointers[0]);
			
			// no need to cleanup, the process which allocated all that space no longer exists!
		} while(0);
		// we should never get here!
		abort();
		break;
	case -1:
		// error!
		break;
	default:
		// parent
		do {
			m_PID = pid;
			DebugEvent event;
			bool ok;
			
			waitForDebugEvent(&event, ok, INT_MAX);
			
			if(event.reason() == DebugEvent::EVENT_STOPPED) {
				switch(event.stopSignal()) {
				case SIGABRT:
					m_PID = 0;
					break;
				default:
					ret = true;
					break;
				}
			} else {
				m_PID = 0;
			}
		} while(0);
		break;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: pid() const
// Desc: returns the pid of the currently debugged process
//------------------------------------------------------------------------------
BaseTypes::pid_t DebuggerCore::pid() const {
	return m_PID;
}

Q_EXPORT_PLUGIN2(DebuggerCorePtrace, DebuggerCore)
