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
23using namespace lldb;
24using namespace lldb_private;
25
26/// Locates the address of the rendezvous structure.  Returns the address on
27/// success and LLDB_INVALID_ADDRESS on failure.
28static 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
48HexagonDYLDRendezvous::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
65bool 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
107void HexagonDYLDRendezvous::SetRendezvousAddress(lldb::addr_t addr) {
108  m_rendezvous_addr = addr;
109}
110
111bool HexagonDYLDRendezvous::IsValid() {
112  return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
113}
114
115bool 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
152bool 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
180bool 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
198bool 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
220addr_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
231addr_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
241std::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
265bool 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
290bool 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
317const HexagonDYLDRendezvous::ThreadInfo &
318HexagonDYLDRendezvous::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
338void 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