Home | History | Annotate | Download | only in asan
      1 //===-- asan_stack.cc -------------------------------------------*- C++ -*-===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of AddressSanitizer, an address sanity checker.
     11 //
     12 // Code for ASan stack trace.
     13 //===----------------------------------------------------------------------===//
     14 #include "asan_interceptors.h"
     15 #include "asan_lock.h"
     16 #include "asan_procmaps.h"
     17 #include "asan_stack.h"
     18 #include "asan_thread.h"
     19 #include "asan_thread_registry.h"
     20 
     21 #ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
     22 extern bool
     23 ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
     24 #endif
     25 
     26 namespace __asan {
     27 
     28 // ----------------------- AsanStackTrace ----------------------------- {{{1
     29 #if defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
     30 void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
     31   for (size_t i = 0; i < size && addr[i]; i++) {
     32     uintptr_t pc = addr[i];
     33     char buff[4096];
     34     ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff));
     35     Printf("  #%zu 0x%zx %s\n", i, pc, buff);
     36   }
     37 }
     38 
     39 #else  // ASAN_USE_EXTERNAL_SYMBOLIZER
     40 void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
     41   AsanProcMaps proc_maps;
     42   for (size_t i = 0; i < size && addr[i]; i++) {
     43     proc_maps.Reset();
     44     uintptr_t pc = addr[i];
     45     uintptr_t offset;
     46     char filename[4096];
     47     if (proc_maps.GetObjectNameAndOffset(pc, &offset,
     48                                          filename, sizeof(filename))) {
     49       Printf("    #%zu 0x%zx (%s+0x%zx)\n", i, pc, filename, offset);
     50     } else {
     51       Printf("    #%zu 0x%zx\n", i, pc);
     52     }
     53   }
     54 }
     55 #endif  // ASAN_USE_EXTERNAL_SYMBOLIZER
     56 
     57 uintptr_t AsanStackTrace::GetCurrentPc() {
     58   return GET_CALLER_PC();
     59 }
     60 
     61 void AsanStackTrace::FastUnwindStack(uintptr_t pc, uintptr_t bp) {
     62   CHECK(size == 0 && trace[0] == pc);
     63   size = 1;
     64   if (!asan_inited) return;
     65   AsanThread *t = asanThreadRegistry().GetCurrent();
     66   if (!t) return;
     67   uintptr_t *frame = (uintptr_t*)bp;
     68   uintptr_t *prev_frame = frame;
     69   uintptr_t *top = (uintptr_t*)t->stack_top();
     70   uintptr_t *bottom = (uintptr_t*)t->stack_bottom();
     71   while (frame >= prev_frame &&
     72          frame < top - 2 &&
     73          frame > bottom &&
     74          size < max_size) {
     75     uintptr_t pc1 = frame[1];
     76     if (pc1 != pc) {
     77       trace[size++] = pc1;
     78     }
     79     prev_frame = frame;
     80     frame = (uintptr_t*)frame[0];
     81   }
     82 }
     83 
     84 // On 32-bits we don't compress stack traces.
     85 // On 64-bits we compress stack traces: if a given pc differes slightly from
     86 // the previous one, we record a 31-bit offset instead of the full pc.
     87 size_t AsanStackTrace::CompressStack(AsanStackTrace *stack,
     88                                    uint32_t *compressed, size_t size) {
     89 #if __WORDSIZE == 32
     90   // Don't compress, just copy.
     91   size_t res = 0;
     92   for (size_t i = 0; i < stack->size && i < size; i++) {
     93     compressed[i] = stack->trace[i];
     94     res++;
     95   }
     96   if (stack->size < size)
     97     compressed[stack->size] = 0;
     98 #else  // 64 bits, compress.
     99   uintptr_t prev_pc = 0;
    100   const uintptr_t kMaxOffset = (1ULL << 30) - 1;
    101   uintptr_t c_index = 0;
    102   size_t res = 0;
    103   for (size_t i = 0, n = stack->size; i < n; i++) {
    104     uintptr_t pc = stack->trace[i];
    105     if (!pc) break;
    106     if ((int64_t)pc < 0) break;
    107     // Printf("C pc[%zu] %zx\n", i, pc);
    108     if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
    109       uintptr_t offset = (int64_t)(pc - prev_pc);
    110       offset |= (1U << 31);
    111       if (c_index >= size) break;
    112       // Printf("C co[%zu] offset %zx\n", i, offset);
    113       compressed[c_index++] = offset;
    114     } else {
    115       uintptr_t hi = pc >> 32;
    116       uintptr_t lo = (pc << 32) >> 32;
    117       CHECK((hi & (1 << 31)) == 0);
    118       if (c_index + 1 >= size) break;
    119       // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo);
    120       compressed[c_index++] = hi;
    121       compressed[c_index++] = lo;
    122     }
    123     res++;
    124     prev_pc = pc;
    125   }
    126   if (c_index < size)
    127     compressed[c_index] = 0;
    128   if (c_index + 1 < size)
    129     compressed[c_index + 1] = 0;
    130 #endif  // __WORDSIZE
    131 
    132   // debug-only code
    133 #if 0
    134   AsanStackTrace check_stack;
    135   UncompressStack(&check_stack, compressed, size);
    136   if (res < check_stack.size) {
    137     Printf("res %zu check_stack.size %zu; c_size %zu\n", res,
    138            check_stack.size, size);
    139   }
    140   // |res| may be greater than check_stack.size, because
    141   // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
    142   CHECK(res >= check_stack.size);
    143   CHECK(0 == REAL(memcmp)(check_stack.trace, stack->trace,
    144                           check_stack.size * sizeof(uintptr_t)));
    145 #endif
    146 
    147   return res;
    148 }
    149 
    150 void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
    151                                      uint32_t *compressed, size_t size) {
    152 #if __WORDSIZE == 32
    153   // Don't uncompress, just copy.
    154   stack->size = 0;
    155   for (size_t i = 0; i < size && i < kStackTraceMax; i++) {
    156     if (!compressed[i]) break;
    157     stack->size++;
    158     stack->trace[i] = compressed[i];
    159   }
    160 #else  // 64 bits, uncompress
    161   uintptr_t prev_pc = 0;
    162   stack->size = 0;
    163   for (size_t i = 0; i < size && stack->size < kStackTraceMax; i++) {
    164     uint32_t x = compressed[i];
    165     uintptr_t pc = 0;
    166     if (x & (1U << 31)) {
    167       // Printf("U co[%zu] offset: %x\n", i, x);
    168       // this is an offset
    169       int32_t offset = x;
    170       offset = (offset << 1) >> 1;  // remove the 31-byte and sign-extend.
    171       pc = prev_pc + offset;
    172       CHECK(pc);
    173     } else {
    174       // CHECK(i + 1 < size);
    175       if (i + 1 >= size) break;
    176       uintptr_t hi = x;
    177       uintptr_t lo = compressed[i+1];
    178       // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo);
    179       i++;
    180       pc = (hi << 32) | lo;
    181       if (!pc) break;
    182     }
    183     // Printf("U pc[%zu] %zx\n", stack->size, pc);
    184     stack->trace[stack->size++] = pc;
    185     prev_pc = pc;
    186   }
    187 #endif  // __WORDSIZE
    188 }
    189 
    190 }  // namespace __asan
    191