/*
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 "Analyzer.h"
#include "PluginAPI.h"
#include "State.h"
#include "Util.h"
#include "MD5.h"
#include "DebuggerCoreInterface.h"
#include "ArchProcessorInterface.h"
#include "SymbolManager.h"
#include "MemoryRegions.h"
#include "Instruction.h"

#include <QMenu>
#include <QProgressDialog>
#include <QCoreApplication>
#include <QtDebug>

#include <boost/smart_ptr.hpp>
#include <boost/bind.hpp>

#if QT_VERSION >= 0x040400
#include <QtConcurrentMap>
//#define USE_QT_CONCURRENT
#endif

#include <cstring>

#ifdef Q_OS_LINUX
#include <elf.h>
#endif

#define MIN_REFCOUNT 2

namespace {
#if defined(EDB_X86)
	const edb::Operand::Register STACK_REG = edb::Operand::REG_ESP;
	const edb::Operand::Register FRAME_REG = edb::Operand::REG_EBP;
#elif defined(EDB_X86_64)
	const edb::Operand::Register STACK_REG = edb::Operand::REG_RSP;
	const edb::Operand::Register FRAME_REG = edb::Operand::REG_RBP;
#endif
}

//------------------------------------------------------------------------------
// Name: Analyzer(
// Desc: 
//------------------------------------------------------------------------------
Analyzer::Analyzer() : m_Menu(0) {
}


//------------------------------------------------------------------------------
// Name: menu(QWidget *parent)
// Desc: 
//------------------------------------------------------------------------------
QMenu *Analyzer::menu(QWidget *parent) {
	if(m_Menu == 0) {
		m_Menu = new QMenu(tr("Analyzer"), parent);
		m_Menu->addAction(tr("&Analyzer Current Region"), this, SLOT(doMenu()), QKeySequence(tr("Ctrl+A")));
	}

	return m_Menu;
}

//------------------------------------------------------------------------------
// Name: privateInit()
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::privateInit() {
	m_API.setAnalyzer(this);
}

//------------------------------------------------------------------------------
// Name: doMenu()
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::doMenu() {
	MemRegion region;
	const State &state = m_API.currentState();
	const edb::address_t eip = state.instructionPointer();
	if(m_API.memoryRegions().findRegion(eip, region)) {
	
		boost::scoped_ptr<QProgressDialog> progress(new QProgressDialog(tr("Performing Analysis"), 0, 0, 100, m_API.debuggerUI));
		connect(this, SIGNAL(updateProgress(int)), progress.get(), SLOT(setValue(int)));
		
		progress->show();
		progress->setValue(0);
	
		analyze(region);
		
		m_API.repaintCPUView();
	}
}

//------------------------------------------------------------------------------
// Name: findFunctionCalls(const MemRegion &region, FunctionMap &results)
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::findFunctionCalls(const MemRegion &region, FunctionMap &results) {
	static const edb::address_t pageSize = m_API.debuggerCore->pageSize();

	const edb::address_t size_in_pages = region.size() / pageSize;
	boost::scoped_array<quint8> pages(new quint8[size_in_pages * pageSize]);

	if(m_API.debuggerCore->readPages(region.start, &pages[0], size_in_pages)) {
		for(edb::address_t i = 0; i < size_in_pages * pageSize; ++i) {

			// no need to bonus calls which came from a known good
			if(m_AnalysisInfo[region].known_calls.contains(region.start + i)) {
				continue;
			}
		
			const edb::Instruction insn(&pages[i], region.size() - i, region.start + i, std::nothrow);

			if(insn.valid() && insn.type() == edb::Instruction::OP_CALL) {
				
				const edb::address_t ip = region.start + i;
				const edb::Operand &op = insn.operand(0);

				if(op.generalType() == edb::Operand::TYPE_REL) {
					edb::address_t ea = op.relativeTarget();

					// skip over ones which are : call <label>; label:
					if(ea != ip + insn.size()) {
						if(ea >= region.start && ea < region.end) {
							if(!isInsideKnown(region, ea)) {
								results[ea].entry_address	= ea;
								results[ea].end_address		= ea;
								results[ea].reference_count++;	
							}
						}
					}
				}

				emit updateProgress(CalculatePercentage(4, 10, i, region.size()));
			}
			QCoreApplication::processEvents();
		}
	}
}

//------------------------------------------------------------------------------
// Name: isStackFrame(edb::address_t addr)
// Desc: 
//------------------------------------------------------------------------------
bool Analyzer::isStackFrame(edb::address_t addr) const {

	// TODO: make a version which can operate on memory
	// directly, so that we can prefetch whole pages

	quint8 buf[edb::Instruction::MAX_SIZE];

	unsigned int i = 0;

	while(i < 2) {
		// gets the bytes for the instruction
		int buf_size = sizeof(buf);
		if(!m_API.getInstructionBytes(addr, buf, buf_size)) {
			break;
		}

		// decode it
		const edb::Instruction insn(buf, buf_size, addr, std::nothrow);
		if(!insn.valid()) {
			break;;
		}

		// which part of the sequence are we looking at?
		switch(i++) {
		case 0:
			// are we looking at a 'push rBP' ?
			if(insn.type() == edb::Instruction::OP_PUSH) {
				const edb::Operand &op = insn.operand(0);
				if(op.completeType() == edb::Operand::TYPE_REGISTER) {
					if(op.reg == FRAME_REG) {
						break;
					}
				}
			// if it is an 'enter 0,0' instruction, then it's a stack frame right away
			} else if(insn.type() == edb::Instruction::OP_ENTER) {
				const edb::Operand &op0 = insn.operand(0);
				const edb::Operand &op1 = insn.operand(1);
				if(op0.immediate() == 0 && op1.immediate() == 0) {
					return true;
				}
			}
			++i;
			break;
		case 1:
			// are we looking at a 'mov rBP, rSP' ?
			if(insn.type() == edb::Instruction::OP_MOV) {
				const edb::Operand &op0 = insn.operand(0);
				const edb::Operand &op1 = insn.operand(1);
				if(op0.completeType() == edb::Operand::TYPE_REGISTER && op1.completeType() == edb::Operand::TYPE_REGISTER) {
					if(op0.reg == FRAME_REG && op1.reg == STACK_REG) {
						return true;
					}
				}
			}
			break;
		default:
			break;
		}

		addr += insn.size();

		QCoreApplication::processEvents();
	}
	
	return false;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::bonusStackFramesHelper(Function &info) const {
	const edb::address_t addr	= info.entry_address;		
	const bool bonus			= isStackFrame(addr);

	if(bonus) {
		info.reference_count++;
	}
}

//------------------------------------------------------------------------------
// Name: bonusStackFrames(FunctionMap &results)
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::bonusStackFrames(FunctionMap &results) {
	// give bonus if we see a:
	// push ebp; mov ebp, esp;
#ifdef USE_QT_CONCURRENT
	QtConcurrent::blockingMap(results, boost::bind(&Analyzer::bonusStackFramesHelper, this, _1));
#else
	std::for_each(results.begin(), results.end(), boost::bind(&Analyzer::bonusStackFramesHelper, this, _1));
#endif
}

//------------------------------------------------------------------------------
// Name: bonusMain(const MemRegion &region, FunctionMap &results)
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::bonusMain(const MemRegion &region, FunctionMap &results) const{
	const QString s = m_API.getProcessExecutable();
	if(!s.isEmpty()) {
		const edb::address_t main = m_API.locateMainFunction(s);
		
		if(main != edb::nullptr) {
		
			if(main >= region.start && main < region.end) {
				// make sure we have an entry for this function
				results[main].entry_address		= main;
				results[main].end_address		= main;
				if(results[main].reference_count == 0) {
					results[main].reference_count = MIN_REFCOUNT;
				} else {
					results[main].reference_count++;
				}
			}
		}
	}
}

//------------------------------------------------------------------------------
// Name: bonusSymbols(FunctionMap &results)
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::bonusSymbols(const MemRegion &region, FunctionMap &results) {
	// give bonus if we have a symbol for the address
	
	QList<SymbolManager::Symbol> symbols = m_API.symbolManager().symbols();
	
	foreach(const SymbolManager::Symbol &sym, symbols) {
		const edb::address_t addr = sym.address;
		
		if(addr >= region.start && addr < region.end) {

			if(sym.isCode()) {

				qDebug() << "[Analyzer] adding:" << sym.name;

				// make sure we have an entry for this function
				results[addr].entry_address		= addr;
				results[addr].end_address		= addr;
				if(results[addr].reference_count == 0) {
					results[addr].reference_count = MIN_REFCOUNT;
				} else {
					results[addr].reference_count++;
				}
			}
		}
	
		QCoreApplication::processEvents();
	}
}

//------------------------------------------------------------------------------
// Name: findFunctionEnds(FunctionMap &results, const MemRegion &region, bool known, QSet<edb::address_t> &walkedFunctions)
// Desc: 
//------------------------------------------------------------------------------
int Analyzer::findFunctionEnds(FunctionMap &results, const MemRegion &region, bool known, QSet<edb::address_t> &walkedFunctions) {
	int ret = 0;
	FunctionMap::iterator it = results.begin();
	while(it != results.end()) {
		Function &info = it.value();
		
		const FunctionMap::iterator next = ++it;
		
		if(walkedFunctions.contains(info.entry_address)) {
			continue;
		}
		
		if(info.reference_count >= MIN_REFCOUNT) {
			int updates;
			edb::address_t next_entry;
		
			if(next != results.end()) {
				next_entry = next.value().entry_address;
			} else {
				// the region's end is the absolute max end this function can have
				next_entry = region.end;
			}
			
			edb::address_t last_instruction;
			const edb::address_t end = findFunctionEnd(region, info.entry_address, next_entry, updates, results, last_instruction, known);
			ret += updates;
			info.end_address		= end;
			info.last_instruction	= last_instruction;


#if 1
			// if the very last instruction happens to be a jmp, then this may
			// be a call/ret -> jmp optimization. This isn't always the case
			// but often enough that it's probably right
			quint8 buf[edb::Instruction::MAX_SIZE];
			int buf_size = sizeof(buf);
			if(m_API.getInstructionBytes(last_instruction, buf, buf_size)) {
				const edb::Instruction insn(buf, buf_size, last_instruction, std::nothrow);
				if(insn.valid() && insn.type() == edb::Instruction::OP_JMP) {
				
					const edb::Operand &op = insn.operand(0);
					
					if(op.generalType() == edb::Operand::TYPE_REL) {
						const edb::address_t target = op.relativeTarget();
						
						Function func;
						
						if(!findContainingFunction(target, func)) {
						
							results[target].entry_address	= target;
							results[target].end_address		= target;
							results[target].reference_count = MIN_REFCOUNT;
							++ret;
						}
					}
				}
			}
#endif
			walkedFunctions.insert(info.entry_address);
		}	
		
		it = next;
		QCoreApplication::processEvents();
	}
	return ret;
}

//------------------------------------------------------------------------------
// Name: findFunctionEnd(const MemRegion &region, edb::address_t start_address, edb::address_t end_address, int &update_count, FunctionMap &results, bool known)
// Desc: 
//------------------------------------------------------------------------------
edb::address_t Analyzer::findFunctionEnd(const MemRegion &region, edb::address_t start_address, edb::address_t end_address, int &update_count, FunctionMap &results, edb::address_t &last_instruction, bool known) {
	edb::address_t				ret = start_address;
	QList<edb::address_t>		jump_targets;
	QMap<edb::address_t, int>	code_addresses;
	
	update_count = 0;
	
	// we start with the entry point of the function
	jump_targets.push_front(start_address);
	
	// while no more jump targets... (includes entry point)
	while(!jump_targets.empty()) {
		edb::address_t addr = jump_targets.front();
		jump_targets.pop_front();

		// keep going until we go out of bounds	
		while(addr >= start_address && addr < end_address) {
				
			quint8 buf[edb::Instruction::MAX_SIZE];
			int buf_size = sizeof(buf);
			if(!m_API.getInstructionBytes(addr, buf, buf_size)) {
				break;
			}
			
			const edb::Instruction insn(buf, buf_size, addr, std::nothrow);
			if(!insn.valid()) {
				break;
			}

			// ok, it was a valid instruction, let's add it to the
			// list of possible 'code addresses'
			code_addresses.insert(addr, insn.size());
			
			const edb::Instruction::Type type = insn.type();

			// is it a branch?
			if(type == edb::Instruction::OP_JCC) {

				const edb::Operand &op = insn.operand(0);
				if(op.generalType() == edb::Operand::TYPE_REL) {
					const edb::address_t ea = op.relativeTarget();

					if(!code_addresses.contains(ea)) {
						if(!jump_targets.contains(ea)) {
							jump_targets.push_front(ea);
						}
					}
				}
			} else if(type == edb::Instruction::OP_JMP) {

				const edb::Operand &op = insn.operand(0);
				if(op.generalType() == edb::Operand::TYPE_REL) {
					const edb::address_t ea = op.relativeTarget();
					
					// is it a jump to another function's entry point?
					// if so, this is a dead end, resume from other branches
					// but give the target a bonus reference
					FunctionMap::iterator it = results.find(ea);
					if(it != results.end()) {
						if(it.value().reference_count < MIN_REFCOUNT) {
							it.value().reference_count++;
							++update_count;
							break;
						}
					}
					
					if(!code_addresses.contains(ea)) {
						addr = ea;
						continue;
					} else {
						break;
					}
				}
			} else if(type == edb::Instruction::OP_CALL) {

				const edb::Operand &op = insn.operand(0);
				if(op.generalType() == edb::Operand::TYPE_REL) {
					const edb::address_t ea = op.relativeTarget();

					// skip over ones which are : call <label>; label:
					if(ea != addr + insn.size()) {
					
						FunctionMap::iterator it = results.find(ea);
						if(it != results.end()) {
							if(it.value().reference_count < MIN_REFCOUNT) {
								it.value().reference_count++;
								++update_count;
								
								if(known) {
									m_AnalysisInfo[region].known_calls.insert(ea);
								}
								
							}
						} else if(known){
							results[ea].entry_address	= ea;
							results[ea].end_address		= ea;
							results[ea].reference_count = MIN_REFCOUNT;
							++update_count;
							m_AnalysisInfo[region].known_calls.insert(ea);
						}				
					}
				} else if(op.generalType() == edb::Operand::TYPE_EXPRESSION) {
					// TODO: is this a jump table?
				}
			} else if(type == edb::Instruction::OP_RET) {
				break;
			} else if(type == edb::Instruction::OP_HLT) {
				break;
			}
			
			addr += insn.size();
			QCoreApplication::processEvents();
		}
	}
	
	last_instruction = 0;
	
	// find the last entry
	for(QMap<edb::address_t, int>::const_iterator it = code_addresses.begin(); it != code_addresses.end(); ++it) {
		const edb::address_t a = it.key() + it.value() - 1;
		ret = qMax(a, ret);
		
		// update entry for last instruction, (useful for detecting call;ret -> jmp optimization)
		last_instruction = qMax(last_instruction, it.key());
		
		QCoreApplication::processEvents();
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: isThunk(edb::address_t address)
// Desc: basically returns true if the first instruction of the function is a 
//       jmp
//------------------------------------------------------------------------------
bool Analyzer::isThunk(edb::address_t address) const {
	quint8 buf[edb::Instruction::MAX_SIZE];
	int buf_size = sizeof(buf);
	bool ret = false;
	if(m_API.getInstructionBytes(address, buf, buf_size)) {
		const edb::Instruction insn(buf, buf_size, address, std::nothrow);
		ret = insn.valid() && insn.type() == edb::Instruction::OP_JMP;
	}
	
	return ret;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::setFunctionTypesHelper(Function &info) const {
	const edb::address_t addr = info.entry_address;

	if(isThunk(addr)) {
		info.type = Function::FUNCTION_THUNK;
	} else {
		info.type = Function::FUNCTION_STANDARD;
	}
}

//------------------------------------------------------------------------------
// Name: setFunctionTypes(FunctionMap &results)
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::setFunctionTypes(FunctionMap &results) {
	// give bonus if we have a symbol for the address
#ifdef USE_QT_CONCURRENT
	QtConcurrent::blockingMap(results, boost::bind(&Analyzer::setFunctionTypesHelper, this, _1));
#else
	std::for_each(results.begin(), results.end(), boost::bind(&Analyzer::setFunctionTypesHelper, this, _1));
#endif
}

//------------------------------------------------------------------------------
// Name: isInsideKnown(const MemRegion &region, edb::address_t address)
// Desc: 
//------------------------------------------------------------------------------
bool Analyzer::isInsideKnown(const MemRegion &region, edb::address_t address) {
	
	const FunctionMap &funcs = functions(region);
	foreach(const Function &func, funcs) {
		if(address >= func.entry_address && address <= func.end_address) {
			return true;
		}
	}
	
	return false;
}

//------------------------------------------------------------------------------
// Name: 
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::findCallsFromFunctions(const MemRegion &region, FunctionMap &results, bool known) {
	
	QSet<edb::address_t> walkedFunctions;
	
	int updates;
	do {
		updates = findFunctionEnds(results, region, known, walkedFunctions);
		qDebug() << "[Analyzer] got" << updates << "updates";
	} while(updates != 0);
}

//------------------------------------------------------------------------------
// Name: analyze(const MemRegion &region)
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::analyze(const MemRegion &region) {

	const QByteArray md5		= md5Region(region);
	const QByteArray prev_md5	= m_AnalysisInfo[region].md5;
	
	if(md5 != prev_md5) {
		FunctionMap &results = m_AnalysisInfo[region].analysis;

		results.clear();
		
		 m_AnalysisInfo[region].known_calls.clear();

		// start with some obvious functions...
		qDebug("[Analyzer] adding ELF entry points to the list...");
		bonusELFEntryPoint(region, results);
		emit updateProgress(CalculatePercentage(0, 10, 100, 100));

		qDebug("[Analyzer] attempting to add 'main' to the list...");
		bonusMain(region, results);
		emit updateProgress(CalculatePercentage(1, 10, 100, 100));

		qDebug("[Analyzer] attempting to add functions with symbols to the list...");
		bonusSymbols(region, results);
		emit updateProgress(CalculatePercentage(2, 10, 100, 100));
		
		// ok, at this point we only have known good ones in the list
		// see which things THEY call
		// we pass "known == true" which basically makes it give an
		// extra bonus to the calls it finds, since they were reachable
		// from a known good
		qDebug("[Analyzer] calculating function bounds... (pass 1)");
		findCallsFromFunctions(region, results, true);
		emit updateProgress(CalculatePercentage(3, 10, 100, 100));

		// ok, at this point, we've done the best we can with knowns
		// let's start looking for unknowns
		qDebug("[Analyzer] finding possible function calls...");
		
		// put these into a temporary bin
		FunctionMap tempResults;
		findFunctionCalls(region, tempResults);
		
		// collect the ones which have enough references
		qDebug("[Analyzer] collecting high reference answers first...");
		for(FunctionMap::iterator it = tempResults.begin(); it != tempResults.end(); ) {
			if(it->reference_count >= MIN_REFCOUNT) {
				results[it->entry_address] = *it;
				tempResults.erase(it++);
			} else {
				++it;
			}
		}
		emit updateProgress(CalculatePercentage(5, 10, 100, 100));
		
		// ok, after this the only ones left should be low reference 
		// count ones...
		// but by now we can gaurantee they won't overlap with 
		// known good functions at all
		qDebug("[Analyzer] calculating function bounds... (pass 2)");
		findCallsFromFunctions(region, results, false);
		emit updateProgress(CalculatePercentage(6, 10, 100, 100));

		
		qDebug("[Analyzer] collecting low reference answers...");
		foreach(const Function &func, tempResults) {
			if(!isInsideKnown(region, func.entry_address)) {
				results[func.entry_address] = func;
			}
		}
		emit updateProgress(CalculatePercentage(7, 10, 100, 100));

		qDebug("[Analyzer] bonusing stack frames...");
		bonusStackFrames(results);
		emit updateProgress(CalculatePercentage(8, 10, 100, 100));
		

		// find the ends of any functions we found outside of the knowns
		// though it will only go after ones with >= MIN_REFCOUNT
		// references
		qDebug("[Analyzer] calculating function bounds... (pass 3)");
		findCallsFromFunctions(region, results, false);
		emit updateProgress(CalculatePercentage(9, 10, 100, 100));

		qDebug("[Analyzer] determining function types...");
		setFunctionTypes(results);
		emit updateProgress(CalculatePercentage(10, 10, 100, 100));

		qDebug("[Analyzer] complete");
		emit updateProgress(100);

		m_AnalysisInfo[region].md5 = md5;
	} else {
		qDebug("[Analyzer] region unchanged, using previous analysis");
	}
}

//------------------------------------------------------------------------------
// Name: catagory(edb::address_t address) const
// Desc: 
//------------------------------------------------------------------------------
AnalyzerInterface::AddressCatagory Analyzer::catagory(edb::address_t address) const {

	Function func;
	if(findContainingFunction(address, func)) {
		if(address == func.entry_address) {
			return ADDRESS_FUNC_START;
		} else if(address == func.end_address) {
			return ADDRESS_FUNC_END;
		} else {
			return ADDRESS_FUNC_BODY;
		}
	}
	return ADDRESS_FUNC_UNKNOWN;
}

//------------------------------------------------------------------------------
// Name: functions(const MemRegion &region) const
// Desc: 
//------------------------------------------------------------------------------
AnalyzerInterface::FunctionMap Analyzer::functions(const MemRegion &region) const {
	return m_AnalysisInfo[region].analysis;
}

//------------------------------------------------------------------------------
// Name: findContainingFunction(edb::address_t address, AnalyzerInterface::Function &function) const
// Desc: 
//------------------------------------------------------------------------------
bool Analyzer::findContainingFunction(edb::address_t address, AnalyzerInterface::Function &function) const {

	MemRegion region;
	if(m_API.memoryRegions().findRegion(address, region)) {
		const FunctionMap &funcs = functions(region);
		foreach(const Function &f, funcs) {
			if(address >= f.entry_address && address <= f.end_address) {
				function = f;
				return true;
			}
		}
	}
	return false;
}

//------------------------------------------------------------------------------
// Name: md5Region(const MemRegion &region) const
// Desc: returns a byte array representing the MD5 of a region
//------------------------------------------------------------------------------
QByteArray Analyzer::md5Region(const MemRegion &region) const{

	static const edb::address_t pageSize = m_API.debuggerCore->pageSize();

	MD5 md5Digest;
	QByteArray ret;

	const edb::address_t size_in_pages = region.size() / pageSize;
	boost::scoped_array<quint8> pages(new quint8[size_in_pages * pageSize]);

	if(m_API.debuggerCore->readPages(region.start, &pages[0], size_in_pages)) {
		md5Digest.update(&pages[0], size_in_pages * pageSize);
		md5Digest.finalize();
		
		// make a deep copy because MD5 is about to go out of scope
		ret = QByteArray(reinterpret_cast<const char *>(md5Digest.digest()), 16);
	}

	return ret;
}


//------------------------------------------------------------------------------
// Name: bonusELFEntryPoint(const MemRegion &region, FunctionMap &results) const
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::bonusELFEntryPoint(const MemRegion &region, FunctionMap &results) const {
#ifdef Q_OS_LINUX
	static const edb::address_t pageSize = m_API.debuggerCore->pageSize();


	edb::address_t entry = edb::nullptr;
	
	do {
		boost::scoped_array<quint8> pages(new quint8[pageSize]);

		if(m_API.debuggerCore->readPages(region.start, &pages[0], 1)) {

			union {
				void		*ptr;
				Elf32_Ehdr	*elf32;
				Elf64_Ehdr	*elf64;
			} code = { &pages[0] };

			if(std::memcmp(code.elf32->e_ident, ELFMAG, SELFMAG) == 0) {
				if(code.elf32->e_ident[EI_CLASS] == ELFCLASS32) {
					entry = code.elf32->e_entry;
				}
			}

			if(entry == edb::nullptr && std::memcmp(code.elf64->e_ident, ELFMAG, SELFMAG) == 0) {
				if(code.elf64->e_ident[EI_CLASS] == ELFCLASS64) {
					entry = code.elf64->e_entry;
				}
			}
		}
	} while(0);

	if(entry != edb::nullptr) {
	
		// if the entry seems like a relative one (like for a library)
		// then add the base of its image
		if(entry < region.start) {
			entry += region.start;
		}
	
		// make sure we have an entry for this function
		
		if(entry >= region.start && entry < region.end) {		
			results[entry].entry_address	= entry;
			results[entry].end_address		= entry;
			if(results[entry].reference_count == 0) {
				results[entry].reference_count = MIN_REFCOUNT;
			} else {
				results[entry].reference_count++;
			}
		}
	}
#else
		Q_UNUSED(region);
		Q_UNUSED(results);
#endif
}

//------------------------------------------------------------------------------
// Name: invalidateAnalysis(const MemRegion &region)
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::invalidateAnalysis(const MemRegion &region) {
	
	m_AnalysisInfo[region].analysis.clear();
	m_AnalysisInfo[region].md5.clear();
	m_AnalysisInfo[region].known_calls.clear();
}

//------------------------------------------------------------------------------
// Name: invalidateAnalysis()
// Desc: 
//------------------------------------------------------------------------------
void Analyzer::invalidateAnalysis() {
	m_AnalysisInfo.clear();
}

Q_EXPORT_PLUGIN2(Analyzer, Analyzer)
