1/*
2 * Copyright 2009-2012, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2011-2012, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include "ArchitectureX86.h"
9
10#include <new>
11
12#include <String.h>
13
14#include <AutoDeleter.h>
15
16#include "CfaContext.h"
17#include "CpuStateX86.h"
18#include "DisassembledCode.h"
19#include "FunctionDebugInfo.h"
20#include "InstructionInfo.h"
21#include "NoOpStackFrameDebugInfo.h"
22#include "RegisterMap.h"
23#include "StackFrame.h"
24#include "Statement.h"
25#include "TeamMemory.h"
26#include "ValueLocation.h"
27#include "X86AssemblyLanguage.h"
28
29#include "disasm/DisassemblerX86.h"
30
31
32static const int32 kFromDwarfRegisters[] = {
33	X86_REGISTER_EAX,
34	X86_REGISTER_ECX,
35	X86_REGISTER_EDX,
36	X86_REGISTER_EBX,
37	X86_REGISTER_ESP,
38	X86_REGISTER_EBP,
39	X86_REGISTER_ESI,
40	X86_REGISTER_EDI,
41	X86_REGISTER_EIP,
42	-1,	// eflags
43	-1,	// trap number
44	-1,	// st(0)
45	-1,	// st(1)
46	-1,	// st(2)
47	-1,	// st(3)
48	-1,	// st(4)
49	-1,	// st(5)
50	-1,	// st(6)
51	-1,	// st(7)
52	-1,	// ?
53	-1,	// ?
54	-1, -1, -1, -1, -1, -1, -1, -1,	// SSE
55	-1, -1, -1, -1, -1, -1, -1, -1	// MMX
56};
57
58static const int32 kFromDwarfRegisterCount = sizeof(kFromDwarfRegisters) / 4;
59
60
61// #pragma mark - ToDwarfRegisterMap
62
63
64struct ArchitectureX86::ToDwarfRegisterMap : RegisterMap {
65	ToDwarfRegisterMap()
66	{
67		// init the index array from the reverse map
68		memset(fIndices, -1, sizeof(fIndices));
69		for (int32 i = 0; i < kFromDwarfRegisterCount; i++) {
70			if (kFromDwarfRegisters[i] >= 0)
71				fIndices[kFromDwarfRegisters[i]] = i;
72		}
73	}
74
75	virtual int32 CountRegisters() const
76	{
77		return X86_REGISTER_COUNT;
78	}
79
80	virtual int32 MapRegisterIndex(int32 index) const
81	{
82		return index >= 0 && index < X86_REGISTER_COUNT ? fIndices[index] : -1;
83	}
84
85private:
86	int32	fIndices[X86_REGISTER_COUNT];
87};
88
89
90// #pragma mark - FromDwarfRegisterMap
91
92
93struct ArchitectureX86::FromDwarfRegisterMap : RegisterMap {
94	virtual int32 CountRegisters() const
95	{
96		return kFromDwarfRegisterCount;
97	}
98
99	virtual int32 MapRegisterIndex(int32 index) const
100	{
101		return index >= 0 && index < kFromDwarfRegisterCount
102			? kFromDwarfRegisters[index] : -1;
103	}
104};
105
106
107// #pragma mark - ArchitectureX86
108
109
110ArchitectureX86::ArchitectureX86(TeamMemory* teamMemory)
111	:
112	Architecture(teamMemory, 4, false),
113	fAssemblyLanguage(NULL),
114	fToDwarfRegisterMap(NULL),
115	fFromDwarfRegisterMap(NULL)
116{
117}
118
119
120ArchitectureX86::~ArchitectureX86()
121{
122	if (fToDwarfRegisterMap != NULL)
123		fToDwarfRegisterMap->ReleaseReference();
124	if (fFromDwarfRegisterMap != NULL)
125		fFromDwarfRegisterMap->ReleaseReference();
126	if (fAssemblyLanguage != NULL)
127		fAssemblyLanguage->ReleaseReference();
128}
129
130
131status_t
132ArchitectureX86::Init()
133{
134	fAssemblyLanguage = new(std::nothrow) X86AssemblyLanguage;
135	if (fAssemblyLanguage == NULL)
136		return B_NO_MEMORY;
137
138	try {
139		_AddIntegerRegister(X86_REGISTER_EIP, "eip", B_UINT32_TYPE,
140			REGISTER_TYPE_INSTRUCTION_POINTER, false);
141		_AddIntegerRegister(X86_REGISTER_ESP, "esp", B_UINT32_TYPE,
142			REGISTER_TYPE_STACK_POINTER, true);
143		_AddIntegerRegister(X86_REGISTER_EBP, "ebp", B_UINT32_TYPE,
144			REGISTER_TYPE_GENERAL_PURPOSE, true);
145
146		_AddIntegerRegister(X86_REGISTER_EAX, "eax", B_UINT32_TYPE,
147			REGISTER_TYPE_GENERAL_PURPOSE, false);
148		_AddIntegerRegister(X86_REGISTER_EBX, "ebx", B_UINT32_TYPE,
149			REGISTER_TYPE_GENERAL_PURPOSE, true);
150		_AddIntegerRegister(X86_REGISTER_ECX, "ecx", B_UINT32_TYPE,
151			REGISTER_TYPE_GENERAL_PURPOSE, false);
152		_AddIntegerRegister(X86_REGISTER_EDX, "edx", B_UINT32_TYPE,
153			REGISTER_TYPE_GENERAL_PURPOSE, false);
154
155		_AddIntegerRegister(X86_REGISTER_ESI, "esi", B_UINT32_TYPE,
156			REGISTER_TYPE_GENERAL_PURPOSE, true);
157		_AddIntegerRegister(X86_REGISTER_EDI, "edi", B_UINT32_TYPE,
158			REGISTER_TYPE_GENERAL_PURPOSE, true);
159
160		_AddIntegerRegister(X86_REGISTER_CS, "cs", B_UINT16_TYPE,
161			REGISTER_TYPE_SPECIAL_PURPOSE, true);
162		_AddIntegerRegister(X86_REGISTER_DS, "ds", B_UINT16_TYPE,
163			REGISTER_TYPE_SPECIAL_PURPOSE, true);
164		_AddIntegerRegister(X86_REGISTER_ES, "es", B_UINT16_TYPE,
165			REGISTER_TYPE_SPECIAL_PURPOSE, true);
166		_AddIntegerRegister(X86_REGISTER_FS, "fs", B_UINT16_TYPE,
167			REGISTER_TYPE_SPECIAL_PURPOSE, true);
168		_AddIntegerRegister(X86_REGISTER_GS, "gs", B_UINT16_TYPE,
169			REGISTER_TYPE_SPECIAL_PURPOSE, true);
170		_AddIntegerRegister(X86_REGISTER_SS, "ss", B_UINT16_TYPE,
171			REGISTER_TYPE_SPECIAL_PURPOSE, true);
172	} catch (std::bad_alloc) {
173		return B_NO_MEMORY;
174	}
175
176	fToDwarfRegisterMap = new(std::nothrow) ToDwarfRegisterMap;
177	fFromDwarfRegisterMap = new(std::nothrow) FromDwarfRegisterMap;
178
179	if (fToDwarfRegisterMap == NULL || fFromDwarfRegisterMap == NULL)
180		return B_NO_MEMORY;
181
182	return B_OK;
183}
184
185
186int32
187ArchitectureX86::StackGrowthDirection() const
188{
189	return STACK_GROWTH_DIRECTION_NEGATIVE;
190}
191
192
193int32
194ArchitectureX86::CountRegisters() const
195{
196	return fRegisters.Count();
197}
198
199
200const Register*
201ArchitectureX86::Registers() const
202{
203	return fRegisters.Elements();
204}
205
206
207status_t
208ArchitectureX86::InitRegisterRules(CfaContext& context) const
209{
210	status_t error = Architecture::InitRegisterRules(context);
211	if (error != B_OK)
212		return error;
213
214	// set up rule for EIP register
215	context.RegisterRule(fToDwarfRegisterMap->MapRegisterIndex(
216		X86_REGISTER_EIP))->SetToLocationOffset(-4);
217
218	return B_OK;
219}
220
221
222status_t
223ArchitectureX86::GetDwarfRegisterMaps(RegisterMap** _toDwarf,
224	RegisterMap** _fromDwarf) const
225{
226	if (_toDwarf != NULL) {
227		*_toDwarf = fToDwarfRegisterMap;
228		fToDwarfRegisterMap->AcquireReference();
229	}
230
231	if (_fromDwarf != NULL) {
232		*_fromDwarf = fFromDwarfRegisterMap;
233		fFromDwarfRegisterMap->AcquireReference();
234	}
235
236	return B_OK;
237}
238
239
240status_t
241ArchitectureX86::CreateCpuState(CpuState*& _state)
242{
243	CpuStateX86* state = new(std::nothrow) CpuStateX86;
244	if (state == NULL)
245		return B_NO_MEMORY;
246
247	_state = state;
248	return B_OK;
249}
250
251
252status_t
253ArchitectureX86::CreateCpuState(const void* cpuStateData, size_t size,
254	CpuState*& _state)
255{
256	if (size != sizeof(x86_debug_cpu_state))
257		return B_BAD_VALUE;
258
259	CpuStateX86* state = new(std::nothrow) CpuStateX86(
260		*(const x86_debug_cpu_state*)cpuStateData);
261	if (state == NULL)
262		return B_NO_MEMORY;
263
264	_state = state;
265	return B_OK;
266}
267
268
269status_t
270ArchitectureX86::CreateStackFrame(Image* image, FunctionDebugInfo* function,
271	CpuState* _cpuState, bool isTopFrame, StackFrame*& _frame,
272	CpuState*& _previousCpuState)
273{
274	CpuStateX86* cpuState = dynamic_cast<CpuStateX86*>(_cpuState);
275
276	uint32 framePointer = cpuState->IntRegisterValue(X86_REGISTER_EBP);
277	uint32 eip = cpuState->IntRegisterValue(X86_REGISTER_EIP);
278
279	bool readStandardFrame = true;
280	uint32 previousFramePointer = 0;
281	uint32 returnAddress = 0;
282
283	// check for syscall frames
284	stack_frame_type frameType;
285	bool hasPrologue = false;
286	if (isTopFrame && cpuState->InterruptVector() == 99) {
287		// The thread is performing a syscall. So this frame is not really the
288		// top-most frame and we need to adjust the eip.
289		frameType = STACK_FRAME_TYPE_SYSCALL;
290		eip -= 2;
291			// int 99, sysenter, and syscall all are 2 byte instructions
292
293		// The syscall stubs are frameless, the return address is on top of the
294		// stack.
295		uint32 esp = cpuState->IntRegisterValue(X86_REGISTER_ESP);
296		uint32 address;
297		if (fTeamMemory->ReadMemory(esp, &address, 4) == 4) {
298			returnAddress = address;
299			previousFramePointer = framePointer;
300			framePointer = 0;
301			readStandardFrame = false;
302		}
303	} else {
304		hasPrologue = _HasFunctionPrologue(function);
305		if (hasPrologue)
306			frameType = STACK_FRAME_TYPE_STANDARD;
307		else
308			frameType = STACK_FRAME_TYPE_FRAMELESS;
309		// TODO: Handling for frameless functions. It's not trivial to find the
310		// return address on the stack, though.
311
312		// If the function is not frameless and we're at the top frame we need
313		// to check whether the prologue has not been executed (completely) or
314		// we're already after the epilogue.
315		if (isTopFrame) {
316			uint32 stack = 0;
317			if (hasPrologue) {
318				if (eip < function->Address() + 3) {
319					// The prologue has not been executed yet, i.e. there's no
320					// stack frame yet. Get the return address from the stack.
321					stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
322					if (eip > function->Address()) {
323						// The "push %ebp" has already been executed.
324						stack += 4;
325					}
326				} else {
327					// Not in the function prologue, but maybe after the
328					// epilogue. The epilogue is a single "pop %ebp", so we
329					// check whether the current instruction is already a
330					// "ret".
331					uint8 code[1];
332					if (fTeamMemory->ReadMemory(eip, &code, 1) == 1
333						&& code[0] == 0xc3) {
334						stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
335					}
336				}
337			} else {
338				// Check if the instruction pointer is at a readable location.
339				// If it isn't, then chances are we got here via a bogus
340				// function pointer, and the prologue hasn't actually been
341				// executed. In such a case, what we need is right at the top
342				// of the stack.
343				uint8 data[1];
344				if (fTeamMemory->ReadMemory(eip, &data, 1) != 1)
345					stack = cpuState->IntRegisterValue(X86_REGISTER_ESP);
346			}
347
348			if (stack != 0) {
349				uint32 address;
350				if (fTeamMemory->ReadMemory(stack, &address, 4) == 4) {
351					returnAddress = address;
352					previousFramePointer = framePointer;
353					framePointer = 0;
354					readStandardFrame = false;
355					frameType = STACK_FRAME_TYPE_FRAMELESS;
356				}
357			}
358		}
359	}
360
361	// create the stack frame
362	StackFrameDebugInfo* stackFrameDebugInfo
363		= new(std::nothrow) NoOpStackFrameDebugInfo;
364	if (stackFrameDebugInfo == NULL)
365		return B_NO_MEMORY;
366	BReference<StackFrameDebugInfo> stackFrameDebugInfoReference(
367		stackFrameDebugInfo, true);
368
369	StackFrame* frame = new(std::nothrow) StackFrame(frameType, cpuState,
370		framePointer, eip, stackFrameDebugInfo);
371	if (frame == NULL)
372		return B_NO_MEMORY;
373	BReference<StackFrame> frameReference(frame, true);
374
375	status_t error = frame->Init();
376	if (error != B_OK)
377		return error;
378
379	// read the previous frame and return address, if this is a standard frame
380	if (readStandardFrame) {
381		uint32 frameData[2];
382		if (framePointer != 0
383			&& fTeamMemory->ReadMemory(framePointer, frameData, 8) == 8) {
384			previousFramePointer = frameData[0];
385			returnAddress = frameData[1];
386		}
387	}
388
389	// create the CPU state, if we have any info
390	CpuStateX86* previousCpuState = NULL;
391	if (returnAddress != 0) {
392		// prepare the previous CPU state
393		previousCpuState = new(std::nothrow) CpuStateX86;
394		if (previousCpuState == NULL)
395			return B_NO_MEMORY;
396
397		previousCpuState->SetIntRegister(X86_REGISTER_EBP,
398			previousFramePointer);
399		previousCpuState->SetIntRegister(X86_REGISTER_EIP, returnAddress);
400		frame->SetPreviousCpuState(previousCpuState);
401	}
402
403	frame->SetReturnAddress(returnAddress);
404
405	_frame = frameReference.Detach();
406	_previousCpuState = previousCpuState;
407	return B_OK;
408}
409
410
411void
412ArchitectureX86::UpdateStackFrameCpuState(const StackFrame* frame,
413	Image* previousImage, FunctionDebugInfo* previousFunction,
414	CpuState* previousCpuState)
415{
416	// This is not a top frame, so we want to offset eip to the previous
417	// (calling) instruction.
418	CpuStateX86* cpuState = dynamic_cast<CpuStateX86*>(previousCpuState);
419
420	// get eip
421	uint32 eip = cpuState->IntRegisterValue(X86_REGISTER_EIP);
422	if (previousFunction == NULL || eip <= previousFunction->Address())
423		return;
424	target_addr_t functionAddress = previousFunction->Address();
425
426	// allocate a buffer for the function code to disassemble
427	size_t bufferSize = eip - functionAddress;
428	void* buffer = malloc(bufferSize);
429	if (buffer == NULL)
430		return;
431	MemoryDeleter bufferDeleter(buffer);
432
433	// read the code
434	ssize_t bytesRead = fTeamMemory->ReadMemory(functionAddress, buffer,
435		bufferSize);
436	if (bytesRead != (ssize_t)bufferSize)
437		return;
438
439	// disassemble to get the previous instruction
440	DisassemblerX86 disassembler;
441	target_addr_t instructionAddress;
442	target_size_t instructionSize;
443	if (disassembler.Init(functionAddress, buffer, bufferSize) == B_OK
444		&& disassembler.GetPreviousInstruction(eip, instructionAddress,
445			instructionSize) == B_OK) {
446		eip -= instructionSize;
447		cpuState->SetIntRegister(X86_REGISTER_EIP, eip);
448	}
449}
450
451
452status_t
453ArchitectureX86::ReadValueFromMemory(target_addr_t address, uint32 valueType,
454	BVariant& _value) const
455{
456	uint8 buffer[64];
457	size_t size = BVariant::SizeOfType(valueType);
458	if (size == 0 || size > sizeof(buffer))
459		return B_BAD_VALUE;
460
461	ssize_t bytesRead = fTeamMemory->ReadMemory(address, buffer, size);
462	if (bytesRead < 0)
463		return bytesRead;
464	if ((size_t)bytesRead != size)
465		return B_ERROR;
466
467	// TODO: We need to swap endianess, if the host is big endian!
468
469	switch (valueType) {
470		case B_INT8_TYPE:
471			_value.SetTo(*(int8*)buffer);
472			return B_OK;
473		case B_UINT8_TYPE:
474			_value.SetTo(*(uint8*)buffer);
475			return B_OK;
476		case B_INT16_TYPE:
477			_value.SetTo(*(int16*)buffer);
478			return B_OK;
479		case B_UINT16_TYPE:
480			_value.SetTo(*(uint16*)buffer);
481			return B_OK;
482		case B_INT32_TYPE:
483			_value.SetTo(*(int32*)buffer);
484			return B_OK;
485		case B_UINT32_TYPE:
486			_value.SetTo(*(uint32*)buffer);
487			return B_OK;
488		case B_INT64_TYPE:
489			_value.SetTo(*(int64*)buffer);
490			return B_OK;
491		case B_UINT64_TYPE:
492			_value.SetTo(*(uint64*)buffer);
493			return B_OK;
494		case B_FLOAT_TYPE:
495			_value.SetTo(*(float*)buffer);
496				// TODO: float on the host might work differently!
497			return B_OK;
498		case B_DOUBLE_TYPE:
499			_value.SetTo(*(double*)buffer);
500				// TODO: double on the host might work differently!
501			return B_OK;
502		default:
503			return B_BAD_VALUE;
504	}
505}
506
507
508status_t
509ArchitectureX86::ReadValueFromMemory(target_addr_t addressSpace,
510	target_addr_t address, uint32 valueType, BVariant& _value) const
511{
512	// n/a on this architecture
513	return B_BAD_VALUE;
514}
515
516
517status_t
518ArchitectureX86::DisassembleCode(FunctionDebugInfo* function,
519	const void* buffer, size_t bufferSize, DisassembledCode*& _sourceCode)
520{
521	DisassembledCode* source = new(std::nothrow) DisassembledCode(
522		fAssemblyLanguage);
523	if (source == NULL)
524		return B_NO_MEMORY;
525	BReference<DisassembledCode> sourceReference(source, true);
526
527	// init disassembler
528	DisassemblerX86 disassembler;
529	status_t error = disassembler.Init(function->Address(), buffer, bufferSize);
530	if (error != B_OK)
531		return error;
532
533	// add a function name line
534	BString functionName(function->PrettyName());
535	if (!source->AddCommentLine((functionName << ':').String()))
536		return B_NO_MEMORY;
537
538	// disassemble the instructions
539	BString line;
540	target_addr_t instructionAddress;
541	target_size_t instructionSize;
542	bool breakpointAllowed;
543	while (disassembler.GetNextInstruction(line, instructionAddress,
544				instructionSize, breakpointAllowed) == B_OK) {
545// TODO: Respect breakpointAllowed!
546		if (!source->AddInstructionLine(line, instructionAddress,
547				instructionSize)) {
548			return B_NO_MEMORY;
549		}
550	}
551
552	_sourceCode = sourceReference.Detach();
553	return B_OK;
554}
555
556
557status_t
558ArchitectureX86::GetStatement(FunctionDebugInfo* function,
559	target_addr_t address, Statement*& _statement)
560{
561// TODO: This is not architecture dependent anymore!
562	// get the instruction info
563	InstructionInfo info;
564	status_t error = GetInstructionInfo(address, info, NULL);
565	if (error != B_OK)
566		return error;
567
568	// create a statement
569	ContiguousStatement* statement = new(std::nothrow) ContiguousStatement(
570		SourceLocation(-1), TargetAddressRange(info.Address(), info.Size()));
571	if (statement == NULL)
572		return B_NO_MEMORY;
573
574	_statement = statement;
575	return B_OK;
576}
577
578
579status_t
580ArchitectureX86::GetInstructionInfo(target_addr_t address,
581	InstructionInfo& _info, CpuState* state)
582{
583	// read the code - maximum x86{-64} instruction size = 15 bytes
584	uint8 buffer[16];
585	ssize_t bytesRead = fTeamMemory->ReadMemory(address, buffer,
586		sizeof(buffer));
587	if (bytesRead < 0)
588		return bytesRead;
589
590	// init disassembler
591	DisassemblerX86 disassembler;
592	status_t error = disassembler.Init(address, buffer, bytesRead);
593	if (error != B_OK)
594		return error;
595
596	return disassembler.GetNextInstructionInfo(_info, state);
597}
598
599
600status_t
601ArchitectureX86::GetWatchpointDebugCapabilities(int32& _maxRegisterCount,
602	int32& _maxBytesPerRegister, uint8& _watchpointCapabilityFlags)
603{
604	// while x86 technically has 4 hardware debug registers, one is reserved by
605	// the kernel, and one is required for breakpoint support, which leaves
606	// two available for watchpoints.
607	_maxRegisterCount = 2;
608	_maxBytesPerRegister = 4;
609
610	// x86 only supports write and read/write watchpoints.
611	_watchpointCapabilityFlags = WATCHPOINT_CAPABILITY_FLAG_WRITE
612		| WATCHPOINT_CAPABILITY_FLAG_READ_WRITE;
613
614	return B_OK;
615}
616
617
618status_t
619ArchitectureX86::GetReturnAddressLocation(StackFrame* frame,
620	target_size_t valueSize, ValueLocation*& _location)
621{
622	// for the calling conventions currently in use on Haiku,
623	// the x86 rules for how values are returned are as follows:
624	//
625	// - 32 bits or smaller values are returned directly in EAX.
626	// - 32-64 bit values are returned across EAX:EDX.
627	// - > 64 bit values are returned on the stack.
628	ValueLocation* location = new(std::nothrow) ValueLocation(
629		IsBigEndian());
630	if (location == NULL)
631		return B_NO_MEMORY;
632	BReference<ValueLocation> locationReference(location,
633		true);
634
635	if (valueSize <= 4) {
636		ValuePieceLocation piece;
637		piece.SetSize(valueSize);
638		piece.SetToRegister(X86_REGISTER_EAX);
639		if (!location->AddPiece(piece))
640			return B_NO_MEMORY;
641	} else if (valueSize <= 8) {
642		ValuePieceLocation piece;
643		piece.SetSize(4);
644		piece.SetToRegister(X86_REGISTER_EAX);
645		if (!location->AddPiece(piece))
646			return B_NO_MEMORY;
647		piece.SetToRegister(X86_REGISTER_EDX);
648		piece.SetSize(valueSize - 4);
649		if (!location->AddPiece(piece))
650			return B_NO_MEMORY;
651	} else {
652		ValuePieceLocation piece;
653		piece.SetToMemory(frame->GetCpuState()->StackPointer());
654		piece.SetSize(valueSize);
655		if (!location->AddPiece(piece))
656			return B_NO_MEMORY;
657	}
658
659	_location = locationReference.Detach();
660	return B_OK;
661}
662
663
664void
665ArchitectureX86::_AddRegister(int32 index, const char* name,
666	uint32 bitSize, uint32 valueType, register_type type, bool calleePreserved)
667{
668	if (!fRegisters.Add(Register(index, name, bitSize, valueType, type,
669			calleePreserved))) {
670		throw std::bad_alloc();
671	}
672}
673
674
675void
676ArchitectureX86::_AddIntegerRegister(int32 index, const char* name,
677	uint32 valueType, register_type type, bool calleePreserved)
678{
679	_AddRegister(index, name, 8 * BVariant::SizeOfType(valueType), valueType,
680		type, calleePreserved);
681}
682
683
684bool
685ArchitectureX86::_HasFunctionPrologue(FunctionDebugInfo* function) const
686{
687	if (function == NULL)
688		return false;
689
690	// check whether the function has the typical prologue
691	if (function->Size() < 3)
692		return false;
693
694	uint8 buffer[3];
695	if (fTeamMemory->ReadMemory(function->Address(), buffer, 3) != 3)
696		return false;
697
698	return buffer[0] == 0x55 && buffer[1] == 0x89 && buffer[2] == 0xe5;
699}
700