DNBDataRef.cpp   [plain text]


//===-- 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 ULLONG_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 = 0;
        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
}