As far as I know, in gdb there is no command to get a pointer to the data stored through pthread_setspecific() . However, there are several options for obtaining a memory address:
- Examine each thread back trace by checking each frame to see if the result of
pthread_getspecific() on the stack. - Modify the existing code to register both the thread identifier and the result of
pthread_getspecific() . - Find the list of stream related data inside the internal stream data, then use the key to find the entry that will contain the address. This approach depends on the implementation of the pthread library; thus, this requires either knowledge of the pthread implementation, or reverse engineering with little patience.
Below is a demo with a simple program on a 32-bit machine:
- g ++ (GCC) 4.1.2 20080704 (Red Hat 4.1.2-48)
- libpthread-2.5.so
- GNU gdb Red Hat Linux (6.5-25.el5rh)
$cat example.cpp #include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void* the_thread( void* ); void get_position(); struct position_t { int x; int y; }; namespace { pthread_key_t position_key; enum { NUMBER_OF_THREADS = 2 }; } // unnamed int main(int argc, char **argv) { int result = pthread_key_create( &position_key, NULL ); printf( "pthread_key_create -- key: %u, result: %i\n", position_key, result ); pthread_t threads[NUMBER_OF_THREADS]; for (unsigned int i = 0; i < NUMBER_OF_THREADS; ++i ) { // Allocate a position per threads. position_t* position = new position_t(); // Set position values. position->x = ( 1 + i ) * 11; position->y = ( 1 + i ) * 13; // Create the thread. result = pthread_create( &threads[i], NULL, the_thread, position ); } // Give time for threads to enter their forever loop. sleep( 5 ); // Abort. abort(); return 0; } void* the_thread( void* position ) { int result = pthread_setspecific( position_key, position ); printf( "Thread: 0x%.8x, key: %u, value: 0x%.8x, result: %i\n", pthread_self(), position_key, position, result ); get_position(); return 0; } void get_position() { position_t* position = reinterpret_cast< position_t* >( pthread_getspecific( position_key ) ); printf( "Thread: 0x%.8x, key: %u, position: 0x%.8x, x: %i, y: %i\n", pthread_self(), position_key, position, position->x, position->y ); // Wait forever. while( true ) {}; } $ g++ -g -lpthread example.cpp && gdb -q ./a.out Using host libthread_db library "/lib/libthread_db.so.1". (gdb) r Starting program: /tmp/a.out [Thread debugging using libthread_db enabled] [New Thread -1209043248 (LWP 17390)] pthread_key_create -- key: 0, result: 0 [New Thread -1209046128 (LWP 17393)] Thread: 0xb7ef6b90, key: 0, value: 0x09a35008, result: 0 Thread: 0xb7ef6b90, key: 0, position: 0x09a35008, x: 11, y: 13 [New Thread -1219535984 (LWP 17394)] Thread: 0xb74f5b90, key: 0, value: 0x09a350b0, result: 0 Thread: 0xb74f5b90, key: 0, position: 0x09a350b0, x: 22, y: 26 Program received signal SIGABRT, Aborted. [Switching to Thread -1209043248 (LWP 17390)] 0x00377402 in __kernel_vsyscall ()
Using addresses still on the stack:
(gdb) info threads 3 Thread -1219535984 (LWP 17394) get_position () at example.cpp:71 2 Thread -1209046128 (LWP 17393) get_position () at example.cpp:71 * 1 Thread -1209043248 (LWP 17390) 0x00377402 in __kernel_vsyscall () (gdb) thread 3 [Switching to thread 3 (Thread -1219535984 (LWP 17394))]#0 get_position () at example.cpp:71 71 while( true ) {}; (gdb) list get_position 57 58 get_position(); 59 return 0; 60 } 61 62 void get_position() 63 { 64 position_t* position = 65 reinterpret_cast< position_t* >( pthread_getspecific( position_key ) ); 66 (gdb) info locals position = (position_t *) 0x9a350b0 (gdb) p position->x $1 = 22 (gdb) p position->y $2 = 26
Using addresses printed with stdout:
(gdb) p ((position_t*)(0x09a350b0))->x $3 = 22 (gdb) p ((position_t*)(0x09a350b0))->y $4 = 26
Find the list of data related to the stream inside the internal data of the streams:
This approach is much simpler if you have the value key and pthread_t .
I will give details about the implementation of pthread, which I use as necessary:
pthread struct is the thread descriptor structure used internally by pthread.pthread_create() returns pthread_t , a unsigned int , which contains the address of the associated pthread structure.
First find the pthread structure for the thread.
(gdb) info threads * 3 Thread -1219535984 (LWP 17394) get_position () at example.cpp:71 2 Thread -1209046128 (LWP 17393) get_position () at example.cpp:71 1 Thread -1209043248 (LWP 17390) 0x00377402 in __kernel_vsyscall () (gdb) thread 1 [Switching to thread 1 (Thread -1209043248 (LWP 17390))]
Ignoring many fields, the pthread structure pthread as follows:
struct pthread { ... pid_t tid;
pthread_key_data.seq : contains a sequence number that should be pretty low and match __pthread_keys[key].seq .pthread_key_data.data : contains the value specified in pthread_setspecific()pthread.specific_1stblock is a block that is used to store data that depends on the thread before trying to dynamically allocate more blocks.pthread is a two-level array for thread related data. Index 0 will contain the memory address pthread.specific_1stblock .PTHREAD_KEY_2NDLEVEL_SIZE has a size of 32.
The definition gives a good idea of ββwhat to look for in memory:
- An integer with the value of the memory address
pthread ( tid ), followed by an integer with the process identifier ( pid ). This is useful to indicate whether the memory in pthread structure. cancelhandling and flags are flags. Specific values ββare not important. These fields can be useful because their values ββare potentially noticeable from other fields, such as those that contain memory addresses or counters.specific_1stblock is an array of size 32. If the pthread structure was initialized to zero, then you need to repeat 0 for 62 ~ words, because the sample code has only one position_key data stream that is two words in size.specific - an array containing memory addresses. If the pthread structure was initialized to zero, then you need to repeat 0 s, but the first value should be the memory address specific_1stblock .
Print out the pthread :
(gdb) p/x *((int*)threads[1])@150 $6 = {0xb74f5b90, 0x9a350c8, 0xb74f5b90, 0x1, 0x377400, 0x7fb99100, 0xcb40329e, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb7ef6bd0, 0x96b118, 0x43f2, 0x43ee, 0xb74f5be0, 0xffffffec, 0x0, 0x0, 0xb74f5470, 0x0, 0x1, 0x9a350b0, 0x0 <repeats 62 times>, 0xb74f5bf8, 0x0 <repeats 31 times>, 0x1000101, 0x0, 0x0, 0x0, 0xc2342345, 0xe0286, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80486ca, 0x9a350b0, 0x0 <repeats 13 times>, 0xb6af5000, 0xa01000}
By analyzing patterns in memory, some words become good candidates for specific pthread fields:
0xb74f5b90, 0x9a350c8, 0xb74f5b90 (pthread.tid), 0x1, 0x377400 (pthread.pid) ... 0x1, 0x9a350b0, 0x0 <repeats 62 times> (pthread.specific_1stblock) ... 0xb74f5bf8, 0x0 <repeats 31 times> (pthread.specific)
You can perform several sensitivity checks, for example, if pthread.specific[0] contains the address pthread.specific_1stblock :
(gdb) p/x *((int*)0xb74f5bf8)@64 $7 = {0x1, 0x9a350b0, 0x0 <repeats 62 times>}
Now that pthread.specific been identified, get its memory address by counting the word offset from &pthread . In this case, it is 90:
(gdb) set $specific=(int*)threads[1] + 90
Calculate the first and second indexes using position_key :
Find pthread_key_data for position_key :
(gdb) set $level2=(int*)*($specific + $index1) (gdb) p/x *($level2 + (2*$index2))@2 $8 = {0x1, 0x9a350b0}
Thus:
pthread_key_data.seq = 1
pthread_key_data.data = 0x9a350b0
The first word is seq , which must match pthread_key_struct[position_key].seq . Due to raw memory __pthread_keys , __pthread_keys will be added to int* , and pointer arithmetic must occur to calculate the size of pthread_key_struct :
(gdb) p *(&((int*)&__pthread_keys)[2*position_key])@2 $9 = {1, 0}
Thus:
pthread_key_struct[position_key].seq = 1
pthread_key_struct[position_key].destr = NULL
The seq number matches, so everything looks good. pthread_key_data.data contains the value to be returned from pthread_getspecific( position_key ) .
(gdb) set $position=(position_t*)0x9a350b0 (gdb) p $position->x $10 = 22 (gdb) p $position->y $11 = 26
Technically, you can still find thread-specific data without knowing the key and pthread_t values:
If a destructor function was provided for pthread_key_create() , then its memory address may be inside the __pthread_keys array. Examine the memory and calculate the offset and divide by sizeof pthread_key_struct . This should result in an index, which is also the key:
void* destr_fn( void* ); pthread_key_create( key, destr_fn ) __pthread_keys[key].destr == destr_fn
If pthread_t not known, it may exist in the thread stack register. This may require examining various memory addresses in an attempt to find a section in memory containing the pthread structure.
(gdb) info thread 3 Thread -1219535984 (LWP 17394) get_position () at example.cpp:71 2 Thread -1209046128 (LWP 17393) get_position () at example.cpp:71 * 1 Thread -1209043248 (LWP 17390) 0x00377402 in __kernel_vsyscall () (gdb) thread 3 [Switching to thread 3 (Thread -1219535984 (LWP 17394))]
In this case, the edi register contains the address of the pthread structure.
Links: descr.h , pthread_key_create.c , pthread_setspecific.c , pthreadP.h , internaltypes.h