Files
Alessandro Di Federico 1429b526ab Introduce libtcg
This commit drops libptc in favor of its new form libtcg.

It brings several improvements, among which:

* The QEMU version we work on has been upgraded.
* CPUStateAccessAnalysis has been reimplemented in a way that makes it
  easier to debug and solves some limitations (e.g., tracking leaking
  pointers).
* Identification of pieces of the CPU state that are read by each helper
  and fixing access to the CPU state is now performed at build-time.
* We no longer mmap the code we need to translate, dropping all the
  issues related to code that needed to be mapped where something is
  already present.
* We now have two distinct flavors of helper modules: the full one and
  the "slim" one. The latter contains the definition only of functions
  we intend to inline. It is used in most of the pipeline, a good thing
  since we spend less time optimizing code we don't really care about.
  The full module is only used on the re-compilation branch of the
  pipeline.
* We no longer split the `cpu_loop` function.
* We change MetaAddress to rely on architectures from `model::` as
  opposed to the LLVM ones.
* We no longer attach debug info to LLVM IR containing the original
  assembly.
* We now verify that the lifted code only contains code we expect.
2025-10-31 17:25:03 +01:00

305 lines
8.3 KiB
C++

#pragma once
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <cstdint>
#include <limits>
#include <optional>
#include <type_traits>
#include "llvm/BinaryFormat/Dwarf.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/LEB128.h"
#include "revng/Support/Assert.h"
#include "revng/Support/MetaAddress.h"
//
// What follows is a set of functions we use to read an integer of a specified
// (or pointer) size using the appropriate endianness associated to an ELF type.
//
template<typename T, typename EE>
struct Endianness {
/// Reads an integer of type T, using the endianness of the ELF type EE
static uint64_t read(const uint8_t *Buf);
};
template<typename T>
struct Endianness<T, llvm::object::ELF32LE> {
static uint64_t read(const uint8_t *Buf) {
using namespace llvm::support;
return llvm::support::endian::read<T, little, unaligned>(Buf);
}
};
template<typename T>
struct Endianness<T, llvm::object::ELF64LE> {
static uint64_t read(const uint8_t *Buf) {
using namespace llvm::support;
return llvm::support::endian::read<T, little, unaligned>(Buf);
}
};
template<typename T>
struct Endianness<T, llvm::object::ELF32BE> {
static uint64_t read(const uint8_t *Buf) {
using namespace llvm::support;
return llvm::support::endian::read<T, big, unaligned>(Buf);
}
};
template<typename T>
struct Endianness<T, llvm::object::ELF64BE> {
static uint64_t read(const uint8_t *Buf) {
using namespace llvm::support;
return llvm::support::endian::read<T, big, unaligned>(Buf);
}
};
/// Read a pointer-sized integer according to the given ELF type EE
template<typename EE>
inline uint64_t readPointer(const uint8_t *Buf);
template<>
inline uint64_t readPointer<llvm::object::ELF32LE>(const uint8_t *Buf) {
return Endianness<uint32_t, llvm::object::ELF32LE>::read(Buf);
}
template<>
inline uint64_t readPointer<llvm::object::ELF32BE>(const uint8_t *Buf) {
return Endianness<uint32_t, llvm::object::ELF32BE>::read(Buf);
}
template<>
inline uint64_t readPointer<llvm::object::ELF64LE>(const uint8_t *Buf) {
return Endianness<uint64_t, llvm::object::ELF64LE>::read(Buf);
}
template<>
inline uint64_t readPointer<llvm::object::ELF64BE>(const uint8_t *Buf) {
return Endianness<uint64_t, llvm::object::ELF64BE>::read(Buf);
}
/// A pair on steroids to wrap a value or a pointer to a value
class Pointer {
public:
Pointer() : IsIndirect(false), Value(MetaAddress::invalid()) {}
Pointer(bool IsIndirect, MetaAddress Value) :
IsIndirect(IsIndirect), Value(Value) {}
bool isIndirect() const { return IsIndirect; }
MetaAddress value() const { return Value; }
private:
bool IsIndirect;
MetaAddress Value;
};
template<typename E>
class DwarfReader {
public:
DwarfReader(model::Architecture::Values Architecture,
llvm::ArrayRef<uint8_t> Buffer,
MetaAddress Address) :
Architecture(Architecture),
Address(Address),
Start(Buffer.data()),
Cursor(Buffer.data()),
End(Buffer.data() + Buffer.size()) {}
uint8_t readNextU8() { return readNext<uint8_t>(); }
uint16_t readNextU16() { return readNext<uint16_t>(); }
uint32_t readNextU32() { return readNext<uint32_t>(); }
uint64_t readNextU64() { return readNext<uint64_t>(); }
uint64_t readNextU() {
if (is64())
return readNextU64();
else
return readNextU32();
}
uint64_t readULEB128() {
unsigned Length;
uint64_t Result = llvm::decodeULEB128(Cursor, &Length);
Cursor += Length;
revng_assert(Cursor <= End);
return Result;
}
int64_t readSLEB128() {
unsigned Length;
int64_t Result = llvm::decodeSLEB128(Cursor, &Length);
Cursor += Length;
revng_assert(Cursor <= End);
return Result;
}
std::optional<int64_t> readSignedValue(unsigned Encoding) {
auto MaybeResult = readValue(Encoding);
if (not MaybeResult)
return std::nullopt;
return static_cast<int64_t>(*MaybeResult);
}
std::optional<uint64_t> readUnsignedValue(unsigned Encoding) {
return readValue(Encoding);
}
Pointer readPointer(unsigned Encoding,
MetaAddress Base = MetaAddress::invalid()) {
using namespace llvm;
revng_assert((Encoding & ~(0x70 | 0x0F | dwarf::DW_EH_PE_indirect)) == 0);
// Handle PC-relative values
revng_assert(Cursor >= Start);
if ((Encoding & 0x70) == dwarf::DW_EH_PE_pcrel) {
revng_assert(Base.isInvalid());
Base = Address + (Cursor - Start);
}
if (isSigned(Encoding & 0x0F)) {
return readPointerInternal(readSignedValue(Encoding), Encoding, Base);
} else {
return readPointerInternal(readUnsignedValue(Encoding), Encoding, Base);
}
}
void moveTo(uint64_t Offset) {
const uint8_t *NewCursor = Start + Offset;
revng_assert(NewCursor >= Cursor && NewCursor <= End);
Cursor = NewCursor;
}
bool eof() const { return Cursor >= End; }
uint64_t offset() const { return Cursor - Start; }
private:
template<typename T>
std::conditional_t<std::numeric_limits<T>::is_signed, int64_t, uint64_t>
readNext() {
using namespace llvm;
constexpr bool IsSigned = std::numeric_limits<T>::is_signed;
using ReturnType = std::conditional_t<IsSigned, int64_t, uint64_t>;
revng_assert(Cursor + sizeof(T) <= End);
auto Result = static_cast<T>(Endianness<T, E>::read(Cursor));
Cursor += sizeof(T);
return static_cast<ReturnType>(Result);
}
static bool isSigned(unsigned Format) {
using namespace llvm;
switch (Format) {
case dwarf::DW_EH_PE_sleb128:
case dwarf::DW_EH_PE_signed:
case dwarf::DW_EH_PE_sdata2:
case dwarf::DW_EH_PE_sdata4:
case dwarf::DW_EH_PE_sdata8:
return true;
case dwarf::DW_EH_PE_absptr:
case dwarf::DW_EH_PE_uleb128:
case dwarf::DW_EH_PE_udata2:
case dwarf::DW_EH_PE_udata4:
case dwarf::DW_EH_PE_udata8:
return false;
default:
revng_abort("Unknown Encoding");
}
}
std::optional<uint64_t> readValue(unsigned Encoding) {
using namespace llvm;
if (Encoding == dwarf::DW_EH_PE_omit)
return std::nullopt;
revng_assert((Encoding & ~(0x70 | 0x0F | dwarf::DW_EH_PE_indirect)) == 0);
// Extract the format
unsigned Format = Encoding & 0x0F;
switch (Format) {
case dwarf::DW_EH_PE_uleb128:
return readULEB128();
case dwarf::DW_EH_PE_sleb128:
return readSLEB128();
case dwarf::DW_EH_PE_absptr:
if (is64())
return readNext<uint64_t>();
else
return readNext<uint32_t>();
case dwarf::DW_EH_PE_signed:
if (is64())
return readNext<int64_t>();
else
return readNext<int32_t>();
case dwarf::DW_EH_PE_udata2:
return readNext<uint16_t>();
case dwarf::DW_EH_PE_sdata2:
return readNext<int16_t>();
case dwarf::DW_EH_PE_udata4:
return readNext<uint32_t>();
case dwarf::DW_EH_PE_sdata4:
return readNext<int32_t>();
case dwarf::DW_EH_PE_udata8:
return readNext<uint64_t>();
case dwarf::DW_EH_PE_sdata8:
return readNext<int64_t>();
default:
revng_abort(("Unknown encoding " + std::to_string(Encoding)).c_str());
}
}
template<typename T>
Pointer readPointerInternal(std::optional<T> MaybeValue,
unsigned Encoding,
MetaAddress Base) {
using namespace llvm;
bool IsIndirect = Encoding & dwarf::DW_EH_PE_indirect;
if (not MaybeValue)
return Pointer(IsIndirect, MetaAddress::invalid());
auto Value = *MaybeValue;
if (Base.isInvalid()) {
return Pointer(IsIndirect, MetaAddress::fromGeneric(Architecture, Value));
} else {
unsigned EncodingRelative = Encoding & 0x70;
revng_assert(EncodingRelative == 0 || EncodingRelative == 0x10);
return Pointer(IsIndirect, Base + Value);
}
}
bool is64() const;
private:
model::Architecture::Values Architecture;
MetaAddress Address;
const uint8_t *Start;
const uint8_t *Cursor;
const uint8_t *End;
};
template<>
inline bool DwarfReader<llvm::object::ELF32BE>::is64() const {
return false;
}
template<>
inline bool DwarfReader<llvm::object::ELF32LE>::is64() const {
return false;
}
template<>
inline bool DwarfReader<llvm::object::ELF64BE>::is64() const {
return true;
}
template<>
inline bool DwarfReader<llvm::object::ELF64LE>::is64() const {
return true;
}