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