📄 morebacktrace.c
字号:
// 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(MoreBTContext *context, MoreBTAddr *pcPtr, MoreBTAddr *framePtr) // This is the handleLeaf routine for the Intel // architecture. See the description of MoreBTHandleLeafProc // 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; MoreBTAddr pc; pc = ((const i386_thread_state_t *) context->threadState)->eip; // 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. err = 0; if ( IntelIsSystemCall(context, pc) ) { AddFrame(context, pc, 0, kMoreBTFrameBadMask); err = ReadAddr(context, ((const i386_thread_state_t *) context->threadState)->esp, &pc); } if (err == 0) { *pcPtr = pc; *framePtr = ((const i386_thread_state_t *) context->threadState)->ebp; } return err;}static bool IntelValidPC(MoreBTContext *context, MoreBTAddr pc) // This is the validPC routine for the Intel // architecture. See the description of // MoreBTValidPCProc 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 ((MoreBTAddr) -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 != (MoreBTAddr) -1) && (context->readBytes(context, pc, &junkInst, sizeof(junkInst)) == 0);}static int IntelGetFrameNextPC(MoreBTContext *context, MoreBTAddr thisFrame, MoreBTAddr nextFrame, MoreBTAddr *nextPCPtr) // This is the getFrameNextPC routine for the Intel // architecture. See the description of // MoreBTGetFrameNextPCProc 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.{ return ReadAddr(context, thisFrame + 4, nextPCPtr); }/* Intel 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 sigframe 0x020? pad 0x258 struct mcontext 0x00c i386_exception_state_t 0x040 i386_thread_state_t 0x20c i386_float_state_t 0x040 siginfo_t 0x020 struct ucontext high memory Things to note about the above: o The kernel aligns the stack such that the catcher field of the sigframe structure is aligned on a 16 byte boundary. This means that there's a variable amount of pad between sigframe and mcontext. This isn't a problem because the sigframe structure contains a field (sinfo) that's a pointer to the siginfo_t. The sinfo field of the sigframe structure is at offset 0x10. 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 IntelCrossSignalFrame(MoreBTContext *context, MoreBTAddr thisFrame, MoreBTAddr *nextPCPtr, MoreBTAddr *nextFramePtr) // This is the crossSignalFrame routine for the Intel // architecture. See the description of // MoreBTCrossSignalFrameProc for a detailed discussion // of its parameters.{ int err; MoreBTAddr sigInfo; MoreBTAddr preSignalSP; // Get the siginfo_t pointer from the parameters to _sigtramp // (the sinfo field of sigframe). err = ReadAddr(context, thisFrame + 0x14, &sigInfo); // Get the previous PC from si_addr field of siginfo_t. if (err == 0) { err = ReadAddr(context, sigInfo + 0x18, nextPCPtr); } // Get the previous frame by simply reading from the frame pointer. // Because of the way things work, this ends up being correct. if (err == 0) { err = ReadAddr(context, thisFrame, nextFramePtr); } // Finally, if we detect a leaf routine, add a dummy frame for it // and then get the pre-signal SP (from the pad[0] of siginfo_t) // 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, kMoreBTFrameBadMask); err = ReadAddr(context, sigInfo + 0x24, &preSignalSP); if (err == 0) { err = ReadAddr(context, preSignalSP, nextPCPtr); } } return err;}#endif /* #ifdef CPU_TYPE_X86 */// 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 MoreBTArchInfo for a detailed description of // each field.static const MoreBTArchInfo kArchitectures[] = { { // PowerPC CPU_TYPE_POWERPC, // cputype 0, // subcputype false, // is64Bit NX_BigEndian, // byteOrder 15, // frameAlignMask PowerPCHandleLeaf, // handleLeaf PowerPCValidPC, // validPC PowerPCGetFrameNextPC, // getFrameNextPC PowerPCCrossSignalFrame, // crossSignalFrame PPC_THREAD_STATE, // stateFlavor PPC_THREAD_STATE_COUNT // stateCount }, { // PowerPC64 CPU_TYPE_POWERPC, // cputype 0, // subcputype true, // is64Bit NX_BigEndian, // byteOrder 15, // frameAlignMask PowerPCHandleLeaf, // handleLeaf PowerPCValidPC, // validPC PowerPCGetFrameNextPC, // getFrameNextPC PowerPCCrossSignalFrame, // crossSignalFrame PPC_THREAD_STATE64, // stateFlavor PPC_THREAD_STATE64_COUNT // stateCount },#ifdef CPU_TYPE_X86 { // Intel CPU_TYPE_X86, // cputype 0, // subcputype false, // is64Bit NX_LittleEndian, // byteOrder 3, // frameAlignMask // Apple's i386 API 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 IntelCrossSignalFrame, // crossSignalFrame i386_THREAD_STATE, // stateFlavor i386_THREAD_STATE_COUNT // stateCount }#endif /* #ifdef CPU_TYPE_X86 */ /* null terminator */ };#pragma mark ***** Public Interfaceextern int MoreBacktraceMachThread( task_t task, thread_t thread, MoreBTAddr stackBottom, MoreBTAddr stackTop, MoreBTFrame * frameArray, size_t frameArrayCount, size_t * frameCountPtr) // See comments in header.{ int err; const MoreBTArchInfo * arch; mach_msg_type_number_t stateCount; void * threadState; 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 ); threadState = NULL; // Get the architecture of the task, and us that to allocate enough // space for the thread's state. err = 0; arch = GetTaskArch(task); if (arch == NULL) { err = EINVAL; } if (err == 0) { stateCount = arch->stateCount; threadState = malloc(stateCount * sizeof(int)); if (threadState == NULL) { err = ENOMEM; } } // Get the thread state. if (err == 0) { err = thread_get_state(thread, arch->stateFlavor, (thread_state_t) threadState, &stateCount); } // Do the backtrace. if (err == 0) { err = MoreBacktraceMach( arch, task, threadState, stackBottom, stackTop, frameArray, frameArrayCount, frameCountPtr ); } // Clean up. free(threadState); return err;}// InitThreadState is a macro that initialises an architecture-specific // thread state structure from the current CPU registers. This only // initialises enough fields to support a backtrace.#if TARGET_CPU_PPC#if 0 /* OMPI CHANGE */ #define InitThreadState(threadState) \ do { \ uint32_t tmpPC = 0; \ uint32_t tmpFP = 0; \ asm { \ bl next ; \ next: mflr tmpPC ; \ mr tmpFP,sp \ } \ ((ppc_thread_state_t *) threadState)->srr0 = tmpPC; \ ((ppc_thread_state_t *) threadState)->r1 = tmpFP; \ } while (0)#else /* OMPI CHANGE */ #define InitThreadState(threadState) \ do { \ uint32_t tmpPC = 0; \ uint32_t tmpFP = 0; \ asm("\tmflr %0\n" \ "\tmr %1,r1" \ : "=r"(tmpPC), "=r"(tmpFP)); \ ((ppc_thread_state_t *) threadState)->srr0 = tmpPC; \ ((ppc_thread_state_t *) threadState)->r1 = tmpFP; \ } while (0)#endif /* OMPI CHANGE */#elif TARGET_CPU_PPC64#if 0 /* OMPI CHANGE */ #define InitThreadState(threadState) \ do { \ uint64_t tmpPC = 0; \ uint64_t tmpFP = 0; \ asm { \ bl next ; \ next: mflr tmpPC ; \ mr tmpFP,sp \ } \ ((ppc_thread_state64_t *) threadState)->srr0 = tmpPC; \ ((ppc_thread_state64_t *) threadState)->r1 = tmpFP; \ } while (0)#else /* OMPI CHANGE */ #define InitThreadState(threadState) \ do { \ uint64_t tmpPC = 0; \ uint64_t tmpFP = 0; \ asm("\tmflr %0\n" \ "\tmr %1,r1" \ : "=r"(tmpPC), "=r"(tmpFP)); \ ((ppc_thread_state64_t *) threadState)->srr0 = tmpPC; \ ((ppc_thread_state64_t *) threadState)->r1 = tmpFP; \ } while (0)#endif /* OMPI CHANGE */#elif TARGET_CPU_X86 // Have to use bizarr-o GCC syntax because the compiler is barfing on the // block syntax, but only for Intel. *sigh* #define InitThreadState(threadState) \ do { \ uint32_t tmpPC = 0; \ uint32_t tmpFP = 0; \ asm( "\tcall Lnext\nLnext: pop %0\n" \ "\tmov %%ebp,%1" \ : "=r" (tmpPC) , "=r" (tmpFP) ); \ ((i386_thread_state_t *) threadState)->eip = tmpPC; \ ((i386_thread_state_t *) threadState)->ebp = tmpFP; \ } while (0) #else #error What architecture?#endifextern int MoreBacktraceMachSelf( MoreBTAddr stackBottom, MoreBTAddr stackTop, MoreBTFrame * frameArray, size_t frameArrayCount, size_t * frameCountPtr) // See comments in header.{ int err; const MoreBTArchInfo * arch; void * threadState; assert( ((stackBottom == 0) && (stackBottom == stackTop)) || (stackBottom < stackTop) ); assert( (frameArrayCount == 0) || (frameArray != NULL) ); assert( frameCountPtr != NULL ); threadState = NULL; // Get the architecture of the current task, and us that to allocate // enough space for our thread's state. err = 0; arch = GetTaskArch(mach_task_self()); if (arch == NULL) { err = EINVAL; } if (err == 0) { threadState = calloc(arch->stateCount, sizeof(int)); if (threadState == NULL) { err = ENOMEM; } } // Initialise the thread state, then do the backtrace. if (err == 0) { InitThreadState(threadState); err = MoreBacktraceMach( GetTaskArch(mach_task_self()), mach_task_self(), threadState, stackBottom, stackTop, frameArray, frameArrayCount, frameCountPtr ); } // Clean up. free(threadState); return err;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -