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