mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
983 lines
24 KiB
C++
983 lines
24 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/ADT/Triple.h"
|
|
|
|
#include "revng/ADT/KeyedObjectContainer.h"
|
|
#include "revng/Support/Debug.h"
|
|
#include "revng/Support/OverflowSafeInt.h"
|
|
|
|
extern "C" {
|
|
#include "revng/Runtime/PlainMetaAddress.h"
|
|
}
|
|
|
|
namespace llvm {
|
|
class TypeDefinition;
|
|
class Constant;
|
|
class ConstantInt;
|
|
class Value;
|
|
class LLVMContext;
|
|
class Module;
|
|
class StructType;
|
|
class GlobalVariable;
|
|
class Instruction;
|
|
class IRBuilderBase;
|
|
} // namespace llvm
|
|
|
|
namespace MetaAddressType {
|
|
|
|
enum Values : uint16_t {
|
|
/// An invalid address
|
|
Invalid,
|
|
|
|
/// A 32-bit generic address
|
|
Generic32,
|
|
|
|
/// A 64-bit generic address
|
|
Generic64,
|
|
|
|
/// The address of a x86 basic block
|
|
Code_x86,
|
|
|
|
/// The address of a x86-64 basic block
|
|
Code_x86_64,
|
|
|
|
/// The address of a MIPS basic block
|
|
Code_mips,
|
|
|
|
/// The address of a MIPS little-endian basic block
|
|
Code_mipsel,
|
|
|
|
/// The address of a regular ARM basic block
|
|
Code_arm,
|
|
|
|
/// The address of a ARM Thumb basic block
|
|
Code_arm_thumb,
|
|
|
|
/// The address of a AArch64 basic block
|
|
Code_aarch64,
|
|
|
|
/// The address of a z/Architecture (s390x) basic block
|
|
Code_systemz
|
|
};
|
|
|
|
inline constexpr bool isValid(Values V) {
|
|
switch (V) {
|
|
case Invalid:
|
|
case Generic32:
|
|
case Generic64:
|
|
case Code_x86:
|
|
case Code_x86_64:
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
case Code_arm_thumb:
|
|
case Code_aarch64:
|
|
case Code_systemz:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inline constexpr const char *toString(Values V) {
|
|
switch (V) {
|
|
case Invalid:
|
|
return "Invalid";
|
|
case Generic32:
|
|
return "Generic32";
|
|
case Generic64:
|
|
return "Generic64";
|
|
case Code_x86:
|
|
return "Code_x86";
|
|
case Code_x86_64:
|
|
return "Code_x86_64";
|
|
case Code_mips:
|
|
return "Code_mips";
|
|
case Code_mipsel:
|
|
return "Code_mipsel";
|
|
case Code_arm:
|
|
return "Code_arm";
|
|
case Code_arm_thumb:
|
|
return "Code_arm_thumb";
|
|
case Code_aarch64:
|
|
return "Code_aarch64";
|
|
case Code_systemz:
|
|
return "Code_systemz";
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
inline constexpr Values fromString(llvm::StringRef String) {
|
|
if (String == "Generic32") {
|
|
return Generic32;
|
|
} else if (String == "Generic64") {
|
|
return Generic64;
|
|
} else if (String == "Code_x86") {
|
|
return Code_x86;
|
|
} else if (String == "Code_x86_64") {
|
|
return Code_x86_64;
|
|
} else if (String == "Code_mips") {
|
|
return Code_mips;
|
|
} else if (String == "Code_mipsel") {
|
|
return Code_mipsel;
|
|
} else if (String == "Code_arm") {
|
|
return Code_arm;
|
|
} else if (String == "Code_arm_thumb") {
|
|
return Code_arm_thumb;
|
|
} else if (String == "Code_aarch64") {
|
|
return Code_aarch64;
|
|
} else if (String == "Code_systemz") {
|
|
return Code_systemz;
|
|
} else {
|
|
return Invalid;
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
inline constexpr const std::optional<llvm::Triple::ArchType> arch(Values V) {
|
|
switch (V) {
|
|
case Code_x86:
|
|
return { llvm::Triple::x86 };
|
|
case Code_x86_64:
|
|
return { llvm::Triple::x86_64 };
|
|
case Code_mips:
|
|
return { llvm::Triple::mips };
|
|
case Code_mipsel:
|
|
return { llvm::Triple::mipsel };
|
|
case Code_arm:
|
|
case Code_arm_thumb:
|
|
return { llvm::Triple::arm };
|
|
case Code_aarch64:
|
|
return { llvm::Triple::aarch64 };
|
|
case Code_systemz:
|
|
return { llvm::Triple::systemz };
|
|
case Invalid:
|
|
case Generic32:
|
|
case Generic64:
|
|
return {};
|
|
default:
|
|
revng_abort();
|
|
}
|
|
}
|
|
|
|
/// Returns Generic32 or Generic64 depending on the size of addresses in \p Arch
|
|
inline constexpr Values genericFromArch(llvm::Triple::ArchType Arch) {
|
|
switch (Arch) {
|
|
case llvm::Triple::x86:
|
|
case llvm::Triple::arm:
|
|
case llvm::Triple::mips:
|
|
case llvm::Triple::mipsel:
|
|
return Generic32;
|
|
case llvm::Triple::x86_64:
|
|
case llvm::Triple::aarch64:
|
|
case llvm::Triple::systemz:
|
|
return Generic64;
|
|
default:
|
|
revng_abort("Unsupported architecture");
|
|
}
|
|
|
|
revng_abort("Unsupported architecture");
|
|
}
|
|
|
|
/// Convert \p Type to the corresponding generic type
|
|
inline constexpr Values toGeneric(Values Type) {
|
|
switch (Type) {
|
|
case Invalid:
|
|
revng_abort("Can't convert to generic an invalid type");
|
|
|
|
case Generic32:
|
|
case Generic64:
|
|
return Type;
|
|
|
|
case Code_x86:
|
|
case Code_arm_thumb:
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
return Generic32;
|
|
|
|
case Code_x86_64:
|
|
case Code_systemz:
|
|
case Code_aarch64:
|
|
return Generic64;
|
|
}
|
|
|
|
revng_abort("Unsupported architecture");
|
|
}
|
|
|
|
/// Get the default type for code of the given architecture
|
|
inline constexpr Values defaultCodeFromArch(llvm::Triple::ArchType Arch) {
|
|
switch (Arch) {
|
|
case llvm::Triple::x86:
|
|
return Code_x86;
|
|
case llvm::Triple::arm:
|
|
return Code_arm;
|
|
case llvm::Triple::mips:
|
|
return Code_mips;
|
|
case llvm::Triple::mipsel:
|
|
return Code_mipsel;
|
|
case llvm::Triple::x86_64:
|
|
return Code_x86_64;
|
|
case llvm::Triple::aarch64:
|
|
return Code_aarch64;
|
|
case llvm::Triple::systemz:
|
|
return Code_systemz;
|
|
default:
|
|
revng_abort("Unsupported architecture");
|
|
}
|
|
}
|
|
|
|
/// Get the alignment of the corresponding type
|
|
///
|
|
/// \note Generic types have alignment of 1
|
|
inline constexpr unsigned alignment(Values Type) {
|
|
switch (Type) {
|
|
case Invalid:
|
|
revng_abort("Invalid addresses have no alignment");
|
|
case Generic32:
|
|
case Generic64:
|
|
case Code_x86:
|
|
case Code_x86_64:
|
|
return 1;
|
|
case Code_arm_thumb:
|
|
case Code_systemz:
|
|
return 2;
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
case Code_aarch64:
|
|
return 4;
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
/// Get the size in bit of an address of the given type
|
|
inline constexpr unsigned bitSize(Values Type) {
|
|
switch (Type) {
|
|
case Invalid:
|
|
revng_abort("Invalid addresses have no bit size");
|
|
case Generic32:
|
|
case Code_x86:
|
|
case Code_arm_thumb:
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
return 32;
|
|
case Generic64:
|
|
case Code_x86_64:
|
|
case Code_systemz:
|
|
case Code_aarch64:
|
|
return 64;
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
/// Get a 64-bits mask representing the relevant bits for the given type
|
|
///
|
|
/// \note The alignment is not considered in this mask.
|
|
inline constexpr uint64_t addressMask(Values Type) {
|
|
revng_assert(bitSize(Type) != 0);
|
|
return std::numeric_limits<uint64_t>::max() >> (64 - bitSize(Type));
|
|
}
|
|
|
|
/// Does \p Type represent a code address?
|
|
inline constexpr bool isCode(Values Type) {
|
|
switch (Type) {
|
|
case Invalid:
|
|
case Generic32:
|
|
case Generic64:
|
|
return false;
|
|
|
|
case Code_x86:
|
|
case Code_arm_thumb:
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
case Code_x86_64:
|
|
case Code_systemz:
|
|
case Code_aarch64:
|
|
return true;
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
/// Does \p Type represent an address pointing to \p Arch code?
|
|
inline constexpr bool isCode(Values Type, llvm::Triple::ArchType Arch) {
|
|
switch (Arch) {
|
|
case llvm::Triple::x86:
|
|
return Type == Code_x86;
|
|
case llvm::Triple::arm:
|
|
return Type == Code_arm or Type == Code_arm_thumb;
|
|
case llvm::Triple::mips:
|
|
return Type == Code_mips;
|
|
case llvm::Triple::mipsel:
|
|
return Type == Code_mipsel;
|
|
case llvm::Triple::x86_64:
|
|
return Type == Code_x86_64;
|
|
case llvm::Triple::aarch64:
|
|
return Type == Code_aarch64;
|
|
case llvm::Triple::systemz:
|
|
return Type == Code_systemz;
|
|
default:
|
|
revng_abort("Unsupported architecture");
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
/// Is \p Type a generic address?
|
|
inline constexpr bool isGeneric(Values Type) {
|
|
switch (Type) {
|
|
case Invalid:
|
|
case Code_x86:
|
|
case Code_arm_thumb:
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
case Code_x86_64:
|
|
case Code_systemz:
|
|
case Code_aarch64:
|
|
return false;
|
|
|
|
case Generic32:
|
|
case Generic64:
|
|
return true;
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
inline constexpr bool isDefaultCode(Values Type) {
|
|
switch (Type) {
|
|
case Code_x86:
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
case Code_x86_64:
|
|
case Code_systemz:
|
|
case Code_aarch64:
|
|
return true;
|
|
|
|
case Invalid:
|
|
case Generic32:
|
|
case Generic64:
|
|
case Code_arm_thumb:
|
|
return false;
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
inline constexpr llvm::StringRef getLLVMCPUFeatures(Values Type) {
|
|
switch (Type) {
|
|
case Code_arm_thumb:
|
|
return "+thumb-mode";
|
|
case Invalid:
|
|
case Generic32:
|
|
case Generic64:
|
|
case Code_x86:
|
|
case Code_mips:
|
|
case Code_mipsel:
|
|
case Code_arm:
|
|
case Code_x86_64:
|
|
case Code_systemz:
|
|
case Code_aarch64:
|
|
return "";
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
} // namespace MetaAddressType
|
|
|
|
/// Represents an address with a type, an address space and epoch
|
|
///
|
|
/// MetaAddress is a uint64_t on steroids.
|
|
///
|
|
/// Its key goal is to allow to distinguish different things at the same address
|
|
/// (e.g., regular and Thumb code at the same address). It also provides
|
|
/// appropriate arithmetic depending on the address type.
|
|
///
|
|
/// MetaAddress represents four things:
|
|
///
|
|
/// 1. The absolute value of the address
|
|
/// 2. The "epoch": a progressive identifier that represents a timestamp. It
|
|
/// enables users to represent the fact that at the same address there might
|
|
/// be different things at different points in time. Its main purpose is to
|
|
/// represent self-modifying code.
|
|
/// 3. The address space: a generic identifier for architectures that have
|
|
/// access to multiple address spaces.
|
|
/// 4. The type: a MetaAddress can be used to represent a generic address or to
|
|
/// code. See MetaAddressType for further details.
|
|
///
|
|
/// \note Generic addresses have no alignment constraints.
|
|
class MetaAddress : private PlainMetaAddress {
|
|
private:
|
|
friend class ProgramCounterHandler;
|
|
|
|
public:
|
|
constexpr static llvm::StringRef Separator = ":";
|
|
|
|
public:
|
|
class Features {
|
|
public:
|
|
llvm::Triple::ArchType Architecture = llvm::Triple::UnknownArch;
|
|
uint32_t Epoch = 0;
|
|
uint16_t AddressSpace = 0;
|
|
|
|
public:
|
|
bool operator==(const Features &Other) const = default;
|
|
};
|
|
|
|
public:
|
|
/// \name Constructors
|
|
///
|
|
/// @{
|
|
|
|
/// Public constructor creating an invalid MetaAddress
|
|
///
|
|
/// \note Prefer MetaAddress::invalid()
|
|
constexpr explicit MetaAddress() : PlainMetaAddress({}) {}
|
|
|
|
/// Public constructor allowing to create a custom instance to validate
|
|
///
|
|
/// \note Prefer MetaAddress:fromPC or MetaAddress::fromGeneric
|
|
constexpr explicit MetaAddress(uint64_t Address,
|
|
MetaAddressType::Values Type,
|
|
uint32_t Epoch = 0,
|
|
uint16_t AddressSpace = 0) :
|
|
PlainMetaAddress({ Epoch, AddressSpace, Type, Address }) {
|
|
|
|
// Verify the given data
|
|
validate();
|
|
}
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// \name Factory methods
|
|
///
|
|
/// @{
|
|
|
|
/// Create an invalid MetaAddress
|
|
static constexpr MetaAddress invalid() { return MetaAddress(); }
|
|
|
|
/// Create a MetaAddress from a pointer to \p Arch code
|
|
static constexpr MetaAddress fromPC(llvm::Triple::ArchType Arch,
|
|
uint64_t PC,
|
|
uint32_t Epoch = 0,
|
|
uint16_t AddressSpace = 0) {
|
|
|
|
// Create the base MetaAddress, it points to code at zero
|
|
MetaAddress Result(0,
|
|
MetaAddressType::defaultCodeFromArch(Arch),
|
|
Epoch,
|
|
AddressSpace);
|
|
|
|
// A code MetaAddress pointing at 0 should always be valid
|
|
revng_assert(Result.isValid());
|
|
|
|
if (Arch == llvm::Triple::arm and (PC & 1) == 1) {
|
|
// A pointer to ARM code with the LSB turned on is Thumb code
|
|
|
|
// Override the type
|
|
Result.Type = MetaAddressType::Code_arm_thumb;
|
|
}
|
|
|
|
Result.setPC(PC);
|
|
|
|
// Check alignment
|
|
Result.validate();
|
|
|
|
return Result;
|
|
}
|
|
|
|
static MetaAddress fromPC(MetaAddress Base, uint64_t Address) {
|
|
return fromPC(*Base.arch(), Address, Base.epoch(), Base.addressSpace());
|
|
}
|
|
|
|
static MetaAddress fromPC(uint64_t Address, const Features &Features) {
|
|
return MetaAddress::fromPC(Features.Architecture,
|
|
Address,
|
|
Features.Epoch,
|
|
Features.AddressSpace);
|
|
}
|
|
|
|
/// Create a generic MetaAddress for architecture \p Arch
|
|
static constexpr MetaAddress fromGeneric(llvm::Triple::ArchType Arch,
|
|
uint64_t Address,
|
|
uint32_t Epoch = 0,
|
|
uint16_t AddressSpace = 0) {
|
|
return MetaAddress(Address,
|
|
MetaAddressType::genericFromArch(Arch),
|
|
Epoch,
|
|
AddressSpace);
|
|
}
|
|
|
|
static constexpr MetaAddress fromGeneric(uint64_t Address,
|
|
Features Features) {
|
|
return MetaAddress(Address,
|
|
MetaAddressType::genericFromArch(Features.Architecture),
|
|
Features.Epoch,
|
|
Features.AddressSpace);
|
|
}
|
|
|
|
/// @}
|
|
|
|
public:
|
|
/// Deserialize a MetaAddress from an llvm::Constant
|
|
static MetaAddress fromValue(llvm::Value *V);
|
|
|
|
/// Serialize a MetaAddress to an llvm::Constant
|
|
llvm::Constant *toValue(llvm::Module *M) const;
|
|
|
|
public:
|
|
static llvm::Instruction *composeIntegerPC(llvm::IRBuilderBase &B,
|
|
llvm::Value *AddressValue,
|
|
llvm::Value *EpochValue,
|
|
llvm::Value *AddressSpaceValue,
|
|
llvm::Value *TypeValue);
|
|
|
|
static MetaAddress decomposeIntegerPC(llvm::ConstantInt *Value);
|
|
static MetaAddress decomposeIntegerPC(const llvm::APInt &Value);
|
|
|
|
public:
|
|
/// If isCode(), let this decay to the corresponding generic address
|
|
constexpr MetaAddress toGeneric() const {
|
|
revng_check(isValid());
|
|
|
|
MetaAddress Result = *this;
|
|
Result.Type = MetaAddressType::toGeneric(type());
|
|
return Result;
|
|
}
|
|
|
|
constexpr MetaAddress toGeneric64() const {
|
|
revng_check(isValid());
|
|
|
|
MetaAddress Result = *this;
|
|
Result.Type = MetaAddressType::Generic64;
|
|
return Result;
|
|
}
|
|
|
|
constexpr MetaAddress toPC(llvm::Triple::ArchType Arch) const {
|
|
return fromPC(Arch, Address, Epoch, AddressSpace);
|
|
}
|
|
|
|
Features features() const {
|
|
return Features(*MetaAddressType::arch(type()), Epoch, AddressSpace);
|
|
}
|
|
|
|
public:
|
|
/// @{
|
|
constexpr bool operator==(const MetaAddress &Other) const {
|
|
return tie() == Other.tie();
|
|
}
|
|
|
|
constexpr bool operator!=(const MetaAddress &Other) const {
|
|
return not(*this == Other);
|
|
}
|
|
|
|
constexpr bool operator<(const MetaAddress &Other) const {
|
|
return tie() < Other.tie();
|
|
}
|
|
constexpr bool operator<=(const MetaAddress &Other) const {
|
|
return tie() <= Other.tie();
|
|
}
|
|
constexpr bool operator>(const MetaAddress &Other) const {
|
|
return tie() > Other.tie();
|
|
}
|
|
constexpr bool operator>=(const MetaAddress &Other) const {
|
|
return tie() >= Other.tie();
|
|
}
|
|
|
|
/// @}
|
|
|
|
/// \name Address comparisons
|
|
///
|
|
/// Comparison operators ignoring the MetaAddress type
|
|
///
|
|
/// @{
|
|
|
|
constexpr bool addressEquals(const MetaAddress &Other) const {
|
|
revng_assert(isValid());
|
|
revng_assert(Other.isValid());
|
|
return toGeneric64() == Other.toGeneric64();
|
|
}
|
|
|
|
constexpr bool addressDiffers(const MetaAddress &Other) const {
|
|
revng_assert(isValid());
|
|
revng_assert(Other.isValid());
|
|
return !addressEquals(Other);
|
|
}
|
|
|
|
constexpr bool addressLowerThan(const MetaAddress &Other) const {
|
|
revng_assert(isValid());
|
|
revng_assert(Other.isValid());
|
|
return toGeneric64() < Other.toGeneric64();
|
|
}
|
|
|
|
constexpr bool addressLowerThanOrEqual(const MetaAddress &Other) const {
|
|
revng_assert(isValid());
|
|
revng_assert(Other.isValid());
|
|
return toGeneric64() <= Other.toGeneric64();
|
|
}
|
|
|
|
constexpr bool addressGreaterThanOrEqual(const MetaAddress &Other) const {
|
|
revng_assert(isValid());
|
|
revng_assert(Other.isValid());
|
|
return not(addressLowerThan(Other));
|
|
}
|
|
|
|
constexpr bool addressGreaterThan(const MetaAddress &Other) const {
|
|
revng_assert(isValid());
|
|
revng_assert(Other.isValid());
|
|
return not(addressLowerThanOrEqual(Other));
|
|
}
|
|
|
|
std::optional<uint64_t> operator-(const MetaAddress &Other) const {
|
|
revng_assert(isValid());
|
|
revng_assert(Other.isValid());
|
|
revng_assert(Epoch == Other.Epoch);
|
|
revng_assert(AddressSpace == Other.AddressSpace);
|
|
return (OverflowSafeInt(Address) - Other.Address).value();
|
|
}
|
|
/// @}
|
|
|
|
/// \name Arithmetic additions/subtractions
|
|
///
|
|
/// @{
|
|
|
|
template<std::integral T>
|
|
MetaAddress &operator+=(T Offset) {
|
|
if (isInvalid())
|
|
return *this;
|
|
|
|
auto Update = [this, Offset](auto NewAddress) {
|
|
if constexpr (std::is_signed_v<T>) {
|
|
if (Offset >= 0)
|
|
NewAddress += Offset;
|
|
else
|
|
NewAddress -= -Offset;
|
|
} else {
|
|
NewAddress += Offset;
|
|
}
|
|
|
|
if (NewAddress)
|
|
setAddress(*NewAddress);
|
|
else
|
|
*this = MetaAddress::invalid();
|
|
};
|
|
|
|
if (bitSize() == 32)
|
|
Update(OverflowSafeInt<uint32_t>(Address));
|
|
else
|
|
Update(OverflowSafeInt<uint64_t>(Address));
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<std::integral T>
|
|
MetaAddress &operator-=(T Offset) {
|
|
if (isInvalid())
|
|
return *this;
|
|
|
|
auto Update = [this, Offset](auto NewAddress) {
|
|
if constexpr (std::is_signed_v<T>) {
|
|
if (Offset >= 0)
|
|
NewAddress -= Offset;
|
|
else
|
|
NewAddress += -Offset;
|
|
} else {
|
|
NewAddress -= Offset;
|
|
}
|
|
|
|
if (NewAddress)
|
|
setAddress(*NewAddress);
|
|
else
|
|
*this = MetaAddress::invalid();
|
|
};
|
|
|
|
if (bitSize() == 32)
|
|
Update(OverflowSafeInt<uint32_t>(Address));
|
|
else
|
|
Update(OverflowSafeInt<uint64_t>(Address));
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<std::integral T>
|
|
MetaAddress operator+(T Offset) const {
|
|
MetaAddress Result = *this;
|
|
Result += Offset;
|
|
return Result;
|
|
}
|
|
|
|
template<std::integral T>
|
|
MetaAddress operator-(T Offset) const {
|
|
MetaAddress Result = *this;
|
|
Result -= Offset;
|
|
return Result;
|
|
}
|
|
/// @}
|
|
|
|
public:
|
|
/// Build a new MetaAddress replacing the address with a new (valid) address
|
|
///
|
|
/// The given address must be valid for the current type. The resulting type
|
|
/// has the same epoch, type and address space as this.
|
|
constexpr MetaAddress replaceAddress(uint64_t Address) const {
|
|
revng_check(isValid());
|
|
|
|
MetaAddress Result = *this;
|
|
Result.setAddress(Address);
|
|
Result.validate();
|
|
return Result;
|
|
}
|
|
|
|
public:
|
|
/// \name Accessors
|
|
///
|
|
/// @{
|
|
constexpr uint64_t address() const {
|
|
revng_assert(isValid());
|
|
return Address;
|
|
}
|
|
|
|
/// Return the wrapped address in its PC representation
|
|
///
|
|
/// \note Don't call this method if `!(isValid() && isCode())`
|
|
constexpr uint64_t asPC() const {
|
|
revng_check(isValid());
|
|
return asPCOrZero();
|
|
}
|
|
|
|
/// Return the wrapped address in its PC representation, or 0 if invalid
|
|
constexpr uint64_t asPCOrZero() const {
|
|
revng_check(isCode() or isInvalid());
|
|
|
|
switch (type()) {
|
|
case MetaAddressType::Invalid:
|
|
revng_assert(Address == 0);
|
|
return 0;
|
|
|
|
case MetaAddressType::Code_arm_thumb:
|
|
revng_assert((Address & 1) == 0);
|
|
return Address | 1;
|
|
|
|
case MetaAddressType::Code_x86:
|
|
case MetaAddressType::Code_x86_64:
|
|
case MetaAddressType::Code_systemz:
|
|
case MetaAddressType::Code_mips:
|
|
case MetaAddressType::Code_mipsel:
|
|
case MetaAddressType::Code_arm:
|
|
case MetaAddressType::Code_aarch64:
|
|
return Address;
|
|
|
|
case MetaAddressType::Generic32:
|
|
case MetaAddressType::Generic64:
|
|
revng_abort();
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
constexpr uint16_t addressSpace() const {
|
|
revng_check(isValid());
|
|
return AddressSpace;
|
|
}
|
|
constexpr bool isDefaultAddressSpace() const { return addressSpace() == 0; }
|
|
|
|
constexpr uint32_t epoch() const {
|
|
revng_check(isValid());
|
|
return Epoch;
|
|
}
|
|
constexpr bool isDefaultEpoch() const { return epoch() == 0; }
|
|
|
|
constexpr MetaAddressType::Values type() const {
|
|
return MetaAddressType::Values(Type);
|
|
}
|
|
constexpr bool isInvalid() const {
|
|
return type() == MetaAddressType::Invalid;
|
|
}
|
|
constexpr bool isValid() const { return not isInvalid(); }
|
|
constexpr bool isCode() const { return MetaAddressType::isCode(type()); }
|
|
constexpr bool isCode(llvm::Triple::ArchType Arch) const {
|
|
return MetaAddressType::isCode(type(), Arch);
|
|
}
|
|
constexpr bool isGeneric() const {
|
|
return MetaAddressType::isGeneric(type());
|
|
}
|
|
constexpr unsigned bitSize() const {
|
|
return MetaAddressType::bitSize(type());
|
|
}
|
|
constexpr unsigned alignment() const {
|
|
return MetaAddressType::alignment(type());
|
|
}
|
|
|
|
std::optional<llvm::Triple::ArchType> arch() const {
|
|
return MetaAddressType::arch(type());
|
|
}
|
|
|
|
constexpr bool isDefaultCode() const {
|
|
return MetaAddressType::isDefaultCode(type());
|
|
}
|
|
|
|
/// @}
|
|
|
|
public:
|
|
constexpr bool isIn(const MetaAddress &Start, const MetaAddress &End) const {
|
|
return Start <= *this and *this < End;
|
|
}
|
|
|
|
public:
|
|
void dump() const debug_function { dump(dbg); }
|
|
|
|
template<typename T>
|
|
void dump(T &Output) const {
|
|
Output << toString();
|
|
}
|
|
|
|
template<typename T>
|
|
void dumpRelativeTo(T &Output,
|
|
const MetaAddress &Base,
|
|
llvm::StringRef BaseName) const {
|
|
Output << BaseName.data();
|
|
|
|
if (Base == *this)
|
|
return;
|
|
|
|
Output << ".";
|
|
|
|
auto MaybeDifference = *this - Base;
|
|
if (not MaybeDifference)
|
|
Output << MetaAddress::invalid().toString();
|
|
else
|
|
Output << "0x" << llvm::Twine::utohexstr(*MaybeDifference).str();
|
|
}
|
|
|
|
public:
|
|
constexpr MetaAddress pageStart() const {
|
|
revng_check(isValid());
|
|
return toGeneric() - (Address % 4096);
|
|
}
|
|
|
|
MetaAddress nextPageStart() const {
|
|
revng_check(isValid());
|
|
|
|
auto Addend = ((OverflowSafeInt(Address) + (4096 - 1)) / 4096) * 4096
|
|
- Address;
|
|
|
|
if (not Addend)
|
|
return MetaAddress::invalid();
|
|
|
|
return toGeneric() + *Addend;
|
|
}
|
|
|
|
private:
|
|
constexpr bool verify() const debug_function {
|
|
// Invalid addresses are all the same
|
|
if (type() == MetaAddressType::Invalid) {
|
|
return *this == invalid();
|
|
}
|
|
|
|
if (static_cast<uint16_t>(Type) > MetaAddressType::Code_systemz)
|
|
return false;
|
|
|
|
// Check alignment
|
|
if (Address % alignment() != 0)
|
|
return false;
|
|
|
|
// Check address mask
|
|
if (Address != (Address & addressMask()))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
constexpr void validate() {
|
|
if (not verify())
|
|
setInvalid();
|
|
}
|
|
|
|
constexpr void setInvalid() { *this = MetaAddress(); }
|
|
|
|
constexpr uint64_t addressMask() const {
|
|
return MetaAddressType::addressMask(type());
|
|
}
|
|
|
|
constexpr void setPC(uint64_t PC) {
|
|
if (type() == MetaAddressType::Code_arm_thumb) {
|
|
|
|
if ((PC & 1) == 0) {
|
|
setInvalid();
|
|
return;
|
|
}
|
|
|
|
PC = PC & ~1;
|
|
}
|
|
|
|
setAddress(PC);
|
|
}
|
|
|
|
constexpr void setAddress(uint64_t NewAddress) {
|
|
Address = NewAddress & addressMask();
|
|
validate();
|
|
}
|
|
|
|
public:
|
|
/// \param Arch specifying the "expected" architecture omits it from
|
|
/// the serialized string. But it also leads to inability
|
|
/// to deserialize it! So only use if you know what you're doing.
|
|
std::string toString(std::optional<llvm::Triple::ArchType> Arch = {}) const;
|
|
static MetaAddress fromString(llvm::StringRef Text);
|
|
|
|
private:
|
|
using Tied = std::tuple<const uint32_t &,
|
|
const uint16_t &,
|
|
const uint16_t &,
|
|
const uint64_t &>;
|
|
constexpr Tied tie() const {
|
|
return std::tie(Epoch, AddressSpace, Type, Address);
|
|
}
|
|
};
|
|
|
|
static_assert(sizeof(MetaAddress) <= 128 / 8,
|
|
"MetaAddress is larger than 128 bits");
|
|
|
|
template<typename T>
|
|
struct CompareAddress {};
|
|
|
|
template<>
|
|
struct CompareAddress<MetaAddress> {
|
|
bool operator()(const MetaAddress &LHS, const MetaAddress &RHS) const {
|
|
return LHS.addressLowerThan(RHS);
|
|
}
|
|
};
|
|
|
|
template<>
|
|
struct KeyedObjectTraits<MetaAddress>
|
|
: public IdentityKeyedObjectTraits<MetaAddress> {};
|
|
|
|
namespace std {
|
|
template<>
|
|
class hash<MetaAddress> {
|
|
public:
|
|
uint64_t operator()(const MetaAddress &Address) const {
|
|
return hash_combine(Address.arch(),
|
|
Address.address(),
|
|
Address.epoch(),
|
|
Address.addressSpace());
|
|
}
|
|
};
|
|
} // namespace std
|