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