1 //===-- sanitizer_stacktrace_libcdep.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 AddressSanitizer and ThreadSanitizer
10 // run-time libraries.
11 //===----------------------------------------------------------------------===//
12
13 #include "sanitizer_common.h"
14 #include "sanitizer_placement_new.h"
15 #include "sanitizer_stacktrace.h"
16 #include "sanitizer_stacktrace_printer.h"
17 #include "sanitizer_symbolizer.h"
18
19 namespace __sanitizer {
20
21 namespace {
22
23 class StackTraceTextPrinter {
24 public:
StackTraceTextPrinter(const char * stack_trace_fmt,char frame_delimiter,InternalScopedString * output,InternalScopedString * dedup_token)25 StackTraceTextPrinter(const char *stack_trace_fmt, char frame_delimiter,
26 InternalScopedString *output,
27 InternalScopedString *dedup_token)
28 : stack_trace_fmt_(stack_trace_fmt),
29 frame_delimiter_(frame_delimiter),
30 output_(output),
31 dedup_token_(dedup_token),
32 symbolize_(StackTracePrinter::GetOrInit()->RenderNeedsSymbolization(
33 stack_trace_fmt)) {}
34
ProcessAddressFrames(uptr pc)35 bool ProcessAddressFrames(uptr pc) {
36 SymbolizedStackHolder symbolized_stack(
37 symbolize_ ? Symbolizer::GetOrInit()->SymbolizePC(pc)
38 : SymbolizedStack::New(pc));
39 const SymbolizedStack *frames = symbolized_stack.get();
40 if (!frames)
41 return false;
42
43 for (const SymbolizedStack *cur = frames; cur; cur = cur->next) {
44 uptr prev_len = output_->length();
45 StackTracePrinter::GetOrInit()->RenderFrame(
46 output_, stack_trace_fmt_, frame_num_++, cur->info.address,
47 symbolize_ ? &cur->info : nullptr, common_flags()->symbolize_vs_style,
48 common_flags()->strip_path_prefix);
49
50 if (prev_len != output_->length())
51 output_->AppendF("%c", frame_delimiter_);
52
53 ExtendDedupToken(cur);
54 }
55 return true;
56 }
57
58 private:
59 // Extend the dedup token by appending a new frame.
ExtendDedupToken(const SymbolizedStack * stack)60 void ExtendDedupToken(const SymbolizedStack *stack) {
61 if (!dedup_token_)
62 return;
63
64 if (dedup_frames_-- > 0) {
65 if (dedup_token_->length())
66 dedup_token_->Append("--");
67 if (stack->info.function)
68 dedup_token_->Append(stack->info.function);
69 }
70 }
71
72 const char *stack_trace_fmt_;
73 const char frame_delimiter_;
74 int dedup_frames_ = common_flags()->dedup_token_length;
75 uptr frame_num_ = 0;
76 InternalScopedString *output_;
77 InternalScopedString *dedup_token_;
78 const bool symbolize_ = false;
79 };
80
CopyStringToBuffer(const InternalScopedString & str,char * out_buf,uptr out_buf_size)81 static void CopyStringToBuffer(const InternalScopedString &str, char *out_buf,
82 uptr out_buf_size) {
83 if (!out_buf_size)
84 return;
85
86 CHECK_GT(out_buf_size, 0);
87 uptr copy_size = Min(str.length(), out_buf_size - 1);
88 internal_memcpy(out_buf, str.data(), copy_size);
89 out_buf[copy_size] = '\0';
90 }
91
92 } // namespace
93
PrintTo(InternalScopedString * output) const94 void StackTrace::PrintTo(InternalScopedString *output) const {
95 CHECK(output);
96
97 InternalScopedString dedup_token;
98 StackTraceTextPrinter printer(common_flags()->stack_trace_format, '\n',
99 output, &dedup_token);
100
101 if (trace == nullptr || size == 0) {
102 output->Append(" <empty stack>\n\n");
103 return;
104 }
105
106 for (uptr i = 0; i < size && trace[i]; i++) {
107 // PCs in stack traces are actually the return addresses, that is,
108 // addresses of the next instructions after the call.
109 uptr pc = GetPreviousInstructionPc(trace[i]);
110 CHECK(printer.ProcessAddressFrames(pc));
111 }
112
113 // Always add a trailing empty line after stack trace.
114 output->Append("\n");
115
116 // Append deduplication token, if non-empty.
117 if (dedup_token.length())
118 output->AppendF("DEDUP_TOKEN: %s\n", dedup_token.data());
119 }
120
PrintTo(char * out_buf,uptr out_buf_size) const121 uptr StackTrace::PrintTo(char *out_buf, uptr out_buf_size) const {
122 CHECK(out_buf);
123
124 InternalScopedString output;
125 PrintTo(&output);
126 CopyStringToBuffer(output, out_buf, out_buf_size);
127
128 return output.length();
129 }
130
Print() const131 void StackTrace::Print() const {
132 InternalScopedString output;
133 PrintTo(&output);
134 Printf("%s", output.data());
135 }
136
Unwind(u32 max_depth,uptr pc,uptr bp,void * context,uptr stack_top,uptr stack_bottom,bool request_fast_unwind)137 void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context,
138 uptr stack_top, uptr stack_bottom,
139 bool request_fast_unwind) {
140 // Ensures all call sites get what they requested.
141 CHECK_EQ(request_fast_unwind, WillUseFastUnwind(request_fast_unwind));
142 top_frame_bp = (max_depth > 0) ? bp : 0;
143 // Avoid doing any work for small max_depth.
144 if (max_depth == 0) {
145 size = 0;
146 return;
147 }
148 if (max_depth == 1) {
149 size = 1;
150 trace_buffer[0] = pc;
151 return;
152 }
153 if (!WillUseFastUnwind(request_fast_unwind)) {
154 #if SANITIZER_CAN_SLOW_UNWIND
155 if (context)
156 UnwindSlow(pc, context, max_depth);
157 else
158 UnwindSlow(pc, max_depth);
159 // If there are too few frames, the program may be built with
160 // -fno-asynchronous-unwind-tables. Fall back to fast unwinder below.
161 if (size > 2 || size >= max_depth)
162 return;
163 #else
164 UNREACHABLE("slow unwind requested but not available");
165 #endif
166 }
167 UnwindFast(pc, bp, stack_top, stack_bottom, max_depth);
168 }
169
GetModuleAndOffsetForPc(uptr pc,char * module_name,uptr module_name_len,uptr * pc_offset)170 int GetModuleAndOffsetForPc(uptr pc, char *module_name, uptr module_name_len,
171 uptr *pc_offset) {
172 const char *found_module_name = nullptr;
173 bool ok = Symbolizer::GetOrInit()->GetModuleNameAndOffsetForPC(
174 pc, &found_module_name, pc_offset);
175
176 if (!ok) return false;
177
178 if (module_name && module_name_len) {
179 internal_strncpy(module_name, found_module_name, module_name_len);
180 module_name[module_name_len - 1] = '\x00';
181 }
182 return true;
183 }
184
185 } // namespace __sanitizer
186 using namespace __sanitizer;
187
188 extern "C" {
189 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_symbolize_pc(uptr pc,const char * fmt,char * out_buf,uptr out_buf_size)190 void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf,
191 uptr out_buf_size) {
192 if (!out_buf_size)
193 return;
194
195 pc = StackTrace::GetPreviousInstructionPc(pc);
196
197 InternalScopedString output;
198 StackTraceTextPrinter printer(fmt, '\0', &output, nullptr);
199 if (!printer.ProcessAddressFrames(pc)) {
200 output.clear();
201 output.Append("<can't symbolize>");
202 }
203 CopyStringToBuffer(output, out_buf, out_buf_size);
204 }
205
206 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_symbolize_global(uptr data_addr,const char * fmt,char * out_buf,uptr out_buf_size)207 void __sanitizer_symbolize_global(uptr data_addr, const char *fmt,
208 char *out_buf, uptr out_buf_size) {
209 if (!out_buf_size) return;
210 out_buf[0] = 0;
211 DataInfo DI;
212 if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return;
213 InternalScopedString data_desc;
214 StackTracePrinter::GetOrInit()->RenderData(&data_desc, fmt, &DI,
215 common_flags()->strip_path_prefix);
216 internal_strncpy(out_buf, data_desc.data(), out_buf_size);
217 out_buf[out_buf_size - 1] = 0;
218 }
219
220 SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_get_module_and_offset_for_pc(void * pc,char * module_name,uptr module_name_len,void ** pc_offset)221 int __sanitizer_get_module_and_offset_for_pc(void *pc, char *module_name,
222 uptr module_name_len,
223 void **pc_offset) {
224 return __sanitizer::GetModuleAndOffsetForPc(
225 reinterpret_cast<uptr>(pc), module_name, module_name_len,
226 reinterpret_cast<uptr *>(pc_offset));
227 }
228 } // extern "C"
229