Fixture 140

thread local storage

C · 4 functions · 4 lanes · 2 of 16 function-lanes behave identically

4 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (0/4), gcc-O0 (0/4), clang-O2 (1/4), gcc-O2 (1/4).

Thread-local storage uses an addressing mode nothing else does: the object is reached through a thread pointer (%fs on x86-64) plus a link-time TLS offset, or through a __tls_get_addr call under the general-dynamic model. A recovery that treats it as an ordinary global is wrong in a way no other fixture can detect, because the address differs per thread.

tests/decompiler_fixtures/src/140_thread_local_storage.c source
#include <stdint.h>

/* Thread-local storage uses an addressing mode nothing else does: the object is
 * reached through a thread pointer (%fs on x86-64) plus a link-time TLS offset,
 * or through a __tls_get_addr call under the general-dynamic model. A recovery
 * that treats it as an ordinary global is wrong in a way no other fixture can
 * detect, because the address differs per thread. */

static __thread int32_t tls_counter = 100;
static __thread int32_t tls_table[4] = {1, 2, 3, 4};
static int32_t global_counter = 100;

__attribute__((noinline)) int32_t tls_increment(int32_t amount) {
    tls_counter += amount;
    return tls_counter;
}

__attribute__((noinline)) int32_t tls_versus_global(int32_t amount) {
    /* Deliberately adjacent: the two loads must not be conflated. */
    tls_counter += amount;
    global_counter += amount;
    return tls_counter - global_counter;
}

__attribute__((noinline)) int32_t tls_indexed(int32_t index, int32_t value) {
    if (index < 0 || index > 3) {
        return -1;
    }
    tls_table[index] += value;
    return tls_table[index];
}

__attribute__((noinline)) int32_t tls_address_is_stable(void) {
    /* Two evaluations in one thread must denote the same object. */
    int32_t *first = &tls_counter;
    int32_t *second = &tls_counter;
    return (first == second) ? 1 : 0;
}

Recovered C

Generated by glaurung decompile --style decbench at b47f6b43. baseline.json records the result after recompiling the C and calling it beside the original with seeded inputs.

clang -O0

0/4
tls_address_is_stable fail 13 lines
// glaurung: tls_address_is_stable @ 0x1230
int32_t tls_address_is_stable(void) {
    extern long __tls_get_addr(long);
    int * first;
    int * second;
    long var0;
    // x86-64 prologue: save rbp, frame 16 bytes
    var0 = __tls_get_addr(0x3fd0);
    first = (int *)(var0);
    second = (int *)(var0);
    // x86-64 epilogue: restore rbp
    return ((first == second) ? 1 : 0);
}
tls_increment fail 10 lines
// glaurung: tls_increment @ 0x1110
int32_t tls_increment(int32_t arg0) {
    extern long __tls_get_addr(long);
    long var1;
    // x86-64 prologue: save rbp, frame 16 bytes
    var1 = __tls_get_addr(0x3fd0);
    *(int *)((var1)) = ((unsigned long)((unsigned int)(*(int *)((var1)))) + (unsigned long)((unsigned int)(arg0)));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)((var1)));
}
tls_indexed fail 24 lines
// glaurung: tls_indexed @ 0x11b0
int32_t tls_indexed(int32_t arg0, int32_t arg1) {
    extern long __tls_get_addr(long);
    long local_18;
    int local_4;
    long var2;
    // x86-64 prologue: save rbp, frame 32 bytes
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 3) | ((long)(arg0) < 3)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    local_18 = arg0;
    var2 = __tls_get_addr(0x3fd0);
    *(int *)((var2 + local_18 * 4 + 0x10)) = ((unsigned long)((unsigned int)(*(int *)((var2 + local_18 * 4 + 0x10)))) + (unsigned long)((unsigned int)(arg1)));
    local_4 = *(int *)(((var2 + ((long)(arg0) * 4)) + 16));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
tls_versus_global fail 13 lines
// glaurung: tls_versus_global @ 0x1150
static unsigned char glaurung_global_4028[16] __attribute__((aligned(16)));
int32_t tls_versus_global(int32_t arg0) {
    extern long __tls_get_addr(long);
    extern unsigned char glaurung_global_4028[16];
    long var1;
    // x86-64 prologue: save rbp, frame 16 bytes
    var1 = __tls_get_addr(0x3fd0);
    *(int *)((var1)) = ((unsigned long)((unsigned int)(*(int *)((var1)))) + (unsigned long)((unsigned int)(arg0)));
    *(int *)(&glaurung_global_4028[0]) = ((unsigned long)((unsigned int)(*(int *)(&glaurung_global_4028[0]))) + (unsigned long)((unsigned int)(arg0)));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(*(int *)((var1)))) - *(int *)(&glaurung_global_4028[0])));
}

gcc -O0

0/4
tls_address_is_stable fail 15 lines
// glaurung: tls_address_is_stable @ 0x1253
int32_t tls_address_is_stable(void) {
    extern long __tls_get_addr(long);
    int * first;
    int * second;
    long var0;
    long var2;
    // x86-64 prologue: save rbp, frame 16 bytes
    var0 = __tls_get_addr(0x3fc0);
    first = (int *)(var0);
    var2 = __tls_get_addr(0x3fc0);
    second = (int *)(var2);
    // x86-64 epilogue: restore rbp
    return (first == second);
}
tls_increment fail 16 lines
// glaurung: tls_increment @ 0x1119
int32_t tls_increment(int32_t arg0) {
    extern long __tls_get_addr(long);
    long var0;
    long var4;
    long var5;
    long var7;
    // x86-64 prologue: save rbp, frame 8 bytes
    var0 = __tls_get_addr(0x3fc0);
    var4 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)((var0)))) + (unsigned long)((unsigned int)(arg0)))));
    var5 = __tls_get_addr(0x3fc0);
    *(int *)((var5)) = var4;
    var7 = __tls_get_addr(0x3fc0);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)((var7)));
}
tls_indexed fail 24 lines
// glaurung: tls_indexed @ 0x11d6
int32_t tls_indexed(int32_t arg0, int32_t arg1) {
    extern long __tls_get_addr(long);
    long var1;
    long var12;
    long var7;
    long var8;
    // x86-64 prologue: save rbp, frame 8 bytes
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 3) | ((long)(arg0) < 3)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    var1 = __tls_get_addr(0x3fd0);
    var7 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)((var1 + ((long)(arg0) * 4))))) + (unsigned long)((unsigned int)(arg1)))));
    var8 = __tls_get_addr(0x3fd0);
    *(int *)((var8 + ((long)(arg0) * 4))) = var7;
    var12 = __tls_get_addr(0x3fd0);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)((var12 + ((long)(arg0) * 4))));
}
tls_versus_global fail 19 lines
// glaurung: tls_versus_global @ 0x116b
static unsigned char glaurung_global_4028[16] __attribute__((aligned(16)));
int32_t tls_versus_global(int32_t arg0) {
    extern long __tls_get_addr(long);
    extern unsigned char glaurung_global_4028[16];
    long var0;
    long var11;
    long var4;
    long var5;
    // x86-64 prologue: save rbp, frame 8 bytes
    var0 = __tls_get_addr(0x3fc0);
    var4 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)((var0)))) + (unsigned long)((unsigned int)(arg0)))));
    var5 = __tls_get_addr(0x3fc0);
    *(int *)((var5)) = var4;
    *(int *)(&glaurung_global_4028[0]) = ((unsigned long)((unsigned int)(arg0)) + (unsigned long)((unsigned int)(*(int *)(&glaurung_global_4028[0]))));
    var11 = __tls_get_addr(0x3fc0);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(*(int *)((var11)))) - (unsigned long)((unsigned int)(*(int *)(&glaurung_global_4028[0])))));
}

clang -O2

1/4
tls_address_is_stable pass 4 lines
// glaurung: tls_address_is_stable @ 0x11a0
int32_t tls_address_is_stable(void) {
    return 1;
}
tls_increment fail 13 lines
// glaurung: tls_increment @ 0x1110
int32_t tls_increment(int32_t arg0) {
    extern long __tls_get_addr(long);
    long var0;
    long var1;
    int var3;
    var0 = (unsigned long)((unsigned int)(arg0));
    var1 = __tls_get_addr(0x3fd0);
    var3 = (var0 + *(int *)((var1)));
    *(int *)((var1)) = var3;
    // x86-64 epilogue: tear down frame
    return (unsigned int)(var3);
}
tls_indexed fail 25 lines
// glaurung: tls_indexed @ 0x1160
__attribute__((no_stack_protector)) int32_t tls_indexed(int32_t arg0, int32_t arg1) {
    extern long __tls_get_addr(long);
    unsigned char local_18[24];
    long ret;
    long var0;
    long var1;
    long var2;
    long var3;
    long var4;
    int var6;
    ret = 0xffffffff;
    if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(3))) {
        *(long *)((&local_18[0] + 16)) = var0;
        *(long *)((&local_18[0] + 8)) = var1;
        *(long *)(&local_18[0]) = ret;
        var2 = (unsigned long)((unsigned int)(arg1));
        var3 = (unsigned long)((unsigned int)(arg0));
        var4 = __tls_get_addr(0x3fd0);
        var6 = (var2 + *(int *)((var4 + var3 * 4 + 0x10)));
        *(int *)((var4 + var3 * 4 + 0x10)) = var6;
        ret = (unsigned long)((unsigned int)(var6));
    }
    return ret;
}
tls_versus_global fail 18 lines
// glaurung: tls_versus_global @ 0x1130
static unsigned char glaurung_global_4028[16] __attribute__((aligned(16)));
int32_t tls_versus_global(int32_t arg0) {
    extern long __tls_get_addr(long);
    extern unsigned char glaurung_global_4028[16];
    long var0;
    long var1;
    int var4;
    int var6;
    var0 = (unsigned long)((unsigned int)(arg0));
    var1 = __tls_get_addr(0x3fd0);
    var4 = ((unsigned int)(*(int *)((var1))) + var0);
    *(int *)((var1)) = var4;
    var6 = (var0 + *(int *)(&glaurung_global_4028[0]));
    *(int *)(&glaurung_global_4028[0]) = var6;
    // x86-64 epilogue: tear down frame
    return (unsigned int)(((unsigned long)((unsigned int)(var4)) - (unsigned long)((unsigned int)(var6))));
}

gcc -O2

1/4
tls_address_is_stable pass 4 lines
// glaurung: tls_address_is_stable @ 0x11e0
int32_t tls_address_is_stable(void) {
    return 1;
}
tls_increment fail 13 lines
// glaurung: tls_increment @ 0x1120
int32_t tls_increment(int32_t arg0) {
    extern long __tls_get_addr(long);
    long var0;
    long var1;
    int var5;
    var0 = (unsigned long)((unsigned int)(arg0));
    var1 = __tls_get_addr(0x3fd0);
    var5 = ((unsigned int)(*(int *)((var1 + 0x10))) + var0);
    *(int *)((var1 + 0x10)) = var5;
    // x86-64 epilogue: tear down frame
    return (unsigned int)(var5);
}
tls_indexed fail 18 lines
// glaurung: tls_indexed @ 0x1190
int32_t tls_indexed(int32_t arg0, int32_t arg1) {
    extern long __tls_get_addr(long);
    long var0;
    long var1;
    long var2;
    int var6;
    var0 = (long)(arg0);
    if (((unsigned long)(3) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    var1 = (unsigned long)((unsigned int)(arg1));
    var2 = __tls_get_addr(0x3fd0);
    var6 = ((unsigned int)(*(int *)((var2 + var0 * 4))) + var1);
    *(int *)((var2 + var0 * 4)) = var6;
    // x86-64 epilogue: tear down frame
    return (unsigned int)(var6);
}
tls_versus_global fail 18 lines
// glaurung: tls_versus_global @ 0x1150
static unsigned char glaurung_global_4028[16] __attribute__((aligned(16)));
int32_t tls_versus_global(int32_t arg0) {
    extern long __tls_get_addr(long);
    extern unsigned char glaurung_global_4028[16];
    long var0;
    long var1;
    long var4;
    long var6;
    var0 = (unsigned long)((unsigned int)(arg0));
    var1 = __tls_get_addr(0x3fd0);
    var4 = (unsigned long)((unsigned int)(*(int *)((var1 + 0x10))));
    *(int *)((var1 + 0x10)) = (var4 + var0);
    var6 = (unsigned long)((unsigned int)(*(int *)(&glaurung_global_4028[0])));
    *(int *)(&glaurung_global_4028[0]) = (var0 + var6);
    // x86-64 epilogue: tear down frame
    return (unsigned int)((var4 - var6));
}

← 213 fixtures