Changes in kernel/arch/mips32/src/debug/stacktrace.c [d99c1d2:76e1121f] in mainline
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
kernel/arch/mips32/src/debug/stacktrace.c
rd99c1d2 r76e1121f 33 33 */ 34 34 35 /* 36 * This stack tracing code is based on the suggested algorithm described on page 37 * 3-27 and 3-28 of: 38 * 39 * SYSTEM V 40 * APPLICATION BINARY INTERFACE 41 * 42 * MIPS RISC Processor 43 * Supplement 44 * 3rd Edition 45 * 46 * Unfortunately, GCC generates code which is not entirely compliant with this 47 * method. For example, it places the "jr ra" instruction quite arbitrarily in 48 * 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 algorithm 51 * that rely on the "jr ra" instruction occurring in the last basic block of a 52 * function, which gives us still usable, but less reliable stack tracer. The 53 * unreliability stems from the fact that under some circumstances it can become 54 * confused and produce incorrect or incomplete stack trace. We apply extra 55 * sanity checks so that the algorithm is still safe and should not crash the 56 * system. 57 * 58 * Even though not perfect, our solution is pretty lightweight, especially when 59 * compared with a prospective alternative solution based on additional 60 * debugging information stored directly in the kernel image. 61 */ 62 35 63 #include <stacktrace.h> 36 64 #include <syscall/copy.h> 37 65 #include <typedefs.h> 38 39 bool kernel_frame_pointer_validate(uintptr_t fp) 66 #include <arch/debugger.h> 67 #include <print.h> 68 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) 40 259 { 41 260 return false; 42 261 } 43 262 44 bool kernel_frame_pointer_prev(uintptr_t fp, uintptr_t *prev)263 bool uspace_frame_pointer_prev(stack_trace_context_t *ctx, uintptr_t *prev) 45 264 { 46 265 return false; 47 266 } 48 267 49 bool kernel_return_address_get(uintptr_t fp, uintptr_t *ra)268 bool uspace_return_address_get(stack_trace_context_t *ctx, uintptr_t *ra) 50 269 { 51 270 return false; 52 271 } 53 272 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)65 {66 return false;67 }68 69 273 /** @} 70 274 */
Note:
See TracChangeset
for help on using the changeset viewer.