1//===-- DNBDataRef.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//  Created by Greg Clayton on 1/11/06.
10//
11//===----------------------------------------------------------------------===//
12
13#include "DNBDataRef.h"
14#include "DNBLog.h"
15#include <cassert>
16#include <cctype>
17#include <libkern/OSByteOrder.h>
18
19// Constructor
20
21DNBDataRef::DNBDataRef()
22    : m_start(NULL), m_end(NULL), m_swap(false), m_ptrSize(0),
23      m_addrPCRelative(INVALID_NUB_ADDRESS), m_addrTEXT(INVALID_NUB_ADDRESS),
24      m_addrDATA(INVALID_NUB_ADDRESS) {}
25
26// Constructor
27
28DNBDataRef::DNBDataRef(const uint8_t *start, size_t size, bool swap)
29    : m_start(start), m_end(start + size), m_swap(swap), m_ptrSize(0),
30      m_addrPCRelative(INVALID_NUB_ADDRESS), m_addrTEXT(INVALID_NUB_ADDRESS),
31      m_addrDATA(INVALID_NUB_ADDRESS) {}
32
33// Destructor
34
35DNBDataRef::~DNBDataRef() = default;
36
37// Get8
38uint8_t DNBDataRef::Get8(offset_t *offset_ptr) const {
39  uint8_t val = 0;
40  if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
41    val = *(m_start + *offset_ptr);
42    *offset_ptr += sizeof(val);
43  }
44  return val;
45}
46
47// Get16
48uint16_t DNBDataRef::Get16(offset_t *offset_ptr) const {
49  uint16_t val = 0;
50  if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
51    const uint8_t *p = m_start + *offset_ptr;
52    memcpy(&val, p, sizeof(uint16_t));
53
54    if (m_swap)
55      val = OSSwapInt16(val);
56
57    // Advance the offset
58    *offset_ptr += sizeof(val);
59  }
60  return val;
61}
62
63// Get32
64uint32_t DNBDataRef::Get32(offset_t *offset_ptr) const {
65  uint32_t val = 0;
66  if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
67    const uint8_t *p = m_start + *offset_ptr;
68    memcpy(&val, p, sizeof(uint32_t));
69    if (m_swap)
70      val = OSSwapInt32(val);
71
72    // Advance the offset
73    *offset_ptr += sizeof(val);
74  }
75  return val;
76}
77
78// Get64
79uint64_t DNBDataRef::Get64(offset_t *offset_ptr) const {
80  uint64_t val = 0;
81  if (ValidOffsetForDataOfSize(*offset_ptr, sizeof(val))) {
82    const uint8_t *p = m_start + *offset_ptr;
83    memcpy(&val, p, sizeof(uint64_t));
84    if (m_swap)
85      val = OSSwapInt64(val);
86
87    // Advance the offset
88    *offset_ptr += sizeof(val);
89  }
90  return val;
91}
92
93// GetMax32
94//
95// Used for calls when the size can vary. Fill in extra cases if they
96// are ever needed.
97uint32_t DNBDataRef::GetMax32(offset_t *offset_ptr, uint32_t byte_size) const {
98  switch (byte_size) {
99  case 1:
100    return Get8(offset_ptr);
101    break;
102  case 2:
103    return Get16(offset_ptr);
104    break;
105  case 4:
106    return Get32(offset_ptr);
107    break;
108  default:
109    assert(false && "GetMax32 unhandled case!");
110    break;
111  }
112  return 0;
113}
114
115// GetMax64
116//
117// Used for calls when the size can vary. Fill in extra cases if they
118// are ever needed.
119uint64_t DNBDataRef::GetMax64(offset_t *offset_ptr, uint32_t size) const {
120  switch (size) {
121  case 1:
122    return Get8(offset_ptr);
123    break;
124  case 2:
125    return Get16(offset_ptr);
126    break;
127  case 4:
128    return Get32(offset_ptr);
129    break;
130  case 8:
131    return Get64(offset_ptr);
132    break;
133  default:
134    assert(false && "GetMax64 unhandled case!");
135    break;
136  }
137  return 0;
138}
139
140// GetPointer
141//
142// Extract a pointer value from the buffer. The pointer size must be
143// set prior to using this using one of the SetPointerSize functions.
144uint64_t DNBDataRef::GetPointer(offset_t *offset_ptr) const {
145  // Must set pointer size prior to using this call
146  assert(m_ptrSize != 0);
147  return GetMax64(offset_ptr, m_ptrSize);
148}
149// GetCStr
150const char *DNBDataRef::GetCStr(offset_t *offset_ptr,
151                                uint32_t fixed_length) const {
152  const char *s = NULL;
153  if (m_start < m_end) {
154    s = (const char *)m_start + *offset_ptr;
155
156    // Advance the offset
157    if (fixed_length)
158      *offset_ptr += fixed_length;
159    else
160      *offset_ptr += strlen(s) + 1;
161  }
162  return s;
163}
164
165// GetData
166const uint8_t *DNBDataRef::GetData(offset_t *offset_ptr,
167                                   uint32_t length) const {
168  const uint8_t *data = NULL;
169  if (length > 0 && ValidOffsetForDataOfSize(*offset_ptr, length)) {
170    data = m_start + *offset_ptr;
171    *offset_ptr += length;
172  }
173  return data;
174}
175
176// Get_ULEB128
177uint64_t DNBDataRef::Get_ULEB128(offset_t *offset_ptr) const {
178  uint64_t result = 0;
179  if (m_start < m_end) {
180    int shift = 0;
181    const uint8_t *src = m_start + *offset_ptr;
182    uint8_t byte;
183    int bytecount = 0;
184
185    while (src < m_end) {
186      bytecount++;
187      byte = *src++;
188      result |= (uint64_t)(byte & 0x7f) << shift;
189      shift += 7;
190      if ((byte & 0x80) == 0)
191        break;
192    }
193
194    *offset_ptr += bytecount;
195  }
196  return result;
197}
198
199// Get_SLEB128
200int64_t DNBDataRef::Get_SLEB128(offset_t *offset_ptr) const {
201  int64_t result = 0;
202
203  if (m_start < m_end) {
204    int shift = 0;
205    int size = sizeof(uint32_t) * 8;
206    const uint8_t *src = m_start + *offset_ptr;
207
208    uint8_t byte = 0;
209    int bytecount = 0;
210
211    while (src < m_end) {
212      bytecount++;
213      byte = *src++;
214      result |= (int64_t)(byte & 0x7f) << shift;
215      shift += 7;
216      if ((byte & 0x80) == 0)
217        break;
218    }
219
220    // Sign bit of byte is 2nd high order bit (0x40)
221    if (shift < size && (byte & 0x40))
222      result |= -(1ll << shift);
223
224    *offset_ptr += bytecount;
225  }
226  return result;
227}
228
229// Skip_LEB128
230//
231// Skips past ULEB128 and SLEB128 numbers (just updates the offset)
232void DNBDataRef::Skip_LEB128(offset_t *offset_ptr) const {
233  if (m_start < m_end) {
234    const uint8_t *start = m_start + *offset_ptr;
235    const uint8_t *src = start;
236
237    while ((src < m_end) && (*src++ & 0x80))
238      /* Do nothing */;
239
240    *offset_ptr += src - start;
241  }
242}
243
244uint32_t DNBDataRef::Dump(uint32_t startOffset, uint32_t endOffset,
245                          uint64_t offsetBase, DNBDataRef::Type type,
246                          uint32_t numPerLine, const char *format) {
247  uint32_t offset;
248  uint32_t count;
249  char str[1024];
250  str[0] = '\0';
251  size_t str_offset = 0;
252
253  for (offset = startOffset, count = 0;
254       ValidOffset(offset) && offset < endOffset; ++count) {
255    if ((count % numPerLine) == 0) {
256      // Print out any previous string
257      if (str[0] != '\0')
258        DNBLog("%s", str);
259      // Reset string offset and fill the current line string with address:
260      str_offset = 0;
261      str_offset += snprintf(str, sizeof(str), "0x%8.8llx:",
262                             (uint64_t)(offsetBase + (offset - startOffset)));
263    }
264
265    // Make sure we don't pass the bounds of our current string buffer on each
266    // iteration through this loop
267    if (str_offset >= sizeof(str)) {
268      // The last snprintf consumed our string buffer, we will need to dump this
269      // out
270      // and reset the string with no address
271      DNBLog("%s", str);
272      str_offset = 0;
273      str[0] = '\0';
274    }
275
276    // We already checked that there is at least some room in the string str
277    // above, so it is safe to make
278    // the snprintf call each time through this loop
279    switch (type) {
280    case TypeUInt8:
281      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
282                             format ? format : " %2.2x", Get8(&offset));
283      break;
284    case TypeChar: {
285      char ch = Get8(&offset);
286      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
287                             format ? format : " %c", isprint(ch) ? ch : ' ');
288    } break;
289    case TypeUInt16:
290      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
291                             format ? format : " %4.4x", Get16(&offset));
292      break;
293    case TypeUInt32:
294      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
295                             format ? format : " %8.8x", Get32(&offset));
296      break;
297    case TypeUInt64:
298      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
299                             format ? format : " %16.16llx", Get64(&offset));
300      break;
301    case TypePointer:
302      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
303                             format ? format : " 0x%llx", GetPointer(&offset));
304      break;
305    case TypeULEB128:
306      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
307                             format ? format : " 0x%llx", Get_ULEB128(&offset));
308      break;
309    case TypeSLEB128:
310      str_offset += snprintf(str + str_offset, sizeof(str) - str_offset,
311                             format ? format : " %lld", Get_SLEB128(&offset));
312      break;
313    }
314  }
315
316  if (str[0] != '\0')
317    DNBLog("%s", str);
318
319  return offset; // Return the offset at which we ended up
320}
321