1//===-- AbortWithPayloadFrameRecognizer.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#include "AbortWithPayloadFrameRecognizer.h"
10
11#include "lldb/Core/Value.h"
12#include "lldb/Target/ABI.h"
13#include "lldb/Target/Process.h"
14#include "lldb/Target/StackFrame.h"
15#include "lldb/Target/Target.h"
16#include "lldb/Target/Thread.h"
17#include "lldb/Utility/LLDBLog.h"
18#include "lldb/Utility/Log.h"
19#include "lldb/Utility/StructuredData.h"
20#include "lldb/ValueObject/ValueObjectConstResult.h"
21
22#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
23
24using namespace lldb;
25using namespace lldb_private;
26
27namespace lldb_private {
28void RegisterAbortWithPayloadFrameRecognizer(Process *process) {
29 // There are two user-level API's that this recognizer captures,
30 // abort_with_reason and abort_with_payload. But they both call the private
31 // __abort_with_payload, the abort_with_reason call fills in a null payload.
32 static ConstString module_name("libsystem_kernel.dylib");
33 static ConstString sym_name("__abort_with_payload");
34
35 if (!process)
36 return;
37
38 process->GetTarget().GetFrameRecognizerManager().AddRecognizer(
39 recognizer: std::make_shared<AbortWithPayloadFrameRecognizer>(), module: module_name,
40 symbols: sym_name, symbol_mangling: Mangled::NamePreference::ePreferDemangled,
41 /*first_instruction_only*/ false);
42}
43
44RecognizedStackFrameSP
45AbortWithPayloadFrameRecognizer::RecognizeFrame(lldb::StackFrameSP frame_sp) {
46 // We have two jobs:
47 // 1) to add the data passed to abort_with_payload to the
48 // ExtraCrashInformation dictionary.
49 // 2) To make up faux arguments for this frame.
50 static constexpr llvm::StringLiteral namespace_key("namespace");
51 static constexpr llvm::StringLiteral code_key("code");
52 static constexpr llvm::StringLiteral payload_addr_key("payload_addr");
53 static constexpr llvm::StringLiteral payload_size_key("payload_size");
54 static constexpr llvm::StringLiteral reason_key("reason");
55 static constexpr llvm::StringLiteral flags_key("flags");
56 static constexpr llvm::StringLiteral info_key("abort_with_payload");
57
58 Log *log = GetLog(mask: LLDBLog::SystemRuntime);
59
60 if (!frame_sp) {
61 LLDB_LOG(log, "abort_with_payload recognizer: invalid frame.");
62 return {};
63 }
64
65 Thread *thread = frame_sp->GetThread().get();
66 if (!thread) {
67 LLDB_LOG(log, "abort_with_payload recognizer: invalid thread.");
68 return {};
69 }
70
71 Process *process = thread->GetProcess().get();
72 if (!thread) {
73 LLDB_LOG(log, "abort_with_payload recognizer: invalid process.");
74 }
75
76 TypeSystemClangSP scratch_ts_sp =
77 ScratchTypeSystemClang::GetForTarget(target&: process->GetTarget());
78 if (!scratch_ts_sp) {
79 LLDB_LOG(log, "abort_with_payload recognizer: invalid scratch typesystem.");
80 return {};
81 }
82
83 // The abort_with_payload signature is:
84 // abort_with_payload(uint32_t reason_namespace, uint64_t reason_code,
85 // void* payload, uint32_t payload_size,
86 // const char* reason_string, uint64_t reason_flags);
87
88 ValueList arg_values;
89 Value input_value_32;
90 Value input_value_64;
91 Value input_value_void_ptr;
92 Value input_value_char_ptr;
93
94 CompilerType clang_void_ptr_type =
95 scratch_ts_sp->GetBasicType(type: eBasicTypeVoid).GetPointerType();
96 CompilerType clang_char_ptr_type =
97 scratch_ts_sp->GetBasicType(type: eBasicTypeChar).GetPointerType();
98 CompilerType clang_uint64_type =
99 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(encoding: lldb::eEncodingUint,
100 bit_size: 64);
101 CompilerType clang_uint32_type =
102 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(encoding: lldb::eEncodingUint,
103 bit_size: 32);
104 CompilerType clang_char_star_type =
105 scratch_ts_sp->GetBuiltinTypeForEncodingAndBitSize(encoding: lldb::eEncodingUint,
106 bit_size: 64);
107
108 input_value_32.SetValueType(Value::ValueType::Scalar);
109 input_value_32.SetCompilerType(clang_uint32_type);
110 input_value_64.SetValueType(Value::ValueType::Scalar);
111 input_value_64.SetCompilerType(clang_uint64_type);
112 input_value_void_ptr.SetValueType(Value::ValueType::Scalar);
113 input_value_void_ptr.SetCompilerType(clang_void_ptr_type);
114 input_value_char_ptr.SetValueType(Value::ValueType::Scalar);
115 input_value_char_ptr.SetCompilerType(clang_char_ptr_type);
116
117 arg_values.PushValue(value: input_value_32);
118 arg_values.PushValue(value: input_value_64);
119 arg_values.PushValue(value: input_value_void_ptr);
120 arg_values.PushValue(value: input_value_32);
121 arg_values.PushValue(value: input_value_char_ptr);
122 arg_values.PushValue(value: input_value_64);
123
124 lldb::ABISP abi_sp = process->GetABI();
125 bool success = abi_sp->GetArgumentValues(thread&: *thread, values&: arg_values);
126 if (!success)
127 return {};
128
129 Value *cur_value;
130 StackFrame *frame = frame_sp.get();
131 ValueObjectListSP arguments_sp = ValueObjectListSP(new ValueObjectList());
132
133 auto add_to_arguments = [&](llvm::StringRef name, Value *value,
134 bool dynamic) {
135 ValueObjectSP cur_valobj_sp =
136 ValueObjectConstResult::Create(exe_scope: frame, value&: *value, name: ConstString(name));
137 cur_valobj_sp = ValueObjectRecognizerSynthesizedValue::Create(
138 parent&: *cur_valobj_sp, type: eValueTypeVariableArgument);
139 ValueObjectSP dyn_valobj_sp;
140 if (dynamic) {
141 dyn_valobj_sp = cur_valobj_sp->GetDynamicValue(valueType: eDynamicDontRunTarget);
142 if (dyn_valobj_sp)
143 cur_valobj_sp = dyn_valobj_sp;
144 }
145 arguments_sp->Append(val_obj_sp: cur_valobj_sp);
146 };
147
148 // Decode the arg_values:
149
150 uint32_t namespace_val = 0;
151 cur_value = arg_values.GetValueAtIndex(idx: 0);
152 add_to_arguments(namespace_key, cur_value, false);
153 namespace_val = cur_value->GetScalar().UInt(fail_value: namespace_val);
154
155 uint32_t code_val = 0;
156 cur_value = arg_values.GetValueAtIndex(idx: 1);
157 add_to_arguments(code_key, cur_value, false);
158 code_val = cur_value->GetScalar().UInt(fail_value: code_val);
159
160 lldb::addr_t payload_addr = LLDB_INVALID_ADDRESS;
161 cur_value = arg_values.GetValueAtIndex(idx: 2);
162 add_to_arguments(payload_addr_key, cur_value, true);
163 payload_addr = cur_value->GetScalar().ULongLong(fail_value: payload_addr);
164
165 uint32_t payload_size = 0;
166 cur_value = arg_values.GetValueAtIndex(idx: 3);
167 add_to_arguments(payload_size_key, cur_value, false);
168 payload_size = cur_value->GetScalar().UInt(fail_value: payload_size);
169
170 lldb::addr_t reason_addr = LLDB_INVALID_ADDRESS;
171 cur_value = arg_values.GetValueAtIndex(idx: 4);
172 add_to_arguments(reason_key, cur_value, false);
173 reason_addr = cur_value->GetScalar().ULongLong(fail_value: payload_addr);
174
175 // For the reason string, we want the string not the address, so fetch that.
176 std::string reason_string;
177 Status error;
178 process->ReadCStringFromMemory(vm_addr: reason_addr, out_str&: reason_string, error);
179 if (error.Fail()) {
180 // Even if we couldn't read the string, return the other data.
181 LLDB_LOG(log, "Couldn't fetch reason string: {0}.", error);
182 reason_string = "<error fetching reason string>";
183 }
184
185 uint32_t flags_val = 0;
186 cur_value = arg_values.GetValueAtIndex(idx: 5);
187 add_to_arguments(flags_key, cur_value, false);
188 flags_val = cur_value->GetScalar().UInt(fail_value: flags_val);
189
190 // Okay, we've gotten all the argument values, now put them in a
191 // StructuredData, and add that to the Process ExtraCrashInformation:
192 StructuredData::DictionarySP abort_dict_sp(new StructuredData::Dictionary());
193 abort_dict_sp->AddIntegerItem(key: namespace_key, value: namespace_val);
194 abort_dict_sp->AddIntegerItem(key: code_key, value: code_val);
195 abort_dict_sp->AddIntegerItem(key: payload_addr_key, value: payload_addr);
196 abort_dict_sp->AddIntegerItem(key: payload_size_key, value: payload_size);
197 abort_dict_sp->AddStringItem(key: reason_key, value: reason_string);
198 abort_dict_sp->AddIntegerItem(key: flags_key, value: flags_val);
199
200 // This will overwrite the abort_with_payload information in the dictionary
201 // already. But we can only crash on abort_with_payload once, so that
202 // shouldn't matter.
203 process->GetExtendedCrashInfoDict()->AddItem(key: info_key, value_sp: abort_dict_sp);
204
205 return RecognizedStackFrameSP(
206 new AbortWithPayloadRecognizedStackFrame(frame_sp, arguments_sp));
207}
208
209AbortWithPayloadRecognizedStackFrame::AbortWithPayloadRecognizedStackFrame(
210 lldb::StackFrameSP &frame_sp, ValueObjectListSP &args_sp)
211 : RecognizedStackFrame() {
212 m_arguments = args_sp;
213 m_stop_desc = "abort with payload or reason";
214}
215
216} // namespace lldb_private
217

source code of lldb/source/Plugins/SystemRuntime/MacOSX/AbortWithPayloadFrameRecognizer.cpp