mirror of https://github.com/BOINC/boinc.git
2532 lines
87 KiB
C
2532 lines
87 KiB
C
// This file is part of BOINC.
|
|
// http://boinc.berkeley.edu
|
|
// Copyright (C) 2008 University of California
|
|
//
|
|
// BOINC is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License
|
|
// as published by the Free Software Foundation,
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// BOINC is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
/*
|
|
* QBacktrace.c
|
|
*
|
|
*/
|
|
|
|
/* This is part of a backtrace generator for boinc project applications.
|
|
*
|
|
* Adapted from Apple Developer Technical Support Sample Code QCrashReport
|
|
*
|
|
* This code handles Mac OS X 10.3.x through 10.4.9. It may require some
|
|
* adjustment for future OS versions; see the discussion of _sigtramp and
|
|
* PowerPC Signal Stack Frames below.
|
|
*
|
|
* For useful tips on using backtrace information, see Apple Tech Note 2123:
|
|
* http://developer.apple.com/technotes/tn2004/tn2123.html#SECNOSYMBOLS
|
|
*
|
|
* To convert addresses to correct symbols, use the atos command-line tool:
|
|
* atos -o path/to/executable/with/symbols address
|
|
* Note: if address 1a23 is hex, use 0x1a23.
|
|
*
|
|
* To demangle mangled C++ symbols, use the c++filt command-line tool.
|
|
* You may need to prefix C++ symbols with an additonal underscore before
|
|
* passing them to c++filt (so they begin with two underscore characters).
|
|
*
|
|
* A very useful shell script to add symbols to a crash dump can be found at:
|
|
* http://developer.apple.com/tools/xcode/symbolizingcrashdumps.html
|
|
* Pipe the output of the shell script through c++filt to demangle C++ symbols.
|
|
*/
|
|
|
|
/*
|
|
File: QBacktrace.c
|
|
|
|
Contains: Code for generating backtraces.
|
|
|
|
Written by: DTS
|
|
|
|
Copyright: Copyright (c) 2007 Apple Inc. All Rights Reserved.
|
|
|
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
|
("Apple") in consideration of your agreement to the following
|
|
terms, and your use, installation, modification or
|
|
redistribution of this Apple software constitutes acceptance of
|
|
these terms. If you do not agree with these terms, please do
|
|
not use, install, modify or redistribute this Apple software.
|
|
|
|
In consideration of your agreement to abide by the following
|
|
terms, and subject to these terms, Apple grants you a personal,
|
|
non-exclusive license, under Apple's copyrights in this
|
|
original Apple software (the "Apple Software"), to use,
|
|
reproduce, modify and redistribute the Apple Software, with or
|
|
without modifications, in source and/or binary forms; provided
|
|
that if you redistribute the Apple Software in its entirety and
|
|
without modifications, you must retain this notice and the
|
|
following text and disclaimers in all such redistributions of
|
|
the Apple Software. Neither the name, trademarks, service marks
|
|
or logos of Apple Inc. may be used to endorse or promote
|
|
products derived from the Apple Software without specific prior
|
|
written permission from Apple. Except as expressly stated in
|
|
this notice, no other rights or licenses, express or implied,
|
|
are granted by Apple herein, including but not limited to any
|
|
patent rights that may be infringed by your derivative works or
|
|
by other works in which the Apple Software may be incorporated.
|
|
|
|
The Apple Software is provided by Apple on an "AS IS" basis.
|
|
APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
|
WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
|
THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
COMBINATION WITH YOUR PRODUCTS.
|
|
|
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
|
INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
|
OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
|
OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
|
OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
|
SUCH DAMAGE.
|
|
|
|
Change History (most recent first):
|
|
|
|
$Log: QBacktrace.c,v $
|
|
Revision 1.2 2007/03/02 13:00:08
|
|
Quieten some warnings.
|
|
|
|
Revision 1.1 2007/03/02 12:19:49
|
|
First checked in.
|
|
|
|
|
|
*/
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
// Compatibility with OS 10.5 SDK and later
|
|
#ifdef __DARWIN_UNIX03
|
|
#undef __DARWIN_UNIX03
|
|
#endif
|
|
#define _NONSTD_SOURCE 1
|
|
#define __DARWIN_UNIX03 0
|
|
|
|
// Our Prototypes
|
|
|
|
#include "QBacktrace.h"
|
|
|
|
// Mac OS Interfaces
|
|
|
|
#include <TargetConditionals.h>
|
|
#include <AvailabilityMacros.h>
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <pthread.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <mach-o/arch.h>
|
|
|
|
#if defined(__cplusplus)
|
|
extern "C" {
|
|
#endif
|
|
|
|
// Some extra Mach interfaces that we don't need in the public header.
|
|
// Again, we need C++ guards.
|
|
|
|
// We want both PowerPC and Intel thread state information.
|
|
// By default, the system only gives us the one that's appropriate
|
|
// for our machine. So we include both here.
|
|
|
|
|
|
#if TARGET_CPU_PPC
|
|
#include <mach/ppc/thread_status.h>
|
|
#endif
|
|
#include <mach/i386/thread_status.h>
|
|
|
|
#if defined(__cplusplus)
|
|
}
|
|
#endif
|
|
|
|
#include "QSymbols.h"
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
|
|
// A new architecture will require substantial changes to this file.
|
|
|
|
#if ! (TARGET_CPU_PPC || TARGET_CPU_PPC64 || TARGET_CPU_X86 || TARGET_CPU_X86_64)
|
|
#error QBacktrace: What architecture?
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
#pragma mark ***** Architecture Specification
|
|
|
|
// This is an extra flag for the flags field of the QBTFrame structure.
|
|
// It is true if the symbol field of that structure was allocated by
|
|
// QBacktrace and should be disposed by QBacktraceDisposeSymbols.
|
|
|
|
enum {
|
|
kQBTFrameSymbolNeedsDisposeMask = 0x0100
|
|
};
|
|
|
|
typedef struct QBTContext QBTContext;
|
|
// forward declaration
|
|
|
|
// Architecture Callbacks -- Called by the core to do architecture-specific tasks.
|
|
|
|
typedef int (*QBTHandleLeafProc)(QBTContext *context, QTMAddr *pcPtr, QTMAddr *fpPtr);
|
|
// This callback is called by the core to start a backtrace.
|
|
// It should extract the first PC and frame from the thread state
|
|
// in the context and return them to the core. Also, if the
|
|
// routine detects a frameless leaf routine, it should add a
|
|
// dummy frame for that routine (by calling AddFrame).
|
|
//
|
|
// On entry, context will be a valid context (as determined by QBTContextIsValid).
|
|
// On entry, pcPtr will not be NULL.
|
|
// On entry, fpPtr will not be NULL.
|
|
// Returns an errno-style error code.
|
|
// On success, *pcPtr must be the PC of the first non-leaf frame.
|
|
// On success, *fpPtr must be the frame pointer of the first non-leaf frame.
|
|
|
|
typedef bool (*QBTValidPCProc)(QBTContext *context, QTMAddr pc);
|
|
// This callback is called by the core to check whether a PC address
|
|
// is valid. This is architecture-specific; for example, on PowerPC a
|
|
// PC value must be a multiple of 4, whereas an Intel an instruction
|
|
// can start at any address.
|
|
//
|
|
// At a minimum, an implementation is expected to check the PC's alignment
|
|
// and read the instruction at the PC.
|
|
//
|
|
// IMPORTANT:
|
|
// The core code assumes that (QTMAddr) -1 is never a valid PC.
|
|
// If that isn't true for your architecture, you'll need to eliminate
|
|
// that assumption from the core.
|
|
//
|
|
// On entry, context will be a valid context (as determined by QBTContextIsValid).
|
|
// On entry, pc can be any value.
|
|
// Returns true if the PC looks reasonably valid, false otherwise.
|
|
|
|
typedef int (*QBTGetFrameNextPCProc)(QBTContext *context, QTMAddr thisFrame, QTMAddr nextFrame, QTMAddr *nextPCPtr);
|
|
// This callback is called by the core to get the PC associated with
|
|
// the next frame. This is necessary because different architectures
|
|
// store the PC in different places. Specifically, PowerPC stores
|
|
// the PC of a frame in that frame, whereas Intel stores the PC of
|
|
// a frame in the previous frame (that is, the PC value in the frame
|
|
// is actually a return address).
|
|
//
|
|
// On entry, context will be a valid context (as determined by QBTContextIsValid).
|
|
// On entry, thisFrame will be a valid frame.
|
|
// On entry, nextFrame will be the valid frame following thisFrame.
|
|
// On entry, nextPCPtr will not be NULL.
|
|
// Returns an errno-style error code.
|
|
// On success, *nextPCPtr must be the PC associated with nextFrame.
|
|
|
|
typedef int (*QBTCrossSignalFrameProc)(QBTContext *context, QTMAddr thisFrame, QTMAddr *nextPCPtr, QTMAddr *nextFramePtr);
|
|
// This callback is called by the core when it detects a cross signal
|
|
// frame and wants to cross that frame in an architecture-specific
|
|
// manner. The code gets a pointer to the cross-signal handler frame
|
|
// and is expected to return the PC and frame pointer of the first
|
|
// non-leaf frame on the other side. Furthermore, it must detect if
|
|
// the first frame on the other side is a leaf frame and add a
|
|
// dummy frame for that routine (by calling AddFrame) before returning.
|
|
//
|
|
// An implementation does not have to check the validity of the
|
|
// returned PC and frame. The core will do that for you.
|
|
//
|
|
// On entry, context will be a valid context (as determined by QBTContextIsValid).
|
|
// On entry, thisFrame will be a valid cross-signal handler frame.
|
|
// On entry, nextPCPtr will not be NULL.
|
|
// On entry, nextFramePtr will not be NULL.
|
|
// Returns an errno-style error code.
|
|
// On success, *nextPCPtr must be the PC of the next non-leaf frame.
|
|
// On success, *nextFramePtr must be the PC of the next non-leaf frame.
|
|
|
|
// Architecture Specification Structure -- Aggregates all of the information
|
|
// associated with a particular architecture.
|
|
|
|
struct QBTArchInfo {
|
|
const char * name; // just for debugging
|
|
|
|
// Information to identify the architecture
|
|
|
|
cpu_type_t cputype; // per NXGetLocalArchInfo in <mach-o/arch.h>
|
|
cpu_subtype_t cpusubtype; // per NXGetLocalArchInfo in <mach-o/arch.h>, 0 for wildcard
|
|
bool is64Bit;
|
|
|
|
// Misc information about the architecture
|
|
|
|
QTMAddr frameAlignMask; // mask to detect frame misalignment
|
|
// FP & frameAlignMask must be 0 for a valid frame
|
|
// Architecture-specific backtrace callbacks
|
|
|
|
QBTHandleLeafProc handleLeaf; // described in detail above
|
|
QBTValidPCProc validPC; // described in detail above
|
|
QBTGetFrameNextPCProc getFrameNextPC; // described in detail above
|
|
QBTCrossSignalFrameProc crossSignalFrame; // described in detail above
|
|
|
|
// Specification of how to call thread_get_state
|
|
|
|
thread_state_flavor_t stateFlavor;
|
|
mach_msg_type_number_t stateCount;
|
|
};
|
|
typedef struct QBTArchInfo QBTArchInfo;
|
|
|
|
static const QBTArchInfo kArchitectures[];
|
|
// forward declaration
|
|
|
|
static const QBTArchInfo * GetTaskArch(QMOImageRef qmoImage)
|
|
// Returns a pointer to the architecture associated with the specific
|
|
// task, or NULL if it's an architecture we don't know about.
|
|
{
|
|
const QBTArchInfo * result;
|
|
const QBTArchInfo * thisArch;
|
|
bool is64Bit;
|
|
cpu_type_t cputype;
|
|
cpu_subtype_t cpusubtype;
|
|
|
|
assert(qmoImage != NULL);
|
|
|
|
result = NULL;
|
|
|
|
// Get the architecture characteristics from the image.
|
|
|
|
is64Bit = QMOImageIs64Bit(qmoImage);
|
|
cputype = QMOImageGetCPUType(qmoImage);
|
|
cpusubtype = QMOImageGetCPUSubType(qmoImage);
|
|
|
|
// Look through the architecture array for an architecture that matches the
|
|
// target architecture. Also, we prefer architectures with an exact CPU
|
|
// subtype match, but we'll accept those with a 0 CPU subtype.
|
|
|
|
thisArch = &kArchitectures[0];
|
|
while ( (thisArch->cputype != 0) && (result == NULL) ) {
|
|
if ( (thisArch->cputype == cputype)
|
|
&& ((thisArch->cpusubtype == 0) || (thisArch->cpusubtype == cpusubtype))
|
|
&& (thisArch->is64Bit == is64Bit)
|
|
) {
|
|
result = thisArch;
|
|
} else {
|
|
thisArch += 1;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
#pragma mark ***** Backtrace Core
|
|
|
|
// Memory Read Callback
|
|
|
|
typedef int (*QBTReadBytesProc)(QBTContext *context, QTMAddr src, void *dst, size_t size);
|
|
// This function pointer is called by the core backtrace code
|
|
// when it needs to read memory. The callback should do a safe
|
|
// read of size bytes from src into the buffer specified by
|
|
// dst. By "safe" we mean that the routine should return an error
|
|
// if the read can't be done (typically because src is a pointer to
|
|
// unmapped memory). It does not need to do any word size adjustment
|
|
// or byte swapping.
|
|
//
|
|
// On entry, context will be a valid context (as determined by QBTContextIsValid).
|
|
// On entry, src can be any value.
|
|
// On entry, dst will not be NULL.
|
|
// On entry, size will be greater than 0.
|
|
// Returns an errno-style error code.
|
|
// On success, the routine has copied size bytes of data from the address src
|
|
// in the remote task to the address dst in the local task.
|
|
// On error, the value at dst is unspecified.
|
|
//
|
|
// Note:
|
|
// In previous versions of QBacktrace, I supported alternative ways to
|
|
// read bytes from the target. For example, I could use the Carbon
|
|
// exception handler mechanism <CoreServices/MachineExceptions.h> to read
|
|
// data from the current task in a safe fashion. This was useful because
|
|
// it would work on both Mac OS X and traditional Mac OS. Now that I
|
|
// require Mac OS X and Mach-O, I can just use the Mach routines to do
|
|
// my reading. However, I've left the abstraction layer in place,
|
|
// Just In Case (tm).
|
|
|
|
struct QBTContext {
|
|
|
|
// Internal parameters that are set up by the caller
|
|
// of the core backtrace code.
|
|
|
|
const QBTArchInfo * arch; // architecture specific info
|
|
task_t task; // target task
|
|
QSymbolsRef symRef; // symbols object for target task
|
|
bool createdSymRef; // true if we created the symbols object
|
|
thread_state_flavor_t threadStateFlavor; // flavor of thread state
|
|
const void * threadState; // architecture-specific current thread state
|
|
// for example, on Intel this is x86_thread_state32_t
|
|
size_t threadStateSize; // size of threadState
|
|
QBTReadBytesProc readBytes; // described in detail above
|
|
|
|
// Stuff worked out internally by MachInitContextFromTask.
|
|
|
|
bool swapBytes; // true if target and current task have different byte orders
|
|
QTMAddr sigTrampLowerBound; // bounds of _sigtramp in target task
|
|
QTMAddr sigTrampUpperBound;
|
|
|
|
// Parameters from client.
|
|
|
|
QTMAddr stackBottom; // bounds of stack in target task
|
|
QTMAddr stackTop;
|
|
QBTFrame * frameArray; // array contents filled out by core
|
|
size_t frameArrayCount; // size of frameArray
|
|
size_t frameCountOut; // returned by core
|
|
};
|
|
|
|
#if ! defined(NDEBUG)
|
|
|
|
// Because QBTContextIsValid is only referenced by assert macros, and these
|
|
// are disabled by NDEBUG, we have to conditionalise this code based on
|
|
// NDEBUG lest we get a "defined but not used" warning.
|
|
|
|
static bool QBTContextIsValid(const QBTContext *context)
|
|
{
|
|
return (context != NULL)
|
|
&& (context->arch != NULL)
|
|
&& (context->task != MACH_PORT_NULL)
|
|
&& (context->symRef != NULL)
|
|
&& ( (context->threadState == NULL) == (context->threadStateSize == 0) )
|
|
&& (context->readBytes != NULL)
|
|
&& (context->sigTrampLowerBound != 0)
|
|
&& (context->sigTrampLowerBound < context->sigTrampUpperBound)
|
|
&& (context->stackBottom <= context->stackTop)
|
|
&& ((context->frameArrayCount == 0) || (context->frameArray != NULL));
|
|
}
|
|
|
|
#endif
|
|
|
|
static int BacktraceAdorn(QBTContext *context)
|
|
// Add symbols to all of the backtrace frames referenced by the context.
|
|
// Uses the 'bulk' symbols-to-address routine, in the hope that this will
|
|
// be faster one day. Also uses the kQBTFrameSymbolNeedsDisposeMask flag
|
|
// to tag each frame to indicate whether the client needs to dispose it.
|
|
//
|
|
// Note that failing to find symbol information for a particular address
|
|
// will not cause this routine to fail. Rather, the only causes of failure
|
|
// are really bad things, like running out of memory.
|
|
{
|
|
int err;
|
|
size_t frameCount;
|
|
size_t frameIndex;
|
|
QTMAddr * addrs;
|
|
QSymSymbolInfo * infos;
|
|
|
|
assert(context != NULL);
|
|
assert(context->symRef != NULL);
|
|
assert(context->frameArray != NULL);
|
|
assert(context->frameArrayCount != 0);
|
|
assert(context->frameCountOut != 0);
|
|
|
|
// Only do the frames that actually exist.
|
|
|
|
frameCount = context->frameCountOut;
|
|
if (frameCount > context->frameArrayCount) {
|
|
frameCount = context->frameArrayCount;
|
|
}
|
|
assert(frameCount > 0); // because of our pre-conditions
|
|
|
|
// Allocate arrays for call QSymGetSymbolsForAddresses.
|
|
|
|
addrs = (QTMAddr *) calloc(frameCount, sizeof(*addrs));
|
|
infos = (QSymSymbolInfo *) calloc(frameCount, sizeof(*infos));
|
|
err = 0;
|
|
if ((addrs == NULL) || (infos == NULL)) {
|
|
err = ENOMEM;
|
|
}
|
|
|
|
// Set up the addrs array and call QSymGetSymbolsForAddresses.
|
|
|
|
if (err == 0) {
|
|
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
addrs[frameIndex] = context->frameArray[frameIndex].pc;
|
|
}
|
|
|
|
err = QSymGetSymbolsForAddresses(
|
|
context->symRef,
|
|
frameCount,
|
|
addrs,
|
|
infos
|
|
);
|
|
}
|
|
|
|
// Place the symbol information into the output frames.
|
|
//
|
|
// For error handling to work, you must not error past this point. Otherwise
|
|
// we break the post-condition that the user doesn't need to do anything on
|
|
// error.
|
|
|
|
if (err == 0) {
|
|
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
if (infos[frameIndex].symbolType != kQSymNoSymbol) {
|
|
if (context->createdSymRef) {
|
|
const char * libName;
|
|
|
|
// Within this block, we ignore any failures; they just leave
|
|
// the relevant string NULL, which the dispose routine can
|
|
// handle.
|
|
|
|
context->frameArray[frameIndex].symbol = strdup(infos[frameIndex].symbolName);
|
|
|
|
libName = QMOImageGetFilePath(infos[frameIndex].symbolImage);
|
|
if (libName != NULL) {
|
|
context->frameArray[frameIndex].library = strdup(libName);
|
|
}
|
|
|
|
// Tell the dispose routine that we need to look at this frame.
|
|
|
|
context->frameArray[frameIndex].flags |= kQBTFrameSymbolNeedsDisposeMask;
|
|
} else {
|
|
assert(infos[frameIndex].symbolImage != NULL);
|
|
context->frameArray[frameIndex].symbol = infos[frameIndex].symbolName;
|
|
context->frameArray[frameIndex].library = QMOImageGetFilePath(infos[frameIndex].symbolImage);
|
|
}
|
|
context->frameArray[frameIndex].offset = infos[frameIndex].symbolOffset;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean up.
|
|
|
|
free(addrs);
|
|
free(infos);
|
|
|
|
return err;
|
|
}
|
|
|
|
static int ReadAddr(QBTContext *context, QTMAddr addr, QTMAddr *valuePtr)
|
|
// Reads an address (that is, a pointer) from the target task,
|
|
// returning an error if the memory is unmapped.
|
|
//
|
|
// On entry, context will be a valid context (as determined by QBTContextIsValid).
|
|
// On entry, addr can be any value.
|
|
// On entry, valuePtr must not be NULL.
|
|
// Returns an errno-style error code.
|
|
// On success, *valuePtr will be the value of the pointer stored at addr in
|
|
// the target task.
|
|
{
|
|
int err;
|
|
QTMAddr value;
|
|
|
|
assert(QBTContextIsValid(context));
|
|
assert(valuePtr != NULL);
|
|
|
|
if (context->arch->is64Bit) {
|
|
|
|
// Read directly into value, and then swap all 8 bytes.
|
|
|
|
err = context->readBytes(context, addr, &value, sizeof(value));
|
|
if (err == 0) {
|
|
if (context->swapBytes) {
|
|
value = OSSwapInt64(value);
|
|
}
|
|
}
|
|
} else {
|
|
uint32_t tmpAddr;
|
|
|
|
// Read into a temporary address, swap 4 bytes, then copy that
|
|
// into value. tmpAddr is unsigned, so we zero fill the top
|
|
// 32 bits.
|
|
|
|
err = context->readBytes(context, addr, &tmpAddr, sizeof(tmpAddr));
|
|
if (err == 0) {
|
|
if (context->swapBytes) {
|
|
tmpAddr = OSSwapInt32(tmpAddr);
|
|
}
|
|
value = tmpAddr;
|
|
}
|
|
}
|
|
if (err == 0) {
|
|
*valuePtr = value;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static void AddFrame(QBTContext *context, QTMAddr pc, QTMAddr fp, QBTFlags flags)
|
|
// Adds a frame to the end of the output array with the
|
|
// value specified by pc, fp, and flags.
|
|
//
|
|
// On entry, context will be a valid context (as determined by QBTContextIsValid).
|
|
{
|
|
// Only actually output the frame if the client supplied an array
|
|
// and we we haven't filled it up yet.
|
|
|
|
assert(QBTContextIsValid(context));
|
|
|
|
if ( (context->frameArray != NULL) && (context->frameCountOut < context->frameArrayCount) ) {
|
|
QBTFrame * frameOutPtr;
|
|
|
|
frameOutPtr = &context->frameArray[context->frameCountOut];
|
|
frameOutPtr->pc = pc;
|
|
frameOutPtr->fp = fp;
|
|
frameOutPtr->flags = flags;
|
|
}
|
|
|
|
// Always increment the frame count.
|
|
|
|
context->frameCountOut += 1;
|
|
}
|
|
|
|
static int BacktraceCore(QBTContext *context, size_t *frameCountPtr)
|
|
// The core backtrace code. This routine is called by all of the various
|
|
// exported routines. It implements the core backtrace functionality.
|
|
// All of the parameters to this routine are contained within
|
|
// the context. This routine traces back through the stack (using the
|
|
// readBytes callback in the context to actually read memory) creating
|
|
// a backtrace.
|
|
{
|
|
int err;
|
|
QTMAddr thisPC;
|
|
QTMAddr thisFrame;
|
|
QTMAddr lowerBound;
|
|
QTMAddr upperBound;
|
|
bool stopNow;
|
|
|
|
assert(QBTContextIsValid(context));
|
|
|
|
// Check that the contents of the frame array, if any, are all zero.
|
|
// MachInitContextFromTask did this for us. We need this to be done
|
|
// to maintain our post-condition, and it would be wasteful to do it twice.
|
|
|
|
#if ! defined(NDEBUG)
|
|
if (context->frameArray != NULL) {
|
|
size_t byteCount;
|
|
size_t byteIndex;
|
|
const char * byteBase;
|
|
|
|
byteCount = context->frameArrayCount * sizeof(*context->frameArray);
|
|
byteBase = (const char *) context->frameArray;
|
|
for (byteIndex = 0; byteIndex < byteCount; byteIndex++) {
|
|
assert(byteBase[byteIndex] == 0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
lowerBound = context->stackBottom;
|
|
upperBound = context->stackTop;
|
|
if (upperBound == 0) {
|
|
if (context->arch->is64Bit) {
|
|
// This actually generates a theoretical off-by-one error (a fp of
|
|
// 0xFFFFFFFF FFFFFFFF is falsely considered invalid), but that's
|
|
// not a problem in practice.
|
|
upperBound = (QTMAddr)0xFFFFFFFFFFFFFFFFLL;
|
|
} else {
|
|
upperBound = (QTMAddr)0x0000000100000000LL;
|
|
}
|
|
}
|
|
|
|
// If you supply bounds, they must make sense.
|
|
|
|
assert(upperBound >= lowerBound);
|
|
|
|
// Handle any leaf frames, and also return to us the initial
|
|
// PC and FP. A failure here is worthy of being reported as a total
|
|
// failure of the routine, so we allow err to propagate.
|
|
|
|
assert(context->frameCountOut == 0); // set up by memset in MachInitContextFromTask
|
|
err = context->arch->handleLeaf(context, &thisPC, &thisFrame);
|
|
|
|
// Handle the normal frames.
|
|
|
|
if (err == 0) {
|
|
stopNow = false;
|
|
do {
|
|
QBTFrame * frameOutPtr;
|
|
QBTFrame tmpFrameOut;
|
|
QTMAddr nextFrame;
|
|
QTMAddr nextPC;
|
|
|
|
// Output to a tmpFrameOut unless the client has supplied
|
|
// a buffer and there's sufficient space left in it.
|
|
//
|
|
// IMPORTANT:
|
|
// You can't just add the frame information (possibly by calling
|
|
// AddFrame) at the end of this loop, because the crossSignalFrame
|
|
// callback may add its own frame, and we have to make sure that
|
|
// this frame is allocated before that one.
|
|
|
|
if ( (context->frameArray != NULL) && (context->frameCountOut < context->frameArrayCount) ) {
|
|
frameOutPtr = &context->frameArray[context->frameCountOut];
|
|
} else {
|
|
frameOutPtr = &tmpFrameOut;
|
|
}
|
|
|
|
// Record this entry.
|
|
|
|
frameOutPtr->pc = thisPC;
|
|
frameOutPtr->fp = thisFrame;
|
|
frameOutPtr->flags = 0;
|
|
|
|
// Now set the flags to indicate the validity of specific information.
|
|
|
|
// Check the validity of the PC. Don't set the err here; a bad PC value
|
|
// does not cause us to quit the backtrace.
|
|
|
|
if ( ! context->arch->validPC(context, thisPC) ) {
|
|
frameOutPtr->flags |= kQBTPCBadMask;
|
|
} else {
|
|
// On PowerPC I used to report the address of the call,
|
|
// rather than the return address. That was easy: I just
|
|
// decremented the returned PC by 4. However, this is
|
|
// much harder on Intel, where instructions are of variable
|
|
// length. So, I decided to do what Apple's tools do,
|
|
// and just report the return address.
|
|
}
|
|
|
|
// Check the validity of the frame pointer. A bad frame pointer *does*
|
|
// cause us to stop tracing.
|
|
|
|
if ( (thisFrame == 0)
|
|
|| (thisFrame & context->arch->frameAlignMask)
|
|
|| (thisFrame < lowerBound)
|
|
|| (thisFrame >= upperBound)
|
|
) {
|
|
frameOutPtr->flags |= kQBTFrameBadMask;
|
|
stopNow = true;
|
|
}
|
|
|
|
if ( ! stopNow ) {
|
|
|
|
// Move to the next frame, either by crossing a signal handler frame
|
|
// or by the usual mechanism.
|
|
|
|
if ( !(frameOutPtr->flags & kQBTPCBadMask)
|
|
&& ( thisPC >= context->sigTrampLowerBound )
|
|
&& ( thisPC < context->sigTrampUpperBound )
|
|
) {
|
|
|
|
// If this frame is running in _sigtramp, get nextPC and nextFrame
|
|
// by delving into the signal handler stack block.
|
|
|
|
// While developing the various per-architecture cross signal
|
|
// frame handlers, there are many cases where I want to look at
|
|
// the stack in detail. In these circumstances, running under
|
|
// the debugger can be difficult because, for signals like
|
|
// SIGSEGV, GDB will catch the signal and not let you continue
|
|
// into the handler. So I typically stop in the signal handler
|
|
// and then attach with GDB. This code let's me do it without
|
|
// have to recompile.
|
|
|
|
#if 0 // Added for BOINC
|
|
#if !defined(NDEBUG)
|
|
{
|
|
static bool sInited;
|
|
static bool sStop;
|
|
const char * envVar;
|
|
|
|
if ( ! sInited ) {
|
|
envVar = getenv("QBACKTRACE_STOP_FOR_SIGNALS");
|
|
sStop = ( (envVar != NULL) && (atoi(envVar) != 0) );
|
|
sInited = true;
|
|
}
|
|
if (sStop) {
|
|
fprintf(stderr, "BacktraceCore: Waiting for debugger, pid = %ld\n", (long) getpid());
|
|
pause();
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
frameOutPtr->flags |= kQBTSignalHandlerMask;
|
|
err = context->arch->crossSignalFrame(context, thisFrame, &nextPC, &nextFrame);
|
|
|
|
// If we get an error crossing the signal frame, we just stop.
|
|
// The trace up to this point is probably OK.
|
|
|
|
if (err != 0) {
|
|
stopNow = true;
|
|
err = 0;
|
|
}
|
|
} else {
|
|
|
|
// Read the next frame pointer. A failure here causes us to quit
|
|
// backtracing. Note that we set kQBTFrameBadMask in frameOutPtr
|
|
// because, if we can't read the contents of the frame pointer, the
|
|
// frame pointer itself must be bad.
|
|
|
|
err = ReadAddr(context, thisFrame, &nextFrame);
|
|
if (err != 0) {
|
|
frameOutPtr->flags |= kQBTFrameBadMask;
|
|
nextFrame = (QTMAddr) -1;
|
|
|
|
stopNow = true;
|
|
err = 0;
|
|
}
|
|
|
|
// Also get the PC of the next frame, or set it to dummy value if
|
|
// there is no next frame or we can't get the PC from that frame.
|
|
|
|
if ( (frameOutPtr->flags & kQBTFrameBadMask)
|
|
|| (context->arch->getFrameNextPC(context, thisFrame, nextFrame, &nextPC) != 0)
|
|
) {
|
|
nextPC = (QTMAddr) -1; // an odd value, to trigger above check on next iteration
|
|
}
|
|
}
|
|
|
|
// Set up for the next iteration.
|
|
|
|
if ( (err == 0) && ! stopNow ) {
|
|
context->frameCountOut += 1;
|
|
|
|
lowerBound = thisFrame;
|
|
thisPC = nextPC;
|
|
thisFrame = nextFrame;
|
|
}
|
|
}
|
|
} while ( (err == 0) && ! stopNow );
|
|
}
|
|
|
|
// Adorn the backtrace with symbol information.
|
|
|
|
if ( (err == 0) && (context->frameArray != NULL) && (context->frameArrayCount != 0) && (context->frameCountOut != 0) ) {
|
|
err = BacktraceAdorn(context);
|
|
}
|
|
|
|
// Clean up.
|
|
|
|
if (err != 0) {
|
|
QBacktraceDisposeSymbols(context->frameArray, context->frameArrayCount);
|
|
if (context->frameArray != NULL) {
|
|
memset(context->frameArray, 0, context->frameArrayCount * sizeof(*context->frameArray));
|
|
}
|
|
context->frameCountOut = 0;
|
|
}
|
|
*frameCountPtr = context->frameCountOut;
|
|
|
|
assert(QBTContextIsValid(context));
|
|
|
|
return err;
|
|
}
|
|
|
|
#pragma mark ***** Mach Infrastructure
|
|
|
|
static int MachReadBytes(QBTContext *context, QTMAddr src, void *dst, size_t size)
|
|
// A memory read callback for Mach. This simply calls through to our
|
|
// QTaskMemory abstraction layer, which in turns calls mach_vm_read_overwrite.
|
|
//
|
|
// See the description of QBTReadBytesProc for information about
|
|
// the parameters.
|
|
{
|
|
assert(QBTContextIsValid(context));
|
|
assert(dst != NULL);
|
|
assert(size > 0);
|
|
|
|
return QTMRead(context->task, src, size, dst);
|
|
}
|
|
|
|
static void MachTermContext(QBTContext *context)
|
|
// Clean up after a MachInitContext. It's safe to call this even
|
|
// if MachInitContext fails.
|
|
{
|
|
assert(context != NULL);
|
|
|
|
if (context->createdSymRef) {
|
|
QSymDestroy(context->symRef);
|
|
}
|
|
}
|
|
|
|
static int MachInitContextFromTask(
|
|
QBTContext * context,
|
|
task_t task,
|
|
cpu_type_t cputype,
|
|
QTMAddr stackBottom,
|
|
QTMAddr stackTop,
|
|
QSymbolsRef symRef,
|
|
QBTFrame * frameArray,
|
|
size_t frameArrayCount
|
|
)
|
|
// Initialise the backtrace context from numerous parameters, mostly supplied
|
|
// by the client. Even if this fails, it's safe to call MachTermContext.
|
|
{
|
|
int err;
|
|
QMOImageRef qmoImage = 0;
|
|
|
|
assert(context != NULL);
|
|
assert(task != MACH_PORT_NULL);
|
|
assert( ((stackBottom == 0) && (stackBottom == stackTop)) || (stackBottom < stackTop) );
|
|
assert( (frameArrayCount == 0) || (frameArray != NULL) );
|
|
|
|
// Clear the context first, to make it safe to call MachTermContext.
|
|
|
|
memset(context, 0, sizeof(*context));
|
|
|
|
// Stuff that can't fail
|
|
|
|
// Zap the input array; the BacktraceCore requires this to make error
|
|
// handling easier.
|
|
|
|
if (frameArray != NULL) {
|
|
memset(frameArray, 0, frameArrayCount * sizeof(*frameArray));
|
|
}
|
|
|
|
// General stuff
|
|
|
|
context->stackBottom = stackBottom;
|
|
context->stackTop = stackTop;
|
|
context->frameArray = frameArray;
|
|
context->frameArrayCount = frameArrayCount;
|
|
|
|
// Platform specific stuff
|
|
|
|
context->readBytes = MachReadBytes;
|
|
context->task = task;
|
|
|
|
// Stuff that might fail.
|
|
|
|
// Create a symbols object for the task, if necessary, and use that to get
|
|
// some basic information about it.
|
|
|
|
err = 0;
|
|
if (symRef == NULL) {
|
|
context->createdSymRef = true;
|
|
err = QSymCreateFromTask(context->task, (context->task != mach_task_self()), cputype, &context->symRef);
|
|
} else {
|
|
context->symRef = symRef;
|
|
}
|
|
if (err == 0) {
|
|
qmoImage = QSymGetExecutableImage(context->symRef);
|
|
if (qmoImage == NULL) {
|
|
err = EINVAL;
|
|
}
|
|
}
|
|
if (err == 0) {
|
|
context->swapBytes = QMOImageIsByteSwapped(qmoImage);
|
|
|
|
context->arch = GetTaskArch(qmoImage);
|
|
if (context->arch == NULL) {
|
|
err = EINVAL;
|
|
}
|
|
}
|
|
|
|
// Determine the address of _sigtramp.
|
|
|
|
if (err == 0) {
|
|
QSymSymbolInfo symInfo;
|
|
QSymSymbolInfo nextSymInfo;
|
|
|
|
err = QSymGetAddressForSymbol(context->symRef, "/usr/lib/libSystem.B.dylib", "__sigtramp", &symInfo);
|
|
if (err == 0) {
|
|
context->sigTrampLowerBound = symInfo.symbolValue;
|
|
}
|
|
if (err == 0) {
|
|
err = QSymGetNextSymbol(context->symRef, &symInfo, &nextSymInfo);
|
|
if (err == 0) {
|
|
context->sigTrampUpperBound = nextSymInfo.symbolValue;
|
|
} else {
|
|
// If QSymGetNextSymbol fails, just take a guess.
|
|
|
|
context->sigTrampUpperBound = context->sigTrampLowerBound + 256;
|
|
err = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert( (err != 0) || QBTContextIsValid(context) );
|
|
|
|
return err;
|
|
}
|
|
|
|
#pragma mark ***** CPU Specific
|
|
|
|
#if TARGET_CPU_PPC
|
|
|
|
#pragma mark - PowerPC
|
|
|
|
/* PowerPC Stack Frame Basics
|
|
--------------------------
|
|
|
|
|
|
Offset Size Purpose
|
|
------ ---- -------
|
|
low memory
|
|
fp == sp == r1 -> 0 X pointer to next frame
|
|
X X place to save CR
|
|
2X X place to save LR
|
|
3X 2X reserved
|
|
5X X place to save TOC (CFM only)
|
|
high memory
|
|
|
|
where X is the address size (4 bytes for 32-bits,
|
|
8 bytes for 64-bits)
|
|
|
|
To get from one frame to the next, you have to indirect an offset
|
|
of 0. To extract the PC from a frame (which, notably, is the
|
|
address of the code running in that frame, not a return address), you
|
|
have to indirect an offset of 2X bytes (8 or 16).
|
|
|
|
There's enough commonality between 32- and 64-bit PowerPC architectures
|
|
that it's easy to handle them both with the same code.
|
|
*/
|
|
|
|
static bool PowerPCIsSystemCall(QBTContext *context, QTMAddr pc)
|
|
// Using the PC from the thread state, walk back through
|
|
// the code stream for 3 instructions looking for a "sc" instruction.
|
|
// If we find one, it's almost certain that we're in a system call
|
|
// frameless leaf routine.
|
|
{
|
|
int err;
|
|
bool isSystemCall;
|
|
int count;
|
|
uint8_t inst[4];
|
|
|
|
isSystemCall = false;
|
|
count = 0;
|
|
do {
|
|
err = context->readBytes(context, pc, &inst, sizeof(inst));
|
|
if (err == 0) {
|
|
isSystemCall = (inst[0] == 0x44) // PPC "sc" instruction
|
|
&& (inst[1] == 0x00) // PPC instructions are always big
|
|
&& (inst[2] == 0x00) // endian, so we compare it byte at
|
|
&& (inst[3] == 0x02); // time for endian neutrality
|
|
|
|
}
|
|
if ( (err == 0) && ! isSystemCall ) {
|
|
count += 1;
|
|
pc -= sizeof(inst);
|
|
}
|
|
} while ( (err == 0) && ! isSystemCall && (count < 3) );
|
|
err = 0;
|
|
|
|
return isSystemCall;
|
|
}
|
|
|
|
static int PowerPCHandleLeaf(QBTContext *context, QTMAddr *pcPtr, QTMAddr *framePtr)
|
|
// This is the handleLeaf routine for the PowerPC
|
|
// architecture. See the description of QBTHandleLeafProc
|
|
// for a detailed discussion of its parameters.
|
|
//
|
|
// The top most frame may be in a weird state because of the
|
|
// possible variations in the routine prologue. There are a
|
|
// variety of combinations, such as:
|
|
//
|
|
// 1. a normal routine, with its return address stored in
|
|
// its caller's stack frame
|
|
//
|
|
// 2. a system call routine, which is a leaf routine with
|
|
// no frame and the return address is in LR
|
|
//
|
|
// 3. a leaf routine with no frame, where the return address
|
|
// is in LR
|
|
//
|
|
// 4. a leaf routine with no frame that accesses a global, where
|
|
// the return address is in r0
|
|
//
|
|
// 5. a normal routine that was stopped midway through
|
|
// constructing its prolog, where the return address is
|
|
// typically in r0
|
|
//
|
|
// Of these, 1 and 2 are most common, and they're the cases I
|
|
// handle. General support for all of the cases requires the
|
|
// ability to accurately determine the start of the routine
|
|
// which is not something that I can do with my current
|
|
// infrastructure.
|
|
//
|
|
// Note that don't handle any cases where the return address is
|
|
// in r0, although r0 is available as part of the threadState
|
|
// if I need it in the future.
|
|
{
|
|
#ifdef __LP64__
|
|
return EINVAL;
|
|
#else
|
|
int err;
|
|
QTMAddr pc;
|
|
QTMAddr lr;
|
|
QTMAddr r1;
|
|
|
|
// Get the pc and lr from the thread state.
|
|
|
|
err = 0;
|
|
switch (context->threadStateFlavor) {
|
|
case PPC_THREAD_STATE:
|
|
pc = ((const ppc_thread_state_t *) context->threadState)->srr0;
|
|
lr = ((const ppc_thread_state_t *) context->threadState)->lr;
|
|
r1 = ((const ppc_thread_state_t *) context->threadState)->r1;
|
|
break;
|
|
case PPC_THREAD_STATE64:
|
|
pc = ((const ppc_thread_state64_t *) context->threadState)->srr0;
|
|
lr = ((const ppc_thread_state64_t *) context->threadState)->lr;
|
|
r1 = ((const ppc_thread_state64_t *) context->threadState)->r1;
|
|
break;
|
|
|
|
default:
|
|
err = EINVAL;
|
|
break;
|
|
}
|
|
|
|
// If we find that we're in a system call frameless leaf routine,
|
|
// add a dummy stack frame (with no frame, because the frame actually
|
|
// belows to frameArray[1]).
|
|
|
|
if (err == 0) {
|
|
if ( PowerPCIsSystemCall(context, pc) ) {
|
|
|
|
AddFrame(context, pc, 0, kQBTFrameBadMask);
|
|
|
|
pc = lr;
|
|
}
|
|
|
|
// Pass the initial pc and frame back to the caller.
|
|
|
|
*pcPtr = pc;
|
|
*framePtr = r1;
|
|
}
|
|
|
|
return err;
|
|
#endif
|
|
}
|
|
|
|
static bool PowerPCValidPC(QBTContext *context, QTMAddr pc)
|
|
// This is the validPC routine for the PowerPC
|
|
// architecture. See the description of
|
|
// QBTValidPCProc for a detailed discussion
|
|
// of its parameters.
|
|
//
|
|
// PowerPC instructions must be word aligned. Also, I check that
|
|
// it's possible to read the instruction. I don't do anything
|
|
// clever like check that the resulting value is a valid instruction.
|
|
{
|
|
uint32_t junkInst;
|
|
|
|
return ((pc & 0x03) == 0) && (context->readBytes(context, pc, &junkInst, sizeof(junkInst)) == 0);
|
|
}
|
|
|
|
static int PowerPCGetFrameNextPC(QBTContext *context, QTMAddr thisFrame, QTMAddr nextFrame, QTMAddr *nextPCPtr)
|
|
// This is the getFrameNextPC routine for the PowerPC
|
|
// architecture. See the description of
|
|
// QBTGetFrameNextPCProc for a detailed discussion
|
|
// of its parameters.
|
|
{
|
|
#pragma unused(thisFrame)
|
|
#pragma unused(nextFrame)
|
|
QTMAddr offset;
|
|
|
|
if ( context->arch->is64Bit ) {
|
|
offset = 16;
|
|
} else {
|
|
offset = 8;
|
|
}
|
|
|
|
return ReadAddr(context, nextFrame + offset, nextPCPtr);
|
|
}
|
|
|
|
/* PowerPC Signal Stack Frames
|
|
---------------------------
|
|
In the current Mac OS X architecture, there is no guaranteed reliable
|
|
way to backtrace a PowerPC signal stack frame. The problem is that the
|
|
kernel pushes a variable amount of data on to the stack when it invokes the
|
|
user space signal trampoline (_sigtramp), and the only handle to the
|
|
information about how much data was pushed is passed in a register
|
|
parameter to _sigtramp. _sigtramp stashes that value away in a
|
|
non-volatile register. So, when _sigtramp calls the user-supplied
|
|
signal handler, there's no way to work out where that register
|
|
ends up being saved.
|
|
|
|
Thus, we devolve into guesswork. It turns out that the offset from
|
|
the stack of the kernel data to the information we need (the place
|
|
where the interrupted thread's registers were stored) is a (relatively)
|
|
constant for any given system release. So, we can just simply add the
|
|
appropriate offset to the frame pointer and grab the data we need.
|
|
|
|
On recent systems (10.3 and later) this fails if the signal handle
|
|
requests 'dual contexts', that is, it requests both 32- and 64-bit
|
|
PowerPC registers. In that case, the size of the pushed data changes,
|
|
and that affects the relative alignment of the data and the stack
|
|
pointer, and things break. I don't know of any way to work around
|
|
this <rdar://problem/4411774>.
|
|
|
|
Finally, these constant vary from release to release.
|
|
This code handles the significant cases that I know about (Mac OS X 10.1.x
|
|
and earlier, Mac OS X 10.2, and Mac OS 10.3 and later), but there's no
|
|
guarantee that this offset won't change again in the future.
|
|
|
|
When the kernel invokes the user space signal trampoline, it pushes
|
|
the following items on to the stack.
|
|
|
|
Mac OS X 10.1.x
|
|
---------------
|
|
Size Purpose
|
|
---- -------
|
|
low memory
|
|
0x030 bytes for C linkage
|
|
0x040 bytes for saving PowerPC parameters
|
|
0x0c0 ppc_saved_state
|
|
0x110 ppc_float_state
|
|
0x018 struct sigcontext
|
|
0x0e0 red zone
|
|
high memory
|
|
The previous frame's SP is at offset 0x00C within
|
|
ppc_saved_state, which makes it equal to
|
|
0x030 + 0x040 + 0x00C, or 0x07C. The offset to
|
|
the previous PC (0x84) follows from that.
|
|
|
|
Mac OS X 10.2.x
|
|
---------------
|
|
Size Purpose
|
|
---- -------
|
|
low memory
|
|
0x030 bytes for C linkage
|
|
0x040 bytes for saving PowerPC parameters
|
|
0x008 alignment padding
|
|
0x408 struct mcontext, comprised of:
|
|
0x020 ppc_exception_state_t
|
|
0x0A0 ppc_thread_state_t
|
|
0x108 ppc_float_state_t
|
|
0x240 ppc_vector_state_t
|
|
0x040 siginfo_t
|
|
0x020 ucontext
|
|
0x0e0 red zone
|
|
high memory
|
|
The previous frame's SP is at offset 0x00C within
|
|
ppc_thread_state_t, which it equal to
|
|
0x030 + 0x040 + 0x008 + 0x020 + 0x00C, or 0x0A4.
|
|
The offsets to the previous PC and LR (0x98 and 0x128)
|
|
follow from that.
|
|
|
|
Mac OS X 10.3.x and 10.4.x
|
|
--------------------------
|
|
|
|
Size (process/hardware/requested) Purpose
|
|
--------------------------------- -------
|
|
32/32/x 32/64/32 32/64/64 64/64/x
|
|
------- -------- -------- -----
|
|
low memory
|
|
align16 align16 align16 align32 alignment
|
|
0x030 0x030 0x030 0x030 bytes for C linkage
|
|
0x040 0x040 0x040 0x040 bytes for saving PowerPC parameters
|
|
0x008 0x000 0x008 0x018 pad
|
|
0x040 0x040 0x040 0x068 [user_]siginfo_t
|
|
0x020 0x020 0x020 0x038 ucontext64
|
|
0x408 0x408 - - mcontext
|
|
- 0x498 0x498 0x498 mcontext64
|
|
align16 align16 align16 align32 alignment
|
|
? ? ? ? pad
|
|
0x0e0 0x0e0 0x0e0 0x140 red zone
|
|
high memory
|
|
Some things to note about the above diagram:
|
|
|
|
o The number and type of mcontexts depends on:
|
|
- whether the process is 32- or 64-bit
|
|
- whether the process is running on 32- or 64-bit hardware
|
|
- whether the process requests 64-bit registers when it
|
|
installs its signal handler (the SA_64REGSET flag)
|
|
|
|
o This, in turns, affects the amount of pad inserted to
|
|
get the proper alignment.
|
|
|
|
o For a 64-bit process, the kernel aligns the stack to a
|
|
32 byte boundary, even though the runtime architecture
|
|
only requires a 16 byte boundary.
|
|
|
|
o The final alignment is done last, but the pad that it
|
|
creates is effectively created between the parameter save
|
|
area and the [user_]siginfo_t because the C linkage area
|
|
and param save areas are both defined to be a fixed offset
|
|
from the frame pointer.
|
|
|
|
o This means that we have to use some heuristics to find the
|
|
amount of pad. These heuristics generally involve looking
|
|
into the mcontext[64] to see if the registers in there
|
|
match the registers in the [user_]siginfo_t. However,
|
|
this is further complicated by the fact that the mcontext
|
|
might be an mcontext64, even on a 32-bit process.
|
|
|
|
o The pad size is highly constrained. This is because the
|
|
kernel aligns the frame as part of setting up the red
|
|
zone and again to set up the linkage. Thus, the only cause
|
|
of misalignment is the amount of data between the two alignments
|
|
operations. For a 64-bit process, there's no variation,
|
|
so the pad size is always 0x018. For a 32-bit system,
|
|
there's also no variability; the pad size is always 0x008.
|
|
The issues arise for 32-bit code on a 64-bit system,
|
|
where the pad size can be either 0x000 or 0x000.
|
|
*/
|
|
|
|
static bool PPCCheckFrameStyle(
|
|
QBTContext *context,
|
|
QTMAddr nextFrame,
|
|
QTMAddr sigInfoFPOffset,
|
|
QTMAddr sigInfoSize,
|
|
QTMAddr ucontextSize,
|
|
QTMAddr padSize,
|
|
QTMAddr mcontextFPOffset
|
|
)
|
|
// Returns true if it seems that we're looking at the frame in the right way.
|
|
// That is, for the signal crossing frame at nextFrame, return true if it
|
|
// can be interpreted correctly the specified padSize and mcontextFPOffset.
|
|
//
|
|
// To check whether we're interpreting the frame correctly, we check that:
|
|
//
|
|
// 1. the uc_mcontext64 field of the ucontext points to the mcontext[64]
|
|
// 2. the FP we get from the mcontext[64] matches the FP we get from the
|
|
// sigInfo (stored in the pad[0] field at sigInfoFPOffset)
|
|
{
|
|
int err;
|
|
bool result;
|
|
QTMAddr sigInfo;
|
|
QTMAddr ucontext;
|
|
QTMAddr mcontext;
|
|
QTMAddr mcontextFromUContext;
|
|
QTMAddr preSignalFP;
|
|
QTMAddr preSignalFPFromMContext;
|
|
|
|
assert(context != NULL);
|
|
assert( ! context->arch->is64Bit);
|
|
|
|
result = false;
|
|
|
|
// Get the sigInfo, ucontext and mcontext addresses. This is, of course,
|
|
// all provisional, based on the padSize.
|
|
|
|
sigInfo = nextFrame + 0x030 + 0x040 + padSize;
|
|
ucontext = sigInfo + sigInfoSize;
|
|
mcontext = ucontext + ucontextSize;
|
|
|
|
// Read the uc_mcontext64 field from the ucontext.
|
|
|
|
err = ReadAddr(context, ucontext + 0x01c, &mcontextFromUContext);
|
|
|
|
// Read the FP from the pad[0] of the siginfo_t.
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, sigInfo + sigInfoFPOffset, &preSignalFP);
|
|
}
|
|
|
|
// Read the FP from the mcontext[64].
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, mcontext + mcontextFPOffset, &preSignalFPFromMContext);
|
|
}
|
|
|
|
// Return true if the mcontext is where we expected and the FPs match.
|
|
|
|
if (err == 0) {
|
|
result = (mcontext == mcontextFromUContext)
|
|
&& (preSignalFP == preSignalFPFromMContext);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PowerPCCrossSignalFrame(QBTContext *context, QTMAddr thisFrame, QTMAddr *nextPCPtr, QTMAddr *nextFramePtr)
|
|
// This is the crossSignalFrame routine for the PowerPC
|
|
// architecture. See the description of QBTCrossSignalFrameProc
|
|
// for a detailed discussion of its parameters.
|
|
{
|
|
int err;
|
|
int major;
|
|
QTMAddr nextFrame;
|
|
QTMAddr padSize = 0;
|
|
QTMAddr sigInfoSize = 0;
|
|
QTMAddr sigInfoFPOffset = 0;
|
|
QTMAddr ucontextSize = 0;
|
|
QTMAddr mcontextPCOffset = 0;
|
|
QTMAddr mcontextFPOffset = 0;
|
|
QTMAddr mcontextLROffset = 0;
|
|
QTMAddr sigInfo;
|
|
QTMAddr ucontext;
|
|
QTMAddr mcontext;
|
|
QTMAddr preSignalFP;
|
|
QTMAddr preSignalFPFromMContext;
|
|
QTMAddr preSignalPCFromMContext;
|
|
QTMAddr preSignalLRFromMContext;
|
|
|
|
// The code depends on the version of the OS. *sigh*
|
|
|
|
err = QTMGetDarwinOSRelease(&major, NULL, NULL);
|
|
|
|
// Read the address of the frame below the _sigtramp frame, because
|
|
// that where all the action is.
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, thisFrame, &nextFrame);
|
|
}
|
|
|
|
// Sniff the frame to see which type it is:
|
|
|
|
if (err == 0) {
|
|
if (context->arch->is64Bit) {
|
|
// For 64-bit processes, everything is easy.
|
|
|
|
padSize = 0x018;
|
|
sigInfoSize = 0x068;
|
|
sigInfoFPOffset = 0x030;
|
|
ucontextSize = 0x038;
|
|
mcontextPCOffset = 0x020 + 0x000;
|
|
mcontextFPOffset = 0x020 + 0x018;
|
|
mcontextLROffset = 0x020 + 0x11c;
|
|
} else if (major < kQTMDarwinOSMajorForMacOSX103) {
|
|
#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_3
|
|
#warning QBacktrace has not been tested properly on pre-10.3 systems.
|
|
#endif
|
|
/*
|
|
I used to have code (that was a lot more naive) that supported
|
|
pre-10.3 systems. I've abandoned these systems for my new code.
|
|
However, I've left the following information around in case I
|
|
ever contemplate resurrecting the support for older systems.
|
|
|
|
Some random numbers from the old pre-10.3 code:
|
|
|
|
o 10.0 through 10.1.x
|
|
offsetToPC = 0x84;
|
|
offsetToFP = 0x7c;
|
|
offsetToLR = 0;
|
|
o 10.2.x
|
|
offsetToPC = 0x98;
|
|
offsetToFP = 0xa4;
|
|
offsetToLR = 0x128;
|
|
but G5-based 10.2.x systems are more like 10.3 (probably).
|
|
*/
|
|
assert(false);
|
|
err = ENOTSUP;
|
|
} else {
|
|
// For 32-bit processes on 10.3 and later, things are much trickier.
|
|
|
|
// padSize needs to be worked out
|
|
sigInfoSize = 0x040;
|
|
sigInfoFPOffset = 0x024;
|
|
ucontextSize = 0x020;
|
|
// mcontextPCOffset needs to be worked out
|
|
// mcontextFPOffset needs to be worked out
|
|
// mcontextLROffset needs to be worked out
|
|
|
|
// Try all three frame style...
|
|
|
|
// 1. Start with the 32/64/64 case.
|
|
|
|
padSize = 8;
|
|
// mcontext64 offsets
|
|
mcontextPCOffset = 0x020 + 0x004;
|
|
mcontextFPOffset = 0x020 + 0x01c;
|
|
mcontextLROffset = 0x020 + 0x120;
|
|
|
|
// IMPORTANT:
|
|
// The above offsets are 4 larger than the equivalent offsets for a
|
|
// 64-bit process. That's because we'll read them using ReadAddr,
|
|
// and ReadAddr will only read a 32-bits word on a 32-bit process.
|
|
// Thus, we have to make sure it reads the bottom 32-bits of the
|
|
// mcontext64 field, which means we add 4 to the offset.
|
|
|
|
if ( ! PPCCheckFrameStyle(context, nextFrame, sigInfoFPOffset, sigInfoSize, ucontextSize, padSize, mcontextFPOffset) ) {
|
|
// 2. OK, it's not that, let's try 32/32/x.
|
|
|
|
// mcontext offsets
|
|
mcontextPCOffset = 0x020 + 0x000;
|
|
mcontextFPOffset = 0x020 + 0x00c;
|
|
mcontextLROffset = 0x020 + 0x090;
|
|
|
|
if ( ! PPCCheckFrameStyle(context, nextFrame, sigInfoFPOffset, sigInfoSize, ucontextSize, padSize, mcontextFPOffset) ) {
|
|
// 3. OK, third time is a charm. Let's try 32/64/32.
|
|
|
|
padSize = 0;
|
|
|
|
if ( ! PPCCheckFrameStyle(context, nextFrame, sigInfoFPOffset, sigInfoSize, ucontextSize, padSize, mcontextFPOffset) ) {
|
|
err = EINVAL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Grab the pre-signal FP from the siginfo_t and the pre-signal FP, PC and LR
|
|
// from the mcontext and use them to set up the results for the client.
|
|
|
|
mcontext = 0; // quieten a warning
|
|
if (err == 0) {
|
|
sigInfo = nextFrame + 0x030 + 0x040 + padSize;
|
|
ucontext = sigInfo + sigInfoSize;
|
|
mcontext = ucontext + ucontextSize;
|
|
|
|
err = ReadAddr(context, sigInfo + sigInfoFPOffset, &preSignalFP);
|
|
}
|
|
if (err == 0) {
|
|
err = ReadAddr(context, mcontext + mcontextFPOffset, &preSignalFPFromMContext);
|
|
}
|
|
if (err == 0) {
|
|
err = ReadAddr(context, mcontext + mcontextPCOffset, &preSignalPCFromMContext);
|
|
}
|
|
if (err == 0) {
|
|
err = ReadAddr(context, mcontext + mcontextLROffset, &preSignalLRFromMContext);
|
|
}
|
|
if (err == 0) {
|
|
assert(preSignalFPFromMContext == preSignalFP);
|
|
|
|
*nextFramePtr = preSignalFP;
|
|
*nextPCPtr = preSignalPCFromMContext;
|
|
}
|
|
|
|
// If the PC is a system call, add a dummy leaf for that PC
|
|
// and then get the next frame's PC from LR.
|
|
|
|
if ( (err == 0) && PowerPCIsSystemCall(context, *nextPCPtr) ) {
|
|
AddFrame(context, *nextPCPtr, 0, kQBTFrameBadMask);
|
|
|
|
*nextPCPtr = preSignalLRFromMContext;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
#endif //TARGET_CPU_PPC
|
|
|
|
#pragma mark - Intel
|
|
#if TARGET_CPU_X86 || TARGET_CPU_X86_64
|
|
|
|
/* 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).
|
|
*/
|
|
|
|
static bool IntelIsSystemCall(QBTContext *context, QTMAddr pc)
|
|
// 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.
|
|
{
|
|
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 int IntelHandleLeaf(QBTContext *context, QTMAddr *pcPtr, QTMAddr *framePtr)
|
|
// This is the handleLeaf routine for the Intel architecture. See the
|
|
// description of QBTHandleLeafProc for a detailed discussion of its
|
|
// parameters.
|
|
//
|
|
// I don't have the experience or the time to fully analyse
|
|
// the leaf routine problem for Intel. Rather, I just implemented
|
|
// a simple system call check, much like I did on PowerPC. This
|
|
// seems to be effective in the cases that I care about.
|
|
{
|
|
int err;
|
|
QTMAddr pc;
|
|
QTMAddr sp;
|
|
QTMAddr fp;
|
|
|
|
err = 0;
|
|
switch (context->threadStateFlavor) {
|
|
#ifdef __LP64__
|
|
case x86_THREAD_STATE64:
|
|
|
|
pc = ((const x86_thread_state64_t *) context->threadState)->rip;
|
|
sp = ((const x86_thread_state64_t *) context->threadState)->rsp;
|
|
fp = ((const x86_thread_state64_t *) context->threadState)->rbp;
|
|
break;
|
|
case x86_THREAD_STATE32:
|
|
pc = ((const x86_thread_state32_t *) context->threadState)->eip;
|
|
sp = ((const x86_thread_state32_t *) context->threadState)->esp;
|
|
fp = ((const x86_thread_state32_t *) context->threadState)->ebp;
|
|
break;
|
|
#else
|
|
case x86_THREAD_STATE64:
|
|
|
|
pc = ((const x86_thread_state64_t *) context->threadState)->rip;
|
|
sp = ((const x86_thread_state64_t *) context->threadState)->rsp;
|
|
fp = ((const x86_thread_state64_t *) context->threadState)->rbp;
|
|
break;
|
|
case x86_THREAD_STATE32:
|
|
pc = ((const x86_thread_state32_t *) context->threadState)->eip;
|
|
sp = ((const x86_thread_state32_t *) context->threadState)->esp;
|
|
fp = ((const x86_thread_state32_t *) context->threadState)->ebp;
|
|
break;
|
|
#endif
|
|
default:
|
|
err = EINVAL;
|
|
}
|
|
|
|
// If the PC is a system call, add a dummy leaf for that PC
|
|
// and then get the next frame's PC from the top of stack.
|
|
|
|
if (err == 0) {
|
|
if ( IntelIsSystemCall(context, pc) ) {
|
|
AddFrame(context, pc, 0, kQBTFrameBadMask);
|
|
|
|
err = ReadAddr(context, sp, &pc);
|
|
}
|
|
if (err == 0) {
|
|
*pcPtr = pc;
|
|
*framePtr = fp;
|
|
}
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static bool IntelValidPC(QBTContext *context, QTMAddr pc)
|
|
// This is the validPC routine for the Intel
|
|
// architecture. See the description of
|
|
// QBTValidPCProc for a detailed discussion
|
|
// of its parameters.
|
|
//
|
|
// Intel instructions are not aligned in any way. All, I can do
|
|
// is check for known bad values ((QTMAddr) -1 is used as a
|
|
// known bad value by the core) and check that I can read at least
|
|
// byte of instruction from the address.
|
|
{
|
|
uint8_t junkInst;
|
|
|
|
return (pc != (QTMAddr) -1) && (context->readBytes(context, pc, &junkInst, sizeof(junkInst)) == 0);
|
|
}
|
|
|
|
static int IntelGetFrameNextPC(QBTContext *context, QTMAddr thisFrame, QTMAddr nextFrame, QTMAddr *nextPCPtr)
|
|
// This is the getFrameNextPC routine for the Intel architecture. See the
|
|
// description of QBTGetFrameNextPCProc for a detailed discussion of its
|
|
// parameters.
|
|
//
|
|
// This is very easy on Intel, because it's the return address, which is at a
|
|
// fixed offset in the frame.
|
|
{
|
|
#pragma unused(nextFrame)
|
|
QTMAddr offset;
|
|
|
|
if ( context->arch->is64Bit ) {
|
|
offset = 8;
|
|
} else {
|
|
offset = 4;
|
|
}
|
|
|
|
return ReadAddr(context, thisFrame + offset, nextPCPtr);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if TARGET_CPU_X86
|
|
|
|
/* Intel 32-Bit Signal Stack Frames
|
|
--------------------------------
|
|
Cross signal stack frames is much more reliable on Intel. The parameters
|
|
to _sigtramp are stored on the stack, and you can reliably pick them up
|
|
from there.
|
|
|
|
Size Purpose
|
|
---- -------
|
|
low memory
|
|
|
|
frame -> 0x004 pre-signal frame pointer
|
|
0x018 struct sigframe32
|
|
0x020? pad
|
|
0x258 struct mcontext
|
|
0x00c x86_exception_state32_t
|
|
0x040 x86_thread_state32_t
|
|
0x20c x86_float_state32_t
|
|
0x040 siginfo_t
|
|
0x020 struct ucontext
|
|
high memory
|
|
|
|
As for other architectures, the kernel aligns the stack such that the catcher
|
|
field of the (struct sigframe32) is aligned on a 16 byte boundary. This means
|
|
that there's a variable amount of pad between the (struct sigframe32) and the
|
|
other fields. However, for x86 this isn't a problem because the
|
|
(struct sigframe32) contains pointers to the other structures that we need.
|
|
|
|
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.
|
|
|
|
The three values we need to get are:
|
|
|
|
o pre-signal FP -- This is easy. It's always pointed to by the current FP.
|
|
|
|
o pre-signal PC -- This is hard because the obvious place that it's stored
|
|
(the si_addr field of the siginfo_t) isn't reliable. Specifically, for
|
|
SIGBUS and SIGSEGV, this field holds the faulting address, not the faulting
|
|
instruction's address. *sigh* So, we get this value by following the
|
|
(struct sigframe32) to the (struct ucontext) to the (struct mcontext) to
|
|
the (x86_thread_state32_t) to the ebp field. *phew*
|
|
|
|
o pre-signal SP -- The kernel stores this in the pad[0] field of the siginfo_t,
|
|
so we use that. We also check it against the esp from the thread state,
|
|
just 'cause that's easy.
|
|
|
|
The sinfo field of the sigframe32 structure is at offset 0x10 and the uctx
|
|
field is at offset 0x14. Once you account for the pre-signal frame pointer
|
|
that's pushed on to the stack by _sigtramp, you need to go 0x14 bytes up the
|
|
frame to get the sinfo field, which is a pointer to a siginfo_t structure.
|
|
The kernel places the pre-signal PC and SP in fields in that structure (si_addr
|
|
and pad[0], offset 0x18 and 0x24 respectively).
|
|
|
|
Finally, if we detect a frameless leaf routine past the signal frame,
|
|
we extract its return address from the top of stack.
|
|
*/
|
|
|
|
static int Intel32CrossSignalFrame(QBTContext *context, QTMAddr thisFrame, QTMAddr *nextPCPtr, QTMAddr *nextFramePtr)
|
|
// This is the crossSignalFrame routine for the Intel
|
|
// architecture. See the description of
|
|
// QBTCrossSignalFrameProc for a detailed discussion
|
|
// of its parameters.
|
|
{
|
|
int err;
|
|
QTMAddr sigFrame;
|
|
QTMAddr sigInfo;
|
|
QTMAddr ucontext;
|
|
QTMAddr mcontext;
|
|
QTMAddr threadState;
|
|
QTMAddr preSignalFP;
|
|
QTMAddr preSignalSP;
|
|
QTMAddr preSignalFPFromMContext;
|
|
QTMAddr preSignalSPFromMContext;
|
|
QTMAddr preSignalPCFromMContext;
|
|
|
|
sigFrame = thisFrame + 4;
|
|
|
|
// Get the pre-signal FP by simply reading from the frame pointer.
|
|
// Because of the way __sigtramp works, this ends up being correct.
|
|
|
|
err = ReadAddr(context, thisFrame, &preSignalFP);
|
|
|
|
// Get the siginfo_t pointer from the parameters to _sigtramp
|
|
// (the sinfo field of sigframe32).
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, sigFrame + 0x10, &sigInfo);
|
|
}
|
|
|
|
// Get the pre-signal SP from the pad[0] field of the siginfo_t.
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, sigInfo + 0x24, &preSignalSP);
|
|
}
|
|
|
|
// Get the ucontext address from the parameters to _sigtramp
|
|
// (the uctx field of the sigframe32).
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, sigFrame + 0x14, &ucontext);
|
|
}
|
|
|
|
// Get the mcontext address from the uc_mcontext field of the ucontext.
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, ucontext + 0x1c, &mcontext);
|
|
}
|
|
|
|
// Get the address of the ss field of the mcontext. This is the
|
|
// x86_thread_state_32_t. Then extract the ebp, esp, and eip fields from
|
|
// that.
|
|
|
|
if (err == 0) {
|
|
threadState = mcontext + 0x0c;
|
|
|
|
err = ReadAddr(context, threadState + 0x18, &preSignalFPFromMContext);
|
|
if (err == 0) {
|
|
err = ReadAddr(context, threadState + 0x1c, &preSignalSPFromMContext);
|
|
}
|
|
if (err == 0) {
|
|
err = ReadAddr(context, threadState + 0x28, &preSignalPCFromMContext);
|
|
}
|
|
}
|
|
if (err == 0) {
|
|
assert(preSignalFPFromMContext == preSignalFP);
|
|
assert(preSignalSPFromMContext == preSignalSP);
|
|
|
|
*nextFramePtr = preSignalFP;
|
|
*nextPCPtr = preSignalPCFromMContext;
|
|
}
|
|
|
|
// 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.
|
|
|
|
if ( (err == 0) && IntelIsSystemCall(context, *nextPCPtr) ) {
|
|
AddFrame(context, *nextPCPtr, 0, kQBTFrameBadMask);
|
|
|
|
err = ReadAddr(context, preSignalSP, nextPCPtr);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if TARGET_CPU_X86_64
|
|
|
|
/*
|
|
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)
|
|
// This is the crossSignalFrame routine for the 64-bit Intel
|
|
// architecture. See the description of
|
|
// QBTCrossSignalFrameProc for a detailed discussion
|
|
// of its parameters.
|
|
{
|
|
int err;
|
|
QTMAddr sigInfo;
|
|
QTMAddr ucontext;
|
|
QTMAddr mcontext;
|
|
QTMAddr threadState;
|
|
QTMAddr preSignalFP;
|
|
QTMAddr preSignalSP;
|
|
QTMAddr preSignalFPFromMContext;
|
|
QTMAddr preSignalSPFromMContext;
|
|
QTMAddr align;
|
|
|
|
// Get the previous frame by simply reading from the frame pointer.
|
|
// Because of the way things work, this ends up being correct.
|
|
|
|
err = ReadAddr(context, thisFrame, &preSignalFP);
|
|
|
|
if (err == 0) {
|
|
// Now try various alignments to see which one yields a valid result.
|
|
|
|
for (align = 0x010; align < 0x020; align += 4) {
|
|
// Use the 'Hail Mary (tm)' algorithm to get a pointer to the user_siginfo_t
|
|
// and the (struct user_ucontext64) that immediately follows it.
|
|
|
|
sigInfo = thisFrame + 0x008 + 0x08 + align + 0x2c4;
|
|
ucontext = sigInfo + 0x068;
|
|
|
|
// Now check whether it makes sense...
|
|
|
|
// Get the previous SP from the pad[0] field of the user_siginfo_t.
|
|
|
|
err = ReadAddr(context, sigInfo + 0x030, &preSignalSP);
|
|
|
|
// 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).
|
|
|
|
if (err == 0) {
|
|
err = ReadAddr(context, ucontext + 0x030, &mcontext);
|
|
}
|
|
|
|
// 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.
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
// At this point we throw away any error status from the above code.
|
|
|
|
err = 0;
|
|
|
|
// 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.
|
|
|
|
if (align == 0x020) {
|
|
assert(false);
|
|
align = 0x014;
|
|
}
|
|
}
|
|
|
|
// Get the address of the sigInfo using the alignment calculated above
|
|
// and then use it to set the result.
|
|
|
|
if (err == 0) {
|
|
// This value is /always/ right, so we copy it out to the client immediately.
|
|
|
|
*nextFramePtr = preSignalFP;
|
|
|
|
sigInfo = thisFrame + 0x008 + 0x08 + align + 0x2c4;
|
|
ucontext = sigInfo + 0x068;
|
|
|
|
// Get the pre-signal SP and mcontext as before.
|
|
|
|
err = ReadAddr(context, sigInfo + 0x030, &preSignalSP);
|
|
if (err == 0) {
|
|
err = ReadAddr(context, ucontext + 0x030, &mcontext);
|
|
}
|
|
|
|
// And from that the thread state and the previous PC.
|
|
|
|
if (err == 0) {
|
|
threadState = mcontext + 0x10;
|
|
err = ReadAddr(context, threadState + 16 * 0x08, nextPCPtr);
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
|
|
if ( (err == 0) && IntelIsSystemCall(context, *nextPCPtr) ) {
|
|
AddFrame(context, *nextPCPtr, 0, kQBTFrameBadMask);
|
|
|
|
err = ReadAddr(context, preSignalSP, nextPCPtr);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
#endif
|
|
|
|
// kArchitectures is an array of all the architectures we support.
|
|
// Things to notes:
|
|
//
|
|
// o GetTaskArch processes this in a forward direction. If you
|
|
// list a more-specific architecture, you should list it before
|
|
// the less-specific one.
|
|
//
|
|
// o The table is terminated by a NULL architecture, signified by
|
|
// a 0 in the cputype field.
|
|
//
|
|
// See the comments near QBTArchInfo for a detailed description of
|
|
// each field.
|
|
|
|
static const QBTArchInfo kArchitectures[] = {
|
|
#if TARGET_CPU_PPC
|
|
{ // PowerPC
|
|
"ppc", // name
|
|
CPU_TYPE_POWERPC, // cputype
|
|
0, // subcputype
|
|
false, // is64Bit
|
|
15, // frameAlignMask
|
|
PowerPCHandleLeaf, // handleLeaf
|
|
PowerPCValidPC, // validPC
|
|
PowerPCGetFrameNextPC, // getFrameNextPC
|
|
PowerPCCrossSignalFrame, // crossSignalFrame
|
|
PPC_THREAD_STATE, // stateFlavor
|
|
PPC_THREAD_STATE_COUNT // stateCount
|
|
},
|
|
#endif // TARGET_CPU_PPC
|
|
|
|
#if 0 // Not supported by BOINC
|
|
{ // PowerPC64
|
|
"ppc64", // name
|
|
CPU_TYPE_POWERPC64, // cputype
|
|
0, // subcputype
|
|
true, // is64Bit
|
|
15, // frameAlignMask
|
|
PowerPCHandleLeaf, // handleLeaf
|
|
PowerPCValidPC, // validPC
|
|
PowerPCGetFrameNextPC, // getFrameNextPC
|
|
PowerPCCrossSignalFrame, // crossSignalFrame
|
|
PPC_THREAD_STATE64, // stateFlavor
|
|
PPC_THREAD_STATE64_COUNT // stateCount
|
|
},
|
|
#endif
|
|
|
|
#if TARGET_CPU_X86
|
|
{ // Intel
|
|
"x86", // name
|
|
CPU_TYPE_X86, // cputype
|
|
0, // subcputype
|
|
false, // is64Bit
|
|
3, // frameAlignMask
|
|
// Apple's x86 ABI requires that the stack be 16 byte aligned,
|
|
// but it says nothing about the frame. It turns out that the
|
|
// frame is typically 8 byte aligned, but I can't find any
|
|
// documentation that requires that, so I'm only checking 4 byte
|
|
// alignment.
|
|
IntelHandleLeaf, // handleLeaf
|
|
IntelValidPC, // validPC
|
|
IntelGetFrameNextPC, // getFrameNextPC
|
|
Intel32CrossSignalFrame, // crossSignalFrame
|
|
x86_THREAD_STATE32, // stateFlavor
|
|
x86_THREAD_STATE32_COUNT // stateCount
|
|
},
|
|
#endif
|
|
|
|
#if TARGET_CPU_X86_64
|
|
{ // x86-64
|
|
"x86-64", // name
|
|
CPU_TYPE_X86_64, // cputype
|
|
0, // subcputype
|
|
true, // is64Bit
|
|
3, // frameAlignMask
|
|
// Apple's x86 ABI requires that the stack be 16 byte aligned,
|
|
// but it says nothing about the frame. It turns out that the
|
|
// frame is typically 8 byte aligned, but I can't find any
|
|
// documentation that requires that, so I'm only checking 4 byte
|
|
// alignment.
|
|
IntelHandleLeaf, // handleLeaf
|
|
IntelValidPC, // validPC
|
|
IntelGetFrameNextPC, // getFrameNextPC
|
|
Intel64CrossSignalFrame, // crossSignalFrame
|
|
x86_THREAD_STATE64, // stateFlavor
|
|
x86_THREAD_STATE64_COUNT // stateCount
|
|
},
|
|
#endif
|
|
|
|
{ /* null terminator */
|
|
}
|
|
};
|
|
|
|
#pragma mark ***** Public Interface
|
|
|
|
#if ! defined(NDEBUG)
|
|
|
|
static bool FrameArrayNeedsDispose(const QBTFrame *frameArray, size_t frameArrayCount)
|
|
// We want to make sure that we don't leak symbol strings if we fail
|
|
// with an error. So, for debugging, we check that the output frame
|
|
// array has no strings that need disposing.
|
|
{
|
|
bool needsDispose;
|
|
size_t frameIndex;
|
|
|
|
needsDispose = false;
|
|
for (frameIndex = 0; frameIndex < frameArrayCount; frameIndex++) {
|
|
if (frameArray[frameIndex].flags & kQBTFrameSymbolNeedsDisposeMask) {
|
|
needsDispose = true;
|
|
}
|
|
}
|
|
|
|
return needsDispose;
|
|
}
|
|
|
|
#endif
|
|
|
|
extern int QBacktraceMachSelf(
|
|
QSymbolsRef symRef,
|
|
QBTFrame * frameArray,
|
|
size_t frameArrayCount,
|
|
size_t * frameCountPtr
|
|
)
|
|
// See comments in header.
|
|
{
|
|
int err;
|
|
QBTContext context;
|
|
uintptr_t stackBottom;
|
|
uintptr_t stackTop;
|
|
uintptr_t stackPtr;
|
|
thread_state_flavor_t stateFlavor;
|
|
void * state;
|
|
size_t stateSize;
|
|
|
|
assert( (frameArrayCount == 0) || (frameArray != NULL) );
|
|
assert( frameCountPtr != NULL );
|
|
|
|
state = NULL;
|
|
stateSize = 0;
|
|
|
|
// Ask pthreads for our stack bounds.
|
|
|
|
stackTop = (uintptr_t) pthread_get_stackaddr_np( pthread_self() );
|
|
stackBottom = stackTop - pthread_get_stacksize_np( pthread_self() );
|
|
stackPtr = (uintptr_t) &context;
|
|
|
|
// Due to a bug <rdar://problem/5007494>, the above calls can return bogus
|
|
// results. Specifically, you get bogus results for the main thread on
|
|
// pre-Leopard systems when running 64-bit code.
|
|
//
|
|
// So, if the results are clearly bogus (because a known good stack address
|
|
// (the address of one of our locals) is out of bounds), we just ignore
|
|
// the values we set up and use zeroes instead.
|
|
|
|
if ( (stackPtr < stackBottom) || (stackPtr >= stackTop) ) {
|
|
stackBottom = 0;
|
|
stackTop = 0;
|
|
}
|
|
|
|
assert( ((stackBottom == 0) && (stackBottom == stackTop)) || (stackBottom < stackTop) );
|
|
|
|
// Get the thread state for the current thread.
|
|
|
|
err = QBTCreateThreadStateSelf(&stateFlavor, &state, &stateSize);
|
|
|
|
// Do the backtrace.
|
|
|
|
if (err == 0) {
|
|
err = QBacktraceMachThreadState(
|
|
mach_task_self(),
|
|
stateFlavor,
|
|
state,
|
|
stateSize,
|
|
QMOGetLocalCPUType(),
|
|
symRef,
|
|
stackBottom,
|
|
stackTop,
|
|
frameArray,
|
|
frameArrayCount,
|
|
frameCountPtr
|
|
);
|
|
}
|
|
|
|
// Clean up.
|
|
|
|
free(state);
|
|
|
|
// Post condition
|
|
|
|
assert( (err == 0) || ! FrameArrayNeedsDispose(frameArray, frameArrayCount) );
|
|
assert( (err == 0) == (*frameCountPtr != 0) );
|
|
|
|
return err;
|
|
}
|
|
|
|
extern int QBacktraceMachThread(
|
|
task_t task,
|
|
thread_t thread,
|
|
cpu_type_t cputype,
|
|
QSymbolsRef symRef,
|
|
QTMAddr stackBottom,
|
|
QTMAddr stackTop,
|
|
QBTFrame * frameArray,
|
|
size_t frameArrayCount,
|
|
size_t * frameCountPtr
|
|
)
|
|
// See comments in header.
|
|
{
|
|
int err;
|
|
thread_state_flavor_t stateFlavor;
|
|
void * state;
|
|
size_t stateSize;
|
|
|
|
assert(task != MACH_PORT_NULL);
|
|
assert(thread != MACH_PORT_NULL);
|
|
assert( ((stackBottom == 0) && (stackBottom == stackTop)) || (stackBottom < stackTop) );
|
|
assert( (frameArrayCount == 0) || (frameArray != NULL) );
|
|
assert( frameCountPtr != NULL );
|
|
|
|
state = NULL;
|
|
stateSize = 0;
|
|
|
|
// Get the thread state for the target thread.
|
|
|
|
if (thread == mach_thread_self()) {
|
|
assert(task == mach_task_self());
|
|
|
|
err = QBTCreateThreadStateSelf(
|
|
&stateFlavor,
|
|
&state,
|
|
&stateSize
|
|
);
|
|
} else {
|
|
err = QBTCreateThreadState(
|
|
task,
|
|
thread,
|
|
cputype,
|
|
symRef,
|
|
&stateFlavor,
|
|
&state,
|
|
&stateSize
|
|
);
|
|
}
|
|
|
|
// Do the backtrace.
|
|
|
|
if (err == 0) {
|
|
err = QBacktraceMachThreadState(
|
|
task,
|
|
stateFlavor,
|
|
state,
|
|
stateSize,
|
|
cputype,
|
|
symRef,
|
|
stackBottom,
|
|
stackTop,
|
|
frameArray,
|
|
frameArrayCount,
|
|
frameCountPtr
|
|
);
|
|
}
|
|
|
|
// Clean up.
|
|
|
|
free(state);
|
|
|
|
// Post condition
|
|
|
|
assert( (err == 0) || ! FrameArrayNeedsDispose(frameArray, frameArrayCount) );
|
|
assert( (err == 0) == (*frameCountPtr != 0) );
|
|
|
|
return err;
|
|
}
|
|
|
|
extern int QBacktraceMachThreadState(
|
|
task_t task,
|
|
thread_state_flavor_t stateFlavor,
|
|
const void * state,
|
|
size_t stateSize,
|
|
cpu_type_t cputype,
|
|
QSymbolsRef symRef,
|
|
QTMAddr stackBottom,
|
|
QTMAddr stackTop,
|
|
QBTFrame * frameArray,
|
|
size_t frameArrayCount,
|
|
size_t * frameCountPtr
|
|
)
|
|
// See comments in header.
|
|
{
|
|
int err;
|
|
QBTContext context;
|
|
|
|
assert(task != MACH_PORT_NULL);
|
|
assert(state != NULL);
|
|
assert(stateSize != 0);
|
|
assert( ((stackBottom == 0) && (stackBottom == stackTop)) || (stackBottom < stackTop) );
|
|
assert( (frameArrayCount == 0) || (frameArray != NULL) );
|
|
assert( frameCountPtr != NULL );
|
|
|
|
// Initialise the context from the task.
|
|
|
|
err = MachInitContextFromTask(
|
|
&context,
|
|
task,
|
|
cputype,
|
|
stackBottom,
|
|
stackTop,
|
|
symRef,
|
|
frameArray,
|
|
frameArrayCount
|
|
);
|
|
|
|
// Initialise the thread state from the input arguments.
|
|
|
|
if (err == 0) {
|
|
context.threadStateFlavor = stateFlavor;
|
|
context.threadState = state;
|
|
context.threadStateSize = stateSize;
|
|
}
|
|
|
|
// Do the backtrace.
|
|
|
|
if (err == 0) {
|
|
err = BacktraceCore(&context, frameCountPtr);
|
|
}
|
|
|
|
// Clean up.
|
|
|
|
MachTermContext(&context);
|
|
|
|
// Post condition
|
|
|
|
assert( (err == 0) || ! FrameArrayNeedsDispose(frameArray, frameArrayCount) );
|
|
assert( (err == 0) == (*frameCountPtr != 0) );
|
|
|
|
return err;
|
|
}
|
|
|
|
extern void QBacktraceDisposeSymbols(QBTFrame frameArray[], size_t frameCount)
|
|
// See comments in header.
|
|
{
|
|
size_t frameIndex;
|
|
|
|
if (frameArray != NULL) {
|
|
for (frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
|
if (frameArray[frameIndex].flags & kQBTFrameSymbolNeedsDisposeMask) {
|
|
free( (void *) frameArray[frameIndex].symbol );
|
|
free( (void *) frameArray[frameIndex].library );
|
|
}
|
|
frameArray[frameIndex].symbol = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
extern int QBTCreateThreadState(
|
|
task_t task,
|
|
thread_t thread,
|
|
cpu_type_t cputype,
|
|
QSymbolsRef symRef,
|
|
thread_state_flavor_t * stateFlavorPtr,
|
|
void ** statePtr,
|
|
size_t * stateSizePtr
|
|
)
|
|
// See comments in header.
|
|
{
|
|
int err;
|
|
kern_return_t kr;
|
|
void * state;
|
|
QMOImageRef dyld;
|
|
bool didCreateDyld;
|
|
const QBTArchInfo * arch = NULL;
|
|
|
|
assert(task != MACH_PORT_NULL);
|
|
assert(thread != MACH_PORT_NULL);
|
|
// cputype can be anything
|
|
// symRef may be NULL
|
|
assert(stateFlavorPtr != NULL);
|
|
assert( statePtr != NULL);
|
|
assert(*statePtr == NULL);
|
|
assert( stateSizePtr != NULL);
|
|
assert(*stateSizePtr == 0);
|
|
|
|
state = NULL;
|
|
dyld = NULL;
|
|
didCreateDyld = false;
|
|
|
|
// If the client supplied us with a sym, use it to get dyld. Otherwise
|
|
// get dyld directly from the QMachOImage module.
|
|
|
|
if (symRef == NULL) {
|
|
err = QMOImageCreateFromTaskDyld(task, cputype, &dyld);
|
|
didCreateDyld = true;
|
|
} else {
|
|
dyld = QSymGetDyldImage(symRef);
|
|
err = 0;
|
|
}
|
|
|
|
// Use the dyld to get the architecture.
|
|
|
|
if (err == 0) {
|
|
arch = GetTaskArch(dyld);
|
|
if (arch == NULL) {
|
|
err = EINVAL;
|
|
}
|
|
}
|
|
|
|
// Use the parameters from the architecture to allocate and initialise the
|
|
// thread state buffer.
|
|
|
|
if (err == 0) {
|
|
state = calloc(arch->stateCount, sizeof(integer_t));
|
|
if (state == NULL) {
|
|
err = ENOMEM;
|
|
}
|
|
}
|
|
if (err == 0) {
|
|
mach_msg_type_number_t stateCount;
|
|
|
|
stateCount = arch->stateCount;
|
|
kr = thread_get_state(thread, arch->stateFlavor, (thread_state_t) state, &stateCount);
|
|
err = QTMErrnoFromMachError(kr);
|
|
}
|
|
if (err == 0) {
|
|
*statePtr = state;
|
|
state = NULL; // so it's not freed below
|
|
*stateFlavorPtr = arch->stateFlavor;
|
|
*stateSizePtr = arch->stateCount * sizeof(integer_t);
|
|
}
|
|
|
|
// Clean up.
|
|
|
|
free(state);
|
|
if (didCreateDyld) {
|
|
QMOImageDestroy(dyld);
|
|
}
|
|
|
|
assert( (err == 0) == (*statePtr != NULL) );
|
|
assert( (err == 0) == (*stateSizePtr != 0) );
|
|
|
|
return err;
|
|
}
|
|
|
|
extern int QBTCreateThreadStateSelf(
|
|
thread_state_flavor_t * stateFlavorPtr,
|
|
void ** statePtr,
|
|
size_t * stateSizePtr
|
|
)
|
|
// See comments in header.
|
|
//
|
|
// I used to do this with a bundle of assembly language gunk, but now I take
|
|
// advantage of GCC's built-in functions.
|
|
{
|
|
int err;
|
|
void * pc;
|
|
void * fp;
|
|
thread_state_flavor_t flavor;
|
|
|
|
assert(stateFlavorPtr != NULL);
|
|
assert( statePtr != NULL);
|
|
assert(*statePtr == NULL);
|
|
assert( stateSizePtr != NULL);
|
|
assert(*stateSizePtr == 0);
|
|
|
|
// Use GCC intrinsics to get the information we need.
|
|
|
|
pc = __builtin_return_address(0);
|
|
fp = __builtin_frame_address(1);
|
|
|
|
// Use CPU-specific code to allocate and initialise a thread state buffer.
|
|
|
|
// *** Could do the allocation using information from the kArchitectures
|
|
// array, but GetTaskArch needs an image object, which we don't have handy.
|
|
|
|
#if TARGET_CPU_PPC
|
|
ppc_thread_state_t * state;
|
|
|
|
flavor = PPC_THREAD_STATE;
|
|
state = (ppc_thread_state_t *) calloc(1, sizeof(*state));
|
|
if (state != NULL) {
|
|
state->srr0 = (uintptr_t) pc;
|
|
state->r1 = (uintptr_t) fp;
|
|
}
|
|
#elif TARGET_CPU_PPC64
|
|
ppc_thread_state64_t * state;
|
|
|
|
flavor = PPC_THREAD_STATE64;
|
|
state = (ppc_thread_state64_t *) calloc(1, sizeof(*state));
|
|
if (state != NULL) {
|
|
state->srr0 = (uintptr_t) pc;
|
|
state->r1 = (uintptr_t) fp;
|
|
}
|
|
#elif TARGET_CPU_X86
|
|
x86_thread_state32_t * state;
|
|
|
|
flavor = x86_THREAD_STATE32;
|
|
state = (x86_thread_state32_t *) calloc(1, sizeof(*state));
|
|
if (state != NULL) {
|
|
state->eip = (uintptr_t) pc;
|
|
state->ebp = (uintptr_t) fp;
|
|
}
|
|
#elif TARGET_CPU_X86_64
|
|
x86_thread_state64_t * state;
|
|
|
|
flavor = x86_THREAD_STATE64;
|
|
state = (x86_thread_state64_t *) calloc(1, sizeof(*state));
|
|
if (state != NULL) {
|
|
#ifdef __LP64__
|
|
state->rip = (uintptr_t) pc;
|
|
state->rbp = (uintptr_t) fp;
|
|
#else
|
|
state->rip = (uintptr_t) pc;
|
|
state->rbp = (uintptr_t) fp;
|
|
#endif
|
|
}
|
|
#else
|
|
#error What architecture?
|
|
#endif
|
|
|
|
// Pass the information back to our client.
|
|
|
|
if (state == NULL) {
|
|
err = ENOMEM;
|
|
} else {
|
|
*stateFlavorPtr = flavor;
|
|
*statePtr = state;
|
|
*stateSizePtr = sizeof(*state);
|
|
err = 0;
|
|
}
|
|
|
|
assert( (err == 0) == (*statePtr != NULL) );
|
|
assert( (err == 0) == (*stateSizePtr != 0) );
|
|
|
|
return err;
|
|
}
|
|
|