Насколько я знаю, в gdb нет команды для получения указателя на данные, хранящиеся через pthread_setspecific()
. Однако есть несколько вариантов получения адреса памяти:
- Изучите обратную трассировку каждого потока, проверяя каждый кадр, чтобы убедиться, что результат
pthread_getspecific()
все еще находится в стеке.
- Измените существующий код, чтобы регистрировать как идентификатор потока, так и результат
pthread_getspecific()
.
- Найдите список данных конкретного потока во внутренних данных потока, затем используйте ключ, чтобы найти запись, которая будет содержать адрес. Этот подход зависит от реализации библиотеки pthread; таким образом, требуется либо знание используемой реализации pthread, либо реверс-инжиниринг с небольшим количеством терпения.
Ниже приведена демонстрация простой программы на 32-битной машине:
- 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 ()
Использование адресов, все еще находящихся в стеке:
(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
Использование адресов, напечатанных из стандартного вывода:
(gdb) p ((position_t*)(0x09a350b0))->x
$3 = 22
(gdb) p ((position_t*)(0x09a350b0))->y
$4 = 26
Найдите список данных, специфичных для потока, во внутренних данных потока:
Этот подход намного проще, если у вас есть значения key
и pthread_t
.
Я буду вводить подробности о реализации pthread, которую я использую, по мере необходимости:
pthread
struct — это структура дескриптора потока, используемая внутри pthread.
pthread_create()
возвращает pthread_t
, unsigned int
, который содержит адрес связанной структуры pthread
.
Сначала найдите структуру pthread
для потока.
(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))]#0 0x00377402 in
__kernel_vsyscall ()
(gdb) bt
#0 0x00377402 in __kernel_vsyscall ()
#1 0x0080ec10 in raise () from /lib/libc.so.6
#2 0x00810521 in abort () from /lib/libc.so.6
#3 0x0804880f in main () at example.cpp:47
(gdb) frame 3
#3 0x0804880f in main () at example.cpp:47
47 abort();
(gdb) info locals
result = 0
threads = {3085921168, 3075431312}
(gdb) p/x threads[1]
$5 = 0xb74f5b90
Игнорируя многие поля, определение структуры pthread
выглядит так:
struct pthread
{
...
pid_t tid; // Thread ID (i.e. this thread descriptor).
pid_t pid; // Process ID.
...
struct pthread_key_data
{
uintptr_t seq;
void *data;
} specific_1stblock[PTHREAD_KEY_2NDLEVEL_SIZE];
struct pthread_key_data *specific[PTHREAD_KEY_1STLEVEL_SIZE];
...
};
pthread_key_data.seq
: содержит порядковый номер, который должен быть довольно низким и соответствовать __pthread_keys[key].seq
.
pthread_key_data.data
: содержит значение, предоставленное pthread_setspecific()
pthread.specific_1stblock
— это блок, который используется для хранения данных, специфичных для потока, перед попыткой динамического выделения дополнительных блоков.
pthread
— это двухуровневый массив данных, специфичных для потока. Индекс 0
будет содержать адрес памяти pthread.specific_1stblock
.
PTHREAD_KEY_2NDLEVEL_SIZE
имеет размер 32.
Определение дает довольно хорошее представление о том, что искать в памяти:
- Целое число со значением адреса памяти
pthread
(tid
), за которым следует целое число с идентификатором процесса (pid
). Это полезно, чтобы указать, является ли исследуемая память структурой pthread
.
cancelhandling
и flags
— это флаги. Конкретные значения не важны. Эти поля могут быть полезны, потому что их значения потенциально визуально отличимы от других полей, таких как поля, содержащие адреса памяти или счетчики.
specific_1stblock
— это массив размером 32. Если структура pthread
была инициализирована нулями, то должны повторяться 0
s для 62~ слов, потому что в примере кода есть только одни данные для конкретного потока position_key
размером в два слова.
specific
— это массив, содержащий адреса памяти. Если структура pthread
была инициализирована нулями, то должны повторяться 0
s, но первым значением должен быть адрес памяти specific_1stblock
.
Распечатайте фрагмент памяти 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}
При анализе шаблонов в памяти некоторые слова становятся хорошими кандидатами для определенных полей pthread:
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)
Можно выполнить некоторые упрощенные проверки работоспособности, например, проверить, содержит ли pthread.specific[0]
адрес pthread.specific_1stblock
:
(gdb) p/x *((int*)0xb74f5bf8)@64
$7 = {0x1, 0x9a350b0, 0x0 <repeats 62 times>} ## matches specific_1stblock
Теперь, когда pthread.specific
идентифицировано, получите его адрес памяти, посчитав смещение слова от &pthread
. В данном случае это 90:
(gdb) set $specific=(int*)threads[1] + 90
Вычислить первый и второй индекс через position_key
:
Найдите pthread_key_data
для position_key
:
(gdb) set $level2=(int*)*($specific + $index1)
(gdb) p/x *($level2 + (2*$index2))@2
$8 = {0x1, 0x9a350b0}
Таким образом:
pthread_key_data.seq = 1
pthread_key_data.data = 0x9a350b0
Первое слово — seq
, которое должно соответствовать pthread_key_struct[position_key].seq
. Из-за работы с необработанной памятью __pthread_keys
будет преобразовано в int*
, и для учета размера pthread_key_struct
придется выполнять арифметические действия с указателями:
(gdb) p *(&((int*)&__pthread_keys)[2*position_key])@2
$9 = {1, 0}
Таким образом:
pthread_key_struct[position_key].seq = 1
pthread_key_struct[position_key].destr = NULL
Цифры seq
совпадают, так что все выглядит хорошо. pthread_key_data.data
содержит значение, которое будет возвращено из pthread_getspecific( position_key )
.
(gdb) set $position=(position_t*)0x9a350b0
(gdb) p $position->x
$10 = 22
(gdb) p $position->y
$11 = 26
Технически все еще возможно найти данные, относящиеся к потоку, не зная значений key
и pthread_t
:
Если для pthread_key_create()
была предоставлена функция деструктора, то ее адрес памяти может находиться в массиве __pthread_keys
. Изучите память, вычислите смещение и разделите на размер pthread_key_struct
. Это должно привести к индексу, который также является ключом:
void* destr_fn( void* );
pthread_key_create( key, destr_fn )
__pthread_keys[key].destr == destr_fn
Если pthread_t
неизвестен, он может существовать в регистре стека потока. Это может потребовать проверки различных адресов памяти, пытаясь найти раздел в памяти, который содержит структуру pthread
.
(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))]#0 g
get_position () at example.cpp:71
71 while( true ) {};
(gdb) bt
#0 get_position () at example.cpp:71
#1 0x0804871d in the_thread (position=0x9a350b0) at example.cpp:58
#2 0x0095c43b in start_thread () from /lib/libpthread.so.0
#3 0x008b3fde in clone () from /lib/libc.so.6
(gdb) frame 2
#2 0x0095c43b in start_thread () from /lib/libpthread.so.0
(gdb) info register
eax 0x3f 63
ecx 0xb74f52ac -1219538260
edx 0x0 0
ebx 0x96aff4 9875444
esp 0xb74f53c0 0xb74f53c0
ebp 0xb74f54a8 0xb74f54a8
esi 0x0 0
edi 0xb74f5b90 -1219535984
eip 0x95c43b 0x95c43b <start_thread+203>
eflags 0x200286 [ PF SF IF ID ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
В этом случае регистр edi
содержит адрес структуры pthread
.
Ссылки: descr.h, pthread_key_create.c, pthread_setspecific.c, pthreadP.h, internaltypes.h
person
Tanner Sansbury
schedule
02.06.2012