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
This commit is contained in:
Greg Clayton 2010-11-04 01:54:29 +00:00
parent 10af7c430a
commit 8f343b09e9
16 changed files with 407 additions and 27 deletions

View File

@ -92,6 +92,9 @@ protected:
const lldb_private::Error &
operator*() const;
lldb_private::Error &
ref();
#endif

View File

@ -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;

View File

@ -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;

View File

@ -147,6 +147,9 @@ public:
Value &
GetValue();
bool
ResolveValue (ExecutionContextScope *exe_scope, Scalar &scalar);
const char *
GetLocationAsCString (ExecutionContextScope *exe_scope);

View File

@ -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.

View File

@ -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<Notifications> m_notifications; ///< The list of notifications that this process can deliver.
std::vector<lldb::addr_t> 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.

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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 <filename> [<filename> ...]",
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; i<argc; ++i)
{
Error error;
const char *image_path = command.GetArgumentAtIndex(i);
FileSpec image_spec (image_path, false);
uint32_t image_token = process->LoadImage(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 <index>",
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; i<argc; ++i)
{
const char *image_token_cstr = command.GetArgumentAtIndex(i);
uint32_t image_token = Args::StringToUInt32(image_token_cstr, LLDB_INVALID_IMAGE_TOKEN, 0);
if (image_token == LLDB_INVALID_IMAGE_TOKEN)
{
result.AppendErrorWithFormat ("invalid image index argument '%s'", image_token_cstr);
result.SetStatus (eReturnStatusFailed);
break;
}
else
{
Error error (process->UnloadImage(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)));

View File

@ -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
{

View File

@ -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;

View File

@ -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()
{

View File

@ -78,6 +78,8 @@ public:
GetStepThroughTrampolinePlan (lldb_private::Thread &thread,
bool stop_others);
virtual lldb_private::Error
CanLoadImage ();
//------------------------------------------------------------------
// PluginInterface protocol

View File

@ -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()
{