You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

531 lines
16 KiB

/*
* Copyright 2002-2019 Intel Corporation.
*
* This software and the related documents are Intel copyrighted materials, and your
* use of them is governed by the express license under which they were provided to
* you ("License"). Unless the License provides otherwise, you may not use, modify,
* copy, publish, distribute, disclose or transmit this software or the related
* documents without Intel's prior written permission.
*
* This software and the related documents are provided as is, with no express or
* implied warranties, other than those that are expressly stated in the License.
*/
// <COMPONENT>: util
// <FILE-TYPE>: component public header
#ifndef UTIL_REGVALUE_HPP
#define UTIL_REGVALUE_HPP
#undef min // Some broken system headers define "min", which conflicts with std::min().
#include <string.h>
#include <algorithm>
#include "util/data.hpp"
namespace UTIL {
/*!
* Utility that holds the value of a register and the size of the register from which
* it came.
*
* @todo: There are many assumptions that the host is little-endian.
*/
class /*<UTILITY>*/ REGVALUE
{
public:
/*!
* Construct an uninitialized register value.
*/
REGVALUE() : _size(0), _value(0) {}
/*!
* Construct a register value that is a copy of an existing value.
*
* @param[in] other The copied value.
*/
REGVALUE(const REGVALUE &other)
{
SetCopy(other);
}
/*!
* Construct a register value that is the same size as "ADDRINT".
*
* @param[in] val Value of the register.
*/
REGVALUE(ADDRINT val)
{
SetAddress(val);
}
/*!
* Construct a register value.
*
* @param[in] val Value of the register.
* @param[in] size Size (bits) of the register.
*/
REGVALUE(UINT64 val, unsigned size)
{
Set64(val, size);
}
/*!
* Construct a register value.
*
* @param[in] lo Low 64 bits of register value.
* @param[in] hi High 64 bits of register value.
* @param[in] size Size (bits) of the register.
*/
REGVALUE(UINT64 lo, UINT64 hi, unsigned size)
{
Set128(lo, hi, size);
}
/*!
* Construct a register value from a DATA buffer. The content of the data
* buffer is interpreted as big- or little-endian according to the host byte
* order.
*
* @param[in] val Value and size of the register.
* @param[in] size If non-zero, the size (bits) of the register. Otherwise,
* the register's width is the size of \a val.
*/
REGVALUE(const UTIL::DATA &val, unsigned size=0)
{
SetBuffer(val.GetBuf<void>(), val.GetSize(), size);
}
/*!
* Construct a register value from a raw byte buffer. The content of the buffer is
* interpreted as a big- or little-endian value according to the host byte order.
*
* @param[in] buf Pointer to raw buffer of bytes.
* @param[in] byteSize Size (in bytes) of the buffer \a buf.
* @param[in] size If non-zero, the size (bits) of the register. Otherwise,
* the register's width is \a byteSize.
*/
template<typename T> REGVALUE(const T *buf, size_t byteSize, unsigned size=0)
{
SetBuffer(static_cast<const void *>(buf), byteSize, size);
}
~REGVALUE() {ClearIfNeeded();}
/*!
* Re-assign to a copy of another register value.
*
* @param[in] other Register value that is copied.
*
* @return Reference to the new register value.
*/
REGVALUE &operator =(const REGVALUE &other)
{
ClearIfNeeded();
SetCopy(other);
return *this;
}
/*!
* Re-assign to a copy of another register value.
*
* @param[in] other Register value that is copied.
*/
void Assign(const REGVALUE &other)
{
ClearIfNeeded();
SetCopy(other);
}
/*!
* Re-assign to a new register value that is the same size as "ADDRINT".
*
* @param[in] val Value of the register.
*/
void Assign(ADDRINT val)
{
ClearIfNeeded();
SetAddress(val);
}
/*!
* Re-assign to a new register value.
*
* @param[in] val Value of the register.
* @param[in] size Size (bits) of the register.
*/
void Assign(UINT64 val, unsigned size)
{
ClearIfNeeded();
Set64(val, size);
}
/*!
* Re-assign to a new register value.
*
* @param[in] lo Low 64 bits of register value.
* @param[in] hi High 64 bits of register value.
* @param[in] size Size (bits) of the register.
*/
void Assign(UINT64 lo, UINT64 hi, unsigned size)
{
ClearIfNeeded();
Set128(lo, hi, size);
}
/*!
* Re-assign to a new register value.
*
* @param[in] data 4-64 bits of register value.
* @param[in] size Size (bits) of the register.
*/
void Assign(UINT64 * data, unsigned size)
{
ClearIfNeeded();
Set256(data, size);
}
/*!
* Re-assign to a new register value from a DATA buffer. The content of the data
* buffer is interpreted as big- or little-endian according to the host byte
* order.
*
* @param[in] val Value and size of the register.
* @param[in] size If non-zero, the size (bits) of the register. Otherwise,
* the register's width is the size of \a val.
*/
void Assign(const UTIL::DATA &val, unsigned size=0)
{
ClearIfNeeded();
SetBuffer(val.GetBuf<void>(), val.GetSize(), size);
}
/*!
* Re-assign to a new register value from a raw byte buffer. The content of the buffer
* is interpreted as a big- or little-endian value according to the host byte order.
*
* @param[in] buf Pointer to raw buffer of bytes.
* @param[in] byteSize Size (in bytes) of the buffer \a buf and of the register.
* @param[in] size If non-zero, the size (bits) of the register. Otherwise,
* the register's width is \a byteSize.
*/
template<typename T> void Assign(const T *buf, size_t byteSize, unsigned size=0)
{
ClearIfNeeded();
SetBuffer(static_cast<const void *>(buf), byteSize, size);
}
/*!
* Change the size of the register value, truncating or zero-extending the value
* as necessary.
*
* @param[in] size New size (bits).
*/
void Resize(unsigned size)
{
if (_size <= 8*sizeof(PTRINT))
{
Set64(_value, size);
}
else
{
UTIL::DATA *d = _bigValue;
SetBuffer(d->GetBuf<void>(), d->GetSize(), size);
delete d;
}
}
/*!
* @return The size (bits) of the register.
*/
unsigned GetBitSize() const {return _size;}
/*!
* Copy the register value to a DATA buffer. The value is written as
* big- or little-endian according to the host byte order.
*
* @param[out] data Receives the value and size of the register.
* If the register size is not an even multiple
* of bytes, it is rounded up and the high-order
* bits are set to zero.
*/
void CopyToData(UTIL::DATA *data) const
{
if (_size <= 8*sizeof(PTRINT))
data->Assign(&_value, GetByteSize());
else
data->Assign(*_bigValue);
}
/*!
* Copy the register value to a raw byte buffer. The value is written
* as big- or little-endian according to the host byte order.
*
* @param[in] data Receives the register value. The \a data buffer
* must be long enough to receive the register's size
* (defined by GetBitSize()).
*/
void CopyToBuffer(void *data) const
{
if (_size <= 8*sizeof(PTRINT))
memcpy(data, &_value, GetByteSize());
else
memcpy(data, _bigValue->GetBuf<void>(), _bigValue->GetSize());
}
/*!
* Return the value of the register cast to an integral type. The size
* of the register need not be the same as the size of the returned type.
* The value is zero-extended or truncated as necessary.
*
* @return The register value.
*/
template<typename T> T GetValueAs() const
{
if (_size <= 8*sizeof(PTRINT))
return static_cast<T>(_value);
return GetIndexedWord<T>(0);
}
/*!
* Considering the register value as an array of words of type "T", return
* one word from that array.
*
* @param[in] index Specifies the word to return. Index zero specifies the
* least significant word.
*
* @return The word, or zero if \a index is out of range for the register's size.
*/
template<typename T> T GetIndexedWord(unsigned index) const
{
size_t byteSize = GetByteSize();
const UINT8 *buf = static_cast<const UINT8 *>(GetBuffer());
size_t offset = index * sizeof(T);
if (offset + sizeof(T) <= byteSize)
{
T tmp;
memcpy(static_cast<void *>(&tmp), static_cast<const void *>(buf+offset), sizeof(T));
return tmp;
}
else if (offset < byteSize)
{
T tmp(0);
memcpy(static_cast<void *>(&tmp), static_cast<const void *>(buf+offset), byteSize-offset);
return tmp;
}
else
{
return T(0);
}
}
private:
/*!
* Set to a copy of another register value.
*
* @param[in] other The copied register value.
*/
void SetCopy(const REGVALUE &other)
{
_size = other._size;
if (_size <= 8*sizeof(PTRINT))
_value = other._value;
else
_bigValue = new UTIL::DATA(*other._bigValue);
}
/*!
* Set to an "ADDRINT" sized value.
*
* @param[in] val Register value.
*/
void SetAddress(ADDRINT val)
{
_size = 8*sizeof(ADDRINT);
#if defined(ADDRINT_SIZE_IN_BITS) && (ADDRINT_SIZE_IN_BITS <= PTRINT_SIZE)
_value = val;
#else
_bigValue = new DATA(&val, sizeof(ADDRINT));
#endif
}
/*!
* Set the value and size. Size could be larger than UINT64.
*
* @param[in] val Register value.
* @param[in] size Size (bits).
*/
void Set64(UINT64 val, unsigned size)
{
_size = size;
if (size == 8*sizeof(PTRINT))
{
_value = static_cast<PTRINT>(val);
}
else if (size < 8*sizeof(PTRINT))
{
PTRINT mask = PTRINT(1);
_value = static_cast<PTRINT>(val) & ((mask << size) - PTRINT(1));
}
else if (size == 8*sizeof(UINT64))
{
_bigValue = new DATA(&val, 8);
}
else if (size > 8*sizeof(UINT64))
{
// Size of DATA > sizeof(val), so top bytes of DATA need to be zeroed.
//
unsigned byteSize = (size + 7) >> 3;
_bigValue = new DATA(byteSize);
memcpy(_bigValue->GetWritableBuf<void>(), static_cast<void *>(&val), sizeof(val));
UINT8 *rest = _bigValue->GetWritableBuf<UINT8>() + sizeof(val);
memset(static_cast<void *>(rest), 0, byteSize - sizeof(val));
_bigValue->ReleaseWritableBuf();
}
else
{
// Register size < UINT64 and > PTRINT.
// This can't happen on 64-bit hosts.
//
UINT64 mask = UINT64(1);
val = val & ((mask << size) - UINT64(1));
unsigned byteSize = (size + 7) >> 3;
_bigValue = new DATA(&val, byteSize);
}
}
/*!
* Set the value and size.
*
* @param[in] lo Low 64 bits of register value.
* @param[in] hi High 64 bits of register value.
* @param[in] size Size (bits).
*/
void Set128(UINT64 lo, UINT64 hi, unsigned size)
{
UINT64 buf[2];
buf[0] = lo;
buf[1] = hi;
SetBuffer(static_cast<const void *>(buf), sizeof(buf), size);
}
/*!
* Set the value and size.
*
* @param[in] data 4-64 bits of register value.
* @param[in] size Size (bits).
*/
void Set256(UINT64 * data, unsigned size)
{
UINT64 buf[4];
buf[0] = data[0];
buf[1] = data[1];
buf[2] = data[2];
buf[3] = data[3];
SetBuffer(static_cast<const void *>(buf), sizeof(buf), size);
}
/*!
* Set to the contents of a raw buffer. The value in the buffer is interpreted
* as big- or little-endian according to the host byte order.
*
* @param[in] buf Buffer with value.
* @param[in] byteSize Size (bytes) of buffer.
* @param[in] size Size (bits) of register. If zero, size is \a byteSize.
*/
void SetBuffer(const void *buf, size_t byteSizeIn, unsigned size)
{
if (!size)
size = static_cast<unsigned>(8*byteSizeIn);
_size = size;
size_t byteSize = (size + 7) >> 3;
if (size == 8*sizeof(PTRINT))
{
// The input buffer might be smaller than PTRINT, so zero out '_value' first to zero-
// extend the buffer's value.
//
_value = 0;
memcpy(static_cast<void *>(&_value), buf, std::min(byteSizeIn, sizeof(PTRINT)));
}
else if (size < 8*sizeof(PTRINT))
{
// Same as above, but we also need to mask the final value down to the register size.
//
_value = 0;
memcpy(static_cast<void *>(&_value), buf, std::min(byteSizeIn, byteSize));
PTRINT mask = PTRINT(1);
_value &= ((mask << size) - PTRINT(1));
}
else
{
// Create a DATA large enough for the register size, which might be different than
// the input buffer size.
//
_bigValue = new DATA(byteSize);
size_t sizeCopied = std::min(byteSizeIn, byteSize);
memcpy(_bigValue->GetWritableBuf<void>(), buf, sizeCopied);
memset(static_cast<void *>(_bigValue->GetWritableBuf<UINT8>()+sizeCopied), 0, byteSize-sizeCopied);
// The last byte in the DATA might need to be masked if the register size is not an even
// multiple of bytes. However, if the buffer size is less than the DATA size, the last
// byte has already been zeroed by the memset() above.
//
if ((byteSize <= byteSizeIn) && (size < 8*byteSize))
{
unsigned numBitsLast = static_cast<unsigned>(8 - (8*byteSize - size));
UINT8 *last = &_bigValue->GetWritableBuf<UINT8>()[_bigValue->GetSize()-1];
UINT8 mask = UINT8(1);
*last &= ((mask << numBitsLast) - UINT8(1));
}
_bigValue->ReleaseWritableBuf();
}
}
/*!
* Clear the _bigValue buffer if it is allocated.
*/
void ClearIfNeeded()
{
if (_size > 8*sizeof(PTRINT))
delete _bigValue;
}
/*!
* @return Size of the register, rounded up to the next byte.
*/
size_t GetByteSize() const
{
return (_size + 7) >> 3;
}
/*!
* @return Pointer to the buffer containing the register value. The value in
* the buffer has the host's byte order. The lifetime of the buffer
* lasts only until the next operation that changes the REGVALUE.
*/
const void *GetBuffer() const
{
if (_size <= 8*sizeof(PTRINT))
return static_cast<const void *>(&_value);
else
return _bigValue->GetBuf<void>();
}
private:
unsigned _size; // Register width in bits.
// Register value. Any unused high-order bits are zero.
//
union
{
PTRINT _value; // Value if size <= sizeof(void *)
UTIL::DATA *_bigValue; // Value if size > sizeof(void *), in host byte order
};
};
} // namespace
#endif // file guard