libdebugger: Fix corruption of thread registers with 32 or more threads

The block logic uses realloc to resize memory used by libdebugger
when creating a snapshot of threads. Thread registers take a
reference to the block and if a realloc changes the base address
any references are invalid. Add rebase handler support to blocks
that is called when the block base changes and add a handler to
thread register blocks to rebase a thread's registers.

Exclude threads using both types of names they can have. This lets
all names be reported in extra thread info packets.

Closes #5662
This commit is contained in:
Chris Johns
2026-07-27 14:26:09 +00:00
committed by Vijay Banerjee
parent 5c074eb03c
commit e2859cf1c5
6 changed files with 591 additions and 557 deletions
+25 -16
View File
@@ -29,20 +29,20 @@
#include "rtems-debugger-block.h"
int
rtems_debugger_block_create(rtems_debugger_block* block,
size_t step,
size_t size)
{
int r = 0;
int rtems_debugger_block_create(rtems_debugger_block* block, size_t step,
size_t size,
rtems_debugger_block_rebase rebaser) {
block->level = 0;
block->step = step;
block->count = step;
block->size = size;
block->rebaser = rebaser;
block->block = calloc(block->count, block->size);
if (block->block == NULL)
if (block->block == NULL) {
errno = ENOMEM;
return r;
return -1;
}
return 0;
}
int
@@ -56,19 +56,28 @@ rtems_debugger_block_destroy(rtems_debugger_block* block)
return 0;
}
int
rtems_debugger_block_resize(rtems_debugger_block* block)
{
int r = 0;
int rtems_debugger_block_resize(rtems_debugger_block* block) {
if (block->level >= block->count) {
block->count += block->step;
block->block = realloc(block->block, block->count * block->size);
if (block->block == NULL) {
void* new_block = realloc(block->block, block->count * block->size);
if (new_block == NULL) {
block->block = NULL;
block->level = 0;
block->count = 0;
errno = ENOMEM;
r = -1;
return -1;
}
if (block->block != new_block && block->rebaser != NULL) {
block->rebaser(block->block, new_block);
}
block->block = new_block;
}
return r;
return 0;
}
void* rtems_debugger_block_transform(void* previous, void* new, void* addr) {
uintptr_t previous_u = (uintptr_t)previous;
uintptr_t new_u = (uintptr_t)new;
uintptr_t addr_u = (uintptr_t)addr;
return (void*)((addr_u - previous_u) + new_u);
}
+24 -10
View File
@@ -37,26 +37,34 @@
extern "C" {
#endif /* __cplusplus */
/**
* Block resize base change handler. This is called when a block resize
* moves the base address of the block. The handler is called with the
* previous and new values and you can call transform to relocate your
* pointer to the new base.
*/
typedef void (*rtems_debugger_block_rebase)(void* previous, void* new);
/**
* DB server block manages a block of re-sizable memory. The block only
* grows. As more threads enter the system the block becomes the peak and then
* sits at that level.
*/
typedef struct rtems_debugger_block
{
void* block; /**< The block of memory. */
size_t step; /**< The step size the block is increased by. */
size_t size; /**< The size of the elements in the block. */
size_t count; /**< The number of elements in the block. */
size_t level; /**< The usage level in the block. */
typedef struct rtems_debugger_block {
void* block; /**< The block of memory. */
size_t step; /**< The step size the block is increased by. */
size_t size; /**< The size of the elements in the block. */
size_t count; /**< The number of elements in the block. */
size_t level; /**< The usage level in the block. */
rtems_debugger_block_rebase rebaser; /**< Rebase handler if set */
} rtems_debugger_block;
/**
* Create a block.
*/
extern int rtems_debugger_block_create(rtems_debugger_block* block,
size_t step,
size_t size);
extern int rtems_debugger_block_create(rtems_debugger_block* block, size_t step,
size_t size,
rtems_debugger_block_rebase rebaser);
/**
* Destroy a block.
@@ -68,6 +76,12 @@ extern int rtems_debugger_block_destroy(rtems_debugger_block* block);
*/
extern int rtems_debugger_block_resize(rtems_debugger_block* block);
/**
* Transform a pointer to a new base. Using when rebasing.
*/
extern void* rtems_debugger_block_transform(void* previous, void* new,
void* addr);
#ifdef __cplusplus
}
#endif /* __cplusplus */
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -94,9 +94,9 @@ rtems_debugger_target_create(void)
return -1;
}
r = rtems_debugger_block_create(&target->swbreaks,
RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
sizeof(rtems_debugger_target_swbreak));
r = rtems_debugger_block_create(
&target->swbreaks, RTEMS_DEBUGGER_TARGET_SWBREAK_NUM,
sizeof(rtems_debugger_target_swbreak), NULL);
if (r < 0) {
free(target);
return -1;
+58 -25
View File
@@ -59,9 +59,31 @@ rtems_debugger_thread_free(rtems_debugger_threads* threads)
threads->next = 0;
}
int
rtems_debugger_thread_create(void)
{
static void rebase_registers(void* previous, void* new) {
rtems_debugger_threads* threads = rtems_debugger->threads;
rtems_debugger_thread* current;
current = rtems_debugger_thread_current(threads);
if (current != NULL) {
size_t i;
for (i = 0; i < threads->current.level; ++i) {
rtems_debugger_thread* thread = &current[i];
thread->registers =
rtems_debugger_block_transform(previous, new, thread->registers);
}
}
}
static bool rtems_debugger_thread_excluded(const char* name) {
size_t i;
for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
if (strcmp(excludes_defaults[i], name) == 0) {
return true;
}
}
return false;
}
int rtems_debugger_thread_create(void) {
rtems_debugger_threads* threads;
int r;
@@ -75,7 +97,7 @@ rtems_debugger_thread_create(void)
r = rtems_debugger_block_create(&threads->current,
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
sizeof(rtems_debugger_thread));
sizeof(rtems_debugger_thread), NULL);
if (r < 0) {
rtems_debugger_thread_free(threads);
free(threads);
@@ -84,9 +106,9 @@ rtems_debugger_thread_create(void)
return -1;
}
r = rtems_debugger_block_create(&threads->registers,
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
rtems_debugger_target_reg_table_size());
r = rtems_debugger_block_create(
&threads->registers, RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
rtems_debugger_target_reg_table_size(), rebase_registers);
if (r < 0) {
rtems_debugger_thread_free(threads);
free(threads);
@@ -97,18 +119,18 @@ rtems_debugger_thread_create(void)
r = rtems_debugger_block_create(&threads->excludes,
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
sizeof(rtems_id));
sizeof(rtems_id), NULL);
if (r < 0) {
rtems_debugger_thread_free(threads);
free(threads);
rtems_debugger_printf("error: rtems-db: thread: exlcudes alloc: (%d) %s\n",
rtems_debugger_printf("error: rtems-db: thread: excludes alloc: (%d) %s\n",
errno, strerror(errno));
return -1;
}
r = rtems_debugger_block_create(&threads->stopped,
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
sizeof(rtems_id));
sizeof(rtems_id), NULL);
if (r < 0) {
rtems_debugger_thread_free(threads);
free(threads);
@@ -119,7 +141,7 @@ rtems_debugger_thread_create(void)
r = rtems_debugger_block_create(&threads->steppers,
RTEMS_DEBUGGER_THREAD_BLOCK_SIZE,
sizeof(rtems_debugger_thread_stepper));
sizeof(rtems_debugger_thread_stepper), NULL);
if (r < 0) {
rtems_debugger_thread_free(threads);
free(threads);
@@ -162,15 +184,14 @@ rtems_debugger_thread_find_index(rtems_id id)
return r;
}
static bool
snapshot_thread(rtems_tcb* tcb, void* arg)
{
static bool snapshot_thread(rtems_tcb* tcb, void* arg) {
rtems_debugger_threads* threads = rtems_debugger->threads;
rtems_id id = tcb->Object.id;
char name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
bool exclude = false;
size_t i;
int sc;
rtems_id id = tcb->Object.id;
char name[RTEMS_DEBUGGER_THREAD_NAME_SIZE];
bool exclude = false;
int sc;
(void)arg;
/*
* The only time the threads pointer is NULL is a realloc error so we stop
@@ -188,12 +209,24 @@ snapshot_thread(rtems_tcb* tcb, void* arg)
exclude = true;
break;
default:
rtems_object_get_name(id, sizeof(name), (char*) &name[0]);
for (i = 0; i < RTEMS_DEBUGGER_NUMOF(excludes_defaults); ++i) {
if (strcmp(excludes_defaults[i], name) == 0) {
exclude = true;
break;
}
/*
* There are object names that can be a string or the classic 4
* byte name and threads that are based on object can optionally
* hold a name in it's Join_queue that points to the last section
* of the TCB. The API to get an object name has no ability to see
* the thread name extension. The POSIX np calls to set the name
* only set the thread extension name. The score is not uniform and
* as a result the APIs are not aligned.
*
* To entrench this state of affairs LibBSD uses the object name
* and thread name and they are not the same. This makes detecting
* exclusions harder. We need to deal with both names.
*/
rtems_object_get_name(id, sizeof(name), (char*)&name[0]);
exclude = rtems_debugger_thread_excluded(name);
if (!exclude) {
_Thread_Get_name(tcb, &name[0], sizeof(name));
exclude = rtems_debugger_thread_excluded(name);
}
break;
}
+1 -1
View File
@@ -44,7 +44,7 @@ extern "C" {
/**
* Debugger thread name size, fixed size. ASCIIZ format.
*/
#define RTEMS_DEBUGGER_THREAD_NAME_SIZE (5)
#define RTEMS_DEBUGGER_THREAD_NAME_SIZE (16)
/**
* Debugger thread allocation block size.