#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/SmallString.h" #include "revng/ADT/Concepts.h" #include "revng/ADT/MutableSet.h" #include "revng/ADT/SortedVector.h" #include "revng/ADT/UpcastablePointer.h" #include "revng/ADT/UpcastablePointer/YAMLTraits.h" #include "revng/Model/ABI.h" #include "revng/Model/Architecture.h" #include "revng/Model/Configuration.h" #include "revng/Model/DefinedType.h" #include "revng/Model/DynamicFunction.h" #include "revng/Model/EnumDefinition.h" #include "revng/Model/Function.h" #include "revng/Model/FunctionAttribute.h" #include "revng/Model/RawFunctionDefinition.h" #include "revng/Model/Register.h" #include "revng/Model/Segment.h" #include "revng/Model/TypeDefinition.h" #include "revng/Support/CommonOptions.h" #include "revng/Support/MetaAddress.h" #include "revng/Support/MetaAddress/MetaAddressRangeSet.h" #include "revng/Support/MetaAddress/YAMLTraits.h" #include "revng/Support/YAMLTraits.h" #include "revng/TupleTree/TupleTree.h" #include "revng/TupleTree/TupleTreeDiff.h" /* TUPLE-TREE-YAML name: Binary doc: |- Data structure representing the whole binary. This is the entry point of the model. It contains the type system (`Types`), the list of functions (`Functions`), loading information (`Segments`) and more. type: struct fields: - name: Architecture doc: The architecture for this binary. type: Architecture optional: true - name: EntryPoint doc: The program entry point, if any. type: MetaAddress optional: true - name: DefaultABI doc: The default ABI to adopt for analysis purposes. type: ABI optional: true - name: DefaultPrototype doc: |- The default function prototype to adopt for functions that do not provide it explicitly. type: Type optional: true upcastable: true - name: Configuration type: Configuration optional: true - name: Segments doc: |- `Segment`s represent instructions on what part of the raw binary needs to be loaded at which address. sequence: type: SortedVector elementType: Segment optional: true - name: ExtraCodeAddresses doc: |- A list of addresses known to contain code. rev.ng is usually able to discover all the code by itself by recursively visiting the control-flow graph of functions and the call graph. However, certain pieces of code cannot be identified through these techniques. A prime example are the addresses of `catch` blocks of C++ exception handlers: no code ever directly jumps there and their address is not stored in jump tables. Their address can only be obtained by interpreting metadata in the ELF. optional: true sequence: type: SortedVector elementType: MetaAddress - name: ImportedLibraries doc: |- The list of imported libraries identified by their file name. For instance, if the input binary is linked to OpenSSL, this list would should `libcrypto.so.1.1`. sequence: type: SortedVector elementType: string optional: true - name: ImportedDynamicFunctions doc: List of functions imported from dynamic libraries (`.so`, `.dll`). sequence: type: SortedVector elementType: DynamicFunction optional: true - name: Functions doc: List of the functions present in the binary. sequence: type: SortedVector elementType: Function optional: true - name: TypeDefinitions doc: |- The set of types used in this binary. It contains `struct`, `union`, `typedef`, `enum` and function prototypes. sequence: type: SortedVector upcastable: true elementType: TypeDefinition optional: true TUPLE-TREE-YAML */ #include "revng/Model/Generated/Early/Binary.h" namespace model { class VerifyHelper; } // TODO: Prevent changing the keys. Currently we need them to be public and // non-const for serialization purposes. class model::Binary : public model::generated::Binary { public: using generated::Binary::Binary; public: /// Introduce a new type definition to the binary. /// /// \note there are also helpers for each of the type definition kinds which /// should be preferred when creating a definition of a known kind. /// as in, prefer `makeStructDefinition` to `makeDefinition`. /// /// \param Arguments A variadic argument list to pass to the type constructor. /// /// \tparam NewType The type of the new definition to make. /// /// \returns A pair of: /// - the reference to the newly made definition which can be used /// to modify it right away, /// - the corresponding defined type ready to be attached to others. template NewType, typename... ArgumentTypes> [[nodiscard]] std::pair makeTypeDefinition(ArgumentTypes &&...Arguments) { using UTD = model::UpcastableTypeDefinition; UTD New = UTD::make(std::forward(Arguments)...); auto &&[Reference, Result] = recordNewType(std::move(New)); return { llvm::cast(Reference), std::move(Result) }; } template [[nodiscard]] auto makeStructDefinition(Ts &&...As) { return makeTypeDefinition(std::forward(As)...); } template [[nodiscard]] auto makeUnionDefinition(Ts &&...As) { return makeTypeDefinition(std::forward(As)...); } template [[nodiscard]] auto makeEnumDefinition(Ts &&...As) { return makeTypeDefinition(std::forward(As)...); } template [[nodiscard]] auto makeTypedefDefinition(Ts &&...As) { return makeTypeDefinition(std::forward(As)...); } template [[nodiscard]] auto makeCABIFunctionDefinition(Ts &&...As) { return makeTypeDefinition(std::forward(As)...); } template [[nodiscard]] auto makeRawFunctionDefinition(Ts &&...As) { return makeTypeDefinition(std::forward(As)...); } public: /// Record the new type into the model and assign a new ID. /// /// \returns A pair of: /// - the reference to the newly inserted definition which can be /// used to modify it right away, /// - the corresponding defined type ready to be attached to others. std::pair recordNewType(model::UpcastableTypeDefinition &&T); /// Uses `SortedVector::batch_insert()` to emplace all the elements from /// \ref NewTypes range into the `TypeDefinitions()` set. /// /// This inserts all the elements at the end of the underlying vector, and /// then triggers sorting, instead of conventional searching for the position /// of each element on its insertion. /// /// \note Unlike recordNewTypeDefinitions, this method does not assign type /// IDs. /// /// \note It takes advantage of `std::move_iterator` to ensure all /// the elements are accessed strictly as r-values, so the original /// container, \ref NewTypes range points to, is left in an unspecified /// state after the invocation, as all of its elements are moved out of. /// /// \note Since a strict version of `batch-insert`'er is used, if this causes /// multiple elements to have the same \ref TypeDefinition::Key, /// an assert will be fired. /// /// \tparam Range constrained input range type. /// \param NewTypes the input range. template> Range> void recordNewTypeDefinitions(Range &&NewTypes) { auto Inserter = TypeDefinitions().batch_insert(); static_assert(std::is_rvalue_reference_v); auto Movable = as_rvalue(std::move(NewTypes)); for (UpcastablePointer &&NewType : Movable) { static_assert(std::is_rvalue_reference_v); Inserter.emplace(std::move(NewType)); } } public: /// \note Only use this when absolutely necessary, for example, when doing /// bulk reference replacement. /// In the general case prefer \ref makeType instead. model::DefinitionReference getDefinitionReference(const model::TypeDefinition::Key &Key) { return DefinitionReference::fromString(this, "/TypeDefinitions/" + getNameFromYAMLScalar(Key)); } model::DefinitionReference getDefinitionReference(const model::TypeDefinition::Key &Key) const { return DefinitionReference::fromString(this, "/TypeDefinitions/" + getNameFromYAMLScalar(Key)); } model::UpcastableType makeType(const model::TypeDefinition::Key &Key) { return model::DefinedType::make(getDefinitionReference(Key)); } model::UpcastableType makeConstType(const model::TypeDefinition::Key &Key) { return model::DefinedType::makeConst(getDefinitionReference(Key)); } model::UpcastableType makeType(const model::TypeDefinition::Key &Key) const { return model::DefinedType::make(getDefinitionReference(Key)); } model::UpcastableType makeConstType(const model::TypeDefinition::Key &Key) const { return model::DefinedType::makeConst(getDefinitionReference(Key)); } /// Return the first available type ID uint64_t getAvailableTypeID() const; public: /// The helper for the prototype unwrapping. /// Use this when you need to access/modify the existing prototype, /// and \ref DefaultPrototype() when you need to assign a new one. model::TypeDefinition *defaultPrototype() { // TODO: after `abi::Definition` is merged back into the model, // the prototype will always be present, so this should return // a reference instead. if (DefaultPrototype().isEmpty()) return nullptr; else return &DefaultPrototype()->toPrototype(); } /// The helper for the prototype unwrapping. /// Use this when you need to access/modify the existing prototype, /// and \ref DefaultPrototype() when you need to assign a new one. const model::TypeDefinition *defaultPrototype() const { // TODO: after `abi::Definition` is merged back into the model, // the prototype will always be present, so this should return // a reference instead. if (DefaultPrototype().isEmpty()) return nullptr; else return &DefaultPrototype()->toPrototype(); } model::TypeDefinition *prototypeOrDefault(model::TypeDefinition *Prototype) { if (Prototype) return Prototype; return defaultPrototype(); } const model::TypeDefinition * prototypeOrDefault(const model::TypeDefinition *Prototype) const { if (Prototype) return Prototype; return defaultPrototype(); } public: bool verify(VerifyHelper &VH) const; bool verify(bool Assert) const debug_function; bool verify() const debug_function; bool verifyTypeDefinitions(VerifyHelper &VH) const; bool verifyTypeDefinitions(bool Assert) const debug_function; bool verifyTypeDefinitions() const debug_function; public: void dumpTypeGraph(const char *Path) const debug_function; public: MetaAddressRangeSet executableRanges() const; }; #include "revng/Model/Generated/Late/Binary.h"