Пример #1
0
static bool gdbExtendedBacktrace(int const dumpFile)
#endif
{
	/*
	 * Pointer to the stackframe of the function containing faulting
	 * instruction (assuming
	 * -fomit-frame-pointer has *not* been used).
	 *
	 * The frame pointer register (x86: %ebp, x64: %rbp) point's to the
	 * local variables of the current function (which are preceded by the
	 * previous frame pointer and the return address).  Hence the
	 * additions to the frame-pointer register's content.
	 */
	void const * const frame =
#if   defined(SA_SIGINFO) && __WORDSIZE == 64
		sigcontext ? (void*)(sigcontext->uc_mcontext.gregs[REG_RBP] + sizeof(greg_t) + sizeof(void (*)(void))) : NULL;
#elif defined(SA_SIGINFO) && __WORDSIZE == 32
		sigcontext ? (void*)(sigcontext->uc_mcontext.gregs[REG_EBP] + sizeof(greg_t) + sizeof(void (*)(void))) : NULL;
#else
		NULL;
#endif

	/*
	 * Faulting instruction.
	 */
	void (*instruction)(void) =
#if   defined(SA_SIGINFO) && __WORDSIZE == 64
		sigcontext ? (void (*)(void))sigcontext->uc_mcontext.gregs[REG_RIP] : NULL;
#elif defined(SA_SIGINFO) && __WORDSIZE == 32
		sigcontext ? (void (*)(void))sigcontext->uc_mcontext.gregs[REG_EIP] : NULL;
#else
		NULL;
#endif

	// Spawn a GDB instance and retrieve a pipe to its stdin
	int gdbPipe;
	int status;
	pid_t wpid;
	                                  // Retrieve a full stack backtrace
	static const char gdbCommands[] = "backtrace full\n"

	                                  // Move to the stack frame where we triggered the crash
	                                  "frame 4\n"

	                                  // Show the assembly code associated with that stack frame
	                                  "disassemble /m\n"

	                                  // Show the content of all registers
	                                  "info registers\n"
	                                  "quit\n";
	const pid_t pid = execGdb(dumpFile, &gdbPipe);
	if (pid == 0)
	{
		return false;
	}

	// Disassemble the faulting instruction (if we know it)
	if (instruction)
	{
		dprintf(gdbPipe, "x/i %p\n", (void*)instruction);
	}

	// We have an intelligent guess for the *correct* frame, disassemble *that* one.
	if (frame)
	{
		dprintf(gdbPipe, "frame %p\n"
		                 "disassemble /m\n", frame);
	}

	write(gdbPipe, gdbCommands, strlen(gdbCommands));

	/* Flush our end of the pipe to make sure that GDB has all commands
	 * directly available to it.
	 */
	fsync(gdbPipe);

	// Wait for our child to terminate
	wpid = waitpid(pid, &status, 0);

	// Clean up our end of the pipe
	close(gdbPipe);

	// waitpid(): on error, -1 is returned
	if (wpid == -1)
	{
		write(dumpFile, "GDB failed\n",
		         strlen("GDB failed\n"));
		printf("GDB failed\n");

		return false;
	}

	/* waitpid(): on success, returns the process ID of the child whose
	 * state has changed
	 *
	 * We only have one child, from our fork() call above, thus these PIDs
	 * should match.
	 */
	assert(pid == wpid);

	/* Check wether our child (which presumably was GDB, but doesn't
	 * necessarily have to be) didn't terminate normally or had a non-zero
	 * return code.
	 */
	if (!WIFEXITED(status)
	 || WEXITSTATUS(status) != 0)
	{
		write(dumpFile, "GDB failed\n",
		         strlen("GDB failed\n"));
		printf("GDB failed\n");

		return false;
	}

	return true;
}
Пример #2
0
/**
 * Dumps a backtrace of the stack to the given output stream.
 *
 * @param dumpFile a POSIX file descriptor to write the resulting backtrace to.
 *
 * @return false if any failure occurred, preventing a full "extended"
 *               backtrace.
 */
static bool gdbExtendedBacktrace(int const dumpFile)
{
	// Spawn a GDB instance and retrieve a pipe to its stdin
	int gdbPipe;
	int status;
	pid_t wpid;
	                                  // Retrieve a full stack backtrace
	static const char gdbCommands[] = "backtrace full\n"

	                                  // Move to the stack frame where we triggered the crash
	                                  "frame 4\n"

	                                  // Show the assembly code associated with that stack frame
	                                  "disassemble\n"

	                                  // Show the content of all registers
	                                  "info registers\n"
	                                  "quit\n";
	const pid_t pid = execGdb(dumpFile, &gdbPipe);
	if (pid == 0)
	{
		return false;
	}

	write(gdbPipe, gdbCommands, sizeof(gdbCommands));

	/* Flush our end of the pipe to make sure that GDB has all commands
	 * directly available to it.
	 */
	fsync(gdbPipe);

	// Wait for our child to terminate
	wpid = waitpid(pid, &status, 0);

	// Clean up our end of the pipe
	close(gdbPipe);

	// waitpid(): on error, -1 is returned
	if (wpid == -1)
	{
		write(dumpFile, "GDB failed\n",
		         strlen("GDB failed\n"));
		printf("GDB failed\n");

		return false;
	}

	/* waitpid(): on success, returns the process ID of the child whose
	 * state has changed
	 *
	 * We only have one child, from our fork() call above, thus these PIDs
	 * should match.
	 */
	assert(pid == wpid);

	/* Check wether our child (which presumably was GDB, but doesn't
	 * necessarily have to be) didn't terminate normally or had a non-zero
	 * return code.
	 */
	if (!WIFEXITED(status)
	 || WEXITSTATUS(status) != 0)
	{
		write(dumpFile, "GDB failed\n",
		         strlen("GDB failed\n"));
		printf("GDB failed\n");

		return false;
	}

	return true;
}