Bug#226716: libc6: TLS doesn't work with LD_ASSUME_KERNEL < 2.6.0
Package: libc6
Version: 2.3.2.ds1-10
Severity: important
TLS doesn't work properly with LD_ASSUME_KERNEL < 2.6.0. I don't have
a machine with 2.4 kernel currently, so I can't say whether it's only
broken on 2.6 kernels when setting LD_ASSUME_KERNEL or whether it's
broken on older kernels too.
The appended code creates 5 additional threads. Each of these and the
main thread set a thread-local variable to unique numbers. When
execution continues after the barrier, all threads still should see
the value they stored to the thread-local variable.
This works fine on 2.6 kernels without setting LD_ASSUME_KERNEL to
something less than 2.6.0:
,----
| % gcc -Wall -D_GNU_SOURCE -pthread tls.c -o tls
| % ./tls
| BEFORE_BARRIER: primordial thread: tls_var is 0 (0x4000f8fc)
| BEFORE BARRIER: thread 1: test tls_var == 1 (0x40810bcc)
| BEFORE BARRIER: thread 2: test tls_var == 2 (0x41011bcc)
| BEFORE BARRIER: thread 3: test tls_var == 3 (0x41812bcc)
| BEFORE BARRIER: thread 4: test tls_var == 4 (0x42013bcc)
| BEFORE BARRIER: thread 5: test tls_var == 5 (0x42814bcc)
| AFTER BARRIER: thread 1: test tls_var == 1 (0x40810bcc) OK
| AFTER BARRIER: thread 2: test tls_var == 2 (0x41011bcc) OK
| AFTER BARRIER: thread 5: test tls_var == 5 (0x42814bcc) OK
| AFTER BARRIER: thread 3: test tls_var == 3 (0x41812bcc) OK
| AFTER BARRIER: thread 4: test tls_var == 4 (0x42013bcc) OK
| AFTER BARRIER: primordial thread: tls_var is 0 (0x4000f8fc) OK
`----
But if I set LD_ASSUME_KERNEL to something less than 2.6.0, the
variable isn't thread-local at all. After the barrier all threads see
the value used by last thread doing 'tls_var = i'. Also note that the
address of tls_var is the same in all threads:
,----
| % LD_ASSUME_KERNEL=2.4.1 ./tls
| BEFORE_BARRIER: primordial thread: tls_var is 0 (0x400608bc)
| BEFORE BARRIER: thread 1: test tls_var == 1 (0x400608bc)
| BEFORE BARRIER: thread 2: test tls_var == 2 (0x400608bc)
| BEFORE BARRIER: thread 3: test tls_var == 3 (0x400608bc)
| BEFORE BARRIER: thread 4: test tls_var == 4 (0x400608bc)
| BEFORE BARRIER: thread 5: test tls_var == 5 (0x400608bc)
| AFTER BARRIER: thread 1: test tls_var == 5 (0x400608bc) BROKEN
| AFTER BARRIER: thread 2: test tls_var == 5 (0x400608bc) BROKEN
| AFTER BARRIER: thread 3: test tls_var == 5 (0x400608bc) BROKEN
| AFTER BARRIER: thread 5: test tls_var == 5 (0x400608bc) OK
| AFTER BARRIER: thread 4: test tls_var == 5 (0x400608bc) BROKEN
| AFTER BARRIER: primordial thread: tls_var is 5 (0x400608bc) BROKEN
`----
[If this is broken on 2.4 kernels too, then this may explain the
problems some people had with the TLS version of NVIDIA's GL stuff.]
// tls.c
#include <pthread.h>
#include <stdio.h>
#define THREADS 5
pthread_barrier_t barrier;
pthread_t threads[THREADS];
__thread int tls_var;
void *f(void *num)
{
int i = (int) num;
int value_before;
tls_var = i;
value_before = tls_var;
printf("BEFORE BARRIER: thread %d: test tls_var == %d (%p)\n",
i, tls_var, &tls_var);
pthread_barrier_wait(&barrier);
printf("AFTER BARRIER: thread %d: test tls_var == %d (%p) %s\n",
i, tls_var, &tls_var, tls_var == value_before ? "OK" : "BROKEN");
return NULL;
}
int main(int argc, char *argv[])
{
int i;
int value_before;
pthread_barrier_init(&barrier, NULL, THREADS);
tls_var = 0;
value_before = tls_var;
printf("BEFORE_BARRIER: primordial thread: tls_var is %d (%p)\n",
tls_var, &tls_var);
for (i = 0; i < THREADS; i++) {
pthread_create(&threads[i], NULL, f, (void*) i + 1);
}
for (i = 0; i < THREADS; i++) {
pthread_join(threads[i], NULL);
}
pthread_barrier_destroy(&barrier);
printf("AFTER BARRIER: primordial thread: tls_var is %d (%p) %s\n",
tls_var, &tls_var, tls_var == value_before ? "OK" : "BROKEN");
return 0;
}
-- System Information:
Debian Release: testing/unstable
Architecture: i386
Kernel: Linux arthur 2.6.1-rc1-mm2 #1 SMP Mon Jan 5 20:59:14 CET 2004 i686
Locale: LANG=C, LC_CTYPE=C
Versions of packages libc6 depends on:
ii libdb1-compat 2.1.3-7 The Berkeley database routines [gl
-- no debconf information
Reply to: