Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/arch/mips32/src/debug/stacktrace.c

    r76e1121f rd99c1d2  
    3333 */
    3434
    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 
    6335#include <stacktrace.h>
    6436#include <syscall/copy.h>
    6537#include <typedefs.h>
    66 #include <arch/debugger.h>
    67 #include <print.h>
    6838
    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)
     39bool kernel_frame_pointer_validate(uintptr_t fp)
    25940{
    26041        return false;
    26142}
    26243
    263 bool uspace_frame_pointer_prev(stack_trace_context_t *ctx, uintptr_t *prev)
     44bool kernel_frame_pointer_prev(uintptr_t fp, uintptr_t *prev)
    26445{
    26546        return false;
    26647}
    26748
    268 bool uspace_return_address_get(stack_trace_context_t *ctx, uintptr_t *ra)
     49bool kernel_return_address_get(uintptr_t fp, uintptr_t *ra)
     50{
     51        return false;
     52}
     53
     54bool uspace_frame_pointer_validate(uintptr_t fp)
     55{
     56        return false;
     57}
     58
     59bool uspace_frame_pointer_prev(uintptr_t fp, uintptr_t *prev)
     60{
     61        return false;
     62}
     63
     64bool uspace_return_address_get(uintptr_t fp, uintptr_t *ra)
    26965{
    27066        return false;
Note: See TracChangeset for help on using the changeset viewer.