| //===-- DNBDataRef.cpp ------------------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Created by Greg Clayton on 1/11/06. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "DNBDataRef.h" |
| #include "DNBLog.h" |
| #include <assert.h> |
| #include <ctype.h> |
| #include <libkern/OSByteOrder.h> |
| |
| //---------------------------------------------------------------------- |
| // Constructor |
| //---------------------------------------------------------------------- |
| |
| DNBDataRef::DNBDataRef() : |
| m_start(NULL), |
| m_end(NULL), |
| m_swap(false), |
| m_ptrSize(0), |
| m_addrPCRelative(INVALID_NUB_ADDRESS), |
| m_addrTEXT(INVALID_NUB_ADDRESS), |
| m_addrDATA(INVALID_NUB_ADDRESS) |
| { |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Constructor |
| //---------------------------------------------------------------------- |
| |
| DNBDataRef::DNBDataRef(const uint8_t *start, size_t size, bool swap) : |
| m_start(start), |
| m_end(start+size), |
| m_swap(swap), |
| m_ptrSize(0), |
| m_addrPCRelative(INVALID_NUB_ADDRESS), |
| m_addrTEXT(INVALID_NUB_ADDRESS), |
| m_addrDATA(INVALID_NUB_ADDRESS) |
| { |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Destructor |
| //---------------------------------------------------------------------- |
| |
| DNBDataRef::~DNBDataRef() |
| { |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Get8 |
| //---------------------------------------------------------------------- |
| uint8_t |
| DNBDataRef::Get8(offset_t *offset_ptr) const |
| { |
| uint8_t val = 0; |
| if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) |
| { |
| val = *(m_start + *offset_ptr); |
| *offset_ptr += sizeof(val); |
| } |
| return val; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Get16 |
| //---------------------------------------------------------------------- |
| uint16_t |
| DNBDataRef::Get16(offset_t *offset_ptr) const |
| { |
| uint16_t val = 0; |
| if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) |
| { |
| const uint8_t *p = m_start + *offset_ptr; |
| val = *(uint16_t*)p; |
| |
| if (m_swap) |
| val = OSSwapInt16(val); |
| |
| // Advance the offset |
| *offset_ptr += sizeof(val); |
| } |
| return val; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Get32 |
| //---------------------------------------------------------------------- |
| uint32_t |
| DNBDataRef::Get32(offset_t *offset_ptr) const |
| { |
| uint32_t val = 0; |
| if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) |
| { |
| const uint8_t *p = m_start + *offset_ptr; |
| val = *(uint32_t*)p; |
| if (m_swap) |
| val = OSSwapInt32(val); |
| |
| // Advance the offset |
| *offset_ptr += sizeof(val); |
| } |
| return val; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Get64 |
| //---------------------------------------------------------------------- |
| uint64_t |
| DNBDataRef::Get64(offset_t *offset_ptr) const |
| { |
| uint64_t val = 0; |
| if ( ValidOffsetForDataOfSize(*offset_ptr, sizeof(val)) ) |
| { |
| const uint8_t *p = m_start + *offset_ptr; |
| val = *(uint64_t*)p; |
| if (m_swap) |
| val = OSSwapInt64(val); |
| |
| // Advance the offset |
| *offset_ptr += sizeof(val); |
| } |
| return val; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // GetMax32 |
| // |
| // Used for calls when the size can vary. Fill in extra cases if they |
| // are ever needed. |
| //---------------------------------------------------------------------- |
| uint32_t |
| DNBDataRef::GetMax32(offset_t *offset_ptr, uint32_t byte_size) const |
| { |
| switch (byte_size) |
| { |
| case 1: return Get8 (offset_ptr); break; |
| case 2: return Get16(offset_ptr); break; |
| case 4: return Get32(offset_ptr); break; |
| default: |
| assert(!"GetMax32 unhandled case!"); |
| break; |
| } |
| return 0; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // GetMax64 |
| // |
| // Used for calls when the size can vary. Fill in extra cases if they |
| // are ever needed. |
| //---------------------------------------------------------------------- |
| uint64_t |
| DNBDataRef::GetMax64(offset_t *offset_ptr, uint32_t size) const |
| { |
| switch (size) |
| { |
| case 1: return Get8 (offset_ptr); break; |
| case 2: return Get16(offset_ptr); break; |
| case 4: return Get32(offset_ptr); break; |
| case 8: return Get64(offset_ptr); break; |
| default: |
| assert(!"GetMax64 unhandled case!"); |
| break; |
| } |
| return 0; |
| } |
| |
| //---------------------------------------------------------------------- |
| // GetPointer |
| // |
| // Extract a pointer value from the buffer. The pointer size must be |
| // set prior to using this using one of the SetPointerSize functions. |
| //---------------------------------------------------------------------- |
| uint64_t |
| DNBDataRef::GetPointer(offset_t *offset_ptr) const |
| { |
| // Must set pointer size prior to using this call |
| assert(m_ptrSize != 0); |
| return GetMax64(offset_ptr, m_ptrSize); |
| } |
| |
| //---------------------------------------------------------------------- |
| // GetDwarfEHPtr |
| // |
| // Used for calls when the value type is specified by a DWARF EH Frame |
| // pointer encoding. |
| //---------------------------------------------------------------------- |
| /* |
| uint64_t |
| DNBDataRef::GetDwarfEHPtr(offset_t *offset_ptr, uint32_t encoding) const |
| { |
| if (encoding == DW_EH_PE_omit) |
| return ULONG_LONG_MAX; // Value isn't in the buffer... |
| |
| uint64_t baseAddress = 0; |
| uint64_t addressValue = 0; |
| |
| BOOL signExtendValue = NO; |
| // Decode the base part or adjust our offset |
| switch (encoding & 0x70) |
| { |
| case DW_EH_PE_pcrel: |
| // SetEHPtrBaseAddresses should be called prior to extracting these |
| // so the base addresses are cached. |
| assert(m_addrPCRelative != INVALID_NUB_ADDRESS); |
| signExtendValue = YES; |
| baseAddress = *offset_ptr + m_addrPCRelative; |
| break; |
| |
| case DW_EH_PE_textrel: |
| // SetEHPtrBaseAddresses should be called prior to extracting these |
| // so the base addresses are cached. |
| assert(m_addrTEXT != INVALID_NUB_ADDRESS); |
| signExtendValue = YES; |
| baseAddress = m_addrTEXT; |
| break; |
| |
| case DW_EH_PE_datarel: |
| // SetEHPtrBaseAddresses should be called prior to extracting these |
| // so the base addresses are cached. |
| assert(m_addrDATA != INVALID_NUB_ADDRESS); |
| signExtendValue = YES; |
| baseAddress = m_addrDATA; |
| break; |
| |
| case DW_EH_PE_funcrel: |
| signExtendValue = YES; |
| break; |
| |
| case DW_EH_PE_aligned: |
| // SetPointerSize should be called prior to extracting these so the |
| // pointer size is cached |
| assert(m_ptrSize != 0); |
| if (m_ptrSize) |
| { |
| // Align to a address size boundary first |
| uint32_t alignOffset = *offset_ptr % m_ptrSize; |
| if (alignOffset) |
| offset_ptr += m_ptrSize - alignOffset; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| // Decode the value part |
| switch (encoding & DW_EH_PE_MASK_ENCODING) |
| { |
| case DW_EH_PE_absptr : addressValue = GetPointer(offset_ptr); break; |
| case DW_EH_PE_uleb128 : addressValue = Get_ULEB128(offset_ptr); break; |
| case DW_EH_PE_udata2 : addressValue = Get16(offset_ptr); break; |
| case DW_EH_PE_udata4 : addressValue = Get32(offset_ptr); break; |
| case DW_EH_PE_udata8 : addressValue = Get64(offset_ptr); break; |
| case DW_EH_PE_sleb128 : addressValue = Get_SLEB128(offset_ptr); break; |
| case DW_EH_PE_sdata2 : addressValue = (int16_t)Get16(offset_ptr); break; |
| case DW_EH_PE_sdata4 : addressValue = (int32_t)Get32(offset_ptr); break; |
| case DW_EH_PE_sdata8 : addressValue = (int64_t)Get64(offset_ptr); break; |
| default: |
| // Unhandled encoding type |
| assert(encoding); |
| break; |
| } |
| |
| // Since we promote everything to 64 bit, we may need to sign extend |
| if (signExtendValue && m_ptrSize < sizeof(baseAddress)) |
| { |
| uint64_t sign_bit = 1ull << ((m_ptrSize * 8ull) - 1ull); |
| if (sign_bit & addressValue) |
| { |
| uint64_t mask = ~sign_bit + 1; |
| addressValue |= mask; |
| } |
| } |
| return baseAddress + addressValue; |
| } |
| */ |
| |
| |
| //---------------------------------------------------------------------- |
| // GetCStr |
| //---------------------------------------------------------------------- |
| const char * |
| DNBDataRef::GetCStr(offset_t *offset_ptr, uint32_t fixed_length) const |
| { |
| const char *s = NULL; |
| if ( m_start < m_end ) |
| { |
| s = (char*)m_start + *offset_ptr; |
| |
| // Advance the offset |
| if (fixed_length) |
| *offset_ptr += fixed_length; |
| else |
| *offset_ptr += strlen(s) + 1; |
| } |
| return s; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // GetData |
| //---------------------------------------------------------------------- |
| const uint8_t * |
| DNBDataRef::GetData(offset_t *offset_ptr, uint32_t length) const |
| { |
| const uint8_t *data = NULL; |
| if ( length > 0 && ValidOffsetForDataOfSize(*offset_ptr, length) ) |
| { |
| data = m_start + *offset_ptr; |
| *offset_ptr += length; |
| } |
| return data; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Get_ULEB128 |
| //---------------------------------------------------------------------- |
| uint64_t |
| DNBDataRef::Get_ULEB128 (offset_t *offset_ptr) const |
| { |
| uint64_t result = 0; |
| if ( m_start < m_end ) |
| { |
| int shift = 0; |
| const uint8_t *src = m_start + *offset_ptr; |
| uint8_t byte; |
| int bytecount = 0; |
| |
| while (src < m_end) |
| { |
| bytecount++; |
| byte = *src++; |
| result |= (byte & 0x7f) << shift; |
| shift += 7; |
| if ((byte & 0x80) == 0) |
| break; |
| } |
| |
| *offset_ptr += bytecount; |
| } |
| return result; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Get_SLEB128 |
| //---------------------------------------------------------------------- |
| int64_t |
| DNBDataRef::Get_SLEB128 (offset_t *offset_ptr) const |
| { |
| int64_t result = 0; |
| |
| if ( m_start < m_end ) |
| { |
| int shift = 0; |
| int size = sizeof (uint32_t) * 8; |
| const uint8_t *src = m_start + *offset_ptr; |
| |
| uint8_t byte; |
| int bytecount = 0; |
| |
| while (src < m_end) |
| { |
| bytecount++; |
| byte = *src++; |
| result |= (byte & 0x7f) << shift; |
| shift += 7; |
| if ((byte & 0x80) == 0) |
| break; |
| } |
| |
| // Sign bit of byte is 2nd high order bit (0x40) |
| if (shift < size && (byte & 0x40)) |
| result |= - (1ll << shift); |
| |
| *offset_ptr += bytecount; |
| } |
| return result; |
| } |
| |
| |
| //---------------------------------------------------------------------- |
| // Skip_LEB128 |
| // |
| // Skips past ULEB128 and SLEB128 numbers (just updates the offset) |
| //---------------------------------------------------------------------- |
| void |
| DNBDataRef::Skip_LEB128 (offset_t *offset_ptr) const |
| { |
| if ( m_start < m_end ) |
| { |
| const uint8_t *start = m_start + *offset_ptr; |
| const uint8_t *src = start; |
| |
| while ((src < m_end) && (*src++ & 0x80)) |
| /* Do nothing */; |
| |
| *offset_ptr += src - start; |
| } |
| } |
| |
| uint32_t |
| DNBDataRef::Dump |
| ( |
| uint32_t startOffset, |
| uint32_t endOffset, |
| uint64_t offsetBase, |
| DNBDataRef::Type type, |
| uint32_t numPerLine, |
| const char *format |
| ) |
| { |
| uint32_t offset; |
| uint32_t count; |
| char str[1024]; |
| str[0] = '\0'; |
| int str_offset = 0; |
| |
| for (offset = startOffset, count = 0; ValidOffset(offset) && offset < endOffset; ++count) |
| { |
| if ((count % numPerLine) == 0) |
| { |
| // Print out any previous string |
| if (str[0] != '\0') |
| DNBLog("%s", str); |
| // Reset string offset and fill the current line string with address: |
| str_offset = 0; |
| str_offset += snprintf(str, sizeof(str), "0x%8.8llx:", (uint64_t)(offsetBase + (offset - startOffset))); |
| } |
| |
| // Make sure we don't pass the bounds of our current string buffer on each iteration through this loop |
| if (str_offset >= sizeof(str)) |
| { |
| // The last snprintf consumed our string buffer, we will need to dump this out |
| // and reset the string with no address |
| DNBLog("%s", str); |
| str_offset = 0; |
| str[0] = '\0'; |
| } |
| |
| // We already checked that there is at least some room in the string str above, so it is safe to make |
| // the snprintf call each time through this loop |
| switch (type) |
| { |
| default: |
| case TypeUInt8: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %2.2x", Get8(&offset)); break; |
| case TypeChar: |
| { |
| char ch = Get8(&offset); |
| str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %c", isprint(ch) ? ch : ' '); |
| } |
| break; |
| case TypeUInt16: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %4.4x", Get16(&offset)); break; |
| case TypeUInt32: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %8.8x", Get32(&offset)); break; |
| case TypeUInt64: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %16.16llx", Get64(&offset)); break; |
| case TypePointer: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " 0x%llx", GetPointer(&offset)); break; |
| case TypeULEB128: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " 0x%llx", Get_ULEB128(&offset)); break; |
| case TypeSLEB128: str_offset += snprintf(str + str_offset, sizeof(str) - str_offset, format ? format : " %lld", Get_SLEB128(&offset)); break; |
| } |
| } |
| |
| if (str[0] != '\0') |
| DNBLog("%s", str); |
| |
| return offset; // Return the offset at which we ended up |
| } |