// Based on https://gist.github.com/jvranish/4441299
#if defined(__linux__) || defined(__FreeBSD__) // Pierre-Marie Baty -- added compile guard
#define _GNU_SOURCE
#include "harness/config.h"
#include "harness/os.h"
#include <assert.h>
#include <ctype.h>
#include <dirent.h>
#include <err.h>
#include <errno.h>
#include <execinfo.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <link.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>
#define ARRAY_SIZE(A) (sizeof(A) / sizeof(A[0]))
#define MAX_STACK_FRAMES 64
#define TRACER_PID_STRING "TracerPid:"
static int stack_nbr = 0;
static char _program_name[1024];
static void* stack_traces[MAX_STACK_FRAMES];
static char name_buf[4096];
struct dl_iterate_callback_data {
int initialized;
intptr_t start;
} dethrace_dl_data;
static int dl_iterate_callback(struct dl_phdr_info* info, size_t size, void* data) {
struct dl_iterate_callback_data* callback_data = data;
if (strcmp(info
->dlpi_name
, "") == 0) {
callback_data->start = info->dlpi_addr;
}
return 0;
}
static intptr_t get_dethrace_offset(void) {
if (!dethrace_dl_data.initialized) {
dethrace_dl_data.initialized = 1;
dl_iterate_phdr(dl_iterate_callback, &dethrace_dl_data);
}
return dethrace_dl_data.start;
}
// Resolve symbol name and source location given the path to the executable and an address
int addr2line(char const* const program_name, void const* const addr) {
static char addr2line_cmd[MAXPATHLEN]; // Pierre-Marie Baty -- use the heap instead
/* have addr2line map the address to the related line in the code */
sprintf(addr2line_cmd
, "addr2line -f -p -e \"%s\" %p", program_name
, addr
- get_dethrace_offset
()); // Pierre-Marie Baty -- enclosed program_name within quotes to support pathnames with spaces
fprintf(stderr
, "%d: ", stack_nbr
++);
}
static void print_stack_trace(void) {
int i, trace_size = 0;
char** messages = (char**)NULL;
fputs("\nStack trace:\n", stderr
);
trace_size = backtrace(stack_traces, MAX_STACK_FRAMES);
messages = backtrace_symbols(stack_traces, trace_size);
/* skip the first couple stack frames (as they are this function and
our handler) and also skip the last frame as it's (always?) junk. */
for (i = 3; i < (trace_size - 1); ++i) {
if (addr2line(_program_name, stack_traces[i]) != 0) {
printf(" error determining line # for: %s\n", messages
[i
]);
}
}
if (messages) {
}
}
static void signal_handler(int sig, siginfo_t* siginfo, void* context) {
(void)context;
fputs("\n******************\n", stderr
);
switch (sig) {
case SIGSEGV:
fputs("Caught SIGSEGV\n", stderr
);
break;
case SIGINT:
fputs("Caught SIGINT\n", stderr
);
break;
case SIGFPE:
switch (siginfo->si_code) {
case FPE_INTDIV:
fputs("Caught SIGFPE: FPE_INTDIV\n", stderr
);
break;
case FPE_INTOVF:
fputs("Caught SIGFPE: FPE_INTOVF\n", stderr
);
break;
case FPE_FLTDIV:
fputs("Caught SIGFPE: FPE_FLTDIV\n", stderr
);
break;
case FPE_FLTOVF:
fputs("Caught SIGFPE: FPE_FLTOVF\n", stderr
);
break;
case FPE_FLTUND:
fputs("Caught SIGFPE: FPE_FLTUND\n", stderr
);
break;
case FPE_FLTRES:
fputs("Caught SIGFPE: FPE_FLTRES\n", stderr
);
break;
case FPE_FLTINV:
fputs("Caught SIGFPE: FPE_FLTINV\n", stderr
);
break;
case FPE_FLTSUB:
fputs("Caught SIGFPE: FPE_FLTSUB\n", stderr
);
break;
default:
fputs("Caught SIGFPE: Arithmetic Exception\n", stderr
);
break;
}
break;
case SIGILL:
switch (siginfo->si_code) {
case ILL_ILLOPC:
fputs("Caught SIGILL: ILL_ILLOPC\n", stderr
);
break;
case ILL_ILLOPN:
fputs("Caught SIGILL: ILL_ILLOPN\n", stderr
);
break;
case ILL_ILLADR:
fputs("Caught SIGILL: ILL_ILLADR\n", stderr
);
break;
case ILL_ILLTRP:
fputs("Caught SIGILL: ILL_ILLTRP\n", stderr
);
break;
case ILL_PRVOPC:
fputs("Caught SIGILL: ILL_PRVOPC\n", stderr
);
break;
case ILL_PRVREG:
fputs("Caught SIGILL: ILL_PRVREG\n", stderr
);
break;
case ILL_COPROC:
fputs("Caught SIGILL: ILL_COPROC\n", stderr
);
break;
case ILL_BADSTK:
fputs("Caught SIGILL: ILL_BADSTK\n", stderr
);
break;
default:
fputs("Caught SIGILL: Illegal Instruction\n", stderr
);
break;
}
break;
case SIGTERM:
fputs("Caught SIGTERM\n", stderr
);
break;
case SIGABRT:
fputs("Caught SIGABRT\n", stderr
);
break;
default:
break;
}
fputs("******************\n", stderr
);
print_stack_trace();
}
void resolve_full_path(char* path, const char* argv0) {
if (argv0[0] == '/') { // run with absolute path
} else { // run with relative path
if (NULL == getcwd(path, PATH_MAX)) {
return;
}
}
}
void OS_InstallSignalHandler(char* program_name) {
resolve_full_path(_program_name, program_name);
/* setup alternate stack */
{
stack_t ss = {};
ss.
ss_sp = malloc(2 * MINSIGSTKSZ
);
if (ss.ss_sp == NULL) {
err(1, "malloc");
}
ss.ss_size = 2 * MINSIGSTKSZ;
ss.ss_flags = 0;
if (sigaltstack(&ss, NULL) != 0) {
err(1, "sigaltstack");
}
}
/* register our signal handlers */
{
struct sigaction sig_action = {};
sig_action.sa_sigaction = signal_handler;
sigemptyset(&sig_action.sa_mask);
sig_action.sa_flags = SA_SIGINFO | SA_ONSTACK;
if (sigaction(SIGSEGV, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGFPE, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGINT, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGILL, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGTERM, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
if (sigaction(SIGABRT, &sig_action, NULL) != 0) {
err(1, "sigaction");
}
}
}
FILE* OS_fopen(const char* pathname, const char* mode) {
FILE
* f
= fopen(pathname
, mode
);
if (f != NULL) {
return f;
}
char buffer[512];
char buffer2[512];
char* pDirName = dirname(buffer);
char* pBaseName = basename(buffer2);
DIR* pDir = opendir(pDirName);
if (pDir == NULL) {
return NULL;
}
for (struct dirent* pDirent = readdir(pDir); pDirent != NULL; pDirent = readdir(pDir)) {
if (strcasecmp(pBaseName, pDirent->d_name) == 0) {
strcat(pDirName
, pDirent
->d_name
);
f
= fopen(pDirName
, mode
);
break;
}
}
closedir(pDir);
if (harness_game_config.verbose) {
if (f == NULL) {
fprintf(stderr
, "Failed to open \"%s\" (%s)\n", pathname
, strerror(errno
));
}
}
return f;
}
size_t OS_ConsoleReadPassword(char* pBuffer, size_t pBufferLen) {
struct termios old, new;
char c;
size_t len;
tcgetattr(STDIN_FILENO, &old);
new = old;
new.c_lflag &= ~(ICANON | ECHO);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &new);
len = 0;
pBuffer[len] = '\0';
while (1) {
if (read(STDIN_FILENO, &c, 1) == 1) {
if (c == 0x7f) {
if (len > 0) {
len--;
pBuffer[len] = '\0';
continue;
}
} else if (c == '\r' || c == '\n') {
break;
} else if (len < pBufferLen - 1) {
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) {
pBuffer[len] = c;
len++;
pBuffer[len] = '\0';
}
}
}
}
tcsetattr(STDIN_FILENO, TCSANOW, &old);
return len;
}
char* OS_Dirname(const char* path) {
return dirname(name_buf);
}
char* OS_Basename(const char* path) {
return basename(name_buf);
}
#endif // __linux__