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