Hello devs!

(I will start out by tagging @grischka, who seems to primarily have written
the code in question) -

Standard long time lurker, first time poster. I want to start up by
thanking you all for maintaining this great project I've been using for
many years now, amazing work!

So I noticed when using libtcc on Windows x64 as a jit engine for emitting
code to memory, that neither structured exceptions nor longjmp/setjmp works
across tcc compiled code. For those that do not know, for runtime code
generation Windows, on x64, requires you to register all functions such
that the OS can walk and unwind the stack when longjmp'ing or throwing
exceptions (whether these are system exceptions like division by zero,
access violation, signals or C++ exceptions).

I can see TCC contains the code required for it to work, namely:
*tccpe.c*:
*pe_add_uwwind_info()*

*pe_add_unwind_data()*
*tccrun.c**:*

*win64_add_function_table()*

*win64_del_function_table()*

However, there's a bug somewhere. When you relocate the code,
*win64_add_function_table
*is called to register all the generated function tables. This calls
*RtlAddFunctionTable*(table, ...) whose first argument is an array of
function entries. TCC passes *s1->uw_pdata->sh_addr *as the function array,
but this memory location is just full of 0xCDCDCDCD (on windows, this is
the signature of uninitialized memory).

So there's something fishy about how *pe_add_unwind_data *stores this
table. I noticed this for loop:

    /* record this function */
    p->BeginAddress = start;
    p->EndAddress = end;
    p->UnwindData = d;

    /* put relocations on it */
    for (n = o + sizeof *p; o < n; o += sizeof p->BeginAddress)
        put_elf_reloc(symtab_section, pd, o,  R_X86_64_RELATIVE,
s1->uw_sym);

I'm new to actually messing around with TCC code, so I won't claim to know
exactly what is going on. But it seems weird that it wants to relocate the
runtime function table, as it is just an array of offsets relative to the
relocated image.

Here's a minimal test I threw together (compile with msvc x64):

typedef int(*Callback)(int arg);
typedef int(*CallbackCaller)(Callback, int);

int callback(int arg)
{
return 10 / arg;
}

void test()
{
const char * program = "int callback(int (* cb)(int), int arg) { return
cb(arg); }";

TCCState * tcc = tcc_new();
tcc_set_lib_path(tcc, /* ... */);
tcc_set_output_type(tcc, TCC_OUTPUT_MEMORY);
tcc_compile_string(tcc, program);
tcc_relocate(tcc, TCC_RELOCATE_AUTO);

CallbackCaller cbi = (CallbackCaller) tcc_get_symbol(tcc, "callback");

cbi(callback, 1);

int exception_caught = 0;

__try
{
cbi(callback, 0);
}
__except(1)
{
exception_caught = 1;
}

assert(exception_caught == 1);
}

*Also, if I directly pass the 'p' pointer above from
**pe_add_unwind_data** into
**RtlAddFunctionTable **(by hacking some stuff), everything works as it
should and the test passes.*

So I wouldn't mind fixing this, but if anyone (especially grischka) could
shed some basic light upon the coding decisions and/or give some pointers
(or maybe knows exactly what is wrong), I would be thankful.

Regards, Janus
_______________________________________________
Tinycc-devel mailing list
Tinycc-devel@nongnu.org
https://lists.nongnu.org/mailman/listinfo/tinycc-devel

Reply via email to