boinc/lib/mac/QBacktrace.c
/*
	Intel 64-Bit Signal Stack Frames
	--------------------------------
    Like PowerPC, x86-64 passes most parameters in registers.  This makes it 
    hard to cross signal stack frames successfully.  However, like PowerPC, some 
    heuristics get us there most of the time.
    
					Size	Purpose
					----	-------
	low memory
	
	frame  ->		0x008	pre-signal frame pointer
                    0x008   space for return address (unused)
					0x014?  alignment padding (typically 0x014 or 0x01c)
					0x2c4	struct mcontext64
								0x010	x86_exception_state64_t
								0x0A8	x86_thread_state64_t
								0x20c	x86_float_state64_t
					0x068	user_siginfo_t
					0x038	struct user_ucontext64
                    0x080   red zone
	high memory

	Things to note about the above:
	
	o The kernel places the pre-signal SP in the pad[0] field (offset 0x30) of 
      the user_siginfo_t structure.
    
    o Most of the time the kernel puts the pre-signal PC in the si_addr field
      of the same structure.  However, for SIGBUS and SIGSEGV this is actually 
      the address of the fault itself, so that doesn't help us.
	
	o The kernel aligns the stack such that the start of the alignment padding 
      is on a 16 byte boundary.  This means that there's a variable amount of pad 
      (from 0x10 through 0x1c) between the current frame and the user_siginfo_t.  
      Argh!

    o The reason why this alignment padding is typically either 0x014 or 0x01c is 
      that the (struct mcontext64) is 0x2c4 bytes.  So, if the stack was reasonably 
      aligned (that is, to an 8 byte boundary) when the signal occurs, by the time 
      we've deducted space for all our junk (0x080 + 0x038 + 0x068 + 0x2c4) we've 
      pushed the alignment to a 4 byte boundary.  To bring it back, we have to 
      pad to with either 0x014 or 0x01c.
    
      However, rather than just guess, I actually try all possible alignments 
      (0x000 through 0x01c) and see which one makes sense.

    Another thing to note is that SA_64REGSET flag isn't significant on x86.  
    A 32-bit process will always get 32-bit structures and a 64-bit process will 
    always get 64-bit structures.  This makes things somewhat easier than on 
    PowerPC.

	Finally, if we detect a frameless leaf routine past the signal frame, 
	we extract its return address from the top of stack.
*/
Intel 64-Bit Signal Stack Frames
Like PowerPC, x86-64 passes most parameters in registers.  This makes it
hard to cross signal stack frames successfully.  However, like PowerPC, some
heuristics get us there most of the time.

Size    Purpose

low memory

frame  ->               0x008   pre-signal frame pointer
0x008   space for return address (unused)
0x014.  alignment padding (typically 0x014 or 0x01c)
0x2c4   struct mcontext64
0x010   x86_exception_state64_t
0x0A8   x86_thread_state64_t
0x20c   x86_float_state64_t
0x068   user_siginfo_t
0x038   struct user_ucontext64
0x080   red zone
high memory

Things to note about the above.

o The kernel places the pre-signal SP in the pad[0] field (offset 0x30) of
the user_siginfo_t structure.

o Most of the time the kernel puts the pre-signal PC in the si_addr field
of the same structure.  However, for SIGBUS and SIGSEGV this is actually
the address of the fault itself, so that doesn't help us.

o The kernel aligns the stack such that the start of the alignment padding
is on a 16 byte boundary.  This means that there's a variable amount of pad
(from 0x10 through 0x1c) between the current frame and the user_siginfo_t.
Argh!

o The reason why this alignment padding is typically either 0x014 or 0x01c is
that the (struct mcontext64) is 0x2c4 bytes.  So, if the stack was reasonably
aligned (that is, to an 8 byte boundary) when the signal occurs, by the time
we've deducted space for all our junk (0x080 + 0x038 + 0x068 + 0x2c4) we've
pushed the alignment to a 4 byte boundary.  To bring it back, we have to
pad to with either 0x014 or 0x01c.

However, rather than just guess, I actually try all possible alignments
(0x000 through 0x01c) and see which one makes sense.

Another thing to note is that SA_64REGSET flag isn't significant on x86.
A 32-bit process will always get 32-bit structures and a 64-bit process will
always get 64-bit structures.  This makes things somewhat easier than on
PowerPC.

Finally, if we detect a frameless leaf routine past the signal frame,
we extract its return address from the top of stack.
Intel 64 - Bit Signal Stack Frames Like PowerPC x86 - 64 passes most parameters in registers . This makes it hard to cross signal stack frames successfully . However like PowerPC some heuristics get us there most of the time . Size Purpose low memory frame - > 0x008 pre - signal frame pointer 0x008 space for return address ( unused ) 0x014 . alignment padding ( typically 0x014 or 0x01c ) 0x2c4 struct mcontext64 0x010 x86_exception_state64_t 0x0A8 x86_thread_state64_t 0x20c x86_float_state64_t 0x068 user_siginfo_t 0x038 struct user_ucontext64 0x080 red zone high memory Things to note about the above . o The kernel places the pre - signal SP in the pad [ 0 ] field ( offset 0x30 ) of the user_siginfo_t structure . o Most of the time the kernel puts the pre - signal PC in the si_addr field of the same structure . However for SIGBUS and SIGSEGV this is actually the address of the fault itself so that doesn ' t help us . o The kernel aligns the stack such that the start of the alignment padding is on a 16 byte boundary . This means that there ' s a variable amount of pad ( from 0x10 through 0x1c ) between the current frame and the user_siginfo_t . Argh! o The reason why this alignment padding is typically either 0x014 or 0x01c is that the ( struct mcontext64 ) is 0x2c4 bytes . So if the stack was reasonably aligned ( that is to an 8 byte boundary ) when the signal occurs by the time we ' ve deducted space for all our junk ( 0x080 + 0x038 + 0x068 + 0x2c4 ) we ' ve pushed the alignment to a 4 byte boundary . To bring it back we have to pad to with either 0x014 or 0x01c . However rather than just guess I actually try all possible alignments ( 0x000 through 0x01c ) and see which one makes sense . Another thing to note is that SA_64REGSET flag isn ' t significant on x86 . A 32 - bit process will always get 32 - bit structures and a 64 - bit process will always get 64 - bit structures . This makes things somewhat easier than on PowerPC . Finally if we detect a frameless leaf routine past the signal frame we extract its return address from the top of stack .
static int Intel64CrossSignalFrame(QBTContext *context, QTMAddr thisFrame, QTMAddr *nextPCPtr, QTMAddr *nextFramePtr)
{
	int         err;
	QTMAddr     sigInfo;
    QTMAddr     ucontext;
    QTMAddr     mcontext;
    QTMAddr     threadState;
	QTMAddr     preSignalFP;
    QTMAddr     preSignalSP;
	QTMAddr     preSignalFPFromMContext;
    QTMAddr     preSignalSPFromMContext;
    QTMAddr     align;
    err = ReadAddr(context, thisFrame, &preSignalFP);
    if (err == 0) {        
        for (align = 0x010; align < 0x020; align += 4) {
            sigInfo = thisFrame + 0x008 + 0x08 + align + 0x2c4;
            ucontext = sigInfo + 0x068;
            err = ReadAddr(context, sigInfo + 0x030, &preSignalSP);
            if (err == 0) {
                err = ReadAddr(context, ucontext + 0x030, &mcontext);
            }
            if (err == 0) {
                threadState = mcontext + 0x10;
                err = ReadAddr(context, threadState + 6 * 0x08, &preSignalFPFromMContext);
            }
            if (err == 0) {
                err = ReadAddr(context, threadState + 7 * 0x08, &preSignalSPFromMContext);
            }
            if ( (err == 0) 
              && (preSignalFP == preSignalFPFromMContext) 
              && (preSignalSP == preSignalSPFromMContext) ) {
                break;
            }
        }
        err = 0;
        if (align == 0x020) {
            assert(false);
            align = 0x014;
        }
    }
    if (err == 0) {
        *nextFramePtr = preSignalFP;
        sigInfo  = thisFrame + 0x008 + 0x08 + align + 0x2c4;
        ucontext = sigInfo + 0x068;
		err = ReadAddr(context, sigInfo + 0x030, &preSignalSP);
        if (err == 0) {
            err = ReadAddr(context, ucontext + 0x030, &mcontext);
        }
        if (err == 0) {
            threadState = mcontext + 0x10;
            err = ReadAddr(context, threadState + 16 * 0x08, nextPCPtr);
        }
	}
	if ( (err == 0) && IntelIsSystemCall(context, *nextPCPtr) ) {
		AddFrame(context, *nextPCPtr, 0, kQBTFrameBadMask);
        err = ReadAddr(context, preSignalSP, nextPCPtr);
	}
	return err;
}
static int Intel64CrossSignalFrame ( QBTContext * context , QTMAddr thisFrame , QTMAddr * nextPCPtr , QTMAddr * nextFramePtr ) { int err ; QTMAddr sigInfo ; QTMAddr ucontext ; QTMAddr mcontext ; QTMAddr threadState ; QTMAddr preSignalFP ; QTMAddr preSignalSP ; QTMAddr preSignalFPFromMContext ; QTMAddr preSignalSPFromMContext ; QTMAddr align ; err = ReadAddr ( context , thisFrame , & preSignalFP ) ; if ( err == 0 ) { for ( align = 0x010 ; align < 0x020 ; align += 4 ) { sigInfo = thisFrame + 0x008 + 0x08 + align + 0x2c4 ; ucontext = sigInfo + 0x068 ; err = ReadAddr ( context , sigInfo + 0x030 , & preSignalSP ) ; if ( err == 0 ) { err = ReadAddr ( context , ucontext + 0x030 , & mcontext ) ; } if ( err == 0 ) { threadState = mcontext + 0x10 ; err = ReadAddr ( context , threadState + 6 * 0x08 , & preSignalFPFromMContext ) ; } if ( err == 0 ) { err = ReadAddr ( context , threadState + 7 * 0x08 , & preSignalSPFromMContext ) ; } if ( ( err == 0 ) && ( preSignalFP == preSignalFPFromMContext ) && ( preSignalSP == preSignalSPFromMContext ) ) { break ; } } err = 0 ; if ( align == 0x020 ) { assert ( false ) ; align = 0x014 ; } } if ( err == 0 ) { * nextFramePtr = preSignalFP ; sigInfo = thisFrame + 0x008 + 0x08 + align + 0x2c4 ; ucontext = sigInfo + 0x068 ; err = ReadAddr ( context , sigInfo + 0x030 , & preSignalSP ) ; if ( err == 0 ) { err = ReadAddr ( context , ucontext + 0x030 , & mcontext ) ; } if ( err == 0 ) { threadState = mcontext + 0x10 ; err = ReadAddr ( context , threadState + 16 * 0x08 , nextPCPtr ) ; } } if ( ( err == 0 ) && IntelIsSystemCall ( context , * nextPCPtr ) ) { AddFrame ( context , * nextPCPtr , 0 , kQBTFrameBadMask ) ; err = ReadAddr ( context , preSignalSP , nextPCPtr ) ; } return err ; }
Intel 64-Bit Signal Stack Frames
Like PowerPC, x86-64 passes most parameters in registers.
Intel 64 - Bit Signal Stack Frames Like PowerPC x86 - 64 passes most parameters in registers .
// This is the crossSignalFrame routine for the 64-bit Intel   // architecture.  See the description of  // QBTCrossSignalFrameProc for a detailed discussion  // of its parameters. // Get the previous frame by simply reading from the frame pointer.  // Because of the way things work, this ends up being correct. // Now try various alignments to see which one yields a valid result. // Use the 'Hail Mary (tm)' algorithm to get a pointer to the user_siginfo_t  // and the (struct user_ucontext64) that immediately follows it. // Now check whether it makes sense... // Get the previous SP from the pad[0] field of the user_siginfo_t. // Get the uc_mcontext64 field of the (struct user_ucontext64) that  // immediately follows the user_siginfo_t.  This results in a pointer  // to our (struct mcontext64). // Calculate the start of the x86_thread_state64_t structure at a fixed  // offset from the start of the (struct mcontext64) and get the  // pre-signal FP and SP from that. // At this point we throw away any error status from the above code. // If none of the alignments worked, just take a guess.  This is  // sufficienly bad that we want to know about it in the debug version. // Get the address of the sigInfo using the alignment calculated above  // and then use it to set the result. // This value is /always/ right, so we copy it out to the client immediately. // Get the pre-signal SP and mcontext as before. // And from that the thread state and the previous PC. // Finally, if we detect a leaf routine, add a dummy frame for it  // and then get the pre-signal SP and, assuming that the top word on  // the stack is a return address, use it for the next PC.