asan_stack.cc revision c6f2223a8772262e5e682403f2d57f0b465a98fc
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_stack.h" 17#include "asan_thread.h" 18#include "asan_thread_registry.h" 19 20#include <string.h> 21 22#if ASAN_USE_SYSINFO == 1 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#if ASAN_USE_SYSINFO == 1 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 281