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
23 using namespace lldb;
24 using namespace lldb_private;
25
26 /// Locates the address of the rendezvous structure. Returns the address on
27 /// success and LLDB_INVALID_ADDRESS on failure.
28 static addr_t
ResolveRendezvousAddress(Process * process)29 ResolveRendezvousAddress(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
DYLDRendezvous(Process * process)50 DYLDRendezvous::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
70 bool
Resolve()71 DYLDRendezvous::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
114 bool
IsValid()115 DYLDRendezvous::IsValid()
116 {
117 return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
118 }
119
120 bool
UpdateSOEntries()121 DYLDRendezvous::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
156 bool
UpdateSOEntriesForAddition()157 DYLDRendezvous::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
189 bool
UpdateSOEntriesForDeletion()190 DYLDRendezvous::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
211 bool
TakeSnapshot(SOEntryList & entry_list)212 DYLDRendezvous::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
236 addr_t
ReadWord(addr_t addr,uint64_t * dst,size_t size)237 DYLDRendezvous::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
248 addr_t
ReadPointer(addr_t addr,addr_t * dst)249 DYLDRendezvous::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
260 std::string
ReadStringFromMemory(addr_t addr)261 DYLDRendezvous::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
286 bool
ReadSOEntryFromMemory(lldb::addr_t addr,SOEntry & entry)287 DYLDRendezvous::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
329 bool
FindMetadata(const char * name,PThreadField field,uint32_t & value)330 DYLDRendezvous::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
354 const DYLDRendezvous::ThreadInfo&
GetThreadInfo()355 DYLDRendezvous::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
373 void
DumpToLog(Log * log) const374 DYLDRendezvous::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