/* * 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 #include #include #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(__INTEL__) // 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(_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 stackFrameDebugInfoReference( stackFrameDebugInfo, true); StackFrame* frame = new(std::nothrow) StackFrame(frameType, cpuState, framePointer, eip, stackFrameDebugInfo); if (frame == NULL) return B_NO_MEMORY; BReference 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(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 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 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(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; }