[Lldb-commits] [lldb] [LLDB] FindLibCppStdFunctionCallableInfo improvements (PR #111892)
Michael Buch via lldb-commits
lldb-commits at lists.llvm.org
Fri Oct 11 00:50:42 PDT 2024
================
@@ -174,6 +174,233 @@ CPPLanguageRuntime::FindLibCppStdFunctionCallableInfo(
if (!valobj_sp)
return optional_info;
+ // std::function has many variants, try to disambiguate
+ ValueObjectSP func_as_base_ptr;
+ {
+ ValueObjectSP outer_f = valobj_sp->GetChildMemberWithName("__f_");
+
+ if (!outer_f)
+ return optional_info; // Unrecognized implementation
+
+ if (outer_f->IsPointerType()) {
+ // git: 3e519524c118651123eecf60c2bbc5d65ad9bac3
+ //
+ // class function<_Rp()> {
+ // aligned_storage<3*sizeof(void*)>::type __buf_;
+ // __base<_Rp>* __f_;
+ // }
+
+ func_as_base_ptr = std::move(outer_f);
+ } else if (auto inner_f = outer_f->GetChildMemberWithName("__f_")) {
+ // git: 050b064f15ee56ee0b42c9b957a3dd0f32532394
+ //
+ // class function<_Rp(_ArgTypes...)> {
+ // __value_func<_Rp(_ArgTypes...)> __f_;
+ // }
+ //
+ // class __value_func<_Rp(_ArgTypes...)> {
+ // aligned_storage<3 * sizeof(void*)>::type __buf_;
+ // __base<_Rp(_ArgTypes...)>* __f_;
+ // }
+
+ func_as_base_ptr = std::move(inner_f);
+ } else
+ return optional_info; // Unrecognized implementation
+ }
+
+ // __base<...> is a pure virtual class with an interface to create/copy/destroy/invoke
+ // the underlying value. This interface is implemented by partial specializations of the
+ // __func<_Fp, _Alloc, ...> template where _Fp is the wrapped functor object
+ Status status;
+ ValueObjectSP func_as_base = func_as_base_ptr->Dereference(status);
+ if (status.Fail())
+ return optional_info;
+
+ // First we'll try to extract the __func<...> template instantiation's type by looking up
+ // the declarations of the member function pointers in it's vtable
+ CompilerType func_type;
+ Address func_method_addr;
+ {
+ ValueObjectSP vtable = func_as_base->GetVTable();
+
+ llvm::Expected<uint32_t> num_entries = vtable->GetNumChildren();
+ if (num_entries.takeError())
+ return optional_info;
+
+ // __base is pure virtual, __func is final. All member function pointers are equally
+ // good candidates to find the enclosing class.
+ //
+ // In practice the first two vtable entries point to artificial destructors which the
+ // type system refuses to elaborate as their artificial specifications are not added
+ // to the enclosing class' declaration context. This causes various warnings, and dont
+ // get us any closer to the concrete type thus we skip them.
+ for (uint32_t idx = 2; idx < *num_entries; idx++) {
+ ValueObjectSP entry = vtable->GetChildAtIndex(idx);
+
+ // Points to a potentially interesting member function
+ addr_t mfunc_load_addr = entry->GetValueAsUnsigned(0);
+ if (!mfunc_load_addr)
+ continue;
+
+ if (!valobj_sp->GetTargetSP()->ResolveLoadAddress(mfunc_load_addr, func_method_addr))
+ continue;
+
+ Function* func = func_method_addr.CalculateSymbolContextFunction();
+ if (!func)
+ continue;
+
+ CompilerDeclContext mfunc_decl_ctx = func->GetDeclContext();
+ if (!mfunc_decl_ctx.IsClassMethod())
+ continue;
+
+ // Member functions are contained in their enclosing class' decl context
+ CompilerDeclContext mfunc_parent = mfunc_decl_ctx.GetDecl().GetDeclContext();
+ if (!mfunc_parent.IsValid())
+ continue;
+
+ func_type = mfunc_parent.GetDecl().GetType();
+ break;
+ }
+ }
+
+ CompilerType callable_type = func_type.GetTypeTemplateArgument(0);
+ if (!callable_type)
+ return optional_info;
+
+ // Now that the __func is a known type we can dig for the wrapped callable
+ ValueObjectSP callable;
+ {
+ // class __func<_Fp, _Alloc, _Rp(_ArgTypes...)> : __base<_Rp(_ArgTypes...)> {
+ // __alloc_func<_Fp, _Alloc, _Rp(_ArgTypes...)> __f_;
+ // }
+ //
+ // class __alloc_func<_Fp, _Ap, _Rp(_ArgTypes...)> {
+ // __compressed_pair<_Fp, _Ap> __f_;
+ // }
+ //
+ // class __compressed_pair : __compressed_pair_elem<_T1, 0>,
+ // __compressed_pair_elem<_T2, 1> {
+ // }
+ //
+ // struct __compressed_pair_elem {
+ // _Tp __value_;
+ // }
+ ValueObjectSP alloc_func = func_as_base->Cast(func_type);
+ if (!alloc_func)
+ return optional_info;
+
+ ValueObjectSP pair = alloc_func->GetChildAtNamePath({"__f_", "__f_"});
+ if (!pair)
+ return optional_info;
+
+ if (callable_type.IsRecordType() && callable_type.GetNumFields() == 0) {
+ // callable_type is an empty class, and has been optimized away! Serve a dummy
+ callable = valobj_sp->CreateValueObjectFromAddress("__value_",
+ pair->GetLoadAddress(),
+ pair->GetExecutionContextRef(),
+ callable_type);
+ } else {
+ ValueObjectSP elem0 = pair->GetChildAtIndex(0);
+ if (!elem0)
+ return optional_info;
+
+ callable = elem0->GetChildMemberWithName("__value_");
+ if (!callable)
+ return optional_info;
+ }
+ }
+
+ if (callable_type.IsFunctionPointerType()) {
+ addr_t target_load_addr = callable->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
+
+ ModuleSP mod = func_method_addr.CalculateSymbolContextModule();
+
+ Address callable_addr;
+ if (!mod->ResolveFileAddress(target_load_addr, callable_addr))
+ return optional_info;
+
+ SymbolContext sc;
+ mod->ResolveSymbolContextForAddress(callable_addr, eSymbolContextSymbol | eSymbolContextLineEntry, sc);
+
+ if (!sc.symbol)
+ return optional_info;
+
+ return LibCppStdFunctionCallableInfo {
+ .callable_symbol = *sc.symbol,
+ .callable_address = sc.symbol->GetAddress(),
+ .callable_line_entry = sc.line_entry,
+ .callable_case = LibCppStdFunctionCallableCase::FreeOrMemberFunction
+ };
+ } else if (callable_type.IsMemberFunctionPointerType()) {
+ // TODO: Member function's unsigned value comes back as invalid! I am guessing
+ // the ValueObject wants to let me know that this is not necessarily as simple
+ // as that.. I remember reading something in the Itanium ABI about member
+ // pointers taking up two pointers of space. Perhaps that's why it is not legal
+ // to read their underlying value raw?
+
+ printf("Curious!");
----------------
Michael137 wrote:
FYI, yes, it's represented as a pointer and possible offset: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#member-function-pointers
https://github.com/llvm/llvm-project/pull/111892
More information about the lldb-commits
mailing list