⛏️ index : haiku.git

/*
 * Copyright 2023, Trung Nguyen, trungnt282910@gmail.com.
 * Distributed under the terms of the MIT License.
 */


#include <stdio.h>

#include <string>

#include "signals.h"
#include "strace.h"
#include "Context.h"
#include "MemoryReader.h"
#include "Syscall.h"
#include "TypeHandler.h"


struct enum_info {
	int index;
	const char *name;
};

#define ENUM_INFO_ENTRY(name) \
	{ name, #name }

static const enum_info kSignals[] = {
	{ 0, "SIG0"}, 					/* 0 */
	ENUM_INFO_ENTRY(SIGHUP),
	ENUM_INFO_ENTRY(SIGINT),
	ENUM_INFO_ENTRY(SIGQUIT),
	ENUM_INFO_ENTRY(SIGILL),
	ENUM_INFO_ENTRY(SIGCHLD),
	ENUM_INFO_ENTRY(SIGABRT),
	ENUM_INFO_ENTRY(SIGPIPE),
	ENUM_INFO_ENTRY(SIGFPE),
	ENUM_INFO_ENTRY(SIGKILL),
	ENUM_INFO_ENTRY(SIGSTOP),	/* 10 */
	ENUM_INFO_ENTRY(SIGSEGV),
	ENUM_INFO_ENTRY(SIGCONT),
	ENUM_INFO_ENTRY(SIGTSTP),
	ENUM_INFO_ENTRY(SIGALRM),
	ENUM_INFO_ENTRY(SIGTERM),
	ENUM_INFO_ENTRY(SIGTTIN),
	ENUM_INFO_ENTRY(SIGTTOU),
	ENUM_INFO_ENTRY(SIGUSR1),
	ENUM_INFO_ENTRY(SIGUSR2),
	ENUM_INFO_ENTRY(SIGWINCH),	/* 20 */
	ENUM_INFO_ENTRY(SIGKILLTHR),
	ENUM_INFO_ENTRY(SIGTRAP),
	ENUM_INFO_ENTRY(SIGPOLL),
	ENUM_INFO_ENTRY(SIGPROF),
	ENUM_INFO_ENTRY(SIGSYS),
	ENUM_INFO_ENTRY(SIGURG),
	ENUM_INFO_ENTRY(SIGVTALRM),
	ENUM_INFO_ENTRY(SIGXCPU),
	ENUM_INFO_ENTRY(SIGXFSZ),
	ENUM_INFO_ENTRY(SIGBUS),	/* 30 */
	ENUM_INFO_ENTRY(SIGRESERVED1),
	ENUM_INFO_ENTRY(SIGRESERVED2),
	{ 0, NULL }
};

static EnumTypeHandler::EnumMap kSignalsMap;


static const enum_info kSigmaskHow[] = {
	ENUM_INFO_ENTRY(SIG_BLOCK),
	ENUM_INFO_ENTRY(SIG_UNBLOCK),
	ENUM_INFO_ENTRY(SIG_SETMASK),

	{ 0, NULL }
};


#define sigmask(sig) (1 << ((sig) - 1))
#define FLAG_INFO_ENTRY(name) \
	{ sigmask( SIG##name ), #name }

static const FlagsTypeHandler::FlagInfo kSigmaskFlagsInfo[] = {
	FLAG_INFO_ENTRY(HUP),
	FLAG_INFO_ENTRY(INT),
	FLAG_INFO_ENTRY(QUIT),
	FLAG_INFO_ENTRY(ILL),
	FLAG_INFO_ENTRY(CHLD),
	FLAG_INFO_ENTRY(ABRT),
	FLAG_INFO_ENTRY(PIPE),
	FLAG_INFO_ENTRY(FPE),
	FLAG_INFO_ENTRY(KILL),
	FLAG_INFO_ENTRY(STOP),
	FLAG_INFO_ENTRY(SEGV),
	FLAG_INFO_ENTRY(CONT),
	FLAG_INFO_ENTRY(TSTP),
	FLAG_INFO_ENTRY(ALRM),
	FLAG_INFO_ENTRY(TERM),
	FLAG_INFO_ENTRY(TTIN),
	FLAG_INFO_ENTRY(TTOU),
	FLAG_INFO_ENTRY(USR1),
	FLAG_INFO_ENTRY(USR2),
	FLAG_INFO_ENTRY(WINCH),
	FLAG_INFO_ENTRY(KILLTHR),
	FLAG_INFO_ENTRY(TRAP),
	FLAG_INFO_ENTRY(POLL),
	FLAG_INFO_ENTRY(PROF),
	FLAG_INFO_ENTRY(SYS),
	FLAG_INFO_ENTRY(URG),
	FLAG_INFO_ENTRY(VTALRM),
	FLAG_INFO_ENTRY(XCPU),
	FLAG_INFO_ENTRY(XFSZ),
	FLAG_INFO_ENTRY(BUS),

	{ 0, NULL }
};


static FlagsTypeHandler::FlagsList kSigmaskFlags;
static EnumTypeHandler::EnumMap kSigmaskHowMap;


std::string
signal_name(int signal)
{
	EnumTypeHandler::EnumMap::const_iterator i = kSignalsMap.find(signal);
	if (i != kSignalsMap.end() && i->second != NULL)
		return i->second;

	static char buffer[32];
	sprintf(buffer, "%d", signal);
	return buffer;
}


static const char *
signal_code(int signal, int code)
{
#define CASE(X) case X: return #X;
	switch (code) {
		CASE(SI_USER)
		CASE(SI_QUEUE)
		CASE(SI_TIMER)
		CASE(SI_ASYNCIO)
		CASE(SI_MESGQ)
	}

	switch (signal) {
		case SIGILL:
			switch (code) {
				CASE(ILL_ILLOPC)
				CASE(ILL_ILLOPN)
				CASE(ILL_ILLADR)
				CASE(ILL_ILLTRP)
				CASE(ILL_PRVOPC)
				CASE(ILL_PRVREG)
				CASE(ILL_COPROC)
				CASE(ILL_BADSTK)
			}
			break;
		case SIGFPE:
			switch (code) {
				CASE(FPE_INTDIV)
				CASE(FPE_INTOVF)
				CASE(FPE_FLTDIV)
				CASE(FPE_FLTOVF)
				CASE(FPE_FLTUND)
				CASE(FPE_FLTRES)
				CASE(FPE_FLTINV)
				CASE(FPE_FLTSUB)
			}
			break;
		case SIGSEGV:
			switch (code) {
				CASE(SEGV_MAPERR)
				CASE(SEGV_ACCERR)
			}
			break;
		case SIGBUS:
			switch (code) {
				CASE(BUS_ADRALN)
				CASE(BUS_ADRERR)
				CASE(BUS_OBJERR)
			}
			break;
		case SIGTRAP:
			switch (code) {
				CASE(TRAP_BRKPT)
				CASE(TRAP_TRACE)
			}
			break;
		case SIGCHLD:
			switch (code) {
				CASE(CLD_EXITED)
				CASE(CLD_KILLED)
				CASE(CLD_DUMPED)
				CASE(CLD_TRAPPED)
				CASE(CLD_STOPPED)
				CASE(CLD_CONTINUED)
			}
			break;
		case SIGPOLL:
			switch (code) {
				CASE(POLL_IN)
				CASE(POLL_OUT)
				CASE(POLL_MSG)
				CASE(POLL_ERR)
				CASE(POLL_PRI)
				CASE(POLL_HUP)
			}
			break;
	}
#undef CASE

	static char buffer[32];
	sprintf(buffer, "%d", code);
	return buffer;
}


std::string
signal_info(siginfo_t& info)
{
	static char buffer[32];
	std::string string;

	string.reserve(256);

	string += "{";
	string += "si_signo=";
	string += signal_name(info.si_signo);
	string += ", si_code=";
	string += signal_code(info.si_signo, info.si_code);

	if (info.si_errno != 0) {
		string += ", si_errno=0x";
		sprintf(buffer, "%x", info.si_errno);
		string += buffer;
	}

	string += ", si_pid=";
	sprintf(buffer, "%d", (int)info.si_pid);
	string += buffer;
	string += ", si_uid=";
	sprintf(buffer, "%d", (int)info.si_uid);
	string += buffer;

	if (info.si_signo == SIGILL || info.si_signo == SIGFPE || info.si_signo == SIGSEGV
		|| info.si_signo == SIGBUS || info.si_signo == SIGTRAP) {
		string += ", si_addr=";
		sprintf(buffer, "%p", info.si_addr);
		string += buffer;
	}

	if (info.si_signo == SIGCHLD) {
		string += ", si_status=";
		sprintf(buffer, "%d", info.si_status);
		string += buffer;
	}

	if (info.si_signo == SIGPOLL) {
		string += ", si_band=";
		sprintf(buffer, "%ld", info.si_band);
		string += buffer;
	}

	string += ", si_value=";
	sprintf(buffer, "%p", info.si_value.sival_ptr);
	string += buffer;
	string += "}";

	return string;
}


class SigsetTypeHandler : public FlagsTypeHandler {
public:
	SigsetTypeHandler()
		:
		FlagsTypeHandler(kSigmaskFlags)
	{
	}

	string GetParameterValue(Context &context, Parameter *param,
		const void *address)
	{
		void *data = *(void **)address;

		if (context.GetContents(Context::POINTER_VALUES)) {
			int32 bytesRead;
			sigset_t value;
			status_t err = context.Reader().Read(data, &value, sizeof(value), bytesRead);
			if (err != B_OK)
				return context.FormatPointer(data);

			uint32 count = 0;
#if __GNUC__ > 2
                count += __builtin_popcount(value);
#else
			{
				sigset_t mask = value;
				while (mask > 0) {
					if ((mask & 1) == 1)
						count++;
					mask >>= 1;
				}
			}
#endif
			string r;
			// when more than 2/3 of the signals are set, inverse
			if (count > SIGRESERVED2 * 2 / 3) {
				value = ~value;
				r += "~";
			}
			value &= (((sigset_t)1 << SIGRESERVED2) - 1);

			r += "[";
			r += RenderValue(context, value);
			r += "]";
			return r;
		}

		return context.FormatPointer(data);
	}

	string GetReturnValue(Context &context, uint64 value)
	{
		return context.FormatPointer((void *)value);
	}
};


void
patch_signal()
{
	for (int i = 0; kSigmaskFlagsInfo[i].name != NULL; i++)
		kSigmaskFlags.push_back(kSigmaskFlagsInfo[i]);
	for (int i = 0; kSigmaskHow[i].name != NULL; i++)
		kSigmaskHowMap[kSigmaskHow[i].index] = kSigmaskHow[i].name;
	for (int i = 0; kSignals[i].name != NULL; i++)
		kSignalsMap[kSignals[i].index] = kSignals[i].name;
	Syscall *setSignalMask = get_syscall("_kern_set_signal_mask");
	setSignalMask->GetParameter("how")->SetHandler(new EnumTypeHandler(kSigmaskHowMap));
	setSignalMask->GetParameter("set")->SetHandler(new SigsetTypeHandler());
	setSignalMask->GetParameter("oldSet")->SetHandler(new SigsetTypeHandler());
	setSignalMask->GetParameter("oldSet")->SetOut(true);

	Syscall *sigwait = get_syscall("_kern_sigwait");
	sigwait->GetParameter("set")->SetHandler(new SigsetTypeHandler());
	sigwait->GetParameter("info")->SetOut(true);

	Syscall *sigsuspend = get_syscall("_kern_sigsuspend");
	sigsuspend->GetParameter("mask")->SetHandler(new SigsetTypeHandler());

	Syscall *sigpending = get_syscall("_kern_sigpending");
	sigpending->GetParameter("set")->SetHandler(new SigsetTypeHandler());
	sigpending->GetParameter("set")->SetOut(true);

	Syscall *sendSignal = get_syscall("_kern_send_signal");
	sendSignal->GetParameter("signal")->SetHandler(new EnumTypeHandler(kSignalsMap));
	Syscall *sigaction = get_syscall("_kern_sigaction");
	sigaction->GetParameter("sig")->SetHandler(new EnumTypeHandler(kSignalsMap));
}