Add the MD parts of dtrace needed to use fbt on ARM. For this we need to
emulate the instructions used in function entry and exit. For function entry ARM will use a push instruction to push up to 16 registers to the stack. While we don't expect all 16 to be used we need to handle any combination the compiler may generate, even if it doesn't make sense (e.g. pushing the program counter). On function return we will either have a pop or branch instruction. The former is similar to the push instruction, but with care to make sure we update the stack pointer and program counter correctly in the cases they are either in the list of registers or not. For branch we need to take the 24-bit offset, sign-extend it, and add that number of 4-byte words to the program counter. Care needs to be taken as, due to historical reasons, the address the branch is relative to is not the current instruction, but 8 bytes later. This allows us to use the following probes on ARM boards: dtrace -n 'fbt::malloc:entry { stack() }' and dtrace -n 'fbt:🆓return { stack() }' Differential Revision: https://reviews.freebsd.org/D2007 Reviewed by: gnn, rpaulo Sponsored by: ABT Systems Ltd
This commit is contained in:
parent
2eaea119b8
commit
4a8169d97b
@ -66,7 +66,7 @@ db_stack_trace_cmd(struct unwind_state *state)
|
||||
|
||||
finished = false;
|
||||
while (!finished) {
|
||||
finished = unwind_stack_one(state, 0);
|
||||
finished = unwind_stack_one(state, 1);
|
||||
|
||||
/* Print the frame details */
|
||||
sym = db_search_symbol(state->start_pc, DB_STGY_ANY, &offset);
|
||||
|
@ -57,11 +57,6 @@ __FBSDID("$FreeBSD$");
|
||||
#ifdef KDTRACE_HOOKS
|
||||
.bss
|
||||
.align 4
|
||||
.global _C_LABEL(dtrace_invop_jump_addr)
|
||||
_C_LABEL(dtrace_invop_jump_addr):
|
||||
.word 0
|
||||
.word 0
|
||||
|
||||
.global _C_LABEL(dtrace_invop_calltrap_addr)
|
||||
_C_LABEL(dtrace_invop_calltrap_addr):
|
||||
.word 0
|
||||
@ -162,7 +157,8 @@ _C_LABEL(dtrace_invop_calltrap_addr):
|
||||
msr cpsr_c, r2; /* Punch into SVC mode */ \
|
||||
mov r2, sp; /* Save SVC sp */ \
|
||||
bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \
|
||||
sub sp, sp, #4; /* Pad trapframe to keep alignment */ \
|
||||
sub sp, sp, #(4 * 17); /* Pad trapframe to keep alignment */ \
|
||||
/* and for dtrace to emulate push/pop */ \
|
||||
str r0, [sp, #-4]!; /* Push return address */ \
|
||||
str lr, [sp, #-4]!; /* Push SVC lr */ \
|
||||
str r2, [sp, #-4]!; /* Push SVC sp */ \
|
||||
@ -199,7 +195,8 @@ _C_LABEL(dtrace_invop_calltrap_addr):
|
||||
msr cpsr_c, r2; /* Punch into SVC mode */ \
|
||||
mov r2, sp; /* Save SVC sp */ \
|
||||
bic sp, sp, #7; /* Align sp to an 8-byte addrress */ \
|
||||
sub sp, sp, #4; /* Pad trapframe to keep alignment */ \
|
||||
sub sp, sp, #(4 * 17); /* Pad trapframe to keep alignment */ \
|
||||
/* and for dtrace to emulate push/pop */ \
|
||||
str r0, [sp, #-4]!; /* Push return address */ \
|
||||
str lr, [sp, #-4]!; /* Push SVC lr */ \
|
||||
str r2, [sp, #-4]!; /* Push SVC sp */ \
|
||||
|
@ -86,6 +86,10 @@ __FBSDID("$FreeBSD$");
|
||||
#include <machine/db_machdep.h>
|
||||
#endif
|
||||
|
||||
#ifdef KDTRACE_HOOKS
|
||||
int (*dtrace_invop_jump_addr)(struct trapframe *);
|
||||
#endif
|
||||
|
||||
static int gdb_trapper(u_int, u_int, struct trapframe *, int);
|
||||
|
||||
LIST_HEAD(, undefined_handler) undefined_handlers[MAX_COPROCS];
|
||||
@ -286,7 +290,14 @@ undefinedinstruction(struct trapframe *frame)
|
||||
printf("No debugger in kernel.\n");
|
||||
#endif
|
||||
return;
|
||||
} else
|
||||
}
|
||||
#ifdef KDTRACE_HOOKS
|
||||
else if (dtrace_invop_jump_addr != 0) {
|
||||
dtrace_invop_jump_addr(frame);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
panic("Undefined instruction in kernel.\n");
|
||||
}
|
||||
|
||||
|
@ -2436,6 +2436,10 @@ extern void dtrace_helpers_destroy(proc_t *);
|
||||
|
||||
#elif defined(__arm__)
|
||||
|
||||
#define DTRACE_INVOP_SHIFT 4
|
||||
#define DTRACE_INVOP_MASK ((1 << DTRACE_INVOP_SHIFT) - 1)
|
||||
#define DTRACE_INVOP_DATA(x) ((x) >> DTRACE_INVOP_SHIFT)
|
||||
|
||||
#define DTRACE_INVOP_PUSHM 1
|
||||
#define DTRACE_INVOP_POPM 2
|
||||
#define DTRACE_INVOP_B 3
|
||||
|
@ -46,7 +46,11 @@ __FBSDID("$FreeBSD$");
|
||||
#include <vm/pmap.h>
|
||||
|
||||
#define DELAYBRANCH(x) ((int)(x) < 0)
|
||||
|
||||
|
||||
#define BIT_PC 15
|
||||
#define BIT_LR 14
|
||||
#define BIT_SP 13
|
||||
|
||||
extern uintptr_t dtrace_in_probe_addr;
|
||||
extern int dtrace_in_probe;
|
||||
extern dtrace_id_t dtrace_probeid_error;
|
||||
@ -231,16 +235,97 @@ dtrace_probe_error(dtrace_state_t *state, dtrace_epid_t epid, int which,
|
||||
static int
|
||||
dtrace_invop_start(struct trapframe *frame)
|
||||
{
|
||||
printf("IMPLEMENT ME: %s\n", __func__);
|
||||
switch (dtrace_invop(frame->tf_pc, (uintptr_t *)frame, frame->tf_pc)) {
|
||||
register_t *r0, *sp;
|
||||
int data, invop, reg, update_sp;
|
||||
|
||||
invop = dtrace_invop(frame->tf_pc, (uintptr_t *)frame, frame->tf_pc);
|
||||
switch (invop & DTRACE_INVOP_MASK) {
|
||||
case DTRACE_INVOP_PUSHM:
|
||||
// TODO:
|
||||
sp = (register_t *)frame->tf_svc_sp;
|
||||
r0 = &frame->tf_r0;
|
||||
data = DTRACE_INVOP_DATA(invop);
|
||||
|
||||
/*
|
||||
* Store the pc, lr, and sp. These have their own
|
||||
* entries in the struct.
|
||||
*/
|
||||
if (data & (1 << BIT_PC)) {
|
||||
sp--;
|
||||
*sp = frame->tf_pc;
|
||||
}
|
||||
if (data & (1 << BIT_LR)) {
|
||||
sp--;
|
||||
*sp = frame->tf_svc_lr;
|
||||
}
|
||||
if (data & (1 << BIT_SP)) {
|
||||
sp--;
|
||||
*sp = frame->tf_svc_sp;
|
||||
}
|
||||
|
||||
/* Store the general registers */
|
||||
for (reg = 12; reg >= 0; reg--) {
|
||||
if (data & (1 << reg)) {
|
||||
sp--;
|
||||
*sp = r0[reg];
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the stack pointer and program counter to continue */
|
||||
frame->tf_svc_sp = (register_t)sp;
|
||||
frame->tf_pc += 4;
|
||||
break;
|
||||
case DTRACE_INVOP_POPM:
|
||||
// TODO:
|
||||
sp = (register_t *)frame->tf_svc_sp;
|
||||
r0 = &frame->tf_r0;
|
||||
data = DTRACE_INVOP_DATA(invop);
|
||||
|
||||
/* Read the general registers */
|
||||
for (reg = 0; reg <= 12; reg++) {
|
||||
if (data & (1 << reg)) {
|
||||
r0[reg] = *sp;
|
||||
sp++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the stack pointer. If we don't update it here we will
|
||||
* need to update it at the end as the instruction would do
|
||||
*/
|
||||
update_sp = 1;
|
||||
if (data & (1 << BIT_SP)) {
|
||||
frame->tf_svc_sp = *sp;
|
||||
*sp++;
|
||||
update_sp = 0;
|
||||
}
|
||||
|
||||
/* Update the link register, we need to use the correct copy */
|
||||
if (data & (1 << BIT_LR)) {
|
||||
frame->tf_svc_lr = *sp;
|
||||
*sp++;
|
||||
}
|
||||
/*
|
||||
* And the program counter. If it's not in the list skip over
|
||||
* it when we return so to not hit this again.
|
||||
*/
|
||||
if (data & (1 << BIT_PC)) {
|
||||
frame->tf_pc = *sp;
|
||||
*sp++;
|
||||
} else
|
||||
frame->tf_pc += 4;
|
||||
|
||||
/* Update the stack pointer if we haven't already done so */
|
||||
if (update_sp)
|
||||
frame->tf_svc_sp = (register_t)sp;
|
||||
break;
|
||||
case DTRACE_INVOP_B:
|
||||
// TODO
|
||||
data = DTRACE_INVOP_DATA(invop) & 0x00ffffff;
|
||||
/* Sign extend the data */
|
||||
if ((data & (1 << 23)) != 0)
|
||||
data |= 0xff000000;
|
||||
/* The data is the number of 4-byte words to change the pc */
|
||||
data *= 4;
|
||||
data += 8;
|
||||
frame->tf_pc += data;
|
||||
break;
|
||||
default:
|
||||
return (-1);
|
||||
|
@ -38,7 +38,7 @@
|
||||
|
||||
#include "fbt.h"
|
||||
|
||||
#define FBT_PATCHVAL 0xe06a0cfe /* illegal instruction */
|
||||
#define FBT_PATCHVAL 0xe7f000f0 /* Specified undefined instruction */
|
||||
|
||||
#define FBT_PUSHM 0xe92d0000
|
||||
#define FBT_POPM 0xe8bd0000
|
||||
@ -66,7 +66,7 @@ fbt_invop(uintptr_t addr, uintptr_t *stack, uintptr_t rval)
|
||||
|
||||
cpu->cpu_dtrace_caller = 0;
|
||||
|
||||
return (fbt->fbtp_rval);
|
||||
return (fbt->fbtp_rval | (fbt->fbtp_savedval << DTRACE_INVOP_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user