Implement execveat syscall decoding

* execve.c (decode_execve): New function.
(sys_execve): Use it.
(sys_execveat): New function.
* linux/dummy.h (sys_execveat): Remove.
* tests/execveat.c: New file.
* tests/execveat.expected: Likewise.
* tests/execveat-v.expected: Likewise.
* tests/execveat.test: New test.
* tests/Makefile.am (check_PROGRAMS): Add execveat.
(TESTS): Add execveat.test.
(EXTRA_DIST): Add execveat.expected and execveat-v.expected.
* tests/.gitignore: Add execveat.
diff --git a/execve.c b/execve.c
index 3f49e20..a8f53f6 100644
--- a/execve.c
+++ b/execve.c
@@ -40,29 +40,45 @@
 	tprintf(fmt, count, count == 1 ? "" : "s");
 }
 
+static void
+decode_execve(struct tcb *tcp, const unsigned int index)
+{
+	printpath(tcp, tcp->u_arg[index + 0]);
+	tprints(", ");
+
+	if (!tcp->u_arg[index + 1] || !verbose(tcp))
+		printaddr(tcp->u_arg[index + 1]);
+	else {
+		tprints("[");
+		printargv(tcp, tcp->u_arg[index + 1]);
+		tprints("]");
+	}
+	tprints(", ");
+
+	if (!tcp->u_arg[index + 2] || !verbose(tcp))
+		printaddr(tcp->u_arg[index + 2]);
+	else if (abbrev(tcp))
+		printargc("[/* %d var%s */]", tcp, tcp->u_arg[index + 2]);
+	else {
+		tprints("[");
+		printargv(tcp, tcp->u_arg[index + 2]);
+		tprints("]");
+	}
+}
+
 SYS_FUNC(execve)
 {
-	printpath(tcp, tcp->u_arg[0]);
-	tprints(", ");
+	decode_execve(tcp, 0);
 
-	if (!tcp->u_arg[1] || !verbose(tcp))
-		printaddr(tcp->u_arg[1]);
-	else {
-		tprints("[");
-		printargv(tcp, tcp->u_arg[1]);
-		tprints("]");
-	}
-	tprints(", ");
+	return RVAL_DECODED;
+}
 
-	if (!tcp->u_arg[2] || !verbose(tcp))
-		printaddr(tcp->u_arg[2]);
-	else if (abbrev(tcp))
-		printargc("[/* %d var%s */]", tcp, tcp->u_arg[2]);
-	else {
-		tprints("[");
-		printargv(tcp, tcp->u_arg[2]);
-		tprints("]");
-	}
+SYS_FUNC(execveat)
+{
+	print_dirfd(tcp, tcp->u_arg[0]);
+	decode_execve(tcp, 1);
+	tprints(", ");
+	printflags(at_flags, tcp->u_arg[4], "AT_???");
 
 	return RVAL_DECODED;
 }
diff --git a/linux/dummy.h b/linux/dummy.h
index 0a3db91..4e9db9e 100644
--- a/linux/dummy.h
+++ b/linux/dummy.h
@@ -32,7 +32,6 @@
 #endif
 
 /* still unfinished */
-#define	sys_execveat		printargs
 #define	sys_ioperm		printargs
 #define	sys_iopl		printargs
 #define	sys_kcmp		printargs
diff --git a/tests/.gitignore b/tests/.gitignore
index 495a737..3ea70db 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,6 +1,7 @@
 bpf
 caps
 execve
+execveat
 fanotify_mark
 filter-unavailable
 getrandom
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 3da24bf..bd3f5cf 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -12,6 +12,7 @@
 	bpf \
 	caps \
 	execve \
+	execveat \
 	fanotify_mark \
 	filter-unavailable \
 	getrandom \
@@ -67,6 +68,7 @@
 	caps.test \
 	dumpio.test \
 	execve.test \
+	execveat.test \
 	fanotify_mark.test \
 	filter-unavailable.test \
 	getdents.test \
@@ -121,6 +123,8 @@
 	     dumpio.expected \
 	     execve.expected \
 	     execve-v.expected \
+	     execveat.expected \
+	     execveat-v.expected \
 	     fanotify_mark.expected \
 	     filter-unavailable.expected \
 	     getdents.awk \
diff --git a/tests/execveat-v.expected b/tests/execveat-v.expected
new file mode 100644
index 0000000..ebac879
--- /dev/null
+++ b/tests/execveat-v.expected
@@ -0,0 +1 @@
+execveat\(AT_FDCWD, "execveat\\nfilename", \["execveat\\nfilename", "first", "second"\], \["foobar=1", "foo\\nbar=2"\], AT_SYMLINK_NOFOLLOW\|AT_EMPTY_PATH\) += -1 .*
diff --git a/tests/execveat.c b/tests/execveat.c
new file mode 100644
index 0000000..4363c94
--- /dev/null
+++ b/tests/execveat.c
@@ -0,0 +1,31 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#ifdef __NR_execveat
+
+#define FILENAME "execveat\nfilename"
+static const char * const argv[] =
+	{ FILENAME, "first", "second", NULL, NULL, NULL };
+static const char * const envp[] =
+	{ "foobar=1", "foo\nbar=2", NULL , "", NULL , "", NULL, NULL};
+
+int
+main(void)
+{
+	syscall(__NR_execveat, -100, FILENAME, argv, envp, 0x1100);
+	return 0;
+}
+
+#else
+
+int
+main(void)
+{
+	return 77;
+}
+
+#endif
diff --git a/tests/execveat.expected b/tests/execveat.expected
new file mode 100644
index 0000000..7383ed2
--- /dev/null
+++ b/tests/execveat.expected
@@ -0,0 +1 @@
+execveat\(AT_FDCWD, "execveat\\nfilename", \["execveat\\nfilename", "first", "second"\], \[/\* 2 vars \*/\], AT_SYMLINK_NOFOLLOW\|AT_EMPTY_PATH\) += -1 .*
diff --git a/tests/execveat.test b/tests/execveat.test
new file mode 100755
index 0000000..b371b07
--- /dev/null
+++ b/tests/execveat.test
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+# Check execveat syscall decoding.
+
+. "${srcdir=.}/init.sh"
+
+run_prog
+run_strace $args
+match_grep
+run_strace -v $args
+match_grep "$LOG" "$srcdir/${ME_%.test}-v.expected"
+
+exit 0