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 // Constructor
20 
DNBDataRef()21 DNBDataRef::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 
DNBDataRef(const uint8_t * start,size_t size,bool swap)28 DNBDataRef::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 
~DNBDataRef()35 DNBDataRef::~DNBDataRef() {}
36 
37 // Get8
Get8(offset_t * offset_ptr) const38 uint8_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
Get16(offset_t * offset_ptr) const48 uint16_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
Get32(offset_t * offset_ptr) const64 uint32_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
Get64(offset_t * offset_ptr) const79 uint64_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.
GetMax32(offset_t * offset_ptr,uint32_t byte_size) const97 uint32_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.
GetMax64(offset_t * offset_ptr,uint32_t size) const119 uint64_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.
GetPointer(offset_t * offset_ptr) const144 uint64_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
GetCStr(offset_t * offset_ptr,uint32_t fixed_length) const150 const 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
GetData(offset_t * offset_ptr,uint32_t length) const166 const 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
Get_ULEB128(offset_t * offset_ptr) const177 uint64_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
Get_SLEB128(offset_t * offset_ptr) const200 int64_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)
Skip_LEB128(offset_t * offset_ptr) const232 void 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 
Dump(uint32_t startOffset,uint32_t endOffset,uint64_t offsetBase,DNBDataRef::Type type,uint32_t numPerLine,const char * format)244 uint32_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