asan_stack.cc revision e5f5895bda30f374b0b51412fd4d837fa59aed66
1//===-- asan_stack.cc -----------------------------------------------------===//
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#include "sanitizer_common/sanitizer_symbolizer.h"
21
22using namespace __sanitizer; // NOLINT
23
24#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
25extern bool
26ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
27#endif
28
29namespace __asan {
30
31// ----------------------- AsanStackTrace ----------------------------- {{{1
32#if defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
33void AsanStackTrace::PrintStack(uptr *addr, uptr size) {
34  for (uptr i = 0; i < size && addr[i]; i++) {
35    uptr pc = addr[i];
36    char buff[4096];
37    ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff));
38    Printf("  #%zu 0x%zx %s\n", i, pc, buff);
39  }
40}
41
42#else  // ASAN_USE_EXTERNAL_SYMBOLIZER
43void AsanStackTrace::PrintStack(uptr *addr, uptr size) {
44  AsanProcMaps proc_maps;
45  uptr frame_num = 0;
46  for (uptr i = 0; i < size && addr[i]; i++) {
47    proc_maps.Reset();
48    uptr pc = addr[i];
49    uptr offset;
50    char filename[4096];
51    if (FLAG_symbolize) {
52      AddressInfoList *address_info_list = SymbolizeCode(pc);
53      for (AddressInfoList *entry = address_info_list; entry;
54           entry = entry->next) {
55        AddressInfo info = entry->info;
56        Printf("    #%zu 0x%zx %s:%d:%d\n", frame_num, pc,
57                                            (info.file) ? info.file : "",
58                                            info.line, info.column);
59        frame_num++;
60      }
61      address_info_list->Clear();
62    } else {
63      if (proc_maps.GetObjectNameAndOffset(pc, &offset,
64                                           filename, sizeof(filename))) {
65        Printf("    #%zu 0x%zx (%s+0x%zx)\n", frame_num, pc, filename,
66                                              offset);
67      } else {
68        Printf("    #%zu 0x%zx\n", frame_num, pc);
69      }
70      frame_num++;
71    }
72  }
73}
74#endif  // ASAN_USE_EXTERNAL_SYMBOLIZER
75
76uptr AsanStackTrace::GetCurrentPc() {
77  return GET_CALLER_PC();
78}
79
80void AsanStackTrace::FastUnwindStack(uptr pc, uptr bp) {
81  CHECK(size == 0 && trace[0] == pc);
82  size = 1;
83  if (!asan_inited) return;
84  AsanThread *t = asanThreadRegistry().GetCurrent();
85  if (!t) return;
86  uptr *frame = (uptr*)bp;
87  uptr *prev_frame = frame;
88  uptr *top = (uptr*)t->stack_top();
89  uptr *bottom = (uptr*)t->stack_bottom();
90  while (frame >= prev_frame &&
91         frame < top - 2 &&
92         frame > bottom &&
93         size < max_size) {
94    uptr pc1 = frame[1];
95    if (pc1 != pc) {
96      trace[size++] = pc1;
97    }
98    prev_frame = frame;
99    frame = (uptr*)frame[0];
100  }
101}
102
103// On 32-bits we don't compress stack traces.
104// On 64-bits we compress stack traces: if a given pc differes slightly from
105// the previous one, we record a 31-bit offset instead of the full pc.
106uptr AsanStackTrace::CompressStack(AsanStackTrace *stack,
107                                   u32 *compressed, uptr size) {
108#if __WORDSIZE == 32
109  // Don't compress, just copy.
110  uptr res = 0;
111  for (uptr i = 0; i < stack->size && i < size; i++) {
112    compressed[i] = stack->trace[i];
113    res++;
114  }
115  if (stack->size < size)
116    compressed[stack->size] = 0;
117#else  // 64 bits, compress.
118  uptr prev_pc = 0;
119  const uptr kMaxOffset = (1ULL << 30) - 1;
120  uptr c_index = 0;
121  uptr res = 0;
122  for (uptr i = 0, n = stack->size; i < n; i++) {
123    uptr pc = stack->trace[i];
124    if (!pc) break;
125    if ((s64)pc < 0) break;
126    // Printf("C pc[%zu] %zx\n", i, pc);
127    if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
128      uptr offset = (s64)(pc - prev_pc);
129      offset |= (1U << 31);
130      if (c_index >= size) break;
131      // Printf("C co[%zu] offset %zx\n", i, offset);
132      compressed[c_index++] = offset;
133    } else {
134      uptr hi = pc >> 32;
135      uptr lo = (pc << 32) >> 32;
136      CHECK((hi & (1 << 31)) == 0);
137      if (c_index + 1 >= size) break;
138      // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo);
139      compressed[c_index++] = hi;
140      compressed[c_index++] = lo;
141    }
142    res++;
143    prev_pc = pc;
144  }
145  if (c_index < size)
146    compressed[c_index] = 0;
147  if (c_index + 1 < size)
148    compressed[c_index + 1] = 0;
149#endif  // __WORDSIZE
150
151  // debug-only code
152#if 0
153  AsanStackTrace check_stack;
154  UncompressStack(&check_stack, compressed, size);
155  if (res < check_stack.size) {
156    Printf("res %zu check_stack.size %zu; c_size %zu\n", res,
157           check_stack.size, size);
158  }
159  // |res| may be greater than check_stack.size, because
160  // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
161  CHECK(res >= check_stack.size);
162  CHECK(0 == REAL(memcmp)(check_stack.trace, stack->trace,
163                          check_stack.size * sizeof(uptr)));
164#endif
165
166  return res;
167}
168
169void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
170                                     u32 *compressed, uptr size) {
171#if __WORDSIZE == 32
172  // Don't uncompress, just copy.
173  stack->size = 0;
174  for (uptr i = 0; i < size && i < kStackTraceMax; i++) {
175    if (!compressed[i]) break;
176    stack->size++;
177    stack->trace[i] = compressed[i];
178  }
179#else  // 64 bits, uncompress
180  uptr prev_pc = 0;
181  stack->size = 0;
182  for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) {
183    u32 x = compressed[i];
184    uptr pc = 0;
185    if (x & (1U << 31)) {
186      // Printf("U co[%zu] offset: %x\n", i, x);
187      // this is an offset
188      s32 offset = x;
189      offset = (offset << 1) >> 1;  // remove the 31-byte and sign-extend.
190      pc = prev_pc + offset;
191      CHECK(pc);
192    } else {
193      // CHECK(i + 1 < size);
194      if (i + 1 >= size) break;
195      uptr hi = x;
196      uptr lo = compressed[i+1];
197      // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo);
198      i++;
199      pc = (hi << 32) | lo;
200      if (!pc) break;
201    }
202    // Printf("U pc[%zu] %zx\n", stack->size, pc);
203    stack->trace[stack->size++] = pc;
204    prev_pc = pc;
205  }
206#endif  // __WORDSIZE
207}
208
209}  // namespace __asan
210