DYLDRendezvous.cpp revision 263363
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/Symbol/Symbol.h"
18#include "lldb/Target/Process.h"
19#include "lldb/Target/Target.h"
20
21#include "DYLDRendezvous.h"
22
23using namespace lldb;
24using namespace lldb_private;
25
26/// Locates the address of the rendezvous structure.  Returns the address on
27/// success and LLDB_INVALID_ADDRESS on failure.
28static addr_t
29ResolveRendezvousAddress(Process *process)
30{
31    addr_t info_location;
32    addr_t info_addr;
33    Error error;
34
35    info_location = process->GetImageInfoAddress();
36
37    if (info_location == LLDB_INVALID_ADDRESS)
38        return LLDB_INVALID_ADDRESS;
39
40    info_addr = process->ReadPointerFromMemory(info_location, error);
41    if (error.Fail())
42        return LLDB_INVALID_ADDRESS;
43
44    if (info_addr == 0)
45        return LLDB_INVALID_ADDRESS;
46
47    return info_addr;
48}
49
50DYLDRendezvous::DYLDRendezvous(Process *process)
51    : m_process(process),
52      m_rendezvous_addr(LLDB_INVALID_ADDRESS),
53      m_current(),
54      m_previous(),
55      m_soentries(),
56      m_added_soentries(),
57      m_removed_soentries()
58{
59    m_thread_info.valid = false;
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 = ReadWord(cursor, &info.version, word_size)))
92        return false;
93
94    if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
95        return false;
96
97    if (!(cursor = ReadPointer(cursor, &info.brk)))
98        return false;
99
100    if (!(cursor = ReadWord(cursor, &info.state, word_size)))
101        return false;
102
103    if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
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::ReadWord(addr_t addr, uint64_t *dst, size_t size)
238{
239    Error error;
240
241    *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
242    if (error.Fail())
243        return 0;
244
245    return addr + size;
246}
247
248addr_t
249DYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst)
250{
251    Error error;
252
253    *dst = m_process->ReadPointerFromMemory(addr, error);
254    if (error.Fail())
255        return 0;
256
257    return addr + m_process->GetAddressByteSize();
258}
259
260std::string
261DYLDRendezvous::ReadStringFromMemory(addr_t addr)
262{
263    std::string str;
264    Error error;
265    size_t size;
266    char c;
267
268    if (addr == LLDB_INVALID_ADDRESS)
269        return std::string();
270
271    for (;;) {
272        size = m_process->DoReadMemory(addr, &c, 1, error);
273        if (size != 1 || error.Fail())
274            return std::string();
275        if (c == 0)
276            break;
277        else {
278            str.push_back(c);
279            addr++;
280        }
281    }
282
283    return str;
284}
285
286bool
287DYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr, SOEntry &entry)
288{
289    entry.clear();
290
291    entry.link_addr = addr;
292
293    if (!(addr = ReadPointer(addr, &entry.base_addr)))
294        return false;
295
296    // mips adds an extra load offset field to the link map struct on
297    // FreeBSD and NetBSD (need to validate other OSes).
298    // http://svnweb.freebsd.org/base/head/sys/sys/link_elf.h?revision=217153&view=markup#l57
299    const ArchSpec &arch = m_process->GetTarget().GetArchitecture();
300    if (arch.GetCore() == ArchSpec::eCore_mips64)
301    {
302        assert (arch.GetTriple().getOS() == llvm::Triple::FreeBSD ||
303                arch.GetTriple().getOS() == llvm::Triple::NetBSD);
304        addr_t mips_l_offs;
305        if (!(addr = ReadPointer(addr, &mips_l_offs)))
306            return false;
307        if (mips_l_offs != 0 && mips_l_offs != entry.base_addr)
308            return false;
309    }
310
311    if (!(addr = ReadPointer(addr, &entry.path_addr)))
312        return false;
313
314    if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
315        return false;
316
317    if (!(addr = ReadPointer(addr, &entry.next)))
318        return false;
319
320    if (!(addr = ReadPointer(addr, &entry.prev)))
321        return false;
322
323    entry.path = ReadStringFromMemory(entry.path_addr);
324
325    return true;
326}
327
328
329bool
330DYLDRendezvous::FindMetadata(const char *name, PThreadField field, uint32_t& value)
331{
332    Target& target = m_process->GetTarget();
333
334    SymbolContextList list;
335    if (!target.GetImages().FindSymbolsWithNameAndType (ConstString(name), eSymbolTypeAny, list))
336        return false;
337
338    Address address = list[0].symbol->GetAddress();
339    addr_t addr = address.GetLoadAddress (&target);
340    if (addr == LLDB_INVALID_ADDRESS)
341        return false;
342
343    Error error;
344    value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(addr + field*sizeof(uint32_t), sizeof(uint32_t), 0, error);
345    if (error.Fail())
346        return false;
347
348    if (field == eSize)
349        value /= 8; // convert bits to bytes
350
351    return true;
352}
353
354const DYLDRendezvous::ThreadInfo&
355DYLDRendezvous::GetThreadInfo()
356{
357    if (!m_thread_info.valid)
358    {
359        bool ok = true;
360
361        ok &= FindMetadata ("_thread_db_pthread_dtvp", eOffset, m_thread_info.dtv_offset);
362        ok &= FindMetadata ("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
363        ok &= FindMetadata ("_thread_db_link_map_l_tls_modid", eOffset, m_thread_info.modid_offset);
364        ok &= FindMetadata ("_thread_db_dtv_t_pointer_val", eOffset, m_thread_info.tls_offset);
365
366        if (ok)
367            m_thread_info.valid = true;
368    }
369
370    return m_thread_info;
371}
372
373void
374DYLDRendezvous::DumpToLog(Log *log) const
375{
376    int state = GetState();
377
378    if (!log)
379        return;
380
381    log->PutCString("DYLDRendezvous:");
382    log->Printf("   Address: %" PRIx64, GetRendezvousAddress());
383    log->Printf("   Version: %" PRIu64, GetVersion());
384    log->Printf("   Link   : %" PRIx64, GetLinkMapAddress());
385    log->Printf("   Break  : %" PRIx64, GetBreakAddress());
386    log->Printf("   LDBase : %" PRIx64, GetLDBase());
387    log->Printf("   State  : %s",
388                (state == eConsistent) ? "consistent" :
389                (state == eAdd)        ? "add"        :
390                (state == eDelete)     ? "delete"     : "unknown");
391
392    iterator I = begin();
393    iterator E = end();
394
395    if (I != E)
396        log->PutCString("DYLDRendezvous SOEntries:");
397
398    for (int i = 1; I != E; ++I, ++i)
399    {
400        log->Printf("\n   SOEntry [%d] %s", i, I->path.c_str());
401        log->Printf("      Base : %" PRIx64, I->base_addr);
402        log->Printf("      Path : %" PRIx64, I->path_addr);
403        log->Printf("      Dyn  : %" PRIx64, I->dyn_addr);
404        log->Printf("      Next : %" PRIx64, I->next);
405        log->Printf("      Prev : %" PRIx64, I->prev);
406    }
407}
408