From 8f343b09e946cce2ef79461b5a86d987f6a31748 Mon Sep 17 00:00:00 2001 From: Greg Clayton Date: Thu, 4 Nov 2010 01:54:29 +0000 Subject: [PATCH] Added support for loading and unloading shared libraries. This was done by adding support into lldb_private::Process: virtual uint32_t lldb_private::Process::LoadImage (const FileSpec &image_spec, Error &error); virtual Error lldb_private::Process::UnloadImage (uint32_t image_token); There is a default implementation that should work for both linux and MacOSX. This ability has also been exported through the SBProcess API: uint32_t lldb::SBProcess::LoadImage (lldb::SBFileSpec &image_spec, lldb::SBError &error); lldb::SBError lldb::SBProcess::UnloadImage (uint32_t image_token); Modified the DynamicLoader plug-in interface to require it to be able to tell us if it is currently possible to load/unload a shared library: virtual lldb_private::Error DynamicLoader::CanLoadImage () = 0; This way the dynamic loader plug-ins are allows to veto whether we can currently load a shared library since the dynamic loader might know if it is currenlty loading/unloading shared libraries. It might also know about the current host system and know where to check to make sure runtime or malloc locks are currently being held. Modified the expression parser to have ClangUserExpression::Evaluate() be the one that causes the dynamic checkers to be loaded instead of other code that shouldn't have to worry about it. llvm-svn: 118227 --- lldb/include/lldb/API/SBError.h | 3 + lldb/include/lldb/API/SBFileSpec.h | 1 + lldb/include/lldb/API/SBProcess.h | 30 ++-- lldb/include/lldb/Core/ValueObject.h | 3 + lldb/include/lldb/Target/DynamicLoader.h | 18 +++ lldb/include/lldb/Target/Process.h | 28 ++++ lldb/include/lldb/lldb-defines.h | 1 + lldb/source/API/SBError.cpp | 6 + lldb/source/API/SBProcess.cpp | 21 +++ .../Commands/CommandObjectExpression.cpp | 15 -- lldb/source/Commands/CommandObjectProcess.cpp | 124 ++++++++++++++++ lldb/source/Core/ValueObject.cpp | 9 ++ .../source/Expression/ClangUserExpression.cpp | 17 +++ .../MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp | 20 +++ .../MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h | 2 + lldb/source/Target/Process.cpp | 136 ++++++++++++++++++ 16 files changed, 407 insertions(+), 27 deletions(-) diff --git a/lldb/include/lldb/API/SBError.h b/lldb/include/lldb/API/SBError.h index 21f4e483d327..44b58ae05455 100644 --- a/lldb/include/lldb/API/SBError.h +++ b/lldb/include/lldb/API/SBError.h @@ -92,6 +92,9 @@ protected: const lldb_private::Error & operator*() const; + lldb_private::Error & + ref(); + #endif diff --git a/lldb/include/lldb/API/SBFileSpec.h b/lldb/include/lldb/API/SBFileSpec.h index 049ca9025500..ed6613ad0053 100644 --- a/lldb/include/lldb/API/SBFileSpec.h +++ b/lldb/include/lldb/API/SBFileSpec.h @@ -62,6 +62,7 @@ private: friend class SBHostOS; friend class SBLineEntry; friend class SBModule; + friend class SBProcess; friend class SBSourceManager; friend class SBThread; friend class SBTarget; diff --git a/lldb/include/lldb/API/SBProcess.h b/lldb/include/lldb/API/SBProcess.h index ad612b929238..c7c67953864e 100644 --- a/lldb/include/lldb/API/SBProcess.h +++ b/lldb/include/lldb/API/SBProcess.h @@ -103,40 +103,40 @@ public: uint32_t GetAddressByteSize() const; - SBError + lldb::SBError Destroy (); lldb::pid_t AttachByPID (lldb::pid_t pid); // DEPRECATED - // DEPRECATED: relocated to "SBProcess SBTarget::AttachToProcess (lldb::pid_t pid, SBError& error)" - SBError + // DEPRECATED: relocated to "SBProcess SBTarget::AttachToProcess (lldb::pid_t pid, lldb::SBError& error)" + lldb::SBError Attach (lldb::pid_t pid); - // DEPRECATED: relocated to "SBProcess SBTarget::AttachToProcess (const char *name, bool wait_for_launch, SBError& error)" - SBError + // DEPRECATED: relocated to "SBProcess SBTarget::AttachToProcess (const char *name, bool wait_for_launch, lldb::SBError& error)" + lldb::SBError AttachByName (const char *name, bool wait_for_launch); - SBError + lldb::SBError Continue (); - SBError + lldb::SBError Stop (); - SBError + lldb::SBError Kill (); - SBError + lldb::SBError Detach (); - SBError + lldb::SBError Signal (int signal); size_t - ReadMemory (addr_t addr, void *buf, size_t size, SBError &error); + ReadMemory (addr_t addr, void *buf, size_t size, lldb::SBError &error); size_t - WriteMemory (addr_t addr, const void *buf, size_t size, SBError &error); + WriteMemory (addr_t addr, const void *buf, size_t size, lldb::SBError &error); // Events static lldb::StateType @@ -154,6 +154,12 @@ public: bool GetDescription (lldb::SBStream &description); + uint32_t + LoadImage (lldb::SBFileSpec &image_spec, lldb::SBError &error); + + lldb::SBError + UnloadImage (uint32_t image_token); + protected: friend class SBAddress; friend class SBBreakpoint; diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 8b1ba1c38781..aaff85e876a5 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -147,6 +147,9 @@ public: Value & GetValue(); + bool + ResolveValue (ExecutionContextScope *exe_scope, Scalar &scalar); + const char * GetLocationAsCString (ExecutionContextScope *exe_scope); diff --git a/lldb/include/lldb/Target/DynamicLoader.h b/lldb/include/lldb/Target/DynamicLoader.h index bed967859729..af1256772742 100644 --- a/lldb/include/lldb/Target/DynamicLoader.h +++ b/lldb/include/lldb/Target/DynamicLoader.h @@ -12,6 +12,7 @@ // Project includes #include "lldb/lldb-private.h" +#include "lldb/Core/Error.h" #include "lldb/Core/PluginInterface.h" namespace lldb_private { @@ -138,6 +139,23 @@ public: virtual lldb::ThreadPlanSP GetStepThroughTrampolinePlan (Thread &thread, bool stop_others) = 0; + + //------------------------------------------------------------------ + /// Ask if it is ok to try and load or unload an shared library + /// (image). + /// + /// The dynamic loader often knows when it would be ok to try and + /// load or unload a shared library. This function call allows the + /// dynamic loader plug-ins to check any current dyld state to make + /// sure it is an ok time to load a shared library. + /// + /// @return + /// \b True if it is currently ok to try and load a shared + /// library into the process, \b false otherwise. + //------------------------------------------------------------------ + virtual Error + CanLoadImage () = 0; + protected: //------------------------------------------------------------------ // Member variables. diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 51fb04bfdd08..82a87aa4947c 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -614,6 +614,33 @@ public: virtual lldb::addr_t GetImageInfoAddress (); + //------------------------------------------------------------------ + /// Load a shared library into this process. + /// + /// Try and load a shared library into the current process. This + /// call might fail in the dynamic loader plug-in says it isn't safe + /// to try and load shared libraries at the moment. + /// + /// @param[in] image_spec + /// The image file spec that points to the shared library that + /// you want to load. + /// + /// @param[out] error + /// An error object that gets filled in with any errors that + /// might occur when trying to load the shared library. + /// + /// @return + /// A token that represents the shared library that can be + /// later used to unload the shared library. A value of + /// LLDB_INVALID_IMAGE_TOKEN will be returned if the shared + /// library can't be opened. + //------------------------------------------------------------------ + virtual uint32_t + LoadImage (const FileSpec &image_spec, Error &error); + + virtual Error + UnloadImage (uint32_t image_token); + //------------------------------------------------------------------ /// Register for process and thread notifications. /// @@ -1642,6 +1669,7 @@ protected: std::string m_exit_string; ///< A textual description of why a process exited. ThreadList m_thread_list; ///< The threads for this process. std::vector m_notifications; ///< The list of notifications that this process can deliver. + std::vector m_image_tokens; Listener &m_listener; BreakpointSiteList m_breakpoint_site_list; ///< This is the list of breakpoint locations we intend ///< to insert in the target. diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h index e927a9d48ce8..38a689ce8067 100644 --- a/lldb/include/lldb/lldb-defines.h +++ b/lldb/include/lldb/lldb-defines.h @@ -55,6 +55,7 @@ //---------------------------------------------------------------------- #define LLDB_INVALID_ADDRESS UINT64_MAX #define LLDB_INVALID_INDEX32 UINT32_MAX +#define LLDB_INVALID_IMAGE_TOKEN UINT32_MAX #define LLDB_INVALID_REGNUM UINT32_MAX #define LLDB_INVALID_UID UINT32_MAX #define LLDB_INVALID_PROCESS_ID 0 diff --git a/lldb/source/API/SBError.cpp b/lldb/source/API/SBError.cpp index 935a869e57ba..2e01cfa8ccf9 100644 --- a/lldb/source/API/SBError.cpp +++ b/lldb/source/API/SBError.cpp @@ -199,6 +199,12 @@ SBError::get() return m_opaque_ap.get(); } +lldb_private::Error & +SBError::ref() +{ + CreateIfNeeded(); + return *m_opaque_ap; +} const lldb_private::Error & SBError::operator*() const diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp index 9ea9f754abc4..0c52b6b15d18 100644 --- a/lldb/source/API/SBProcess.cpp +++ b/lldb/source/API/SBProcess.cpp @@ -703,3 +703,24 @@ SBProcess::GetDescription (SBStream &description) return true; } + +uint32_t +SBProcess::LoadImage (lldb::SBFileSpec &sb_image_spec, lldb::SBError &sb_error) +{ + if (m_opaque_sp) + m_opaque_sp->LoadImage (*sb_image_spec, sb_error.ref()); + return LLDB_INVALID_IMAGE_TOKEN; +} + +lldb::SBError +SBProcess::UnloadImage (uint32_t image_token) +{ + lldb::SBError sb_error; + if (m_opaque_sp) + sb_error.SetError (m_opaque_sp->UnloadImage (image_token)); + else + sb_error.SetErrorString("invalid process"); + return sb_error; +} + + diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index 7f7c780978ae..2045c59d058c 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -218,21 +218,6 @@ CommandObjectExpression::EvaluateExpression return false; } - if (!m_exe_ctx.process->GetDynamicCheckers()) - { - DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); - - StreamString install_errors; - - if (!dynamic_checkers->Install(install_errors, m_exe_ctx)) - { - error_stream.Printf("Couldn't install dynamic checkers into the execution context: %s\n", install_errors.GetData()); - return false; - } - - m_exe_ctx.process->SetDynamicCheckers(dynamic_checkers); - } - const char *prefix = NULL; if (m_exe_ctx.target) diff --git a/lldb/source/Commands/CommandObjectProcess.cpp b/lldb/source/Commands/CommandObjectProcess.cpp index 8c4ffdd96bb6..88fdca3086bf 100644 --- a/lldb/source/Commands/CommandObjectProcess.cpp +++ b/lldb/source/Commands/CommandObjectProcess.cpp @@ -852,6 +852,128 @@ public: } }; +//------------------------------------------------------------------------- +// CommandObjectProcessLoad +//------------------------------------------------------------------------- + +class CommandObjectProcessLoad : public CommandObject +{ +public: + + CommandObjectProcessLoad (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "process load", + "Load a shared library into the current process.", + "process load [ ...]", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + ~CommandObjectProcessLoad () + { + } + + bool + Execute (Args& command, + CommandReturnObject &result) + { + Process *process = m_interpreter.GetDebugger().GetExecutionContext().process; + if (process == NULL) + { + result.AppendError ("must have a valid process in order to load a shared library"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const uint32_t argc = command.GetArgumentCount(); + + for (uint32_t i=0; iLoadImage(image_spec, error); + if (image_token != LLDB_INVALID_IMAGE_TOKEN) + { + result.AppendMessageWithFormat ("Loading \"%s\"...ok\nImage %u loaded.\n", image_path, image_token); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("failed to load '%s': %s", image_path, error.AsCString()); + result.SetStatus (eReturnStatusFailed); + } + } + return result.Succeeded(); + } +}; + + +//------------------------------------------------------------------------- +// CommandObjectProcessUnload +//------------------------------------------------------------------------- + +class CommandObjectProcessUnload : public CommandObject +{ +public: + + CommandObjectProcessUnload (CommandInterpreter &interpreter) : + CommandObject (interpreter, + "process unload", + "Unload a shared library from the current process using the index returned by a previous call to \"process load\".", + "process unload ", + eFlagProcessMustBeLaunched | eFlagProcessMustBePaused) + { + } + + ~CommandObjectProcessUnload () + { + } + + bool + Execute (Args& command, + CommandReturnObject &result) + { + Process *process = m_interpreter.GetDebugger().GetExecutionContext().process; + if (process == NULL) + { + result.AppendError ("must have a valid process in order to load a shared library"); + result.SetStatus (eReturnStatusFailed); + return false; + } + + const uint32_t argc = command.GetArgumentCount(); + + for (uint32_t i=0; iUnloadImage(image_token)); + if (error.Success()) + { + result.AppendMessageWithFormat ("Unloading shared library with index %u...ok\n", image_token); + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.AppendErrorWithFormat ("failed to unload image: %s", error.AsCString()); + result.SetStatus (eReturnStatusFailed); + break; + } + } + } + return result.Succeeded(); + } +}; + //------------------------------------------------------------------------- // CommandObjectProcessSignal //------------------------------------------------------------------------- @@ -1450,6 +1572,8 @@ CommandObjectMultiwordProcess::CommandObjectMultiwordProcess (CommandInterpreter LoadSubCommand ("launch", CommandObjectSP (new CommandObjectProcessLaunch (interpreter))); LoadSubCommand ("continue", CommandObjectSP (new CommandObjectProcessContinue (interpreter))); LoadSubCommand ("detach", CommandObjectSP (new CommandObjectProcessDetach (interpreter))); + LoadSubCommand ("load", CommandObjectSP (new CommandObjectProcessLoad (interpreter))); + LoadSubCommand ("unload", CommandObjectSP (new CommandObjectProcessUnload (interpreter))); LoadSubCommand ("signal", CommandObjectSP (new CommandObjectProcessSignal (interpreter))); LoadSubCommand ("handle", CommandObjectSP (new CommandObjectProcessHandle (interpreter))); LoadSubCommand ("status", CommandObjectSP (new CommandObjectProcessStatus (interpreter))); diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 2efd4432d58a..a3da90d9c3aa 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -221,6 +221,15 @@ ValueObject::GetValue() const return m_value; } +bool +ValueObject::ResolveValue (ExecutionContextScope *exe_scope, Scalar &scalar) +{ + ExecutionContext exe_ctx; + exe_scope->CalculateExecutionContext(exe_ctx); + scalar = m_value.ResolveValue(&exe_ctx, GetClangAST ()); + return scalar.IsValid(); +} + bool ValueObject::GetValueIsValid () const { diff --git a/lldb/source/Expression/ClangUserExpression.cpp b/lldb/source/Expression/ClangUserExpression.cpp index dc93d8e69717..5181c98c9aa3 100644 --- a/lldb/source/Expression/ClangUserExpression.cpp +++ b/lldb/source/Expression/ClangUserExpression.cpp @@ -30,6 +30,7 @@ #include "lldb/Host/Host.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" +#include "lldb/Target/Process.h" #include "lldb/Target/StackFrame.h" #include "lldb/Target/Target.h" @@ -437,6 +438,22 @@ ClangUserExpression::Evaluate (ExecutionContext &exe_ctx, { Error error; lldb::ValueObjectSP result_valobj_sp; + + if (exe_ctx.process == NULL) + return result_valobj_sp; + + if (!exe_ctx.process->GetDynamicCheckers()) + { + DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); + + StreamString install_errors; + + if (!dynamic_checkers->Install(install_errors, exe_ctx)) + return result_valobj_sp; + + exe_ctx.process->SetDynamicCheckers(dynamic_checkers); + } + ClangUserExpression user_expression (expr_cstr, expr_prefix); StreamString error_stream; diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp index fa46c77553aa..bdfc47c48ff7 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.cpp @@ -1167,6 +1167,26 @@ DynamicLoaderMacOSXDYLD::GetStepThroughTrampolinePlan (Thread &thread, bool stop return thread_plan_sp; } +Error +DynamicLoaderMacOSXDYLD::CanLoadImage () +{ + Error error; + // In order for us to tell if we can load a shared library we verify that + // the dylib_info_addr isn't zero (which means no shared libraries have + // been set yet, or dyld is currently mucking with the shared library list). + if (ReadAllImageInfosStructure ()) + { + // TODO: also check the _dyld_global_lock_held variable in libSystem.B.dylib? + // TODO: check the malloc lock? + // TODO: check the objective C lock? + if (m_dyld_all_image_infos.dylib_info_addr != 0) + return error; // Success + } + + error.SetErrorString("unsafe to load or unload shared libraries"); + return error; +} + void DynamicLoaderMacOSXDYLD::Initialize() { diff --git a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h index 8f22dbdcb630..34ea8a46781f 100644 --- a/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h +++ b/lldb/source/Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h @@ -78,6 +78,8 @@ public: GetStepThroughTrampolinePlan (lldb_private::Thread &thread, bool stop_others); + virtual lldb_private::Error + CanLoadImage (); //------------------------------------------------------------------ // PluginInterface protocol diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index d71626790bff..fa7ac71c89e0 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -21,6 +21,7 @@ #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Host/Host.h" #include "lldb/Target/ABI.h" +#include "lldb/Target/DynamicLoader.h" #include "lldb/Target/LanguageRuntime.h" #include "lldb/Target/CPPLanguageRuntime.h" #include "lldb/Target/ObjCLanguageRuntime.h" @@ -471,6 +472,141 @@ Process::GetImageInfoAddress() return LLDB_INVALID_ADDRESS; } +//---------------------------------------------------------------------- +// LoadImage +// +// This function provides a default implementation that works for most +// unix variants. Any Process subclasses that need to do shared library +// loading differently should override LoadImage and UnloadImage and +// do what is needed. +//---------------------------------------------------------------------- +uint32_t +Process::LoadImage (const FileSpec &image_spec, Error &error) +{ + DynamicLoader *loader = GetDynamicLoader(); + if (loader) + { + error = loader->CanLoadImage(); + if (error.Fail()) + return LLDB_INVALID_IMAGE_TOKEN; + } + + if (error.Success()) + { + ThreadSP thread_sp(GetThreadList ().GetSelectedThread()); + if (thread_sp == NULL) + thread_sp = GetThreadList ().GetThreadAtIndex(0, true); + + if (thread_sp) + { + StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex (0)); + + if (frame_sp) + { + ExecutionContext exe_ctx; + frame_sp->CalculateExecutionContext (exe_ctx); + + StreamString expr; + char path[PATH_MAX]; + image_spec.GetPath(path, sizeof(path)); + expr.Printf("dlopen (\"%s\", 2)", path); + const char *prefix = "extern \"C\" void* dlopen (const char *path, int mode);\n"; + lldb::ValueObjectSP result_valobj_sp (ClangUserExpression::Evaluate (exe_ctx, expr.GetData(), prefix)); + if (result_valobj_sp->GetError().Success()) + { + Scalar scalar; + if (result_valobj_sp->ResolveValue (frame_sp.get(), scalar)) + { + addr_t image_ptr = scalar.ULongLong(LLDB_INVALID_ADDRESS); + if (image_ptr != 0 && image_ptr != LLDB_INVALID_ADDRESS) + { + uint32_t image_token = m_image_tokens.size(); + m_image_tokens.push_back (image_ptr); + return image_token; + } + } + } + } + } + } + return LLDB_INVALID_IMAGE_TOKEN; +} + +//---------------------------------------------------------------------- +// UnloadImage +// +// This function provides a default implementation that works for most +// unix variants. Any Process subclasses that need to do shared library +// loading differently should override LoadImage and UnloadImage and +// do what is needed. +//---------------------------------------------------------------------- +Error +Process::UnloadImage (uint32_t image_token) +{ + Error error; + if (image_token < m_image_tokens.size()) + { + const addr_t image_addr = m_image_tokens[image_token]; + if (image_addr == LLDB_INVALID_ADDRESS) + { + error.SetErrorString("image already unloaded"); + } + else + { + DynamicLoader *loader = GetDynamicLoader(); + if (loader) + error = loader->CanLoadImage(); + + if (error.Success()) + { + ThreadSP thread_sp(GetThreadList ().GetSelectedThread()); + if (thread_sp == NULL) + thread_sp = GetThreadList ().GetThreadAtIndex(0, true); + + if (thread_sp) + { + StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex (0)); + + if (frame_sp) + { + ExecutionContext exe_ctx; + frame_sp->CalculateExecutionContext (exe_ctx); + + StreamString expr; + expr.Printf("dlclose ((void *)0x%llx)", image_addr); + const char *prefix = "extern \"C\" int dlclose(void* handle);\n"; + lldb::ValueObjectSP result_valobj_sp (ClangUserExpression::Evaluate (exe_ctx, expr.GetData(), prefix)); + if (result_valobj_sp->GetError().Success()) + { + Scalar scalar; + if (result_valobj_sp->ResolveValue (frame_sp.get(), scalar)) + { + if (scalar.UInt(1)) + { + error.SetErrorStringWithFormat("expression failed: \"%s\"", expr.GetData()); + } + else + { + m_image_tokens[image_token] = LLDB_INVALID_ADDRESS; + } + } + } + else + { + error = result_valobj_sp->GetError(); + } + } + } + } + } + } + else + { + error.SetErrorString("invalid image token"); + } + return error; +} + DynamicLoader * Process::GetDynamicLoader() {