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 
22 using namespace lldb;
23 using namespace lldb_private;
24 
25 /// Locates the address of the rendezvous structure.  Returns the address on
26 /// success and LLDB_INVALID_ADDRESS on failure.
27 static addr_t
ResolveRendezvousAddress(Process * process)28 ResolveRendezvousAddress(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 
DYLDRendezvous(Process * process)52 DYLDRendezvous::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 
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 = 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 
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
ReadMemory(addr_t addr,void * dst,size_t size)237 DYLDRendezvous::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 
249 std::string
ReadStringFromMemory(addr_t addr)250 DYLDRendezvous::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 
275 bool
ReadSOEntryFromMemory(lldb::addr_t addr,SOEntry & entry)276 DYLDRendezvous::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 
302 void
DumpToLog(Log * log) const303 DYLDRendezvous::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