const lldb::SBFileSpec &remote_image_spec,
lldb::SBError &error);
+ //------------------------------------------------------------------
+ /// Load a shared library into this process, starting with a
+ /// library name and a list of paths, searching along the list of
+ /// paths till you find a matching library.
+ ///
+ /// @param[in] local_spec
+ /// The name of the shared library that you want to load.
+ /// If local_spec is a relative path, the relative path will be
+ /// appended to the search paths.
+ /// If the local_spec is an absolute path, just the basename is used.
+ ///
+ /// @param[in] paths
+ /// A list of paths to search for the library whose basename is
+ /// local_spec.
+ ///
+ /// @param[out] loaded_path
+ /// If the library was found along the paths, this will store the
+ /// full path to the found library.
+ ///
+ /// @param[out] error
+ /// An error object that gets filled in with any errors that
+ /// might occur when trying to search for the shared library.
+ ///
+ /// @return
+ /// A token that represents the shared library that can be
+ /// later passed to UnloadImage. A value of
+ /// LLDB_INVALID_IMAGE_TOKEN will be returned if the shared
+ /// library can't be opened.
+ //------------------------------------------------------------------
+ uint32_t LoadImageUsingPaths(const lldb::SBFileSpec &image_spec,
+ SBStringList &paths,
+ lldb::SBFileSpec &loaded_path,
+ lldb::SBError &error);
+
lldb::SBError UnloadImage(uint32_t image_token);
lldb::SBError SendEventData(const char *data);
const lldb_private::FileSpec &remote_file,
lldb_private::Status &error);
+ //------------------------------------------------------------------
+ /// Load a shared library specified by base name into this process,
+ /// looking by hand along a set of paths.
+ ///
+ /// @param[in] process
+ /// The process to load the image.
+ ///
+ /// @param[in] library_name
+ /// The name of the library to look for.
+ ///
+ /// @param[in] path_list
+ /// The list of paths to use to search for the library. First
+ /// match wins.
+ ///
+ /// @param[out] error
+ /// An error object that gets filled in with any errors that
+ /// might occur when trying to load the shared library.
+ ///
+ /// @param[out] loaded_path
+ /// If non-null, the path to the dylib that was successfully loaded
+ /// is stored in this path.
+ ///
+ /// @return
+ /// A token that represents the shared library which can be
+ /// passed to UnloadImage. A value of
+ /// LLDB_INVALID_IMAGE_TOKEN will be returned if the shared
+ /// library can't be opened.
+ //------------------------------------------------------------------
+ uint32_t LoadImageUsingPaths(lldb_private::Process *process,
+ const lldb_private::FileSpec &library_name,
+ const std::vector<std::string> &paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_path);
+
virtual uint32_t DoLoadImage(lldb_private::Process *process,
const lldb_private::FileSpec &remote_file,
- lldb_private::Status &error);
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_path = nullptr);
virtual Status UnloadImage(lldb_private::Process *process,
uint32_t image_token);
--- /dev/null
+basic_process
--- /dev/null
+LEVEL := ../../make
+
+CXX_SOURCES := main.cpp
+
+include $(LEVEL)/Makefile.rules
+
+all: hidden_lib a.out
+
+hidden_lib:
+ $(MAKE) VPATH=$(SRCDIR)/hidden -I $(SRCDIR)/hidden -C hidden -f $(SRCDIR)/hidden/Makefile
+
+clean::
+ $(MAKE) -I $(SRCDIR)/hidden -C hidden -f $(SRCDIR)/hidden/Makefile clean
--- /dev/null
+"""
+Test that SBProcess.LoadImageUsingPaths works correctly.
+"""
+
+from __future__ import print_function
+
+
+import os
+import time
+import re
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+
+
+@skipIfWindows # The Windows platform doesn't implement DoLoadImage.
+class LoadUsingPathsTestCase(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ NO_DEBUG_INFO_TESTCASE = True
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Make the hidden directory in the build hierarchy:
+ lldbutil.mkdir_p(self.getBuildArtifact("hidden"))
+
+ # Invoke the default build rule.
+ self.build()
+
+ ext = 'so'
+ if self.platformIsDarwin():
+ ext = 'dylib'
+ self.lib_name = 'libloadunload.' + ext
+
+ self.wd = self.getBuildDir()
+ self.hidden_dir = os.path.join(self.wd, 'hidden')
+ self.hidden_lib = os.path.join(self.hidden_dir, self.lib_name)
+
+ @skipIfFreeBSD # llvm.org/pr14424 - missing FreeBSD Makefiles/testcase support
+ @not_remote_testsuite_ready
+ @skipIfWindows # Windows doesn't have dlopen and friends, dynamic libraries work differently
+ def test_load_using_paths(self):
+ """Test that we can load a module by providing a set of search paths."""
+ if self.platformIsDarwin():
+ dylibName = 'libloadunload_d.dylib'
+ else:
+ dylibName = 'libloadunload_d.so'
+
+ # The directory with the dynamic library we did not link to.
+ path_dir = os.path.join(self.getBuildDir(), "hidden")
+
+ (target, process, thread,
+ _) = lldbutil.run_to_source_breakpoint(self,
+ "Break here to do the load using paths",
+ lldb.SBFileSpec("main.cpp"))
+ error = lldb.SBError()
+ lib_spec = lldb.SBFileSpec(self.lib_name)
+ paths = lldb.SBStringList()
+ paths.AppendString(self.wd)
+ paths.AppendString(os.path.join(self.wd, "no_such_dir"))
+
+ out_spec = lldb.SBFileSpec()
+
+ # First try with no correct directories on the path, and make sure that doesn't blow up:
+ token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error)
+ self.assertEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Only looked on the provided path.")
+
+ # Now add the correct dir to the paths list and try again:
+ paths.AppendString(self.hidden_dir)
+ token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error)
+
+ self.assertNotEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Got a valid token")
+ self.assertEqual(out_spec, lldb.SBFileSpec(self.hidden_lib), "Found the expected library")
+
+ # Make sure this really is in the image list:
+ loaded_module = target.FindModule(out_spec)
+
+ self.assertTrue(loaded_module.IsValid(), "The loaded module is in the image list.")
+
+ # Now see that we can call a function in the loaded module.
+ value = thread.frames[0].EvaluateExpression("d_function()", lldb.SBExpressionOptions())
+ self.assertTrue(value.GetError().Success(), "Got a value from the expression")
+ ret_val = value.GetValueAsSigned()
+ self.assertEqual(ret_val, 12345, "Got the right value")
+
+ # Make sure the token works to unload it:
+ process.UnloadImage(token)
+
+ # Make sure this really is no longer in the image list:
+ loaded_module = target.FindModule(out_spec)
+
+ self.assertFalse(loaded_module.IsValid(), "The unloaded module is no longer in the image list.")
+
+ # Make sure a relative path also works:
+ paths.Clear()
+ paths.AppendString(os.path.join(self.wd, "no_such_dir"))
+ paths.AppendString(self.wd)
+ relative_spec = lldb.SBFileSpec(os.path.join("hidden", self.lib_name))
+
+ out_spec = lldb.SBFileSpec()
+ token = process.LoadImageUsingPaths(relative_spec, paths, out_spec, error)
+
+ self.assertNotEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Got a valid token")
+ self.assertEqual(out_spec, lldb.SBFileSpec(self.hidden_lib), "Found the expected library")
+
+ process.UnloadImage(token)
+
+ # Finally, passing in an absolute path should work like the basename:
+ # This should NOT work because we've taken hidden_dir off the paths:
+ abs_spec = lldb.SBFileSpec(os.path.join(self.hidden_dir, self.lib_name))
+
+ token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error)
+ self.assertEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Only looked on the provided path.")
+
+ # But it should work when we add the dir:
+ # Now add the correct dir to the paths list and try again:
+ paths.AppendString(self.hidden_dir)
+ token = process.LoadImageUsingPaths(lib_spec, paths, out_spec, error)
+
+ self.assertNotEqual(token, lldb.LLDB_INVALID_IMAGE_TOKEN, "Got a valid token")
+ self.assertEqual(out_spec, lldb.SBFileSpec(self.hidden_lib), "Found the expected library")
+
+
--- /dev/null
+LEVEL := ../../../make
+
+DYLIB_NAME := loadunload
+DYLIB_CXX_SOURCES := d.cpp
+DYLIB_ONLY := YES
+
+include $(LEVEL)/Makefile.rules
--- /dev/null
+//===-- c.c -----------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+int d_init()
+{
+ return 456;
+}
+
+int d_global = d_init();
+
+int
+d_function ()
+{ // Find this line number within d_dunction().
+ return 12345;
+}
--- /dev/null
+//===-- main.c --------------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include <stdio.h>
+
+int
+main (int argc, char const *argv[])
+{
+ printf("Break here to do the load using paths.");
+ return 0;
+}
uint32_t
LoadImage (lldb::SBFileSpec &image_spec, lldb::SBError &error);
+ %feature("autodoc", "
+ Load the library whose filename is given by image_spec looking in all the
+ paths supplied in the paths argument. If successful, return a token that
+ can be passed to UnloadImage and fill loaded_path with the path that was
+ successfully loaded. On failure, return
+ lldb.LLDB_INVALID_IMAGE_TOKEN.
+ ") LoadImageUsingPaths;
+ uint32_t
+ LoadImageUsingPaths(const lldb::SBFileSpec &image_spec,
+ SBStringList &paths,
+ lldb::SBFileSpec &loaded_path,
+ SBError &error);
+
lldb::SBError
UnloadImage (uint32_t image_token);
return LLDB_INVALID_IMAGE_TOKEN;
}
+uint32_t SBProcess::LoadImageUsingPaths(const lldb::SBFileSpec &image_spec,
+ SBStringList &paths,
+ lldb::SBFileSpec &loaded_path,
+ lldb::SBError &error) {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_API));
+ ProcessSP process_sp(GetSP());
+ if (process_sp) {
+ Process::StopLocker stop_locker;
+ if (stop_locker.TryLock(&process_sp->GetRunLock())) {
+ if (log)
+ log->Printf("SBProcess(%p)::LoadImageUsingPaths() => "
+ "calling Platform::LoadImageUsingPaths for: %s",
+ static_cast<void *>(process_sp.get()),
+ image_spec.GetFilename());
+
+ std::lock_guard<std::recursive_mutex> guard(
+ process_sp->GetTarget().GetAPIMutex());
+ PlatformSP platform_sp = process_sp->GetTarget().GetPlatform();
+ size_t num_paths = paths.GetSize();
+ std::vector<std::string> paths_vec;
+ paths_vec.reserve(num_paths);
+ for (size_t i = 0; i < num_paths; i++)
+ paths_vec.push_back(paths.GetStringAtIndex(i));
+ FileSpec loaded_spec;
+
+ uint32_t token = platform_sp->LoadImageUsingPaths(process_sp.get(),
+ *image_spec,
+ paths_vec,
+ error.ref(),
+ &loaded_spec);
+ if (token != LLDB_INVALID_IMAGE_TOKEN)
+ loaded_path = loaded_spec;
+ return token;
+ } else {
+ if (log)
+ log->Printf("SBProcess(%p)::LoadImageUsingPaths() => error: "
+ "process is running",
+ static_cast<void *>(process_sp.get()));
+ error.SetErrorString("process is running");
+ }
+ } else {
+ if (log)
+ log->Printf("SBProcess(%p)::LoadImageUsingPaths() => error: "
+ "called with invalid process",
+ static_cast<void *>(process_sp.get()));
+ error.SetErrorString("process is invalid");
+ }
+
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
lldb::SBError SBProcess::UnloadImage(uint32_t image_token) {
lldb::SBError sb_error;
ProcessSP process_sp(GetSP());
m_jit_start_addr, m_jit_end_addr, m_execution_unit_sp, exe_ctx,
can_interpret, eExecutionPolicyAlways));
- if (!jit_error.Success())
+ if (!jit_error.Success()) {
+ diagnostic_manager.Printf(eDiagnosticSeverityError,
+ "Error in PrepareForExecution: %s.",
+ jit_error.AsCString());
return false;
+ }
if (m_parser->GetGenerateDebugInfo()) {
lldb::ModuleSP jit_module_sp(m_execution_unit_sp->GetJITModule());
void *image_ptr;
const char *error_str;
};
+
+ extern void *memcpy(void *, void *, size_t size);
+ extern size_t strlen(const char *);
+
- void * __lldb_dlopen_wrapper (const char *path,
+ void * __lldb_dlopen_wrapper (const char *name,
+ const char *path_strings,
+ char *buffer,
__lldb_dlopen_result *result_ptr)
{
- result_ptr->image_ptr = dlopen(path, 2);
- if (result_ptr->image_ptr == (void *) 0x0)
+ // This is the case where the name is the full path:
+ if (path_strings == (char *) 0x0) {
+ result_ptr->image_ptr = dlopen(name, 2);
+ if (result_ptr->image_ptr != (void *) 0x0)
+ result_ptr->error_str = nullptr;
+ return nullptr;
+ }
+
+ // This is the case where we have a list of paths:
+ size_t name_len = strlen(name);
+ while (path_strings != (void *) 0x0 && path_strings[0] != '\0') {
+ size_t path_len = strlen(path_strings);
+ memcpy((void *) buffer, (void *) path_strings, path_len);
+ buffer[path_len] = '/';
+ char *target_ptr = buffer+path_len+1;
+ memcpy((void *) target_ptr, (void *) name, name_len + 1);
+ result_ptr->image_ptr = dlopen(buffer, 2);
+ if (result_ptr->image_ptr != (void *) 0x0) {
+ result_ptr->error_str = nullptr;
+ break;
+ }
result_ptr->error_str = dlerror();
+ path_strings = path_strings + path_len + 1;
+ }
return nullptr;
}
)";
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:
+ // We are passing four arguments, the basename, the list of places to look,
+ // a buffer big enough for all the path + name combos, 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);
+ arguments.PushValue(value);
+ arguments.PushValue(value);
do_dlopen_function = dlopen_utility_func_up->MakeFunctionCaller(
clang_void_pointer_type, arguments, exe_ctx.GetThreadSP(), utility_error);
uint32_t PlatformPOSIX::DoLoadImage(lldb_private::Process *process,
const lldb_private::FileSpec &remote_file,
- lldb_private::Status &error) {
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) {
+ if (loaded_image)
+ loaded_image->Clear();
+
std::string path;
path = remote_file.GetPath();
process->DeallocateMemory(return_addr);
});
+ // This will be the address of the storage for paths, if we are using them,
+ // or nullptr to signal we aren't.
+ lldb::addr_t path_array_addr = 0x0;
+ llvm::Optional<CleanUp> path_array_cleanup;
+
+ // This is the address to a buffer large enough to hold the largest path
+ // conjoined with the library name we're passing in. This is a convenience
+ // to avoid having to call malloc in the dlopen function.
+ lldb::addr_t buffer_addr = 0x0;
+ llvm::Optional<CleanUp> buffer_cleanup;
+
// Set the values into our args and write them to the target:
- arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
- arguments.GetValueAtIndex(1)->GetScalar() = return_addr;
+ if (paths != nullptr) {
+ // First insert the paths into the target. This is expected to be a
+ // continuous buffer with the strings laid out null terminated and
+ // end to end with an empty string terminating the buffer.
+ // We also compute the buffer's required size as we go.
+ size_t buffer_size = 0;
+ std::string path_array;
+ for (auto path : *paths) {
+ size_t path_size = path.size();
+ path_array.append(path);
+ path_array.push_back('\0');
+ if (path_size > buffer_size)
+ buffer_size = path_size;
+ }
+ path_array.push_back('\0');
+
+ path_array_addr = process->AllocateMemory(path_array.size(),
+ permissions,
+ utility_error);
+ if (path_array_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for path array: %s",
+ utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ // Make sure we deallocate the paths array.
+ path_array_cleanup.emplace([process, path_array_addr] {
+ process->DeallocateMemory(path_array_addr);
+ });
+
+ process->WriteMemory(path_array_addr, path_array.data(),
+ path_array.size(), utility_error);
+
+ if (utility_error.Fail()) {
+ error.SetErrorStringWithFormat("dlopen error: could not write path array:"
+ " %s", utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ // Now make spaces in the target for the buffer. We need to add one for
+ // the '/' that the utility function will insert and one for the '\0':
+ buffer_size += path.size() + 2;
+
+ buffer_addr = process->AllocateMemory(buffer_size,
+ permissions,
+ utility_error);
+ if (buffer_addr == LLDB_INVALID_ADDRESS) {
+ error.SetErrorStringWithFormat("dlopen error: could not allocate memory"
+ "for buffer: %s",
+ utility_error.AsCString());
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+ // Make sure we deallocate the buffer memory:
+ buffer_cleanup.emplace([process, buffer_addr] {
+ process->DeallocateMemory(buffer_addr);
+ });
+ }
+
+ arguments.GetValueAtIndex(0)->GetScalar() = path_addr;
+ arguments.GetValueAtIndex(1)->GetScalar() = path_array_addr;
+ arguments.GetValueAtIndex(2)->GetScalar() = buffer_addr;
+ arguments.GetValueAtIndex(3)->GetScalar() = return_addr;
+
lldb::addr_t func_args_addr = LLDB_INVALID_ADDRESS;
diagnostics.Clear();
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 "
+ error.SetErrorStringWithFormat("dlopen error: failed executing "
"dlopen wrapper function: %s",
diagnostics.GetString().c_str());
return LLDB_INVALID_IMAGE_TOKEN;
}
// The dlopen succeeded!
- if (token != 0x0)
+ if (token != 0x0) {
+ if (loaded_image && buffer_addr != 0x0)
+ {
+ // Capture the image which was loaded. We leave it in the buffer on
+ // exit from the dlopen function, so we can just read it from there:
+ std::string name_string;
+ process->ReadCStringFromMemory(buffer_addr, name_string, utility_error);
+ if (utility_error.Success())
+ loaded_image->SetFile(name_string, false,
+ llvm::sys::path::Style::posix);
+ }
return process->AddImageToken(token);
+ }
// We got an error, lets read in the error string:
std::string dlopen_error_str;
uint32_t DoLoadImage(lldb_private::Process *process,
const lldb_private::FileSpec &remote_file,
- lldb_private::Status &error) override;
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) override;
lldb_private::Status UnloadImage(lldb_private::Process *process,
uint32_t image_token) override;
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
}
- return DoLoadImage(process, remote_file, error);
+ return DoLoadImage(process, remote_file, nullptr, error);
}
if (local_file) {
if (error.Fail())
return LLDB_INVALID_IMAGE_TOKEN;
}
- return DoLoadImage(process, target_file, error);
- }
+ return DoLoadImage(process, target_file, nullptr, error);
+ }
if (remote_file) {
// Only remote file was specified so we don't have to do any copying
- return DoLoadImage(process, remote_file, error);
+ return DoLoadImage(process, remote_file, nullptr, error);
}
error.SetErrorString("Neither local nor remote file was specified");
uint32_t Platform::DoLoadImage(lldb_private::Process *process,
const lldb_private::FileSpec &remote_file,
- lldb_private::Status &error) {
+ const std::vector<std::string> *paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_image) {
error.SetErrorString("LoadImage is not supported on the current platform");
return LLDB_INVALID_IMAGE_TOKEN;
}
+uint32_t Platform::LoadImageUsingPaths(lldb_private::Process *process,
+ const lldb_private::FileSpec &remote_filename,
+ const std::vector<std::string> &paths,
+ lldb_private::Status &error,
+ lldb_private::FileSpec *loaded_path)
+{
+ FileSpec file_to_use;
+ if (remote_filename.IsAbsolute())
+ file_to_use = FileSpec(remote_filename.GetFilename().GetStringRef(),
+ false,
+ remote_filename.GetPathStyle());
+ else
+ file_to_use = remote_filename;
+
+ return DoLoadImage(process, file_to_use, &paths, error, loaded_path);
+}
+
Status Platform::UnloadImage(lldb_private::Process *process,
uint32_t image_token) {
return Status("UnloadImage is not supported on the current platform");