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.
#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/4tls_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/4tls_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/4tls_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/4tls_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));
}