kNCurses/Debug.cpp

503 lines
25 KiB
C++

/**
* @author Christian Burger (christian@krikkel.de)
* @todo Switch over to <seccomp.h>? For resolving system call numbers?
* Maybe keep the current solution as a fallback?
* @todo catch `out of range` exception from stoi()
*
* Contains mapping of system calls numbers to names from original dev system:
* "Linux 5.13.0-28-generic #31~20.04.1-Ubuntu SMP \
* Wed Jan 19 14:08:10 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux"
* See comment at the end for the actual command to gather mapping information.
*/
#ifndef NDEBUG
#include "Debug.hpp"
#include <algorithm>
#include <cstring>
#include <filesystem>
#include <cstdio>
#include <syscall.h>
#include <sys/ptrace.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFER_SIZE 128
namespace krikkel
{
using std::string;
using std::fstream;
using std::filesystem::is_directory;
using std::endl;
using std::time;
using std::localtime;
using std::strftime;
using std::min;
Debug::Debug()
{
string directory = ".";
if(is_directory("sandbox"))
directory = "./sandbox";
logFile = fstream(directory + "/debug.log", fstream::out | fstream::app);//| fstream::trunc);
logFile << endl << endl << endl << endl << "New instance of class Debug." << endl;
logFile << getUname();
logFile.flush();
}
string Debug::getUname()
{
string result = "";
char buffer[BUFFER_SIZE];
FILE *unameProcess = popen("uname -srvmpio", "r");
if(unameProcess && fgets(buffer, BUFFER_SIZE, unameProcess))
result = buffer;
pclose(unameProcess);
return result;
}
string Debug::getTimestamp()
{
time_t now = time(NULL);
tm *localNow = localtime(&now);
static char formattedLocalNow[32];
strftime(formattedLocalNow, 32, "%c", localNow);
return formattedLocalNow;
}
void Debug::log(string message, string fileName, int lineNo, string functionName)
{
string output = "";
size_t position = message.find("sysCall(");
if(position != string::npos)
{
size_t end = message.find_first_not_of("0123456789", position + 8);
unsigned long long systemCallNumber = std::stoi(message.substr(position + 8, end - position - 8));
string systemCallName = strlen(syscalls[systemCallNumber]) != 0 ? syscalls[systemCallNumber]
: "unknown_system_call";
if(systemCallNumber < MAX_NUMBER_OF_SYSCALLS)
output = message.replace(position, end - position + 1, systemCallName + "(");
}
if(output == "")
output = message;
logFile << getTimestamp() << ": " << output << " (in "
<< fileName.substr(fileName.rfind('/') + 1) << ":"
<< lineNo << " in " << functionName << "())" << endl;
logFile.flush();
}
std::string Debug::peekData(pid_t shellPid, void *data, size_t length)
{
std::string result;
for(size_t index = 0; index < length / sizeof(long); ++index)
{
long datum = ptrace(PTRACE_PEEKDATA, shellPid, ((char *) data) + index * sizeof(long));
if(length == (size_t) -1)
{
size_t datumLength = strnlen((char *) &datum, sizeof(long));
result += string((char *) &datum, datumLength);
if(datumLength < sizeof(long))
break;
}
else
result += string((char *) &datum, sizeof(long));
}
return result;
}
std::string Debug::formatToAddress(unsigned long long address)
{
char addressString[17];
snprintf(addressString, 17, "%016llx", address);
return string("0x") + addressString;
}
void Debug::logPtraceSysCall(pid_t shellPid, uint16_t sysCallId, bool returnedFromSysCall, unsigned long long result, unsigned long long firstArgument, unsigned long long secondArgument, unsigned long long thirdArgument, unsigned long long fourthArgument, std::string fileName, int lineNo, std::string functionName)
{
const static uint8_t maxStringLength = 32;
string message = "syscall: ";
string sysCallName;
if(sysCallId < MAX_NUMBER_OF_SYSCALLS)
sysCallName = syscalls[sysCallId];
if(sysCallName.empty())
sysCallName = "syscall" + std::to_string(sysCallId);
switch(sysCallId)
{
case SYS_read:
if(returnedFromSysCall)
message += (result == (unsigned long long) -1 ? "-1" : std::to_string((size_t) result)) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", \"" + (returnedFromSysCall ? __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength))) : "")
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
+ ", " + std::to_string((size_t) thirdArgument)
+ ")";
break;
case SYS_write:
if(returnedFromSysCall)
message += (result == (unsigned long long) -1 ? "-1" : std::to_string((size_t) result)) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
+ (maxStringLength < thirdArgument || returnedFromSysCall == true ? "[…]" : "") + "\""
+ ", " + std::to_string((size_t) thirdArgument)
+ ")";
break;
case SYS_close:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ")";
break;
case SYS_stat:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(\"" + peekData(shellPid, (char *) firstArgument, -1) + "\""
+ ", " + formatToAddress(secondArgument)
+ ")";
break;
case SYS_fstat:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + formatToAddress(secondArgument)
+ ")";
break;
case SYS_openat:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + ((int) firstArgument == AT_FDCWD ? string("AT_FDCWD") : std::to_string((int) firstArgument))
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, -1)) + "\""
+ ", " + std::to_string((int) thirdArgument)
+ ")";
break;
case SYS_rt_sigprocmask:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + formatToAddress(secondArgument)
+ ", " + formatToAddress(thirdArgument)
+ ", " + std::to_string((size_t) fourthArgument)
+ ")";
break;
case SYS_access:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + peekData(shellPid, (char *) firstArgument, -1)
+ ", " + std::to_string((int) secondArgument)
+ ")";
break;
case SYS_dup2:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + std::to_string((int) secondArgument)
+ ")";
break;
case SYS_fcntl:
if(returnedFromSysCall)
message += std::to_string((int) result) + " = ";
message += sysCallName
+ "(" + std::to_string((int) firstArgument)
+ ", " + (secondArgument == 0 ? string("F_DUP_FD") : std::to_string((int) secondArgument))
+ ", …"
+ ")";
break;
/* case SYS_fstat:
struct stat statbuf;
message += "(" + std::to_string(firstArgument)
+ ", \"" + __debug_make_bytes_printable(peekData(shellPid, (char *) secondArgument, (size_t) min<unsigned long long>(thirdArgument, maxStringLength)))
+ (maxStringLength < thirdArgument ? "[…]" : "") + "\""
+ ", " + std::to_string((size_t) thirdArgument)
+ ")";
break;
*/ default:
if(returnedFromSysCall)
message += (result == (unsigned long long) -1 ? "-1" : std::to_string(result)) + " = ";
message += sysCallName
+ "(" + formatToAddress(firstArgument)
+ ", " + formatToAddress(secondArgument)
+ ", " + formatToAddress(thirdArgument)
+ ", " + formatToAddress(fourthArgument)
+ ")";
}
// source: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
log(message, fileName, lineNo, functionName);
}
Debug *Debug::getInstance()
{
static Debug *debug = new Debug();
return debug;
}
const char *Debug::syscalls[MAX_NUMBER_OF_SYSCALLS] = {/*[0] =*/ "read",/*[1] =*/ "write",
/*[2] =*/ "open",/*[3] =*/ "close",
/*[4] =*/ "stat",/*[5] =*/ "fstat",
/*[6] =*/ "lstat",/*[7] =*/ "poll",
/*[8] =*/ "lseek",/*[9] =*/ "mmap",
/*[10] =*/ "mprotect",/*[11] =*/ "munmap",
/*[12] =*/ "brk",/*[13] =*/ "rt_sigaction",
/*[14] =*/ "rt_sigprocmask",/*[15] =*/ "rt_sigreturn",
/*[16] =*/ "ioctl",/*[17] =*/ "pread64",
/*[18] =*/ "pwrite64",/*[19] =*/ "readv",
/*[20] =*/ "writev",/*[21] =*/ "access",
/*[22] =*/ "pipe",/*[23] =*/ "select",
/*[24] =*/ "sched_yield",/*[25] =*/ "mremap",
/*[26] =*/ "msync",/*[27] =*/ "mincore",
/*[28] =*/ "madvise",/*[29] =*/ "shmget",
/*[30] =*/ "shmat",/*[31] =*/ "shmctl",
/*[32] =*/ "dup",/*[33] =*/ "dup2",
/*[34] =*/ "pause",/*[35] =*/ "nanosleep",
/*[36] =*/ "getitimer",/*[37] =*/ "alarm",
/*[38] =*/ "setitimer",/*[39] =*/ "getpid",
/*[40] =*/ "sendfile",/*[41] =*/ "socket",
/*[42] =*/ "connect",/*[43] =*/ "accept",
/*[44] =*/ "sendto",/*[45] =*/ "recvfrom",
/*[46] =*/ "sendmsg",/*[47] =*/ "recvmsg",
/*[48] =*/ "shutdown",/*[49] =*/ "bind",
/*[50] =*/ "listen",/*[51] =*/ "getsockname",
/*[52] =*/ "getpeername",/*[53] =*/ "socketpair",
/*[54] =*/ "setsockopt",/*[55] =*/ "getsockopt",
/*[56] =*/ "clone",/*[57] =*/ "fork",
/*[58] =*/ "vfork",/*[59] =*/ "execve",
/*[60] =*/ "exit",/*[61] =*/ "wait4",
/*[62] =*/ "kill",/*[63] =*/ "uname",
/*[64] =*/ "semget",/*[65] =*/ "semop",
/*[66] =*/ "semctl",/*[67] =*/ "shmdt",
/*[68] =*/ "msgget",/*[69] =*/ "msgsnd",
/*[70] =*/ "msgrcv",/*[71] =*/ "msgctl",
/*[72] =*/ "fcntl",/*[73] =*/ "flock",
/*[74] =*/ "fsync",/*[75] =*/ "fdatasync",
/*[76] =*/ "truncate",/*[77] =*/ "ftruncate",
/*[78] =*/ "getdents",/*[79] =*/ "getcwd",
/*[80] =*/ "chdir",/*[81] =*/ "fchdir",
/*[82] =*/ "rename",/*[83] =*/ "mkdir",
/*[84] =*/ "rmdir",/*[85] =*/ "creat",
/*[86] =*/ "link",/*[87] =*/ "unlink",
/*[88] =*/ "symlink",/*[89] =*/ "readlink",
/*[90] =*/ "chmod",/*[91] =*/ "fchmod",
/*[92] =*/ "chown",/*[93] =*/ "fchown",
/*[94] =*/ "lchown",/*[95] =*/ "umask",
/*[96] =*/ "gettimeofday",/*[97] =*/ "getrlimit",
/*[98] =*/ "getrusage",/*[99] =*/ "sysinfo",
/*[100] =*/ "times",/*[101] =*/ "ptrace",
/*[102] =*/ "getuid",/*[103] =*/ "syslog",
/*[104] =*/ "getgid",/*[105] =*/ "setuid",
/*[106] =*/ "setgid",/*[107] =*/ "geteuid",
/*[108] =*/ "getegid",/*[109] =*/ "setpgid",
/*[110] =*/ "getppid",/*[111] =*/ "getpgrp",
/*[112] =*/ "setsid",/*[113] =*/ "setreuid",
/*[114] =*/ "setregid",/*[115] =*/ "getgroups",
/*[116] =*/ "setgroups",/*[117] =*/ "setresuid",
/*[118] =*/ "getresuid",/*[119] =*/ "setresgid",
/*[120] =*/ "getresgid",/*[121] =*/ "getpgid",
/*[122] =*/ "setfsuid",/*[123] =*/ "setfsgid",
/*[124] =*/ "getsid",/*[125] =*/ "capget",
/*[126] =*/ "capset",/*[127] =*/ "rt_sigpending",
/*[128] =*/ "rt_sigtimedwait",/*[129] =*/ "rt_sigqueueinfo",
/*[130] =*/ "rt_sigsuspend",/*[131] =*/ "sigaltstack",
/*[132] =*/ "utime",/*[133] =*/ "mknod",
/*[134] =*/ "uselib",/*[135] =*/ "personality",
/*[136] =*/ "ustat",/*[137] =*/ "statfs",
/*[138] =*/ "fstatfs",/*[139] =*/ "sysfs",
/*[140] =*/ "getpriority",/*[141] =*/ "setpriority",
/*[142] =*/ "sched_setparam",/*[143] =*/ "sched_getparam",
/*[144] =*/ "sched_setscheduler",/*[145] =*/ "sched_getscheduler",
/*[146] =*/ "sched_get_priority_max",/*[147] =*/ "sched_get_priority_min",
/*[148] =*/ "sched_rr_get_interval",/*[149] =*/ "mlock",
/*[150] =*/ "munlock",/*[151] =*/ "mlockall",
/*[152] =*/ "munlockall",/*[153] =*/ "vhangup",
/*[154] =*/ "modify_ldt",/*[155] =*/ "pivot_root",
/*[156] =*/ "",/*[157] =*/ "prctl",
/*[158] =*/ "arch_prctl",/*[159] =*/ "adjtimex",
/*[160] =*/ "setrlimit",/*[161] =*/ "chroot",
/*[162] =*/ "sync",/*[163] =*/ "acct",
/*[164] =*/ "settimeofday",/*[165] =*/ "mount",
/*[166] =*/ "",/*[167] =*/ "swapon",
/*[168] =*/ "swapoff",/*[169] =*/ "reboot",
/*[170] =*/ "sethostname",/*[171] =*/ "setdomainname",
/*[172] =*/ "iopl",/*[173] =*/ "ioperm",
/*[174] =*/ "",/*[175] =*/ "init_module",
/*[176] =*/ "delete_module",/*[177] =*/ "",
/*[178] =*/ "",/*[179] =*/ "quotactl",
/*[180] =*/ "",/*[181] =*/ "",
/*[182] =*/ "",/*[183] =*/ "",
/*[184] =*/ "",/*[185] =*/ "",
/*[186] =*/ "gettid",/*[187] =*/ "readahead",
/*[188] =*/ "setxattr",/*[189] =*/ "lsetxattr",
/*[190] =*/ "fsetxattr",/*[191] =*/ "getxattr",
/*[192] =*/ "lgetxattr",/*[193] =*/ "fgetxattr",
/*[194] =*/ "listxattr",/*[195] =*/ "llistxattr",
/*[196] =*/ "flistxattr",/*[197] =*/ "removexattr",
/*[198] =*/ "lremovexattr",/*[199] =*/ "fremovexattr",
/*[200] =*/ "tkill",/*[201] =*/ "time",
/*[202] =*/ "futex",/*[203] =*/ "sched_setaffinity",
/*[204] =*/ "sched_getaffinity",/*[205] =*/ "set_thread_area",
/*[206] =*/ "io_setup",/*[207] =*/ "io_destroy",
/*[208] =*/ "io_getevents",/*[209] =*/ "io_submit",
/*[210] =*/ "io_cancel",/*[211] =*/ "get_thread_area",
/*[212] =*/ "",/*[213] =*/ "epoll_create",
/*[214] =*/ "",/*[215] =*/ "",
/*[216] =*/ "remap_file_pages",/*[217] =*/ "getdents64",
/*[218] =*/ "set_tid_address",/*[219] =*/ "restart_syscall",
/*[220] =*/ "semtimedop",/*[221] =*/ "fadvise64",
/*[222] =*/ "timer_create",/*[223] =*/ "timer_settime",
/*[224] =*/ "timer_gettime",/*[225] =*/ "timer_getoverrun",
/*[226] =*/ "timer_delete",/*[227] =*/ "clock_settime",
/*[228] =*/ "clock_gettime",/*[229] =*/ "clock_getres",
/*[230] =*/ "clock_nanosleep",/*[231] =*/ "exit_group",
/*[232] =*/ "epoll_wait",/*[233] =*/ "epoll_ctl",
/*[234] =*/ "tgkill",/*[235] =*/ "utimes",
/*[236] =*/ "",/*[237] =*/ "mbind",
/*[238] =*/ "set_mempolicy",/*[239] =*/ "get_mempolicy",
/*[240] =*/ "mq_open",/*[241] =*/ "mq_unlink",
/*[242] =*/ "mq_timedsend",/*[243] =*/ "mq_timedreceive",
/*[244] =*/ "mq_notify",/*[245] =*/ "mq_getsetattr",
/*[246] =*/ "kexec_load",/*[247] =*/ "waitid",
/*[248] =*/ "add_key",/*[249] =*/ "request_key",
/*[250] =*/ "keyctl",/*[251] =*/ "ioprio_set",
/*[252] =*/ "ioprio_get",/*[253] =*/ "inotify_init",
/*[254] =*/ "inotify_add_watch",/*[255] =*/ "inotify_rm_watch",
/*[256] =*/ "migrate_pages",/*[257] =*/ "openat",
/*[258] =*/ "mkdirat",/*[259] =*/ "mknodat",
/*[260] =*/ "fchownat",/*[261] =*/ "futimesat",
/*[262] =*/ "newfstatat",/*[263] =*/ "unlinkat",
/*[264] =*/ "renameat",/*[265] =*/ "linkat",
/*[266] =*/ "symlinkat",/*[267] =*/ "readlinkat",
/*[268] =*/ "fchmodat",/*[269] =*/ "faccessat",
/*[270] =*/ "pselect6",/*[271] =*/ "ppoll",
/*[272] =*/ "unshare",/*[273] =*/ "set_robust_list",
/*[274] =*/ "get_robust_list",/*[275] =*/ "splice",
/*[276] =*/ "tee",/*[277] =*/ "sync_file_range",
/*[278] =*/ "vmsplice",/*[279] =*/ "move_pages",
/*[280] =*/ "utimensat",/*[281] =*/ "epoll_pwait",
/*[282] =*/ "signalfd",/*[283] =*/ "timerfd_create",
/*[284] =*/ "eventfd",/*[285] =*/ "fallocate",
/*[286] =*/ "timerfd_settime",/*[287] =*/ "timerfd_gettime",
/*[288] =*/ "accept4",/*[289] =*/ "signalfd4",
/*[290] =*/ "eventfd2",/*[291] =*/ "epoll_create1",
/*[292] =*/ "dup3",/*[293] =*/ "pipe2",
/*[294] =*/ "inotify_init1",/*[295] =*/ "preadv",
/*[296] =*/ "pwritev",/*[297] =*/ "rt_tgsigqueueinfo",
/*[298] =*/ "perf_event_open",/*[299] =*/ "recvmmsg",
/*[300] =*/ "fanotify_init",/*[301] =*/ "fanotify_mark",
/*[302] =*/ "prlimit64",/*[303] =*/ "name_to_handle_at",
/*[304] =*/ "open_by_handle_at",/*[305] =*/ "clock_adjtime",
/*[306] =*/ "syncfs",/*[307] =*/ "sendmmsg",
/*[308] =*/ "setns",/*[309] =*/ "getcpu",
/*[310] =*/ "process_vm_readv",/*[311] =*/ "process_vm_writev",
/*[312] =*/ "kcmp",/*[313] =*/ "finit_module",
/*[314] =*/ "sched_setattr",/*[315] =*/ "sched_getattr",
/*[316] =*/ "renameat2",/*[317] =*/ "seccomp",
/*[318] =*/ "getrandom",/*[319] =*/ "memfd_create",
/*[320] =*/ "kexec_file_load",/*[321] =*/ "bpf",
/*[322] =*/ "execveat",/*[323] =*/ "userfaultfd",
/*[324] =*/ "membarrier",/*[325] =*/ "mlock2",
/*[326] =*/ "copy_file_range",/*[327] =*/ "preadv2",
/*[328] =*/ "pwritev2",/*[329] =*/ "pkey_mprotect",
/*[330] =*/ "pkey_alloc",/*[331] =*/ "pkey_free",
/*[332] =*/ "statx",/*[333] =*/ "io_pgetevents",
/*[334] =*/ "rseq",/*[335] =*/ "",
/*[336] =*/ "",/*[337] =*/ "",
/*[338] =*/ "",/*[339] =*/ "",
/*[340] =*/ "",/*[341] =*/ "",
/*[342] =*/ "",/*[343] =*/ "",
/*[344] =*/ "",/*[345] =*/ "",
/*[346] =*/ "",/*[347] =*/ "",
/*[348] =*/ "",/*[349] =*/ "",
/*[350] =*/ "",/*[351] =*/ "",
/*[352] =*/ "",/*[353] =*/ "",
/*[354] =*/ "",/*[355] =*/ "",
/*[356] =*/ "",/*[357] =*/ "",
/*[358] =*/ "",/*[359] =*/ "",
/*[360] =*/ "",/*[361] =*/ "",
/*[362] =*/ "",/*[363] =*/ "",
/*[364] =*/ "",/*[365] =*/ "",
/*[366] =*/ "",/*[367] =*/ "",
/*[368] =*/ "",/*[369] =*/ "",
/*[370] =*/ "",/*[371] =*/ "",
/*[372] =*/ "",/*[373] =*/ "",
/*[374] =*/ "",/*[375] =*/ "",
/*[376] =*/ "",/*[377] =*/ "",
/*[378] =*/ "",/*[379] =*/ "",
/*[380] =*/ "",/*[381] =*/ "",
/*[382] =*/ "",/*[383] =*/ "",
/*[384] =*/ "",/*[385] =*/ "",
/*[386] =*/ "",/*[387] =*/ "",
/*[388] =*/ "",/*[389] =*/ "",
/*[390] =*/ "",/*[391] =*/ "",
/*[392] =*/ "",/*[393] =*/ "",
/*[394] =*/ "",/*[395] =*/ "",
/*[396] =*/ "",/*[397] =*/ "",
/*[398] =*/ "",/*[399] =*/ "",
/*[400] =*/ "",/*[401] =*/ "",
/*[402] =*/ "",/*[403] =*/ "",
/*[404] =*/ "",/*[405] =*/ "",
/*[406] =*/ "",/*[407] =*/ "",
/*[408] =*/ "",/*[409] =*/ "",
/*[410] =*/ "",/*[411] =*/ "",
/*[412] =*/ "",/*[413] =*/ "",
/*[414] =*/ "",/*[415] =*/ "",
/*[416] =*/ "",/*[417] =*/ "",
/*[418] =*/ "",/*[419] =*/ "",
/*[420] =*/ "",/*[421] =*/ "",
/*[422] =*/ "",/*[423] =*/ "",
/*[424] =*/ "pidfd_send_signal",/*[425] =*/ "io_uring_setup",
/*[426] =*/ "io_uring_enter",/*[427] =*/ "io_uring_register",
/*[428] =*/ "open_tree",/*[429] =*/ "move_mount",
/*[430] =*/ "fsopen",/*[431] =*/ "fsconfig",
/*[432] =*/ "fsmount",/*[433] =*/ "fspick",
/*[434] =*/ "pidfd_open",/*[435] =*/ "clone3" };
}
#endif
/*
Following command in part thx to:
https://unix.stackexchange.com/questions/445507/syscall-number-%E2%86%92-name-mapping-at-runtime
Command to create mapping of system call number to system call name:
#!/bin/bash
awk 'BEGIN { print "#include <sys/syscall.h>" }
/p_syscall_meta/ { syscall = substr($NF, 19);
printf "[SYS_%s] = \"%s\", \n", syscall, syscall }' /proc/kallsyms \
| gcc -E -P - \
| sort -V \
| grep "\[[0-9]" \
| awk 'BEGIN {expectedIndex = 0;}
{ actualIndex = $1;
gsub(/[\[\]]/, "", actualIndex);
if (actualIndex != expectedIndex)
for (; expectedIndex < actualIndex; expectedIndex++)
print "[" expectedIndex "] = \"\",";
print $0; expectedIndex++ }' \
| tr -d "\n" \
| sed -e "s/^/const char *syscalls[1024] = {/; s/,$/ };/" \
| sed -e 's/,/,\n /2;P;D' \
| cat - <(echo) # new line at the end
*/