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