[Lldb-commits] [lldb] r330214 - Change PlatformPosix::DoLoadImage to use a UtilityFunction.

Jim Ingham via lldb-commits lldb-commits at lists.llvm.org
Tue Apr 17 13:44:48 PDT 2018


Author: jingham
Date: Tue Apr 17 13:44:47 2018
New Revision: 330214

URL: http://llvm.org/viewvc/llvm-project?rev=330214&view=rev
Log:
Change PlatformPosix::DoLoadImage to use a UtilityFunction.

That way we won't have to compile a new expression every time we want
dlopen a library.

<rdar://problem/32626584> 

Differential Revision: https://reviews.llvm.org/D45703

Modified:
    lldb/trunk/include/lldb/Target/Process.h
    lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
    lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h
    lldb/trunk/source/Target/Process.cpp

Modified: lldb/trunk/include/lldb/Target/Process.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/include/lldb/Target/Process.h?rev=330214&r1=330213&r2=330214&view=diff
==============================================================================
--- lldb/trunk/include/lldb/Target/Process.h (original)
+++ lldb/trunk/include/lldb/Target/Process.h Tue Apr 17 13:44:47 2018
@@ -808,6 +808,54 @@ public:
   }
 
   //------------------------------------------------------------------
+  // FUTURE WORK: {Set,Get}LoadImageUtilityFunction are the first use we've
+  // had of having other plugins cache data in the Process.  This is handy for
+  // long-living plugins - like the Platform - which manage interactions whose 
+  // lifetime is governed by the Process lifetime.  If we find we need to do 
+  // this more often, we should construct a general solution to the problem.
+  // The consensus suggestion was that we have a token based registry in the
+  // Process.
+  // Some undecided questions are 
+  // (1) who manages the tokens.  It's probably best that you add the element 
+  // and get back a token that represents it.  That will avoid collisions.  But
+  // there may be some utility in the registerer controlling the token?
+  // (2) whether the thing added should be simply owned by Process, and
+  // just go away when it does
+  // (3) whether the registree should be notified of the Process' demise.
+  //
+  // We are postponing designing this till we have at least a second use case.
+  //------------------------------------------------------------------
+  //------------------------------------------------------------------
+  /// Set the cached UtilityFunction that assists in loading binary
+  /// images into the process.
+  ///
+  /// This UtilityFunction is maintained in the Process since the Platforms
+  /// don't track the lifespan of the Targets/Processes that use them.  
+  /// But it is not intended to be comprehended by the Process, it's up to the
+  /// Platform that set it to do it right.
+  ///
+  /// @param[in] utility_func_up
+  ///     The incoming utility_function.  The process will manage the function's
+  ///     lifetime.
+  ///
+  //------------------------------------------------------------------
+  void SetLoadImageUtilityFunction(std::unique_ptr<UtilityFunction> 
+                                   utility_func_up);
+  
+  //------------------------------------------------------------------
+  /// Get the cached UtilityFunction that assists in loading binary
+  /// images into the process.
+  ///
+  /// @param[in] platform
+  ///     The platform fetching the UtilityFunction.
+  /// 
+  /// @return
+  ///     The cached utility function or null if the platform is not the
+  ///     same as the target's platform.
+  //------------------------------------------------------------------
+  UtilityFunction *GetLoadImageUtilityFunction(Platform *platform);
+
+  //------------------------------------------------------------------
   /// Get the dynamic loader plug-in for this process.
   ///
   /// The default action is to let the DynamicLoader plug-ins check
@@ -3132,6 +3180,8 @@ protected:
   StructuredDataPluginMap m_structured_data_plugin_map;
 
   enum { eCanJITDontKnow = 0, eCanJITYes, eCanJITNo } m_can_jit;
+  
+  std::unique_ptr<UtilityFunction> m_dlopen_utility_func_up;
 
   size_t RemoveBreakpointOpcodesFromBuffer(lldb::addr_t addr, size_t size,
                                            uint8_t *buf) const;

Modified: lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp?rev=330214&r1=330213&r2=330214&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp (original)
+++ lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.cpp Tue Apr 17 13:44:47 2018
@@ -18,17 +18,22 @@
 #include "lldb/Core/Module.h"
 #include "lldb/Core/ModuleSpec.h"
 #include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/DiagnosticManager.h"
+#include "lldb/Expression/FunctionCaller.h"
 #include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
 #include "lldb/Host/File.h"
 #include "lldb/Host/FileCache.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/ClangASTContext.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/ExecutionContext.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/ProcessLaunchInfo.h"
 #include "lldb/Target/Thread.h"
+#include "lldb/Utility/CleanUp.h"
 #include "lldb/Utility/DataBufferHeap.h"
 #include "lldb/Utility/FileSpec.h"
 #include "lldb/Utility/Log.h"
@@ -923,64 +928,263 @@ Status PlatformPOSIX::EvaluateLibdlExpre
   return Status();
 }
 
+UtilityFunction *
+PlatformPOSIX::MakeLoadImageUtilityFunction(ExecutionContext &exe_ctx, 
+                                            Status &error)
+{
+  // Remember to prepend this with the prefix from GetLibdlFunctionDeclarations.
+  // The returned values are all in __lldb_dlopen_result for consistency.
+  // The wrapper returns a void * but doesn't use it because
+  // UtilityFunctions don't work with void returns at present.
+  static const char *dlopen_wrapper_code = R"(
+  struct __lldb_dlopen_result {
+    void *image_ptr;
+    const char *error_str;
+  };
+
+  void * __lldb_dlopen_wrapper (const char *path, 
+                                __lldb_dlopen_result *result_ptr)
+  {
+    result_ptr->image_ptr = dlopen(path, 2);
+    if (result_ptr->image_ptr == (void *) 0x0)
+      result_ptr->error_str = dlerror();
+    return nullptr;
+  }
+  )";
+
+  static const char *dlopen_wrapper_name = "__lldb_dlopen_wrapper";
+  Process *process = exe_ctx.GetProcessSP().get();
+  // Insert the dlopen shim defines into our generic expression:
+  std::string expr(GetLibdlFunctionDeclarations(process));
+  expr.append(dlopen_wrapper_code);
+  Status utility_error;
+  DiagnosticManager diagnostics;
+  
+  std::unique_ptr<UtilityFunction> dlopen_utility_func_up(process
+      ->GetTarget().GetUtilityFunctionForLanguage(expr.c_str(),
+                                                  eLanguageTypeObjC,
+                                                  dlopen_wrapper_name,
+                                                  utility_error));
+  if (utility_error.Fail()) {
+    error.SetErrorStringWithFormat("dlopen error: could not make utility"
+                                   "function: %s", utility_error.AsCString());
+    return nullptr;
+  }
+  if (!dlopen_utility_func_up->Install(diagnostics, exe_ctx)) {
+    error.SetErrorStringWithFormat("dlopen error: could not install utility"
+                                   "function: %s", 
+                                   diagnostics.GetString().c_str());
+    return nullptr;
+  }
+
+  Value value;
+  ValueList arguments;
+  FunctionCaller *do_dlopen_function = nullptr;
+  UtilityFunction *dlopen_utility_func = nullptr;
+
+  // Fetch the clang types we will need:
+  ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+  CompilerType clang_void_pointer_type
+      = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+  CompilerType clang_char_pointer_type
+        = ast->GetBasicType(eBasicTypeChar).GetPointerType();
+
+  // We are passing two arguments, the path to dlopen, and a pointer to the
+  // storage we've made for the result:
+  value.SetValueType(Value::eValueTypeScalar);
+  value.SetCompilerType(clang_void_pointer_type);
+  arguments.PushValue(value);
+  value.SetCompilerType(clang_char_pointer_type);
+  arguments.PushValue(value);
+  
+  do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
+      clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
+  if (utility_error.Fail()) {
+    error.SetErrorStringWithFormat("dlopen error: could not make function"
+                                   "caller: %s", utility_error.AsCString());
+    return nullptr;
+  }
+  
+  do_dlopen_function = dlopen_utility_func_up->GetFunctionCaller();
+  if (!do_dlopen_function) {
+    error.SetErrorString("dlopen error: could not get function caller.");
+    return nullptr;
+  }
+  
+  // We made a good utility function, so cache it in the process:
+  dlopen_utility_func = dlopen_utility_func_up.get();
+  process->SetLoadImageUtilityFunction(std::move(dlopen_utility_func_up));
+  return dlopen_utility_func;
+}
+
 uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
                                     const lldb_private::FileSpec &remote_file,
                                     lldb_private::Status &error) {
-  char path[PATH_MAX];
-  remote_file.GetPath(path, sizeof(path));
-
-  StreamString expr;
-  expr.Printf(R"(
-                   struct __lldb_dlopen_result { void *image_ptr; const char *error_str; } the_result;
-                   the_result.image_ptr = dlopen ("%s", 2);
-                   if (the_result.image_ptr == (void *) 0x0)
-                   {
-                       the_result.error_str = dlerror();
-                   }
-                   else
-                   {
-                       the_result.error_str = (const char *) 0x0;
-                   }
-                   the_result;
-                  )",
-              path);
-  llvm::StringRef prefix = GetLibdlFunctionDeclarations(process);
-  lldb::ValueObjectSP result_valobj_sp;
-  error = EvaluateLibdlExpression(process, expr.GetData(), prefix,
-                                  result_valobj_sp);
-  if (error.Fail())
+  std::string path;
+  path = remote_file.GetPath();
+  
+  ThreadSP thread_sp = process->GetThreadList().GetExpressionExecutionThread();
+  if (!thread_sp) {
+    error.SetErrorString("dlopen error: no thread available to call dlopen.");
     return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  
+  DiagnosticManager diagnostics;
+  
+  ExecutionContext exe_ctx;
+  thread_sp->CalculateExecutionContext(exe_ctx);
 
-  error = result_valobj_sp->GetError();
-  if (error.Fail())
+  Status utility_error;
+  
+  // The UtilityFunction is held in the Process.  Platforms don't track the 
+  // lifespan of the Targets that use them, we can't put this in the Platform.
+  UtilityFunction *dlopen_utility_func 
+      = process->GetLoadImageUtilityFunction(this);
+  ValueList arguments;
+  FunctionCaller *do_dlopen_function = nullptr;
+  
+  if (!dlopen_utility_func) {
+    // Make the UtilityFunction:
+    dlopen_utility_func = MakeLoadImageUtilityFunction(exe_ctx, error);
+  }
+  // If we couldn't make it, the error will be in error, so we can exit here.
+  if (!dlopen_utility_func)
     return LLDB_INVALID_IMAGE_TOKEN;
-
-  Scalar scalar;
-  ValueObjectSP image_ptr_sp = result_valobj_sp->GetChildAtIndex(0, true);
-  if (!image_ptr_sp || !image_ptr_sp->ResolveValue(scalar)) {
-    error.SetErrorStringWithFormat("unable to load '%s'", path);
+    
+  do_dlopen_function = dlopen_utility_func->GetFunctionCaller();
+  if (!do_dlopen_function) {
+    error.SetErrorString("dlopen error: could not get function caller.");
     return LLDB_INVALID_IMAGE_TOKEN;
   }
-
-  addr_t image_ptr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
-  if (image_ptr != 0 && image_ptr != LLDB_INVALID_ADDRESS)
-    return process->AddImageToken(image_ptr);
-
-  if (image_ptr == 0) {
-    ValueObjectSP error_str_sp = result_valobj_sp->GetChildAtIndex(1, true);
-    if (error_str_sp) {
-      DataBufferSP buffer_sp(new DataBufferHeap(10240, 0));
-      size_t num_chars =
-          error_str_sp->ReadPointedString(buffer_sp, error, 10240).first;
-      if (error.Success() && num_chars > 0)
-        error.SetErrorStringWithFormat("dlopen error: %s",
-                                       buffer_sp->GetBytes());
-      else
-        error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
-      return LLDB_INVALID_IMAGE_TOKEN;
-    }
+  arguments = do_dlopen_function->GetArgumentValues();
+  
+  // Now insert the path we are searching for and the result structure into
+  // the target.
+  uint32_t permissions = ePermissionsReadable|ePermissionsWritable;
+  size_t path_len = path.size() + 1;
+  lldb::addr_t path_addr = process->AllocateMemory(path_len, 
+                                                   permissions,
+                                                   utility_error);
+  if (path_addr == LLDB_INVALID_ADDRESS) {
+    error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+                                    "for path: %s", utility_error.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  
+  // Make sure we deallocate the input string memory:
+  CleanUp path_cleanup([process, path_addr] { 
+      process->DeallocateMemory(path_addr); 
+  });
+  
+  process->WriteMemory(path_addr, path.c_str(), path_len, utility_error);
+  if (utility_error.Fail()) {
+    error.SetErrorStringWithFormat("dlopen error: could not write path string:"
+                                    " %s", utility_error.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  
+  // Make space for our return structure.  It is two pointers big: the token and
+  // the error string.
+  const uint32_t addr_size = process->GetAddressByteSize();
+  lldb::addr_t return_addr = process->CallocateMemory(2*addr_size,
+                                                      permissions,
+                                                      utility_error);
+  if (utility_error.Fail()) {
+    error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+                                    "for path: %s", utility_error.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  
+  // Make sure we deallocate the result structure memory
+  CleanUp return_cleanup([process, return_addr] {
+      process->DeallocateMemory(return_addr);
+  });
+  
+  // Set the values into our args and write them to the target:
+  arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
+  arguments.GetValueAtIndex(1)->GetScalar() = return_addr;
+  
+  lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
+  
+  diagnostics.Clear();
+  if (!do_dlopen_function->WriteFunctionArguments(exe_ctx, 
+                                                 func_args_addr,
+                                                 arguments,
+                                                 diagnostics)) {
+    error.SetErrorStringWithFormat("dlopen error: could not write function "
+                                   "arguments: %s", 
+                                   diagnostics.GetString().c_str());
+    return LLDB_INVALID_IMAGE_TOKEN;
   }
-  error.SetErrorStringWithFormat("unable to load '%s'", path);
+  
+  // Make sure we clean up the args structure.  We can't reuse it because the
+  // Platform lives longer than the process and the Platforms don't get a
+  // signal to clean up cached data when a process goes away.
+  CleanUp args_cleanup([do_dlopen_function, &exe_ctx, func_args_addr] {
+    do_dlopen_function->DeallocateFunctionResults(exe_ctx, func_args_addr);
+  });
+  
+  // Now run the caller:
+  EvaluateExpressionOptions options;
+  options.SetExecutionPolicy(eExecutionPolicyAlways);
+  options.SetLanguage(eLanguageTypeC_plus_plus);
+  options.SetIgnoreBreakpoints(true);
+  options.SetUnwindOnError(true);
+  options.SetTrapExceptions(false); // dlopen can't throw exceptions, so
+                                    // don't do the work to trap them.
+  options.SetTimeout(std::chrono::seconds(2));
+  
+  Value return_value;
+  // Fetch the clang types we will need:
+  ClangASTContext *ast = process->GetTarget().GetScratchClangASTContext();
+
+  CompilerType clang_void_pointer_type
+      = ast->GetBasicType(eBasicTypeVoid).GetPointerType();
+
+  return_value.SetCompilerType(clang_void_pointer_type);
+  
+  ExpressionResults results = do_dlopen_function->ExecuteFunction(
+      exe_ctx, &func_args_addr, options, diagnostics, return_value);
+  if (results != eExpressionCompleted) {
+    error.SetErrorStringWithFormat("dlopen error: could write execute "
+                                   "dlopen wrapper function: %s", 
+                                   diagnostics.GetString().c_str());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  
+  // Read the dlopen token from the return area:
+  lldb::addr_t token = process->ReadPointerFromMemory(return_addr, 
+                                                      utility_error);
+  if (utility_error.Fail()) {
+    error.SetErrorStringWithFormat("dlopen error: could not read the return "
+                                    "struct: %s", utility_error.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  
+  // The dlopen succeeded!
+  if (token != 0x0)
+    return process->AddImageToken(token);
+    
+  // We got an error, lets read in the error string:
+  std::string dlopen_error_str;
+  lldb::addr_t error_addr 
+    = process->ReadPointerFromMemory(return_addr + addr_size, utility_error);
+  if (utility_error.Fail()) {
+    error.SetErrorStringWithFormat("dlopen error: could not read error string: "
+                                    "%s", utility_error.AsCString());
+    return LLDB_INVALID_IMAGE_TOKEN;
+  }
+  
+  size_t num_chars = process->ReadCStringFromMemory(error_addr + addr_size, 
+                                                    dlopen_error_str, 
+                                                    utility_error);
+  if (utility_error.Success() && num_chars > 0)
+    error.SetErrorStringWithFormat("dlopen error: %s",
+                                   dlopen_error_str.c_str());
+  else
+    error.SetErrorStringWithFormat("dlopen failed for unknown reasons.");
+
   return LLDB_INVALID_IMAGE_TOKEN;
 }
 

Modified: lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h?rev=330214&r1=330213&r2=330214&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h (original)
+++ lldb/trunk/source/Plugins/Platform/POSIX/PlatformPOSIX.h Tue Apr 17 13:44:47 2018
@@ -200,6 +200,10 @@ protected:
   EvaluateLibdlExpression(lldb_private::Process *process, const char *expr_cstr,
                           llvm::StringRef expr_prefix,
                           lldb::ValueObjectSP &result_valobj_sp);
+  
+  lldb_private::UtilityFunction *                        
+  MakeLoadImageUtilityFunction(lldb_private::ExecutionContext &exe_ctx, 
+                               lldb_private::Status &error); 
 
   virtual
   llvm::StringRef GetLibdlFunctionDeclarations(lldb_private::Process *process);

Modified: lldb/trunk/source/Target/Process.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Target/Process.cpp?rev=330214&r1=330213&r2=330214&view=diff
==============================================================================
--- lldb/trunk/source/Target/Process.cpp (original)
+++ lldb/trunk/source/Target/Process.cpp Tue Apr 17 13:44:47 2018
@@ -30,6 +30,7 @@
 #include "lldb/Expression/DiagnosticManager.h"
 #include "lldb/Expression/IRDynamicChecks.h"
 #include "lldb/Expression/UserExpression.h"
+#include "lldb/Expression/UtilityFunction.h"
 #include "lldb/Host/ConnectionFileDescriptor.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/Host.h"
@@ -6245,3 +6246,15 @@ Status Process::UpdateAutomaticSignalFil
   // No automatic signal filtering to speak of.
   return Status();
 }
+
+UtilityFunction *Process::GetLoadImageUtilityFunction(Platform *platform) {
+  if (platform != GetTarget().GetPlatform().get())
+    return nullptr;
+  return m_dlopen_utility_func_up.get();
+}
+
+void Process::SetLoadImageUtilityFunction(std::unique_ptr<UtilityFunction> 
+                                          utility_func_up) {
+  m_dlopen_utility_func_up.swap(utility_func_up);
+}
+




More information about the lldb-commits mailing list