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     pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
172     if (pos == m_soentries.end()) {
173       m_soentries.push_back(entry);
174       m_added_soentries.push_back(entry);
175     }
176   }
177 
178   return true;
179 }
180 
181 bool HexagonDYLDRendezvous::UpdateSOEntriesForDeletion() {
182   SOEntryList entry_list;
183   iterator pos;
184 
185   assert(m_previous.state == eDelete);
186 
187   if (!TakeSnapshot(entry_list))
188     return false;
189 
190   for (iterator I = begin(); I != end(); ++I) {
191     pos = std::find(entry_list.begin(), entry_list.end(), *I);
192     if (pos == entry_list.end())
193       m_removed_soentries.push_back(*I);
194   }
195 
196   m_soentries = entry_list;
197   return true;
198 }
199 
200 bool HexagonDYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
201   SOEntry entry;
202 
203   if (m_current.map_addr == 0)
204     return false;
205 
206   for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
207     if (!ReadSOEntryFromMemory(cursor, entry))
208       return false;
209 
210     // Only add shared libraries and not the executable. On Linux this is
211     // indicated by an empty path in the entry. On FreeBSD it is the name of
212     // the executable.
213     if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
214       continue;
215 
216     entry_list.push_back(entry);
217   }
218 
219   return true;
220 }
221 
222 addr_t HexagonDYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst,
223                                        size_t size) {
224   Status error;
225 
226   *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
227   if (error.Fail())
228     return 0;
229 
230   return addr + size;
231 }
232 
233 addr_t HexagonDYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
234   Status error;
235 
236   *dst = m_process->ReadPointerFromMemory(addr, error);
237   if (error.Fail())
238     return 0;
239 
240   return addr + m_process->GetAddressByteSize();
241 }
242 
243 std::string HexagonDYLDRendezvous::ReadStringFromMemory(addr_t addr) {
244   std::string str;
245   Status error;
246   size_t size;
247   char c;
248 
249   if (addr == LLDB_INVALID_ADDRESS)
250     return std::string();
251 
252   for (;;) {
253     size = m_process->ReadMemory(addr, &c, 1, error);
254     if (size != 1 || error.Fail())
255       return std::string();
256     if (c == 0)
257       break;
258     else {
259       str.push_back(c);
260       addr++;
261     }
262   }
263 
264   return str;
265 }
266 
267 bool HexagonDYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr,
268                                                   SOEntry &entry) {
269   entry.clear();
270   entry.link_addr = addr;
271 
272   if (!(addr = ReadPointer(addr, &entry.base_addr)))
273     return false;
274 
275   if (!(addr = ReadPointer(addr, &entry.path_addr)))
276     return false;
277 
278   if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
279     return false;
280 
281   if (!(addr = ReadPointer(addr, &entry.next)))
282     return false;
283 
284   if (!(addr = ReadPointer(addr, &entry.prev)))
285     return false;
286 
287   entry.path = ReadStringFromMemory(entry.path_addr);
288 
289   return true;
290 }
291 
292 bool HexagonDYLDRendezvous::FindMetadata(const char *name, PThreadField field,
293                                          uint32_t &value) {
294   Target &target = m_process->GetTarget();
295 
296   SymbolContextList list;
297   target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
298                                                 eSymbolTypeAny, list);
299   if (list.IsEmpty())
300     return false;
301 
302   Address address = list[0].symbol->GetAddress();
303   addr_t addr = address.GetLoadAddress(&target);
304   if (addr == LLDB_INVALID_ADDRESS)
305     return false;
306 
307   Status error;
308   value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
309       addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
310   if (error.Fail())
311     return false;
312 
313   if (field == eSize)
314     value /= 8; // convert bits to bytes
315 
316   return true;
317 }
318 
319 const HexagonDYLDRendezvous::ThreadInfo &
320 HexagonDYLDRendezvous::GetThreadInfo() {
321   if (!m_thread_info.valid) {
322     bool ok = true;
323 
324     ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
325                        m_thread_info.dtv_offset);
326     ok &=
327         FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
328     ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
329                        m_thread_info.modid_offset);
330     ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
331                        m_thread_info.tls_offset);
332 
333     if (ok)
334       m_thread_info.valid = true;
335   }
336 
337   return m_thread_info;
338 }
339 
340 void HexagonDYLDRendezvous::DumpToLog(Log *log) const {
341   int state = GetState();
342 
343   if (!log)
344     return;
345 
346   log->PutCString("HexagonDYLDRendezvous:");
347   LLDB_LOGF(log, "   Address: %" PRIx64, GetRendezvousAddress());
348   LLDB_LOGF(log, "   Version: %" PRIu64, GetVersion());
349   LLDB_LOGF(log, "   Link   : %" PRIx64, GetLinkMapAddress());
350   LLDB_LOGF(log, "   Break  : %" PRIx64, GetBreakAddress());
351   LLDB_LOGF(log, "   LDBase : %" PRIx64, GetLDBase());
352   LLDB_LOGF(log, "   State  : %s",
353             (state == eConsistent)
354                 ? "consistent"
355                 : (state == eAdd) ? "add"
356                                   : (state == eDelete) ? "delete" : "unknown");
357 
358   iterator I = begin();
359   iterator E = end();
360 
361   if (I != E)
362     log->PutCString("HexagonDYLDRendezvous SOEntries:");
363 
364   for (int i = 1; I != E; ++I, ++i) {
365     LLDB_LOGF(log, "\n   SOEntry [%d] %s", i, I->path.c_str());
366     LLDB_LOGF(log, "      Base : %" PRIx64, I->base_addr);
367     LLDB_LOGF(log, "      Path : %" PRIx64, I->path_addr);
368     LLDB_LOGF(log, "      Dyn  : %" PRIx64, I->dyn_addr);
369     LLDB_LOGF(log, "      Next : %" PRIx64, I->next);
370     LLDB_LOGF(log, "      Prev : %" PRIx64, I->prev);
371   }
372 }
373