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