[lldb-dev] Better support for Shared Object load address discovery
Aidan Dodds
aidan at codeplay.com
Thu Apr 30 10:11:11 PDT 2015
Sorry for the delayed response Greg.
I have been working on a proof of concept which adds a new dynamic
loader for gdb-remote as you suggest, and perhaps when I get it working with
the behavior I'm expecting, I can post it for review and we can have a
look at how best to integrate something like this with upstream LLDB.
Perhaps
it can indeed by integrated cleanly with the posixDYLD.
There is one issue that has emerged while I have been working on this,
with regard to how LLDB regards the target executable as the first
module loaded.
Since the first and potentially only module that I can give to LLDB is a
shared library it doesn't seem to make sense to regard it as the
executable module.
My thoughts were to change the way the target handles this, so that LLDB
instead looks for the first executable module loaded, which would
exclude shared object.
I changed it to be something like this:
ModuleSP
Target::GetExecutableModule ()
{
for ( uint32_t i = 0; i < (uint32_t)m_images.GetSize( ); ++i )
{
ModuleSP modsp = m_images.GetModuleAtIndex( i );
lldb_private::ObjectFile * obj = modsp->GetObjectFile( );
if ( obj == nullptr )
continue;
ObjectFile::Type type = obj->GetType( );
if ( type == ObjectFile::Type::eTypeExecutable )
return modsp;
}
return ModuleSP();
}
Module*
Target::GetExecutableModulePointer ()
{
return GetExecutableModule().get();
}
This problem surfaced when I noticed the posixDYLD (which I was using at
the time) takes the executable (module index 0), and forcefully sets its
load address based
on the entry point of the program. In the case of my shared library
this is the incorrect address.
Does anyone have thoughts or opinions on this?
On 24/04/2015 18:31, Greg Clayton wrote:
> A few things to check on:
>
> lldb_private::Process already has:
>
> //------------------------------------------------------------------
> // Returns AUXV structure found in many ELF-based environments.
> //
> // The default action is to return an empty data buffer.
> //
> // @return
> // A data buffer containing the contents of the AUXV data.
> //------------------------------------------------------------------
> virtual const lldb::DataBufferSP
> GetAuxvData();
>
> Would this help?
>
> What dynamic loader is currently being selected when you debug? PluginDynamicLoaderPosixDYLD? If so, maybe PluginDynamicLoaderPosixDYLD can just use Process::GetAuxvData()? It would be nice to abstract this through lldb_private::Process if PluginDynamicLoaderPosixDYLD just needs a bit of help to get started as I would prefer to keep using PluginDynamicLoaderPosixDYLD or its subclasses if possible so that we don't have to create DynamicLoaderGDBRemote if we don't need to.
>
> Greg
>
>
>> On Apr 24, 2015, at 10:21 AM, Aidan Dodds <aidan at codeplay.com> wrote:
>>
>> Thanks for the quick response Greg.
>>
>> Its an interesting point you raise when it comes to knowing how any DynamicLoaderGDBRemote would detect newly loaded
>> modules. Since my target is Linux based the standard approach of hooking the rendezvous address should still work.
>> Presumably since I will have the full list of loaded modules and their locations, I would be able to find ld-linux.so,
>> and I additionally have access to the auxvector via the $qXfer:auxv packet. Perhaps these will provide enough data for me
>> to locate the dyld rendezvous address.
>>
>> Perhaps lldbPluginDynamicLoaderPosixDYLD would serve as a suitable base class for DynamicLoaderGDBRemote.
>>
>> I'll mull over your suggestions this weekend and get back to you with something more concrete on Monday.
>> In the mean time, if anyone else also has ideas or suggestions I'm all ears.
>>
>> Thanks again,
>> Aidan
>>
>>
>> On 24/04/2015 17:45, Greg Clayton wrote:
>>> Actually scratch this last approach.
>>>
>>> A better approach would be to make a new DynamicLoader subclass called DynamicLoaderGDBRemote. This plug-in would only be created if the process' name is "gdb-remote" and if the ProcessGDBRemote supports the qXfer:libraries:read command. Then the DynamicLoaderGDBRemote can cast the abstract lldb_private::Process to a ProcessGDBRemote and use any special functions in ProcessGDBRemote to get this info and make the shared libraries get loaded correctly.
>>>
>>> We try to make our GDB server binaries and simple as possible and we don't vend shared library info from them. In this case we want to use the correct DynamicLoader plug-ins for the current process. It also allows us to use the DynamicLoader plug-ins with multiple processes. For example, DynamicLoaderDarwinKernel can either be used with ProcessGDBRemote or ProcessKDP. It just uses generic process calls on lldb_private::Process to do its thing so these plug-ins can work with any lldb_private::Process. In this case we have a case where the DynamicLoaderGDBRemote would only work with ProcessGDBRemote. In the static class function you would need to check the process plug-in name:
>>>
>>> DynamicLoader *
>>> DynamicLoaderGDBRemote::CreateInstance (Process* process, bool force)
>>> {
>>> if (process->GetPluginName() == ProcessGDBRemote::GetPluginNameStatic())
>>> {
>>> ProcessGDBRemote* gdb_process = (ProcessGDBRemote*)process;
>>> if (gdb_process->Supports_qXfer_libraries())
>>> {
>>> // Create a new instance of DynamicLoaderGDBRemote and return it
>>> }
>>> }
>>> return NULL;
>>> }
>>>
>>> Then the question becomes how do shared library loaded notifications come in when a shared library is loaded dynamically?
>>>
>>> Greg Clayton
>>>
>>>
>>>> On Apr 24, 2015, at 9:28 AM, Greg Clayton <gclayton at apple.com> wrote:
>>>>
>>>> This if fine. We will want to make these changes as a virtual call in lldb_private::Process where we ask the process if it can help discover loaded shared libraries. The dynamic loader plug-ins will need to be modified to take advantage of this as they get an abstract "lldb_private::Process*" when they are created. Then the dynamic loader plug-ins can ask the process if they can supply the module loaded info, and take advantage of this if needed.
>>>>
>>>> Maybe something like:
>>>>
>>>> namespace lldb_private {
>>>> class Process {
>>>> ...
>>>> virtual Error GetLoadedSharedLibraries(std::vector<LoadedModuleInfo> &loaded_modules)
>>>> {
>>>> Error error;
>>>> loaded_modules.clear();
>>>> error.SetErrorString("unimplemented");
>>>> return error;
>>>> }
>>>>
>>>>
>>>> I don't believe qModuleInfo if the correct call to be using. I believe this is a platform packet only that allows you to ask about what a remote file contains. It answers with the info on each contained file that is contained within a file and returns the architecture, file offset, and file size for each different slice. Most files only have one item inside them, so they would return info on that single file. This isn't designed to be tied to a process nor is it designed to pass along any module load info for a specific process.
>>>>
>>>> I would just use the qXfer:libraries:read command and implement this as a new call in "lldb_private::Process *" that returns an error in the default implementation, and the ProcessGDBRemote will have a override version of this function that can give us the info we need.
>>>>
>>>> Let me know if you need any help in making this happen.
>>>>
>>>> Greg
>>>>
>>>>> On Apr 24, 2015, at 7:06 AM, Aidan Dodds <aidan at codeplay.com> wrote:
>>>>>
>>>>> Hi,
>>>>>
>>>>> I would like to improve support for the case where LLDB is debugging a remote target (via gdbserver)
>>>>> yet doesn't have access to the target executable, and only has access to a shared object already
>>>>> loaded by the target.
>>>>>
>>>>> This is supported from GDB, as I can connect to a remote GDBServer without specifying any executable,
>>>>> and after issuing 'set solib-search-paths' it will resolve all of the loaded shared objects correctly, while
>>>>> still being blind to the target executable.
>>>>>
>>>>> I feel like LLDB should be able to connect to a remote target, and then after issuing a 'target modules add'
>>>>> command be able to resolve its load address automatically. I believe LLDB can already do this to some extent
>>>>> via the 'qModuleInfo' RSP packet.
>>>>>
>>>>> The 'qXfer:libraries:read' packet is provided by GDBServer, and will return an XML document containing
>>>>> all of the shared objects loaded by the target and their load addresses. It is possible to parse this now using the
>>>>> recent libxml2 support in LLDB.
>>>>>
>>>>> GDBRemoteCommunicationClient::GetModuleInfo() could be extended to try and gain information about a
>>>>> loaded module via the 'qXfer:libraries:read' packet if 'qModuleInfo' fails or is not supported.
>>>>>
>>>>> I would be interested to hear what others think, or get some feedback if I have perhaps misunderstood something.
>>>>>
>>>>> Thanks,
>>>>> Aidan Dodds
>>>>>
>>>>> _______________________________________________
>>>>> lldb-dev mailing list
>>>>> lldb-dev at cs.uiuc.edu
>>>>> http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev
>>>> _______________________________________________
>>>> lldb-dev mailing list
>>>> lldb-dev at cs.uiuc.edu
>>>> http://lists.cs.uiuc.edu/mailman/listinfo/lldb-dev
More information about the lldb-dev
mailing list