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