Skip to main content
After compilation and linking, you can either run the code in memory or generate an output file.

Memory relocation

tcc_relocate

Performs all relocations needed to run the compiled code in memory. This must be called before using tcc_get_symbol().
int tcc_relocate(TCCState *s);
s
TCCState*
required
The compilation context.
return
int
Returns 0 on success, negative value on error.
Example:
if (tcc_relocate(s) < 0) {
    fprintf(stderr, "Relocation failed\n");
    return 1;
}

// Now you can get symbols
void (*func)(void) = tcc_get_symbol(s, "my_function");
Do not call tcc_relocate() if you’re using tcc_output_file() or tcc_run(). Those functions handle relocation internally.

Running code directly

tcc_run

Compiles, links, and runs the main() function directly. Do not call tcc_relocate() before this.
int tcc_run(TCCState *s, int argc, char **argv);
s
TCCState*
required
The compilation context.
argc
int
required
Number of command-line arguments to pass to main().
argv
char**
required
Array of command-line arguments.
return
int
Returns the value returned by the compiled main() function.
Example:
TCCState *s = tcc_new();
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);

const char *code = 
    "#include <stdio.h>\n"
    "int main() {\n"
    "    printf(\"Hello from compiled code!\\n\");\n"
    "    return 0;\n"
    "}\n";

tcc_compile_string(s, code);

// Run main() with no arguments
int exit_code = tcc_run(s, 0, NULL);

printf("Program exited with code: %d\n", exit_code);
tcc_delete(s);

Getting compiled symbols

tcc_get_symbol

Returns a pointer to a symbol (function or variable) from the compiled code. You must call tcc_relocate() first.
void *tcc_get_symbol(TCCState *s, const char *name);
s
TCCState*
required
The compilation context.
name
const char*
required
Name of the symbol to retrieve.
return
void*
Pointer to the symbol, or NULL if not found.
Example:
TCCState *s = tcc_new();
tcc_set_output_type(s, TCC_OUTPUT_MEMORY);

const char *code = 
    "int add(int a, int b) { return a + b; }\n"
    "int multiply(int a, int b) { return a * b; }\n";

tcc_compile_string(s, code);
tcc_relocate(s);

// Get function pointers
int (*add_func)(int, int) = tcc_get_symbol(s, "add");
int (*mul_func)(int, int) = tcc_get_symbol(s, "multiply");

if (!add_func || !mul_func) {
    fprintf(stderr, "Failed to get symbols\n");
    return 1;
}

// Call the functions
printf("%d + %d = %d\n", 5, 3, add_func(5, 3));
printf("%d * %d = %d\n", 5, 3, mul_func(5, 3));

tcc_delete(s);

tcc_list_symbols

Lists all global symbols and their values via a callback function.
void tcc_list_symbols(TCCState *s, void *ctx,
    void (*symbol_cb)(void *ctx, const char *name, const void *val));
s
TCCState*
required
The compilation context.
ctx
void*
User data pointer passed to the callback.
symbol_cb
void (*)(void*, const char*, const void*)
required
Callback function called for each symbol.
Example:
void print_symbol(void *ctx, const char *name, const void *val) {
    printf("Symbol: %s at %p\n", name, val);
}

const char *code = 
    "int var1 = 10;\n"
    "int var2 = 20;\n"
    "void func1(void) {}\n"
    "void func2(void) {}\n";

tcc_compile_string(s, code);
tcc_relocate(s);

// List all symbols
tcc_list_symbols(s, NULL, print_symbol);

// Output:
// Symbol: var1 at 0x...
// Symbol: var2 at 0x...
// Symbol: func1 at 0x...
// Symbol: func2 at 0x...

Advanced execution patterns

Plugin system

// Plugin interface
typedef struct {
    const char* name;
    void (*init)(void);
    void (*execute)(void);
    void (*cleanup)(void);
} Plugin;

Plugin* load_plugin(const char *source_code) {
    TCCState *s = tcc_new();
    tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
    
    // Compile plugin
    if (tcc_compile_string(s, source_code) < 0) {
        tcc_delete(s);
        return NULL;
    }
    
    if (tcc_relocate(s) < 0) {
        tcc_delete(s);
        return NULL;
    }
    
    // Get plugin interface
    Plugin *plugin = tcc_get_symbol(s, "plugin");
    if (!plugin) {
        tcc_delete(s);
        return NULL;
    }
    
    return plugin;
}

// Use plugin
Plugin *p = load_plugin(
    "#include <stdio.h>\n"
    "void my_init() { printf(\"Init\\n\"); }\n"
    "void my_exec() { printf(\"Execute\\n\"); }\n"
    "void my_cleanup() { printf(\"Cleanup\\n\"); }\n"
    "Plugin plugin = {\n"
    "    .name = \"MyPlugin\",\n"
    "    .init = my_init,\n"
    "    .execute = my_exec,\n"
    "    .cleanup = my_cleanup\n"
    "};\n");

if (p) {
    p->init();
    p->execute();
    p->cleanup();
}

Expression evaluator

double eval_expression(const char *expr) {
    TCCState *s = tcc_new();
    tcc_set_output_type(s, TCC_OUTPUT_MEMORY);
    
    char code[1024];
    snprintf(code, sizeof(code),
        "#include <math.h>\n"
        "double evaluate() {\n"
        "    return %s;\n"
        "}\n", expr);
    
    if (tcc_compile_string(s, code) < 0) {
        tcc_delete(s);
        return 0.0;
    }
    
    tcc_add_library(s, "m");
    
    if (tcc_relocate(s) < 0) {
        tcc_delete(s);
        return 0.0;
    }
    
    double (*evaluate)(void) = tcc_get_symbol(s, "evaluate");
    double result = evaluate();
    
    tcc_delete(s);
    return result;
}

// Usage
printf("2 + 2 = %f\n", eval_expression("2 + 2"));
printf("sqrt(16) = %f\n", eval_expression("sqrt(16)"));
printf("sin(3.14159/2) = %f\n", eval_expression("sin(3.14159/2)"));

Hot code reloading

typedef struct {
    TCCState *state;
    void (*update)(float dt);
    void (*render)(void);
} GameModule;

GameModule* reload_module(GameModule *old, const char *source) {
    // Clean up old module
    if (old) {
        tcc_delete(old->state);
        free(old);
    }
    
    // Create new module
    GameModule *module = malloc(sizeof(GameModule));
    module->state = tcc_new();
    tcc_set_output_type(module->state, TCC_OUTPUT_MEMORY);
    
    if (tcc_compile_string(module->state, source) < 0) {
        free(module);
        return NULL;
    }
    
    if (tcc_relocate(module->state) < 0) {
        tcc_delete(module->state);
        free(module);
        return NULL;
    }
    
    module->update = tcc_get_symbol(module->state, "update");
    module->render = tcc_get_symbol(module->state, "render");
    
    return module;
}

// Main game loop
GameModule *game = NULL;
while (running) {
    // Check if source file changed
    if (source_file_modified()) {
        char *new_source = load_source_file("game.c");
        GameModule *new_game = reload_module(game, new_source);
        if (new_game) {
            game = new_game;
            printf("Hot reload successful!\n");
        }
        free(new_source);
    }
    
    if (game) {
        game->update(delta_time);
        game->render();
    }
}

Error handling

void *get_symbol_safe(TCCState *s, const char *name) {
    void *sym = tcc_get_symbol(s, name);
    if (!sym) {
        fprintf(stderr, "Error: Symbol '%s' not found\n", name);
        fprintf(stderr, "Available symbols:\n");
        tcc_list_symbols(s, NULL, print_symbol);
    }
    return sym;
}

// Usage
int (*func)(int) = get_symbol_safe(s, "my_function");
if (!func) {
    // Handle error
    return 1;
}

Build docs developers (and LLMs) love