tinycc-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Tinycc-devel] Win64 PE JIT codegen: Bad unwind info / runtime function


From: Janus Lynggaard Thorborg
Subject: [Tinycc-devel] Win64 PE JIT codegen: Bad unwind info / runtime function tables
Date: Thu, 2 Nov 2017 01:45:09 +0100

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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]