Skip to content

Commit 146930a

Browse files
committed
libdrgn: replace arch frame_registers with callbacks
We currently unwind from pt_regs and NT_PRSTATUS using an array of register definitions. It's more flexible and more efficient to do this with an architecture-specific callback. For x86-64, this change also makes us depend on the binary layout rather than member names of struct pt_regs, but that shouldn't matter unless people are defining their own, weird struct pt_regs.
1 parent 4d8597f commit 146930a

File tree

3 files changed

+99
-140
lines changed

3 files changed

+99
-140
lines changed

libdrgn/arch_x86_64.c.in

Lines changed: 72 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -107,25 +107,76 @@ bnd2
107107
bnd3
108108
%%
109109

110-
static const struct drgn_frame_register frame_registers_x86_64[] = {
111-
{ DRGN_REGISTER_X86_64_rax, 8, 192, "ax", "rax", },
112-
{ DRGN_REGISTER_X86_64_rdx, 8, 208, "dx", "rdx", },
113-
{ DRGN_REGISTER_X86_64_rcx, 8, 200, "cx", "rcx", },
114-
{ DRGN_REGISTER_X86_64_rbx, 8, 152, "bx", "rbx", },
115-
{ DRGN_REGISTER_X86_64_rsi, 8, 216, "si", "rsi", },
116-
{ DRGN_REGISTER_X86_64_rdi, 8, 224, "di", "rdi", },
117-
{ DRGN_REGISTER_X86_64_rbp, 8, 144, "bp", "rbp", },
118-
{ DRGN_REGISTER_X86_64_rsp, 8, 264, "sp", "rsp", },
119-
{ DRGN_REGISTER_X86_64_r8, 8, 184, "r8", },
120-
{ DRGN_REGISTER_X86_64_r9, 8, 176, "r9", },
121-
{ DRGN_REGISTER_X86_64_r10, 8, 168, "r10", },
122-
{ DRGN_REGISTER_X86_64_r11, 8, 160, "r11", },
123-
{ DRGN_REGISTER_X86_64_r12, 8, 136, "r12", },
124-
{ DRGN_REGISTER_X86_64_r13, 8, 128, "r13", },
125-
{ DRGN_REGISTER_X86_64_r14, 8, 120, "r14", },
126-
{ DRGN_REGISTER_X86_64_r15, 8, 112, "r15", },
127-
{ DRGN_REGISTER_X86_64_rip, 8, 240, "ip", "rip", },
128-
};
110+
/*
111+
* The in-kernel struct pt_regs, UAPI struct pt_regs, elf_gregset_t, and struct
112+
* user_regs_struct all have the same layout.
113+
*/
114+
static struct drgn_error *
115+
set_initial_registers_from_struct_x86_64(Dwfl_Thread *thread, const void *regs,
116+
size_t size, bool bswap)
117+
{
118+
Dwarf_Word dwarf_regs[17];
119+
120+
if (size < 160) {
121+
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
122+
"registers are truncated");
123+
}
124+
125+
#define READ_REGISTER(n) ({ \
126+
uint64_t reg; \
127+
memcpy(&reg, (uint64_t *)regs + n, sizeof(reg)); \
128+
bswap ? bswap_64(reg) : reg; \
129+
})
130+
dwarf_regs[0] = READ_REGISTER(10); /* rax */
131+
dwarf_regs[1] = READ_REGISTER(12); /* rdx */
132+
dwarf_regs[2] = READ_REGISTER(11); /* rcx */
133+
dwarf_regs[3] = READ_REGISTER(5); /* rbx */
134+
dwarf_regs[4] = READ_REGISTER(13); /* rsi */
135+
dwarf_regs[5] = READ_REGISTER(14); /* rdi */
136+
dwarf_regs[6] = READ_REGISTER(4); /* rbp */
137+
dwarf_regs[7] = READ_REGISTER(19); /* rsp */
138+
dwarf_regs[8] = READ_REGISTER(9); /* r8 */
139+
dwarf_regs[9] = READ_REGISTER(8); /* r9 */
140+
dwarf_regs[10] = READ_REGISTER(7); /* r10 */
141+
dwarf_regs[11] = READ_REGISTER(6); /* r11 */
142+
dwarf_regs[12] = READ_REGISTER(3); /* r12 */
143+
dwarf_regs[13] = READ_REGISTER(2); /* r13 */
144+
dwarf_regs[14] = READ_REGISTER(1); /* r14 */
145+
dwarf_regs[15] = READ_REGISTER(0); /* r15 */
146+
dwarf_regs[16] = READ_REGISTER(16); /* rip */
147+
#undef READ_REGISTER
148+
149+
if (!dwfl_thread_state_registers(thread, 0, 17, dwarf_regs))
150+
return drgn_error_libdwfl();
151+
return NULL;
152+
}
153+
154+
static struct drgn_error *
155+
pt_regs_set_initial_registers_x86_64(Dwfl_Thread *thread,
156+
const struct drgn_object *obj)
157+
{
158+
bool bswap = (obj->value.little_endian !=
159+
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__));
160+
return set_initial_registers_from_struct_x86_64(thread,
161+
drgn_object_buffer(obj),
162+
drgn_buffer_object_size(obj),
163+
bswap);
164+
}
165+
166+
static struct drgn_error *
167+
prstatus_set_initial_registers_x86_64(struct drgn_program *prog,
168+
Dwfl_Thread *thread, const void *prstatus,
169+
size_t size)
170+
{
171+
if (size < 112) {
172+
return drgn_error_create(DRGN_ERROR_INVALID_ARGUMENT,
173+
"NT_PRSTATUS is truncated");
174+
}
175+
return set_initial_registers_from_struct_x86_64(thread,
176+
(char *)prstatus + 112,
177+
size - 112,
178+
drgn_program_bswap(prog));
179+
}
129180

130181
static inline struct drgn_error *read_register(struct drgn_object *reg_obj,
131182
struct drgn_object *frame_obj,
@@ -485,8 +536,8 @@ const struct drgn_architecture_info arch_info_x86_64 = {
485536
ARCHITECTURE_INFO,
486537
.default_flags = (DRGN_PLATFORM_IS_64_BIT |
487538
DRGN_PLATFORM_IS_LITTLE_ENDIAN),
488-
.frame_registers = frame_registers_x86_64,
489-
.num_frame_registers = ARRAY_SIZE(frame_registers_x86_64),
539+
.pt_regs_set_initial_registers = pt_regs_set_initial_registers_x86_64,
540+
.prstatus_set_initial_registers = prstatus_set_initial_registers_x86_64,
490541
.linux_kernel_set_initial_registers =
491542
linux_kernel_set_initial_registers_x86_64,
492543
.linux_kernel_get_page_offset = linux_kernel_get_page_offset_x86_64,

libdrgn/platform.h

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,6 @@ struct drgn_register {
1313
enum drgn_register_number number;
1414
};
1515

16-
/* Register in NT_PRSTATUS note or struct pt_regs used for stack unwinding. */
17-
struct drgn_frame_register {
18-
enum drgn_register_number number;
19-
size_t size;
20-
size_t prstatus_offset;
21-
/* Name used in the kernel. */
22-
const char *pt_regs_name;
23-
/* Name used for the UAPI, if different from above. */
24-
const char *pt_regs_name2;
25-
};
26-
2716
/* Page table iterator. */
2817
struct pgtable_iterator {
2918
struct drgn_program *prog;
@@ -69,8 +58,13 @@ struct drgn_architecture_info {
6958
const struct drgn_register *registers;
7059
size_t num_registers;
7160
const struct drgn_register *(*register_by_name)(const char *name);
72-
const struct drgn_frame_register *frame_registers;
73-
size_t num_frame_registers;
61+
/* Given pt_regs as a value buffer object. */
62+
struct drgn_error *(*pt_regs_set_initial_registers)(Dwfl_Thread *,
63+
const struct drgn_object *);
64+
struct drgn_error *(*prstatus_set_initial_registers)(struct drgn_program *,
65+
Dwfl_Thread *,
66+
const void *,
67+
size_t);
7468
struct drgn_error *(*linux_kernel_set_initial_registers)(Dwfl_Thread *,
7569
const struct drgn_object *);
7670
struct drgn_error *(*linux_kernel_get_page_offset)(struct drgn_program *,

libdrgn/stack_trace.c

Lines changed: 20 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -241,51 +241,6 @@ drgn_get_stack_trace_obj(struct drgn_object *res, struct drgn_program *prog,
241241
", struct task_struct *" : "");
242242
}
243243

244-
static struct drgn_error *
245-
drgn_pt_regs_set_initial_registers(Dwfl_Thread *thread,
246-
struct drgn_platform *platform,
247-
const struct drgn_object *pt_regs)
248-
{
249-
struct drgn_error *err;
250-
struct drgn_object obj;
251-
size_t i;
252-
253-
drgn_object_init(&obj, pt_regs->prog);
254-
255-
if (!platform->arch->num_frame_registers) {
256-
return drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
257-
"pt_regs stack unwinding is not supported for %s architecture",
258-
platform->arch->name);
259-
}
260-
261-
for (i = 0; i < platform->arch->num_frame_registers; i++) {
262-
const struct drgn_frame_register *reg;
263-
union drgn_value value;
264-
Dwarf_Word word;
265-
266-
reg = &platform->arch->frame_registers[i];
267-
err = drgn_object_member(&obj, pt_regs, reg->pt_regs_name);
268-
if (err && err->code == DRGN_ERROR_LOOKUP &&
269-
reg->pt_regs_name2) {
270-
drgn_error_destroy(err);
271-
err = drgn_object_member(&obj, pt_regs,
272-
reg->pt_regs_name2);
273-
}
274-
if (err)
275-
goto out;
276-
err = drgn_object_read_integer(&obj, &value);
277-
if (err)
278-
goto out;
279-
word = value.uvalue;
280-
if (!dwfl_thread_state_registers(thread, reg->number, 1, &word))
281-
return drgn_error_libdwfl();
282-
}
283-
err = NULL;
284-
out:
285-
drgn_object_deinit(&obj);
286-
return err;
287-
}
288-
289244
static struct drgn_error *
290245
drgn_get_task_pid(const struct drgn_object *task, uint32_t *ret)
291246
{
@@ -306,60 +261,6 @@ drgn_get_task_pid(const struct drgn_object *task, uint32_t *ret)
306261
return err;
307262
}
308263

309-
static struct drgn_error *
310-
drgn_prstatus_set_initial_registers(Dwfl_Thread *thread,
311-
struct drgn_platform *platform,
312-
struct string *prstatus)
313-
{
314-
bool bswap = (!!(platform->flags & DRGN_PLATFORM_IS_LITTLE_ENDIAN) !=
315-
(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__));
316-
size_t i;
317-
318-
if (!platform->arch->num_frame_registers) {
319-
return drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
320-
"core dump stack unwinding is not supported for %s architecture",
321-
platform->arch->name);
322-
}
323-
324-
for (i = 0; i < platform->arch->num_frame_registers; i++) {
325-
const struct drgn_frame_register *reg;
326-
const char *p;
327-
Dwarf_Word word;
328-
329-
reg = &platform->arch->frame_registers[i];
330-
if (prstatus->len < reg->prstatus_offset + reg->size) {
331-
return drgn_error_create(DRGN_ERROR_OTHER,
332-
"NT_PRSTATUS is truncated");
333-
}
334-
p = prstatus->str + reg->prstatus_offset;
335-
switch (reg->size) {
336-
case 4: {
337-
uint32_t tmp;
338-
339-
memcpy(&tmp, p, sizeof(tmp));
340-
if (bswap)
341-
tmp = bswap_32(tmp);
342-
word = tmp;
343-
break;
344-
}
345-
case 8: {
346-
uint64_t tmp;
347-
348-
memcpy(&tmp, p, sizeof(tmp));
349-
if (bswap)
350-
tmp = bswap_64(tmp);
351-
word = tmp;
352-
break;
353-
}
354-
default:
355-
UNREACHABLE();
356-
}
357-
if (!dwfl_thread_state_registers(thread, reg->number, 1, &word))
358-
return drgn_error_libdwfl();
359-
}
360-
return NULL;
361-
}
362-
363264
static bool drgn_thread_set_initial_registers(Dwfl_Thread *thread,
364265
void *thread_arg)
365266
{
@@ -380,10 +281,16 @@ static bool drgn_thread_set_initial_registers(Dwfl_Thread *thread,
380281
goto out;
381282

382283
if (is_pt_regs) {
383-
assert(obj.kind == DRGN_OBJECT_BUFFER);
384-
err = drgn_pt_regs_set_initial_registers(thread,
385-
&prog->platform,
386-
&obj);
284+
assert(obj.kind == DRGN_OBJECT_BUFFER &&
285+
!obj.is_reference);
286+
if (!prog->platform.arch->pt_regs_set_initial_registers) {
287+
err = drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
288+
"pt_regs stack unwinding is not supported for %s architecture",
289+
prog->platform.arch->name);
290+
goto out;
291+
}
292+
err = prog->platform.arch->pt_regs_set_initial_registers(thread,
293+
&obj);
387294
goto out;
388295
}
389296
}
@@ -408,9 +315,16 @@ static bool drgn_thread_set_initial_registers(Dwfl_Thread *thread,
408315
if (err)
409316
goto out;
410317
if (prstatus.str) {
411-
err = drgn_prstatus_set_initial_registers(thread,
412-
&prog->platform,
413-
&prstatus);
318+
if (!prog->platform.arch->prstatus_set_initial_registers) {
319+
err = drgn_error_format(DRGN_ERROR_INVALID_ARGUMENT,
320+
"core dump stack unwinding is not supported for %s architecture",
321+
prog->platform.arch->name);
322+
goto out;
323+
}
324+
err = prog->platform.arch->prstatus_set_initial_registers(prog,
325+
thread,
326+
prstatus.str,
327+
prstatus.len);
414328
goto out;
415329
}
416330
}

0 commit comments

Comments
 (0)