Changes in kernel/arch/mips32/src/debug/stacktrace.c [76e1121f:d99c1d2] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/arch/mips32/src/debug/stacktrace.c
r76e1121f rd99c1d2 33 33 */ 34 34 35 /*36 * This stack tracing code is based on the suggested algorithm described on page37 * 3-27 and 3-28 of:38 *39 * SYSTEM V40 * APPLICATION BINARY INTERFACE41 *42 * MIPS RISC Processor43 * Supplement44 * 3rd Edition45 *46 * Unfortunately, GCC generates code which is not entirely compliant with this47 * method. For example, it places the "jr ra" instruction quite arbitrarily in48 * the middle of the function which makes the original algorithm unapplicable.49 *50 * We deal with this problem by simply not using those parts of the algorithm51 * that rely on the "jr ra" instruction occurring in the last basic block of a52 * function, which gives us still usable, but less reliable stack tracer. The53 * unreliability stems from the fact that under some circumstances it can become54 * confused and produce incorrect or incomplete stack trace. We apply extra55 * sanity checks so that the algorithm is still safe and should not crash the56 * system.57 *58 * Even though not perfect, our solution is pretty lightweight, especially when59 * compared with a prospective alternative solution based on additional60 * debugging information stored directly in the kernel image.61 */62 63 35 #include <stacktrace.h> 64 36 #include <syscall/copy.h> 65 37 #include <typedefs.h> 66 #include <arch/debugger.h>67 #include <print.h>68 38 69 #define R0 0U 70 #define SP 29U 71 #define RA 31U 72 73 #define OP_SHIFT 26 74 #define RS_SHIFT 21 75 #define RT_SHIFT 16 76 #define RD_SHIFT 11 77 78 #define HINT_SHIFT 6 79 #define BASE_SHIFT RS_SHIFT 80 #define IMM_SHIFT 0 81 #define OFFSET_SHIFT IMM_SHIFT 82 83 #define RS_MASK (0x1f << RS_SHIFT) 84 #define RT_MASK (0x1f << RT_SHIFT) 85 #define RD_MASK (0x1f << RD_SHIFT) 86 #define HINT_MASK (0x1f << HINT_SHIFT) 87 #define BASE_MASK RS_MASK 88 #define IMM_MASK (0xffff << IMM_SHIFT) 89 #define OFFSET_MASK IMM_MASK 90 91 #define RS_GET(inst) (((inst) & RS_MASK) >> RS_SHIFT) 92 #define RD_GET(inst) (((inst) & RD_MASK) >> RD_SHIFT) 93 #define IMM_GET(inst) (int16_t)(((inst) & IMM_MASK) >> IMM_SHIFT) 94 #define BASE_GET(inst) RS_GET(inst) 95 #define OFFSET_GET(inst) IMM_GET(inst) 96 97 #define ADDU_R_SP_R0_TEMPL \ 98 ((0x0 << OP_SHIFT) | (SP << RS_SHIFT) | (R0 << RT_SHIFT) | 0x21) 99 #define ADDU_SP_R_R0_TEMPL \ 100 ((0x0 << OP_SHIFT) | (SP << RD_SHIFT) | (R0 << RT_SHIFT) | 0x21) 101 #define ADDI_SP_SP_IMM_TEMPL \ 102 ((0x8 << OP_SHIFT) | (SP << RS_SHIFT) | (SP << RT_SHIFT)) 103 #define ADDIU_SP_SP_IMM_TEMPL \ 104 ((0x9 << OP_SHIFT) | (SP << RS_SHIFT) | (SP << RT_SHIFT)) 105 #define JR_RA_TEMPL \ 106 ((0x0 << OP_SHIFT) | (RA << RS_SHIFT) | (0x0 << HINT_SHIFT) | 0x8) 107 #define SW_RA_TEMPL \ 108 ((0x2b << OP_SHIFT) | (RA << RT_SHIFT)) 109 110 #define IS_ADDU_R_SP_R0(inst) \ 111 (((inst) & ~RD_MASK) == ADDU_R_SP_R0_TEMPL) 112 #define IS_ADDU_SP_R_R0(inst) \ 113 (((inst) & ~RS_MASK) == ADDU_SP_R_R0_TEMPL) 114 #define IS_ADDI_SP_SP_IMM(inst) \ 115 (((inst) & ~IMM_MASK) == ADDI_SP_SP_IMM_TEMPL) 116 #define IS_ADDIU_SP_SP_IMM(inst) \ 117 (((inst) & ~IMM_MASK) == ADDIU_SP_SP_IMM_TEMPL) 118 #define IS_JR_RA(inst) \ 119 (((inst) & ~HINT_MASK) == JR_RA_TEMPL) 120 #define IS_SW_RA(inst) \ 121 (((inst) & ~(BASE_MASK | OFFSET_MASK)) == SW_RA_TEMPL) 122 123 extern char ktext_start; 124 extern char ktext_end; 125 126 static bool bounds_check(uintptr_t pc) 127 { 128 return (pc >= (uintptr_t) &ktext_start) && 129 (pc < (uintptr_t) &ktext_end); 130 } 131 132 static bool 133 scan(stack_trace_context_t *ctx, uintptr_t *prev_fp, uintptr_t *prev_ra) 134 { 135 uint32_t *inst = (void *) ctx->pc; 136 bool has_fp = false; 137 size_t frame_size; 138 unsigned int fp = SP; 139 140 do { 141 inst--; 142 if (!bounds_check((uintptr_t) inst)) 143 return false; 144 #if 0 145 /* 146 * This is one of the situations in which the theory (ABI) does 147 * not meet the practice (GCC). GCC simply does not place the 148 * JR $ra instruction as dictated by the ABI, rendering the 149 * official stack tracing algorithm somewhat unapplicable. 150 */ 151 152 if (IS_ADDU_R_SP_R0(*inst)) { 153 uint32_t *cur; 154 fp = RD_GET(*inst); 155 /* 156 * We have a candidate for frame pointer. 157 */ 158 159 /* Seek to the end of this function. */ 160 for (cur = inst + 1; !IS_JR_RA(*cur); cur++) 161 ; 162 /* Scan the last basic block */ 163 for (cur--; !is_jump(*(cur - 1)); cur--) { 164 if (IS_ADDU_SP_R_R0(*cur) && 165 (fp == RS_GET(*cur))) { 166 has_fp = true; 167 } 168 } 169 continue; 170 } 171 172 if (IS_JR_RA(*inst)) { 173 if (!ctx->istate) 174 return false; 175 /* 176 * No stack frame has been allocated yet. 177 * Use the values stored in istate. 178 */ 179 if (prev_fp) 180 *prev_fp = ctx->istate->sp; 181 if (prev_ra) 182 *prev_ra = ctx->istate->ra - 8; 183 ctx->istate = NULL; 184 return true; 185 } 186 #endif 187 188 } while ((!IS_ADDIU_SP_SP_IMM(*inst) && !IS_ADDI_SP_SP_IMM(*inst)) || 189 (IMM_GET(*inst) >= 0)); 190 191 /* 192 * We are at the instruction which allocates the space for the current 193 * stack frame. 194 */ 195 frame_size = -IMM_GET(*inst); 196 if (prev_fp) 197 *prev_fp = ctx->fp + frame_size; 198 199 /* 200 * Scan the first basic block for the occurrence of 201 * SW $ra, OFFSET($base). 202 */ 203 for (inst++; !is_jump(*(inst - 1)) && (uintptr_t) inst < ctx->pc; 204 inst++) { 205 if (IS_SW_RA(*inst)) { 206 unsigned int base = BASE_GET(*inst); 207 int16_t offset = OFFSET_GET(*inst); 208 209 if (base == SP || (has_fp && base == fp)) { 210 uint32_t *addr = (void *) (ctx->fp + offset); 211 212 if (offset % 4 != 0) 213 return false; 214 /* cannot store below current stack pointer */ 215 if (offset < 0) 216 return false; 217 /* too big offsets are suspicious */ 218 if ((size_t) offset > sizeof(istate_t)) 219 return false; 220 221 if (prev_ra) 222 *prev_ra = *addr; 223 return true; 224 } 225 } 226 } 227 228 /* 229 * The first basic block does not save the return address or saves it 230 * after ctx->pc, which means that the correct value is in istate. 231 */ 232 if (prev_ra) { 233 if (!ctx->istate) 234 return false; 235 *prev_ra = ctx->istate->ra - 8; 236 ctx->istate = NULL; 237 } 238 return true; 239 } 240 241 242 bool kernel_stack_trace_context_validate(stack_trace_context_t *ctx) 243 { 244 return !((ctx->fp == 0) || ((ctx->fp % 8) != 0) || 245 (ctx->pc % 4 != 0) || !bounds_check(ctx->pc)); 246 } 247 248 bool kernel_frame_pointer_prev(stack_trace_context_t *ctx, uintptr_t *prev) 249 { 250 return scan(ctx, prev, NULL); 251 } 252 253 bool kernel_return_address_get(stack_trace_context_t *ctx, uintptr_t *ra) 254 { 255 return scan(ctx, NULL, ra); 256 } 257 258 bool uspace_stack_trace_context_validate(stack_trace_context_t *ctx) 39 bool kernel_frame_pointer_validate(uintptr_t fp) 259 40 { 260 41 return false; 261 42 } 262 43 263 bool uspace_frame_pointer_prev(stack_trace_context_t *ctx, uintptr_t *prev)44 bool kernel_frame_pointer_prev(uintptr_t fp, uintptr_t *prev) 264 45 { 265 46 return false; 266 47 } 267 48 268 bool uspace_return_address_get(stack_trace_context_t *ctx, uintptr_t *ra) 49 bool kernel_return_address_get(uintptr_t fp, uintptr_t *ra) 50 { 51 return false; 52 } 53 54 bool uspace_frame_pointer_validate(uintptr_t fp) 55 { 56 return false; 57 } 58 59 bool uspace_frame_pointer_prev(uintptr_t fp, uintptr_t *prev) 60 { 61 return false; 62 } 63 64 bool uspace_return_address_get(uintptr_t fp, uintptr_t *ra) 269 65 { 270 66 return false;
Note:
See TracChangeset
for help on using the changeset viewer.