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