├── .github └── workflows │ └── main.yml ├── .gitignore ├── Makefile ├── README.md ├── main.c ├── target.c └── tcc_local ├── libtcc.a ├── libtcc.h ├── libtcc1.a └── tcclib.h /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | push: 4 | branches: [ master ] 5 | jobs: 6 | test-independent-run: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v2 10 | - run: cd $GITHUB_WORKSPACE && make 11 | - run: cd $GITHUB_WORKSPACE && timeout --preserve-status -s 2 1 ./main target.c 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | main 2 | main.o 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | 3 | all: main.c target.c 4 | gcc -c main.c 5 | gcc -pthread -o main main.o -L$(ROOT_DIR) -Wl,-Bstatic -l:./tcc_local/libtcc.a -Wl,-Bdynamic -ldl 6 | 7 | clean: 8 | rm main main.o 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a very simple example of **tinycc**'s dynamic compilation feature. 2 | 3 | Your C application can compile another C source file(s). You can play around with this running: 4 | 5 | ``` 6 | make 7 | ./main target.c 8 | ``` 9 | 10 | You should see two strings being printed. Keep the app running and edit strings in the `target.c` 11 | file. The running application should start printing the new strings. 12 | 13 | I'll hopefully update this with checking if the file was modified, instead of running the basic 14 | `while` loop. 15 | 16 | ## Contents of this repo 17 | 18 | This example should be self contained. You should need to install anything for it to work. The files 19 | listed below can be fetched from the **tinycc** build directory. Just clone the **tinycc** repo and 20 | build it. 21 | 22 | - `tcc_local/libtcc1.a` - a runtime dependency necessary for JIT (just-in-time compilation). 23 | - `tcc_local/tcclib.a` - a compile time dependency. This gets linked statically. 24 | - `tcc_local/libtcc.h` - header for the **tinycc** API. 25 | - `tcc_local/tcclib.h` - header runtime `libtcc1.a` runtime dependency. 26 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "tcc_local/libtcc.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | void signal_handler(int sig) 11 | { 12 | exit(EXIT_SUCCESS); 13 | } 14 | 15 | 16 | // Here, a set of functions that the dynamically compiled script has to have get typedefed. 17 | typedef char* (*script_hello)(void); 18 | typedef char* (*script_bye)(void); 19 | 20 | 21 | typedef struct 22 | script_t 23 | { 24 | char* path; 25 | void* program; 26 | time_t time_modified; 27 | 28 | script_hello hello; 29 | script_bye bye; 30 | } script_t; 31 | 32 | 33 | static void 34 | tcc_error(void* opaque, const char* msg) 35 | { 36 | printf("[TCC:ERR] %s\n", msg); 37 | } 38 | 39 | 40 | static int 41 | compile_program(script_t* script) 42 | { 43 | TCCState* tcc = tcc_new(); 44 | if (!tcc) 45 | { 46 | printf("[TCC:ERR] Failed to create tcc context!\n"); 47 | return -1; 48 | } 49 | 50 | tcc_set_lib_path(tcc, "./tcc_local"); 51 | tcc_add_include_path(tcc, "./tcc_local"); 52 | 53 | tcc_set_error_func(tcc, 0x0, tcc_error); 54 | tcc_set_options(tcc, "-g"); 55 | tcc_set_output_type(tcc, TCC_OUTPUT_MEMORY); 56 | 57 | int ret = tcc_add_file(tcc, script->path); 58 | if (ret < 0) 59 | { 60 | printf("[TCC:ERR] Failed to add tcc file!\n"); 61 | tcc_delete(tcc); 62 | return -1; 63 | } 64 | 65 | // tcc_relocate called with NULL returns the size that's necessary for the added files. 66 | script->program = calloc(1, tcc_relocate(tcc, NULL)); 67 | if (!script->program) 68 | { 69 | printf("[TCC:ERR] Failed to allocate memory for the program!\n"); 70 | tcc_delete(tcc); 71 | return -1; 72 | } 73 | 74 | // Copy code to memory passed by the caller. This is where the compilation happens (I think...). 75 | ret = tcc_relocate(tcc, script->program); 76 | if (ret < 0) 77 | { 78 | printf("[TCC:ERR] Failed to allocate memory for the program!\n"); 79 | tcc_delete(tcc); 80 | return -1; 81 | } 82 | 83 | script->hello = (script_hello)tcc_get_symbol(tcc, "target_hello"); 84 | script->bye = (script_bye)tcc_get_symbol(tcc, "target_bye"); 85 | 86 | return 0; 87 | } 88 | 89 | 90 | int 91 | main(int argc, char* argv[]) 92 | { 93 | signal(SIGINT, signal_handler); 94 | 95 | script_t script; 96 | script.path = argv[1]; 97 | 98 | while (1) 99 | { 100 | struct timespec t = {}; 101 | t.tv_sec = 0; 102 | t.tv_nsec = 200000000; 103 | nanosleep(&t, &t); 104 | 105 | compile_program(&script); 106 | printf("%s\n", script.hello()); 107 | printf("%s\n", script.bye()); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /target.c: -------------------------------------------------------------------------------- 1 | char* str_hello = "Hello from the otter side!"; 2 | char* str_bye = "Bye from the otter side!"; 3 | 4 | 5 | char* target_hello(void) 6 | { 7 | return str_hello; 8 | } 9 | 10 | char* target_bye(void) 11 | { 12 | return str_bye; 13 | } 14 | -------------------------------------------------------------------------------- /tcc_local/libtcc.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrOneTwo/tinycc-dynamic-compilation/b1e34c2acf778f13015afc9bbe2b99614c008777/tcc_local/libtcc.a -------------------------------------------------------------------------------- /tcc_local/libtcc.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBTCC_H 2 | #define LIBTCC_H 3 | 4 | #ifndef LIBTCCAPI 5 | # define LIBTCCAPI 6 | #endif 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | struct TCCState; 13 | 14 | typedef struct TCCState TCCState; 15 | 16 | typedef void (*TCCErrorFunc)(void *opaque, const char *msg); 17 | 18 | /* create a new TCC compilation context */ 19 | LIBTCCAPI TCCState *tcc_new(void); 20 | 21 | /* free a TCC compilation context */ 22 | LIBTCCAPI void tcc_delete(TCCState *s); 23 | 24 | /* set CONFIG_TCCDIR at runtime */ 25 | LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path); 26 | 27 | /* set error/warning display callback */ 28 | LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, TCCErrorFunc error_func); 29 | 30 | /* return error/warning callback */ 31 | LIBTCCAPI TCCErrorFunc tcc_get_error_func(TCCState *s); 32 | 33 | /* return error/warning callback opaque pointer */ 34 | LIBTCCAPI void *tcc_get_error_opaque(TCCState *s); 35 | 36 | /* set options as from command line (multiple supported) */ 37 | LIBTCCAPI void tcc_set_options(TCCState *s, const char *str); 38 | 39 | /*****************************/ 40 | /* preprocessor */ 41 | 42 | /* add include path */ 43 | LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname); 44 | 45 | /* add in system include path */ 46 | LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname); 47 | 48 | /* define preprocessor symbol 'sym'. value can be NULL, sym can be "sym=val" */ 49 | LIBTCCAPI void tcc_define_symbol(TCCState *s, const char *sym, const char *value); 50 | 51 | /* undefine preprocess symbol 'sym' */ 52 | LIBTCCAPI void tcc_undefine_symbol(TCCState *s, const char *sym); 53 | 54 | /*****************************/ 55 | /* compiling */ 56 | 57 | /* add a file (C file, dll, object, library, ld script). Return -1 if error. */ 58 | LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename); 59 | 60 | /* compile a string containing a C source. Return -1 if error. */ 61 | LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf); 62 | 63 | /*****************************/ 64 | /* linking commands */ 65 | 66 | /* set output type. MUST BE CALLED before any compilation */ 67 | LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type); 68 | #define TCC_OUTPUT_MEMORY 1 /* output will be run in memory (default) */ 69 | #define TCC_OUTPUT_EXE 2 /* executable file */ 70 | #define TCC_OUTPUT_DLL 3 /* dynamic library */ 71 | #define TCC_OUTPUT_OBJ 4 /* object file */ 72 | #define TCC_OUTPUT_PREPROCESS 5 /* only preprocess (used internally) */ 73 | 74 | /* equivalent to -Lpath option */ 75 | LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname); 76 | 77 | /* the library name is the same as the argument of the '-l' option */ 78 | LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname); 79 | 80 | /* add a symbol to the compiled program */ 81 | LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, const void *val); 82 | 83 | /* output an executable, library or object file. DO NOT call 84 | tcc_relocate() before. */ 85 | LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename); 86 | 87 | /* link and run main() function and return its value. DO NOT call 88 | tcc_relocate() before. */ 89 | LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv); 90 | 91 | /* do all relocations (needed before using tcc_get_symbol()) */ 92 | LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr); 93 | /* possible values for 'ptr': 94 | - TCC_RELOCATE_AUTO : Allocate and manage memory internally 95 | - NULL : return required memory size for the step below 96 | - memory address : copy code to memory passed by the caller 97 | returns -1 if error. */ 98 | #define TCC_RELOCATE_AUTO (void*)1 99 | 100 | /* return symbol value or NULL if not found */ 101 | LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name); 102 | 103 | /* return symbol value or NULL if not found */ 104 | LIBTCCAPI void tcc_list_symbols(TCCState *s, void *ctx, 105 | void (*symbol_cb)(void *ctx, const char *name, const void *val)); 106 | 107 | #ifdef __cplusplus 108 | } 109 | #endif 110 | 111 | #endif 112 | -------------------------------------------------------------------------------- /tcc_local/libtcc1.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrOneTwo/tinycc-dynamic-compilation/b1e34c2acf778f13015afc9bbe2b99614c008777/tcc_local/libtcc1.a -------------------------------------------------------------------------------- /tcc_local/tcclib.h: -------------------------------------------------------------------------------- 1 | /* Simple libc header for TCC 2 | * 3 | * Add any function you want from the libc there. This file is here 4 | * only for your convenience so that you do not need to put the whole 5 | * glibc include files on your floppy disk 6 | */ 7 | #ifndef _TCCLIB_H 8 | #define _TCCLIB_H 9 | 10 | #include 11 | #include 12 | 13 | /* stdlib.h */ 14 | void *calloc(size_t nmemb, size_t size); 15 | void *malloc(size_t size); 16 | void free(void *ptr); 17 | void *realloc(void *ptr, size_t size); 18 | int atoi(const char *nptr); 19 | long int strtol(const char *nptr, char **endptr, int base); 20 | unsigned long int strtoul(const char *nptr, char **endptr, int base); 21 | void exit(int); 22 | 23 | /* stdio.h */ 24 | typedef struct __FILE FILE; 25 | #define EOF (-1) 26 | extern FILE *stdin; 27 | extern FILE *stdout; 28 | extern FILE *stderr; 29 | FILE *fopen(const char *path, const char *mode); 30 | FILE *fdopen(int fildes, const char *mode); 31 | FILE *freopen(const char *path, const char *mode, FILE *stream); 32 | int fclose(FILE *stream); 33 | size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 34 | size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); 35 | int fgetc(FILE *stream); 36 | char *fgets(char *s, int size, FILE *stream); 37 | int getc(FILE *stream); 38 | int getchar(void); 39 | char *gets(char *s); 40 | int ungetc(int c, FILE *stream); 41 | int fflush(FILE *stream); 42 | int putchar (int c); 43 | 44 | int printf(const char *format, ...); 45 | int fprintf(FILE *stream, const char *format, ...); 46 | int sprintf(char *str, const char *format, ...); 47 | int snprintf(char *str, size_t size, const char *format, ...); 48 | int asprintf(char **strp, const char *format, ...); 49 | int dprintf(int fd, const char *format, ...); 50 | int vprintf(const char *format, va_list ap); 51 | int vfprintf(FILE *stream, const char *format, va_list ap); 52 | int vsprintf(char *str, const char *format, va_list ap); 53 | int vsnprintf(char *str, size_t size, const char *format, va_list ap); 54 | int vasprintf(char **strp, const char *format, va_list ap); 55 | int vdprintf(int fd, const char *format, va_list ap); 56 | 57 | void perror(const char *s); 58 | 59 | /* string.h */ 60 | char *strcat(char *dest, const char *src); 61 | char *strchr(const char *s, int c); 62 | char *strrchr(const char *s, int c); 63 | char *strcpy(char *dest, const char *src); 64 | void *memcpy(void *dest, const void *src, size_t n); 65 | void *memmove(void *dest, const void *src, size_t n); 66 | void *memset(void *s, int c, size_t n); 67 | char *strdup(const char *s); 68 | size_t strlen(const char *s); 69 | 70 | /* dlfcn.h */ 71 | #define RTLD_LAZY 0x001 72 | #define RTLD_NOW 0x002 73 | #define RTLD_GLOBAL 0x100 74 | 75 | void *dlopen(const char *filename, int flag); 76 | const char *dlerror(void); 77 | void *dlsym(void *handle, char *symbol); 78 | int dlclose(void *handle); 79 | 80 | #endif /* _TCCLIB_H */ 81 | --------------------------------------------------------------------------------