[Lldb-commits] [lldb] [LLDB] FindLibCppStdFunctionCallableInfo improvements (PR #111892)

David Mentler via lldb-commits lldb-commits at lists.llvm.org
Fri Oct 11 11:50:18 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!");
----------------
mentlerd wrote:

What would be the ABI agnostic way of extracting the underlying function pointer? Or is it fine to assume that the target is using the Itanium ABI?

https://github.com/llvm/llvm-project/pull/111892


More information about the lldb-commits mailing list