/* * JoeyDev * Copyright (C) 2018-2023 Scott Duensing * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. */ #include "common.h" #include "compiler.h" #include "libtcc.h" #include "sigsegv.h" #include "project.h" #include "messages.h" #include "utils.h" #include "array.h" #if HAVE_SIGSEGV_RECOVERY #include #include #if defined _WIN32 && !defined __CYGWIN__ // Windows doesn't have sigset_t. typedef int sigset_t; #define sigemptyset(set) #define sigprocmask(how,set,oldset) #endif volatile int runPass = 0; jmp_buf runRecipe; sigset_t runSigSet; static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3); int sigHandler(void *faultAddress, int serious); #endif char **___recipeTargets = NULL; static void compilerErrorHandler(void *opaque, const char *msg); void recipeAddTarget(char *target); void recipeMessage(char *format, ...); static void compilerErrorHandler(void *opaque, const char *msg) { char *isWarning = strstr(msg, " warning: "); (void)opaque; message(isWarning == NULL ? MSG_ERROR : MSG_WARN, "%s", msg); } // All the paths passed in here are expected to be complete and absolute. int compilerRunRecipe(char *recipe, char *input, char *outputPath, void *context) { TCCState *s; char *oldLocation; char c; int x; int result = -255; int (*entry)(char *, char *); ___recipeTargets = NULL; s = tcc_new(); if (!s) { // Something bad happened. return -1; } // __resourcePath comes in with a trailing slash. Temporarily remove it. x = (int)strlen(__resourcePath) - 1; c = __resourcePath[x]; __resourcePath[x] = 0; tcc_set_lib_path(s, __resourcePath); tcc_add_sysinclude_path(s, __resourcePath); __resourcePath[x] = c; tcc_set_options(s, "-Wall -Wno-write-strings"); tcc_set_error_func(s, stdout, compilerErrorHandler); tcc_set_output_type(s, TCC_OUTPUT_MEMORY); if (tcc_add_file(s, recipe) < 0) { // Errors in code. tcc_delete(s); return -4; } tcc_add_symbol(s, "fputc", fputc); tcc_add_symbol(s, "qsort", qsort); tcc_add_symbol(s, "___recipeTargets", ___recipeTargets); tcc_add_symbol(s, "recipeAddTarget", recipeAddTarget); tcc_add_symbol(s, "recipeMessage", recipeMessage); tcc_add_symbol(s, "utilCreateString", utilCreateString); if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) { // Something bad happened. tcc_delete(s); return -2; } entry = tcc_get_symbol(s, "recipe"); if (!entry) { // Something bad happened. tcc_delete(s); return -3; } getcwd(__utilFilenameBuffer, FILENAME_MAX); oldLocation = strdup(__utilFilenameBuffer); chdir(outputPath); #if HAVE_SIGSEGV_RECOVERY sigset_t emptySet; runPass = 0; if (sigsegv_install_handler(&sigHandler) < 0) return -5; sigemptyset(&emptySet); sigprocmask(SIG_BLOCK, &emptySet, &runSigSet); switch (setjmp(runRecipe)) { case 0: result = entry(input, outputPath); case 1: sigprocmask(SIG_SETMASK, &runSigSet, NULL); sigsegv_install_handler(NULL); if (runPass != 0) { message(MSG_SEVERE, "%s caused a segmentation fault!", recipe); } break; } #else result = entry(input, outputPath); #endif chdir(oldLocation); DEL(oldLocation); while (arrlen(___recipeTargets) > 0) { if (projectAddToTree(context, ___recipeTargets[0])) { utilSetDirty((WindowDataT *)context, TRUE); } DEL(___recipeTargets[0]); arrdel(___recipeTargets, 0); } ARRFREE(___recipeTargets); tcc_delete(s); return result; } #if HAVE_SIGSEGV_RECOVERY static void sigHandlerContinuation(void *arg1, void *arg2, void *arg3) { (void)arg1; (void)arg2; (void)arg3; longjmp(runRecipe, runPass); } int sigHandler(void *faultAddress, int serious) { (void)faultAddress; (void)serious; runPass++; return sigsegv_leave_handler(sigHandlerContinuation, NULL, NULL, NULL); } #endif void recipeAddTarget(char *target) { arrput(___recipeTargets, strdup(target)); } void recipeMessage(char *format, ...) { va_list args; char *string; va_start(args, format); string = utilCreateStringVArgs(format, args); va_end(args); message(MSG_INFO, "%s", string); DEL(string); }