#pragma once #include "analysis.h" #include #include #include #include #include #include #include struct SimpleTypeInfo { uint64_t size_bits; uint64_t alignment_bits; bool operator==(const SimpleTypeInfo& other) const { return size_bits == other.size_bits && alignment_bits == other.alignment_bits; } }; struct StructInfo : SimpleTypeInfo { struct MemberInfo { uint64_t size_bits; // size of this member. For arrays, total size of all elements uint64_t offset_bits; std::string type_name; std::string member_name; std::optional array_size; bool is_function_pointer; bool is_integral; bool is_signed_integer; bool operator==(const MemberInfo& other) const { return size_bits == other.size_bits && offset_bits == other.offset_bits && // The type name may differ for integral types if all other parameters are equal (type_name == other.type_name || (is_integral && other.is_integral)) && member_name == other.member_name && array_size == other.array_size && is_function_pointer == other.is_function_pointer && is_integral == other.is_integral; } }; std::vector members; bool operator==(const StructInfo& other) const { return (const SimpleTypeInfo&)*this == (const SimpleTypeInfo&)other && std::equal(members.begin(), members.end(), other.members.begin(), other.members.end()); } }; struct TypeInfo : std::variant { using Parent = std::variant; TypeInfo() = default; TypeInfo(const SimpleTypeInfo& info) : Parent(info) {} TypeInfo(const StructInfo& info) : Parent(info) {} // Opaque declaration with no full definition. // Pointers to these can still be passed along ABI boundaries assuming // implementation details are only ever accessed on one side. bool is_opaque() const { return std::holds_alternative(*this); } const StructInfo* get_if_struct() const { return std::get_if(this); } StructInfo* get_if_struct() { return std::get_if(this); } const SimpleTypeInfo* get_if_simple_or_struct() const { auto as_struct = std::get_if(this); if (as_struct) { return as_struct; } return std::get_if(this); } }; struct FuncPtrInfo { std::array sha256; std::string result; std::vector args; }; struct ABI : std::unordered_map { std::unordered_map thunked_funcptrs; int pointer_size; // in bytes }; std::unordered_map ComputeDataLayout(const clang::ASTContext& context, const std::unordered_map& types); // Convert the output of ComputeDataLayout to a format that isn't tied to a libclang session. // As a consequence, type information is indexed by type name instead of clang::Type. ABI GetStableLayout(const clang::ASTContext& context, const std::unordered_map& data_layout); /** * Returns the type of the given name, but replaces any mentions of integer * types with fixed-size equivalents. * * Examples: * - int -> int32_t * - unsigned long long* -> uint64_t* * - MyStruct -> MyStruct (no change) */ std::string GetTypeNameWithFixedSizeIntegers(clang::ASTContext&, clang::QualType); enum class TypeCompatibility { Full, // Type has matching data layout across architectures Repackable, // Type has different data layout but can be repacked automatically None, // Type has different data layout and cannot be repacked automatically }; class DataLayoutCompareAction : public AnalysisAction { public: DataLayoutCompareAction(const ABI& guest_abi) : guest_abi(guest_abi) {} TypeCompatibility GetTypeCompatibility(const clang::ASTContext&, const clang::Type*, const std::unordered_map host_abi, std::unordered_map& type_compat); FuncPtrInfo LookupGuestFuncPtrInfo(const char* funcptr_id); protected: const ABI& guest_abi; };