From 26916f3a17369c5c1ccab65265cea0454a72808f Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 23 Dec 2025 13:12:02 -0700 Subject: [PATCH 1/4] Refactor calling conventions to support correct representation of structures --- binaryninjaapi.h | 231 +++++++- binaryninjacore.h | 203 ++++++- callingconvention.cpp | 696 ++++++++++++++++++++++++ function.cpp | 151 +++-- lang/rust/rusttypes.cpp | 29 +- mediumlevelilinstruction.cpp | 15 + mediumlevelilinstruction.h | 7 + plugins/pdb-ng/src/symbol_parser.rs | 11 +- plugins/warp/src/convert.rs | 2 + python/callingconvention.py | 50 +- python/function.py | 191 +++++-- python/mediumlevelil.py | 30 +- python/types.py | 252 ++++++--- python/variable.py | 122 ++++- rust/src/calling_convention.rs | 116 +++- rust/src/confidence.rs | 17 +- rust/src/ffi.rs | 13 + rust/src/function.rs | 113 ++-- rust/src/medium_level_il/instruction.rs | 12 + rust/src/medium_level_il/lift.rs | 3 + rust/src/medium_level_il/operation.rs | 11 + rust/src/types.rs | 333 ++++++++++-- rust/src/variable.rs | 25 + type.cpp | 457 ++++++++++++---- 24 files changed, 2649 insertions(+), 441 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index e28ebfb1a2..9e81d01521 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -10201,21 +10201,98 @@ namespace BinaryNinja { ILReferenceSource source; }; + struct ValueLocationComponent + { + Variable variable; + int64_t offset = 0; + std::optional size; + bool indirect = false; + std::optional returnedPointer; + + ValueLocationComponent() = default; + ValueLocationComponent(Variable var, int64_t ofs = 0, std::optional sz = std::nullopt, + bool indir = false, std::optional retPtr = std::nullopt) + : variable(var), offset(ofs), size(sz), indirect(indir), returnedPointer(retPtr) + {} + + ValueLocationComponent RemapVariables(const std::function& remap) const; + + bool operator==(const ValueLocationComponent& component) const; + bool operator!=(const ValueLocationComponent& component) const; + + static ValueLocationComponent FromAPIObject(const BNValueLocationComponent* loc); + BNValueLocationComponent ToAPIObject() const; + }; + + struct ValueLocation + { + std::vector components; + + ValueLocation() {} + ValueLocation(Variable var) : components {var} {} + ValueLocation(const std::vector& components) : components(components) {} + ValueLocation(std::vector&& components) : components(std::move(components)) {} + + ValueLocation(BNVariableSourceType type, uint64_t storage) : components {Variable(type, storage)} {} + ValueLocation(BNVariableSourceType type, uint32_t index, uint64_t storage) : + components {Variable(type, index, storage)} + {} + + std::optional GetVariableForReturnValue() const; + std::optional GetVariableForParameter(size_t idx) const; + ValueLocation RemapVariables(const std::function& remap) const; + void ForEachVariable(const std::function& func) const; + bool ContainsVariable(Variable var) const; + bool IsValid() const { return !components.empty(); } + + bool operator==(const ValueLocation& loc) const; + bool operator!=(const ValueLocation& loc) const; + + static ValueLocation FromAPIObject(const BNValueLocation* loc); + BNValueLocation ToAPIObject() const; + static void FreeAPIObject(BNValueLocation* loc); + }; + struct FunctionParameter { std::string name; Confidence> type; bool defaultLocation; - Variable location; + ValueLocation location; FunctionParameter() = default; FunctionParameter(const std::string& name, Confidence> type): name(name), type(type), defaultLocation(true) {} FunctionParameter(const std::string& name, const Confidence>& type, bool defaultLocation, - const Variable& location): + const ValueLocation& location) : name(name), type(type), defaultLocation(defaultLocation), location(location) {} + + static FunctionParameter FromAPIObject(const BNFunctionParameter* param); + BNFunctionParameter ToAPIObject() const; + static void FreeAPIObject(BNFunctionParameter* param); + }; + + struct ReturnValue + { + Confidence> type; + bool defaultLocation = true; + Confidence location; + + ReturnValue(Type* ty) : type(ty) {} + ReturnValue(Ref ty) : type(ty) {} + ReturnValue(const Confidence>& ty) : type(ty) {} + ReturnValue(const Confidence>& ty, bool defaultLoc, const Confidence& loc) : + type(ty), defaultLocation(defaultLoc), location(loc) {}; + ReturnValue() = default; + + bool operator==(const ReturnValue& nt) const; + bool operator!=(const ReturnValue& nt) const; + + static ReturnValue FromAPIObject(const BNReturnValue* returnValue); + BNReturnValue ToAPIObject() const; + static void FreeAPIObject(BNReturnValue* returnValue); }; class FieldResolutionInfo : public CoreRefCountObject @@ -10381,6 +10458,22 @@ namespace BinaryNinja { */ Confidence> GetChildType() const; + /*! Get the return value type and location for this Type if one exists + + \return The return value type and location + */ + ReturnValue GetReturnValue() const; + + /*! Whether the return value is in the default location + */ + bool IsReturnValueDefaultLocation() const; + + /*! Get the return value location for this Type + + \return The return value location + */ + Confidence GetReturnValueLocation() const; + /*! For Function Types, get the calling convention \return The CallingConvention @@ -10595,14 +10688,14 @@ namespace BinaryNinja { auto functionType = Type::FunctionType(retType, cc, params); \endcode - \param returnValue Return value Type + \param returnValue Return value type and location \param callingConvention Calling convention for the function \param params list of FunctionParameter s \param varArg Whether this function has variadic arguments, default false \param stackAdjust Stack adjustment for this function, default 0 \return The created function types */ - static Ref FunctionType(const Confidence>& returnValue, + static Ref FunctionType(const ReturnValue& returnValue, const Confidence>& callingConvention, const std::vector& params, const Confidence& varArg = Confidence(false, 0), const Confidence& stackAdjust = Confidence(0, 0)); @@ -10623,23 +10716,21 @@ namespace BinaryNinja { auto functionType = Type::FunctionType(retType, cc, params); \endcode - \param returnValue Return value Type + \param returnValue Return value type and location \param callingConvention Calling convention for the function \param params list of FunctionParameters \param varArg Whether this function has variadic arguments, default false \param stackAdjust Stack adjustment for this function, default 0 - \param regStackAdjust Register stack adjustmemt - \param returnRegs Return registers + \param regStackAdjust Register stack adjustmemt \return The created function types */ - static Ref FunctionType(const Confidence>& returnValue, + static Ref FunctionType(const ReturnValue& returnValue, const Confidence>& callingConvention, const std::vector& params, const Confidence& hasVariableArguments, const Confidence& canReturn, const Confidence& stackAdjust, const std::map>& regStackAdjust = std::map>(), - const Confidence>& returnRegs = Confidence>(std::vector(), 0), BNNameType ft = NoNameType, const Confidence& pure = Confidence(false, 0)); static Ref VarArgsType(); @@ -10835,6 +10926,9 @@ namespace BinaryNinja { void SetIntegerTypeDisplayType(BNIntegerDisplayType displayType); Confidence> GetChildType() const; + ReturnValue GetReturnValue() const; + bool IsReturnValueDefaultLocation() const; + Confidence GetReturnValueLocation() const; Confidence> GetCallingConvention() const; BNCallingConventionName GetCallingConventionName() const; std::vector GetParameters() const; @@ -10854,6 +10948,9 @@ namespace BinaryNinja { TypeBuilder& SetConst(const Confidence& cnst); TypeBuilder& SetVolatile(const Confidence& vltl); TypeBuilder& SetChildType(const Confidence>& child); + TypeBuilder& SetReturnValue(const ReturnValue& rv); + TypeBuilder& SetIsReturnValueDefaultLocation(bool defaultLocation); + TypeBuilder& SetReturnValueLocation(const Confidence& location); TypeBuilder& SetCallingConvention(const Confidence>& cc); TypeBuilder& SetCallingConventionName(BNCallingConventionName cc); TypeBuilder& SetSigned(const Confidence& vltl); @@ -10929,18 +11026,17 @@ namespace BinaryNinja { const Confidence& cnst = Confidence(false, 0), const Confidence& vltl = Confidence(false, 0), BNReferenceType refType = PointerReferenceType); static TypeBuilder ArrayType(const Confidence>& type, uint64_t elem); - static TypeBuilder FunctionType(const Confidence>& returnValue, + static TypeBuilder FunctionType(const ReturnValue& returnValue, const Confidence>& callingConvention, const std::vector& params, const Confidence& varArg = Confidence(false, 0), const Confidence& stackAdjust = Confidence(0, 0)); - static TypeBuilder FunctionType(const Confidence>& returnValue, + static TypeBuilder FunctionType(const ReturnValue& returnValue, const Confidence>& callingConvention, const std::vector& params, const Confidence& hasVariableArguments, const Confidence& canReturn, const Confidence& stackAdjust, const std::map>& regStackAdjust = std::map>(), - const Confidence>& returnRegs = Confidence>(std::vector(), 0), BNNameType ft = NoNameType, const Confidence& pure = Confidence(false, 0)); static TypeBuilder VarArgsType(); @@ -12792,9 +12888,13 @@ namespace BinaryNinja { Ref GetType() const; Confidence> GetReturnType() const; + ReturnValue GetReturnValue() const; + bool IsReturnValueDefaultLocation() const; + Confidence GetReturnValueLocation() const; Confidence> GetReturnRegisters() const; Confidence> GetCallingConvention() const; Confidence> GetParameterVariables() const; + Confidence> GetParameterLocations() const; Confidence HasVariableArguments() const; Confidence GetStackAdjustment() const; std::map> GetRegisterStackAdjustments() const; @@ -12802,9 +12902,11 @@ namespace BinaryNinja { void SetAutoType(Type* type); void SetAutoReturnType(const Confidence>& type); - void SetAutoReturnRegisters(const Confidence>& returnRegs); + void SetAutoReturnValue(const ReturnValue& rv); + void SetAutoIsReturnValueDefaultLocation(bool defaultLocation); + void SetAutoReturnValueLocation(const Confidence& location); void SetAutoCallingConvention(const Confidence>& convention); - void SetAutoParameterVariables(const Confidence>& vars); + void SetAutoParameterLocations(const Confidence>& locations); void SetAutoHasVariableArguments(const Confidence& varArgs); void SetAutoCanReturn(const Confidence& returns); void SetAutoPure(const Confidence& pure); @@ -12814,9 +12916,11 @@ namespace BinaryNinja { void SetUserType(Type* type); void SetReturnType(const Confidence>& type); - void SetReturnRegisters(const Confidence>& returnRegs); + void SetReturnValue(const ReturnValue& rv); + void SetIsReturnValueDefaultLocation(bool defaultLocation); + void SetReturnValueLocation(const Confidence& location); void SetCallingConvention(const Confidence>& convention); - void SetParameterVariables(const Confidence>& vars); + void SetParameterLocations(const Confidence>& locations); void SetHasVariableArguments(const Confidence& varArgs); void SetCanReturn(const Confidence& returns); void SetPure(const Confidence& pure); @@ -15359,6 +15463,7 @@ namespace BinaryNinja { size_t size, ExprId a, ExprId b, const ILSourceLocation& loc = ILSourceLocation()); ExprId FloatCompareOrdered(size_t size, ExprId a, ExprId b, const ILSourceLocation& loc = ILSourceLocation()); ExprId FloatCompareUnordered(size_t size, ExprId a, ExprId b, const ILSourceLocation& loc = ILSourceLocation()); + ExprId BlockToExpand(const std::vector& sources, const ILSourceLocation& loc = ILSourceLocation()); ExprId Goto(BNMediumLevelILLabel& label, const ILSourceLocation& loc = ILSourceLocation()); ExprId If(ExprId operand, BNMediumLevelILLabel& t, BNMediumLevelILLabel& f, @@ -17232,7 +17337,22 @@ namespace BinaryNinja { }; /*! - \ingroup callingconvention + \ingroup callingconvention + */ + struct CallLayout + { + std::vector parameters; + std::optional returnValue; + int64_t stackAdjustment = 0; + std::map registerStackAdjustments; + + static CallLayout FromAPIObject(BNCallLayout* layout); + BNCallLayout ToAPIObject() const; + static void FreeAPIObject(BNCallLayout* layout); + }; + + /*! + \ingroup callingconvention */ class CallingConvention : public CoreRefCountObject @@ -17270,7 +17390,30 @@ namespace BinaryNinja { static void GetParameterVariableForIncomingVariableCallback( void* ctxt, const BNVariable* var, BNFunction* func, BNVariable* result); - public: + static bool IsReturnTypeRegisterCompatibleCallback(void* ctxt, BNType* type); + static BNVariable GetIndirectReturnValueLocationCallback(void* ctxt); + static bool GetReturnedIndirectReturnValuePointerCallback(void* ctxt, BNVariable* outVar); + + static bool IsArgumentTypeRegisterCompatibleCallback(void* ctxt, BNType* type); + static bool AreNonRegisterArgumentsIndirectCallback(void* ctxt); + static bool AreStackArgumentsNaturallyAlignedCallback(void* ctxt); + + static BNCallLayout GetCallLayoutCallback(void* ctxt, BNReturnValue* returnValue, BNFunctionParameter* params, + size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, size_t permittedRegCount); + static void FreeCallLayoutCallback(void* ctxt, BNCallLayout* layout); + static BNValueLocation GetReturnValueLocationCallback(void* ctxt, BNReturnValue* returnValue); + static void FreeValueLocationCallback(void* ctxt, BNValueLocation* location); + static BNValueLocation* GetParameterLocationsCallback(void* ctxt, BNValueLocation* returnValue, + BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, + size_t permittedRegCount, size_t* outLocationCount); + static void FreeParameterLocationsCallback(void* ctxt, BNValueLocation* locations, size_t count); + static int64_t GetStackAdjustmentForLocationsCallback( + void* ctxt, BNValueLocation* returnValue, BNValueLocation* locations, BNType** types, size_t paramCount); + static size_t GetRegisterStackAdjustmentsCallback(void* ctxt, BNValueLocation* returnValue, + BNValueLocation* params, size_t paramCount, uint32_t** outRegs, int32_t** outAdjust); + static void FreeRegisterStackAdjustmentsCallback(void* ctxt, uint32_t* regs, int32_t* adjust, size_t count); + + public: Ref GetArchitecture() const; std::string GetName() const; @@ -17296,6 +17439,39 @@ namespace BinaryNinja { virtual Variable GetIncomingVariableForParameterVariable(const Variable& var, Function* func); virtual Variable GetParameterVariableForIncomingVariable(const Variable& var, Function* func); + + virtual bool IsReturnTypeRegisterCompatible(Type* type); + bool DefaultIsReturnTypeRegisterCompatible(Type* type); + virtual Variable GetIndirectReturnValueLocation(); + Variable GetDefaultIndirectReturnValueLocation(); + virtual std::optional GetReturnedIndirectReturnValuePointer(); + + virtual bool IsArgumentTypeRegisterCompatible(Type* type); + bool DefaultIsArgumentTypeRegisterCompatible(Type* type); + virtual bool AreNonRegisterArgumentsIndirect(); + virtual bool AreStackArgumentsNaturallyAligned(); + + virtual CallLayout GetCallLayout(const ReturnValue& returnValue, const std::vector& params, + const std::optional>& permittedRegs = std::nullopt); + virtual ValueLocation GetReturnValueLocation(const ReturnValue& returnValue); + virtual std::vector GetParameterLocations(const std::optional& returnValue, + const std::vector& params, + const std::optional>& permittedRegs = std::nullopt); + virtual int64_t GetStackAdjustmentForLocations(const std::optional& returnValue, + const std::vector& locations, const std::vector>& types); + virtual std::map GetRegisterStackAdjustments( + const std::optional& returnValue, const std::vector& params); + + CallLayout GetDefaultCallLayout(const ReturnValue& returnValue, const std::vector& params, + const std::optional>& permittedRegs = std::nullopt); + ValueLocation GetDefaultReturnValueLocation(const ReturnValue& returnValue); + std::vector GetDefaultParameterLocations(const std::optional& returnValue, + const std::vector& params, + const std::optional>& permittedRegs = std::nullopt); + int64_t GetDefaultStackAdjustmentForLocations(const std::optional& returnValue, + const std::vector& locations, const std::vector>& types); + std::map GetDefaultRegisterStackAdjustments( + const std::optional& returnValue, const std::vector& params); }; /*! @@ -17328,6 +17504,25 @@ namespace BinaryNinja { virtual Variable GetIncomingVariableForParameterVariable(const Variable& var, Function* func) override; virtual Variable GetParameterVariableForIncomingVariable(const Variable& var, Function* func) override; + + virtual bool IsReturnTypeRegisterCompatible(Type* type) override; + virtual Variable GetIndirectReturnValueLocation() override; + virtual std::optional GetReturnedIndirectReturnValuePointer() override; + + virtual bool IsArgumentTypeRegisterCompatible(Type* type) override; + virtual bool AreNonRegisterArgumentsIndirect() override; + virtual bool AreStackArgumentsNaturallyAligned() override; + + virtual CallLayout GetCallLayout(const ReturnValue& returnValue, const std::vector& params, + const std::optional>& permittedRegs = std::nullopt) override; + virtual ValueLocation GetReturnValueLocation(const ReturnValue& returnValue) override; + virtual std::vector GetParameterLocations(const std::optional& returnValue, + const std::vector& params, + const std::optional>& permittedRegs = std::nullopt) override; + virtual int64_t GetStackAdjustmentForLocations(const std::optional& returnValue, + const std::vector& locations, const std::vector>& types) override; + virtual std::map GetRegisterStackAdjustments( + const std::optional& returnValue, const std::vector& params) override; }; /*! diff --git a/binaryninjacore.h b/binaryninjacore.h index 1e346bb19d..c7e4e087fb 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -1123,6 +1123,9 @@ extern "C" // Cue for use-def heuristics to follow through simple copies (e.g., register windowing for Xtensa) ILTransparentCopy = 0x1000, + + // Instruction is defining an implicit trait of the calling convention + MLILCallingConventionImplicit = 0x2000 }; BN_ENUM(uint8_t, BNIntrinsicClass) @@ -1208,6 +1211,8 @@ extern "C" StackFrameOffset, ReturnAddressValue, ImportedAddressValue, + ResultPointerValue, + ParameterPointerValue, // The following are only valid in BNPossibleValueSet SignedRangeValue, @@ -1484,7 +1489,9 @@ extern "C" MLIL_MEMORY_INTRINSIC_SSA, MLIL_FREE_VAR_SLOT_SSA, MLIL_VAR_PHI, - MLIL_MEM_PHI + MLIL_MEM_PHI, + + MLIL_BLOCK_TO_EXPAND, // Must be expanded by a future workflow step, used temporarily to insert instructions }; typedef struct BNMediumLevelILInstruction @@ -1508,7 +1515,9 @@ extern "C" { StackVariableSourceType, RegisterVariableSourceType, - FlagVariableSourceType + FlagVariableSourceType, + CompositeReturnValueSourceType, + CompositeParameterSourceType, }; typedef struct BNVariable @@ -2556,15 +2565,66 @@ extern "C" uint8_t confidence; } BNRegisterSetWithConfidence; + typedef struct BNValueLocationComponent + { + BNVariable variable; + int64_t offset; + bool sizeValid; + uint64_t size; + bool indirect; + bool returnedPointerValid; + BNVariable returnedPointer; + } BNValueLocationComponent; + + typedef struct BNValueLocation + { + size_t count; + BNValueLocationComponent* components; + } BNValueLocation; + + typedef struct BNValueLocationWithConfidence + { + BNValueLocation location; + uint8_t confidence; + } BNValueLocationWithConfidence; + + typedef struct BNValueLocationListWithConfidence + { + BNValueLocation* locations; + size_t count; + uint8_t confidence; + } BNValueLocationListWithConfidence; + typedef struct BNFunctionParameter { char* name; BNType* type; uint8_t typeConfidence; bool defaultLocation; - BNVariable location; + BNValueLocation location; } BNFunctionParameter; + typedef struct BNReturnValue + { + BNType* type; + uint8_t typeConfidence; + bool defaultLocation; + BNValueLocation location; + uint8_t locationConfidence; + } BNReturnValue; + + typedef struct BNCallLayout + { + BNValueLocation* parameters; + size_t parameterCount; + bool returnValueValid; + BNValueLocation returnValue; + int64_t stackAdjustment; + uint32_t* registerStackAdjustmentRegisters; + int32_t* registerStackAdjustmentAmounts; + size_t registerStackAdjustmentCount; + } BNCallLayout; + typedef struct BNQualifiedNameAndType { BNQualifiedName name; @@ -2813,12 +2873,35 @@ extern "C" void (*getIncomingRegisterValue)(void* ctxt, uint32_t reg, BNFunction* func, BNRegisterValue* result); void (*getIncomingFlagValue)(void* ctxt, uint32_t flag, BNFunction* func, BNRegisterValue* result); + bool (*isReturnTypeRegisterCompatible)(void* ctxt, BNType* type); + BNVariable (*getIndirectReturnValueLocation)(void* ctxt); + bool (*getReturnedIndirectReturnValuePointer)(void* ctxt, BNVariable* outVar); + + bool (*isArgumentTypeRegisterCompatible)(void* ctxt, BNType* type); + bool (*areNonRegisterArgumentsIndirect)(void* ctxt); + bool (*areStackArgumentsNaturallyAligned)(void* ctxt); + void (*getIncomingVariableForParameterVariable)( void* ctxt, const BNVariable* var, BNFunction* func, BNVariable* result); void (*getParameterVariableForIncomingVariable)( void* ctxt, const BNVariable* var, BNFunction* func, BNVariable* result); bool (*areArgumentRegistersUsedForVarArgs)(void* ctxt); + + BNCallLayout (*getCallLayout)(void* ctxt, BNReturnValue* returnValue, BNFunctionParameter* params, + size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, size_t permittedRegCount); + void (*freeCallLayout)(void* ctxt, BNCallLayout* layout); + BNValueLocation (*getReturnValueLocation)(void* ctxt, BNReturnValue* returnValue); + void (*freeValueLocation)(void* ctxt, BNValueLocation* location); + BNValueLocation* (*getParameterLocations)(void* ctxt, BNValueLocation* returnValue, BNFunctionParameter* params, + size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, size_t permittedRegCount, + size_t* outLocationCount); + void (*freeParameterLocations)(void* ctxt, BNValueLocation* locations, size_t count); + int64_t (*getStackAdjustmentForLocations)( + void* ctxt, BNValueLocation* returnValue, BNValueLocation* locations, BNType** types, size_t paramCount); + size_t (*getRegisterStackAdjustments)(void* ctxt, BNValueLocation* returnValue, BNValueLocation* params, + size_t paramCount, uint32_t** outRegs, int32_t** outAdjust); + void (*freeRegisterStackAdjustments)(void* ctxt, uint32_t* regs, int32_t* adjust, size_t count); } BNCustomCallingConvention; typedef struct BNVariableNameAndType @@ -5150,10 +5233,19 @@ extern "C" BINARYNINJACOREAPI BNType* BNGetFunctionType(BNFunction* func); BINARYNINJACOREAPI BNTypeWithConfidence BNGetFunctionReturnType(BNFunction* func); + BINARYNINJACOREAPI BNReturnValue BNGetFunctionReturnValue(BNFunction* func); + BINARYNINJACOREAPI void BNFreeReturnValue(BNReturnValue* ret); + BINARYNINJACOREAPI bool BNIsFunctionReturnValueDefaultLocation(BNFunction* func); + BINARYNINJACOREAPI BNValueLocationWithConfidence BNGetFunctionReturnValueLocation(BNFunction* func); BINARYNINJACOREAPI BNRegisterSetWithConfidence BNGetFunctionReturnRegisters(BNFunction* func); BINARYNINJACOREAPI BNCallingConventionWithConfidence BNGetFunctionCallingConvention(BNFunction* func); BINARYNINJACOREAPI BNParameterVariablesWithConfidence BNGetFunctionParameterVariables(BNFunction* func); BINARYNINJACOREAPI void BNFreeParameterVariables(BNParameterVariablesWithConfidence* vars); + BINARYNINJACOREAPI BNValueLocationListWithConfidence BNGetFunctionParameterLocations(BNFunction* func); + BINARYNINJACOREAPI void BNFreeParameterLocations(BNValueLocationListWithConfidence* locations); + BINARYNINJACOREAPI bool BNGetValueLocationVariableForReturnValue(const BNValueLocation* location, BNVariable* var); + BINARYNINJACOREAPI bool BNGetValueLocationVariableForParameter( + const BNValueLocation* location, BNVariable* var, size_t idx); BINARYNINJACOREAPI BNBoolWithConfidence BNFunctionHasVariableArguments(BNFunction* func); BINARYNINJACOREAPI BNOffsetWithConfidence BNGetFunctionStackAdjustment(BNFunction* func); BINARYNINJACOREAPI BNRegisterStackAdjustment* BNGetFunctionRegisterStackAdjustments( @@ -5163,11 +5255,14 @@ extern "C" BINARYNINJACOREAPI void BNFreeRegisterSet(BNRegisterSetWithConfidence* regs); BINARYNINJACOREAPI void BNSetAutoFunctionReturnType(BNFunction* func, BNTypeWithConfidence* type); - BINARYNINJACOREAPI void BNSetAutoFunctionReturnRegisters(BNFunction* func, BNRegisterSetWithConfidence* regs); + BINARYNINJACOREAPI void BNSetAutoIsFunctionReturnValueDefaultLocation(BNFunction* func, bool defaultLocation); + BINARYNINJACOREAPI void BNSetAutoFunctionReturnValueLocation( + BNFunction* func, BNValueLocationWithConfidence* location); + BINARYNINJACOREAPI void BNSetAutoFunctionReturnValue(BNFunction* func, BNReturnValue* returnValue); BINARYNINJACOREAPI void BNSetAutoFunctionCallingConvention( BNFunction* func, BNCallingConventionWithConfidence* convention); - BINARYNINJACOREAPI void BNSetAutoFunctionParameterVariables( - BNFunction* func, BNParameterVariablesWithConfidence* vars); + BINARYNINJACOREAPI void BNSetAutoFunctionParameterLocations( + BNFunction* func, BNValueLocationListWithConfidence* locations); BINARYNINJACOREAPI void BNSetAutoFunctionHasVariableArguments(BNFunction* func, BNBoolWithConfidence* varArgs); BINARYNINJACOREAPI void BNSetAutoFunctionCanReturn(BNFunction* func, BNBoolWithConfidence* returns); BINARYNINJACOREAPI void BNSetAutoFunctionPure(BNFunction* func, BNBoolWithConfidence* pure); @@ -5177,11 +5272,14 @@ extern "C" BINARYNINJACOREAPI void BNSetAutoFunctionClobberedRegisters(BNFunction* func, BNRegisterSetWithConfidence* regs); BINARYNINJACOREAPI void BNSetUserFunctionReturnType(BNFunction* func, BNTypeWithConfidence* type); - BINARYNINJACOREAPI void BNSetUserFunctionReturnRegisters(BNFunction* func, BNRegisterSetWithConfidence* regs); + BINARYNINJACOREAPI void BNSetUserIsFunctionReturnValueDefaultLocation(BNFunction* func, bool defaultLocation); + BINARYNINJACOREAPI void BNSetUserFunctionReturnValueLocation( + BNFunction* func, BNValueLocationWithConfidence* location); + BINARYNINJACOREAPI void BNSetUserFunctionReturnValue(BNFunction* func, BNReturnValue* returnValue); BINARYNINJACOREAPI void BNSetUserFunctionCallingConvention( BNFunction* func, BNCallingConventionWithConfidence* convention); - BINARYNINJACOREAPI void BNSetUserFunctionParameterVariables( - BNFunction* func, BNParameterVariablesWithConfidence* vars); + BINARYNINJACOREAPI void BNSetUserFunctionParameterLocations( + BNFunction* func, BNValueLocationListWithConfidence* locations); BINARYNINJACOREAPI void BNSetUserFunctionHasVariableArguments(BNFunction* func, BNBoolWithConfidence* varArgs); BINARYNINJACOREAPI void BNSetUserFunctionCanReturn(BNFunction* func, BNBoolWithConfidence* returns); BINARYNINJACOREAPI void BNSetUserFunctionPure(BNFunction* func, BNBoolWithConfidence* pure); @@ -6904,11 +7002,11 @@ extern "C" BINARYNINJACOREAPI BNType* BNCreatePointerTypeOfWidth(size_t width, const BNTypeWithConfidence* const type, BNBoolWithConfidence* cnst, BNBoolWithConfidence* vltl, BNReferenceType refType); BINARYNINJACOREAPI BNType* BNCreateArrayType(const BNTypeWithConfidence* const type, uint64_t elem); - BINARYNINJACOREAPI BNType* BNCreateFunctionType(BNTypeWithConfidence* returnValue, BNCallingConventionWithConfidence* callingConvention, - BNFunctionParameter* params, size_t paramCount, BNBoolWithConfidence* varArg, - BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust, - uint32_t* regStackAdjustRegs, BNOffsetWithConfidence* regStackAdjustValues, size_t regStackAdjustCount, - BNRegisterSetWithConfidence* returnRegs, BNNameType ft, BNBoolWithConfidence* pure); + BINARYNINJACOREAPI BNType* BNCreateFunctionType(BNReturnValue* returnValue, + BNCallingConventionWithConfidence* callingConvention, BNFunctionParameter* params, size_t paramCount, + BNBoolWithConfidence* varArg, BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust, + uint32_t* regStackAdjustRegs, BNOffsetWithConfidence* regStackAdjustValues, size_t regStackAdjustCount, + BNNameType ft, BNBoolWithConfidence* pure); BINARYNINJACOREAPI BNType* BNCreateVarArgsType(); BINARYNINJACOREAPI BNType* BNCreateValueType(const char* value); BINARYNINJACOREAPI char* BNGetNameTypeString(BNNameType classFunctionType); @@ -6939,11 +7037,11 @@ extern "C" const BNTypeWithConfidence* const type, BNBoolWithConfidence* cnst, BNBoolWithConfidence* vltl, BNReferenceType refType); BINARYNINJACOREAPI BNTypeBuilder* BNCreateArrayTypeBuilder(const BNTypeWithConfidence* const type, uint64_t elem); - BINARYNINJACOREAPI BNTypeBuilder* BNCreateFunctionTypeBuilder(BNTypeWithConfidence* returnValue, BNCallingConventionWithConfidence* callingConvention, - BNFunctionParameter* params, size_t paramCount, BNBoolWithConfidence* varArg, - BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust, + BINARYNINJACOREAPI BNTypeBuilder* BNCreateFunctionTypeBuilder(BNReturnValue* returnValue, + BNCallingConventionWithConfidence* callingConvention, BNFunctionParameter* params, size_t paramCount, + BNBoolWithConfidence* varArg, BNBoolWithConfidence* canReturn, BNOffsetWithConfidence* stackAdjust, uint32_t* regStackAdjustRegs, BNOffsetWithConfidence* regStackAdjustValues, size_t regStackAdjustCount, - BNRegisterSetWithConfidence* returnRegs, BNNameType ft, BNBoolWithConfidence* pure); + BNNameType ft, BNBoolWithConfidence* pure); BINARYNINJACOREAPI BNTypeBuilder* BNCreateVarArgsTypeBuilder(); BINARYNINJACOREAPI BNTypeBuilder* BNCreateValueTypeBuilder(const char* value); BINARYNINJACOREAPI BNType* BNFinalizeTypeBuilder(BNTypeBuilder* type); @@ -6962,6 +7060,10 @@ extern "C" BINARYNINJACOREAPI BNBoolWithConfidence BNIsTypeVolatile(BNType* type); BINARYNINJACOREAPI bool BNIsTypeFloatingPoint(BNType* type); BINARYNINJACOREAPI BNTypeWithConfidence BNGetChildType(BNType* type); + BINARYNINJACOREAPI BNReturnValue BNGetTypeReturnValue(BNType* type); + BINARYNINJACOREAPI bool BNIsTypeReturnValueDefaultLocation(BNType* type); + BINARYNINJACOREAPI BNValueLocationWithConfidence BNGetTypeReturnValueLocation(BNType* type); + BINARYNINJACOREAPI void BNFreeValueLocation(BNValueLocation* location); BINARYNINJACOREAPI BNCallingConventionWithConfidence BNGetTypeCallingConvention(BNType* type); BINARYNINJACOREAPI BNCallingConventionName BNGetTypeCallingConventionName(BNType* type); BINARYNINJACOREAPI BNFunctionParameter* BNGetTypeParameters(BNType* type, size_t* count); @@ -7030,6 +7132,9 @@ extern "C" BINARYNINJACOREAPI BNBoolWithConfidence BNIsTypeBuilderVolatile(BNTypeBuilder* type); BINARYNINJACOREAPI bool BNIsTypeBuilderFloatingPoint(BNTypeBuilder* type); BINARYNINJACOREAPI BNTypeWithConfidence BNGetTypeBuilderChildType(BNTypeBuilder* type); + BINARYNINJACOREAPI BNReturnValue BNGetTypeBuilderReturnValue(BNTypeBuilder* type); + BINARYNINJACOREAPI bool BNIsTypeBuilderReturnValueDefaultLocation(BNTypeBuilder* type); + BINARYNINJACOREAPI BNValueLocationWithConfidence BNGetTypeBuilderReturnValueLocation(BNTypeBuilder* type); BINARYNINJACOREAPI BNCallingConventionWithConfidence BNGetTypeBuilderCallingConvention(BNTypeBuilder* type); BINARYNINJACOREAPI BNCallingConventionName BNGetTypeBuilderCallingConventionName(BNTypeBuilder* type); BINARYNINJACOREAPI BNFunctionParameter* BNGetTypeBuilderParameters(BNTypeBuilder* type, size_t* count); @@ -7054,6 +7159,10 @@ extern "C" BINARYNINJACOREAPI void BNTypeBuilderSetVolatile(BNTypeBuilder* type, BNBoolWithConfidence* vltl); BINARYNINJACOREAPI void BNTypeBuilderSetSigned(BNTypeBuilder* type, BNBoolWithConfidence* sign); BINARYNINJACOREAPI void BNTypeBuilderSetChildType(BNTypeBuilder* type, BNTypeWithConfidence* child); + BINARYNINJACOREAPI void BNTypeBuilderSetReturnValue(BNTypeBuilder* type, BNReturnValue* rv); + BINARYNINJACOREAPI void BNTypeBuilderSetIsReturnValueDefaultLocation(BNTypeBuilder* type, bool defaultLocation); + BINARYNINJACOREAPI void BNTypeBuilderSetReturnValueLocation( + BNTypeBuilder* type, BNValueLocationWithConfidence* location); BINARYNINJACOREAPI void BNTypeBuilderSetCallingConvention(BNTypeBuilder* type, BNCallingConventionWithConfidence* cc); BINARYNINJACOREAPI void BNTypeBuilderSetCallingConventionName(BNTypeBuilder* type, BNCallingConventionName cc); BINARYNINJACOREAPI BNOffsetWithConfidence BNGetTypeBuilderStackAdjustment(BNTypeBuilder* type); @@ -7456,21 +7565,45 @@ extern "C" BNCallingConvention* cc, uint32_t reg, BNFunction* func); BINARYNINJACOREAPI BNRegisterValue BNGetIncomingFlagValue(BNCallingConvention* cc, uint32_t reg, BNFunction* func); - BINARYNINJACOREAPI BNVariable* BNGetVariablesForParametersDefaultPermittedArgs( - BNCallingConvention* cc, const BNFunctionParameter* params, size_t paramCount, size_t* count); - BINARYNINJACOREAPI BNVariable* BNGetVariablesForParameters(BNCallingConvention* cc, - const BNFunctionParameter* params, size_t paramCount, const uint32_t* permittedArgs, size_t permittedArgCount, - size_t* count); + BINARYNINJACOREAPI BNCallLayout BNGetCallLayout(BNCallingConvention* cc, const BNReturnValue* returnValue, + const BNFunctionParameter* params, size_t paramCount, const uint32_t* permittedRegs, size_t permittedRegCount); + BINARYNINJACOREAPI BNCallLayout BNGetCallLayoutDefaultPermittedArgs(BNCallingConvention* cc, + const BNReturnValue* returnValue, const BNFunctionParameter* params, size_t paramCount); + BINARYNINJACOREAPI BNCallLayout BNGetDefaultCallLayout(BNCallingConvention* cc, const BNReturnValue* returnValue, + const BNFunctionParameter* params, size_t paramCount, const uint32_t* permittedRegs, size_t permittedRegCount); + BINARYNINJACOREAPI BNCallLayout BNGetDefaultCallLayoutDefaultPermittedArgs(BNCallingConvention* cc, + const BNReturnValue* returnValue, const BNFunctionParameter* params, size_t paramCount); + BINARYNINJACOREAPI void BNFreeCallLayout(BNCallLayout* layout); + BINARYNINJACOREAPI BNValueLocation BNGetReturnValueLocation(BNCallingConvention* cc, BNReturnValue* returnValue); + BINARYNINJACOREAPI BNValueLocation BNGetDefaultReturnValueLocation( + BNCallingConvention* cc, BNReturnValue* returnValue); + BINARYNINJACOREAPI BNValueLocation* BNGetParameterLocations(BNCallingConvention* cc, BNValueLocation* returnValue, + BNFunctionParameter* params, size_t paramCount, const uint32_t* permittedRegs, size_t permittedRegCount, + size_t* outCount); + BINARYNINJACOREAPI BNValueLocation* BNGetParameterLocationsDefaultPermittedArgs(BNCallingConvention* cc, + BNValueLocation* returnValue, BNFunctionParameter* params, size_t paramCount, size_t* outCount); + BINARYNINJACOREAPI BNValueLocation* BNGetDefaultParameterLocations(BNCallingConvention* cc, + BNValueLocation* returnValue, BNFunctionParameter* params, size_t paramCount, const uint32_t* permittedRegs, + size_t permittedRegCount, size_t* outCount); + BINARYNINJACOREAPI BNValueLocation* BNGetDefaultParameterLocationsDefaultPermittedArgs(BNCallingConvention* cc, + BNValueLocation* returnValue, BNFunctionParameter* params, size_t paramCount, size_t* outCount); + BINARYNINJACOREAPI void BNFreeValueLocationList(BNValueLocation* locations, size_t count); + BINARYNINJACOREAPI BNVariable* BNGetParameterOrderingForVariables( BNCallingConvention* cc, const BNVariable* paramVars, const BNType** paramTypes, size_t paramCount, size_t* count); - BINARYNINJACOREAPI int64_t BNGetStackAdjustmentForVariables( - BNCallingConvention* cc, const BNVariable* paramVars, const BNType** paramTypes, - size_t paramCount); - BINARYNINJACOREAPI size_t BNGetRegisterStackAdjustments( - BNCallingConvention* cc, const uint32_t* returnRegs, size_t returnRegCount, BNType* returnType, - const BNVariable* params, size_t paramCount, const BNType** types, size_t typeCount, - uint32_t** resultRegisters, uint32_t** resultAdjustments); + BINARYNINJACOREAPI int64_t BNGetStackAdjustmentForLocations(BNCallingConvention* cc, BNValueLocation* returnValue, + const BNValueLocation* paramLocations, const BNType** paramTypes, size_t paramCount); + BINARYNINJACOREAPI int64_t BNGetDefaultStackAdjustmentForLocations(BNCallingConvention* cc, + BNValueLocation* returnValue, const BNValueLocation* paramLocations, const BNType** paramTypes, + size_t paramCount); + BINARYNINJACOREAPI size_t BNGetCallingConventionRegisterStackAdjustments(BNCallingConvention* cc, + BNValueLocation* returnValue, BNValueLocation* params, size_t paramCount, uint32_t** outRegs, + int32_t** outAdjust); + BINARYNINJACOREAPI size_t BNGetCallingConventionDefaultRegisterStackAdjustments(BNCallingConvention* cc, + BNValueLocation* returnValue, BNValueLocation* params, size_t paramCount, uint32_t** outRegs, + int32_t** outAdjust); + BINARYNINJACOREAPI void BNFreeCallingConventionRegisterStackAdjustments(uint32_t* regs, int32_t* adjust); BINARYNINJACOREAPI BNVariable BNGetIncomingVariableForParameterVariable( BNCallingConvention* cc, const BNVariable* var, BNFunction* func); @@ -7481,6 +7614,16 @@ extern "C" BINARYNINJACOREAPI BNVariable BNGetDefaultParameterVariableForIncomingVariable( BNCallingConvention* cc, const BNVariable* var); + BINARYNINJACOREAPI bool BNIsReturnTypeRegisterCompatible(BNCallingConvention* cc, BNType* type); + BINARYNINJACOREAPI bool BNDefaultIsReturnTypeRegisterCompatible(BNCallingConvention* cc, BNType* type); + BINARYNINJACOREAPI BNVariable BNGetIndirectReturnValueLocation(BNCallingConvention* cc); + BINARYNINJACOREAPI BNVariable BNGetDefaultIndirectReturnValueLocation(BNCallingConvention* cc); + BINARYNINJACOREAPI bool BNGetReturnedIndirectReturnValuePointer(BNCallingConvention* cc, BNVariable* outVar); + BINARYNINJACOREAPI bool BNIsArgumentTypeRegisterCompatible(BNCallingConvention* cc, BNType* type); + BINARYNINJACOREAPI bool BNDefaultIsArgumentTypeRegisterCompatible(BNCallingConvention* cc, BNType* type); + BINARYNINJACOREAPI bool BNAreNonRegisterArgumentsIndirect(BNCallingConvention* cc); + BINARYNINJACOREAPI bool BNAreStackArgumentsNaturallyAligned(BNCallingConvention* cc); + BINARYNINJACOREAPI BNCallingConvention* BNGetArchitectureDefaultCallingConvention(BNArchitecture* arch); BINARYNINJACOREAPI BNCallingConvention* BNGetArchitectureCdeclCallingConvention(BNArchitecture* arch); BINARYNINJACOREAPI BNCallingConvention* BNGetArchitectureStdcallCallingConvention(BNArchitecture* arch); diff --git a/callingconvention.cpp b/callingconvention.cpp index abc9305358..cfbe6f7d7a 100644 --- a/callingconvention.cpp +++ b/callingconvention.cpp @@ -24,6 +24,61 @@ using namespace std; using namespace BinaryNinja; +CallLayout CallLayout::FromAPIObject(BNCallLayout* layout) +{ + CallLayout result; + result.parameters.reserve(layout->parameterCount); + for (size_t i = 0; i < layout->parameterCount; i++) + result.parameters.push_back(ValueLocation::FromAPIObject(&layout->parameters[i])); + if (layout->returnValueValid) + result.returnValue = ValueLocation::FromAPIObject(&layout->returnValue); + result.stackAdjustment = layout->stackAdjustment; + for (size_t i = 0; i < layout->registerStackAdjustmentCount; i++) + { + result.registerStackAdjustments[layout->registerStackAdjustmentRegisters[i]] = + layout->registerStackAdjustmentAmounts[i]; + } + return result; +} + + +BNCallLayout CallLayout::ToAPIObject() const +{ + BNCallLayout result; + result.parameters = new BNValueLocation[parameters.size()]; + result.parameterCount = parameters.size(); + for (size_t i = 0; i < parameters.size(); i++) + result.parameters[i] = parameters[i].ToAPIObject(); + result.returnValue = returnValue.value_or(ValueLocation()).ToAPIObject(); + result.returnValueValid = returnValue.has_value(); + result.stackAdjustment = stackAdjustment; + + result.registerStackAdjustmentCount = registerStackAdjustments.size(); + result.registerStackAdjustmentRegisters = new uint32_t[registerStackAdjustments.size()]; + result.registerStackAdjustmentAmounts = new int32_t[registerStackAdjustments.size()]; + size_t i = 0; + for (auto [reg, adjust] : registerStackAdjustments) + { + result.registerStackAdjustmentRegisters[i] = reg; + result.registerStackAdjustmentAmounts[i] = adjust; + i++; + } + + return result; +} + + +void CallLayout::FreeAPIObject(BNCallLayout* layout) +{ + for (size_t i = 0; i < layout->parameterCount; i++) + ValueLocation::FreeAPIObject(&layout->parameters[i]); + delete[] layout->parameters; + ValueLocation::FreeAPIObject(&layout->returnValue); + delete[] layout->registerStackAdjustmentRegisters; + delete[] layout->registerStackAdjustmentAmounts; +} + + CallingConvention::CallingConvention(BNCallingConvention* cc) { m_object = cc; @@ -54,6 +109,21 @@ CallingConvention::CallingConvention(Architecture* arch, const string& name) cc.getIncomingFlagValue = GetIncomingFlagValueCallback; cc.getIncomingVariableForParameterVariable = GetIncomingVariableForParameterVariableCallback; cc.getParameterVariableForIncomingVariable = GetParameterVariableForIncomingVariableCallback; + cc.isReturnTypeRegisterCompatible = IsReturnTypeRegisterCompatibleCallback; + cc.getIndirectReturnValueLocation = GetIndirectReturnValueLocationCallback; + cc.getReturnedIndirectReturnValuePointer = GetReturnedIndirectReturnValuePointerCallback; + cc.isArgumentTypeRegisterCompatible = IsArgumentTypeRegisterCompatibleCallback; + cc.areNonRegisterArgumentsIndirect = AreNonRegisterArgumentsIndirectCallback; + cc.areStackArgumentsNaturallyAligned = AreStackArgumentsNaturallyAlignedCallback; + cc.getCallLayout = GetCallLayoutCallback; + cc.freeCallLayout = FreeCallLayoutCallback; + cc.getReturnValueLocation = GetReturnValueLocationCallback; + cc.freeValueLocation = FreeValueLocationCallback; + cc.getParameterLocations = GetParameterLocationsCallback; + cc.freeParameterLocations = FreeParameterLocationsCallback; + cc.getStackAdjustmentForLocations = GetStackAdjustmentForLocationsCallback; + cc.getRegisterStackAdjustments = GetRegisterStackAdjustmentsCallback; + cc.freeRegisterStackAdjustments = FreeRegisterStackAdjustmentsCallback; AddRefForRegistration(); m_object = BNCreateCallingConvention(arch->GetObject(), name.c_str(), &cc); @@ -245,6 +315,194 @@ void CallingConvention::GetParameterVariableForIncomingVariableCallback( } +bool CallingConvention::IsReturnTypeRegisterCompatibleCallback(void* ctxt, BNType* type) +{ + CallbackRef cc(ctxt); + Ref typeObj; + if (type) + typeObj = new Type(BNNewTypeReference(type)); + return cc->IsReturnTypeRegisterCompatible(typeObj); +} + + +BNVariable CallingConvention::GetIndirectReturnValueLocationCallback(void* ctxt) +{ + CallbackRef cc(ctxt); + return cc->GetIndirectReturnValueLocation(); +} + + +bool CallingConvention::GetReturnedIndirectReturnValuePointerCallback(void* ctxt, BNVariable* outVar) +{ + CallbackRef cc(ctxt); + auto var = cc->GetReturnedIndirectReturnValuePointer(); + if (var.has_value()) + *outVar = var.value(); + return var.has_value(); +} + + +bool CallingConvention::IsArgumentTypeRegisterCompatibleCallback(void* ctxt, BNType* type) +{ + CallbackRef cc(ctxt); + Ref typeObj; + if (type) + typeObj = new Type(BNNewTypeReference(type)); + return cc->IsArgumentTypeRegisterCompatible(typeObj); +} + + +bool CallingConvention::AreNonRegisterArgumentsIndirectCallback(void* ctxt) +{ + CallbackRef cc(ctxt); + return cc->AreNonRegisterArgumentsIndirect(); +} + + +bool CallingConvention::AreStackArgumentsNaturallyAlignedCallback(void* ctxt) +{ + CallbackRef cc(ctxt); + return cc->AreStackArgumentsNaturallyAligned(); +} + + +BNCallLayout CallingConvention::GetCallLayoutCallback(void* ctxt, BNReturnValue* returnValue, + BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, + size_t permittedRegCount) +{ + CallbackRef cc(ctxt); + auto ret = ReturnValue::FromAPIObject(returnValue); + vector paramObjs; + paramObjs.reserve(paramCount); + for (size_t i = 0; i < paramCount; i++) + paramObjs.push_back(FunctionParameter::FromAPIObject(¶ms[i])); + optional> regOpt; + if (hasPermittedRegs) + { + set regs; + for (size_t i = 0; i < permittedRegCount; i++) + regs.insert(permittedRegs[i]); + regOpt = regs; + } + + auto layout = cc->GetCallLayout(ret, paramObjs, regOpt); + return layout.ToAPIObject(); +} + + +void CallingConvention::FreeCallLayoutCallback(void*, BNCallLayout* layout) +{ + CallLayout::FreeAPIObject(layout); +} + + +BNValueLocation CallingConvention::GetReturnValueLocationCallback(void* ctxt, BNReturnValue* returnValue) +{ + CallbackRef cc(ctxt); + ReturnValue ret = ReturnValue::FromAPIObject(returnValue); + ValueLocation location = cc->GetReturnValueLocation(ret); + return location.ToAPIObject(); +} + + +void CallingConvention::FreeValueLocationCallback(void*, BNValueLocation* location) +{ + ValueLocation::FreeAPIObject(location); +} + + +BNValueLocation* CallingConvention::GetParameterLocationsCallback(void* ctxt, BNValueLocation* returnValue, + BNFunctionParameter* params, size_t paramCount, bool hasPermittedRegs, uint32_t* permittedRegs, + size_t permittedRegCount, size_t* outLocationCount) +{ + CallbackRef cc(ctxt); + std::optional ret; + if (returnValue) + ret = ValueLocation::FromAPIObject(returnValue); + vector paramObjs; + paramObjs.reserve(paramCount); + for (size_t i = 0; i < paramCount; i++) + paramObjs.push_back(FunctionParameter::FromAPIObject(¶ms[i])); + optional> regOpt; + if (hasPermittedRegs) + { + set regs; + for (size_t i = 0; i < permittedRegCount; i++) + regs.insert(permittedRegs[i]); + regOpt = regs; + } + + vector locations = cc->GetParameterLocations(ret, paramObjs, regOpt); + + *outLocationCount = locations.size(); + BNValueLocation* result = new BNValueLocation[locations.size()]; + for (size_t i = 0; i < locations.size(); i++) + result[i] = locations[i].ToAPIObject(); + return result; +} + + +void CallingConvention::FreeParameterLocationsCallback(void*, BNValueLocation* locations, size_t count) +{ + for (size_t i = 0; i < count; i++) + ValueLocation::FreeAPIObject(&locations[i]); + delete[] locations; +} + + +int64_t CallingConvention::GetStackAdjustmentForLocationsCallback( + void* ctxt, BNValueLocation* returnValue, BNValueLocation* locations, BNType** types, size_t paramCount) +{ + CallbackRef cc(ctxt); + std::optional ret; + if (returnValue) + ret = ValueLocation::FromAPIObject(returnValue); + vector locationObjs; + locationObjs.reserve(paramCount); + for (size_t i = 0; i < paramCount; i++) + locationObjs.push_back(ValueLocation::FromAPIObject(&locations[i])); + vector> typeObjs; + typeObjs.reserve(paramCount); + for (size_t i = 0; i < paramCount; i++) + typeObjs.push_back(new Type(BNNewTypeReference(types[i]))); + + return cc->GetStackAdjustmentForLocations(ret, locationObjs, typeObjs); +} + + +size_t CallingConvention::GetRegisterStackAdjustmentsCallback(void* ctxt, BNValueLocation* returnValue, + BNValueLocation* params, size_t paramCount, uint32_t** outRegs, int32_t** outAdjust) +{ + CallbackRef cc(ctxt); + std::optional ret; + if (returnValue) + ret = ValueLocation::FromAPIObject(returnValue); + vector paramObjs; + paramObjs.reserve(paramCount); + for (size_t i = 0; i < paramCount; i++) + paramObjs.push_back(ValueLocation::FromAPIObject(¶ms[i])); + + auto result = cc->GetRegisterStackAdjustments(ret, paramObjs); + + *outRegs = new uint32_t[result.size()]; + *outAdjust = new int32_t[result.size()]; + size_t i = 0; + for (auto it = result.begin(); it != result.end(); ++it, ++i) + { + (*outRegs)[i] = it->first; + (*outAdjust)[i] = it->second; + } + return result.size(); +} + + +void CallingConvention::FreeRegisterStackAdjustmentsCallback(void*, uint32_t* regs, int32_t* adjust, size_t) +{ + delete[] regs; + delete[] adjust; +} + + Ref CallingConvention::GetArchitecture() const { return new CoreArchitecture(BNGetCallingConventionArchitecture(m_object)); @@ -370,6 +628,249 @@ Variable CallingConvention::GetParameterVariableForIncomingVariable(const Variab } +bool CallingConvention::IsReturnTypeRegisterCompatible(Type* type) +{ + return DefaultIsReturnTypeRegisterCompatible(type); +} + + +bool CallingConvention::DefaultIsReturnTypeRegisterCompatible(Type* type) +{ + return BNDefaultIsReturnTypeRegisterCompatible(m_object, type ? type->GetObject() : nullptr); +} + + +Variable CallingConvention::GetIndirectReturnValueLocation() +{ + return GetDefaultIndirectReturnValueLocation(); +} + + +Variable CallingConvention::GetDefaultIndirectReturnValueLocation() +{ + return BNGetDefaultIndirectReturnValueLocation(m_object); +} + + +std::optional CallingConvention::GetReturnedIndirectReturnValuePointer() +{ + return std::nullopt; +} + + +bool CallingConvention::IsArgumentTypeRegisterCompatible(Type* type) +{ + return DefaultIsArgumentTypeRegisterCompatible(type); +} + + +bool CallingConvention::DefaultIsArgumentTypeRegisterCompatible(Type* type) +{ + return BNDefaultIsArgumentTypeRegisterCompatible(m_object, type ? type->GetObject() : nullptr); +} + + +bool CallingConvention::AreNonRegisterArgumentsIndirect() +{ + return false; +} + + +bool CallingConvention::AreStackArgumentsNaturallyAligned() +{ + return false; +} + + +CallLayout CallingConvention::GetCallLayout(const ReturnValue& returnValue, const vector& params, + const optional>& permittedRegs) +{ + return GetDefaultCallLayout(returnValue, params, permittedRegs); +} + + +ValueLocation CallingConvention::GetReturnValueLocation(const ReturnValue& returnValue) +{ + return GetDefaultReturnValueLocation(returnValue); +} + + +vector CallingConvention::GetParameterLocations(const optional& returnValue, + const vector& params, const optional>& permittedRegs) +{ + return GetDefaultParameterLocations(returnValue, params, permittedRegs); +} + +int64_t CallingConvention::GetStackAdjustmentForLocations(const std::optional& returnValue, + const std::vector& locations, const std::vector>& types) +{ + return GetDefaultStackAdjustmentForLocations(returnValue, locations, types); +} + + +std::map CallingConvention::GetRegisterStackAdjustments( + const std::optional& returnValue, const std::vector& params) +{ + return GetDefaultRegisterStackAdjustments(returnValue, params); +} + + +CallLayout CallingConvention::GetDefaultCallLayout(const ReturnValue& returnValue, + const vector& params, const optional>& permittedRegs) +{ + BNReturnValue ret = returnValue.ToAPIObject(); + BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; + for (size_t i = 0; i < params.size(); i++) + paramArray[i] = params[i].ToAPIObject(); + + BNCallLayout layout; + if (permittedRegs.has_value()) + { + uint32_t* regs = new uint32_t[permittedRegs->size()]; + size_t i = 0; + for (auto reg : *permittedRegs) + regs[i++] = reg; + layout = BNGetDefaultCallLayout(m_object, &ret, paramArray, params.size(), regs, permittedRegs->size()); + delete[] regs; + } + else + { + layout = BNGetDefaultCallLayoutDefaultPermittedArgs(m_object, &ret, paramArray, params.size()); + } + + ReturnValue::FreeAPIObject(&ret); + for (size_t i = 0; i < params.size(); i++) + FunctionParameter::FreeAPIObject(¶mArray[i]); + delete[] paramArray; + + CallLayout result = CallLayout::FromAPIObject(&layout); + BNFreeCallLayout(&layout); + return result; +} + + +ValueLocation CallingConvention::GetDefaultReturnValueLocation(const ReturnValue& returnValue) +{ + BNReturnValue ret = returnValue.ToAPIObject(); + BNValueLocation location = BNGetDefaultReturnValueLocation(m_object, &ret); + ReturnValue::FreeAPIObject(&ret); + + ValueLocation result = ValueLocation::FromAPIObject(&location); + BNFreeValueLocation(&location); + return result; +} + + +vector CallingConvention::GetDefaultParameterLocations(const optional& returnValue, + const vector& params, const optional>& permittedRegs) +{ + BNValueLocation* retOpt = nullptr; + BNValueLocation ret; + if (returnValue.has_value()) + { + ret = returnValue->ToAPIObject(); + retOpt = &ret; + } + BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; + for (size_t i = 0; i < params.size(); i++) + paramArray[i] = params[i].ToAPIObject(); + + size_t locationCount = 0; + BNValueLocation* locations; + if (permittedRegs.has_value()) + { + uint32_t* regs = new uint32_t[permittedRegs->size()]; + size_t i = 0; + for (auto reg : *permittedRegs) + regs[i++] = reg; + locations = BNGetDefaultParameterLocations( + m_object, retOpt, paramArray, params.size(), regs, permittedRegs->size(), &locationCount); + } + else + { + locations = BNGetDefaultParameterLocationsDefaultPermittedArgs( + m_object, retOpt, paramArray, params.size(), &locationCount); + } + + if (retOpt) + ValueLocation::FreeAPIObject(retOpt); + for (size_t i = 0; i < params.size(); i++) + FunctionParameter::FreeAPIObject(¶mArray[i]); + delete[] paramArray; + + vector result; + result.reserve(locationCount); + for (size_t i = 0; i < locationCount; i++) + result.push_back(ValueLocation::FromAPIObject(&locations[i])); + BNFreeValueLocationList(locations, locationCount); + return result; +} + + +int64_t CallingConvention::GetDefaultStackAdjustmentForLocations(const std::optional& returnValue, + const std::vector& locations, const std::vector>& types) +{ + BNValueLocation* retOpt = nullptr; + BNValueLocation ret; + if (returnValue.has_value()) + { + ret = returnValue->ToAPIObject(); + retOpt = &ret; + } + size_t count = locations.size(); + if (types.size() < count) + count = types.size(); + BNValueLocation* locationArray = new BNValueLocation[count]; + for (size_t i = 0; i < count; i++) + locationArray[i] = locations[i].ToAPIObject(); + const BNType** typeArray = new const BNType*[count]; + for (size_t i = 0; i < count; i++) + typeArray[i] = types[i] ? types[i]->GetObject() : nullptr; + + int64_t result = BNGetDefaultStackAdjustmentForLocations(m_object, retOpt, locationArray, typeArray, count); + if (retOpt) + ValueLocation::FreeAPIObject(retOpt); + for (size_t i = 0; i < count; i++) + ValueLocation::FreeAPIObject(&locationArray[i]); + delete[] locationArray; + delete[] typeArray; + return result; +} + + +std::map CallingConvention::GetDefaultRegisterStackAdjustments( + const std::optional& returnValue, const std::vector& params) +{ + BNValueLocation* retOpt = nullptr; + BNValueLocation ret; + if (returnValue.has_value()) + { + ret = returnValue->ToAPIObject(); + retOpt = &ret; + } + BNValueLocation* paramArray = new BNValueLocation[params.size()]; + for (size_t i = 0; i < params.size(); i++) + paramArray[i] = params[i].ToAPIObject(); + + uint32_t* regs = nullptr; + int32_t* adjust = nullptr; + size_t count = BNGetCallingConventionDefaultRegisterStackAdjustments( + m_object, retOpt, paramArray, params.size(), ®s, &adjust); + + if (retOpt) + ValueLocation::FreeAPIObject(retOpt); + for (size_t i = 0; i < params.size(); i++) + ValueLocation::FreeAPIObject(¶mArray[i]); + delete[] paramArray; + + map result; + for (size_t i = 0; i < count; i++) + result[regs[i]] = adjust[i]; + BNFreeCallingConventionRegisterStackAdjustments(regs, adjust); + return result; +} + + CoreCallingConvention::CoreCallingConvention(BNCallingConvention* cc) : CallingConvention(cc) {} @@ -504,3 +1005,198 @@ Variable CoreCallingConvention::GetParameterVariableForIncomingVariable(const Va { return BNGetParameterVariableForIncomingVariable(m_object, &var, func ? func->GetObject() : nullptr); } + + +bool CoreCallingConvention::IsReturnTypeRegisterCompatible(Type* type) +{ + return BNIsReturnTypeRegisterCompatible(m_object, type ? type->GetObject() : nullptr); +} + + +Variable CoreCallingConvention::GetIndirectReturnValueLocation() +{ + return BNGetIndirectReturnValueLocation(m_object); +} + + +std::optional CoreCallingConvention::GetReturnedIndirectReturnValuePointer() +{ + BNVariable var; + if (BNGetReturnedIndirectReturnValuePointer(m_object, &var)) + return var; + return std::nullopt; +} + + +bool CoreCallingConvention::IsArgumentTypeRegisterCompatible(Type* type) +{ + return BNIsArgumentTypeRegisterCompatible(m_object, type ? type->GetObject() : nullptr); +} + + +bool CoreCallingConvention::AreNonRegisterArgumentsIndirect() +{ + return BNAreNonRegisterArgumentsIndirect(m_object); +} + + +bool CoreCallingConvention::AreStackArgumentsNaturallyAligned() +{ + return BNAreStackArgumentsNaturallyAligned(m_object); +} + + +CallLayout CoreCallingConvention::GetCallLayout(const ReturnValue& returnValue, const vector& params, + const optional>& permittedRegs) +{ + BNReturnValue ret = returnValue.ToAPIObject(); + BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; + for (size_t i = 0; i < params.size(); i++) + paramArray[i] = params[i].ToAPIObject(); + + BNCallLayout layout; + if (permittedRegs.has_value()) + { + uint32_t* regs = new uint32_t[permittedRegs->size()]; + size_t i = 0; + for (auto reg : *permittedRegs) + regs[i++] = reg; + layout = BNGetCallLayout(m_object, &ret, paramArray, params.size(), regs, permittedRegs->size()); + delete[] regs; + } + else + { + layout = BNGetCallLayoutDefaultPermittedArgs(m_object, &ret, paramArray, params.size()); + } + + ReturnValue::FreeAPIObject(&ret); + for (size_t i = 0; i < params.size(); i++) + FunctionParameter::FreeAPIObject(¶mArray[i]); + delete[] paramArray; + + CallLayout result = CallLayout::FromAPIObject(&layout); + BNFreeCallLayout(&layout); + return result; +} + + +ValueLocation CoreCallingConvention::GetReturnValueLocation(const ReturnValue& returnValue) +{ + BNReturnValue ret = returnValue.ToAPIObject(); + BNValueLocation location = BNGetReturnValueLocation(m_object, &ret); + ReturnValue::FreeAPIObject(&ret); + + ValueLocation result = ValueLocation::FromAPIObject(&location); + BNFreeValueLocation(&location); + return result; +} + + +vector CoreCallingConvention::GetParameterLocations(const optional& returnValue, + const vector& params, const optional>& permittedRegs) +{ + BNValueLocation* retOpt = nullptr; + BNValueLocation ret; + if (returnValue.has_value()) + { + ret = returnValue->ToAPIObject(); + retOpt = &ret; + } + BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; + for (size_t i = 0; i < params.size(); i++) + paramArray[i] = params[i].ToAPIObject(); + + size_t locationCount = 0; + BNValueLocation* locations; + if (permittedRegs.has_value()) + { + uint32_t* regs = new uint32_t[permittedRegs->size()]; + size_t i = 0; + for (auto reg : *permittedRegs) + regs[i++] = reg; + locations = BNGetParameterLocations( + m_object, retOpt, paramArray, params.size(), regs, permittedRegs->size(), &locationCount); + } + else + { + locations = + BNGetParameterLocationsDefaultPermittedArgs(m_object, retOpt, paramArray, params.size(), &locationCount); + } + + if (retOpt) + ValueLocation::FreeAPIObject(retOpt); + for (size_t i = 0; i < params.size(); i++) + FunctionParameter::FreeAPIObject(¶mArray[i]); + delete[] paramArray; + + vector result; + result.reserve(locationCount); + for (size_t i = 0; i < locationCount; i++) + result.push_back(ValueLocation::FromAPIObject(&locations[i])); + BNFreeValueLocationList(locations, locationCount); + return result; +} + + +int64_t CoreCallingConvention::GetStackAdjustmentForLocations(const std::optional& returnValue, + const std::vector& locations, const std::vector>& types) +{ + BNValueLocation* retOpt = nullptr; + BNValueLocation ret; + if (returnValue.has_value()) + { + ret = returnValue->ToAPIObject(); + retOpt = &ret; + } + size_t count = locations.size(); + if (types.size() < count) + count = types.size(); + BNValueLocation* locationArray = new BNValueLocation[count]; + for (size_t i = 0; i < count; i++) + locationArray[i] = locations[i].ToAPIObject(); + const BNType** typeArray = new const BNType*[count]; + for (size_t i = 0; i < count; i++) + typeArray[i] = types[i] ? types[i]->GetObject() : nullptr; + + int64_t result = BNGetStackAdjustmentForLocations(m_object, retOpt, locationArray, typeArray, count); + if (retOpt) + ValueLocation::FreeAPIObject(retOpt); + for (size_t i = 0; i < count; i++) + ValueLocation::FreeAPIObject(&locationArray[i]); + delete[] locationArray; + delete[] typeArray; + return result; +} + + +std::map CoreCallingConvention::GetRegisterStackAdjustments( + const std::optional& returnValue, const std::vector& params) +{ + BNValueLocation* retOpt = nullptr; + BNValueLocation ret; + if (returnValue.has_value()) + { + ret = returnValue->ToAPIObject(); + retOpt = &ret; + } + BNValueLocation* paramArray = new BNValueLocation[params.size()]; + for (size_t i = 0; i < params.size(); i++) + paramArray[i] = params[i].ToAPIObject(); + + uint32_t* regs = nullptr; + int32_t* adjust = nullptr; + size_t count = + BNGetCallingConventionRegisterStackAdjustments(m_object, retOpt, paramArray, params.size(), ®s, &adjust); + + if (retOpt) + ValueLocation::FreeAPIObject(retOpt); + for (size_t i = 0; i < params.size(); i++) + ValueLocation::FreeAPIObject(¶mArray[i]); + delete[] paramArray; + + map result; + for (size_t i = 0; i < count; i++) + result[regs[i]] = adjust[i]; + BNFreeCallingConventionRegisterStackAdjustments(regs, adjust); + return result; +} diff --git a/function.cpp b/function.cpp index 3f5f5b2ea3..912347fedd 100644 --- a/function.cpp +++ b/function.cpp @@ -63,6 +63,12 @@ bool RegisterValue::operator==(const RegisterValue& a) const case StackFrameOffset: return (state == StackFrameOffset) && (a.value == value); + case ResultPointerValue: + return (state == ResultPointerValue) && (a.value == value); + + case ParameterPointerValue: + return (state == ParameterPointerValue) && (a.value == value) && (a.offset == offset); + case UndeterminedValue: return state == UndeterminedValue; @@ -729,6 +735,30 @@ Confidence> Function::GetReturnType() const } +ReturnValue Function::GetReturnValue() const +{ + BNReturnValue ret = BNGetFunctionReturnValue(m_object); + ReturnValue result = ReturnValue::FromAPIObject(&ret); + BNFreeReturnValue(&ret); + return result; +} + + +bool Function::IsReturnValueDefaultLocation() const +{ + return BNIsFunctionReturnValueDefaultLocation(m_object); +} + + +Confidence Function::GetReturnValueLocation() const +{ + auto location = BNGetFunctionReturnValueLocation(m_object); + Confidence result(ValueLocation::FromAPIObject(&location.location), location.confidence); + BNFreeValueLocation(&location.location); + return result; +} + + Confidence> Function::GetReturnRegisters() const { BNRegisterSetWithConfidence regs = BNGetFunctionReturnRegisters(m_object); @@ -763,6 +793,19 @@ Confidence> Function::GetParameterVariables() const } +Confidence> Function::GetParameterLocations() const +{ + BNValueLocationListWithConfidence locations = BNGetFunctionParameterLocations(m_object); + vector locationList; + locationList.reserve(locations.count); + for (size_t i = 0; i < locations.count; i++) + locationList.push_back(ValueLocation::FromAPIObject(&locations.locations[i])); + Confidence> result(locationList, locations.confidence); + BNFreeParameterLocations(&locations); + return result; +} + + Confidence Function::HasVariableArguments() const { BNBoolWithConfidence bc = BNFunctionHasVariableArguments(m_object); @@ -817,16 +860,27 @@ void Function::SetAutoReturnType(const Confidence>& type) } -void Function::SetAutoReturnRegisters(const Confidence>& returnRegs) +void Function::SetAutoReturnValue(const ReturnValue& rv) { - BNRegisterSetWithConfidence regs; - regs.regs = new uint32_t[returnRegs.GetValue().size()]; - regs.count = returnRegs.GetValue().size(); - for (size_t i = 0; i < regs.count; i++) - regs.regs[i] = returnRegs.GetValue()[i]; - regs.confidence = returnRegs.GetConfidence(); - BNSetAutoFunctionReturnRegisters(m_object, ®s); - delete[] regs.regs; + BNReturnValue ret = rv.ToAPIObject(); + BNSetAutoFunctionReturnValue(m_object, &ret); + ReturnValue::FreeAPIObject(&ret); +} + + +void Function::SetAutoIsReturnValueDefaultLocation(bool defaultLocation) +{ + BNSetAutoIsFunctionReturnValueDefaultLocation(m_object, defaultLocation); +} + + +void Function::SetAutoReturnValueLocation(const Confidence& location) +{ + BNValueLocationWithConfidence loc; + loc.location = location->ToAPIObject(); + loc.confidence = location.GetConfidence(); + BNSetAutoFunctionReturnValueLocation(m_object, &loc); + ValueLocation::FreeAPIObject(&loc.location); } @@ -839,22 +893,21 @@ void Function::SetAutoCallingConvention(const Confidence> } -void Function::SetAutoParameterVariables(const Confidence>& vars) +void Function::SetAutoParameterLocations(const Confidence>& locations) { - BNParameterVariablesWithConfidence varConf; - varConf.vars = new BNVariable[vars->size()]; - varConf.count = vars->size(); + BNValueLocationListWithConfidence varConf; + varConf.locations = new BNValueLocation[locations->size()]; + varConf.count = locations->size(); size_t i = 0; - for (auto it = vars->begin(); it != vars->end(); ++it, ++i) - { - varConf.vars[i].type = it->type; - varConf.vars[i].index = it->index; - varConf.vars[i].storage = it->storage; - } - varConf.confidence = vars.GetConfidence(); + for (auto it = locations->begin(); it != locations->end(); ++it, ++i) + varConf.locations[i] = it->ToAPIObject(); + varConf.confidence = locations.GetConfidence(); - BNSetAutoFunctionParameterVariables(m_object, &varConf); - delete[] varConf.vars; + BNSetAutoFunctionParameterLocations(m_object, &varConf); + + for (i = 0; i < locations->size(); i++) + ValueLocation::FreeAPIObject(&varConf.locations[i]); + delete[] varConf.locations; } @@ -946,16 +999,27 @@ void Function::SetReturnType(const Confidence>& type) } -void Function::SetReturnRegisters(const Confidence>& returnRegs) +void Function::SetReturnValue(const ReturnValue& rv) { - BNRegisterSetWithConfidence regs; - regs.regs = new uint32_t[returnRegs.GetValue().size()]; - regs.count = returnRegs.GetValue().size(); - for (size_t i = 0; i < regs.count; i++) - regs.regs[i] = returnRegs.GetValue()[i]; - regs.confidence = returnRegs.GetConfidence(); - BNSetUserFunctionReturnRegisters(m_object, ®s); - delete[] regs.regs; + BNReturnValue ret = rv.ToAPIObject(); + BNSetUserFunctionReturnValue(m_object, &ret); + ReturnValue::FreeAPIObject(&ret); +} + + +void Function::SetIsReturnValueDefaultLocation(bool defaultLocation) +{ + BNSetUserIsFunctionReturnValueDefaultLocation(m_object, defaultLocation); +} + + +void Function::SetReturnValueLocation(const Confidence& location) +{ + BNValueLocationWithConfidence loc; + loc.location = location->ToAPIObject(); + loc.confidence = location.GetConfidence(); + BNSetUserFunctionReturnValueLocation(m_object, &loc); + ValueLocation::FreeAPIObject(&loc.location); } @@ -968,22 +1032,21 @@ void Function::SetCallingConvention(const Confidence>& co } -void Function::SetParameterVariables(const Confidence>& vars) +void Function::SetParameterLocations(const Confidence>& locations) { - BNParameterVariablesWithConfidence varConf; - varConf.vars = new BNVariable[vars->size()]; - varConf.count = vars->size(); + BNValueLocationListWithConfidence varConf; + varConf.locations = new BNValueLocation[locations->size()]; + varConf.count = locations->size(); size_t i = 0; - for (auto it = vars->begin(); it != vars->end(); ++it, ++i) - { - varConf.vars[i].type = it->type; - varConf.vars[i].index = it->index; - varConf.vars[i].storage = it->storage; - } - varConf.confidence = vars.GetConfidence(); + for (auto it = locations->begin(); it != locations->end(); ++it, ++i) + varConf.locations[i] = it->ToAPIObject(); + varConf.confidence = locations.GetConfidence(); + + BNSetUserFunctionParameterLocations(m_object, &varConf); - BNSetUserFunctionParameterVariables(m_object, &varConf); - delete[] varConf.vars; + for (i = 0; i < locations->size(); i++) + ValueLocation::FreeAPIObject(&varConf.locations[i]); + delete[] varConf.locations; } diff --git a/lang/rust/rusttypes.cpp b/lang/rust/rusttypes.cpp index 70bf31620c..0147c12efc 100644 --- a/lang/rust/rusttypes.cpp +++ b/lang/rust/rusttypes.cpp @@ -147,12 +147,13 @@ vector RustTypePrinter::GetTypeTokensAfterNameInternal( vector paramTokens = GetTypeTokensAfterName(params[i].type.GetValue(), platform, params[i].type.GetCombinedConfidence(baseConfidence), type, escaping); - if (functionHeader) + auto var = params[i].location.GetVariableForParameter(i); + if (functionHeader && var.has_value()) { for (auto& token : paramTokens) { token.context = LocalVariableTokenContext; - token.address = params[i].location.ToIdentifier(); + token.address = var->ToIdentifier(); } } @@ -164,42 +165,46 @@ vector RustTypePrinter::GetTypeTokensAfterNameInternal( else { nameToken = InstructionTextToken(ArgumentNameToken, NameList::EscapeTypeName(params[i].name, escaping), i); - if (functionHeader) + if (functionHeader && var.has_value()) { nameToken.context = LocalVariableTokenContext; - nameToken.address = params[i].location.ToIdentifier(); + nameToken.address = var->ToIdentifier(); } } tokens.push_back(nameToken); tokens.emplace_back(TextToken, ": "); tokens.insert(tokens.end(), paramTokens.begin(), paramTokens.end()); - if (!params[i].defaultLocation && platform) + if (!params[i].defaultLocation && platform && var.has_value()) { - switch (params[i].location.type) + // TODO: Emit a syntax for parameters spanning multiple storage locations + switch (var->type) { case RegisterVariableSourceType: { - string registerName = platform->GetArchitecture()->GetRegisterName((uint32_t)params[i].location.storage); + string registerName = platform->GetArchitecture()->GetRegisterName((uint32_t)var->storage); tokens.emplace_back(TextToken, " @ "); - tokens.emplace_back(RegisterToken, NameList::EscapeTypeName(registerName, escaping), params[i].location.storage); + tokens.emplace_back(RegisterToken, NameList::EscapeTypeName(registerName, escaping), var->storage); break; } case FlagVariableSourceType: { - string flagName = platform->GetArchitecture()->GetFlagName((uint32_t)params[i].location.storage); + string flagName = platform->GetArchitecture()->GetFlagName((uint32_t)var->storage); tokens.emplace_back(TextToken, " @ "); - tokens.emplace_back(AnnotationToken, NameList::EscapeTypeName(flagName, escaping), params[i].location.storage); + tokens.emplace_back(AnnotationToken, NameList::EscapeTypeName(flagName, escaping), var->storage); break; } case StackVariableSourceType: { tokens.emplace_back(TextToken, " @ "); char storageStr[32]; - snprintf(storageStr, sizeof(storageStr), "%" PRIi64, params[i].location.storage); - tokens.emplace_back(IntegerToken, storageStr, params[i].location.storage); + snprintf(storageStr, sizeof(storageStr), "%" PRIi64, var->storage); + tokens.emplace_back(IntegerToken, storageStr, var->storage); break; } + case CompositeReturnValueSourceType: + case CompositeParameterSourceType: + break; } } } diff --git a/mediumlevelilinstruction.cpp b/mediumlevelilinstruction.cpp index 07272602c2..ab37080b6b 100644 --- a/mediumlevelilinstruction.cpp +++ b/mediumlevelilinstruction.cpp @@ -292,6 +292,7 @@ static constexpr std::array s_instructionOperandUsage = { OperandUsage{MLIL_FREE_VAR_SLOT_SSA, {DestSSAVariableMediumLevelOperandUsage, PartialSSAVariableSourceMediumLevelOperandUsage}}, OperandUsage{MLIL_VAR_PHI, {DestSSAVariableMediumLevelOperandUsage, SourceSSAVariablesMediumLevelOperandUsages}}, OperandUsage{MLIL_MEM_PHI, {DestMemoryVersionMediumLevelOperandUsage, SourceMemoryVersionsMediumLevelOperandUsage}}, + OperandUsage{MLIL_BLOCK_TO_EXPAND, {SourceExprsMediumLevelOperandUsage}}, }; VALIDATE_INSTRUCTION_ORDER(s_instructionOperandUsage); @@ -1557,6 +1558,10 @@ void MediumLevelILInstruction::VisitExprs(bn::base::function_ref()) + i.VisitExprs(func); + break; default: break; } @@ -1896,6 +1901,10 @@ ExprId MediumLevelILInstruction::CopyTo(MediumLevelILFunction* dest, return dest->Undefined(loc); case MLIL_UNIMPL: return dest->Unimplemented(loc); + case MLIL_BLOCK_TO_EXPAND: + for (auto i : GetSourceExprs()) + params.push_back(subExprHandler(i)); + return dest->BlockToExpand(params, loc); default: throw MediumLevelILInstructionAccessException(); } @@ -3129,6 +3138,12 @@ ExprId MediumLevelILFunction::FloatCompareUnordered(size_t size, ExprId a, ExprI } +ExprId MediumLevelILFunction::BlockToExpand(const vector& sources, const ILSourceLocation& loc) +{ + return AddExprWithLocation(MLIL_BLOCK_TO_EXPAND, loc, 0, sources.size(), AddOperandList(sources)); +} + + fmt::format_context::iterator fmt::formatter::format(const MediumLevelILInstruction& obj, format_context& ctx) const { if (!obj.function) diff --git a/mediumlevelilinstruction.h b/mediumlevelilinstruction.h index 2b4950104a..e74b91b8d2 100644 --- a/mediumlevelilinstruction.h +++ b/mediumlevelilinstruction.h @@ -1565,6 +1565,13 @@ namespace BinaryNinja int64_t GetOffset() const { return GetRawOperandAsInteger(1); } }; + template <> + struct MediumLevelILInstructionAccessor : public MediumLevelILInstructionBase + { + MediumLevelILInstructionList GetSourceExprs() const { return GetRawOperandAsExprList(0); } + void SetSourceExprs(const _STD_VECTOR& exprs) { UpdateRawOperandAsExprList(0, exprs); } + }; + template <> struct MediumLevelILInstructionAccessor : public MediumLevelILInstructionBase {}; diff --git a/plugins/pdb-ng/src/symbol_parser.rs b/plugins/pdb-ng/src/symbol_parser.rs index 4e14eb8c5f..28b79e54b6 100644 --- a/plugins/pdb-ng/src/symbol_parser.rs +++ b/plugins/pdb-ng/src/symbol_parser.rs @@ -961,7 +961,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { MIN_CONFIDENCE, )), p.name.clone(), - p.storage.first().map(|loc| loc.location), + p.storage.first().map(|loc| loc.location.into()), ); // Ignore thisptr because it's not technically part of the raw type signature if p.name != "this" { @@ -976,7 +976,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { MIN_CONFIDENCE, )), p.name.clone(), - p.storage.first().map(|loc| loc.location), + p.storage.first().map(|loc| loc.location.into()), ); // Ignore thisptr because it's not technically part of the raw type signature if p.name != "this" { @@ -1035,7 +1035,6 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } } - // Now apply the default location for the params from the cc let cc = fancy_type .contents .calling_convention() @@ -1049,12 +1048,6 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { }); self.log(|| format!("Default calling convention: {:?}", self.default_cc)); self.log(|| format!("Result calling convention: {:?}", cc)); - - let locations = cc.contents.variables_for_parameters(&fancy_params, None); - for (p, new_location) in fancy_params.iter_mut().zip(locations.into_iter()) { - p.location = Some(new_location); - } - self.log(|| format!("Final params: {:#x?}", fancy_params)); // Use the new locals we've parsed to make the Real Definitely True function type diff --git a/plugins/warp/src/convert.rs b/plugins/warp/src/convert.rs index bacf0024be..85a0631441 100644 --- a/plugins/warp/src/convert.rs +++ b/plugins/warp/src/convert.rs @@ -27,6 +27,8 @@ pub fn bn_var_to_location(bn_variable: BNVariable) -> Option { Some(Location::Register(reg_loc)) } VariableSourceType::FlagVariableSourceType => None, + VariableSourceType::CompositeReturnValueSourceType => None, + VariableSourceType::CompositeParameterSourceType => None, } } diff --git a/python/callingconvention.py b/python/callingconvention.py index 2023e37f6f..85f8adc8ce 100644 --- a/python/callingconvention.py +++ b/python/callingconvention.py @@ -20,7 +20,8 @@ import traceback import ctypes -from typing import Optional, Union +from typing import Optional, Union, List, Dict +from dataclasses import dataclass # Binary Ninja components from . import _binaryninjacore as core @@ -28,12 +29,37 @@ from . import variable from . import function from . import architecture +from . import types FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction", "binaryninja.mediumlevelil.MediumLevelILFunction", "binaryninja.highlevelil.HighLevelILFunction"] +@dataclass +class CallLayout: + parameters: List['types.ValueLocation'] + return_value: Optional['types.ValueLocation'] + stack_adjustment: int + reg_stack_adjustments: Dict['architecture.RegisterIndex', int] + + @staticmethod + def _from_core_struct(struct: core.BNCallLayout, func: Optional['function.Function'] = None) -> 'CallLayout': + params = [] + for i in range(struct.parameterCount): + params.append(types.ValueLocation._from_core_struct(struct.parameters[i], func)) + if struct.returnValueValid: + return_value = types.ValueLocation._from_core_struct(struct.returnValue, func) + else: + return_value = None + stack_adjust = struct.stackAdjustment + reg_stack_adjust = dict() + for i in range(struct.registerStackAdjustmentCount): + reg = architecture.RegisterIndex(struct.registerStackAdjustmentRegisters[i]) + reg_stack_adjust[reg] = struct.registerStackAdjustmentAmounts[i] + return CallLayout(params, return_value, stack_adjust, reg_stack_adjust) + + class CallingConvention: name = None caller_saved_regs = [] @@ -516,3 +542,25 @@ def arch(self) -> 'architecture.Architecture': @arch.setter def arch(self, value: 'architecture.Architecture') -> None: self._arch = value + + def get_call_layout( + self, return_value: Optional['types.ReturnValueOrType'], params: 'types.ParamsType', + func: Optional['function.Function'] = None, permitted_regs: Optional[List['architecture.RegisterIndex']] = None + ) -> 'CallLayout': + if return_value is None: + ret = types.ReturnValue(types.Type.void())._to_core_struct() + elif isinstance(return_value, types.ReturnValue): + ret = return_value._to_core_struct() + else: + ret = types.ReturnValue(return_value)._to_core_struct() + param_structs = types.FunctionBuilder._to_core_struct(params) + if permitted_regs is None: + layout = core.BNGetCallLayoutDefaultPermittedArgs(self.handle, ret, param_structs, len(params)) + else: + regs = (ctypes.c_uint * len(permitted_regs))() + for i in range(len(permitted_regs)): + regs[i] = int(permitted_regs[i]) + layout = core.BNGetCallLayout(self.handle, ret, param_structs, len(params), regs, len(permitted_regs)) + result = CallLayout._from_core_struct(layout, func) + core.BNFreeCallLayout(layout) + return result diff --git a/python/function.py b/python/function.py index 29527f4dbc..c8b006f210 100644 --- a/python/function.py +++ b/python/function.py @@ -29,7 +29,7 @@ from .enums import ( AnalysisSkipReason, FunctionGraphType, SymbolType, SymbolBinding, InstructionTextTokenType, HighlightStandardColor, HighlightColorStyle, DisassemblyOption, IntegerDisplayType, FunctionAnalysisSkipOverride, FunctionUpdateType, - BuiltinType, ExprFolding, EarlyReturn, SwitchRecovery + BuiltinType, ExprFolding, EarlyReturn, SwitchRecovery, VariableSourceType ) from . import associateddatastore # Required in the main scope due to being an argument for _FunctionAssociatedDataStore @@ -1363,9 +1363,43 @@ def return_type(self, value: Optional[StringOrType]) -> None: # type: ignore type_conf.confidence = value.confidence core.BNSetUserFunctionReturnType(self.handle, type_conf) + @property + def return_value(self) -> 'types.ReturnValue': + """Return type and location""" + ret = core.BNGetFunctionReturnValue(self.handle) + result = types.ReturnValue._from_core_struct(ret) + core.BNFreeReturnValue(ret) + return result + + @return_value.setter + def return_value(self, value: 'types.ReturnValue') -> None: # type: ignore + ret = value._to_core_struct() + core.BNSetUserFunctionReturnValue(self.handle, ret) + + @property + def return_value_location(self) -> Optional['types.ValueLocationWithConfidence']: + location = core.BNGetFunctionReturnValueLocation(self.handle) + result = types.ValueLocation._from_core_struct(location.location).with_confidence(location.confidence) + core.BNFreeValueLocation(location.location) + return result + + @return_value_location.setter + def return_value_location(self, value: 'types.OptionalLocation'): + struct = core.BNValueLocationWithConfidence() + location = types.ValueLocationWithConfidence.from_optional_location(value) + if location is None: + struct.location.count = 0 + struct.confidence = 0 + else: + struct.location = location.location._to_core_struct() + struct.confidence = location.confidence + core.BNSetUserIsFunctionReturnValueDefaultLocation(self.handle, value is None) + if value is not None: + core.BNSetUserFunctionReturnValueLocation(self.handle, struct) + @property def return_regs(self) -> 'types.RegisterSet': - """Registers that are used for the return value""" + """Registers that are used for the return value (read-only)""" result = core.BNGetFunctionReturnRegisters(self.handle) assert result is not None, "core.BNGetFunctionReturnRegisters returned None" try: @@ -1376,19 +1410,6 @@ def return_regs(self) -> 'types.RegisterSet': finally: core.BNFreeRegisterSet(result) - @return_regs.setter - def return_regs(self, value: Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: # type: ignore - regs = core.BNRegisterSetWithConfidence() - regs.regs = (ctypes.c_uint * len(value))() - regs.count = len(value) - for i in range(0, len(value)): - regs.regs[i] = self.arch.get_reg_index(value[i]) - if isinstance(value, types.RegisterSet): - regs.confidence = value.confidence - else: - regs.confidence = core.max_confidence - core.BNSetUserFunctionReturnRegisters(self.handle, regs) - @property def calling_convention(self) -> Optional['callingconvention.CallingConvention']: """Calling convention used by the function""" @@ -1427,20 +1448,60 @@ def parameter_vars( var_list = [] else: var_list = list(value) - var_conf = core.BNParameterVariablesWithConfidence() - var_conf.vars = (core.BNVariable * len(var_list))() - var_conf.count = len(var_list) - for i in range(0, len(var_list)): - var_conf.vars[i].type = var_list[i].source_type - var_conf.vars[i].index = var_list[i].index - var_conf.vars[i].storage = var_list[i].storage + locations = [] + for i in range(len(var_list)): + if (var_list[i].source_type != VariableSourceType.RegisterVariableSourceType and + var_list[i].source_type != VariableSourceType.StackVariableSourceType and + var_list[i].source_type != VariableSourceType.FlagVariableSourceType): + raise ValueError(f"Parameter {i} is a composite variable. Use parameter_locations instead.") + locations.append(types.ValueLocation([types.ValueLocationComponent(var_list[i])])) + if value is None: + conf = 0 + elif isinstance(value, variable.ParameterVariables): + conf = value.confidence + else: + conf = core.max_confidence + self.parameter_locations = variable.ParameterLocations(locations, conf, self) + + @property + def parameter_locations(self) -> 'variable.ParameterLocations': + """List of locations for the incoming function parameters""" + result = core.BNGetFunctionParameterLocations(self.handle) + location_list = [] + for i in range(0, result.count): + location_list.append(types.ValueLocation._from_core_struct(result.locations[i], self)) + confidence = result.confidence + core.BNFreeParameterLocations(result) + return variable.ParameterLocations(location_list, confidence, self) + + @parameter_locations.setter + def parameter_locations( + self, value: Optional[Union[List[Union['types.ValueLocation', 'variable.CoreVariable']], + 'variable.CoreVariable', 'variable.ParameterLocations']] + ) -> None: # type: ignore + if value is None: + location_list = [] + elif isinstance(value, variable.CoreVariable): + location_list = [value] + elif isinstance(value, variable.ParameterLocations): + location_list = value.locations + else: + location_list = list(value) + location_conf = core.BNValueLocationListWithConfidence() + location_conf.locations = (core.BNValueLocation * len(location_list))() + location_conf.count = len(location_list) + for i in range(0, len(location_list)): + if isinstance(location_list[i], types.ValueLocation): + location_conf.locations[i] = location_list[i]._to_core_struct() + else: + location_conf.locations[i] = types.ValueLocation([types.ValueLocationComponent(location_list[i])])._to_core_struct() if value is None: - var_conf.confidence = 0 - elif isinstance(value, types.RegisterSet): - var_conf.confidence = value.confidence + location_conf.confidence = 0 + elif isinstance(value, variable.ParameterLocations): + location_conf.confidence = value.confidence else: - var_conf.confidence = core.max_confidence - core.BNSetUserFunctionParameterVariables(self.handle, var_conf) + location_conf.confidence = core.max_confidence + core.BNSetUserFunctionParameterLocations(self.handle, location_conf) @property def has_variable_arguments(self) -> 'types.BoolWithConfidence': @@ -2466,18 +2527,18 @@ def set_auto_return_type(self, value: StringOrType) -> None: type_conf.confidence = value.confidence core.BNSetAutoFunctionReturnType(self.handle, type_conf) - def set_auto_return_regs(self, value: Union['types.RegisterSet', List['architecture.RegisterType']]) -> None: - regs = core.BNRegisterSetWithConfidence() - regs.regs = (ctypes.c_uint * len(value))() - regs.count = len(value) - - for i in range(0, len(value)): - regs.regs[i] = self.arch.get_reg_index(value[i]) - if isinstance(value, types.RegisterSet): - regs.confidence = value.confidence + def set_auto_return_value_location(self, value: 'types.OptionalLocation'): + struct = core.BNValueLocationWithConfidence() + location = types.ValueLocationWithConfidence.from_optional_location(value) + if location is None: + struct.location.count = 0 + struct.confidence = 0 else: - regs.confidence = core.max_confidence - core.BNSetAutoFunctionReturnRegisters(self.handle, regs) + struct.location = location.location._to_core_struct() + struct.confidence = location.confidence + core.BNSetAutoIsFunctionReturnValueDefaultLocation(self.handle, value is None) + if value is not None: + core.BNSetAutoFunctionReturnValueLocation(self.handle, struct) def set_auto_calling_convention(self, value: 'callingconvention.CallingConvention') -> None: conv_conf = core.BNCallingConventionWithConfidence() @@ -2490,30 +2551,58 @@ def set_auto_calling_convention(self, value: 'callingconvention.CallingConventio core.BNSetAutoFunctionCallingConvention(self.handle, conv_conf) def set_auto_parameter_vars( - self, value: Optional[Union[List['variable.Variable'], 'variable.Variable', 'variable.ParameterVariables']] + self, value: Optional[Union[List['variable.CoreVariable'], 'variable.CoreVariable', 'variable.ParameterVariables']] ) -> None: if value is None: var_list = [] - elif isinstance(value, variable.Variable): + elif isinstance(value, variable.CoreVariable): var_list = [value] elif isinstance(value, variable.ParameterVariables): var_list = value.vars else: var_list = list(value) - var_conf = core.BNParameterVariablesWithConfidence() - var_conf.vars = (core.BNVariable * len(var_list))() - var_conf.count = len(var_list) - for i in range(0, len(var_list)): - var_conf.vars[i].type = var_list[i].source_type - var_conf.vars[i].index = var_list[i].index - var_conf.vars[i].storage = var_list[i].storage + locations = [] + for i in range(len(var_list)): + if (var_list[i].source_type != VariableSourceType.RegisterVariableSourceType and + var_list[i].source_type != VariableSourceType.StackVariableSourceType and + var_list[i].source_type != VariableSourceType.FlagVariableSourceType): + raise ValueError(f"Parameter {i} is a composite variable. Use set_auto_parameter_locations instead.") + locations.append(types.ValueLocation([types.ValueLocationComponent(var_list[i])])) + if value is None: + conf = 0 + elif isinstance(value, variable.ParameterVariables): + conf = value.confidence + else: + conf = core.max_confidence + self.set_auto_parameter_locations(variable.ParameterLocations(locations, conf, self)) + + def set_auto_parameter_locations( + self, value: Optional[Union[List[Union['variable.CoreVariable', 'types.ValueLocation']], + 'variable.CoreVariable', 'types.ValueLocation', 'variable.ParameterLocations']] + ) -> None: + if value is None: + location_list = [] + elif isinstance(value, variable.CoreVariable): + location_list = [value] + elif isinstance(value, variable.ParameterLocations): + location_list = value.locations + else: + location_list = list(value) + location_conf = core.BNValueLocationListWithConfidence() + location_conf.locations = (core.BNValueLocation * len(location_list))() + location_conf.count = len(location_list) + for i in range(0, len(location_list)): + if isinstance(location_list[i], types.ValueLocation): + location_conf.locations[i] = location_list[i]._to_core_struct() + else: + location_conf.locations[i] = types.ValueLocation([types.ValueLocationComponent(location_list[i])])._to_core_struct() if value is None: - var_conf.confidence = 0 + location_conf.confidence = 0 elif isinstance(value, variable.ParameterVariables): - var_conf.confidence = value.confidence + location_conf.confidence = value.confidence else: - var_conf.confidence = core.max_confidence - core.BNSetAutoFunctionParameterVariables(self.handle, var_conf) + location_conf.confidence = core.max_confidence + core.BNSetAutoFunctionParameterLocations(self.handle, location_conf) def set_auto_has_variable_arguments(self, value: Union[bool, 'types.BoolWithConfidence']) -> None: bc = core.BNBoolWithConfidence() diff --git a/python/mediumlevelil.py b/python/mediumlevelil.py index db043c7c5f..fd95580aa7 100644 --- a/python/mediumlevelil.py +++ b/python/mediumlevelil.py @@ -278,7 +278,9 @@ class MediumLevelILInstruction(BaseILInstruction): ("params", "expr_list") ], MediumLevelILOperation.MLIL_RET: [ ("src", "expr_list") - ], MediumLevelILOperation.MLIL_NORET: [], MediumLevelILOperation.MLIL_IF: [ + ], MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND: [ + ("src", "expr_list") + ], MediumLevelILOperation.MLIL_NORET: [], MediumLevelILOperation.MLIL_IF: [ ("condition", "expr"), ("true", "int"), ("false", "int") ], MediumLevelILOperation.MLIL_GOTO: [("dest", "int")], MediumLevelILOperation.MLIL_CMP_E: [ ("left", "expr"), ("right", "expr") @@ -3096,6 +3098,15 @@ def dest(self) -> SSAVariable: def src(self) -> SSAVariable: return self._get_var_ssa(2, 3) +@dataclass(frozen=True, repr=False, eq=False) +class MediumLevelILBlockToExpand(MediumLevelILInstruction): + @property + def exprs(self) -> List[MediumLevelILInstruction]: + return self._get_expr_list(0, 1) + + @property + def detailed_operands(self) -> List[Tuple[str, MediumLevelILOperandType, str]]: + return [("exprs", self.exprs, "List[MediumLevelILInstruction]")] ILInstruction = { @@ -3264,6 +3275,7 @@ def src(self) -> SSAVariable: MediumLevelILOperation.MLIL_ASSERT_SSA: MediumLevelILAssertSsa, MediumLevelILOperation.MLIL_FORCE_VER: MediumLevelILForceVer, MediumLevelILOperation.MLIL_FORCE_VER_SSA: MediumLevelILForceVerSsa, + MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND: MediumLevelILBlockToExpand, # [("exprs", "expr_list")], } @@ -3953,6 +3965,10 @@ def do_copy( if expr.operation == MediumLevelILOperation.MLIL_UNIMPL: expr: MediumLevelILUnimpl return dest.unimplemented(loc) + if expr.operation == MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND: + expr: MediumLevelILBlockToExpand + params = [sub_expr_handler(src) for src in expr.src] + return dest.block_to_expand(params, loc) raise NotImplementedError(f"unknown expr operation {expr.operation} in copy_expr_to") new_index = do_copy(expr, dest, sub_expr_handler) @@ -5767,6 +5783,18 @@ def float_compare_unordered( """ return self.expr(MediumLevelILOperation.MLIL_FCMP_UO, a, b, size=size, source_location=loc) + def block_to_expand(self, exprs: List[ExpressionIndex], loc: Optional['ILSourceLocation'] = None) -> ExpressionIndex: + """ + ``block_to_expand`` returns an expression to expand into multiple expressions. This expression must + be expanded by a future workflow step and is used temporarily to insert instructions. + + :param List[ExpressionIndex] exprs: list of expressions + :param ILSourceLocation loc: location of returned expression + :return: The expression ``{ exprs... }`` + :rtype: ExpressionIndex + """ + return self.expr(MediumLevelILOperation.MLIL_BLOCK_TO_EXPAND, len(exprs), self.add_operand_list(exprs), size=0, source_location=loc) + def goto( self, label: MediumLevelILLabel, loc: Optional['ILSourceLocation'] = None ) -> ExpressionIndex: diff --git a/python/types.py b/python/types.py index 44583c4de1..bf3614c990 100644 --- a/python/types.py +++ b/python/types.py @@ -38,6 +38,7 @@ from . import variable from . import architecture from . import binaryview +from . import function from . import platform as _platform from . import typecontainer from . import typelibrary @@ -50,11 +51,13 @@ MembersType = Union[List['StructureMember'], List['Type'], List[Tuple['Type', str]]] EnumMembersType = Union[List[Tuple[str, int]], List[str], List['EnumerationMember']] SomeType = Union['TypeBuilder', 'Type'] +ReturnValueOrType = Union['TypeBuilder', 'Type', 'ReturnValue'] TypeContainerType = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary'] NameSpaceType = Optional[Union[str, List[str], 'NameSpace']] TypeParserResult = typeparser.TypeParserResult BasicTypeParserResult = typeparser.BasicTypeParserResult ResolveMemberCallback = Callable[['NamedTypeReferenceType', 'StructureType', int, int, int, 'StructureMember'], None] +OptionalLocation = Optional[Union['ValueLocation', 'ValueLocationWithConfidence', 'variable.CoreVariable']] # The following are needed to prevent the type checker from getting # confused as we have member functions in `Type` named the same thing _int = int @@ -428,15 +431,137 @@ def __init__( super(Symbol, self).__init__(_handle) +@dataclass +class ValueLocationComponent: + var: 'variable.CoreVariable' + offset: int = 0 + size: Optional[int] = None + indirect: bool = False + returned_pointer: Optional['variable.CoreVariable'] = None + + @staticmethod + def _from_core_struct(struct: core.BNValueLocationComponent, func: Optional['function.Function'] = None): + if func is None: + var = variable.CoreVariable.from_BNVariable(struct.variable) + else: + var = variable.Variable.from_BNVariable(func, struct.variable) + offset = struct.offset + size = None + if struct.sizeValid: + size = struct.size + indirect = struct.indirect + returned_pointer = None + if struct.returnedPointerValid: + if func is None: + returned_pointer = variable.CoreVariable.from_BNVariable(struct.returnedPointer) + else: + returned_pointer = variable.Variable.from_BNVariable(func, struct.returnedPointer) + return ValueLocationComponent(var, offset, size, indirect, returned_pointer) + + def _to_core_struct(self) -> core.BNValueLocationComponent: + struct = core.BNValueLocationComponent() + struct.variable = self.var.to_BNVariable() + struct.offset = self.offset + struct.sizeValid = self.size is not None + if self.size is not None: + struct.size = self.size + struct.indirect = self.indirect + struct.returnedPointerValid = self.returned_pointer is not None + if self.returned_pointer is not None: + struct.returnedPointer = self.returned_pointer.to_BNVariable() + return struct + + +@dataclass +class ValueLocation: + components: List['ValueLocationComponent'] + + @staticmethod + def _from_core_struct(struct: core.BNValueLocation, func: Optional['function.Function'] = None): + components = [] + for i in range(struct.count): + components.append(ValueLocationComponent._from_core_struct(struct.components[i], func)) + return ValueLocation(components) + + def _to_core_struct(self) -> core.BNValueLocation: + struct = core.BNValueLocation() + struct.count = len(self.components) + components = (core.BNValueLocationComponent * len(self.components))() + for i in range(len(self.components)): + components[i] = self.components[i]._to_core_struct() + struct.components = components + return struct + + def with_confidence(self, confidence: int) -> 'ValueLocationWithConfidence': + return ValueLocationWithConfidence(self, confidence) + + +@dataclass +class ValueLocationWithConfidence: + location: 'ValueLocation' + confidence: int = core.max_confidence + + @staticmethod + def from_optional_location(location: OptionalLocation) -> Optional['ValueLocationWithConfidence']: + if isinstance(location, ValueLocation): + return location.with_confidence(core.max_confidence) + elif isinstance(location, ValueLocationWithConfidence): + return location + elif location is not None: + return ValueLocation([ValueLocationComponent(location)]).with_confidence(core.max_confidence) + return None + + +@dataclass +class ReturnValue: + type: SomeType + location: Optional['ValueLocationWithConfidence'] + + def __init__(self, ty: SomeType, location: OptionalLocation = None): + self.type = ty.immutable_copy() + self.location = ValueLocationWithConfidence.from_optional_location(location) + + @staticmethod + def _from_core_struct(struct: core.BNReturnValue): + ty = Type.from_core_struct(struct.type).with_confidence(struct.typeConfidence) + if struct.defaultLocation: + location = ValueLocation._from_core_struct(struct.location).with_confidence(struct.locationConfidence) + else: + location = None + return ReturnValue(ty, location) + + def _to_core_struct(self) -> core.BNReturnValue: + struct = core.BNReturnValue() + struct.type = self.type.handle + struct.typeConfidence = self.type.confidence + struct.defaultLocation = self.location is None + if self.location is None: + struct.location.count = 0 + struct.locationConfidence = 0 + else: + struct.location = self.location.location._to_core_struct() + struct.locationConfidence = self.location.confidence + return struct + + @dataclass class FunctionParameter: type: SomeType name: str = "" - location: Optional['variable.VariableNameAndType'] = None + location: Optional['ValueLocation'] = None + + def __init__(self, type: SomeType, name: str = "", location: OptionalLocation = None): + self.type = type + self.name = name + location = ValueLocationWithConfidence.from_optional_location(location) + if location is not None: + self.location = location.location + else: + self.location = None def __repr__(self): - if (self.location is not None) and (self.location.name != self.name): - return f"{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()} @ {self.location.name}" + if self.location is not None: + return f"{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()} @ {self.location}" return f"{self.type.immutable_copy().get_string_before_name()} {self.name}{self.type.immutable_copy().get_string_after_name()}" def immutable_copy(self) -> 'FunctionParameter': @@ -760,7 +885,7 @@ def array(type: 'Type', count: _int) -> 'ArrayBuilder': @staticmethod def function( - ret: Optional['Type'] = None, params: Optional[ParamsType] = None, + ret: Optional[ReturnValueOrType] = None, params: Optional[ParamsType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, variable_arguments: Optional[BoolWithConfidenceType] = None, stack_adjust: Optional[OffsetWithConfidenceType] = None @@ -1151,20 +1276,21 @@ def children(self) -> List[TypeBuilder]: class FunctionBuilder(TypeBuilder): @classmethod def create( - cls, return_type: Optional[SomeType] = None, + cls, return_type: Optional[ReturnValueOrType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, params: Optional[ParamsType] = None, var_args: Optional[BoolWithConfidenceType] = None, stack_adjust: Optional[OffsetWithConfidenceType] = None, platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence, can_return: Optional[BoolWithConfidence] = None, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None, - return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None, name_type: 'NameType' = NameType.NoNameType, pure: Optional[BoolWithConfidence] = None ) -> 'FunctionBuilder': param_buf = FunctionBuilder._to_core_struct(params) if return_type is None: - ret_conf = Type.void()._to_core_struct() - else: + ret_conf = ReturnValue(Type.void())._to_core_struct() + elif isinstance(return_type, ReturnValue): ret_conf = return_type._to_core_struct() + else: + ret_conf = ReturnValue(return_type)._to_core_struct() conv_conf = core.BNCallingConventionWithConfidence() if calling_convention is None: @@ -1184,18 +1310,6 @@ def create( reg_stack_adjust_values[i].value = adjust.value reg_stack_adjust_values[i].confidence = adjust.confidence - return_regs_set = core.BNRegisterSetWithConfidence() - if return_regs is None or platform is None: - return_regs_set.count = 0 - return_regs_set.confidence = 0 - else: - return_regs_set.count = len(return_regs) - return_regs_set.confidence = 255 - return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))() - - for i, reg in enumerate(return_regs): - return_regs_set[i] = platform.arch.get_reg_index(reg) - if var_args is None: vararg_conf = BoolWithConfidence.get_core_struct(False, 0) else: @@ -1220,7 +1334,7 @@ def create( handle = core.BNCreateFunctionTypeBuilder( ret_conf, conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf, reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust), - return_regs_set, name_type, pure_conf + name_type, pure_conf ) assert handle is not None, "BNCreateFunctionTypeBuilder returned None" return cls(handle, platform, confidence) @@ -1237,6 +1351,27 @@ def return_value(self) -> TypeBuilder: def return_value(self, value: SomeType) -> None: self.child = value + @property + def return_value_location(self) -> Optional[ValueLocationWithConfidence]: + location = core.BNGetTypeReturnValueLocation(self._handle) + result = ValueLocation._from_core_struct(location.location).with_confidence(location.confidence) + core.BNFreeValueLocation(location) + return result + + @return_value_location.setter + def return_value_location(self, value: OptionalLocation): + struct = core.BNValueLocationWithConfidence() + location = ValueLocationWithConfidence.from_optional_location(value) + if location is None: + struct.location.count = 0 + struct.confidence = 0 + else: + struct.location = location.location._to_core_struct() + struct.confidence = location.confidence + core.BNTypeBuilderIsReturnValueDefaultLocation(self._handle, value is None) + if value is not None: + core.BNTypeBuilderSetReturnValueLocation(self._handle, struct) + def append(self, type: Union[SomeType, FunctionParameter], name: str = ""): if isinstance(type, FunctionParameter): self.parameters = [*self.parameters, type] @@ -1294,15 +1429,7 @@ def parameters(self) -> List[FunctionParameter]: if params[i].defaultLocation: param_location = None else: - name = params[i].name - if (params[i].location.type - == VariableSourceType.RegisterVariableSourceType) and (self.platform is not None): - name = self.platform.arch.get_reg_name(params[i].location.storage) - elif params[i].location.type == VariableSourceType.StackVariableSourceType: - name = "arg_%x" % params[i].location.storage - param_location = variable.VariableNameAndType( - params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type - ) + param_location = ValueLocation._from_core_struct(params[i].location) result.append(FunctionParameter(param_type, params[i].name, param_location)) core.BNFreeTypeParameterList(params, count.value) return result @@ -1324,6 +1451,7 @@ def _to_core_struct(params: Optional[ParamsType] = None): core_param.type = param.handle core_param.typeConfidence = param.confidence core_param.defaultLocation = True + core_param.location.count = 0 elif isinstance(param, FunctionParameter): assert param.type is not None, "Attempting to construct function parameter without properly constructed type" core_param.name = param.name @@ -1331,11 +1459,15 @@ def _to_core_struct(params: Optional[ParamsType] = None): core_param.typeConfidence = param.type.confidence if param.location is None: core_param.defaultLocation = True + core_param.location.count = 0 else: core_param.defaultLocation = False - core_param.location.type = param.location.source_type - core_param.location.index = param.location.index - core_param.location.storage = param.location.storage + if isinstance(param.location, ValueLocation): + core_param.location = param.location._to_core_struct() + elif isinstance(param.location, variable.CoreVariable): + core_param.location = ValueLocation([ValueLocationComponent(param.location)])._to_core_struct() + else: + raise ValueError(f"Conversion from unsupported parameter location type {type(param.location)}") elif isinstance(param, tuple): name, _type = param if not isinstance(name, str) or not isinstance(_type, (Type, TypeBuilder)): @@ -1344,6 +1476,7 @@ def _to_core_struct(params: Optional[ParamsType] = None): core_param.type = _type.handle core_param.typeConfidence = _type.confidence core_param.defaultLocation = True + core_param.location.count = 0 else: raise ValueError(f"Conversion from unsupported function parameter type {type(param)}") return param_buf @@ -2431,7 +2564,7 @@ def array(type: 'Type', count: _int) -> 'ArrayType': @staticmethod def function( - ret: Optional['Type'] = None, params: Optional[ParamsType] = None, + ret: Optional[Union['Type', 'ReturnValue']] = None, params: Optional[ParamsType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, variable_arguments: BoolWithConfidenceType = False, stack_adjust: OffsetWithConfidence = OffsetWithConfidence(0) @@ -3102,18 +3235,19 @@ def children(self) -> List[Type]: class FunctionType(Type): @classmethod def create( - cls, ret: Optional[Type] = None, params: Optional[ParamsType] = None, + cls, ret: Optional[Union[Type, ReturnValue]] = None, params: Optional[ParamsType] = None, calling_convention: Optional['callingconvention.CallingConvention'] = None, variable_arguments: BoolWithConfidenceType = BoolWithConfidence(False), stack_adjust: OffsetWithConfidence = OffsetWithConfidence(0), platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence, can_return: Union[BoolWithConfidence, bool] = True, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None, - return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None, name_type: 'NameType' = NameType.NoNameType, pure: Union[BoolWithConfidence, bool] = False ) -> 'FunctionType': if ret is None: - ret = VoidType.create() + ret = ReturnValue(VoidType.create()) + elif not isinstance(ret, ReturnValue): + ret = ReturnValue(ret) if params is None: params = [] param_buf = FunctionBuilder._to_core_struct(params) @@ -3147,18 +3281,6 @@ def create( reg_stack_adjust_values[i].value = adjust.value reg_stack_adjust_values[i].confidence = adjust.confidence - return_regs_set = core.BNRegisterSetWithConfidence() - if return_regs is None or platform is None: - return_regs_set.count = 0 - return_regs_set.confidence = 0 - else: - return_regs_set.count = len(return_regs) - return_regs_set.confidence = 255 - return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))() - - for i, reg in enumerate(return_regs): - return_regs_set[i] = platform.arch.get_reg_index(reg) - _can_return = BoolWithConfidence.get_core_struct(can_return) _pure = BoolWithConfidence.get_core_struct(pure) if params is None: @@ -3166,7 +3288,7 @@ def create( func_type = core.BNCreateFunctionType( ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _can_return, _stack_adjust, reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust), - return_regs_set, name_type, _pure + name_type, _pure ) assert func_type is not None, f"core.BNCreateFunctionType returned None {ret_conf} {conv_conf} {param_buf} {_variable_arguments} {_stack_adjust}" @@ -3186,6 +3308,16 @@ def return_value(self) -> Type: return Type.void() return Type.create(result.type, platform=self._platform, confidence=result.confidence) + @property + def return_value_location(self) -> Optional[ValueLocationWithConfidence]: + """Return value location (read-only)""" + if core.BNIsTypeReturnValueDefaultLocation(self._handle): + return None + location = core.BNGetTypeReturnValueLocation(self._handle) + result = ValueLocation._from_core_struct(location.location).with_confidence(location.confidence) + core.BNFreeValueLocation(location.location) + return result + @property def calling_convention(self) -> Optional[callingconvention.CallingConvention]: """Calling convention (read-only)""" @@ -3208,15 +3340,7 @@ def parameters(self) -> List[FunctionParameter]: if params[i].defaultLocation: param_location = None else: - name = params[i].name - if (params[i].location.type - == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): - name = self._platform.arch.get_reg_name(params[i].location.storage) - elif params[i].location.type == VariableSourceType.StackVariableSourceType: - name = "arg_%x" % params[i].location.storage - param_location = variable.VariableNameAndType( - params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type - ) + param_location = ValueLocation._from_core_struct(params[i].location) result.append(FunctionParameter(param_type, params[i].name, param_location)) core.BNFreeTypeParameterList(params, count.value) return result @@ -3232,15 +3356,7 @@ def parameters_with_all_locations(self) -> List[FunctionParameter]: param_type = Type.create( core.BNNewTypeReference(params[i].type), platform=self._platform, confidence=params[i].typeConfidence ) - name = params[i].name - if (params[i].location.type - == VariableSourceType.RegisterVariableSourceType) and (self._platform is not None): - name = self._platform.arch.get_reg_name(params[i].location.storage) - elif params[i].location.type == VariableSourceType.StackVariableSourceType: - name = "arg_%x" % params[i].location.storage - param_location = variable.VariableNameAndType( - params[i].location.type, params[i].location.index, params[i].location.storage, name, param_type - ) + param_location = ValueLocation._from_core_struct(params[i].location) result.append(FunctionParameter(param_type, params[i].name, param_location)) core.BNFreeTypeParameterList(params, count.value) return result diff --git a/python/variable.py b/python/variable.py index 5ae87a8934..80be1de6b0 100644 --- a/python/variable.py +++ b/python/variable.py @@ -27,6 +27,7 @@ from . import _binaryninjacore as core from . import databuffer from . import decorators +from . import types from .enums import RegisterValueType, VariableSourceType, DeadStoreElimination, FunctionGraphType, BuiltinType FunctionOrILFunction = Union["binaryninja.function.Function", "binaryninja.lowlevelil.LowLevelILFunction", @@ -111,6 +112,10 @@ def from_BNRegisterValue( return ConstantPointerRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.StackFrameOffset: return StackFrameOffsetRegisterValue(reg_value.value, confidence=confidence) + elif reg_value.state == RegisterValueType.ResultPointerValue: + return ResultPointerRegisterValue(reg_value.value, confidence=confidence) + elif reg_value.state == RegisterValueType.ParameterPointerValue: + return ParameterPointerRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.ImportedAddressValue: return ImportedAddressRegisterValue(reg_value.value, confidence=confidence) elif reg_value.state == RegisterValueType.UndeterminedValue: @@ -196,6 +201,23 @@ def __repr__(self): return f"" +@dataclass(frozen=True, eq=False) +class ResultPointerRegisterValue(RegisterValue): + offset: int = 0 + type: RegisterValueType = RegisterValueType.ResultPointerValue + + def __repr__(self): + return f"" + +@dataclass(frozen=True, eq=False) +class ParameterPointerRegisterValue(RegisterValue): + offset: int = 0 + type: RegisterValueType = RegisterValueType.ParameterPointerValue + + def __repr__(self): + return f"" + + @dataclass(frozen=True, eq=False) class ExternalPointerRegisterValue(RegisterValue): type: RegisterValueType = RegisterValueType.ExternalPointerValue @@ -287,6 +309,11 @@ def __init__( self._value = value.value elif value.state == RegisterValueType.StackFrameOffset: self._offset = value.value + elif value.state == RegisterValueType.ResultPointerValue: + self._offset = value.value + elif value.state == RegisterValueType.ParameterPointerValue: + self._value = value.value + self._offset = value.offset elif value.state & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: self._value = value.value self._size = value.size @@ -334,6 +361,10 @@ def __repr__(self): return f"" if self._type == RegisterValueType.StackFrameOffset: return f"" + if self._type == RegisterValueType.ResultPointerValue: + return f"" + if self._type == RegisterValueType.ParameterPointerValue: + return f"" if self._type == RegisterValueType.ConstantDataZeroExtendValue: return f"" if self._type == RegisterValueType.ConstantDataSignExtendValue: @@ -364,7 +395,7 @@ def __contains__(self, other): if not isinstance(other, int): return NotImplemented #Initial implementation only checks numbers, no set logic - if self.type == RegisterValueType.StackFrameOffset: + if self.type in [RegisterValueType.StackFrameOffset, RegisterValueType.ResultPointerValue, RegisterValueType.ParameterPointerValue]: return NotImplemented if self.type in [RegisterValueType.SignedRangeValue, RegisterValueType.UnsignedRangeValue]: for rng in self.ranges: @@ -395,6 +426,10 @@ def __eq__(self, other): return self.value == other.value elif self.type == RegisterValueType.StackFrameOffset: return self.offset == other.offset + elif self.type == RegisterValueType.ResultPointerValue: + return self.offset == other.offset + elif self.type == RegisterValueType.ParameterPointerValue: + return self.value == other.value and self.offset == other.offset elif self.type & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: return self.value == other.value and self._size == other._size elif self.type in [RegisterValueType.SignedRangeValue, RegisterValueType.UnsignedRangeValue]: @@ -423,6 +458,11 @@ def _to_core_struct(self) -> core.BNPossibleValueSet: result.value = self.value elif self.type == RegisterValueType.StackFrameOffset: result.offset = self.offset + elif self.type == RegisterValueType.ResultPointerValue: + result.value = self.offset + elif self.type == RegisterValueType.ParameterPointerValue: + result.value = self.value + result.offset = self.offset elif self.type & RegisterValueType.ConstantDataValue == RegisterValueType.ConstantDataValue: result.value = self.value result.size = self.size @@ -558,6 +598,39 @@ def stack_frame_offset(offset: int) -> 'PossibleValueSet': result._offset = offset return result + @staticmethod + def result_pointer(offset: int) -> 'PossibleValueSet': + """ + Create a PossibleValueSet object for a pointer to the return value when the return value + is stored at an unknown location in memory. This is typically used for calling conventions + that pass in a pointer to the storage location for the return value. + + :param int offset: Integer value of the offset + :rtype: PossibleValueSet + """ + result = PossibleValueSet() + result._type = RegisterValueType.ResultPointerValue + result._value = offset + return result + + @staticmethod + def parameter_pointer(idx: int, offset: int) -> 'PossibleValueSet': + """ + Create a PossibleValueSet object for a pointer to a parameter when the parameter is + stored at an unknown location in memory. This is typically used for calling conventions + that pass in a pointer to the storage location for parameters (usually larger than + can be held in a register). + + :param int idx: Index of the parameter + :param int offset: Integer value of the offset + :rtype: PossibleValueSet + """ + result = PossibleValueSet() + result._type = RegisterValueType.ParameterPointerValue + result._value = idx + result._offset = offset + return result + @staticmethod def signed_range_value(ranges: List[ValueRange]) -> 'PossibleValueSet': """ @@ -1151,6 +1224,53 @@ def function(self) -> Optional['binaryninja.function.Function']: return self._func +@decorators.passive +class ParameterLocations: + def __init__( + self, location_list: List['types.ValueLocation'], confidence: int = core.max_confidence, + func: Optional['binaryninja.function.Function'] = None + ): + self._locations = location_list + self._confidence = confidence + self._func = func + + def __repr__(self): + return f"" + + def __len__(self): + return len(self._vars) + + def __iter__(self) -> Generator['types.ValueLocation', None, None]: + for location in self._locations: + yield location + + def __eq__(self, other) -> bool: + return (self._locations, self._confidence, self._func) == (other._locations, other._confidence, other._func) + + def __getitem__(self, idx) -> 'types.ValueLocation': + return self._locations[idx] + + def __setitem__(self, idx: int, value: 'types.ValueLocation'): + self._locations[idx] = value + if self._func is not None: + self._func.parameter_locations = self + + def with_confidence(self, confidence: int) -> 'ParameterLocations': + return ParameterLocations(list(self._locations), confidence, self._func) + + @property + def locations(self) -> List['types.ValueLocation']: + return self._locations + + @property + def confidence(self) -> int: + return self._confidence + + @property + def function(self) -> Optional['binaryninja.function.Function']: + return self._func + + @dataclass(frozen=True, order=True) class AddressRange: start: int # Inclusive starting address diff --git a/rust/src/calling_convention.rs b/rust/src/calling_convention.rs index 4dc54a5b7a..52b0cfc050 100644 --- a/rust/src/calling_convention.rs +++ b/rust/src/calling_convention.rs @@ -15,6 +15,7 @@ //! Contains and provides information about different systems' calling conventions to analysis. use std::borrow::Borrow; +use std::collections::BTreeMap; use std::ffi::c_void; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; @@ -25,10 +26,10 @@ use binaryninjacore_sys::*; use crate::architecture::{ Architecture, ArchitectureExt, CoreArchitecture, CoreRegister, Register, RegisterId, }; -use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; +use crate::ffi::slice_from_raw_parts; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::*; -use crate::types::FunctionParameter; -use crate::variable::Variable; +use crate::types::{FunctionParameter, ReturnValue, ValueLocation}; // TODO // force valid registers once Arch has _from_id methods // CallingConvention impl @@ -409,6 +410,23 @@ where getParameterVariableForIncomingVariable: Some(cb_incoming_param_for_var::), areArgumentRegistersUsedForVarArgs: Some(cb_are_argument_registers_used_for_var_args::), + + isReturnTypeRegisterCompatible: None, + getIndirectReturnValueLocation: None, + getReturnedIndirectReturnValuePointer: None, + isArgumentTypeRegisterCompatible: None, + areNonRegisterArgumentsIndirect: None, + areStackArgumentsNaturallyAligned: None, + + getCallLayout: None, + freeCallLayout: None, + getReturnValueLocation: None, + freeValueLocation: None, + getParameterLocations: None, + freeParameterLocations: None, + getStackAdjustmentForLocations: None, + getRegisterStackAdjustments: None, + freeRegisterStackAdjustments: None, }; unsafe { @@ -458,46 +476,60 @@ impl CoreCallingConvention { unsafe { BnString::into_string(BNGetCallingConventionName(self.handle)) } } - pub fn variables_for_parameters( + pub fn call_layout( &self, + return_value: impl Into, params: &[FunctionParameter], permitted_registers: Option<&[CoreRegister]>, - ) -> Vec { - let mut count: usize = 0; + ) -> CallLayout { + let raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let raw_params: Vec = params .iter() .cloned() .map(FunctionParameter::into_raw) .collect(); - let raw_vars_ptr: *mut BNVariable = if let Some(permitted_args) = permitted_registers { + let raw_layout: BNCallLayout = if let Some(permitted_args) = permitted_registers { let permitted_regs = permitted_args.iter().map(|r| r.id().0).collect::>(); unsafe { - BNGetVariablesForParameters( + BNGetCallLayout( self.handle, + &raw_return_value, raw_params.as_ptr(), raw_params.len(), permitted_regs.as_ptr(), permitted_regs.len(), - &mut count, ) } } else { unsafe { - BNGetVariablesForParametersDefaultPermittedArgs( + BNGetCallLayoutDefaultPermittedArgs( self.handle, + &raw_return_value, raw_params.as_ptr(), raw_params.len(), - &mut count, ) } }; - for raw_param in raw_params { - FunctionParameter::free_raw(raw_param); - } + ReturnValue::free_rust_raw(raw_return_value); + CallLayout::from_owned_core_raw(raw_layout) + } - unsafe { Array::::new(raw_vars_ptr, count, ()) }.to_vec() + pub fn return_value_location(&self, return_value: impl Into) -> ValueLocation { + let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into()); + let mut raw_location = unsafe { + BNGetReturnValueLocation( + self.handle, + &mut raw_return_value, + ) + }; + ReturnValue::free_rust_raw(raw_return_value); + let result = ValueLocation::from_raw(&raw_location); + unsafe { + BNFreeValueLocation(&mut raw_location); + } + result } } @@ -914,3 +946,57 @@ impl CallingConvention for ConventionBuilder { unsafe impl Send for ConventionBuilder {} unsafe impl Sync for ConventionBuilder {} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct CallLayout { + pub parameters: Vec, + pub return_value: Option, + pub stack_adjustment: i64, + pub register_stack_adjustments: BTreeMap, +} + +impl CallLayout { + pub(crate) fn from_raw(value: &BNCallLayout) -> Self { + let raw_params = unsafe { slice_from_raw_parts(value.parameters, value.parameterCount) }; + let parameters = raw_params.iter().map(ValueLocation::from_raw).collect(); + let return_value = if value.returnValueValid { + Some(ValueLocation::from_raw(&value.returnValue)) + } else { + None + }; + let raw_regs = unsafe { + slice_from_raw_parts( + value.registerStackAdjustmentRegisters, + value.registerStackAdjustmentCount, + ) + }; + let raw_amounts = unsafe { + slice_from_raw_parts( + value.registerStackAdjustmentAmounts, + value.registerStackAdjustmentCount, + ) + }; + let mut register_stack_adjustments = BTreeMap::new(); + for i in 0..value.registerStackAdjustmentCount { + register_stack_adjustments.insert(RegisterId(raw_regs[i]), raw_amounts[i]); + } + Self { + parameters, + return_value, + stack_adjustment: value.stackAdjustment, + register_stack_adjustments, + } + } + + /// Take ownership over an "owned" **core allocated** value. Do not call this for a rust allocated value. + pub(crate) fn from_owned_core_raw(mut value: BNCallLayout) -> Self { + let owned = Self::from_raw(&value); + Self::free_core_raw(&mut value); + owned + } + + /// Free a CORE ALLOCATED value. Do not use this with [Self::into_rust_raw] values. + pub(crate) fn free_core_raw(value: &mut BNCallLayout) { + unsafe { BNFreeCallLayout(value) } + } +} diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs index f8745180fb..31ce7582e1 100644 --- a/rust/src/confidence.rs +++ b/rust/src/confidence.rs @@ -3,11 +3,11 @@ use crate::architecture::{Architecture, CoreArchitecture}; use crate::calling_convention::CoreCallingConvention; use crate::rc::{Ref, RefCountable}; -use crate::types::Type; +use crate::types::{Type, ValueLocation}; use binaryninjacore_sys::{ BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture, BNInlineDuringAnalysis, BNInlineDuringAnalysisWithConfidence, BNOffsetWithConfidence, - BNTypeWithConfidence, + BNTypeWithConfidence, BNValueLocation, BNValueLocationWithConfidence, }; use std::fmt; use std::fmt::{Debug, Display, Formatter}; @@ -225,6 +225,19 @@ impl Conf> { } } +impl Conf { + pub(crate) fn into_rust_raw(value: Self) -> BNValueLocationWithConfidence { + BNValueLocationWithConfidence { + location: ValueLocation::into_rust_raw(&value.contents), + confidence: value.confidence, + } + } + + pub(crate) fn free_rust_raw(value: BNValueLocationWithConfidence) { + ValueLocation::free_rust_raw(value.location); + } +} + impl Conf> { pub(crate) fn from_raw(value: &BNCallingConventionWithConfidence) -> Self { let arch = unsafe { diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs index d1c63216a6..b7231ceb89 100644 --- a/rust/src/ffi.rs +++ b/rust/src/ffi.rs @@ -31,6 +31,19 @@ pub(crate) fn time_from_bn(timestamp: u64) -> SystemTime { UNIX_EPOCH + m } +pub(crate) unsafe fn slice_from_raw_parts<'a, T>(data: *const T, len: usize) -> &'a [T] { + if len == 0 { + // C can and will pass null pointers for data in the case of zero length arrays. + // According to the documentation of std::slice::from_raw_parts, data must be + // non-null and properly aligned. To avoid creating unsound slices, return an + // empty slice directly on any zero-length array, avoiding the unsound call + // to std::slice::from_raw_parts. + &[] + } else { + unsafe { std::slice::from_raw_parts(data, len) } + } +} + #[macro_export] macro_rules! ffi_span { ($name:expr, $bv:expr) => {{ diff --git a/rust/src/function.rs b/rust/src/function.rs index c19e2107a2..06a17a1d18 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -21,6 +21,7 @@ use crate::{ calling_convention::CoreCallingConvention, component::Component, disassembly::{DisassemblySettings, DisassemblyTextLine}, + ffi::slice_from_raw_parts, flowgraph::FlowGraph, medium_level_il::FunctionGraphType, platform::Platform, @@ -28,7 +29,7 @@ use crate::{ string::*, symbol::{Binding, Symbol}, tags::{Tag, TagReference, TagType}, - types::{IntegerDisplayType, QualifiedName, Type}, + types::{IntegerDisplayType, QualifiedName, ReturnValue, Type, ValueLocation}, }; use crate::{data_buffer::DataBuffer, disassembly::InstructionTextToken, rc::*}; pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason; @@ -649,6 +650,11 @@ impl Function { Conf::>::from_owned_raw(raw_return_type) } + pub fn return_value(&self) -> ReturnValue { + let raw_return_value = unsafe { BNGetFunctionReturnValue(self.handle) }; + ReturnValue::from_owned_core_raw(raw_return_value) + } + pub fn set_auto_return_type<'a, C>(&self, return_type: C) where C: Into>, @@ -657,6 +663,18 @@ impl Function { unsafe { BNSetAutoFunctionReturnType(self.handle, &mut raw_return_type) } } + pub fn set_auto_return_value_location(&self, location: impl Into>) { + let mut raw_location = Conf::::into_rust_raw(location.into()); + unsafe { BNSetAutoFunctionReturnValueLocation(self.handle, &mut raw_location) }; + Conf::::free_rust_raw(raw_location); + } + + pub fn set_auto_return_value(&self, return_value: impl Into) { + let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into()); + unsafe { BNSetAutoFunctionReturnValue(self.handle, &mut raw_return_value) } + ReturnValue::free_rust_raw(raw_return_value); + } + pub fn set_user_return_type<'a, C>(&self, return_type: C) where C: Into>, @@ -665,6 +683,18 @@ impl Function { unsafe { BNSetUserFunctionReturnType(self.handle, &mut raw_return_type) } } + pub fn set_user_return_value_location(&self, location: impl Into>) { + let mut raw_location = Conf::::into_rust_raw(location.into()); + unsafe { BNSetUserFunctionReturnValueLocation(self.handle, &mut raw_location) }; + Conf::::free_rust_raw(raw_location); + } + + pub fn set_user_return_value(&self, return_value: impl Into) { + let mut raw_return_value = ReturnValue::into_rust_raw(return_value.into()); + unsafe { BNSetUserFunctionReturnValue(self.handle, &mut raw_return_value) } + ReturnValue::free_rust_raw(raw_return_value); + } + pub fn function_type(&self) -> Ref { unsafe { Type::ref_from_raw(BNGetFunctionType(self.handle)) } } @@ -1009,38 +1039,65 @@ impl Function { } } - pub fn set_user_parameter_variables(&self, values: I, confidence: u8) + pub fn parameter_locations(&self) -> Conf> { + unsafe { + let mut raw_locations = BNGetFunctionParameterLocations(self.handle); + let raw_location_list = + slice_from_raw_parts(raw_locations.locations, raw_locations.count); + let locations: Vec = raw_location_list + .iter() + .map(ValueLocation::from_raw) + .collect(); + let confidence = raw_locations.confidence; + BNFreeParameterLocations(&mut raw_locations); + Conf::new(locations, confidence) + } + } + + pub fn set_user_parameter_locations(&self, values: I, confidence: u8) where - I: IntoIterator, + I: IntoIterator, { - let vars: Vec = values.into_iter().map(Into::into).collect(); + let locations: Vec = values + .into_iter() + .map(|location| ValueLocation::into_rust_raw(&location.into())) + .collect(); unsafe { - BNSetUserFunctionParameterVariables( + BNSetUserFunctionParameterLocations( self.handle, - &mut BNParameterVariablesWithConfidence { - vars: vars.as_ptr() as *mut _, - count: vars.len(), + &mut BNValueLocationListWithConfidence { + locations: locations.as_ptr() as *mut _, + count: locations.len(), confidence, }, ) } + locations + .into_iter() + .for_each(|location| ValueLocation::free_rust_raw(location.into())); } - pub fn set_auto_parameter_variables(&self, values: I, confidence: u8) + pub fn set_auto_parameter_locations(&self, values: I, confidence: u8) where - I: IntoIterator, + I: IntoIterator, { - let vars: Vec = values.into_iter().map(Into::into).collect(); + let locations: Vec = values + .into_iter() + .map(|location| ValueLocation::into_rust_raw(&location.into())) + .collect(); unsafe { - BNSetAutoFunctionParameterVariables( + BNSetAutoFunctionParameterLocations( self.handle, - &mut BNParameterVariablesWithConfidence { - vars: vars.as_ptr() as *mut _, - count: vars.len(), + &mut BNValueLocationListWithConfidence { + locations: locations.as_ptr() as *mut _, + count: locations.len(), confidence, }, ) } + locations + .into_iter() + .for_each(|location| ValueLocation::free_rust_raw(location.into())); } pub fn parameter_at( @@ -2528,32 +2585,6 @@ impl Function { Conf::new(regs, result.confidence) } - pub fn set_user_return_registers(&self, values: I, confidence: u8) - where - I: IntoIterator, - { - let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect(); - let mut regs = BNRegisterSetWithConfidence { - regs: regs.as_mut_ptr(), - count: regs.len(), - confidence, - }; - unsafe { BNSetUserFunctionReturnRegisters(self.handle, &mut regs) } - } - - pub fn set_auto_return_registers(&self, values: I, confidence: u8) - where - I: IntoIterator, - { - let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect(); - let mut regs = BNRegisterSetWithConfidence { - regs: regs.as_mut_ptr(), - count: regs.len(), - confidence, - }; - unsafe { BNSetAutoFunctionReturnRegisters(self.handle, &mut regs) } - } - /// Flow graph of unresolved stack adjustments pub fn unresolved_stack_adjustment_graph(&self) -> Option> { let graph = unsafe { BNGetUnresolvedStackAdjustmentGraph(self.handle) }; diff --git a/rust/src/medium_level_il/instruction.rs b/rust/src/medium_level_il/instruction.rs index 17534b312a..c42a3b190f 100644 --- a/rust/src/medium_level_il/instruction.rs +++ b/rust/src/medium_level_il/instruction.rs @@ -680,6 +680,10 @@ impl MediumLevelILInstruction { MLIL_TRAP => Op::Trap(Trap { vector: op.operands[0], }), + MLIL_BLOCK_TO_EXPAND => Op::BlockToExpand(BlockToExpand { + num_operands: op.operands[0] as usize, + first_operand: op.operands[1] as usize, + }), }; Self { @@ -1113,6 +1117,13 @@ impl MediumLevelILInstruction { VarSsaField(op) => Lifted::VarSsaField(op), VarAliasedField(op) => Lifted::VarAliasedField(op), Trap(op) => Lifted::Trap(op), + BlockToExpand(_op) => Lifted::BlockToExpand(LiftedBlockToExpand { + exprs: self + .get_expr_list(0) + .iter() + .map(|expr| expr.lift()) + .collect(), + }), }; MediumLevelILLiftedInstruction { @@ -1826,6 +1837,7 @@ pub enum MediumLevelILInstructionKind { VarSsaField(VarSsaField), VarAliasedField(VarSsaField), Trap(Trap), + BlockToExpand(BlockToExpand), // A placeholder for instructions that the Rust bindings do not yet support. // Distinct from `Unimpl` as that is a valid instruction. NotYetImplemented, diff --git a/rust/src/medium_level_il/lift.rs b/rust/src/medium_level_il/lift.rs index ff12c0e2d5..3f5d4060e4 100644 --- a/rust/src/medium_level_il/lift.rs +++ b/rust/src/medium_level_il/lift.rs @@ -183,6 +183,7 @@ pub enum MediumLevelILLiftedInstructionKind { VarSsaField(VarSsaField), VarAliasedField(VarSsaField), Trap(Trap), + BlockToExpand(LiftedBlockToExpand), // A placeholder for instructions that the Rust bindings do not yet support. // Distinct from `Unimpl` as that is a valid instruction. NotYetImplemented, @@ -329,6 +330,7 @@ impl MediumLevelILLiftedInstruction { VarSsaField(_) => "VarSsaField", VarAliasedField(_) => "VarAliasedField", Trap(_) => "Trap", + BlockToExpand(_) => "BlockToExpand", } } @@ -554,6 +556,7 @@ impl MediumLevelILLiftedInstruction { ("offset", Operand::Int(op.offset)), ], Trap(op) => vec![("vector", Operand::Int(op.vector))], + BlockToExpand(op) => vec![("exprs", Operand::ExprList(op.exprs.clone()))], } } } diff --git a/rust/src/medium_level_il/operation.rs b/rust/src/medium_level_il/operation.rs index 578087d016..6a8c8bb18c 100644 --- a/rust/src/medium_level_il/operation.rs +++ b/rust/src/medium_level_il/operation.rs @@ -658,3 +658,14 @@ pub struct VarSsaField { pub struct Trap { pub vector: u64, } + +// BLOCK_TO_EXPAND +#[derive(Debug, Copy, Clone)] +pub struct BlockToExpand { + pub first_operand: usize, + pub num_operands: usize, +} +#[derive(Clone, Debug, PartialEq)] +pub struct LiftedBlockToExpand { + pub exprs: Vec, +} diff --git a/rust/src/types.rs b/rust/src/types.rs index 4f6f82d2ee..4baf133116 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -35,7 +35,7 @@ pub mod structure; use binaryninjacore_sys::*; use crate::{ - architecture::Architecture, + architecture::{Architecture, Register, RegisterId}, binary_view::{BinaryView, BinaryViewExt}, calling_convention::CoreCallingConvention, rc::*, @@ -794,12 +794,12 @@ impl Type { } // TODO: FunctionBuilder - pub fn function<'a, T: Into>>( - return_type: T, + pub fn function<'a, T: Into>( + return_value: T, parameters: Vec, variable_arguments: bool, ) -> Ref { - let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); + let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); @@ -818,15 +818,9 @@ impl Type { let reg_stack_adjust_regs = std::ptr::null_mut(); let reg_stack_adjust_values = std::ptr::null_mut(); - let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: std::ptr::null_mut(), - count: 0, - confidence: 0, - }; - let result = unsafe { Self::ref_from_raw(BNCreateFunctionType( - &mut owned_raw_return_type, + &mut owned_raw_return_value, &mut raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), @@ -836,12 +830,12 @@ impl Type { reg_stack_adjust_regs, reg_stack_adjust_values, 0, - &mut return_regs, BNNameType::NoNameType, &mut pure, )) }; + ReturnValue::free_rust_raw(owned_raw_return_value); for raw_param in raw_parameters { FunctionParameter::free_raw(raw_param); } @@ -852,16 +846,16 @@ impl Type { // TODO: FunctionBuilder pub fn function_with_opts< 'a, - T: Into>, + T: Into, C: Into>>, >( - return_type: T, + return_value: T, parameters: &[FunctionParameter], variable_arguments: bool, calling_convention: C, stack_adjust: Conf, ) -> Ref { - let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into()); + let mut owned_raw_return_value = ReturnValue::into_rust_raw(return_value.into()); let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into(); let mut can_return = Conf::new(true, MIN_CONFIDENCE).into(); let mut pure = Conf::new(false, MIN_CONFIDENCE).into(); @@ -880,15 +874,9 @@ impl Type { let reg_stack_adjust_regs = std::ptr::null_mut(); let reg_stack_adjust_values = std::ptr::null_mut(); - let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence { - regs: std::ptr::null_mut(), - count: 0, - confidence: 0, - }; - let result = unsafe { Self::ref_from_raw(BNCreateFunctionType( - &mut owned_raw_return_type, + &mut owned_raw_return_value, &mut owned_raw_calling_convention, raw_parameters.as_mut_ptr(), raw_parameters.len(), @@ -898,12 +886,12 @@ impl Type { reg_stack_adjust_regs, reg_stack_adjust_values, 0, - &mut return_regs, BNNameType::NoNameType, &mut pure, )) }; + ReturnValue::free_rust_raw(owned_raw_return_value); for raw_param in raw_parameters { FunctionParameter::free_raw(raw_param); } @@ -1057,11 +1045,285 @@ unsafe impl CoreArrayProviderInner for Type { } } +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ValueLocationComponent { + pub variable: Variable, + pub offset: i64, + pub size: Option, + pub indirect: bool, + pub returned_pointer: Option, +} + +impl ValueLocationComponent { + pub(crate) fn from_raw(value: &BNValueLocationComponent) -> Self { + let variable = Variable::from(&value.variable); + let size = if value.sizeValid { + Some(value.size) + } else { + None + }; + Self { + variable, + offset: value.offset, + size, + indirect: value.indirect, + returned_pointer: if value.returnedPointerValid { + Some(Variable::from(&value.returnedPointer)) + } else { + None + }, + } + } + + pub(crate) fn into_raw(value: &Self) -> BNValueLocationComponent { + BNValueLocationComponent { + variable: value.variable.into(), + offset: value.offset, + sizeValid: value.size.is_some(), + size: value.size.unwrap_or(0), + indirect: value.indirect, + returnedPointerValid: value.returned_pointer.is_some(), + returnedPointer: if let Some(ptr) = value.returned_pointer { + ptr.into() + } else { + Variable::new(VariableSourceType::RegisterVariableSourceType, 0, 0).into() + }, + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ValueLocation { + pub components: Vec, +} + +impl ValueLocation { + pub fn from_variable(var: Variable) -> Self { + Self { + components: vec![ValueLocationComponent { + variable: var, + offset: 0, + size: None, + indirect: false, + returned_pointer: None, + }], + } + } + + pub fn from_register(reg: impl Register) -> Self { + Self::from_variable(Variable::new( + VariableSourceType::RegisterVariableSourceType, + 0, + reg.id().0 as i64, + )) + } + + pub fn from_register_id(reg: RegisterId) -> Self { + Self::from_variable(Variable::new( + VariableSourceType::RegisterVariableSourceType, + 0, + reg.0 as i64, + )) + } + + pub fn from_stack_offset(offset: i64) -> Self { + Self::from_variable(Variable::new( + VariableSourceType::StackVariableSourceType, + 0, + offset, + )) + } + + pub fn is_valid(&self) -> bool { + !self.components.is_empty() + } + + pub fn variable_for_return_value(&self) -> Option { + let value_raw = Self::into_rust_raw(&self); + let mut var_raw = BNVariable::default(); + let valid = unsafe { BNGetValueLocationVariableForReturnValue(&value_raw, &mut var_raw) }; + Self::free_rust_raw(value_raw); + if valid { + Some(var_raw.into()) + } else { + None + } + } + + pub fn variable_for_parameter(&self, idx: usize) -> Option { + let value_raw = Self::into_rust_raw(&self); + let mut var_raw = BNVariable::default(); + let valid = + unsafe { BNGetValueLocationVariableForParameter(&value_raw, &mut var_raw, idx) }; + Self::free_rust_raw(value_raw); + if valid { + Some(var_raw.into()) + } else { + None + } + } + + pub(crate) fn from_raw(components: &BNValueLocation) -> Self { + if components.count == 0 { + return Self { + components: Vec::new(), + }; + } + + let components_raw: &[BNValueLocationComponent] = + unsafe { std::slice::from_raw_parts(components.components, components.count) }; + Self { + components: components_raw + .iter() + .map(|component| ValueLocationComponent::from_raw(component)) + .collect(), + } + } + + pub(crate) fn into_rust_raw(value: &Self) -> BNValueLocation { + let components: Box<[BNValueLocationComponent]> = value + .components + .iter() + .map(|component| ValueLocationComponent::into_raw(component)) + .collect(); + BNValueLocation { + count: components.len(), + components: Box::leak(components).as_mut_ptr(), + } + } + + /// Free a RUST ALLOCATED possible value set. Do not use this with CORE ALLOCATED values. + pub(crate) fn free_rust_raw(value: BNValueLocation) { + let raw_components = + unsafe { std::slice::from_raw_parts_mut(value.components, value.count) }; + let _ = unsafe { Box::from_raw(raw_components) }; + } +} + +impl Into for Variable { + fn into(self) -> ValueLocation { + ValueLocation { + components: vec![ValueLocationComponent { + variable: self, + offset: 0, + size: None, + indirect: false, + returned_pointer: None, + }], + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct ReturnValue { + pub ty: Conf>, + pub location: Option>, +} + +impl ReturnValue { + pub(crate) fn from_raw(value: &BNReturnValue) -> Self { + Self { + ty: Conf::new( + unsafe { Type::from_raw(value.type_).to_owned() }, + value.typeConfidence, + ), + location: match value.defaultLocation { + false => Some(Conf::new( + ValueLocation::from_raw(&value.location), + value.locationConfidence, + )), + true => None, + }, + } + } + + /// Take ownership over an "owned" **core allocated** value. Do not call this for a rust allocated value. + pub(crate) fn from_owned_core_raw(mut value: BNReturnValue) -> Self { + let owned = Self::from_raw(&value); + Self::free_core_raw(&mut value); + owned + } + + pub(crate) fn into_rust_raw(value: Self) -> BNReturnValue { + BNReturnValue { + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, + typeConfidence: value.ty.confidence, + defaultLocation: value.location.is_none(), + location: ValueLocation::into_rust_raw( + value + .location + .as_ref() + .map(|v| &v.contents) + .unwrap_or(&ValueLocation { + components: Vec::new(), + }), + ), + locationConfidence: value.location.as_ref().map(|v| v.confidence).unwrap_or(0), + } + } + + /// Free a CORE ALLOCATED possible value set. Do not use this with [Self::into_rust_raw] values. + pub(crate) fn free_core_raw(value: &mut BNReturnValue) { + unsafe { BNFreeReturnValue(value) } + } + + /// Free a RUST ALLOCATED possible value set. Do not use this with CORE ALLOCATED values. + pub(crate) fn free_rust_raw(value: BNReturnValue) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + ValueLocation::free_rust_raw(value.location); + } +} + +impl Into for Ref { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.into(), + location: None, + } + } +} + +impl Into for &Ref { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.clone().into(), + location: None, + } + } +} + +impl Into for &Type { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.to_owned().into(), + location: None, + } + } +} + +impl Into for Conf> { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self, + location: None, + } + } +} + +impl Into for &Conf> { + fn into(self) -> ReturnValue { + ReturnValue { + ty: self.clone(), + location: None, + } + } +} + #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct FunctionParameter { pub ty: Conf>, pub name: String, - pub location: Option, + pub location: Option, } impl FunctionParameter { @@ -1069,13 +1331,7 @@ impl FunctionParameter { // TODO: I copied this from the original `from_raw` function. // TODO: So this actually needs to be audited later. let name = if value.name.is_null() { - if value.location.type_ == VariableSourceType::RegisterVariableSourceType { - format!("reg_{}", value.location.storage) - } else if value.location.type_ == VariableSourceType::StackVariableSourceType { - format!("arg_{}", value.location.storage) - } else { - String::new() - } + String::new() } else { raw_to_string(value.name as *const _).unwrap() }; @@ -1087,7 +1343,7 @@ impl FunctionParameter { ), name, location: match value.defaultLocation { - false => Some(Variable::from(value.location)), + false => Some(ValueLocation::from_raw(&value.location)), true => None, }, } @@ -1107,20 +1363,27 @@ impl FunctionParameter { type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, typeConfidence: value.ty.confidence, defaultLocation: value.location.is_none(), - location: value.location.map(Into::into).unwrap_or_default(), + location: ValueLocation::into_rust_raw(&value.location.unwrap_or(ValueLocation { + components: Vec::new(), + })), } } pub(crate) fn free_raw(value: BNFunctionParameter) { unsafe { BnString::free_raw(value.name) }; let _ = unsafe { Type::ref_from_raw(value.type_) }; + ValueLocation::free_rust_raw(value.location); } - pub fn new>>>(ty: T, name: String, location: Option) -> Self { + pub fn new>>>( + ty: T, + name: String, + location: Option, + ) -> Self { Self { ty: ty.into(), name, - location, + location: location.map(|v| v.into()), } } } diff --git a/rust/src/variable.rs b/rust/src/variable.rs index f9384b5d44..efabf67b5b 100644 --- a/rust/src/variable.rs +++ b/rust/src/variable.rs @@ -417,6 +417,8 @@ impl Variable { } VariableSourceType::StackVariableSourceType => None, VariableSourceType::FlagVariableSourceType => None, + VariableSourceType::CompositeReturnValueSourceType => None, + VariableSourceType::CompositeParameterSourceType => None, } } } @@ -674,6 +676,13 @@ pub enum PossibleValueSet { StackFrameOffset { value: i64, }, + ResultPointer { + offset: i64, + }, + ParameterPointer { + index: u64, + offset: i64, + }, ReturnAddressValue, ImportedAddressValue { value: i64, @@ -730,6 +739,13 @@ impl PossibleValueSet { offset: value.offset, }, RegisterValueType::StackFrameOffset => Self::StackFrameOffset { value: value.value }, + RegisterValueType::ResultPointerValue => Self::ResultPointer { + offset: value.value, + }, + RegisterValueType::ParameterPointerValue => Self::ParameterPointer { + index: value.value as u64, + offset: value.offset, + }, RegisterValueType::ReturnAddressValue => Self::ReturnAddressValue, RegisterValueType::ImportedAddressValue => { Self::ImportedAddressValue { value: value.value } @@ -815,6 +831,13 @@ impl PossibleValueSet { PossibleValueSet::StackFrameOffset { value } => { raw.value = value; } + PossibleValueSet::ResultPointer { offset } => { + raw.value = offset; + } + PossibleValueSet::ParameterPointer { index, offset } => { + raw.value = index as i64; + raw.offset = offset; + } PossibleValueSet::ReturnAddressValue => {} PossibleValueSet::ImportedAddressValue { value } => { raw.value = value; @@ -910,6 +933,8 @@ impl PossibleValueSet { RegisterValueType::ExternalPointerValue } PossibleValueSet::StackFrameOffset { .. } => RegisterValueType::StackFrameOffset, + PossibleValueSet::ResultPointer { .. } => RegisterValueType::ResultPointerValue, + PossibleValueSet::ParameterPointer { .. } => RegisterValueType::ParameterPointerValue, PossibleValueSet::ReturnAddressValue => RegisterValueType::ReturnAddressValue, PossibleValueSet::ImportedAddressValue { .. } => { RegisterValueType::ImportedAddressValue diff --git a/type.cpp b/type.cpp index 817e0934c8..d7e3a9f817 100644 --- a/type.cpp +++ b/type.cpp @@ -534,6 +534,230 @@ BaseStructure::BaseStructure(Type* _type, uint64_t _offset) } +ValueLocationComponent ValueLocationComponent::RemapVariables(const std::function& remap) const +{ + if (returnedPointer.has_value()) + return {remap(variable), offset, size, indirect, remap(returnedPointer.value())}; + return {remap(variable), offset, size, indirect, std::nullopt}; +} + +bool ValueLocationComponent::operator==(const ValueLocationComponent& component) const +{ + return variable == component.variable && offset == component.offset && size == component.size + && indirect == component.indirect && (!indirect || returnedPointer == component.returnedPointer); +} + + +bool ValueLocationComponent::operator!=(const ValueLocationComponent& component) const +{ + return !(*this == component); +} + + +ValueLocationComponent ValueLocationComponent::FromAPIObject(const BNValueLocationComponent* loc) +{ + return {Variable(loc->variable.type, loc->variable.index, loc->variable.storage), loc->offset, + loc->sizeValid ? std::optional(loc->size) : std::nullopt, loc->indirect, + loc->returnedPointerValid ? std::optional( + Variable(loc->returnedPointer.type, loc->returnedPointer.index, loc->returnedPointer.storage)) + : std::nullopt}; +} + + +BNValueLocationComponent ValueLocationComponent::ToAPIObject() const +{ + BNValueLocationComponent result; + result.variable.type = variable.type; + result.variable.index = variable.index; + result.variable.storage = variable.storage; + result.offset = offset; + result.sizeValid = size.has_value(); + result.size = size.value_or(0); + result.indirect = indirect; + result.returnedPointerValid = returnedPointer.has_value(); + if (returnedPointer.has_value()) + { + result.returnedPointer.type = returnedPointer->type; + result.returnedPointer.index = returnedPointer->index; + result.returnedPointer.storage = returnedPointer->storage; + } + else + { + result.returnedPointer.type = RegisterVariableSourceType; + result.returnedPointer.index = 0; + result.returnedPointer.storage = 0; + } + return result; +} + + +std::optional ValueLocation::GetVariableForReturnValue() const +{ + BNValueLocation loc = ToAPIObject(); + BNVariable var; + bool valid = BNGetValueLocationVariableForReturnValue(&loc, &var); + FreeAPIObject(&loc); + if (valid) + return var; + return std::nullopt; +} + + +std::optional ValueLocation::GetVariableForParameter(size_t idx) const +{ + BNValueLocation loc = ToAPIObject(); + BNVariable var; + bool valid = BNGetValueLocationVariableForParameter(&loc, &var, idx); + FreeAPIObject(&loc); + if (valid) + return var; + return std::nullopt; +} + + +ValueLocation ValueLocation::RemapVariables(const std::function& remap) const +{ + vector result; + result.reserve(components.size()); + for (auto& i : components) + result.push_back(i.RemapVariables(remap)); + return {result}; +} + + +void ValueLocation::ForEachVariable(const std::function& func) const +{ + for (auto& i : components) + func(i.variable, i.indirect); +} + + +bool ValueLocation::ContainsVariable(Variable var) const +{ + for (auto& i : components) + if (i.variable == var) + return true; + return false; +} + + +bool ValueLocation::operator==(const ValueLocation& loc) const +{ + return components == loc.components; +} + + +bool ValueLocation::operator!=(const ValueLocation& loc) const +{ + return components != loc.components; +} + + +ValueLocation ValueLocation::FromAPIObject(const BNValueLocation* loc) +{ + ValueLocation result; + result.components.reserve(loc->count); + for (size_t i = 0; i < loc->count; i++) + result.components.push_back(ValueLocationComponent::FromAPIObject(&loc->components[i])); + return result; +} + + +BNValueLocation ValueLocation::ToAPIObject() const +{ + BNValueLocation result; + result.count = components.size(); + result.components = new BNValueLocationComponent[components.size()]; + for (size_t i = 0; i < components.size(); i++) + result.components[i] = components[i].ToAPIObject(); + return result; +} + + +void ValueLocation::FreeAPIObject(BNValueLocation* loc) +{ + delete[] loc->components; +} + + +bool ReturnValue::operator==(const ReturnValue& nt) const +{ + if (type != nt.type) + return false; + if (defaultLocation != nt.defaultLocation) + return false; + if (defaultLocation) + return true; + return location == nt.location; +} + + +bool ReturnValue::operator!=(const ReturnValue& nt) const +{ + return !((*this) == nt); +} + + +ReturnValue ReturnValue::FromAPIObject(const BNReturnValue* returnValue) +{ + ReturnValue result; + result.type = Confidence>( + returnValue->type ? new Type(BNNewTypeReference(returnValue->type)) : nullptr, returnValue->typeConfidence); + result.defaultLocation = returnValue->defaultLocation; + result.location = Confidence( + ValueLocation::FromAPIObject(&returnValue->location), returnValue->locationConfidence); + return result; +} + + +BNReturnValue ReturnValue::ToAPIObject() const +{ + BNReturnValue result; + result.type = type.GetValue() ? type.GetValue()->GetObject() : nullptr; + result.typeConfidence = type.GetConfidence(); + result.defaultLocation = defaultLocation; + result.location = location->ToAPIObject(); + result.locationConfidence = location.GetConfidence(); + return result; +} + + +void ReturnValue::FreeAPIObject(BNReturnValue* returnValue) +{ + ValueLocation::FreeAPIObject(&returnValue->location); +} + + +FunctionParameter FunctionParameter::FromAPIObject(const BNFunctionParameter* param) +{ + FunctionParameter result; + result.name = param->name; + result.type = + Confidence>(param->type ? new Type(BNNewTypeReference(param->type)) : nullptr, param->typeConfidence); + result.defaultLocation = param->defaultLocation; + result.location = ValueLocation::FromAPIObject(¶m->location); + return result; +} + + +BNFunctionParameter FunctionParameter::ToAPIObject() const +{ + BNFunctionParameter result; + result.name = (char*)name.c_str(); + result.type = type->GetObject(); + result.typeConfidence = type.GetConfidence(); + result.defaultLocation = defaultLocation; + result.location = location.ToAPIObject(); + return result; +} + + +void FunctionParameter::FreeAPIObject(BNFunctionParameter* param) +{ + ValueLocation::FreeAPIObject(¶m->location); +} + + Type::Type(BNType* type) { m_object = type; @@ -599,6 +823,30 @@ Confidence> Type::GetChildType() const } +ReturnValue Type::GetReturnValue() const +{ + BNReturnValue ret = BNGetTypeReturnValue(m_object); + ReturnValue result = ReturnValue::FromAPIObject(&ret); + BNFreeReturnValue(&ret); + return result; +} + + +bool Type::IsReturnValueDefaultLocation() const +{ + return BNIsTypeReturnValueDefaultLocation(m_object); +} + + +Confidence Type::GetReturnValueLocation() const +{ + BNValueLocationWithConfidence location = BNGetTypeReturnValueLocation(m_object); + Confidence result(ValueLocation::FromAPIObject(&location.location), location.confidence); + BNFreeValueLocation(&location.location); + return result; +} + + Confidence> Type::GetCallingConvention() const { BNCallingConventionWithConfidence cc = BNGetTypeCallingConvention(m_object); @@ -622,16 +870,7 @@ vector Type::GetParameters() const vector result; result.reserve(count); for (size_t i = 0; i < count; i++) - { - FunctionParameter param; - param.name = types[i].name; - param.type = Confidence>(new Type(BNNewTypeReference(types[i].type)), types[i].typeConfidence); - param.defaultLocation = types[i].defaultLocation; - param.location.type = types[i].location.type; - param.location.index = types[i].location.index; - param.location.storage = types[i].location.storage; - result.push_back(param); - } + result.push_back(FunctionParameter::FromAPIObject(&types[i])); BNFreeTypeParameterList(types, count); return result; @@ -1000,13 +1239,11 @@ Ref Type::ArrayType(const Confidence>& type, uint64_t elem) } -Ref Type::FunctionType(const Confidence>& returnValue, - const Confidence>& callingConvention, const std::vector& params, - const Confidence& varArg, const Confidence& stackAdjust) +Ref Type::FunctionType(const ReturnValue& returnValue, + const Confidence>& callingConvention, const std::vector& params, + const Confidence& varArg, const Confidence& stackAdjust) { - BNTypeWithConfidence returnValueConf; - returnValueConf.type = returnValue->GetObject(); - returnValueConf.confidence = returnValue.GetConfidence(); + BNReturnValue ret = returnValue.ToAPIObject(); BNCallingConventionWithConfidence callingConventionConf; callingConventionConf.convention = callingConvention.GetValue() ? callingConvention->GetObject() : nullptr; @@ -1014,15 +1251,7 @@ Ref Type::FunctionType(const Confidence>& returnValue, BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; for (size_t i = 0; i < params.size(); i++) - { - paramArray[i].name = (char*)params[i].name.c_str(); - paramArray[i].type = params[i].type->GetObject(); - paramArray[i].typeConfidence = params[i].type.GetConfidence(); - paramArray[i].defaultLocation = params[i].defaultLocation; - paramArray[i].location.type = params[i].location.type; - paramArray[i].location.index = params[i].location.index; - paramArray[i].location.storage = params[i].location.storage; - } + paramArray[i] = params[i].ToAPIObject(); BNBoolWithConfidence varArgConf; varArgConf.value = varArg.GetValue(); @@ -1036,37 +1265,33 @@ Ref Type::FunctionType(const Confidence>& returnValue, stackAdjustConf.value = stackAdjust.GetValue(); stackAdjustConf.confidence = stackAdjust.GetConfidence(); - BNRegisterSetWithConfidence returnRegsConf; - returnRegsConf.regs = nullptr; - returnRegsConf.count = 0; - returnRegsConf.confidence = 0; - BNBoolWithConfidence pureConf; pureConf.value = false; pureConf.confidence = 0; Type* type = new Type(BNCreateFunctionType( - &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, - &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, &returnRegsConf, NoNameType, &pureConf)); + &ret, &callingConventionConf, paramArray, params.size(), &varArgConf, + &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, NoNameType, &pureConf)); + + ReturnValue::FreeAPIObject(&ret); + for (size_t i = 0; i < params.size(); i++) + FunctionParameter::FreeAPIObject(¶mArray[i]); delete[] paramArray; return type; } -Ref Type::FunctionType(const Confidence>& returnValue, +Ref Type::FunctionType(const ReturnValue& returnValue, const Confidence>& callingConvention, const std::vector& params, const Confidence& hasVariableArguments, const Confidence& canReturn, const Confidence& stackAdjust, const std::map>& regStackAdjust, - const Confidence>& returnRegs, BNNameType ft, const Confidence& pure) { - BNTypeWithConfidence returnValueConf; - returnValueConf.type = returnValue->GetObject(); - returnValueConf.confidence = returnValue.GetConfidence(); + BNReturnValue ret = returnValue.ToAPIObject(); BNCallingConventionWithConfidence callingConventionConf; callingConventionConf.convention = callingConvention.GetValue() ? callingConvention->GetObject() : nullptr; @@ -1074,15 +1299,7 @@ Ref Type::FunctionType(const Confidence>& returnValue, BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; for (size_t i = 0; i < params.size(); i++) - { - paramArray[i].name = (char*)params[i].name.c_str(); - paramArray[i].type = params[i].type->GetObject(); - paramArray[i].typeConfidence = params[i].type.GetConfidence(); - paramArray[i].defaultLocation = params[i].defaultLocation; - paramArray[i].location.type = params[i].location.type; - paramArray[i].location.index = params[i].location.index; - paramArray[i].location.storage = params[i].location.storage; - } + paramArray[i] = params[i].ToAPIObject(); BNBoolWithConfidence varArgConf; varArgConf.value = hasVariableArguments.GetValue(); @@ -1107,21 +1324,18 @@ Ref Type::FunctionType(const Confidence>& returnValue, i ++; } - std::vector returnRegsRegs = returnRegs.GetValue(); - - BNRegisterSetWithConfidence returnRegsConf; - returnRegsConf.regs = returnRegsRegs.data(); - returnRegsConf.count = returnRegs->size(); - returnRegsConf.confidence = returnRegs.GetConfidence(); - BNBoolWithConfidence pureConf; pureConf.value = pure.GetValue(); pureConf.confidence = pure.GetConfidence(); Type* type = new Type(BNCreateFunctionType( - &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, + &ret, &callingConventionConf, paramArray, params.size(), &varArgConf, &canReturnConf, &stackAdjustConf, regStackAdjustRegs.data(), - regStackAdjustValues.data(), regStackAdjust.size(), &returnRegsConf, NoNameType, &pureConf)); + regStackAdjustValues.data(), regStackAdjust.size(), NoNameType, &pureConf)); + + ReturnValue::FreeAPIObject(&ret); + for (i = 0; i < params.size(); i++) + FunctionParameter::FreeAPIObject(¶mArray[i]); delete[] paramArray; return type; } @@ -1590,6 +1804,30 @@ Confidence> TypeBuilder::GetChildType() const } +ReturnValue TypeBuilder::GetReturnValue() const +{ + BNReturnValue ret = BNGetTypeBuilderReturnValue(m_object); + ReturnValue result = ReturnValue::FromAPIObject(&ret); + BNFreeReturnValue(&ret); + return result; +} + + +bool TypeBuilder::IsReturnValueDefaultLocation() const +{ + return BNIsTypeBuilderReturnValueDefaultLocation(m_object); +} + + +Confidence TypeBuilder::GetReturnValueLocation() const +{ + BNValueLocationWithConfidence location = BNGetTypeBuilderReturnValueLocation(m_object); + Confidence result(ValueLocation::FromAPIObject(&location.location), location.confidence); + BNFreeValueLocation(&location.location); + return result; +} + + TypeBuilder& TypeBuilder::SetChildType(const Confidence>& child) { BNTypeWithConfidence childType; @@ -1600,6 +1838,32 @@ TypeBuilder& TypeBuilder::SetChildType(const Confidence>& child) } +TypeBuilder& TypeBuilder::SetReturnValue(const ReturnValue& rv) +{ + BNReturnValue ret = rv.ToAPIObject(); + BNTypeBuilderSetReturnValue(m_object, &ret); + ReturnValue::FreeAPIObject(&ret); + return *this; +} + + +TypeBuilder& TypeBuilder::SetIsReturnValueDefaultLocation(bool defaultLocation) +{ + BNTypeBuilderSetIsReturnValueDefaultLocation(m_object, defaultLocation); + return *this; +} + + +TypeBuilder& TypeBuilder::SetReturnValueLocation(const Confidence& location) +{ + BNValueLocationWithConfidence loc; + loc.location = location->ToAPIObject(); + loc.confidence = location.GetConfidence(); + BNTypeBuilderSetReturnValueLocation(m_object, &loc); + return *this; +} + + TypeBuilder& TypeBuilder::SetCallingConvention(const Confidence>& cc) { BNCallingConventionWithConfidence ccwc; @@ -1640,16 +1904,7 @@ vector TypeBuilder::GetParameters() const vector result; result.reserve(count); for (size_t i = 0; i < count; i++) - { - FunctionParameter param; - param.name = types[i].name; - param.type = Confidence>(new Type(BNNewTypeReference(types[i].type)), types[i].typeConfidence); - param.defaultLocation = types[i].defaultLocation; - param.location.type = types[i].location.type; - param.location.index = types[i].location.index; - param.location.storage = types[i].location.storage; - result.push_back(param); - } + result.push_back(FunctionParameter::FromAPIObject(&types[i])); BNFreeTypeParameterList(types, count); return result; @@ -1998,26 +2253,25 @@ static BNFunctionParameter* GetParamArray(const std::vector& { BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; for (size_t i = 0; i < params.size(); i++) - { - paramArray[i].name = (char*)params[i].name.c_str(); - paramArray[i].type = params[i].type->GetObject(); - paramArray[i].typeConfidence = params[i].type.GetConfidence(); - paramArray[i].defaultLocation = params[i].defaultLocation; - paramArray[i].location.type = params[i].location.type; - paramArray[i].location.index = params[i].location.index; - paramArray[i].location.storage = params[i].location.storage; - } + paramArray[i] = params[i].ToAPIObject(); count = params.size(); return paramArray; } -TypeBuilder TypeBuilder::FunctionType(const Confidence>& returnValue, + +static void FreeParamArray(BNFunctionParameter* params, size_t count) +{ + for (size_t i = 0; i < count; i++) + FunctionParameter::FreeAPIObject(¶ms[i]); + delete[] params; +} + + +TypeBuilder TypeBuilder::FunctionType(const ReturnValue& returnValue, const Confidence>& callingConvention, const std::vector& params, const Confidence& varArg, const Confidence& stackAdjust) { - BNTypeWithConfidence returnValueConf; - returnValueConf.type = returnValue->GetObject(); - returnValueConf.confidence = returnValue.GetConfidence(); + BNReturnValue ret = returnValue.ToAPIObject(); BNCallingConventionWithConfidence callingConventionConf; callingConventionConf.convention = callingConvention.GetValue() ? callingConvention->GetObject() : nullptr; @@ -2038,53 +2292,36 @@ TypeBuilder TypeBuilder::FunctionType(const Confidence>& returnValue, canReturnConf.value = true; canReturnConf.confidence = 0; - BNRegisterSetWithConfidence returnRegsConf; - returnRegsConf.regs = nullptr; - returnRegsConf.count = 0; - returnRegsConf.confidence = 0; - BNBoolWithConfidence pureConf; pureConf.value = false; pureConf.confidence = 0; - TypeBuilder type(BNCreateFunctionTypeBuilder( - &returnValueConf, &callingConventionConf, paramArray, paramCount, &varArgConf, - &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, &returnRegsConf, NoNameType, &pureConf)); - delete[] paramArray; + TypeBuilder type(BNCreateFunctionTypeBuilder(&ret, &callingConventionConf, paramArray, paramCount, &varArgConf, + &canReturnConf, &stackAdjustConf, nullptr, nullptr, 0, NoNameType, &pureConf)); + ReturnValue::FreeAPIObject(&ret); + FreeParamArray(paramArray, paramCount); return type; } -TypeBuilder TypeBuilder::FunctionType(const Confidence>& returnValue, +TypeBuilder TypeBuilder::FunctionType(const ReturnValue& returnValue, const Confidence>& callingConvention, const std::vector& params, const Confidence& hasVariableArguments, const Confidence& canReturn, const Confidence& stackAdjust, const std::map>& regStackAdjust, - const Confidence>& returnRegs, BNNameType ft, const Confidence& pure) { - BNTypeWithConfidence returnValueConf; - returnValueConf.type = returnValue->GetObject(); - returnValueConf.confidence = returnValue.GetConfidence(); + BNReturnValue ret = returnValue.ToAPIObject(); BNCallingConventionWithConfidence callingConventionConf; callingConventionConf.convention = callingConvention.GetValue() ? callingConvention->GetObject() : nullptr; callingConventionConf.confidence = callingConvention.GetConfidence(); - BNFunctionParameter* paramArray = new BNFunctionParameter[params.size()]; - for (size_t i = 0; i < params.size(); i++) - { - paramArray[i].name = (char*)params[i].name.c_str(); - paramArray[i].type = params[i].type->GetObject(); - paramArray[i].typeConfidence = params[i].type.GetConfidence(); - paramArray[i].defaultLocation = params[i].defaultLocation; - paramArray[i].location.type = params[i].location.type; - paramArray[i].location.index = params[i].location.index; - paramArray[i].location.storage = params[i].location.storage; - } + size_t paramCount = 0; + BNFunctionParameter* paramArray = GetParamArray(params, paramCount); BNBoolWithConfidence varArgConf; varArgConf.value = hasVariableArguments.GetValue(); @@ -2109,22 +2346,16 @@ TypeBuilder TypeBuilder::FunctionType(const Confidence>& returnValue, i ++; } - std::vector returnRegsRegs = returnRegs.GetValue(); - - BNRegisterSetWithConfidence returnRegsConf; - returnRegsConf.regs = returnRegsRegs.data(); - returnRegsConf.count = returnRegs->size(); - returnRegsConf.confidence = returnRegs.GetConfidence(); - BNBoolWithConfidence pureConf; pureConf.value = pure.GetValue(); pureConf.confidence = pure.GetConfidence(); TypeBuilder type(BNCreateFunctionTypeBuilder( - &returnValueConf, &callingConventionConf, paramArray, params.size(), &varArgConf, + &ret, &callingConventionConf, paramArray, paramCount, &varArgConf, &canReturnConf, &stackAdjustConf, regStackAdjustRegs.data(), - regStackAdjustValues.data(), regStackAdjust.size(), &returnRegsConf, NoNameType, &pureConf)); - delete[] paramArray; + regStackAdjustValues.data(), regStackAdjust.size(), NoNameType, &pureConf)); + ReturnValue::FreeAPIObject(&ret); + FreeParamArray(paramArray, paramCount); return type; } From a69948893eff354ffa945c19eaeebd02569908be Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 23 Dec 2025 13:12:49 -0700 Subject: [PATCH 2/4] Add handling of structure returns and parameters in aarch64 calling convention --- arch/arm64/arch_arm64.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/arch/arm64/arch_arm64.cpp b/arch/arm64/arch_arm64.cpp index 4dc59f8691..97cca592b7 100644 --- a/arch/arm64/arch_arm64.cpp +++ b/arch/arm64/arch_arm64.cpp @@ -2645,9 +2645,42 @@ class Arm64CallingConvention : public CallingConvention virtual uint32_t GetIntegerReturnValueRegister() override { return REG_X0; } + virtual uint32_t GetHighIntegerReturnValueRegister() override { return REG_X1; } virtual uint32_t GetFloatReturnValueRegister() override { return REG_V0; } + + + bool IsReturnTypeRegisterCompatible(Type* type) override + { + if (!type) + return false; + if (type->IsFloat()) + return true; + return type->GetWidth() <= 16; + } + + + Variable GetIndirectReturnValueLocation() override + { + return Variable(RegisterVariableSourceType, 0, REG_X8); + } + + + bool IsArgumentTypeRegisterCompatible(Type* type) override + { + if (!type) + return false; + if (type->IsFloat()) + return true; + return type->GetWidth() <= 16; + } + + + bool AreNonRegisterArgumentsIndirect() override + { + return true; + } }; @@ -2663,6 +2696,12 @@ class AppleArm64CallingConvention: public Arm64CallingConvention { return false; } + + + virtual bool AreStackArgumentsNaturallyAligned() override + { + return true; + } }; From 804dee52c587d0e430437d9175641364644fc82e Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Mon, 12 Jan 2026 15:19:16 -0700 Subject: [PATCH 3/4] Add handling of structure returns and parameters in Windows x86/x64 calling conventions --- arch/x86/arch_x86.cpp | 47 ++++++++ plugins/pdb-ng/src/lib.rs | 12 ++ plugins/pdb-ng/src/type_parser.rs | 177 ++++++++++++++++++++++-------- 3 files changed, 192 insertions(+), 44 deletions(-) diff --git a/arch/x86/arch_x86.cpp b/arch/x86/arch_x86.cpp index 708135a7da..98b2c4c558 100644 --- a/arch/x86/arch_x86.cpp +++ b/arch/x86/arch_x86.cpp @@ -3743,6 +3743,23 @@ class X86BaseCallingConvention: public CallingConvention } return result; } + + bool IsReturnTypeRegisterCompatible(Type* type) override + { + if (!type) + return false; + if (type->IsFloat()) + return true; + if (type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4 + || type->GetWidth() == 8) + return true; + return false; + } + + std::optional GetReturnedIndirectReturnValuePointer() override + { + return Variable(RegisterVariableSourceType, 0, XED_REG_RAX); + } }; @@ -3971,6 +3988,36 @@ class X64WindowsCallingConvention: public X64BaseCallingConvention { return true; } + + bool IsReturnTypeRegisterCompatible(Type* type) override + { + if (!type) + return false; + if (type->IsFloat()) + return true; + return type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4 + || type->GetWidth() == 8; + } + + std::optional GetReturnedIndirectReturnValuePointer() override + { + return Variable(RegisterVariableSourceType, 0, XED_REG_RAX); + } + + bool IsArgumentTypeRegisterCompatible(Type* type) override + { + if (!type) + return false; + if (type->IsFloat()) + return true; + return type->GetWidth() == 0 || type->GetWidth() == 1 || type->GetWidth() == 2 || type->GetWidth() == 4 + || type->GetWidth() == 8; + } + + bool AreNonRegisterArgumentsIndirect() override + { + return true; + } }; diff --git a/plugins/pdb-ng/src/lib.rs b/plugins/pdb-ng/src/lib.rs index 86ae1cdd07..2b7a41fb79 100644 --- a/plugins/pdb-ng/src/lib.rs +++ b/plugins/pdb-ng/src/lib.rs @@ -898,6 +898,18 @@ fn init_plugin() -> bool { }"#, ); + settings.register_setting_json( + "pdb.features.passStructuresByValue", + r#"{ + "title" : "Always Pass Structures By Value", + "type" : "boolean", + "default" : false, + "aliases" : [], + "description" : "Always pass structures by value even if they are implicitly passed by pointer in the calling convention (experimental). This more closely matches the original source code.", + "ignore" : [] + }"#, + ); + true } diff --git a/plugins/pdb-ng/src/type_parser.rs b/plugins/pdb-ng/src/type_parser.rs index 2ab2a0cc4c..8fcb2ad421 100644 --- a/plugins/pdb-ng/src/type_parser.rs +++ b/plugins/pdb-ng/src/type_parser.rs @@ -20,15 +20,17 @@ use crate::PDBParserInstance; use anyhow::{anyhow, Result}; use binaryninja::architecture::Architecture; use binaryninja::binary_view::BinaryViewExt; -use binaryninja::calling_convention::CoreCallingConvention; +use binaryninja::calling_convention::{CallingConvention, CoreCallingConvention}; use binaryninja::confidence::{Conf, MAX_CONFIDENCE}; use binaryninja::platform::Platform; use binaryninja::rc::Ref; use binaryninja::types::{ BaseStructure, EnumerationBuilder, EnumerationMember, FunctionParameter, MemberAccess, - MemberScope, NamedTypeReference, NamedTypeReferenceClass, StructureBuilder, StructureMember, - StructureType, Type, TypeBuilder, TypeClass, + MemberScope, NamedTypeReference, NamedTypeReferenceClass, ReturnValue, StructureBuilder, + StructureMember, StructureType, Type, TypeBuilder, TypeClass, ValueLocation, + ValueLocationComponent, }; +use binaryninja::variable::{Variable, VariableSourceType}; use pdb::Error::UnimplementedTypeKind; use pdb::{ ArgumentList, ArrayType, BaseClassType, BitfieldType, ClassKind, ClassType, EnumerateType, @@ -1143,30 +1145,12 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } } - let mut fancy_return_type = return_type.clone(); + let mut fancy_return_value = ReturnValue { + ty: Conf::new(return_type.clone(), MAX_CONFIDENCE), + location: None, + }; let mut fancy_arguments = arguments.clone(); - if data.attributes.cxx_return_udt() - || !self.can_fit_in_register(data.return_type, finder, true) - { - // Return UDT?? - // This probably means the return value got pushed to the stack - fancy_return_type = - Type::pointer(&self.arch, &Conf::new(return_type.clone(), MAX_CONFIDENCE)); - fancy_arguments.insert( - 0, - FunctionParameter::new( - Conf::new(fancy_return_type.clone(), MAX_CONFIDENCE), - "__return".to_string(), - None, - ), - ); - } - - if let Some(this_ptr) = &this_pointer_type { - self.insert_this_pointer(&mut fancy_arguments, this_ptr.clone())?; - } - let convention = self .cv_call_t_to_calling_convention(data.attributes.calling_convention()) .map(|cc| Conf::new(cc, MAX_CONFIDENCE)) @@ -1180,6 +1164,37 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } }); + if data.attributes.cxx_return_udt() + || !self.can_fit_in_register(data.return_type, finder, true) + { + // Return UDT?? + // This probably means the return value got pushed to the stack + if self.settings.get_bool_with_opts( + "pdb.features.passStructuresByValue", + &mut self.settings_query_opts, + ) { + fancy_return_value.location = + self.indirect_return_value_location(&convention, &fancy_return_value); + } else { + fancy_return_value.ty = Conf::new( + Type::pointer(&self.arch, &return_type.clone()), + MAX_CONFIDENCE, + ); + fancy_arguments.insert( + 0, + FunctionParameter::new( + fancy_return_value.ty.clone(), + "__return".to_string(), + None, + ), + ); + } + } + + if let Some(this_ptr) = &this_pointer_type { + self.insert_this_pointer(&mut fancy_arguments, this_ptr.clone())?; + } + let func = Type::function_with_opts( &Conf::new(return_type, MAX_CONFIDENCE), arguments.as_slice(), @@ -1189,7 +1204,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ); let fancy_func = Type::function_with_opts( - &Conf::new(fancy_return_type, MAX_CONFIDENCE), + fancy_return_value, fancy_arguments.as_slice(), is_varargs, convention, @@ -1423,31 +1438,46 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { } } - let mut fancy_return_type = return_type.clone(); + let mut fancy_return_value = ReturnValue { + ty: return_type.clone(), + location: None, + }; let mut fancy_arguments = arguments.clone(); + let convention = self + .cv_call_t_to_calling_convention(data.attributes.calling_convention()) + .map(|cc| Conf::new(cc, MAX_CONFIDENCE)) + .unwrap_or(Conf::new(self.default_cc.clone(), 0)); + self.log(|| format!("Convention: {:?}", convention)); + let mut return_stacky = data.attributes.cxx_return_udt(); if let Some(return_type_index) = data.return_type { return_stacky |= !self.can_fit_in_register(return_type_index, finder, true); } if return_stacky { // Stack return via a pointer in the first parameter - fancy_return_type = Conf::new( - Type::pointer(&self.arch, &return_type.clone()), - MAX_CONFIDENCE, - ); - fancy_arguments.insert( - 0, - FunctionParameter::new(fancy_return_type.clone(), "__return".to_string(), None), - ); + if self.settings.get_bool_with_opts( + "pdb.features.passStructuresByValue", + &mut self.settings_query_opts, + ) { + fancy_return_value.location = + self.indirect_return_value_location(&convention, &fancy_return_value); + } else { + fancy_return_value.ty = Conf::new( + Type::pointer(&self.arch, &return_type.clone()), + MAX_CONFIDENCE, + ); + fancy_arguments.insert( + 0, + FunctionParameter::new( + fancy_return_value.ty.clone(), + "__return".to_string(), + None, + ), + ); + } } - let convention = self - .cv_call_t_to_calling_convention(data.attributes.calling_convention()) - .map(|cc| Conf::new(cc, MAX_CONFIDENCE)) - .unwrap_or(Conf::new(self.default_cc.clone(), 0)); - self.log(|| format!("Convention: {:?}", convention)); - let func = Type::function_with_opts( &return_type, arguments.as_slice(), @@ -1457,7 +1487,7 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { ); let fancy_func = Type::function_with_opts( - &fancy_return_type, + fancy_return_value, fancy_arguments.as_slice(), is_varargs, convention, @@ -1815,8 +1845,13 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { Some(ty) => { // On x86_32, structures are stored on the stack directly // On x64, they are put into pointers if they are not a int size - // TODO: Ugly hack - if self.arch.address_size() == 4 || Self::size_can_fit_in_register(ty.width()) { + if self.arch.address_size() == 4 + || Self::size_can_fit_in_register(ty.width()) + || self.settings.get_bool_with_opts( + "pdb.features.passStructuresByValue", + &mut self.settings_query_opts, + ) + { args.push(FunctionParameter::new( Conf::new(ty.clone(), MAX_CONFIDENCE), "".to_string(), @@ -2372,4 +2407,58 @@ impl<'a, S: Source<'a> + 'a> PDBParserInstance<'a, S> { _ => false, } } + + /// Determines if there is a non-default location for an indirect return value and returns + /// the location if there is one. + fn indirect_return_value_location( + &self, + convention: &Conf>, + return_value: &ReturnValue, + ) -> Option> { + // Non-POD data types are always returned as indirect values. The calling convention + // may not know this and try to place them in registers, so check the calling convention + // to see if it wants to pass indirectly. + // TODO: The structures themselves should have some kind of non-POD attribute so + // that the calling convention can determine this by default + let default_return_location = convention + .contents + .return_value_location(return_value.clone()); + let default_return_indrect = default_return_location + .components + .iter() + .any(|c| c.indirect); + if default_return_indrect { + None + } else { + let variable = if let Some(reg) = convention.contents.int_arg_registers().get(0) { + Variable::new( + VariableSourceType::RegisterVariableSourceType, + 0, + reg.0 as i64, + ) + } else { + Variable::new( + VariableSourceType::StackVariableSourceType, + 0, + self.arch.address_size() as i64, + ) + }; + Some(Conf::new( + ValueLocation { + components: vec![ValueLocationComponent { + variable, + offset: 0, + size: Some(return_value.ty.contents.width()), + indirect: true, + returned_pointer: convention.contents.return_int_reg().map(|reg| Variable::new( + VariableSourceType::RegisterVariableSourceType, + 0, + reg.0 as i64, + )), + }], + }, + MAX_CONFIDENCE, + )) + } + } } From 081439286e64f8b14381e8220a9f3b7d8066aaf0 Mon Sep 17 00:00:00 2001 From: Rusty Wagner Date: Tue, 13 Jan 2026 19:02:38 -0700 Subject: [PATCH 4/4] Parameter location display and parsing --- binaryninjaapi.h | 5 +++++ binaryninjacore.h | 5 +++++ lang/rust/rusttypes.cpp | 16 +++++++++++--- python/examples/pseudo_python.py | 14 ++++++++++-- python/types.py | 28 +++++++++++++++++++++++ rust/src/disassembly.rs | 5 +++++ rust/src/types.rs | 4 ++-- type.cpp | 38 ++++++++++++++++++++++++++++++++ 8 files changed, 108 insertions(+), 7 deletions(-) diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 9e81d01521..cac894492d 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -10222,6 +10222,8 @@ namespace BinaryNinja { static ValueLocationComponent FromAPIObject(const BNValueLocationComponent* loc); BNValueLocationComponent ToAPIObject() const; + + std::string ToString(Architecture* arch) const; }; struct ValueLocation @@ -10251,6 +10253,9 @@ namespace BinaryNinja { static ValueLocation FromAPIObject(const BNValueLocation* loc); BNValueLocation ToAPIObject() const; static void FreeAPIObject(BNValueLocation* loc); + + static std::optional Parse(const std::string& str, Architecture* arch, std::string& error); + std::string ToString(Architecture* arch) const; }; struct FunctionParameter diff --git a/binaryninjacore.h b/binaryninjacore.h index c7e4e087fb..2ab5ccbe8d 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -478,6 +478,7 @@ extern "C" BaseStructureNameToken = 37, BaseStructureSeparatorToken = 38, BraceToken = 39, + ValueLocationToken = 40, // The following are output by the analysis system automatically, these should // not be used directly by the architecture plugins CodeSymbolToken = 64, @@ -7063,6 +7064,10 @@ extern "C" BINARYNINJACOREAPI BNReturnValue BNGetTypeReturnValue(BNType* type); BINARYNINJACOREAPI bool BNIsTypeReturnValueDefaultLocation(BNType* type); BINARYNINJACOREAPI BNValueLocationWithConfidence BNGetTypeReturnValueLocation(BNType* type); + BINARYNINJACOREAPI bool BNParseValueLocation( + const char* str, BNArchitecture* arch, BNValueLocation* location, char** error); + BINARYNINJACOREAPI char* BNValueLocationToString(BNValueLocation* location, BNArchitecture* arch); + BINARYNINJACOREAPI char* BNValueLocationComponentToString(BNValueLocationComponent* component, BNArchitecture* arch); BINARYNINJACOREAPI void BNFreeValueLocation(BNValueLocation* location); BINARYNINJACOREAPI BNCallingConventionWithConfidence BNGetTypeCallingConvention(BNType* type); BINARYNINJACOREAPI BNCallingConventionName BNGetTypeCallingConventionName(BNType* type); diff --git a/lang/rust/rusttypes.cpp b/lang/rust/rusttypes.cpp index 0147c12efc..7ad9867995 100644 --- a/lang/rust/rusttypes.cpp +++ b/lang/rust/rusttypes.cpp @@ -177,7 +177,6 @@ vector RustTypePrinter::GetTypeTokensAfterNameInternal( if (!params[i].defaultLocation && platform && var.has_value()) { - // TODO: Emit a syntax for parameters spanning multiple storage locations switch (var->type) { case RegisterVariableSourceType: @@ -202,10 +201,14 @@ vector RustTypePrinter::GetTypeTokensAfterNameInternal( tokens.emplace_back(IntegerToken, storageStr, var->storage); break; } - case CompositeReturnValueSourceType: - case CompositeParameterSourceType: + default: + { + string locationStr = params[i].location.ToString(platform->GetArchitecture()); + tokens.emplace_back(TextToken, " @ "); + tokens.emplace_back(ValueLocationToken, locationStr); break; } + } } } @@ -235,6 +238,13 @@ vector RustTypePrinter::GetTypeTokensAfterNameInternal( i.context = FunctionReturnTokenContext; } tokens.insert(tokens.end(), retn.begin(), retn.end()); + if (!type->GetReturnValue().defaultLocation) + { + auto location = type->GetReturnValue().location; + string locationStr = location->ToString(platform->GetArchitecture()); + tokens.emplace_back(TextToken, " @ "); + tokens.emplace_back(location.GetCombinedConfidence(baseConfidence), ValueLocationToken, locationStr); + } } break; } diff --git a/python/examples/pseudo_python.py b/python/examples/pseudo_python.py index d29d4e5795..411418f3f8 100644 --- a/python/examples/pseudo_python.py +++ b/python/examples/pseudo_python.py @@ -1136,23 +1136,33 @@ def function_type_tokens(self, func: Function, settings: DisassemblySettings) -> tokens.append(InstructionTextToken(InstructionTextTokenType.KeywordToken, "def ")) tokens.append(InstructionTextToken(InstructionTextTokenType.CodeSymbolToken, func.name, value=func.start)) tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, "(")) + params = func.type.parameters for (i, param) in enumerate(func.type.parameters_with_all_locations): if i > 0: tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ", ")) + var = param.location.variable_for_parameter(i) tokens.append(InstructionTextToken(InstructionTextTokenType.ArgumentNameToken, param.name, context=InstructionTextTokenContext.LocalVariableTokenContext, - address=param.location.identifier)) + address=var.identifier)) tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ": ")) for token in param.type.get_tokens(): token.context = InstructionTextTokenContext.LocalVariableTokenContext - token.address = param.location.identifier + token.address = var.identifier tokens.append(token) + if i < len(params) and params[i].location is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " @ ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.ValueLocationToken, + params[i].location.to_string(func.arch))) tokens.append(InstructionTextToken(InstructionTextTokenType.BraceToken, ")")) if func.can_return.value and func.type.return_value is not None and not isinstance(func.type.return_value, VoidType): tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " -> ")) for token in func.type.return_value.get_tokens(): token.context = InstructionTextTokenContext.FunctionReturnTokenContext tokens.append(token) + if func.type.return_value_location is not None: + tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, " @ ")) + tokens.append(InstructionTextToken(InstructionTextTokenType.ValueLocationToken, + func.type.return_value_location.location.to_string(func.arch))) tokens.append(InstructionTextToken(InstructionTextTokenType.TextToken, ":")) return [DisassemblyTextLine(tokens, func.start)] diff --git a/python/types.py b/python/types.py index bf3614c990..ecc00b5d04 100644 --- a/python/types.py +++ b/python/types.py @@ -471,6 +471,10 @@ def _to_core_struct(self) -> core.BNValueLocationComponent: struct.returnedPointer = self.returned_pointer.to_BNVariable() return struct + def to_string(self, arch: 'architecture.Architecture'): + struct = self._to_core_struct() + return core.BNValueLocationComponentToString(struct, arch.handle) + @dataclass class ValueLocation: @@ -495,6 +499,30 @@ def _to_core_struct(self) -> core.BNValueLocation: def with_confidence(self, confidence: int) -> 'ValueLocationWithConfidence': return ValueLocationWithConfidence(self, confidence) + def variable_for_parameter(self, idx: int) -> Optional['variable.CoreVariable']: + struct = self._to_core_struct() + var = core.BNVariable() + if core.BNGetValueLocationVariableForParameter(struct, var, idx): + return variable.CoreVariable.from_BNVariable(var) + return None + + @staticmethod + def parse(string: str, arch: 'architecture.Architecture') -> 'ValueLocation': + struct = core.BNValueLocation() + error = ctypes.c_char_p() + if not core.BNParseValueLocation(string, arch.handle, struct, error): + assert error.value is not None, "core.BNParseValueLocation returned 'error' set to None" + error_str = error.value.decode("utf-8") + core.free_string(error) + raise SyntaxError(error_str) + result = ValueLocation._from_core_struct(struct) + core.BNFreeValueLocation(struct) + return result + + def to_string(self, arch: 'architecture.Architecture'): + struct = self._to_core_struct() + return core.BNValueLocationToString(struct, arch.handle) + @dataclass class ValueLocationWithConfidence: diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs index cb9bf6a19c..718c77376a 100644 --- a/rust/src/disassembly.rs +++ b/rust/src/disassembly.rs @@ -506,6 +506,7 @@ pub enum InstructionTextTokenKind { // TODO: Explain what this is hash: Option, }, + ValueLocation, CodeSymbol { // Target address of the symbol value: u64, @@ -700,6 +701,7 @@ impl InstructionTextTokenKind { hash => Some(hash), }, }, + BNInstructionTextTokenType::ValueLocationToken => Self::ValueLocation, BNInstructionTextTokenType::CodeSymbolToken => Self::CodeSymbol { value: value.value, size: value.size, @@ -914,6 +916,9 @@ impl From for BNInstructionTextTokenType { BNInstructionTextTokenType::BaseStructureSeparatorToken } InstructionTextTokenKind::Brace { .. } => BNInstructionTextTokenType::BraceToken, + InstructionTextTokenKind::ValueLocation => { + BNInstructionTextTokenType::ValueLocationToken + } InstructionTextTokenKind::CodeSymbol { .. } => { BNInstructionTextTokenType::CodeSymbolToken } diff --git a/rust/src/types.rs b/rust/src/types.rs index 4baf133116..e9d2d446f6 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -1180,7 +1180,7 @@ impl ValueLocation { } } - pub(crate) fn into_rust_raw(value: &Self) -> BNValueLocation { + pub fn into_rust_raw(value: &Self) -> BNValueLocation { let components: Box<[BNValueLocationComponent]> = value .components .iter() @@ -1193,7 +1193,7 @@ impl ValueLocation { } /// Free a RUST ALLOCATED possible value set. Do not use this with CORE ALLOCATED values. - pub(crate) fn free_rust_raw(value: BNValueLocation) { + pub fn free_rust_raw(value: BNValueLocation) { let raw_components = unsafe { std::slice::from_raw_parts_mut(value.components, value.count) }; let _ = unsafe { Box::from_raw(raw_components) }; diff --git a/type.cpp b/type.cpp index d7e3a9f817..45c7aec353 100644 --- a/type.cpp +++ b/type.cpp @@ -591,6 +591,16 @@ BNValueLocationComponent ValueLocationComponent::ToAPIObject() const } +std::string ValueLocationComponent::ToString(Architecture* arch) const +{ + auto componentRaw = ToAPIObject(); + char* str = BNValueLocationComponentToString(&componentRaw, arch->GetObject()); + string result = str; + BNFreeString(str); + return result; +} + + std::optional ValueLocation::GetVariableForReturnValue() const { BNValueLocation loc = ToAPIObject(); @@ -680,6 +690,34 @@ void ValueLocation::FreeAPIObject(BNValueLocation* loc) } +std::optional ValueLocation::Parse(const std::string& str, Architecture* arch, std::string& error) +{ + BNValueLocation locationRaw; + char* errorRaw; + if (BNParseValueLocation(str.c_str(), arch->GetObject(), &locationRaw, &errorRaw)) + { + auto location = FromAPIObject(&locationRaw); + BNFreeValueLocation(&locationRaw); + return location; + } + + error = errorRaw; + BNFreeString(errorRaw); + return std::nullopt; +} + + +std::string ValueLocation::ToString(Architecture* arch) const +{ + auto locationRaw = ToAPIObject(); + char* str = BNValueLocationToString(&locationRaw, arch->GetObject()); + FreeAPIObject(&locationRaw); + string result = str; + BNFreeString(str); + return result; +} + + bool ReturnValue::operator==(const ReturnValue& nt) const { if (type != nt.type)