1 //===-- sanitizer_common.cpp ----------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file is shared between sanitizers' run-time libraries. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "sanitizer_stacktrace_printer.h" 14 15 #include "sanitizer_common.h" 16 #include "sanitizer_file.h" 17 #include "sanitizer_flags.h" 18 #include "sanitizer_fuchsia.h" 19 #include "sanitizer_symbolizer_markup.h" 20 21 namespace __sanitizer { 22 23 StackTracePrinter *StackTracePrinter::GetOrInit() { 24 static StackTracePrinter *stacktrace_printer; 25 static StaticSpinMutex init_mu; 26 SpinMutexLock l(&init_mu); 27 if (stacktrace_printer) 28 return stacktrace_printer; 29 30 stacktrace_printer = StackTracePrinter::NewStackTracePrinter(); 31 32 CHECK(stacktrace_printer); 33 return stacktrace_printer; 34 } 35 36 const char *StackTracePrinter::StripFunctionName(const char *function) { 37 if (!common_flags()->demangle) 38 return function; 39 if (!function) 40 return nullptr; 41 auto try_strip = [function](const char *prefix) -> const char * { 42 const uptr prefix_len = internal_strlen(prefix); 43 if (!internal_strncmp(function, prefix, prefix_len)) 44 return function + prefix_len; 45 return nullptr; 46 }; 47 if (SANITIZER_APPLE) { 48 if (const char *s = try_strip("wrap_")) 49 return s; 50 } else if (SANITIZER_WINDOWS) { 51 if (const char *s = try_strip("__asan_wrap_")) 52 return s; 53 } else { 54 if (const char *s = try_strip("___interceptor_")) 55 return s; 56 if (const char *s = try_strip("__interceptor_")) 57 return s; 58 } 59 return function; 60 } 61 62 // sanitizer_symbolizer_markup.cpp implements these differently. 63 #if !SANITIZER_SYMBOLIZER_MARKUP 64 65 StackTracePrinter *StackTracePrinter::NewStackTracePrinter() { 66 if (common_flags()->enable_symbolizer_markup) 67 return new (GetGlobalLowLevelAllocator()) MarkupStackTracePrinter(); 68 69 return new (GetGlobalLowLevelAllocator()) FormattedStackTracePrinter(); 70 } 71 72 static const char *DemangleFunctionName(const char *function) { 73 if (!common_flags()->demangle) 74 return function; 75 if (!function) 76 return nullptr; 77 78 // NetBSD uses indirection for old threading functions for historical reasons 79 // The mangled names are internal implementation detail and should not be 80 // exposed even in backtraces. 81 #if SANITIZER_NETBSD 82 if (!internal_strcmp(function, "__libc_mutex_init")) 83 return "pthread_mutex_init"; 84 if (!internal_strcmp(function, "__libc_mutex_lock")) 85 return "pthread_mutex_lock"; 86 if (!internal_strcmp(function, "__libc_mutex_trylock")) 87 return "pthread_mutex_trylock"; 88 if (!internal_strcmp(function, "__libc_mutex_unlock")) 89 return "pthread_mutex_unlock"; 90 if (!internal_strcmp(function, "__libc_mutex_destroy")) 91 return "pthread_mutex_destroy"; 92 if (!internal_strcmp(function, "__libc_mutexattr_init")) 93 return "pthread_mutexattr_init"; 94 if (!internal_strcmp(function, "__libc_mutexattr_settype")) 95 return "pthread_mutexattr_settype"; 96 if (!internal_strcmp(function, "__libc_mutexattr_destroy")) 97 return "pthread_mutexattr_destroy"; 98 if (!internal_strcmp(function, "__libc_cond_init")) 99 return "pthread_cond_init"; 100 if (!internal_strcmp(function, "__libc_cond_signal")) 101 return "pthread_cond_signal"; 102 if (!internal_strcmp(function, "__libc_cond_broadcast")) 103 return "pthread_cond_broadcast"; 104 if (!internal_strcmp(function, "__libc_cond_wait")) 105 return "pthread_cond_wait"; 106 if (!internal_strcmp(function, "__libc_cond_timedwait")) 107 return "pthread_cond_timedwait"; 108 if (!internal_strcmp(function, "__libc_cond_destroy")) 109 return "pthread_cond_destroy"; 110 if (!internal_strcmp(function, "__libc_rwlock_init")) 111 return "pthread_rwlock_init"; 112 if (!internal_strcmp(function, "__libc_rwlock_rdlock")) 113 return "pthread_rwlock_rdlock"; 114 if (!internal_strcmp(function, "__libc_rwlock_wrlock")) 115 return "pthread_rwlock_wrlock"; 116 if (!internal_strcmp(function, "__libc_rwlock_tryrdlock")) 117 return "pthread_rwlock_tryrdlock"; 118 if (!internal_strcmp(function, "__libc_rwlock_trywrlock")) 119 return "pthread_rwlock_trywrlock"; 120 if (!internal_strcmp(function, "__libc_rwlock_unlock")) 121 return "pthread_rwlock_unlock"; 122 if (!internal_strcmp(function, "__libc_rwlock_destroy")) 123 return "pthread_rwlock_destroy"; 124 if (!internal_strcmp(function, "__libc_thr_keycreate")) 125 return "pthread_key_create"; 126 if (!internal_strcmp(function, "__libc_thr_setspecific")) 127 return "pthread_setspecific"; 128 if (!internal_strcmp(function, "__libc_thr_getspecific")) 129 return "pthread_getspecific"; 130 if (!internal_strcmp(function, "__libc_thr_keydelete")) 131 return "pthread_key_delete"; 132 if (!internal_strcmp(function, "__libc_thr_once")) 133 return "pthread_once"; 134 if (!internal_strcmp(function, "__libc_thr_self")) 135 return "pthread_self"; 136 if (!internal_strcmp(function, "__libc_thr_exit")) 137 return "pthread_exit"; 138 if (!internal_strcmp(function, "__libc_thr_setcancelstate")) 139 return "pthread_setcancelstate"; 140 if (!internal_strcmp(function, "__libc_thr_equal")) 141 return "pthread_equal"; 142 if (!internal_strcmp(function, "__libc_thr_curcpu")) 143 return "pthread_curcpu_np"; 144 if (!internal_strcmp(function, "__libc_thr_sigsetmask")) 145 return "pthread_sigmask"; 146 #endif 147 148 return function; 149 } 150 151 static void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace, 152 InternalScopedString *buffer) { 153 if (info.uuid_size) { 154 if (PrefixSpace) 155 buffer->AppendF(" "); 156 buffer->AppendF("(BuildId: "); 157 for (uptr i = 0; i < info.uuid_size; ++i) { 158 buffer->AppendF("%02x", info.uuid[i]); 159 } 160 buffer->AppendF(")"); 161 } 162 } 163 164 static const char kDefaultFormat[] = " #%n %p %F %L"; 165 166 void FormattedStackTracePrinter::RenderFrame(InternalScopedString *buffer, 167 const char *format, int frame_no, 168 uptr address, 169 const AddressInfo *info, 170 bool vs_style, 171 const char *strip_path_prefix) { 172 // info will be null in the case where symbolization is not needed for the 173 // given format. This ensures that the code below will get a hard failure 174 // rather than print incorrect information in case RenderNeedsSymbolization 175 // ever ends up out of sync with this function. If non-null, the addresses 176 // should match. 177 CHECK(!info || address == info->address); 178 if (0 == internal_strcmp(format, "DEFAULT")) 179 format = kDefaultFormat; 180 for (const char *p = format; *p != '\0'; p++) { 181 if (*p != '%') { 182 buffer->AppendF("%c", *p); 183 continue; 184 } 185 p++; 186 switch (*p) { 187 case '%': 188 buffer->Append("%"); 189 break; 190 // Frame number and all fields of AddressInfo structure. 191 case 'n': 192 buffer->AppendF("%u", frame_no); 193 break; 194 case 'p': 195 buffer->AppendF("0x%zx", address); 196 break; 197 case 'm': 198 buffer->AppendF("%s", StripPathPrefix(info->module, strip_path_prefix)); 199 break; 200 case 'o': 201 buffer->AppendF("0x%zx", info->module_offset); 202 break; 203 case 'b': 204 MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer); 205 break; 206 case 'f': 207 buffer->AppendF("%s", 208 DemangleFunctionName(StripFunctionName(info->function))); 209 break; 210 case 'q': 211 buffer->AppendF("0x%zx", info->function_offset != AddressInfo::kUnknown 212 ? info->function_offset 213 : 0x0); 214 break; 215 case 's': 216 buffer->AppendF("%s", StripPathPrefix(info->file, strip_path_prefix)); 217 break; 218 case 'l': 219 buffer->AppendF("%d", info->line); 220 break; 221 case 'c': 222 buffer->AppendF("%d", info->column); 223 break; 224 // Smarter special cases. 225 case 'F': 226 // Function name and offset, if file is unknown. 227 if (info->function) { 228 buffer->AppendF( 229 "in %s", DemangleFunctionName(StripFunctionName(info->function))); 230 if (!info->file && info->function_offset != AddressInfo::kUnknown) 231 buffer->AppendF("+0x%zx", info->function_offset); 232 } 233 break; 234 case 'S': 235 // File/line information. 236 RenderSourceLocation(buffer, info->file, info->line, info->column, 237 vs_style, strip_path_prefix); 238 break; 239 case 'L': 240 // Source location, or module location. 241 if (info->file) { 242 RenderSourceLocation(buffer, info->file, info->line, info->column, 243 vs_style, strip_path_prefix); 244 } else if (info->module) { 245 RenderModuleLocation(buffer, info->module, info->module_offset, 246 info->module_arch, strip_path_prefix); 247 248 #if !SANITIZER_APPLE 249 MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer); 250 #endif 251 } else { 252 buffer->AppendF("(<unknown module>)"); 253 } 254 break; 255 case 'M': 256 // Module basename and offset, or PC. 257 if (address & kExternalPCBit) { 258 // There PCs are not meaningful. 259 } else if (info->module) { 260 // Always strip the module name for %M. 261 RenderModuleLocation(buffer, StripModuleName(info->module), 262 info->module_offset, info->module_arch, ""); 263 #if !SANITIZER_APPLE 264 MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer); 265 #endif 266 } else { 267 buffer->AppendF("(%p)", (void *)address); 268 } 269 break; 270 default: 271 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p, 272 (void *)p); 273 Die(); 274 } 275 } 276 } 277 278 bool FormattedStackTracePrinter::RenderNeedsSymbolization(const char *format) { 279 if (0 == internal_strcmp(format, "DEFAULT")) 280 format = kDefaultFormat; 281 for (const char *p = format; *p != '\0'; p++) { 282 if (*p != '%') 283 continue; 284 p++; 285 switch (*p) { 286 case '%': 287 break; 288 case 'n': 289 // frame_no 290 break; 291 case 'p': 292 // address 293 break; 294 default: 295 return true; 296 } 297 } 298 return false; 299 } 300 301 void FormattedStackTracePrinter::RenderData(InternalScopedString *buffer, 302 const char *format, 303 const DataInfo *DI, 304 const char *strip_path_prefix) { 305 for (const char *p = format; *p != '\0'; p++) { 306 if (*p != '%') { 307 buffer->AppendF("%c", *p); 308 continue; 309 } 310 p++; 311 switch (*p) { 312 case '%': 313 buffer->Append("%"); 314 break; 315 case 's': 316 buffer->AppendF("%s", StripPathPrefix(DI->file, strip_path_prefix)); 317 break; 318 case 'l': 319 buffer->AppendF("%zu", DI->line); 320 break; 321 case 'g': 322 buffer->AppendF("%s", DI->name); 323 break; 324 default: 325 Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p, 326 (void *)p); 327 Die(); 328 } 329 } 330 } 331 332 #endif // !SANITIZER_SYMBOLIZER_MARKUP 333 334 void StackTracePrinter::RenderSourceLocation(InternalScopedString *buffer, 335 const char *file, int line, 336 int column, bool vs_style, 337 const char *strip_path_prefix) { 338 if (vs_style && line > 0) { 339 buffer->AppendF("%s(%d", StripPathPrefix(file, strip_path_prefix), line); 340 if (column > 0) 341 buffer->AppendF(",%d", column); 342 buffer->AppendF(")"); 343 return; 344 } 345 346 buffer->AppendF("%s", StripPathPrefix(file, strip_path_prefix)); 347 if (line > 0) { 348 buffer->AppendF(":%d", line); 349 if (column > 0) 350 buffer->AppendF(":%d", column); 351 } 352 } 353 354 void StackTracePrinter::RenderModuleLocation(InternalScopedString *buffer, 355 const char *module, uptr offset, 356 ModuleArch arch, 357 const char *strip_path_prefix) { 358 buffer->AppendF("(%s", StripPathPrefix(module, strip_path_prefix)); 359 if (arch != kModuleArchUnknown) { 360 buffer->AppendF(":%s", ModuleArchToString(arch)); 361 } 362 buffer->AppendF("+0x%zx)", offset); 363 } 364 365 } // namespace __sanitizer 366