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