⛏️ index : haiku.git

/*
 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2011-2016, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */


#include "ArchitectureX86.h"

#include <new>

#include <String.h>

#include <AutoDeleter.h>

#include "CfaContext.h"
#include "CpuStateX86.h"
#include "DisassembledCode.h"
#include "FunctionDebugInfo.h"
#include "InstructionInfo.h"
#include "NoOpStackFrameDebugInfo.h"
#include "RegisterMap.h"
#include "StackFrame.h"
#include "Statement.h"
#include "TeamMemory.h"
#include "ValueLocation.h"
#include "X86AssemblyLanguage.h"

#include "disasm/DisassemblerX86.h"


#define IA32_FEATURE_MMX	(1 << 23)
#define IA32_FEATURE_SSE	(1 << 25)


static const int32 kFromDwarfRegisters[] = {
	X86_REGISTER_EAX,
	X86_REGISTER_ECX,
	X86_REGISTER_EDX,
	X86_REGISTER_EBX,
	X86_REGISTER_ESP,
	X86_REGISTER_EBP,
	X86_REGISTER_ESI,
	X86_REGISTER_EDI,
	X86_REGISTER_EIP,
	-1,	// eflags
	-1,	// trap number
	X86_REGISTER_ST0,
	X86_REGISTER_ST1,
	X86_REGISTER_ST2,
	X86_REGISTER_ST3,
	X86_REGISTER_ST4,
	X86_REGISTER_ST5,
	X86_REGISTER_ST6,
	X86_REGISTER_ST7,
	-1,	// ?
	-1,	// ?
	X86_REGISTER_XMM0,
	X86_REGISTER_XMM1,
	X86_REGISTER_XMM2,
	X86_REGISTER_XMM3,
	X86_REGISTER_XMM4,
	X86_REGISTER_XMM5,
	X86_REGISTER_XMM6,
	X86_REGISTER_XMM7,
	X86_REGISTER_MM0,
	X86_REGISTER_MM1,
	X86_REGISTER_MM2,
	X86_REGISTER_MM3,
	X86_REGISTER_MM4,
	X86_REGISTER_MM5,
	X86_REGISTER_MM6,
	X86_REGISTER_MM7,
};

static const int32 kFromDwarfRegisterCount = sizeof(kFromDwarfRegisters) / 4;


// #pragma mark - ToDwarfRegisterMap


struct ArchitectureX86::ToDwarfRegisterMap : RegisterMap {
	ToDwarfRegisterMap()
	{
		// init the index array from the reverse map
		memset(fIndices, -1, sizeof(fIndices));
		for (int32 i = 0; i < kFromDwarfRegisterCount; i++) {
			if (kFromDwarfRegisters[i] >= 0)
				fIndices[kFromDwarfRegisters[i]] = i;
		}
	}

	virtual int32 CountRegisters() const
	{
		return X86_REGISTER_COUNT;
	}

	virtual int32 MapRegisterIndex(int32 index) const
	{
		return index >= 0 && index < X86_REGISTER_COUNT ? fIndices[index] : -1;
	}

private:
	int32	fIndices[X86_REGISTER_COUNT];
};


// #pragma mark - FromDwarfRegisterMap


struct ArchitectureX86::FromDwarfRegisterMap : RegisterMap {
	virtual int32 CountRegisters() const
	{
		return kFromDwarfRegisterCount;
	}

	virtual int32 MapRegisterIndex(int32 index) const
	{
		return index >= 0 && index < kFromDwarfRegisterCount
			? kFromDwarfRegisters[index] : -1;
	}
};


// #pragma mark - ArchitectureX86


ArchitectureX86::ArchitectureX86(TeamMemory* teamMemory)
	:
	Architecture(teamMemory, 4, sizeof(x86_debug_cpu_state), false),
	fFeatureFlags(0),
	fAssemblyLanguage(NULL),
	fToDwarfRegisterMap(NULL),
	fFromDwarfRegisterMap(NULL)
{
}


ArchitectureX86::~ArchitectureX86()
{
	if (fToDwarfRegisterMap != NULL)
		fToDwarfRegisterMap->ReleaseReference();
	if (fFromDwarfRegisterMap != NULL)
		fFromDwarfRegisterMap->ReleaseReference();
	if (fAssemblyLanguage != NULL)
		fAssemblyLanguage->ReleaseReference();
}


status_t
ArchitectureX86::Init()
{
	fAssemblyLanguage = new(std::nothrow) X86AssemblyLanguage;
	if (fAssemblyLanguage == NULL)
		return B_NO_MEMORY;

#if defined(__i386__)
	// TODO: this needs to be determined/retrieved indirectly from the
	// target host interface, as in the remote case the CPU features may
	// differ from those of the local CPU.
	cpuid_info info;
	status_t error = get_cpuid(&info, 1, 0);
	if (error != B_OK)
		return error;

	if ((info.eax_1.features & IA32_FEATURE_MMX) != 0)
		fFeatureFlags |= X86_CPU_FEATURE_FLAG_MMX;

	if ((info.eax_1.features & IA32_FEATURE_SSE) != 0)
		fFeatureFlags |= X86_CPU_FEATURE_FLAG_SSE;

#endif

	try {
		_AddIntegerRegister(X86_REGISTER_EIP, "eip", B_UINT32_TYPE,
			REGISTER_TYPE_INSTRUCTION_POINTER, false);
		_AddIntegerRegister(X86_REGISTER_ESP, "esp", B_UINT32_TYPE,
			REGISTER_TYPE_STACK_POINTER, true);
		_AddIntegerRegister(X86_REGISTER_EBP, "ebp", B_UINT32_TYPE,
			REGISTER_TYPE_GENERAL_PURPOSE, true);

		_AddIntegerRegister(X86_REGISTER_EAX, "eax", B_UINT32_TYPE,
			REGISTER_TYPE_GENERAL_PURPOSE, false);
		_AddIntegerRegister(X86_REGISTER_EBX, "ebx", B_UINT32_TYPE,
			REGISTER_TYPE_GENERAL_PURPOSE, true);
		_AddIntegerRegister(X86_REGISTER_ECX, "ecx", B_UINT32_TYPE,
			REGISTER_TYPE_GENERAL_PURPOSE, false);
		_AddIntegerRegister(X86_REGISTER_EDX, "edx", B_UINT32_TYPE,
			REGISTER_TYPE_GENERAL_PURPOSE, false);

		_AddIntegerRegister(X86_REGISTER_ESI, "esi", B_UINT32_TYPE,
			REGISTER_TYPE_GENERAL_PURPOSE, true);
		_AddIntegerRegister(X86_REGISTER_EDI, "edi", B_UINT32_TYPE,
			REGISTER_TYPE_GENERAL_PURPOSE, true);

		_AddIntegerRegister(X86_REGISTER_CS, "cs", B_UINT16_TYPE,
			REGISTER_TYPE_SPECIAL_PURPOSE, true);
		_AddIntegerRegister(X86_REGISTER_DS, "ds", B_UINT16_TYPE,
			REGISTER_TYPE_SPECIAL_PURPOSE, true);
		_AddIntegerRegister(X86_REGISTER_ES, "es", B_UINT16_TYPE,
			REGISTER_TYPE_SPECIAL_PURPOSE, true);
		_AddIntegerRegister(X86_REGISTER_FS, "fs", B_UINT16_TYPE,
			REGISTER_TYPE_SPECIAL_PURPOSE, true);
		_AddIntegerRegister(X86_REGISTER_GS, "gs", B_UINT16_TYPE,
			REGISTER_TYPE_SPECIAL_PURPOSE, true);
		_AddIntegerRegister(X86_REGISTER_SS, "ss", B_UINT16_TYPE,
			REGISTER_TYPE_SPECIAL_PURPOSE, true);

		_AddFPRegister(X86_REGISTER_ST0, "st0");
		_AddFPRegister(X86_REGISTER_ST1, "st1");
		_AddFPRegister(X86_REGISTER_ST2, "st2");
		_AddFPRegister(X86_REGISTER_ST3, "st3");
		_AddFPRegister(X86_REGISTER_ST4, "st4");
		_AddFPRegister(X86_REGISTER_ST5, "st5");
		_AddFPRegister(X86_REGISTER_ST6, "st6");
		_AddFPRegister(X86_REGISTER_ST7, "st7");

		if ((fFeatureFlags & X86_CPU_FEATURE_FLAG_MMX) != 0) {
			_AddSIMDRegister(X86_REGISTER_MM0, "mm0", sizeof(uint64));
			_AddSIMDRegister(X86_REGISTER_MM1, "mm1", sizeof(uint64));
			_AddSIMDRegister(X86_REGISTER_MM2, "mm2", sizeof(uint64));
			_AddSIMDRegister(X86_REGISTER_MM3, "mm3", sizeof(uint64));
			_AddSIMDRegister(X86_REGISTER_MM4, "mm4", sizeof(uint64));
			_AddSIMDRegister(X86_REGISTER_MM5, "mm5", sizeof(uint64));
			_AddSIMDRegister(X86_REGISTER_MM6, "mm6", sizeof(uint64));
			_AddSIMDRegister(X86_REGISTER_MM7, "mm7", sizeof(uint64));
		}

		if ((fFeatureFlags & X86_CPU_FEATURE_FLAG_SSE) != 0) {
			_AddSIMDRegister(X86_REGISTER_XMM0, "xmm0",
				sizeof(x86_xmm_register));
			_AddSIMDRegister(X86_REGISTER_XMM1, "xmm1",
				sizeof(x86_xmm_register));
			_AddSIMDRegister(X86_REGISTER_XMM2, "xmm2",
				sizeof(x86_xmm_register));
			_AddSIMDRegister(X86_REGISTER_XMM3, "xmm3",
				sizeof(x86_xmm_register));
			_AddSIMDRegister(X86_REGISTER_XMM4, "xmm4",
				sizeof(x86_xmm_register));
			_AddSIMDRegister(X86_REGISTER_XMM5, "xmm5",
				sizeof(x86_xmm_register));
			_AddSIMDRegister(X86_REGISTER_XMM6, "xmm6",
				sizeof(x86_xmm_register));
			_AddSIMDRegister(X86_REGISTER_XMM7, "xmm7",
				sizeof(x86_xmm_register));
		}

	} catch (std::bad_alloc&) {
		return B_NO_MEMORY;
	}

	fToDwarfRegisterMap = new(std::nothrow) ToDwarfRegisterMap;
	fFromDwarfRegisterMap = new(std::nothrow) FromDwarfRegisterMap;

	if (fToDwarfRegisterMap == NULL || fFromDwarfRegisterMap == NULL)
		return B_NO_MEMORY;

	return B_OK;
}


int32
ArchitectureX86::StackGrowthDirection() const
{
	return STACK_GROWTH_DIRECTION_NEGATIVE;
}


int32
ArchitectureX86::CountRegisters() const
{
	return fRegisters.Count();
}


const Register*
ArchitectureX86::Registers() const
{
	return fRegisters.Elements();
}



status_t
ArchitectureX86::InitRegisterRules(CfaContext& context) const
{
	status_t error = Architecture::InitRegisterRules(context);
	if (error != B_OK)
		return error;

	// set up rule for EIP register
	context.RegisterRule(fToDwarfRegisterMap->MapRegisterIndex(
		X86_REGISTER_EIP))->SetToLocationOffset(-4);

	return B_OK;
}


status_t
ArchitectureX86::GetDwarfRegisterMaps(RegisterMap** _toDwarf,
	RegisterMap** _fromDwarf) const
{
	if (_toDwarf != NULL) {
		*_toDwarf = fToDwarfRegisterMap;
		fToDwarfRegisterMap->AcquireReference();
	}

	if (_fromDwarf != NULL) {
		*_fromDwarf = fFromDwarfRegisterMap;
		fFromDwarfRegisterMap->AcquireReference();
	}

	return B_OK;
}


status_t
ArchitectureX86::GetCpuFeatures(uint32& flags)
{
	flags = fFeatureFlags;

	return B_OK;
}


status_t
ArchitectureX86::CreateCpuState(CpuState*& _state)
{
	CpuStateX86* state = new(std::nothrow) CpuStateX86;
	if (state == NULL)
		return B_NO_MEMORY;

	_state = state;
	return B_OK;
}


status_t
ArchitectureX86::CreateCpuState(const void* cpuStateData, size_t size,
	CpuState*& _state)
{
	if (size != sizeof(x86_debug_cpu_state))
		return B_BAD_VALUE;

	CpuStateX86* state = new(std::nothrow) CpuStateX86(
		*(const x86_debug_cpu_state*)cpuStateData);
	if (state == NULL)
		return B_NO_MEMORY;

	_state = state;
	return B_OK;
}


status_t
ArchitectureX86::CreateStackFrame(Image* image, FunctionDebugInfo* function,
	CpuState* _cpuState, bool isTopFrame, StackFrame*& _frame,
	CpuState*& _previousCpuState)
{
	CpuStateX86* cpuState = dynamic_cast<CpuStateX86*>(_cpuState);

	uint32 framePointer = cpuState->IntRegisterValue(X86_REGISTER_EBP);
	uint32 eip = cpuState->IntRegisterValue(X86_REGISTER_EIP);

	bool readStandardFrame = true;
	uint32 previousFramePointer = 0;
	uint32 returnAddress = 0;

	// check for syscall frames
	stack_frame_type frameType;
	bool hasPrologue = false;
	if (isTopFrame && cpuState->InterruptVector() == 99) {
		// The thread is performing a syscall. So this frame is not really the
		// top-most frame and we need to adjust the eip.
		frameType = STACK_FRAME_TYPE_SYSCALL;
		eip -= 2;
			// int 99, sysenter, and syscall all are 2 byte instructions

		// The syscall stubs are frameless, the return address is on top of the
		// stack.
		uint32 esp = cpuState->IntRegisterValue(X86_REGISTER_ESP);
		uint32 address;
		if (fTeamMemory->ReadMemory(esp, &address, 4) == 4) {
			returnAddress = address;
			previousFramePointer = framePointer;
			framePointer = 0;
			readStandardFrame = false;
		}
	} else {
		hasPrologue = _HasFunctionPrologue(function);
		if (hasPrologue)
			frameType = STACK_FRAME_TYPE_STANDARD;
		else
			frameType = STACK_FRAME_TYPE_FRAMELESS;
		// TODO: Handling for frameless functions. It's not trivial to find the
		// return address on the stack, though.

		// If the function is not frameless and we're at the top frame we need
		// to check whether the prologue has not been executed (completely) or
		// we're already after the epilogue.
		if (isTopFrame) {
			uint32 stack = 0;
			if (hasPrologue) {
				if (eip < function->Address() + 3) {
					// The prologue has not been executed yet, i.e. there's no
					// stack frame yet. Get the return address from the stack.
					stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
					if (eip > function->Address()) {
						// The "push %ebp" has already been executed.
						stack += 4;
					}
				} else {
					// Not in the function prologue, but maybe after the
					// epilogue. The epilogue is a single "pop %ebp", so we
					// check whether the current instruction is already a
					// "ret".
					uint8 code[1];
					if (fTeamMemory->ReadMemory(eip, &code, 1) == 1
						&& code[0] == 0xc3) {
						stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
					}
				}
			} else {
				// Check if the instruction pointer is at a readable location.
				// If it isn't, then chances are we got here via a bogus
				// function pointer, and the prologue hasn't actually been
				// executed. In such a case, what we need is right at the top
				// of the stack.
				uint8 data[1];
				if (fTeamMemory->ReadMemory(eip, &data, 1) != 1)
					stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
			}

			if (stack != 0) {
				uint32 address;
				if (fTeamMemory->ReadMemory(stack, &address, 4) == 4) {
					returnAddress = address;
					previousFramePointer = framePointer;
					framePointer = 0;
					readStandardFrame = false;
					frameType = STACK_FRAME_TYPE_FRAMELESS;
				}
			}
		}
	}

	// create the stack frame
	StackFrameDebugInfo* stackFrameDebugInfo
		= new(std::nothrow) NoOpStackFrameDebugInfo;
	if (stackFrameDebugInfo == NULL)
		return B_NO_MEMORY;
	BReference<StackFrameDebugInfo> stackFrameDebugInfoReference(
		stackFrameDebugInfo, true);

	StackFrame* frame = new(std::nothrow) StackFrame(frameType, cpuState,
		framePointer, eip, stackFrameDebugInfo);
	if (frame == NULL)
		return B_NO_MEMORY;
	BReference<StackFrame> frameReference(frame, true);

	status_t error = frame->Init();
	if (error != B_OK)
		return error;

	// read the previous frame and return address, if this is a standard frame
	if (readStandardFrame) {
		uint32 frameData[2];
		if (framePointer != 0
			&& fTeamMemory->ReadMemory(framePointer, frameData, 8) == 8) {
			previousFramePointer = frameData[0];
			returnAddress = frameData[1];
		}
	}

	// create the CPU state, if we have any info
	CpuStateX86* previousCpuState = NULL;
	if (returnAddress != 0) {
		// prepare the previous CPU state
		previousCpuState = new(std::nothrow) CpuStateX86;
		if (previousCpuState == NULL)
			return B_NO_MEMORY;

		previousCpuState->SetIntRegister(X86_REGISTER_EBP,
			previousFramePointer);
		previousCpuState->SetIntRegister(X86_REGISTER_EIP, returnAddress);
		frame->SetPreviousCpuState(previousCpuState);
	}

	frame->SetReturnAddress(returnAddress);

	_frame = frameReference.Detach();
	_previousCpuState = previousCpuState;
	return B_OK;
}


void
ArchitectureX86::UpdateStackFrameCpuState(const StackFrame* frame,
	Image* previousImage, FunctionDebugInfo* previousFunction,
	CpuState* previousCpuState)
{
	// This is not a top frame, so we want to offset eip to the previous
	// (calling) instruction.
	CpuStateX86* cpuState = dynamic_cast<CpuStateX86*>(previousCpuState);

	// get eip
	uint32 eip = cpuState->IntRegisterValue(X86_REGISTER_EIP);
	if (previousFunction == NULL || eip <= previousFunction->Address())
		return;
	target_addr_t functionAddress = previousFunction->Address();

	// allocate a buffer for the function code to disassemble
	size_t bufferSize = eip - functionAddress;
	void* buffer = malloc(bufferSize);
	if (buffer == NULL)
		return;
	MemoryDeleter bufferDeleter(buffer);

	// read the code
	ssize_t bytesRead = fTeamMemory->ReadMemory(functionAddress, buffer,
		bufferSize);
	if (bytesRead != (ssize_t)bufferSize)
		return;

	// disassemble to get the previous instruction
	DisassemblerX86 disassembler;
	target_addr_t instructionAddress;
	target_size_t instructionSize;
	if (disassembler.Init(functionAddress, buffer, bufferSize) == B_OK
		&& disassembler.GetPreviousInstruction(eip, instructionAddress,
			instructionSize) == B_OK) {
		eip -= instructionSize;
		cpuState->SetIntRegister(X86_REGISTER_EIP, eip);
	}
}


status_t
ArchitectureX86::ReadValueFromMemory(target_addr_t address, uint32 valueType,
	BVariant& _value) const
{
	uint8 buffer[64];
	size_t size = BVariant::SizeOfType(valueType);
	if (size == 0 || size > sizeof(buffer))
		return B_BAD_VALUE;

	ssize_t bytesRead = fTeamMemory->ReadMemory(address, buffer, size);
	if (bytesRead < 0)
		return bytesRead;
	if ((size_t)bytesRead != size)
		return B_ERROR;

	// TODO: We need to swap endianess, if the host is big endian!

	switch (valueType) {
		case B_INT8_TYPE:
			_value.SetTo(*(int8*)buffer);
			return B_OK;
		case B_UINT8_TYPE:
			_value.SetTo(*(uint8*)buffer);
			return B_OK;
		case B_INT16_TYPE:
			_value.SetTo(*(int16*)buffer);
			return B_OK;
		case B_UINT16_TYPE:
			_value.SetTo(*(uint16*)buffer);
			return B_OK;
		case B_INT32_TYPE:
			_value.SetTo(*(int32*)buffer);
			return B_OK;
		case B_UINT32_TYPE:
			_value.SetTo(*(uint32*)buffer);
			return B_OK;
		case B_INT64_TYPE:
			_value.SetTo(*(int64*)buffer);
			return B_OK;
		case B_UINT64_TYPE:
			_value.SetTo(*(uint64*)buffer);
			return B_OK;
		case B_FLOAT_TYPE:
			_value.SetTo(*(float*)buffer);
				// TODO: float on the host might work differently!
			return B_OK;
		case B_DOUBLE_TYPE:
			_value.SetTo(*(double*)buffer);
				// TODO: double on the host might work differently!
			return B_OK;
		default:
			return B_BAD_VALUE;
	}
}


status_t
ArchitectureX86::ReadValueFromMemory(target_addr_t addressSpace,
	target_addr_t address, uint32 valueType, BVariant& _value) const
{
	// n/a on this architecture
	return B_BAD_VALUE;
}


status_t
ArchitectureX86::DisassembleCode(FunctionDebugInfo* function,
	const void* buffer, size_t bufferSize, DisassembledCode*& _sourceCode)
{
	DisassembledCode* source = new(std::nothrow) DisassembledCode(
		fAssemblyLanguage);
	if (source == NULL)
		return B_NO_MEMORY;
	BReference<DisassembledCode> sourceReference(source, true);

	// init disassembler
	DisassemblerX86 disassembler;
	status_t error = disassembler.Init(function->Address(), buffer, bufferSize);
	if (error != B_OK)
		return error;

	// add a function name line
	BString functionName(function->PrettyName());
	if (!source->AddCommentLine((functionName << ':').String()))
		return B_NO_MEMORY;

	// disassemble the instructions
	BString line;
	target_addr_t instructionAddress;
	target_size_t instructionSize;
	bool breakpointAllowed;
	while (disassembler.GetNextInstruction(line, instructionAddress,
				instructionSize, breakpointAllowed) == B_OK) {
// TODO: Respect breakpointAllowed!
		if (!source->AddInstructionLine(line, instructionAddress,
				instructionSize)) {
			return B_NO_MEMORY;
		}
	}

	_sourceCode = sourceReference.Detach();
	return B_OK;
}


status_t
ArchitectureX86::GetStatement(FunctionDebugInfo* function,
	target_addr_t address, Statement*& _statement)
{
// TODO: This is not architecture dependent anymore!
	// get the instruction info
	InstructionInfo info;
	status_t error = GetInstructionInfo(address, info, NULL);
	if (error != B_OK)
		return error;

	// create a statement
	ContiguousStatement* statement = new(std::nothrow) ContiguousStatement(
		SourceLocation(-1), TargetAddressRange(info.Address(), info.Size()));
	if (statement == NULL)
		return B_NO_MEMORY;

	_statement = statement;
	return B_OK;
}


status_t
ArchitectureX86::GetInstructionInfo(target_addr_t address,
	InstructionInfo& _info, CpuState* state)
{
	// read the code - maximum x86{-64} instruction size = 15 bytes
	uint8 buffer[16];
	ssize_t bytesRead = fTeamMemory->ReadMemory(address, buffer,
		sizeof(buffer));
	if (bytesRead < 0)
		return bytesRead;

	// init disassembler
	DisassemblerX86 disassembler;
	status_t error = disassembler.Init(address, buffer, bytesRead);
	if (error != B_OK)
		return error;

	return disassembler.GetNextInstructionInfo(_info, state);
}


status_t
ArchitectureX86::ResolvePICFunctionAddress(target_addr_t instructionAddress,
	CpuState* state, target_addr_t& _targetAddress)
{
	// if the function in question is position-independent, the call
	// will actually have taken us to its corresponding PLT slot.
	// in such a case, look at the disassembled jump to determine
	// where to find the actual function address.
	InstructionInfo info;
	if (GetInstructionInfo(instructionAddress, info, state) != B_OK) {
		return B_BAD_VALUE;
	}
	target_addr_t subroutineAddress = info.TargetAddress();

	ssize_t bytesRead = fTeamMemory->ReadMemory(info.TargetAddress(),
		&subroutineAddress, fAddressSize);

	if (bytesRead != fAddressSize)
		return B_BAD_VALUE;

	_targetAddress = subroutineAddress;
	return B_OK;
}


status_t
ArchitectureX86::GetWatchpointDebugCapabilities(int32& _maxRegisterCount,
	int32& _maxBytesPerRegister, uint8& _watchpointCapabilityFlags)
{
	// while x86 technically has 4 hardware debug registers, one is reserved by
	// the kernel, and one is required for breakpoint support, which leaves
	// two available for watchpoints.
	_maxRegisterCount = 2;
	_maxBytesPerRegister = 4;

	// x86 only supports write and read/write watchpoints.
	_watchpointCapabilityFlags = WATCHPOINT_CAPABILITY_FLAG_WRITE
		| WATCHPOINT_CAPABILITY_FLAG_READ_WRITE;

	return B_OK;
}


status_t
ArchitectureX86::GetReturnAddressLocation(StackFrame* frame,
	target_size_t valueSize, ValueLocation*& _location)
{
	// for the calling conventions currently in use on Haiku,
	// the x86 rules for how values are returned are as follows:
	//
	// - 32 bits or smaller values are returned directly in EAX.
	// - 32-64 bit values are returned across EAX:EDX.
	// - > 64 bit values are returned on the stack.
	ValueLocation* location = new(std::nothrow) ValueLocation(
		IsBigEndian());
	if (location == NULL)
		return B_NO_MEMORY;
	BReference<ValueLocation> locationReference(location,
		true);

	if (valueSize <= 4) {
		ValuePieceLocation piece;
		piece.SetSize(valueSize);
		piece.SetToRegister(X86_REGISTER_EAX);
		if (!location->AddPiece(piece))
			return B_NO_MEMORY;
	} else if (valueSize <= 8) {
		ValuePieceLocation piece;
		piece.SetSize(4);
		piece.SetToRegister(X86_REGISTER_EAX);
		if (!location->AddPiece(piece))
			return B_NO_MEMORY;
		piece.SetToRegister(X86_REGISTER_EDX);
		piece.SetSize(valueSize - 4);
		if (!location->AddPiece(piece))
			return B_NO_MEMORY;
	} else {
		ValuePieceLocation piece;
		CpuStateX86* state = dynamic_cast<CpuStateX86*>(frame->GetCpuState());
		piece.SetToMemory(state->IntRegisterValue(X86_REGISTER_EAX));
		piece.SetSize(valueSize);
		if (!location->AddPiece(piece))
			return B_NO_MEMORY;
	}

	_location = locationReference.Detach();
	return B_OK;
}


void
ArchitectureX86::_AddRegister(int32 index, const char* name,
	uint32 bitSize, uint32 valueType, register_type type, bool calleePreserved)
{
	if (!fRegisters.Add(Register(index, name, bitSize, valueType, type,
			calleePreserved))) {
		throw std::bad_alloc();
	}
}


void
ArchitectureX86::_AddIntegerRegister(int32 index, const char* name,
	uint32 valueType, register_type type, bool calleePreserved)
{
	_AddRegister(index, name, 8 * BVariant::SizeOfType(valueType), valueType,
		type, calleePreserved);
}


void
ArchitectureX86::_AddFPRegister(int32 index, const char* name)
{
	_AddRegister(index, name, 8 * BVariant::SizeOfType(B_DOUBLE_TYPE),
		B_DOUBLE_TYPE, REGISTER_TYPE_GENERAL_PURPOSE, true);
}


void
ArchitectureX86::_AddSIMDRegister(int32 index, const char* name,
	uint32 byteSize)
{
	_AddRegister(index, name, byteSize * 8, B_RAW_TYPE,
		REGISTER_TYPE_GENERAL_PURPOSE, true);
}


bool
ArchitectureX86::_HasFunctionPrologue(FunctionDebugInfo* function) const
{
	if (function == NULL)
		return false;

	// check whether the function has the typical prologue
	if (function->Size() < 3)
		return false;

	uint8 buffer[3];
	if (fTeamMemory->ReadMemory(function->Address(), buffer, 3) != 3)
		return false;

	return buffer[0] == 0x55 && buffer[1] == 0x89 && buffer[2] == 0xe5;
}