29#include "clang/CodeGen/ModuleBuilder.h"
39constexpr llvm::StringLiteral
41constexpr llvm::StringLiteral
44constexpr std::array<llvm::StringLiteral, 2>
49#define SOFT_TRAP_CATEGORY_PREFIX "Soft "
50#define SOFT_TRAP_FALLBACK_CATEGORY \
51 SOFT_TRAP_CATEGORY_PREFIX "Bounds check failed"
54 std::pair<std::optional<std::string>, std::optional<uint32_t>>;
64 std::optional<uint32_t>
86 bool &warning_emitted_for_failure);
90 bool &warning_emitted_for_failure);
101 bool warning_emitted_for_failure =
false;
102 auto [MaybeDescription, MaybeSuggestedStackIndex] =
104 if (MaybeDescription)
107 LLDB_LOG(log_category,
"failed to compute description");
109 if (MaybeSuggestedStackIndex)
110 m_value = MaybeSuggestedStackIndex.value();
112 LLDB_LOG(log_category,
"failed to compute suggested stack index");
116 if ((!MaybeDescription.has_value()) && !warning_emitted_for_failure) {
119 thread_sp->GetProcess()->GetTarget().GetDebugger().GetID();
121 "specific BoundsSafety trap reason could not be computed",
127 "computed InstrumentationBoundsSafetyStopInfo: stack index: {0}, "
128 "description:\"{1}\"",
133template <
typename T,
typename... ArgTys>
139template <
typename... ArgTys>
146 bool &warning_emitted_for_failure) {
150 return LogFailedCSI(
"failed to get thread while stopped");
153 thread_sp->GetProcess()->GetTarget().GetDebugger().GetID();
155 StackFrameSP parent_sf = thread_sp->GetStackFrameAtIndex(1);
157 return LogFailedCSI(
"got nullptr when fetching stackframe at index 1");
159 if (parent_sf->HasDebugInformation()) {
161 "frame {0} has debug info so trying to compute "
162 "BoundsSafety stop info from debug info",
163 parent_sf->GetFrameIndex());
165 parent_sf, debugger_id, warning_emitted_for_failure);
171 "frame {0} has no debug info so trying to compute "
172 "BoundsSafety stop info from registers",
173 parent_sf->GetFrameIndex());
175 thread_sp, debugger_id, warning_emitted_for_failure);
181 bool &warning_emitted_for_failure) {
191 const char *TrapReasonFuncName = parent_sf->GetFunctionName();
193 auto MaybeTrapReason =
194 clang::CodeGen::DemangleTrapReasonInDebugInfo(TrapReasonFuncName);
195 if (!MaybeTrapReason.has_value())
197 "clang::CodeGen::DemangleTrapReasonInDebugInfo(\"{0}\") call failed",
200 llvm::StringRef category = MaybeTrapReason.value().first;
201 llvm::StringRef message = MaybeTrapReason.value().second;
204 std::string stop_reason;
205 llvm::raw_string_ostream ss(stop_reason);
207 if (category.empty())
208 ss <<
"<empty category>";
211 if (message.empty()) {
214 "specific BoundsSafety trap reason is not "
215 "available because the compiler omitted it from the debug info",
218 ss <<
": " << message;
223 return std::make_pair(stop_reason, parent_sf->GetFrameIndex() + 1);
229 bool &warning_emitted_for_failure) {
231 StackFrameSP softtrap_sf = thread_sp->GetStackFrameAtIndex(0);
233 return LogFailedCSI(
"got nullptr when fetching stackframe at index 0");
234 llvm::StringRef trap_reason_func_name = softtrap_sf->GetFunctionName();
254 llvm::raw_string_ostream ss(
warning);
255 ss <<
"specific BoundsSafety trap reason is not available because debug "
256 "info is missing on the caller of '"
259 warning_emitted_for_failure =
true;
266 assert(0 &&
"hit breakpoint for unexpected function name");
268 "unexpected function name. Expected \"{0}\" but got \"{1}\"",
281 ProcessSP process = thread_sp->GetProcess();
285 switch (process->GetTarget().GetArchitecture().GetCore()) {
293 llvm::raw_string_ostream ss(
warning);
294 ss <<
"specific BoundsSafety trap reason cannot be inferred on x86 when "
298 warning_emitted_for_failure =
true;
310 "failed to get register info for LLDB_REGNUM_GENERIC_ARG1");
312 if (!rc->ReadRegister(arg0_info, reg_value))
316 return LogFailedCSI(
"failed to read register {0} as a UInt64",
319 if (reg_value_as_int == 0) {
323 "specific BoundsSafety trap reason cannot be inferred because the "
324 "compiler omitted the reason",
326 warning_emitted_for_failure =
true;
332 std::string out_string;
334 thread_sp->GetProcess()->ReadCStringFromMemory(reg_value_as_int, out_string,
336 if (error_status.
Fail())
337 return LogFailedCSI(
"failed to read C string from address {0}",
338 (
void *)reg_value_as_int);
341 "read C string from {0} found in register {1}: \"{2}\"",
342 (
void *)reg_value_as_int, arg0_info->
name, out_string.c_str());
343 std::string stop_reason;
344 llvm::raw_string_ostream SS(stop_reason);
346 if (!stop_reason.empty()) {
347 SS <<
": " << out_string;
351 return {stop_reason, 0};
367 "BoundsSafety instrumentation runtime plugin.",
392 if (module_sp->FindFirstSymbolWithNameAndType(test_sym,
394 LLDB_LOG(log_category,
"found \"{0}\" in {1}",
396 module_sp->GetFileSpec().GetPath());
401 "did not find BoundsSafety soft trap functions in module {0}",
402 module_sp->GetFileSpec().GetPath());
409 assert(baton &&
"null baton");
422 "failed to get thread from StoppointCallbackContext");
426 "process from baton ({0}) and StoppointCallbackContext ({1}) do "
428 (
void *)process_sp.get(),
431 if (process_sp->GetModIDRef().IsLastResumeForUserExpression())
436 thread_sp->SetStopInfo(
438 CreateInstrumentationBoundsSafetyStopInfo(*thread_sp));
450 std::vector<std::string> breakpoints;
452 breakpoints.emplace_back(breakpoint_func);
454 BreakpointSP breakpoint = process_sp->GetTarget().CreateBreakpoint(
456 nullptr, breakpoints, eFunctionNameTypeFull,
466 if (!breakpoint->HasResolvedLocations()) {
467 assert(0 &&
"breakpoint has no resolved locations");
468 process_sp->GetTarget().RemoveBreakpointByID(breakpoint->GetID());
470 "breakpoint {0} for BoundsSafety soft traps did not resolve to "
472 breakpoint->GetID());
477 breakpoint->SetCallback(
480 breakpoint->SetBreakpointKind(
"bounds-safety-soft-trap");
483 "created breakpoint {0} for BoundsSafety soft traps",
484 breakpoint->GetID());
499 "{0}removed breakpoint {1} for BoundsSafety soft traps",
500 success ?
"" :
"failed to ",
503 LLDB_LOG(log_category,
"no process available during Deactivate()");
static llvm::raw_ostream & warning(Stream &strm)
ComputedStopInfo LogFailedCSI(ArgTys &&...Args)
#define SOFT_TRAP_FALLBACK_CATEGORY
std::pair< std::optional< std::string >, std::optional< uint32_t > > ComputedStopInfo
#define SOFT_TRAP_CATEGORY_PREFIX
constexpr llvm::StringLiteral BoundsSafetySoftTrapMinimal("__bounds_safety_soft_trap")
T LogBeforeReturn(ArgTys &&...Args)
constexpr std::array< llvm::StringLiteral, 2 > getBoundsSafetySoftTrapRuntimeFuncs()
constexpr llvm::StringLiteral BoundsSafetySoftTrapStr("__bounds_safety_soft_trap_s")
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
#define LLDB_PLUGIN_DEFINE(PluginName)
~InstrumentationBoundsSafetyStopInfo() override=default
ComputedStopInfo ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo(ThreadSP thread_sp, lldb::user_id_t debugger_id, bool &warning_emitted_for_failure)
const char * GetDescription() override
ComputedStopInfo ComputeStopReasonAndSuggestedStackFrameWithDebugInfo(lldb::StackFrameSP parent_sf, lldb::user_id_t debugger_id, bool &warning_emitted_for_failure)
std::optional< uint32_t > GetSuggestedStackFrameIndex(bool inlined_stack) override
This gives the StopInfo a chance to suggest a stack frame to select.
lldb::StopReason GetStopReason() const override
InstrumentationBoundsSafetyStopInfo(Thread &thread)
bool DoShouldNotify(Event *event_ptr) override
static lldb::StopInfoSP CreateInstrumentationBoundsSafetyStopInfo(Thread &thread)
ComputedStopInfo ComputeStopReasonAndSuggestedStackFrame(bool &warning_emitted_for_failure)
A command line argument class.
A uniqued constant string class.
const char * AsCString(const char *value_if_empty=nullptr) const
Get the string value as a C string.
static void ReportWarning(std::string message, std::optional< lldb::user_id_t > debugger_id=std::nullopt, std::once_flag *once=nullptr)
Report warning events.
lldb::ThreadSP GetThreadSP() const
Get accessor that creates a strong reference from the weak thread reference contained in this object.
lldb::ProcessSP GetProcessSP() const
Get accessor that creates a strong reference from the weak process reference contained in this object...
~InstrumentationRuntimeBoundsSafety() override
static lldb::InstrumentationRuntimeSP CreateInstance(const lldb::ProcessSP &process_sp)
static bool NotifyBreakpointHit(void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id)
static lldb::InstrumentationRuntimeType GetTypeStatic()
static llvm::StringRef GetPluginNameStatic()
InstrumentationRuntimeBoundsSafety(const lldb::ProcessSP &process_sp)
bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override
Check whether module_sp corresponds to a valid runtime library.
const RegularExpression & GetPatternForRuntimeLibrary() override
Return a regular expression which can be used to identify a valid version of the runtime library.
void Activate() override
Register a breakpoint in the runtime library and perform any other necessary initialization.
void SetBreakpointID(lldb::user_id_t ID)
void SetActive(bool IsActive)
lldb::user_id_t GetBreakpointID() const
lldb::ProcessSP GetProcessSP()
static bool RegisterPlugin(llvm::StringRef name, llvm::StringRef description, ABICreateInstance create_callback)
static bool UnregisterPlugin(ABICreateInstance create_callback)
uint64_t GetAsUInt64(uint64_t fail_value=UINT64_MAX, bool *success_ptr=nullptr) const
bool Fail() const
Test for error condition.
std::string m_description
StructuredData::ObjectSP m_extended_info
lldb::ThreadSP GetThread() const
StopInfo(Thread &thread, uint64_t value)
General Outline: When we hit a breakpoint we need to package up whatever information is needed to eva...
ExecutionContextRef exe_ctx_ref
#define LLDB_INVALID_BREAK_ID
#define LLDB_REGNUM_GENERIC_ARG1
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
std::shared_ptr< lldb_private::StackFrame > StackFrameSP
std::shared_ptr< lldb_private::Thread > ThreadSP
@ eLanguageTypeUnknown
Unknown or invalid language value.
std::shared_ptr< lldb_private::Breakpoint > BreakpointSP
std::shared_ptr< lldb_private::Process > ProcessSP
InstrumentationRuntimeType
@ eInstrumentationRuntimeTypeBoundsSafety
std::shared_ptr< lldb_private::StopInfo > StopInfoSP
StopReason
Thread stop reasons.
@ eStopReasonInstrumentation
std::shared_ptr< lldb_private::RegisterContext > RegisterContextSP
std::shared_ptr< lldb_private::InstrumentationRuntime > InstrumentationRuntimeSP
std::shared_ptr< lldb_private::Module > ModuleSP
@ eRegisterKindGeneric
insn ptr reg, stack ptr reg, etc not specific to any particular target
Every register is described in detail including its name, alternate name (optional),...
const char * name
Name of this register, can't be NULL.