boinc/lib/mac/QBacktrace.c
/*	Intel Stack Frame Basics
	------------------------
	
                        Offset	Size	Purpose
                        ------	----	-------
	low memory
	sp == ESP/RSP ->    -??     ??		general work area
                        -??		??		local variables
	fp == EBP/RBP ->	0		X		pointer to next frame
                        X		X		return address
                        -??		??		parameters
	high memory

                    where X is the address size (4 bytes for 32-bits, 
                    8 bytes for 64-bits)

	The stack frame on Intel is remarkably traditional.  Two registers 
	are used to manage the stack: ESP/RSP points to the bottom of the stack, 
	and EBP/RBP points to the stack frame itself.  The memory at offset 0 
	off the frame stores the address of the next stack frame.  The memory at 
	offset X stores the saved PC for the next stack frame (that is, the return 
	address for this stack frame).
*/




The stack frame on Intel is remarkably traditional.  Two registers
are used to manage the stack: ESP/RSP points to the bottom of the stack,
and EBP/RBP points to the stack frame itself.  The memory at offset 0
off the frame stores the address of the next stack frame.  The memory at
offset X stores the saved PC for the next stack frame (that is, the return
address for this stack frame).
The stack frame on Intel is remarkably traditional . Two registers are used to manage the stack : ESP / RSP points to the bottom of the stack and EBP / RBP points to the stack frame itself . The memory at offset 0 off the frame stores the address of the next stack frame . The memory at offset X stores the saved PC for the next stack frame ( that is the return address for this stack frame ) .
static bool IntelIsSystemCall(QBTContext *context, QTMAddr pc)
	 For 64-bit, all systems calls are done via SYSCALL.  Nice!
     For 32-bit, we need to look for two instructions:
	 o INT 81 -- used for Mach system calls
	 o sysenter -- used by BSD system calls
	 We detect INT 81 simply by looking for its bytes.  It's no 
	 so easy to detect sysenter, because the PC we get is an 
	 address in the specific system call, which actually calls 
	 another routine (_sysenter_trap) to do the sysenter.  
	 We look for the CALL disp32 instruction and, if we see, 
	 work out the address that it calls.  We then get the 
	 instructions from that address.  If that looks like a 
	 sysenter, we're probably looking at a system call.
{
	int	err;
	bool		isSystemCall;
	uint8_t		buf[5];
	uint32_t	sysEnterOffset;
	isSystemCall = false;
	err = context->readBytes(context, pc - sizeof(buf), buf, sizeof(buf));
	if (err == 0) {
        if (context->arch->is64Bit) {
            isSystemCall = ( buf[3] == 0x0f && buf [4] == 0x05 );            syscall
        } else {
            isSystemCall = ( buf[3] == 0xcd && buf[4] == 0x81);				 INT 81
            if ( ! isSystemCall && (buf[0] == 0xe8) ) {						 CALL disp32
                 Get the disp32.
                sysEnterOffset = (buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24));
                 Read the instructions at that offset from the PC and see if they're 
                 the standard _sysenter_trap code.
                 It's a happy coincidence that the size of the _sysenter_trap code is 
                 5 bytes, which is also the size of the buffer that I have lying around 
                 to read the instructions in front of the PC.  The upshot is that I can 
                 reuse buf rather than needing a second one.
                err = context->readBytes(context, pc + sysEnterOffset, buf, sizeof(buf));
                if (err == 0) {
                    isSystemCall = (buf[0] == 0x5a)								 pop      %edx
                                && (buf[1] == 0x89)	&& (buf[2] == 0xe1)			 mov      %esp,%ecx
                                && (buf[3] == 0x0f) && (buf[4] == 0x34);		 sysenter
                }
            }
        }
	}
	return isSystemCall;
}
static bool IntelIsSystemCall ( QBTContext * context , QTMAddr pc ) { int err ; bool isSystemCall ; uint8_t buf [ 5 ] ; uint32_t sysEnterOffset ; isSystemCall = false ; err = context -> readBytes ( context , pc - sizeof ( buf ) , buf , sizeof ( buf ) ) ; if ( err == 0 ) { if ( context -> arch -> is64Bit ) { isSystemCall = ( buf [ 3 ] == 0x0f && buf [ 4 ] == 0x05 ) ; } else { isSystemCall = ( buf [ 3 ] == 0xcd && buf [ 4 ] == 0x81 ) ; if ( ! isSystemCall && ( buf [ 0 ] == 0xe8 ) ) { sysEnterOffset = ( buf [ 1 ] | ( buf [ 2 ] << 8 ) | ( buf [ 3 ] << 16 ) | ( buf [ 4 ] << 24 ) ) ; err = context -> readBytes ( context , pc + sysEnterOffset , buf , sizeof ( buf ) ) ; if ( err == 0 ) { isSystemCall = ( buf [ 0 ] == 0x5a ) && ( buf [ 1 ] == 0x89 ) && ( buf [ 2 ] == 0xe1 ) && ( buf [ 3 ] == 0x0f ) && ( buf [ 4 ] == 0x34 ) ; } } } } return isSystemCall ; }
Intel Stack Frame Basics
Offset  Size    Purpose
  
low memory
sp == ESP/RSP ->    -??
Intel Stack Frame Basics Offset Size Purpose low memory sp == ESP / RSP - > - ??
// Using the PC from the thread state, look back in the code  // stream to see if the previous bytes look something like a  // system call.  This is a heuristic rather than solid design.  // Because Intel instructions are of variable length, there's no  // guarantee that these bytes are part of some other instruction.  // Still, it works most of the time. // // For 64-bit, all systems calls are done via SYSCALL.  Nice! // // For 32-bit, we need to look for two instructions: // // o INT 81 -- used for Mach system calls // o sysenter -- used by BSD system calls //  // We detect INT 81 simply by looking for its bytes.  It's no  // so easy to detect sysenter, because the PC we get is an  // address in the specific system call, which actually calls  // another routine (_sysenter_trap) to do the sysenter.   // We look for the CALL disp32 instruction and, if we see,  // work out the address that it calls.  We then get the  // instructions from that address.  If that looks like a  // sysenter, we're probably looking at a system call. // syscall // INT 81 // CALL disp32 // Get the disp32. // Read the instructions at that offset from the PC and see if they're  // the standard _sysenter_trap code. // // It's a happy coincidence that the size of the _sysenter_trap code is  // 5 bytes, which is also the size of the buffer that I have lying around  // to read the instructions in front of the PC.  The upshot is that I can  // reuse buf rather than needing a second one. // pop      %edx // mov      %esp,%ecx // sysenter