#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <linux/filter.h>
#include <sys/syscall.h>
#include <err.h>
#include <sys/prctl.h>
#include <stddef.h>
#include <sys/ucontext.h>

#ifndef __ARM_EABI__
#error Must be compiled for ARM EABI
#endif

struct seccomp_data {
	int nr;
	__u32 arch;
	__u64 instruction_pointer;
	__u64 args[6];
};

#ifndef PR_SET_NO_NEW_PRIVS
#define PR_SET_NO_NEW_PRIVS	38
#endif

#define SECCOMP_RET_KILL 0x00000000U
#define SECCOMP_RET_TRAP 0x00030000U
#define SECCOMP_RET_ERRNO 0x00050000U
#define SECCOMP_RET_TRACE 0x7ff00000U
#define SECCOMP_RET_ALLOW 0x7fff0000U

struct sifields_sigsys {
	void *_call_addr;
	int _syscall;
	unsigned int _arch;
};

#define SIGINFO_SIGSYS(si) ((struct sifields_sigsys *)&(si)->_sifields)

__attribute__((noinline,optimize("2"))) long call_getgid_oabi(void)
{
	// If OABI compatibility is disabled, this will call exit instead
	// (That's what r7==1 means.)
	register long ret asm("r0");
	asm volatile("mov r0, $1\n\tmov r1, $2\n\tmov r2, $3\n\t"
		     "mov r3, $4\n\tmov r4, $5\n\tmov r5, $6\n\t"
		     "mov r7, $1\n\tsvc $0x90002f\n\t" : : :
		     "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "memory");
	return ret;
}

void handler(int signum, siginfo_t *si, void *uc_void)
{
	const struct ucontext *uc = uc_void;
	const struct sifields_sigsys *ss = SIGINFO_SIGSYS(si);

	printf("SIGSYS\n\n");
	printf("nr: 0x%X (__NR_getgid = 0x%X, OABI nr = 0x90002F)\n",
	       ss->_syscall, __NR_getgid);

#define DO_REG(i) printf("r" #i ":  0x%08X\n", uc->uc_mcontext.arm_r##i);
	DO_REG(0);
	DO_REG(1);
	DO_REG(2);
	DO_REG(3);
	DO_REG(4);
	DO_REG(5);
	DO_REG(6);
	DO_REG(7);
}

int main()
{
	int rc;

	struct sock_filter filter[] = {
		BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
			 (offsetof(struct seccomp_data, nr))),
		BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_getgid, 0, 1),
		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
		BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
	};

	struct sock_fprog prog = {
		.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
		.filter = filter,
	};

	struct sigaction sa = {};
	sa.sa_sigaction = handler;
	sa.sa_flags = SA_SIGINFO;
	if (sigaction(SIGSYS, &sa, NULL) != 0)
		err(1, "sigaction");

	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
		err(1, "PR_SET_NO_NEW_PRIVS");
	if (prctl(PR_SET_SECCOMP, 2, &prog))
		err(1, "PR_SET_SECCOMP");

	call_getgid_oabi();

	return 0;
}
