1//===-- DYLDRendezvous.cpp --------------------------------------*- C++ -*-===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10// C Includes
11// C++ Includes
12// Other libraries and framework includes
13#include "lldb/Core/ArchSpec.h"
14#include "lldb/Core/Error.h"
15#include "lldb/Core/Log.h"
16#include "lldb/Core/Module.h"
17#include "lldb/Target/Process.h"
18#include "lldb/Target/Target.h"
19
20#include "DYLDRendezvous.h"
21
22using namespace lldb;
23using namespace lldb_private;
24
25/// Locates the address of the rendezvous structure.  Returns the address on
26/// success and LLDB_INVALID_ADDRESS on failure.
27static addr_t
28ResolveRendezvousAddress(Process *process)
29{
30    addr_t info_location;
31    addr_t info_addr;
32    Error error;
33    size_t size;
34
35    info_location = process->GetImageInfoAddress();
36
37    if (info_location == LLDB_INVALID_ADDRESS)
38        return LLDB_INVALID_ADDRESS;
39
40    info_addr = 0;
41    size = process->DoReadMemory(info_location, &info_addr,
42                                 process->GetAddressByteSize(), error);
43    if (size != process->GetAddressByteSize() || error.Fail())
44        return LLDB_INVALID_ADDRESS;
45
46    if (info_addr == 0)
47        return LLDB_INVALID_ADDRESS;
48
49    return info_addr;
50}
51
52DYLDRendezvous::DYLDRendezvous(Process *process)
53    : m_process(process),
54      m_rendezvous_addr(LLDB_INVALID_ADDRESS),
55      m_current(),
56      m_previous(),
57      m_soentries(),
58      m_added_soentries(),
59      m_removed_soentries()
60{
61    // Cache a copy of the executable path
62    if (m_process)
63    {
64        Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
65        if (exe_mod)
66            exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX);
67    }
68}
69
70bool
71DYLDRendezvous::Resolve()
72{
73    const size_t word_size = 4;
74    Rendezvous info;
75    size_t address_size;
76    size_t padding;
77    addr_t info_addr;
78    addr_t cursor;
79
80    address_size = m_process->GetAddressByteSize();
81    padding = address_size - word_size;
82
83    if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
84        cursor = info_addr = ResolveRendezvousAddress(m_process);
85    else
86        cursor = info_addr = m_rendezvous_addr;
87
88    if (cursor == LLDB_INVALID_ADDRESS)
89        return false;
90
91    if (!(cursor = ReadMemory(cursor, &info.version, word_size)))
92        return false;
93
94    if (!(cursor = ReadMemory(cursor + padding, &info.map_addr, address_size)))
95        return false;
96
97    if (!(cursor = ReadMemory(cursor, &info.brk, address_size)))
98        return false;
99
100    if (!(cursor = ReadMemory(cursor, &info.state, word_size)))
101        return false;
102
103    if (!(cursor = ReadMemory(cursor + padding, &info.ldbase, address_size)))
104        return false;
105
106    // The rendezvous was successfully read.  Update our internal state.
107    m_rendezvous_addr = info_addr;
108    m_previous = m_current;
109    m_current = info;
110
111    return UpdateSOEntries();
112}
113
114bool
115DYLDRendezvous::IsValid()
116{
117    return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
118}
119
120bool
121DYLDRendezvous::UpdateSOEntries()
122{
123    SOEntry entry;
124
125    if (m_current.map_addr == 0)
126        return false;
127
128    // When the previous and current states are consistent this is the first
129    // time we have been asked to update.  Just take a snapshot of the currently
130    // loaded modules.
131    if (m_previous.state == eConsistent && m_current.state == eConsistent)
132        return TakeSnapshot(m_soentries);
133
134    // If we are about to add or remove a shared object clear out the current
135    // state and take a snapshot of the currently loaded images.
136    if (m_current.state == eAdd || m_current.state == eDelete)
137    {
138        assert(m_previous.state == eConsistent);
139        m_soentries.clear();
140        m_added_soentries.clear();
141        m_removed_soentries.clear();
142        return TakeSnapshot(m_soentries);
143    }
144    assert(m_current.state == eConsistent);
145
146    // Otherwise check the previous state to determine what to expect and update
147    // accordingly.
148    if (m_previous.state == eAdd)
149        return UpdateSOEntriesForAddition();
150    else if (m_previous.state == eDelete)
151        return UpdateSOEntriesForDeletion();
152
153    return false;
154}
155
156bool
157DYLDRendezvous::UpdateSOEntriesForAddition()
158{
159    SOEntry entry;
160    iterator pos;
161
162    assert(m_previous.state == eAdd);
163
164    if (m_current.map_addr == 0)
165        return false;
166
167    for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next)
168    {
169        if (!ReadSOEntryFromMemory(cursor, entry))
170            return false;
171
172        // Only add shared libraries and not the executable.
173        // On Linux this is indicated by an empty path in the entry.
174        // On FreeBSD it is the name of the executable.
175        if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
176            continue;
177
178        pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
179        if (pos == m_soentries.end())
180        {
181            m_soentries.push_back(entry);
182            m_added_soentries.push_back(entry);
183        }
184    }
185
186    return true;
187}
188
189bool
190DYLDRendezvous::UpdateSOEntriesForDeletion()
191{
192    SOEntryList entry_list;
193    iterator pos;
194
195    assert(m_previous.state == eDelete);
196
197    if (!TakeSnapshot(entry_list))
198        return false;
199
200    for (iterator I = begin(); I != end(); ++I)
201    {
202        pos = std::find(entry_list.begin(), entry_list.end(), *I);
203        if (pos == entry_list.end())
204            m_removed_soentries.push_back(*I);
205    }
206
207    m_soentries = entry_list;
208    return true;
209}
210
211bool
212DYLDRendezvous::TakeSnapshot(SOEntryList &entry_list)
213{
214    SOEntry entry;
215
216    if (m_current.map_addr == 0)
217        return false;
218
219    for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next)
220    {
221        if (!ReadSOEntryFromMemory(cursor, entry))
222            return false;
223
224        // Only add shared libraries and not the executable.
225        // On Linux this is indicated by an empty path in the entry.
226        // On FreeBSD it is the name of the executable.
227        if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
228            continue;
229
230        entry_list.push_back(entry);
231    }
232
233    return true;
234}
235
236addr_t
237DYLDRendezvous::ReadMemory(addr_t addr, void *dst, size_t size)
238{
239    size_t bytes_read;
240    Error error;
241
242    bytes_read = m_process->DoReadMemory(addr, dst, size, error);
243    if (bytes_read != size || error.Fail())
244        return 0;
245
246    return addr + bytes_read;
247}
248
249std::string
250DYLDRendezvous::ReadStringFromMemory(addr_t addr)
251{
252    std::string str;
253    Error error;
254    size_t size;
255    char c;
256
257    if (addr == LLDB_INVALID_ADDRESS)
258        return std::string();
259
260    for (;;) {
261        size = m_process->DoReadMemory(addr, &c, 1, error);
262        if (size != 1 || error.Fail())
263            return std::string();
264        if (c == 0)
265            break;
266        else {
267            str.push_back(c);
268            addr++;
269        }
270    }
271
272    return str;
273}
274
275bool
276DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry)
277{
278    size_t address_size = m_process->GetAddressByteSize();
279
280    entry.clear();
281
282    if (!(addr = ReadMemory(addr, &entry.base_addr, address_size)))
283        return false;
284
285    if (!(addr = ReadMemory(addr, &entry.path_addr, address_size)))
286        return false;
287
288    if (!(addr = ReadMemory(addr, &entry.dyn_addr, address_size)))
289        return false;
290
291    if (!(addr = ReadMemory(addr, &entry.next, address_size)))
292        return false;
293
294    if (!(addr = ReadMemory(addr, &entry.prev, address_size)))
295        return false;
296
297    entry.path = ReadStringFromMemory(entry.path_addr);
298
299    return true;
300}
301
302void
303DYLDRendezvous::DumpToLog(Log *log) const
304{
305    int state = GetState();
306
307    if (!log)
308        return;
309
310    log->PutCString("DYLDRendezvous:");
311    log->Printf("   Address: %" PRIx64, GetRendezvousAddress());
312    log->Printf("   Version: %" PRIu64, GetVersion());
313    log->Printf("   Link   : %" PRIx64, GetLinkMapAddress());
314    log->Printf("   Break  : %" PRIx64, GetBreakAddress());
315    log->Printf("   LDBase : %" PRIx64, GetLDBase());
316    log->Printf("   State  : %s",
317                (state == eConsistent) ? "consistent" :
318                (state == eAdd)        ? "add"        :
319                (state == eDelete)     ? "delete"     : "unknown");
320
321    iterator I = begin();
322    iterator E = end();
323
324    if (I != E)
325        log->PutCString("DYLDRendezvous SOEntries:");
326
327    for (int i = 1; I != E; ++I, ++i)
328    {
329        log->Printf("\n   SOEntry [%d] %s", i, I->path.c_str());
330        log->Printf("      Base : %" PRIx64, I->base_addr);
331        log->Printf("      Path : %" PRIx64, I->path_addr);
332        log->Printf("      Dyn  : %" PRIx64, I->dyn_addr);
333        log->Printf("      Next : %" PRIx64, I->next);
334        log->Printf("      Prev : %" PRIx64, I->prev);
335    }
336}
337