[PATCH] Add stack unwind support for the functions in .ko
Howard Su
howard0su at gmail.com
Sat Dec 7 12:16:37 UTC 2013
I need this function when working on dtrace/arm support. the basic idea is
locate the ARM.EXIDX section by the symbol __exidx_start and __exidx_stop.
To keep it simple, I always look through the link_file_list when unwind the
stack. From the testing, the performance seems ok.
Also, I implement the count parameter when unwind the stack which can make
'show threads' output more readable. I also disabled some print output by
#if 0 which makes output more align with the other platform.
Please review.
Thanks,
Howard Su
diff --git a/sys/arm/arm/db_trace.c b/sys/arm/arm/db_trace.c
index 57119da..9a4728d 100644
--- a/sys/arm/arm/db_trace.c
+++ b/sys/arm/arm/db_trace.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/proc.h>
#include <sys/kdb.h>
#include <sys/stack.h>
+#include <sys/linker.h>
#include <machine/armreg.h>
#include <machine/asm.h>
#include <machine/cpufunc.h>
@@ -79,12 +80,6 @@ __FBSDID("$FreeBSD$");
#define PC 15
/*
- * These are set in the linker script. Their addresses will be
- * either the start or end of the exception table or index.
- */
-extern int extab_start, extab_end, exidx_start, exidx_end;
-
-/*
* Entry types.
* These are the only entry types that have been seen in the kernel.
*/
@@ -135,6 +130,28 @@ db_expand_prel31(uint32_t prel31)
return ((int32_t)(prel31 & 0x7fffffffu) << 1) / 2;
}
+struct db_find_index_context_t
+{
+ int valid;
+ caddr_t addr;
+ caddr_t exidx_start, exidx_end;
+};
+
+static int
+db_find_index_file_cb(linker_file_t lf, void* arg)
+{
+ struct db_find_index_context_t *context = (struct
db_find_index_context_t*)arg;
+ if (context->addr >= lf->address && context->addr < lf->address +
lf->size)
+ {
+ context->exidx_start = linker_file_lookup_symbol(lf, "__exidx_start", 0);
+ context->exidx_end = linker_file_lookup_symbol(lf, "__exidx_end", 0);
+ context->valid = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* Perform a binary search of the index table to find the function
* with the largest address that doesn't exceed addr.
@@ -148,10 +165,18 @@ db_find_index(uint32_t addr)
int32_t prel31_addr;
uint32_t func_addr;
- start = (struct unwind_idx *)&exidx_start;
+ struct db_find_index_context_t context;
+ context.valid = 0;
+ context.addr = (caddr_t)addr;
+
+ linker_file_foreach(db_find_index_file_cb, &context);
+ if (!context.valid)
+ return 0;
+
+ start = (struct unwind_idx *)context.exidx_start;
min = 0;
- max = (&exidx_end - &exidx_start) / 2;
+ max = (context.exidx_end - context.exidx_start) / 2;
while (min != max) {
mid = min + (max - min + 1) / 2;
@@ -269,7 +294,7 @@ db_unwind_exec_insn(struct unwind_state *state)
/* Stop processing */
state->entries = 0;
- } else if ((insn == INSN_POP_REGS)) {
+ } else if (insn == INSN_POP_REGS) {
unsigned int mask, reg;
mask = db_unwind_exec_read_byte(state);
@@ -352,20 +377,22 @@ db_unwind_tab(struct unwind_state *state)
}
static void
-db_stack_trace_cmd(struct unwind_state *state)
+db_stack_trace_cmd(struct unwind_state *state, int count)
{
struct unwind_idx *index;
const char *name;
db_expr_t value;
db_expr_t offset;
c_db_sym_t sym;
+#if 0
u_int reg, i;
char *sep;
uint16_t upd_mask;
+#endif
bool finished;
finished = false;
- while (!finished) {
+ while (!finished && count--) {
/* Reset the mask of updated registers */
state->update_mask = 0;
@@ -375,7 +402,7 @@ db_stack_trace_cmd(struct unwind_state *state)
/* Find the item to run */
index = db_find_index(state->start_pc);
- if (index->insn != EXIDX_CANTUNWIND) {
+ if (index && index->insn != EXIDX_CANTUNWIND) {
if (index->insn & (1U << 31)) {
/* The data is within the instruction */
state->insn = &index->insn;
@@ -399,6 +426,7 @@ db_stack_trace_cmd(struct unwind_state *state)
db_printf("%s() at ", name);
db_printsym(state->start_pc, DB_STGY_PROC);
db_printf("\n");
+#if 0
db_printf("\t pc = 0x%08x lr = 0x%08x (", state->start_pc,
state->registers[LR]);
db_printsym(state->registers[LR], DB_STGY_PROC);
@@ -425,7 +453,7 @@ db_stack_trace_cmd(struct unwind_state *state)
}
}
db_printf("\n");
-
+#endif
/*
* Stop if directed to do so, or if we've unwound back to the
* kernel entry point, or if the unwind function didn't change
@@ -435,14 +463,17 @@ db_stack_trace_cmd(struct unwind_state *state)
* the last frame printed before you see the unwind failure
* message (maybe it needs a STOP_UNWINDING).
*/
- if (index->insn == EXIDX_CANTUNWIND) {
- db_printf("Unable to unwind further\n");
+ if (index && index->insn == EXIDX_CANTUNWIND) {
+ if (count)
+ db_printf("Unable to unwind further\n");
finished = true;
} else if (state->registers[PC] < VM_MIN_KERNEL_ADDRESS) {
- db_printf("Unable to unwind into user mode\n");
+ if (count)
+ db_printf("Unable to unwind into user mode\n");
finished = true;
} else if (state->update_mask == 0) {
- db_printf("Unwind failure (no registers changed)\n");
+ if (count)
+ db_printf("Unwind failure (no registers changed)\n");
finished = true;
}
}
@@ -479,7 +510,7 @@ db_stack_trace_cmd(struct unwind_state *state)
#ifndef __ARM_EABI__ /* The frame format is differend in AAPCS */
static void
-db_stack_trace_cmd(db_expr_t addr, db_expr_t count, boolean_t kernel_only)
+db_stack_trace_cmd(db_expr_t addr, int count, boolean_t kernel_only)
{
u_int32_t *frame, *lastframe;
c_db_sym_t sym;
@@ -608,9 +639,9 @@ db_trace_thread(struct thread *thr, int count)
state.registers[LR] = ctx->un_32.pcb32_lr;
state.registers[PC] = ctx->un_32.pcb32_pc;
- db_stack_trace_cmd(&state);
+ db_stack_trace_cmd(&state, count);
#else
- db_stack_trace_cmd(ctx->un_32.pcb32_r11, -1, TRUE);
+ db_stack_trace_cmd(ctx->un_32.pcb32_r11, count, TRUE);
#endif
} else
db_trace_self();
@@ -632,7 +663,7 @@ db_trace_self(void)
state.registers[LR] = (uint32_t)__builtin_return_address(0);
state.registers[PC] = (uint32_t)db_trace_self;
- db_stack_trace_cmd(&state);
+ db_stack_trace_cmd(&state, -1);
#else
db_addr_t addr;
diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk
index bd05878..d0a1f0f 100644
--- a/sys/conf/kmod.mk
+++ b/sys/conf/kmod.mk
@@ -133,6 +133,16 @@ CFLAGS+= -mlongcall -fno-omit-frame-pointer
CFLAGS+= -G0 -fno-pic -mno-abicalls -mlong-calls
.endif
+.if ${MACHINE_CPUARCH} == arm
+.if !defined(WITHOUT_ARM_EABI)
+CFLAGS+= -funwind-tables
+.if ${COMPILER_TYPE} == "clang"
+# clang requires us to tell it to emit assembly with unwind information
+CFLAGS+= -mllvm -arm-enable-ehabi
+.endif
+.endif
+.endif
+
.if defined(DEBUG) || defined(DEBUG_FLAGS)
CTFFLAGS+= -g
.endif
diff --git a/sys/conf/ldscript.arm b/sys/conf/ldscript.arm
index 0d1c7ee..2482ce7 100644
--- a/sys/conf/ldscript.arm
+++ b/sys/conf/ldscript.arm
@@ -57,17 +57,11 @@ SECTIONS
.plt : { *(.plt) }
. = ALIGN(4);
- _extab_start = .;
- PROVIDE(extab_start = .);
.ARM.extab : { *(.ARM.extab) }
- _extab.end = .;
- PROVIDE(extab_end = .);
- _exidx_start = .;
- PROVIDE(exidx_start = .);
+ __exidx_start = .;
.ARM.exidx : { *(.ARM.exidx) }
- _exidx_end = .;
- PROVIDE(exidx_end = .);
+ __exidx_end = .;
/* Adjust the address for the data segment. We want to adjust up to
the same address within the page on the next page up. */
--
-Howard
More information about the freebsd-arm
mailing list