blob: 08a70aa985f78357c904ca093d1f8266dcb7d442 [file] [log] [blame]
Kostya Serebryany1e172b42011-11-30 01:07:02 +00001//===-- 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_stack.h"
17#include "asan_thread.h"
18#include "asan_thread_registry.h"
19
20#include <string.h>
21
22#ifdef ASAN_USE_SYSINFO
23#include "sysinfo/sysinfo.h"
24#endif
25
26#ifdef ASAN_USE_EXTERNAL_SYMBOLIZER
27extern bool
28ASAN_USE_EXTERNAL_SYMBOLIZER(const void *pc, char *out, int out_size);
29#endif
30
31namespace __asan {
32
33// ----------------------- ProcSelfMaps ----------------------------- {{{1
34#ifdef ASAN_USE_SYSINFO
35class ProcSelfMaps {
36 public:
37 void Init() {
38 ScopedLock lock(&mu_);
39 if (map_size_ != 0) return; // already inited
40 if (FLAG_v >= 2) {
41 Printf("ProcSelfMaps::Init()\n");
42 }
43 ProcMapsIterator it(0, &proc_self_maps_); // 0 means "current pid"
44
45 uint64 start, end, offset;
46 int64 inode;
47 char *flags, *filename;
48 CHECK(map_size_ == 0);
49 while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
50 CHECK(map_size_ < kMaxProcSelfMapsSize);
51 Mapping &mapping = memory_map[map_size_];
52 mapping.beg = start;
53 mapping.end = end;
54 mapping.offset = offset;
55 real_strncpy(mapping.name,
56 filename, ASAN_ARRAY_SIZE(mapping.name));
57 mapping.name[ASAN_ARRAY_SIZE(mapping.name) - 1] = 0;
58 if (FLAG_v >= 2) {
59 Printf("[%ld] [%p,%p] off %p %s\n", map_size_,
60 mapping.beg, mapping.end, mapping.offset, mapping.name);
61 }
62 map_size_++;
63 }
64 }
65
66 void Print() {
67 Printf("%s\n", proc_self_maps_);
68 }
69
70 void PrintPc(uintptr_t pc, int idx) {
71 for (size_t i = 0; i < map_size_; i++) {
72 Mapping &m = memory_map[i];
73 if (pc >= m.beg && pc < m.end) {
74 uintptr_t offset = pc - m.beg;
75 if (i == 0) offset = pc;
76 Printf(" #%d 0x%lx (%s+0x%lx)\n", idx, pc, m.name, offset);
77 return;
78 }
79 }
80 Printf(" #%d 0x%lx\n", idx, pc);
81 }
82
83 private:
84 void copy_until_new_line(const char *str, char *dest, size_t max_size) {
85 size_t i = 0;
86 for (; str[i] && str[i] != '\n' && i < max_size - 1; i++) {
87 dest[i] = str[i];
88 }
89 dest[i] = 0;
90 }
91
92
93 struct Mapping {
94 uintptr_t beg, end, offset;
95 char name[1000];
96 };
97 static const size_t kMaxNumMapEntries = 4096;
98 static const size_t kMaxProcSelfMapsSize = 1 << 20;
99 ProcMapsIterator::Buffer proc_self_maps_;
100 size_t map_size_;
101 Mapping memory_map[kMaxNumMapEntries];
102
103 static AsanLock mu_;
104};
105
106static ProcSelfMaps proc_self_maps;
107AsanLock ProcSelfMaps::mu_(LINKER_INITIALIZED);
108
109
110void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
111 proc_self_maps.Init();
112 for (size_t i = 0; i < size && addr[i]; i++) {
113 uintptr_t pc = addr[i];
114 // int line;
115 proc_self_maps.PrintPc(pc, i);
116 // Printf(" #%ld 0x%lx %s\n", i, pc, rtn.c_str());
117 }
118}
119#elif defined(ASAN_USE_EXTERNAL_SYMBOLIZER)
120void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
121 for (size_t i = 0; i < size && addr[i]; i++) {
122 uintptr_t pc = addr[i];
123 char buff[4096];
124 ASAN_USE_EXTERNAL_SYMBOLIZER((void*)pc, buff, sizeof(buff));
125 Printf(" #%ld 0x%lx %s\n", i, pc, buff);
126 }
127}
128
129#else // ASAN_USE_SYSINFO
130void AsanStackTrace::PrintStack(uintptr_t *addr, size_t size) {
131 for (size_t i = 0; i < size && addr[i]; i++) {
132 uintptr_t pc = addr[i];
133 Printf(" #%ld 0x%lx\n", i, pc);
134 }
135}
136#endif // ASAN_USE_SYSINFO
137
138#ifdef __arm__
139#define UNWIND_STOP _URC_END_OF_STACK
140#define UNWIND_CONTINUE _URC_OK
141#else
142#define UNWIND_STOP _URC_NORMAL_STOP
143#define UNWIND_CONTINUE _URC_NO_REASON
144#endif
145
146// ----------------------- AsanStackTrace ----------------------------- {{{1
147uintptr_t AsanStackTrace::GetCurrentPc() {
148 return GET_CALLER_PC();
149}
150
151void AsanStackTrace::FastUnwindStack(uintptr_t pc, uintptr_t bp) {
152 CHECK(size == 0 && trace[0] == pc);
153 size = 1;
154 if (!asan_inited) return;
155 AsanThread *t = asanThreadRegistry().GetCurrent();
156 if (!t) return;
157 uintptr_t *frame = (uintptr_t*)bp;
158 uintptr_t *prev_frame = frame;
159 uintptr_t *top = (uintptr_t*)t->stack_top();
160 uintptr_t *bottom = (uintptr_t*)t->stack_bottom();
161 while (frame >= prev_frame &&
162 frame < top &&
163 frame > bottom &&
164 size < max_size) {
165 uintptr_t pc1 = frame[1];
166 if (pc1 != pc) {
167 trace[size++] = pc1;
168 }
169 prev_frame = frame;
170 frame = (uintptr_t*)frame[0];
171 }
172}
173
174// On 32-bits we don't compress stack traces.
175// On 64-bits we compress stack traces: if a given pc differes slightly from
176// the previous one, we record a 31-bit offset instead of the full pc.
177size_t AsanStackTrace::CompressStack(AsanStackTrace *stack,
178 uint32_t *compressed, size_t size) {
179#if __WORDSIZE == 32
180 // Don't compress, just copy.
181 size_t res = 0;
182 for (size_t i = 0; i < stack->size && i < size; i++) {
183 compressed[i] = stack->trace[i];
184 res++;
185 }
186 for (size_t i = stack->size; i < size; i++) {
187 compressed[i] = 0;
188 }
189#else // 64 bits, compress.
190 uintptr_t prev_pc = 0;
191 const uintptr_t kMaxOffset = (1ULL << 30) - 1;
192 uintptr_t c_index = 0;
193 size_t res = 0;
194 for (size_t i = 0, n = stack->size; i < n; i++) {
195 uintptr_t pc = stack->trace[i];
196 if (!pc) break;
197 if ((int64_t)pc < 0) break;
198 // Printf("C pc[%ld] %lx\n", i, pc);
199 if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) {
200 uintptr_t offset = (int64_t)(pc - prev_pc);
201 offset |= (1U << 31);
202 if (c_index >= size) break;
203 // Printf("C co[%ld] offset %lx\n", i, offset);
204 compressed[c_index++] = offset;
205 } else {
206 uintptr_t hi = pc >> 32;
207 uintptr_t lo = (pc << 32) >> 32;
208 CHECK((hi & (1 << 31)) == 0);
209 if (c_index + 1 >= size) break;
210 // Printf("C co[%ld] hi/lo: %lx %lx\n", c_index, hi, lo);
211 compressed[c_index++] = hi;
212 compressed[c_index++] = lo;
213 }
214 res++;
215 prev_pc = pc;
216 }
217 for (size_t i = c_index; i < size; i++) {
218 compressed[i] = 0;
219 }
220#endif // __WORDSIZE
221
222 // debug-only code
223#if 0
224 AsanStackTrace check_stack;
225 UncompressStack(&check_stack, compressed, size);
226 if (res < check_stack.size) {
227 Printf("res %ld check_stack.size %ld; c_size %ld\n", res,
228 check_stack.size, size);
229 }
230 // |res| may be greater than check_stack.size, because
231 // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames.
232 CHECK(res >= check_stack.size);
233 CHECK(0 == memcmp(check_stack.trace, stack->trace,
234 check_stack.size * sizeof(uintptr_t)));
235#endif
236
237 return res;
238}
239
240void AsanStackTrace::UncompressStack(AsanStackTrace *stack,
241 uint32_t *compressed, size_t size) {
242#if __WORDSIZE == 32
243 // Don't uncompress, just copy.
244 stack->size = 0;
245 for (size_t i = 0; i < size && i < kStackTraceMax; i++) {
246 if (!compressed[i]) break;
247 stack->size++;
248 stack->trace[i] = compressed[i];
249 }
250#else // 64 bits, uncompress
251 uintptr_t prev_pc = 0;
252 stack->size = 0;
253 for (size_t i = 0; i < size && stack->size < kStackTraceMax; i++) {
254 uint32_t x = compressed[i];
255 uintptr_t pc = 0;
256 if (x & (1U << 31)) {
257 // Printf("U co[%ld] offset: %x\n", i, x);
258 // this is an offset
259 int32_t offset = x;
260 offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend.
261 pc = prev_pc + offset;
262 CHECK(pc);
263 } else {
264 // CHECK(i + 1 < size);
265 if (i + 1 >= size) break;
266 uintptr_t hi = x;
267 uintptr_t lo = compressed[i+1];
268 // Printf("U co[%ld] hi/lo: %lx %lx\n", i, hi, lo);
269 i++;
270 pc = (hi << 32) | lo;
271 if (!pc) break;
272 }
273 // Printf("U pc[%ld] %lx\n", stack->size, pc);
274 stack->trace[stack->size++] = pc;
275 prev_pc = pc;
276 }
277#endif // __WORDSIZE
278}
279
280} // namespace __asan