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 void MaybeBuildIdToBuffer(const AddressInfo &info, bool PrefixSpace,
108                                 InternalScopedString *buffer) {
109  if (info.uuid_size) {
110    if (PrefixSpace)
111      buffer->append(" ");
112    buffer->append("(BuildId: ");
113    for (uptr i = 0; i < info.uuid_size; ++i) {
114      buffer->append("%02x", info.uuid[i]);
115    }
116    buffer->append(")");
117  }
118}
119
120static const char kDefaultFormat[] = "    #%n %p %F %L";
121
122void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
123                 uptr address, const AddressInfo *info, bool vs_style,
124                 const char *strip_path_prefix, const char *strip_func_prefix) {
125  // info will be null in the case where symbolization is not needed for the
126  // given format. This ensures that the code below will get a hard failure
127  // rather than print incorrect information in case RenderNeedsSymbolization
128  // ever ends up out of sync with this function. If non-null, the addresses
129  // should match.
130  CHECK(!info || address == info->address);
131  if (0 == internal_strcmp(format, "DEFAULT"))
132    format = kDefaultFormat;
133  for (const char *p = format; *p != '\0'; p++) {
134    if (*p != '%') {
135      buffer->append("%c", *p);
136      continue;
137    }
138    p++;
139    switch (*p) {
140    case '%':
141      buffer->append("%%");
142      break;
143    // Frame number and all fields of AddressInfo structure.
144    case 'n':
145      buffer->append("%u", frame_no);
146      break;
147    case 'p':
148      buffer->append("0x%zx", address);
149      break;
150    case 'm':
151      buffer->append("%s", StripPathPrefix(info->module, strip_path_prefix));
152      break;
153    case 'o':
154      buffer->append("0x%zx", info->module_offset);
155      break;
156    case 'b':
157      MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/false, buffer);
158      break;
159    case 'f':
160      buffer->append("%s", DemangleFunctionName(StripFunctionName(
161                               info->function, strip_func_prefix)));
162      break;
163    case 'q':
164      buffer->append("0x%zx", info->function_offset != AddressInfo::kUnknown
165                                  ? info->function_offset
166                                  : 0x0);
167      break;
168    case 's':
169      buffer->append("%s", StripPathPrefix(info->file, strip_path_prefix));
170      break;
171    case 'l':
172      buffer->append("%d", info->line);
173      break;
174    case 'c':
175      buffer->append("%d", info->column);
176      break;
177    // Smarter special cases.
178    case 'F':
179      // Function name and offset, if file is unknown.
180      if (info->function) {
181        buffer->append("in %s", DemangleFunctionName(StripFunctionName(
182                                    info->function, strip_func_prefix)));
183        if (!info->file && info->function_offset != AddressInfo::kUnknown)
184          buffer->append("+0x%zx", info->function_offset);
185      }
186      break;
187    case 'S':
188      // File/line information.
189      RenderSourceLocation(buffer, info->file, info->line, info->column,
190                           vs_style, strip_path_prefix);
191      break;
192    case 'L':
193      // Source location, or module location.
194      if (info->file) {
195        RenderSourceLocation(buffer, info->file, info->line, info->column,
196                             vs_style, strip_path_prefix);
197      } else if (info->module) {
198        RenderModuleLocation(buffer, info->module, info->module_offset,
199                             info->module_arch, strip_path_prefix);
200
201        MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
202      } else {
203        buffer->append("(<unknown module>)");
204      }
205      break;
206    case 'M':
207      // Module basename and offset, or PC.
208      if (address & kExternalPCBit) {
209        // There PCs are not meaningful.
210      } else if (info->module) {
211        // Always strip the module name for %M.
212        RenderModuleLocation(buffer, StripModuleName(info->module),
213                             info->module_offset, info->module_arch, "");
214        MaybeBuildIdToBuffer(*info, /*PrefixSpace=*/true, buffer);
215      } else {
216        buffer->append("(%p)", (void *)address);
217      }
218      break;
219    default:
220      Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
221             (void *)p);
222      Die();
223    }
224  }
225}
226
227bool RenderNeedsSymbolization(const char *format) {
228  if (0 == internal_strcmp(format, "DEFAULT"))
229    format = kDefaultFormat;
230  for (const char *p = format; *p != '\0'; p++) {
231    if (*p != '%')
232      continue;
233    p++;
234    switch (*p) {
235      case '%':
236        break;
237      case 'n':
238        // frame_no
239        break;
240      case 'p':
241        // address
242        break;
243      default:
244        return true;
245    }
246  }
247  return false;
248}
249
250void RenderData(InternalScopedString *buffer, const char *format,
251                const DataInfo *DI, const char *strip_path_prefix) {
252  for (const char *p = format; *p != '\0'; p++) {
253    if (*p != '%') {
254      buffer->append("%c", *p);
255      continue;
256    }
257    p++;
258    switch (*p) {
259      case '%':
260        buffer->append("%%");
261        break;
262      case 's':
263        buffer->append("%s", StripPathPrefix(DI->file, strip_path_prefix));
264        break;
265      case 'l':
266        buffer->append("%zu", DI->line);
267        break;
268      case 'g':
269        buffer->append("%s", DI->name);
270        break;
271      default:
272        Report("Unsupported specifier in stack frame format: %c (%p)!\n", *p,
273               (void *)p);
274        Die();
275    }
276  }
277}
278
279#endif  // !SANITIZER_SYMBOLIZER_MARKUP
280
281void RenderSourceLocation(InternalScopedString *buffer, const char *file,
282                          int line, int column, bool vs_style,
283                          const char *strip_path_prefix) {
284  if (vs_style && line > 0) {
285    buffer->append("%s(%d", StripPathPrefix(file, strip_path_prefix), line);
286    if (column > 0)
287      buffer->append(",%d", column);
288    buffer->append(")");
289    return;
290  }
291
292  buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
293  if (line > 0) {
294    buffer->append(":%d", line);
295    if (column > 0)
296      buffer->append(":%d", column);
297  }
298}
299
300void RenderModuleLocation(InternalScopedString *buffer, const char *module,
301                          uptr offset, ModuleArch arch,
302                          const char *strip_path_prefix) {
303  buffer->append("(%s", StripPathPrefix(module, strip_path_prefix));
304  if (arch != kModuleArchUnknown) {
305    buffer->append(":%s", ModuleArchToString(arch));
306  }
307  buffer->append("+0x%zx)", offset);
308}
309
310} // namespace __sanitizer
311