* 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,
-1,
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;
struct ArchitectureX86::ToDwarfRegisterMap : RegisterMap {
ToDwarfRegisterMap()
{
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];
};
struct ArchitectureX86::FromDwarfRegisterMap : RegisterMap {
virtual int32 CountRegisters() const
{
return kFromDwarfRegisterCount;
}
virtual int32 MapRegisterIndex(int32 index) const
{
return index >= 0 && index < kFromDwarfRegisterCount
? kFromDwarfRegisters[index] : -1;
}
};
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__)
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;
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;
stack_frame_type frameType;
bool hasPrologue = false;
if (isTopFrame && cpuState->InterruptVector() == 99) {
frameType = STACK_FRAME_TYPE_SYSCALL;
eip -= 2;
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;
if (isTopFrame) {
uint32 stack = 0;
if (hasPrologue) {
if (eip < function->Address() + 3) {
stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
if (eip > function->Address()) {
stack += 4;
}
} else {
uint8 code[1];
if (fTeamMemory->ReadMemory(eip, &code, 1) == 1
&& code[0] == 0xc3) {
stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
}
}
} else {
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;
}
}
}
}
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;
if (readStandardFrame) {
uint32 frameData[2];
if (framePointer != 0
&& fTeamMemory->ReadMemory(framePointer, frameData, 8) == 8) {
previousFramePointer = frameData[0];
returnAddress = frameData[1];
}
}
CpuStateX86* previousCpuState = NULL;
if (returnAddress != 0) {
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)
{
CpuStateX86* cpuState = dynamic_cast<CpuStateX86*>(previousCpuState);
uint32 eip = cpuState->IntRegisterValue(X86_REGISTER_EIP);
if (previousFunction == NULL || eip <= previousFunction->Address())
return;
target_addr_t functionAddress = previousFunction->Address();
size_t bufferSize = eip - functionAddress;
void* buffer = malloc(bufferSize);
if (buffer == NULL)
return;
MemoryDeleter bufferDeleter(buffer);
ssize_t bytesRead = fTeamMemory->ReadMemory(functionAddress, buffer,
bufferSize);
if (bytesRead != (ssize_t)bufferSize)
return;
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;
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);
return B_OK;
case B_DOUBLE_TYPE:
_value.SetTo(*(double*)buffer);
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
{
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);
DisassemblerX86 disassembler;
status_t error = disassembler.Init(function->Address(), buffer, bufferSize);
if (error != B_OK)
return error;
BString functionName(function->PrettyName());
if (!source->AddCommentLine((functionName << ':').String()))
return B_NO_MEMORY;
BString line;
target_addr_t instructionAddress;
target_size_t instructionSize;
bool breakpointAllowed;
while (disassembler.GetNextInstruction(line, instructionAddress,
instructionSize, breakpointAllowed) == B_OK) {
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)
{
InstructionInfo info;
status_t error = GetInstructionInfo(address, info, NULL);
if (error != B_OK)
return error;
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)
{
uint8 buffer[16];
ssize_t bytesRead = fTeamMemory->ReadMemory(address, buffer,
sizeof(buffer));
if (bytesRead < 0)
return bytesRead;
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)
{
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)
{
_maxRegisterCount = 2;
_maxBytesPerRegister = 4;
_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)
{
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;
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;
}